From f09916d72034242a9bc4966ecf6f5590ed40d2cc Mon Sep 17 00:00:00 2001 From: coffeepots Date: Tue, 14 Jul 2015 11:58:26 +0100 Subject: [PATCH 01/18] Procs to decode Time to TimeInfo & TimeInterval Fills in the missing functionality of decoding Time to TimeInfo and TimeInterval, whilst also adding some procs to work with leap years and to get the day of the week based on a date. --- lib/pure/times.nim | 130 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index e4d3f7494e..efed1bbc05 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1039,6 +1039,126 @@ proc parse*(value, layout: string): TimeInfo = info.weekday = getLocalTime(timeInfoToTime(info)).weekday return info +# Leap year calculations are adapted from: +# from http://www.codeproject.com/Articles/7358/Ultra-fast-Algorithms-for-Working-with-Leap-Years +# The dayOfTheWeek procs are adapated from: +# from http://stason.org/TULARC/society/calendars/2-5-What-day-of-the-week-was-2-August-1953.html + +# Note: for leap years, start date is assumed to be 1 AD. +# counts the number of leap years up to January 1st of a given year. +# Keep in mind that if specified year is a leap year, the leap day +# has not happened before January 1st of that year. +proc countLeapYears(yearSpan: int): int = + (((yearSpan - 1) / 4) - ((yearSpan - 1) / 100) + ((yearSpan - 1)/400)).int + +proc countDays(yearSpan: int): int = + (yearSpan - 1) * 365 + countLeapYears(yearSpan) + +# counts the number of years spanned by a given number of days. +proc countYears(daySpan: int): int = + ((daySpan - countLeapYears(daySpan div 365)) div 365) + +proc countYearsAndDays(daySpan: int): tuple[years: int, days: int] = + let days = daySpan - countLeapYears(daySpan div 365) + result.years = days div 365 + result.days = days mod 365 + +type + SecondScale = enum ssMinute, ssHour, ssDay, ssMonth, ssYear + +# these are very approximate beyond day +const secondsIn*: array[SecondScale.low..SecondScale.high, int] =[ + 60, # minute + 60*60, # hour + 60*60*24, # day + 60*60*24*30, # month (estimate) + 60*60*24*30*12] # year (estimate) + +const + epochStartYear = 1970 + leapYearsSinceEpoch* = countLeapYears(epochStartYear) + +proc dayOfWeek*(day, month, year: int): WeekDay = + # This is for the Gregorian calendar + # Day & month start from one. + let + a = (14 - month) div 12 + y = year - a + m = month + (12*a) - 2 + d = (day + y + (y div 4) - (y div 100) + (y div 400) + (31*m) div 12) mod 7 + # The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc. so we must correct + # for the WeekDay type. + if d == 0: return dSun + result = (d-1).WeekDay + +proc dayOfWeekJulian*(day, month, year: int): WeekDay = + # This is for the Julian calendar + # Day & month start from one. + let + a = (14 - month) div 12 + y = year - a + m = month + (12*a) - 2 + d = (5 + day + y + (y div 4) + (31*m) div 12) mod 7 + # The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc. so we must correct + # for the WeekDay type. + if d == 0: return dSun + result = (d-1).WeekDay + +proc decodeTime*(t: Time): TimeInfo = + let + daysSinceEpoch = t.int div secondsIn[ssDay] + (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch) + daySeconds = t.int mod secondsIn[ssDay] + + y = yearsSinceEpoch + epochStartYear + + var + mon = mJan + days = daysRemaining + daysInMonth = getDaysInMonth(mon, y) + + # calculate month and day remainder + while days > daysInMonth and mon <= mDec: + days -= daysInMonth + mon.inc + daysInMonth = getDaysInMonth(mon, y) + + let + yd = daysRemaining + m = mon # month is zero indexed enum + md = days + # NB: month is zero indexed but dayOfWeek expects 1 indexed. + wd = dayOfWeek(days, mon.int + 1, y).Weekday + h = daySeconds div secondsIn[ssHour] + 1 + mi = (daySeconds mod secondsIn[ssHour]) div secondsIn[ssMinute] + s = daySeconds mod secondsIn[ssMinute] + result = TimeInfo(year: y, yearday: yd, month: m, monthday: md, weekday: wd, hour: h, minute: mi, second: s) + +proc decodeTimeInterval*(t: Time): TimeInterval = + var + daysSinceEpoch = t.int div secondsIn[ssDay] + (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch) + daySeconds = t.int mod secondsIn[ssDay] + + result.years = yearsSinceEpoch + epochStartYear + + var + mon = mJan + days = daysRemaining + daysInMonth = getDaysInMonth(mon, result.years) + + # calculate month and day remainder + while days > daysInMonth and mon <= mDec: + days -= daysInMonth + mon.inc + daysInMonth = getDaysInMonth(mon, result.years) + + result.months = mon.int + 1 # month is 1 indexed int + result.days = days + result.hours = daySeconds div secondsIn[ssHour] + 1 + result.minutes = (daySeconds mod secondsIn[ssHour]) div secondsIn[ssMinute] + result.seconds = daySeconds mod secondsIn[ssMinute] + # Milliseconds not available from Time when isMainModule: # $ date --date='@2147483647' @@ -1122,3 +1242,13 @@ when isMainModule: assert "15:04:00" in $s.parse(f) when not defined(testing): echo "Kitchen: " & $s.parse(f) + var ti = decodeTime(getTime()) + echo "Todays date after decoding: ", ti + var tint = decodeTimeInterval(getTime()) + echo "Todays date after decoding to interval: ", tint + # checking dayOfWeek matches known days + assert dayOfWeek(21, 9, 1900) == dFri + assert dayOfWeek(1, 1, 1970) == dThu + assert dayOfWeek(21, 9, 1970) == dMon + assert dayOfWeek(1, 1, 2000) == dSat + assert dayOfWeek(1, 1, 2021) == dFri From a2f0fe03b634753fb26c6da899fcdefbb8e46035 Mon Sep 17 00:00:00 2001 From: coffeepots Date: Thu, 16 Jul 2015 10:20:34 +0100 Subject: [PATCH 02/18] Fixed dayOfWeekJulian, exported SecondScale --- lib/pure/times.nim | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index efed1bbc05..a85998cb5c 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1064,7 +1064,7 @@ proc countYearsAndDays(daySpan: int): tuple[years: int, days: int] = result.days = days mod 365 type - SecondScale = enum ssMinute, ssHour, ssDay, ssMonth, ssYear + SecondScale* = enum ssMinute, ssHour, ssDay, ssMonth, ssYear # these are very approximate beyond day const secondsIn*: array[SecondScale.low..SecondScale.high, int] =[ @@ -1099,10 +1099,7 @@ proc dayOfWeekJulian*(day, month, year: int): WeekDay = y = year - a m = month + (12*a) - 2 d = (5 + day + y + (y div 4) + (31*m) div 12) mod 7 - # The value of d is 0 for a Sunday, 1 for a Monday, 2 for a Tuesday, etc. so we must correct - # for the WeekDay type. - if d == 0: return dSun - result = (d-1).WeekDay + result = d.WeekDay proc decodeTime*(t: Time): TimeInfo = let @@ -1252,3 +1249,8 @@ when isMainModule: assert dayOfWeek(21, 9, 1970) == dMon assert dayOfWeek(1, 1, 2000) == dSat assert dayOfWeek(1, 1, 2021) == dFri + # Julian tests + assert dayOfWeekJulian(21, 9, 1900) == dFri + assert dayOfWeekJulian(21, 9, 1970) == dMon + assert dayOfWeekJulian(1, 1, 2000) == dSat + assert dayOfWeekJulian(1, 1, 2021) == dFri From 3fe39798e6a124d8318c8a6aa1b0ede08bee1cf8 Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Wed, 29 Jul 2015 11:47:21 -0500 Subject: [PATCH 03/18] Fix contributing guide rST --- contributing.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contributing.rst b/contributing.rst index 68b706c735..2fd04df366 100644 --- a/contributing.rst +++ b/contributing.rst @@ -66,12 +66,14 @@ Running tests You can run the tests with :: + ./koch tests which will run a good subset of tests. Some tests may fail. If you only want to see the output of failing tests, go for :: + ./koch tests --failing all You can also run only a single category of tests. A category is a subdirectory @@ -79,6 +81,7 @@ in the ``tests`` directory. There are a couple of special categories; for a list of these, see ``tests/testament/categories.nim``, at the bottom. :: + ./koch tests c lib Comparing tests @@ -92,6 +95,7 @@ reference test. You'll also need to the commit id, because that's what the tester needs to know in order to compare the two. :: + git checkout devel DEVEL_COMMIT=$(git rev-parse HEAD) ./koch tests @@ -99,6 +103,7 @@ the tester needs to know in order to compare the two. Then switch over to your changes and run the tester again. :: + git checkout your-changes ./koch tests @@ -106,6 +111,7 @@ Then you can ask the tester to create a ``testresults.html`` which will tell you if any new tests passed/failed. :: + ./koch --print html $DEVEL_COMMIT From 5e0b8d5ef6ce616361cedf963afde667edfadb89 Mon Sep 17 00:00:00 2001 From: coffeepots Date: Fri, 31 Jul 2015 09:44:37 +0100 Subject: [PATCH 04/18] Implemented changes suggested by dom96 * Removed extraneous exports (and converted const array to separate consts) * Renamed dayOfWeek, dayOfWeekJulian to getDayOfWeek and getDayOfWeekJulian * Renamed decodeTime procs to timeToTimeInfo and timeToTimeInterval * Added some basic descriptions to docs --- lib/pure/times.nim | 79 ++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 41 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index a85998cb5c..e69357597d 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1054,32 +1054,27 @@ proc countLeapYears(yearSpan: int): int = proc countDays(yearSpan: int): int = (yearSpan - 1) * 365 + countLeapYears(yearSpan) -# counts the number of years spanned by a given number of days. proc countYears(daySpan: int): int = + # counts the number of years spanned by a given number of days. ((daySpan - countLeapYears(daySpan div 365)) div 365) proc countYearsAndDays(daySpan: int): tuple[years: int, days: int] = + # counts the number of years spanned by a given number of days and the remainder as days. let days = daySpan - countLeapYears(daySpan div 365) result.years = days div 365 result.days = days mod 365 -type - SecondScale* = enum ssMinute, ssHour, ssDay, ssMonth, ssYear - -# these are very approximate beyond day -const secondsIn*: array[SecondScale.low..SecondScale.high, int] =[ - 60, # minute - 60*60, # hour - 60*60*24, # day - 60*60*24*30, # month (estimate) - 60*60*24*30*12] # year (estimate) +const + secondsInMin = 60 + secondsInHour = 60*60 + secondsInDay = 60*60*24 const epochStartYear = 1970 - leapYearsSinceEpoch* = countLeapYears(epochStartYear) + leapYearsSinceEpoch = countLeapYears(epochStartYear) -proc dayOfWeek*(day, month, year: int): WeekDay = - # This is for the Gregorian calendar +proc getDayOfWeek*(day, month, year: int): WeekDay = + ## Returns the day of the week enum from day, month and year. # Day & month start from one. let a = (14 - month) div 12 @@ -1091,8 +1086,8 @@ proc dayOfWeek*(day, month, year: int): WeekDay = if d == 0: return dSun result = (d-1).WeekDay -proc dayOfWeekJulian*(day, month, year: int): WeekDay = - # This is for the Julian calendar +proc getDayOfWeekJulian*(day, month, year: int): WeekDay = + ## Returns the day of the week enum from day, month and year, according to the Julian calender. # Day & month start from one. let a = (14 - month) div 12 @@ -1101,11 +1096,12 @@ proc dayOfWeekJulian*(day, month, year: int): WeekDay = d = (5 + day + y + (y div 4) + (31*m) div 12) mod 7 result = d.WeekDay -proc decodeTime*(t: Time): TimeInfo = +proc timeToTimeInfo*(t: Time): TimeInfo = + ## Converts a Time to TimeInfo. let - daysSinceEpoch = t.int div secondsIn[ssDay] + daysSinceEpoch = t.int div secondsInDay (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch) - daySeconds = t.int mod secondsIn[ssDay] + daySeconds = t.int mod secondsInDay y = yearsSinceEpoch + epochStartYear @@ -1125,17 +1121,18 @@ proc decodeTime*(t: Time): TimeInfo = m = mon # month is zero indexed enum md = days # NB: month is zero indexed but dayOfWeek expects 1 indexed. - wd = dayOfWeek(days, mon.int + 1, y).Weekday - h = daySeconds div secondsIn[ssHour] + 1 - mi = (daySeconds mod secondsIn[ssHour]) div secondsIn[ssMinute] - s = daySeconds mod secondsIn[ssMinute] - result = TimeInfo(year: y, yearday: yd, month: m, monthday: md, weekday: wd, hour: h, minute: mi, second: s) + wd = getDayOfWeek(days, mon.int + 1, y).Weekday + h = daySeconds div secondsInHour + 1 + mi = (daySeconds mod secondsInHour) div secondsInMin + s = daySeconds mod secondsInMin + result = TimeInfo(year: y, yearday: yd, month: m, monthday: md, weekday: wd, hour: h, minute: mi, second: s) -proc decodeTimeInterval*(t: Time): TimeInterval = +proc timetoTimeInterval*(t: Time): TimeInterval = + ## Converts a Time to a TimeInterval. var - daysSinceEpoch = t.int div secondsIn[ssDay] + daysSinceEpoch = t.int div secondsInDay (yearsSinceEpoch, daysRemaining) = countYearsAndDays(daysSinceEpoch) - daySeconds = t.int mod secondsIn[ssDay] + daySeconds = t.int mod secondsInDay result.years = yearsSinceEpoch + epochStartYear @@ -1152,9 +1149,9 @@ proc decodeTimeInterval*(t: Time): TimeInterval = result.months = mon.int + 1 # month is 1 indexed int result.days = days - result.hours = daySeconds div secondsIn[ssHour] + 1 - result.minutes = (daySeconds mod secondsIn[ssHour]) div secondsIn[ssMinute] - result.seconds = daySeconds mod secondsIn[ssMinute] + result.hours = daySeconds div secondsInHour + 1 + result.minutes = (daySeconds mod secondsInHour) div secondsInMin + result.seconds = daySeconds mod secondsInMin # Milliseconds not available from Time when isMainModule: @@ -1239,18 +1236,18 @@ when isMainModule: assert "15:04:00" in $s.parse(f) when not defined(testing): echo "Kitchen: " & $s.parse(f) - var ti = decodeTime(getTime()) + var ti = timeToTimeInfo(getTime()) echo "Todays date after decoding: ", ti - var tint = decodeTimeInterval(getTime()) + var tint = timeToTimeInterval(getTime()) echo "Todays date after decoding to interval: ", tint # checking dayOfWeek matches known days - assert dayOfWeek(21, 9, 1900) == dFri - assert dayOfWeek(1, 1, 1970) == dThu - assert dayOfWeek(21, 9, 1970) == dMon - assert dayOfWeek(1, 1, 2000) == dSat - assert dayOfWeek(1, 1, 2021) == dFri + assert getDayOfWeek(21, 9, 1900) == dFri + assert getDayOfWeek(1, 1, 1970) == dThu + assert getDayOfWeek(21, 9, 1970) == dMon + assert getDayOfWeek(1, 1, 2000) == dSat + assert getDayOfWeek(1, 1, 2021) == dFri # Julian tests - assert dayOfWeekJulian(21, 9, 1900) == dFri - assert dayOfWeekJulian(21, 9, 1970) == dMon - assert dayOfWeekJulian(1, 1, 2000) == dSat - assert dayOfWeekJulian(1, 1, 2021) == dFri + assert getDayOfWeekJulian(21, 9, 1900) == dFri + assert getDayOfWeekJulian(21, 9, 1970) == dMon + assert getDayOfWeekJulian(1, 1, 2000) == dSat + assert getDayOfWeekJulian(1, 1, 2021) == dFri From f9d909bb947749666c0241f64bd8adef8c071a9b Mon Sep 17 00:00:00 2001 From: coffeepots Date: Fri, 31 Jul 2015 10:20:21 +0100 Subject: [PATCH 05/18] Removed unused leapYearsSinceEpoch --- lib/pure/times.nim | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index e69357597d..0b77f02c57 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1068,10 +1068,7 @@ const secondsInMin = 60 secondsInHour = 60*60 secondsInDay = 60*60*24 - -const epochStartYear = 1970 - leapYearsSinceEpoch = countLeapYears(epochStartYear) proc getDayOfWeek*(day, month, year: int): WeekDay = ## Returns the day of the week enum from day, month and year. From eac484167c7b9d6115133bc936993ae7534cd4e9 Mon Sep 17 00:00:00 2001 From: Nycto Date: Sat, 1 Aug 2015 18:48:41 -0700 Subject: [PATCH 06/18] Fix multiple requires in a test --- lib/pure/unittest.nim | 2 +- tests/stdlib/tunittest.nim | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index c459023a96..064937ad8b 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -285,7 +285,7 @@ macro check*(conditions: stmt): stmt {.immediate.} = result = getAst(rewrite(checked, checked.lineinfo, checked.toStrLit)) -template require*(conditions: stmt): stmt {.immediate, dirty.} = +template require*(conditions: stmt): stmt {.immediate.} = ## Same as `check` except any failed test causes the program to quit ## immediately. Any teardown statements are not executed and the failed ## test output is not generated. diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim index fb9b022438..1389214ea5 100644 --- a/tests/stdlib/tunittest.nim +++ b/tests/stdlib/tunittest.nim @@ -21,6 +21,11 @@ test "unittest typedescs": check(none(int) != some(1)) +test "unittest multiple requires": + require(true) + require(true) + + import math from strutils import parseInt proc defectiveRobot() = From 436b847a7ecc0b4ce5a7952ea9e6af846a0d5491 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Mon, 3 Aug 2015 14:11:48 +0300 Subject: [PATCH 07/18] Workaround for #3179. --- lib/system.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index 383002fcfc..85d16708ca 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1490,7 +1490,7 @@ when not defined(nimrodVM): ## containing zero, so it is somewhat safer than ``createU``. ## The allocated memory belongs to its allocating thread! ## Use `createShared` to allocate from a shared heap. - cast[ptr T](alloc0(T.sizeof * size)) + cast[ptr T](alloc0(sizeof(T) * size)) proc realloc*(p: pointer, newSize: Natural): pointer {.noconv, rtl, tags: [], benign.} ## grows or shrinks a given memory block. If p is **nil** then a new From 0d8942d45e5d477224d486ab66adfcf38230aa8b Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 5 Aug 2015 21:27:53 +0200 Subject: [PATCH 08/18] destructors now work with overloaded assignment operators; fixes #2811; fixes #1632 --- compiler/semdestruct.nim | 9 ++ compiler/semexprs.nim | 5 +- compiler/semstmts.nim | 20 +++-- compiler/transf.nim | 83 ++++++++++++++++++- tests/destructor/tdestructor3.nim | 47 +++++++++++ tests/exception/tdefer1.nim | 26 +++++- .../tdictdestruct.nim | 0 7 files changed, 175 insertions(+), 15 deletions(-) create mode 100644 tests/destructor/tdestructor3.nim rename tests/{destructor => generics}/tdictdestruct.nim (100%) diff --git a/compiler/semdestruct.nim b/compiler/semdestruct.nim index aaab49a101..af671f6e00 100644 --- a/compiler/semdestruct.nim +++ b/compiler/semdestruct.nim @@ -177,6 +177,15 @@ proc instantiateDestructor(c: PContext, typ: PType): PType = else: return nil +proc createDestructorCall(c: PContext, s: PSym): PNode = + let varTyp = s.typ + if varTyp == nil or sfGlobal in s.flags: return + let destructableT = instantiateDestructor(c, varTyp) + if destructableT != nil: + let call = semStmt(c, newNode(nkCall, s.info, @[ + useSym(destructableT.destructor), useSym(s)])) + result = newNode(nkDefer, s.info, @[call]) + proc insertDestructors(c: PContext, varSection: PNode): tuple[outer, inner: PNode] = # Accepts a var or let section. diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index b4308def3c..af6919d972 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2259,7 +2259,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkStaticStmt: result = semStaticStmt(c, n) of nkDefer: - localError(n.info, errGenerated, "'defer' not allowed in this context") + n.sons[0] = semExpr(c, n.sons[0]) + if not n.sons[0].typ.isEmptyType: + localError(n.info, errGenerated, "'defer' takes a 'void' expression") + #localError(n.info, errGenerated, "'defer' not allowed in this context") else: localError(n.info, errInvalidExpressionX, renderTree(n, {renderNoComments})) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 84a09a7e63..b9b1759626 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -369,6 +369,15 @@ proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) = else: result.add identDefs +proc addDefer(c: PContext; result: var PNode; s: PSym) = + let deferDestructorCall = createDestructorCall(c, s) + if deferDestructorCall != nil: + if result.kind != nkStmtList: + let oldResult = result + result = newNodeI(nkStmtList, result.info) + result.add oldResult + result.add deferDestructorCall + proc isDiscardUnderscore(v: PSym): bool = if v.name.s == "_": v.flags.incl(sfGenSym) @@ -469,6 +478,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if def.kind == nkPar: v.ast = def[j] v.typ = tup.sons[j] b.sons[j] = newSymNode(v) + addDefer(c, result, v) checkNilable(v) if sfCompileTime in v.flags: hasCompileTime = true if hasCompileTime: vm.setupCompileTimeVar(c.module, result) @@ -1371,7 +1381,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = for i in countup(0, length - 1): let k = n.sons[i].kind case k - of nkFinally, nkExceptBranch, nkDefer: + of nkFinally, nkExceptBranch: # stand-alone finally and except blocks are # transformed into regular try blocks: # @@ -1424,14 +1434,6 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = n.typ = n.sons[i].typ if not isEmptyType(n.typ): n.kind = nkStmtListExpr case n.sons[i].kind - of nkVarSection, nkLetSection: - let (outer, inner) = insertDestructors(c, n.sons[i]) - if outer != nil: - n.sons[i] = outer - var rest = newNode(nkStmtList, n.info, n.sons[i+1 .. length-1]) - inner.addSon(semStmtList(c, rest, flags)) - n.sons.setLen(i+1) - return of LastBlockStmts: for j in countup(i + 1, length - 1): case n.sons[j].kind diff --git a/compiler/transf.nim b/compiler/transf.nim index dddbd51c41..2ac5e1f399 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -16,6 +16,7 @@ # * converts "continue" to "break"; disambiguates "break" # * introduces method dispatchers # * performs lambda lifting for closure support +# * transforms 'defer' into a 'try finally' statement import intsets, strutils, lists, options, ast, astalgo, trees, treetab, msgs, os, @@ -44,6 +45,7 @@ type inlining: int # > 0 if we are in inlining context (copy vars) nestedProcs: int # > 0 if we are in a nested proc contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' + deferDetected: bool PTransf = ref TTransfContext proc newTransNode(a: PNode): PTransNode {.inline.} = @@ -680,6 +682,14 @@ proc commonOptimizations*(c: PSym, n: PNode): PNode = result = n proc transform(c: PTransf, n: PNode): PTransNode = + when false: + var oldDeferAnchor: PNode + if n.kind in {nkElifBranch, nkOfBranch, nkExceptBranch, nkElifExpr, + nkElseExpr, nkElse, nkForStmt, nkWhileStmt, nkFinally, + nkBlockStmt, nkBlockExpr}: + oldDeferAnchor = c.deferAnchor + c.deferAnchor = n + case n.kind of nkSym: result = transformSym(c, n) @@ -712,13 +722,36 @@ proc transform(c: PTransf, n: PNode): PTransNode = result = transformFor(c, n) of nkParForStmt: result = transformSons(c, n) - of nkCaseStmt: result = transformCase(c, n) + of nkCaseStmt: + result = transformCase(c, n) + of nkWhileStmt: result = transformWhile(c, n) + of nkBlockStmt, nkBlockExpr: + result = transformBlock(c, n) + of nkDefer: + c.deferDetected = true + result = transformSons(c, n) + when false: + let deferPart = newNodeI(nkFinally, n.info) + deferPart.add n.sons[0] + let tryStmt = newNodeI(nkTryStmt, n.info) + if c.deferAnchor.isNil: + tryStmt.add c.root + c.root = tryStmt + result = PTransNode(tryStmt) + else: + # modify the corresponding *action*, don't rely on nkStmtList: + let L = c.deferAnchor.len-1 + tryStmt.add c.deferAnchor.sons[L] + c.deferAnchor.sons[L] = tryStmt + result = newTransNode(nkCommentStmt, n.info, 0) + tryStmt.addSon(deferPart) + # disable the original 'defer' statement: + n.kind = nkCommentStmt of nkContinueStmt: result = PTransNode(newNodeI(nkBreakStmt, n.info)) var labl = c.contSyms[c.contSyms.high] add(result, PTransNode(newSymNode(labl))) of nkBreakStmt: result = transformBreak(c, n) - of nkWhileStmt: result = transformWhile(c, n) of nkCallKinds: result = transformCall(c, n) of nkAddr, nkHiddenAddr: @@ -754,8 +787,6 @@ proc transform(c: PTransf, n: PNode): PTransNode = result = transformYield(c, n) else: result = transformSons(c, n) - of nkBlockStmt, nkBlockExpr: - result = transformBlock(c, n) of nkIdentDefs, nkConstDef: result = transformSons(c, n) # XXX comment handling really sucks: @@ -764,6 +795,8 @@ proc transform(c: PTransf, n: PNode): PTransNode = of nkClosure: return PTransNode(n) else: result = transformSons(c, n) + when false: + if oldDeferAnchor != nil: c.deferAnchor = oldDeferAnchor var cnst = getConstExpr(c.module, PNode(result)) # we inline constants if they are not complex constants: if cnst != nil and not dontInlineConstant(n, cnst): @@ -785,12 +818,52 @@ proc openTransf(module: PSym, filename: string): PTransf = result.breakSyms = @[] result.module = module +proc flattenStmts(n: PNode) = + var goOn = true + while goOn: + goOn = false + for i in 0.. Date: Wed, 5 Aug 2015 21:42:59 +0200 Subject: [PATCH 09/18] fix regressions --- compiler/transf.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/transf.nim b/compiler/transf.nim index 2ac5e1f399..5c7472a39c 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -840,7 +840,7 @@ proc liftDeferAux(n: PNode) = let deferPart = newNodeI(nkFinally, n.sons[i].info) deferPart.add n.sons[i].sons[0] var tryStmt = newNodeI(nkTryStmt, n.sons[i].info) - var body = newNodeI(nkStmtList, n.sons[i].info) + var body = newNodeI(n.kind, n.sons[i].info) if i < last: body.sons = n.sons[(i+1)..last] tryStmt.addSon(body) From 41a2a9f00bd7e823223ce4b0830d6c9d8d6bc294 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Thu, 6 Aug 2015 22:19:33 +0300 Subject: [PATCH 10/18] Fixes #3185 --- compiler/semexprs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index af6919d972..0e9b9ae5f1 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2260,7 +2260,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = semStaticStmt(c, n) of nkDefer: n.sons[0] = semExpr(c, n.sons[0]) - if not n.sons[0].typ.isEmptyType: + if not n.sons[0].typ.isEmptyType and not implicitlyDiscardable(n.sons[0]): localError(n.info, errGenerated, "'defer' takes a 'void' expression") #localError(n.info, errGenerated, "'defer' not allowed in this context") else: From 4a7c1d5fd7b0ed37fb4cd9b968f282ac48b53a4b Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 6 Aug 2015 21:45:52 +0100 Subject: [PATCH 11/18] Don't crash on `nil` in get* procs in the JSON module. --- lib/pure/json.nim | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 49915b7e99..540a1a8eb7 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -608,29 +608,29 @@ proc newJArray*(): JsonNode = proc getStr*(n: JsonNode, default: string = ""): string = ## Retrieves the string value of a `JString JsonNode`. ## - ## Returns ``default`` if ``n`` is not a ``JString``. - if n.kind != JString: return default + ## Returns ``default`` if ``n`` is not a ``JString``, or if ``n`` is nil. + if n.isNil or n.kind != JString: return default else: return n.str proc getNum*(n: JsonNode, default: BiggestInt = 0): BiggestInt = ## Retrieves the int value of a `JInt JsonNode`. ## - ## Returns ``default`` if ``n`` is not a ``JInt``. - if n.kind != JInt: return default + ## Returns ``default`` if ``n`` is not a ``JInt``, or if ``n`` is nil. + if n.isNil or n.kind != JInt: return default else: return n.num proc getFNum*(n: JsonNode, default: float = 0.0): float = ## Retrieves the float value of a `JFloat JsonNode`. ## - ## Returns ``default`` if ``n`` is not a ``JFloat``. - if n.kind != JFloat: return default + ## Returns ``default`` if ``n`` is not a ``JFloat``, or if ``n`` is nil. + if n.isNil or n.kind != JFloat: return default else: return n.fnum proc getBVal*(n: JsonNode, default: bool = false): bool = ## Retrieves the bool value of a `JBool JsonNode`. ## - ## Returns ``default`` if ``n`` is not a ``JBool``. - if n.kind != JBool: return default + ## Returns ``default`` if ``n`` is not a ``JBool``, or if ``n`` is nil. + if n.isNil or n.kind != JBool: return default else: return n.bval proc getFields*(n: JsonNode, @@ -638,15 +638,15 @@ proc getFields*(n: JsonNode, seq[tuple[key: string, val: JsonNode]] = ## Retrieves the key, value pairs of a `JObject JsonNode`. ## - ## Returns ``default`` if ``n`` is not a ``JObject``. - if n.kind != JObject: return default + ## Returns ``default`` if ``n`` is not a ``JObject``, or if ``n`` is nil. + if n.isNil or n.kind != JObject: return default else: return n.fields proc getElems*(n: JsonNode, default: seq[JsonNode] = @[]): seq[JsonNode] = ## Retrieves the int value of a `JArray JsonNode`. ## - ## Returns ``default`` if ``n`` is not a ``JArray``. - if n.kind != JArray: return default + ## Returns ``default`` if ``n`` is not a ``JArray``, or if ``n`` is nil. + if n.isNil or n.kind != JArray: return default else: return n.elems proc `%`*(s: string): JsonNode = From 842a26317cf23859d7dd9ca44818d3fbf872899e Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Fri, 7 Aug 2015 00:20:40 +0300 Subject: [PATCH 12/18] Fixed defer test. --- tests/exception/tdefer1.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/exception/tdefer1.nim b/tests/exception/tdefer1.nim index d7c6917134..cb3d09b01f 100644 --- a/tests/exception/tdefer1.nim +++ b/tests/exception/tdefer1.nim @@ -3,6 +3,7 @@ discard """ hi 1 hi +2 B A''' """ From 7bc3d7da75c0d3b504c90842016e887407e6ed40 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Fri, 7 Aug 2015 17:22:28 +0300 Subject: [PATCH 13/18] Fixes #3186 --- compiler/semstmts.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index b9b1759626..3d9363d773 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1440,7 +1440,7 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = of nkPragma, nkCommentStmt, nkNilLit, nkEmpty: discard else: localError(n.sons[j].info, errStmtInvalidAfterReturn) else: discard - if result.len == 1: + if result.len == 1 and result.sons[0].kind != nkDefer: result = result.sons[0] when defined(nimfix): if result.kind == nkCommentStmt and not result.comment.isNil and From 94e504113edfb93ac4eaf59cc78213323fa8a4c0 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 7 Aug 2015 19:40:58 +0200 Subject: [PATCH 14/18] fixes #3193 --- lib/system.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index 8cae7c5dbe..78769208c0 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -188,7 +188,7 @@ proc new*(T: typedesc): auto = ## reference to it as result value. ## ## When ``T`` is a ref type then the resulting type will be ``T``, - ## otherwise it will be ``ref T``. + ## otherwise it will be ``ref T``. when (T is ref): var r: T else: @@ -572,6 +572,7 @@ proc unsafeNew*[T](a: var ref T, size: Natural) {.magic: "New", noSideEffect.} ## purposes when you know what you're doing! proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.} +proc sizeof*(x: typedesc): int {.magic: "SizeOf", noSideEffect.} ## returns the size of ``x`` in bytes. Since this is a low-level proc, ## its usage is discouraged - using ``new`` for the most cases suffices ## that one never needs to know ``x``'s size. As a special semantic rule, From 2e4b59f3d3fc1ac99601988a482bad86a7ef7f53 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 7 Aug 2015 20:29:38 +0200 Subject: [PATCH 15/18] fixes #3192 --- compiler/lambdalifting.nim | 2 +- compiler/vm.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index d11776cf6d..c669fc7458 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -184,7 +184,7 @@ proc addHiddenParam(routine: PSym, param: PSym) = var params = routine.ast.sons[paramsPos] # -1 is correct here as param.position is 0 based but we have at position 0 # some nkEffect node: - param.position = params.len-1 + param.position = routine.typ.n.len-1 addSon(params, newSymNode(param)) incl(routine.typ.flags, tfCapturesEnv) assert sfFromGeneric in param.flags diff --git a/compiler/vm.nim b/compiler/vm.nim index d7495d77f1..e381af97fb 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -432,7 +432,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = assert regs[rb].kind == rkNode let nb = regs[rb].node case nb.kind - of nkCharLit..nkInt64Lit: + of nkCharLit..nkUInt64Lit: ensureKind(rkInt) regs[ra].intVal = nb.intVal of nkFloatLit..nkFloat64Lit: From c733b3181ea21144fe485a5af3c78543a45d0564 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 7 Aug 2015 21:28:45 +0200 Subject: [PATCH 16/18] breaking change: symbol lookups in generics follows spec more closely; fixes #2664 --- compiler/semgnrc.nim | 124 ++++++++++++++++---------------- lib/pure/collections/tables.nim | 8 +-- tests/generics/mclosed_sym.nim | 10 +++ tests/generics/tclosed_sym.nim | 11 +++ 4 files changed, 88 insertions(+), 65 deletions(-) create mode 100644 tests/generics/mclosed_sym.nim create mode 100644 tests/generics/tclosed_sym.nim diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index db910600bd..205af36a85 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -39,9 +39,9 @@ type proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode -proc semGenericStmtScope(c: PContext, n: PNode, +proc semGenericStmtScope(c: PContext, n: PNode, flags: TSemGenericFlags, - ctx: var GenericCtx): PNode = + ctx: var GenericCtx): PNode = openScope(c) result = semGenericStmt(c, n, flags, ctx) closeScope(c) @@ -73,7 +73,7 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, result = semGenericStmt(c, result, {}, ctx) else: result = symChoice(c, n, s, scOpen) - of skGenericParam: + of skGenericParam: if s.typ != nil and s.typ.kind == tyStatic: if s.typ.n != nil: result = s.typ.n @@ -85,18 +85,18 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, of skParam: result = n styleCheckUse(n.info, s) - of skType: + of skType: if (s.typ != nil) and (s.typ.flags * {tfGenericTypeParam, tfImplicitTypeParam} == {}): result = newSymNodeTypeDesc(s, n.info) - else: + else: result = n styleCheckUse(n.info, s) else: result = newSymNode(s, n.info) styleCheckUse(n.info, s) -proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, +proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode = result = n let ident = considerQuotedIdent(n) @@ -118,13 +118,13 @@ proc newDot(n, b: PNode): PNode = result.add(n.sons[0]) result.add(b) -proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, +proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx; isMacro: var bool): PNode = assert n.kind == nkDotExpr semIdeForTemplateOrGenericCheck(n, ctx.cursorInBody) let luf = if withinMixin notin flags: {checkUndeclared} else: {} - + var s = qualifiedLookUp(c, n, luf) if s != nil: result = semGenericStmtSymbol(c, n, s, ctx) @@ -141,18 +141,20 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, elif s.name.id in ctx.toMixin: result = newDot(result, symChoice(c, n, s, scForceOpen)) else: - let sym = semGenericStmtSymbol(c, n, s, ctx) - if sym.kind == nkSym: - result = newDot(result, symChoice(c, n, s, scForceOpen)) + let syms = semGenericStmtSymbol(c, n, s, ctx) + if syms.kind == nkSym: + let choice = symChoice(c, n, s, scForceOpen) + choice.kind = nkClosedSymChoice + result = newDot(result, choice) else: - result = newDot(result, sym) + result = newDot(result, syms) proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) = let s = newSymS(skUnknown, getIdentNode(n), c) addPrelimDecl(c, s) styleCheckDef(n.info, s, kind) -proc semGenericStmt(c: PContext, n: PNode, +proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags, ctx: var GenericCtx): PNode = result = n #if gCmd == cmdIdeTools: suggestStmt(c, n) @@ -181,16 +183,16 @@ proc semGenericStmt(c: PContext, n: PNode, result = semGenericStmt(c, n.sons[0], flags+{withinBind}, ctx) of nkMixinStmt: result = semMixinStmt(c, n, ctx.toMixin) - of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit: + of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit: # check if it is an expression macro: checkMinSonsLen(n, 1) let fn = n.sons[0] var s = qualifiedLookUp(c, fn, {}) if s == nil and withinMixin notin flags and - fn.kind in {nkIdent, nkAccQuoted} and + fn.kind in {nkIdent, nkAccQuoted} and considerQuotedIdent(fn).id notin ctx.toMixin: localError(n.info, errUndeclaredIdentifier, fn.renderTree) - + var first = 0 var mixinContext = false if s != nil: @@ -220,7 +222,7 @@ proc semGenericStmt(c: PContext, n: PNode, # we need to put the ``c`` in ``t(c)`` in a mixin context to prevent # the famous "undeclared identifier: it" bug: mixinContext = true - of skUnknown, skParam: + of skUnknown, skParam: # Leave it as an identifier. discard of skProc, skMethod, skIterators, skConverter: @@ -230,9 +232,9 @@ proc semGenericStmt(c: PContext, n: PNode, result.sons[0] = newSymNodeTypeDesc(s, fn.info) styleCheckUse(fn.info, s) first = 1 - of skType: + of skType: # bad hack for generics: - if (s.typ != nil) and (s.typ.kind != tyGenericParam): + if (s.typ != nil) and (s.typ.kind != tyGenericParam): result.sons[0] = newSymNodeTypeDesc(s, fn.info) styleCheckUse(fn.info, s) first = 1 @@ -244,34 +246,34 @@ proc semGenericStmt(c: PContext, n: PNode, result.sons[0] = fuzzyLookup(c, fn, flags, ctx, mixinContext) first = 1 # Consider 'when declared(globalsSlot): ThreadVarSetValue(globalsSlot, ...)' - # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which + # in threads.nim: the subtle preprocessing here binds 'globalsSlot' which # is not exported and yet the generic 'threadProcWrapper' works correctly. let flags = if mixinContext: flags+{withinMixin} else: flags for i in countup(first, sonsLen(result) - 1): result.sons[i] = semGenericStmt(c, result.sons[i], flags, ctx) - of nkIfStmt: - for i in countup(0, sonsLen(n)-1): + of nkIfStmt: + for i in countup(0, sonsLen(n)-1): n.sons[i] = semGenericStmtScope(c, n.sons[i], flags, ctx) of nkWhenStmt: for i in countup(0, sonsLen(n)-1): n.sons[i] = semGenericStmt(c, n.sons[i], flags+{withinMixin}, ctx) - of nkWhileStmt: + of nkWhileStmt: openScope(c) - for i in countup(0, sonsLen(n)-1): + for i in countup(0, sonsLen(n)-1): n.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx) closeScope(c) - of nkCaseStmt: + of nkCaseStmt: openScope(c) n.sons[0] = semGenericStmt(c, n.sons[0], flags, ctx) - for i in countup(1, sonsLen(n)-1): + for i in countup(1, sonsLen(n)-1): var a = n.sons[i] checkMinSonsLen(a, 1) var L = sonsLen(a) - for j in countup(0, L-2): + for j in countup(0, L-2): a.sons[j] = semGenericStmt(c, a.sons[j], flags, ctx) a.sons[L - 1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx) closeScope(c) - of nkForStmt, nkParForStmt: + of nkForStmt, nkParForStmt: var L = sonsLen(n) openScope(c) n.sons[L - 2] = semGenericStmt(c, n.sons[L-2], flags, ctx) @@ -279,27 +281,27 @@ proc semGenericStmt(c: PContext, n: PNode, addTempDecl(c, n.sons[i], skForVar) n.sons[L - 1] = semGenericStmt(c, n.sons[L-1], flags, ctx) closeScope(c) - of nkBlockStmt, nkBlockExpr, nkBlockType: + of nkBlockStmt, nkBlockExpr, nkBlockType: checkSonsLen(n, 2) openScope(c) - if n.sons[0].kind != nkEmpty: + if n.sons[0].kind != nkEmpty: addTempDecl(c, n.sons[0], skLabel) n.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx) closeScope(c) - of nkTryStmt: + of nkTryStmt: checkMinSonsLen(n, 2) n.sons[0] = semGenericStmtScope(c, n.sons[0], flags, ctx) - for i in countup(1, sonsLen(n)-1): + for i in countup(1, sonsLen(n)-1): var a = n.sons[i] checkMinSonsLen(a, 1) var L = sonsLen(a) - for j in countup(0, L-2): + for j in countup(0, L-2): a.sons[j] = semGenericStmt(c, a.sons[j], flags+{withinTypeDesc}, ctx) a.sons[L-1] = semGenericStmtScope(c, a.sons[L-1], flags, ctx) - of nkVarSection, nkLetSection: - for i in countup(0, sonsLen(n) - 1): + of nkVarSection, nkLetSection: + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a) checkMinSonsLen(a, 3) var L = sonsLen(a) @@ -307,49 +309,49 @@ proc semGenericStmt(c: PContext, n: PNode, a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) for j in countup(0, L-3): addTempDecl(c, getIdentNode(a.sons[j]), skVar) - of nkGenericParams: - for i in countup(0, sonsLen(n) - 1): + of nkGenericParams: + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] if (a.kind != nkIdentDefs): illFormedAst(a) checkMinSonsLen(a, 3) var L = sonsLen(a) - a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) - # do not perform symbol lookup for default expressions - for j in countup(0, L-3): + a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) + # do not perform symbol lookup for default expressions + for j in countup(0, L-3): addTempDecl(c, getIdentNode(a.sons[j]), skType) - of nkConstSection: - for i in countup(0, sonsLen(n) - 1): + of nkConstSection: + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if (a.kind != nkConstDef): illFormedAst(a) checkSonsLen(a, 3) addTempDecl(c, getIdentNode(a.sons[0]), skConst) a.sons[1] = semGenericStmt(c, a.sons[1], flags+{withinTypeDesc}, ctx) a.sons[2] = semGenericStmt(c, a.sons[2], flags, ctx) of nkTypeSection: - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if (a.kind != nkTypeDef): illFormedAst(a) checkSonsLen(a, 3) addTempDecl(c, getIdentNode(a.sons[0]), skType) - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): var a = n.sons[i] - if a.kind == nkCommentStmt: continue + if a.kind == nkCommentStmt: continue if (a.kind != nkTypeDef): illFormedAst(a) checkSonsLen(a, 3) - if a.sons[1].kind != nkEmpty: + if a.sons[1].kind != nkEmpty: openScope(c) a.sons[1] = semGenericStmt(c, a.sons[1], flags, ctx) a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx) closeScope(c) - else: + else: a.sons[2] = semGenericStmt(c, a.sons[2], flags+{withinTypeDesc}, ctx) - of nkEnumTy: + of nkEnumTy: if n.sonsLen > 0: - if n.sons[0].kind != nkEmpty: + if n.sons[0].kind != nkEmpty: n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx) - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): var a: PNode case n.sons[i].kind of nkEnumFieldDef: a = n.sons[i].sons[0] @@ -360,26 +362,26 @@ proc semGenericStmt(c: PContext, n: PNode, discard of nkFormalParams: checkMinSonsLen(n, 1) - if n.sons[0].kind != nkEmpty: + if n.sons[0].kind != nkEmpty: n.sons[0] = semGenericStmt(c, n.sons[0], flags+{withinTypeDesc}, ctx) - for i in countup(1, sonsLen(n) - 1): + for i in countup(1, sonsLen(n) - 1): var a = n.sons[i] if (a.kind != nkIdentDefs): illFormedAst(a) checkMinSonsLen(a, 3) var L = sonsLen(a) a.sons[L-2] = semGenericStmt(c, a.sons[L-2], flags+{withinTypeDesc}, ctx) a.sons[L-1] = semGenericStmt(c, a.sons[L-1], flags, ctx) - for j in countup(0, L-3): + for j in countup(0, L-3): addTempDecl(c, getIdentNode(a.sons[j]), skParam) - of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, - nkIteratorDef, nkLambdaKinds: + of nkProcDef, nkMethodDef, nkConverterDef, nkMacroDef, nkTemplateDef, + nkIteratorDef, nkLambdaKinds: checkSonsLen(n, bodyPos + 1) if n.sons[namePos].kind != nkEmpty: addTempDecl(c, getIdentNode(n.sons[0]), skProc) openScope(c) - n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos], + n.sons[genericParamsPos] = semGenericStmt(c, n.sons[genericParamsPos], flags, ctx) - if n.sons[paramsPos].kind != nkEmpty: + if n.sons[paramsPos].kind != nkEmpty: if n.sons[paramsPos].sons[0].kind != nkEmpty: addPrelimDecl(c, newSym(skUnknown, getIdent("result"), nil, n.info)) n.sons[paramsPos] = semGenericStmt(c, n.sons[paramsPos], flags, ctx) @@ -394,7 +396,7 @@ proc semGenericStmt(c: PContext, n: PNode, checkMinSonsLen(n, 2) result.sons[1] = semGenericStmt(c, n.sons[1], flags, ctx) else: - for i in countup(0, sonsLen(n) - 1): + for i in countup(0, sonsLen(n) - 1): result.sons[i] = semGenericStmt(c, n.sons[i], flags, ctx) proc semGenericStmt(c: PContext, n: PNode): PNode = diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index ec0d9623fb..be6b755ed2 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -226,6 +226,10 @@ proc `$`*[A, B](t: Table[A, B]): string = ## The `$` operator for hash tables. dollarImpl() +proc hasKey*[A, B](t: TableRef[A, B], key: A): bool = + ## returns true iff `key` is in the table `t`. + result = t[].hasKey(key) + template equalsImpl() = if s.counter == t.counter: # different insertion orders mean different 'data' seqs, so we have @@ -293,10 +297,6 @@ proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool = ## returns true iff `key` is in the table, otherwise inserts `value`. t[].hasKeyOrPut(key, val) -proc hasKey*[A, B](t: TableRef[A, B], key: A): bool = - ## returns true iff `key` is in the table `t`. - result = t[].hasKey(key) - proc contains*[A, B](t: TableRef[A, B], key: A): bool = ## alias of `hasKey` for use with the `in` operator. return hasKey[A, B](t, key) diff --git a/tests/generics/mclosed_sym.nim b/tests/generics/mclosed_sym.nim new file mode 100644 index 0000000000..bcccd9a85d --- /dev/null +++ b/tests/generics/mclosed_sym.nim @@ -0,0 +1,10 @@ + +type R* = object + +type Data*[T] = object + d*: T + +proc same(r:R, d:int) = echo "TEST2" + +proc doIt*(d:Data, r:R) = + r.same(1) # Expecting this to invoke the local `same()` method diff --git a/tests/generics/tclosed_sym.nim b/tests/generics/tclosed_sym.nim new file mode 100644 index 0000000000..ff620c2675 --- /dev/null +++ b/tests/generics/tclosed_sym.nim @@ -0,0 +1,11 @@ +discard """ + output: "TEST2" +""" + +# bug #2664 + +import mclosed_sym + +proc same(r:R, d:int) = echo "TEST1" + +doIt(Data[int](d:123), R()) From 87815cbdf76a7aa7c414d0fb82b46778fa1d8b92 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 7 Aug 2015 22:32:06 +0200 Subject: [PATCH 17/18] attempt to fix bootstrapping; refs #3139 --- compiler/condsyms.nim | 1 + lib/system.nim | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index aecbde66e4..297b865b22 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -90,3 +90,4 @@ proc initDefines*() = defineSymbol("nimnode") defineSymbol("nimnomagic64") defineSymbol("nimvarargstyped") + defineSymbol("nimtypedescfixed") diff --git a/lib/system.nim b/lib/system.nim index 78769208c0..7dae074f32 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -572,12 +572,14 @@ proc unsafeNew*[T](a: var ref T, size: Natural) {.magic: "New", noSideEffect.} ## purposes when you know what you're doing! proc sizeof*[T](x: T): int {.magic: "SizeOf", noSideEffect.} -proc sizeof*(x: typedesc): int {.magic: "SizeOf", noSideEffect.} ## returns the size of ``x`` in bytes. Since this is a low-level proc, ## its usage is discouraged - using ``new`` for the most cases suffices ## that one never needs to know ``x``'s size. As a special semantic rule, ## ``x`` may also be a type identifier (``sizeof(int)`` is valid). +when defined(nimtypedescfixed): + proc sizeof*(x: typedesc): int {.magic: "SizeOf", noSideEffect.} + proc `<`*[T](x: Ordinal[T]): T {.magic: "UnaryLt", noSideEffect.} ## unary ``<`` that can be used for nice looking excluding ranges: ## From 4f8d982d5b0944f7f2bfc905c428ed81c3bd75bd Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 8 Aug 2015 14:39:32 +0200 Subject: [PATCH 18/18] fixes #2670 --- compiler/semtempl.nim | 23 +++++++++++++++++++---- tests/template/twhen_gensym.nim | 13 +++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 tests/template/twhen_gensym.nim diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index a138981b7d..4d1eae48f2 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -184,10 +184,25 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = else: let ident = getIdentNode(c, n) if not isTemplParam(c, ident): - let local = newGenSym(k, ident, c) - addPrelimDecl(c.c, local) - styleCheckDef(n.info, local) - replaceIdentBySym(n, newSymNode(local, n.info)) + # fix #2670, consider: + # + # when b: + # var a = "hi" + # else: + # var a = 5 + # echo a + # + # We need to ensure that both 'a' produce the same gensym'ed symbol. + # So we need only check the *current* scope. + let s = localSearchInScope(c.c, considerQuotedIdent ident) + if s != nil and s.owner == c.owner and sfGenSym in s.flags: + styleCheckUse(n.info, s) + replaceIdentBySym(n, newSymNode(s, n.info)) + else: + let local = newGenSym(k, ident, c) + addPrelimDecl(c.c, local) + styleCheckDef(n.info, local) + replaceIdentBySym(n, newSymNode(local, n.info)) else: replaceIdentBySym(n, ident) diff --git a/tests/template/twhen_gensym.nim b/tests/template/twhen_gensym.nim new file mode 100644 index 0000000000..d84ee6f036 --- /dev/null +++ b/tests/template/twhen_gensym.nim @@ -0,0 +1,13 @@ +discard """ + output: "hi" +""" + +# bug #2670 +template testTemplate(b: bool): stmt = + when b: + var a = "hi" + else: + var a = 5 + echo a + +testTemplate(true)