From 45c4c8cc1411328d6d28723e22e4034fbea8b7f6 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Wed, 1 Oct 2014 22:26:51 +0100 Subject: [PATCH 1/6] Update to D 2.066.1 --- gcc/d/Make-lang.in | 33 +- gcc/d/VERSION | 2 +- gcc/d/d-asmstmt.cc | 205 - gcc/d/d-builtins.cc | 90 +- gcc/d/d-codegen.cc | 808 +- gcc/d/d-codegen.h | 113 +- gcc/d/d-convert.cc | 72 +- gcc/d/d-ctype.cc | 113 +- gcc/d/d-decls.cc | 169 +- gcc/d/d-dmd-gcc.h | 17 +- gcc/d/d-elem.cc | 871 +- gcc/d/d-glue.cc | 238 +- gcc/d/d-intrinsics.def | 151 - gcc/d/d-irstate.cc | 56 +- gcc/d/d-irstate.h | 46 +- gcc/d/d-lang.cc | 245 +- gcc/d/d-lang.h | 16 +- gcc/d/d-longdouble.cc | 34 +- gcc/d/d-objfile.cc | 310 +- gcc/d/d-objfile.h | 8 +- gcc/d/d-port.cc | 2 +- gcc/d/d-spec.cc | 2 +- gcc/d/d-target.cc | 129 +- gcc/d/d-todt.cc | 366 +- gcc/d/d-typinf.cc | 128 +- gcc/d/dfrontend/aav.c | 30 +- gcc/d/dfrontend/aav.h | 22 +- gcc/d/dfrontend/access.c | 161 +- gcc/d/dfrontend/aggregate.h | 127 +- gcc/d/dfrontend/aliasthis.c | 17 +- gcc/d/dfrontend/aliasthis.h | 17 +- gcc/d/dfrontend/apply.c | 251 +- gcc/d/dfrontend/argtypes.c | 773 +- gcc/d/dfrontend/array.h | 32 +- gcc/d/dfrontend/arrayop.c | 840 +- gcc/d/dfrontend/arraytypes.h | 22 +- gcc/d/dfrontend/artistic.txt | 117 - gcc/d/dfrontend/attrib.c | 651 +- gcc/d/dfrontend/attrib.h | 69 +- gcc/d/dfrontend/boostlicense.txt | 23 + gcc/d/dfrontend/canthrow.c | 177 +- gcc/d/dfrontend/cast.c | 4402 +- gcc/d/dfrontend/class.c | 1125 +- gcc/d/dfrontend/clone.c | 551 +- gcc/d/dfrontend/complex_t.h | 17 +- gcc/d/dfrontend/cond.c | 34 +- gcc/d/dfrontend/cond.h | 34 +- gcc/d/dfrontend/constfold.c | 93 +- gcc/d/dfrontend/cppmangle.c | 195 +- gcc/d/dfrontend/ctfe.h | 36 +- gcc/d/dfrontend/ctfeexpr.c | 320 +- gcc/d/dfrontend/declaration.c | 629 +- gcc/d/dfrontend/declaration.h | 167 +- gcc/d/dfrontend/delegatize.c | 159 +- gcc/d/dfrontend/doc.c | 1300 +- gcc/d/dfrontend/doc.h | 21 +- gcc/d/dfrontend/dsymbol.c | 324 +- gcc/d/dfrontend/dsymbol.h | 66 +- gcc/d/dfrontend/dump.c | 152 - gcc/d/dfrontend/entity.c | 17 +- gcc/d/dfrontend/enum.c | 69 +- gcc/d/dfrontend/enum.h | 31 +- gcc/d/dfrontend/expression.c | 6193 +-- gcc/d/dfrontend/expression.h | 789 +- gcc/d/dfrontend/file.c | 22 +- gcc/d/dfrontend/file.h | 14 +- gcc/d/dfrontend/filename.c | 46 +- gcc/d/dfrontend/filename.h | 14 +- gcc/d/dfrontend/func.c | 1350 +- gcc/d/dfrontend/gpl.txt | 248 - gcc/d/dfrontend/hdrgen.c | 2016 +- gcc/d/dfrontend/hdrgen.h | 24 +- gcc/d/dfrontend/identifier.c | 30 +- gcc/d/dfrontend/identifier.h | 17 +- gcc/d/dfrontend/idgen.c | 34 +- gcc/d/dfrontend/impcnvgen.c | 16 +- gcc/d/dfrontend/imphint.c | 17 +- gcc/d/dfrontend/import.c | 57 +- gcc/d/dfrontend/import.h | 30 +- gcc/d/dfrontend/init.c | 436 +- gcc/d/dfrontend/init.h | 51 +- gcc/d/dfrontend/inline.c | 2721 +- gcc/d/dfrontend/interpret.c | 10359 ++--- gcc/d/dfrontend/intrange.c | 17 +- gcc/d/dfrontend/intrange.h | 18 +- gcc/d/dfrontend/json.c | 53 +- gcc/d/dfrontend/json.h | 18 +- gcc/d/dfrontend/lexer.c | 276 +- gcc/d/dfrontend/lexer.h | 40 +- gcc/d/dfrontend/macro.c | 28 +- gcc/d/dfrontend/macro.h | 17 +- gcc/d/dfrontend/mangle.c | 588 +- gcc/d/dfrontend/mars.h | 127 +- gcc/d/dfrontend/module.c | 170 +- gcc/d/dfrontend/module.h | 38 +- gcc/d/dfrontend/mtype.c | 2178 +- gcc/d/dfrontend/mtype.h | 298 +- gcc/d/dfrontend/nogc.c | 228 + gcc/d/dfrontend/nspace.c | 236 + gcc/d/dfrontend/nspace.h | 39 + gcc/d/dfrontend/object.c | 23 +- gcc/d/dfrontend/object.h | 14 +- gcc/d/dfrontend/opover.c | 2026 +- gcc/d/dfrontend/optimize.c | 1922 +- gcc/d/dfrontend/outbuffer.c | 48 +- gcc/d/dfrontend/outbuffer.h | 19 +- gcc/d/dfrontend/parse.c | 1666 +- gcc/d/dfrontend/parse.h | 53 +- gcc/d/dfrontend/port.h | 11 +- gcc/d/dfrontend/readme.txt | 9 +- gcc/d/dfrontend/rmem.c | 32 +- gcc/d/dfrontend/rmem.h | 8 +- gcc/d/dfrontend/root.h | 14 +- gcc/d/dfrontend/sapply.c | 305 +- gcc/d/dfrontend/scope.c | 211 +- gcc/d/dfrontend/scope.h | 36 +- gcc/d/dfrontend/sideeffect.c | 387 +- gcc/d/dfrontend/speller.c | 17 +- gcc/d/dfrontend/speller.h | 14 +- gcc/d/dfrontend/statement.c | 2383 +- gcc/d/dfrontend/statement.h | 346 +- gcc/d/dfrontend/staticassert.c | 33 +- gcc/d/dfrontend/staticassert.h | 22 +- gcc/d/dfrontend/stringtable.c | 26 +- gcc/d/dfrontend/stringtable.h | 15 +- gcc/d/dfrontend/struct.c | 587 +- gcc/d/dfrontend/target.h | 20 +- gcc/d/dfrontend/template.c | 3603 +- gcc/d/dfrontend/template.h | 149 +- gcc/d/dfrontend/traits.c | 600 +- gcc/d/dfrontend/unittests.c | 16 +- gcc/d/dfrontend/utf.c | 36 +- gcc/d/dfrontend/utf.h | 20 +- gcc/d/dfrontend/version.c | 30 +- gcc/d/dfrontend/version.h | 21 +- gcc/d/dfrontend/visitor.h | 277 +- gcc/d/intrinsics.def | 152 + gcc/d/lang.opt | 4 + gcc/d/longdouble.h | 34 +- gcc/d/runtime.def | 232 + gcc/d/toir.cc | 67 +- gcc/testsuite/gdc.test/compilable/aliasdecl.d | 5 + gcc/testsuite/gdc.test/compilable/bug11735.d | 14 + gcc/testsuite/gdc.test/compilable/bug6963.d | 6 +- gcc/testsuite/gdc.test/compilable/compile1.d | 203 + gcc/testsuite/gdc.test/compilable/cppmangle.d | 2 +- gcc/testsuite/gdc.test/compilable/ddoc1.d | 2 +- gcc/testsuite/gdc.test/compilable/ddoc10366.d | 20 + gcc/testsuite/gdc.test/compilable/ddoc11511.d | 20 + gcc/testsuite/gdc.test/compilable/ddoc11823.d | 7 + gcc/testsuite/gdc.test/compilable/ddoc12706.d | 9 + gcc/testsuite/gdc.test/compilable/ddoc198.d | 35 + gcc/testsuite/gdc.test/compilable/ddoc648.d | 90 + gcc/testsuite/gdc.test/compilable/ddoc7555.d | 53 + .../gdc.test/compilable/ddocunittest.d | 130 + .../gdc.test/compilable/deprecate12979a.d | 27 + .../gdc.test/compilable/deprecate2.d | 2 +- gcc/testsuite/gdc.test/compilable/diag10768.d | 2 +- gcc/testsuite/gdc.test/compilable/diag11066.d | 13 + .../compilable/extra-files/ddoc10366.html | 28 + .../compilable/extra-files/ddoc11511.html | 34 + .../compilable/extra-files/ddoc11823.html | 15 + .../compilable/extra-files/ddoc12706.html | 14 + .../compilable/extra-files/ddoc198.ddoc | 2 + .../compilable/extra-files/ddoc198.html | 38 + .../compilable/extra-files/ddoc648.html | 123 + .../compilable/extra-files/ddoc7555.html | 56 + .../{inlineheader.d => extra-files/header1.d} | 23 +- .../extra-files/{header.di => header1.di} | 22 +- .../compilable/extra-files/header12567a.di | 2 + .../compilable/extra-files/header12567b.di | 2 + .../{inlineheader.di => header1i.di} | 22 +- .../{xheader.d => extra-files/header2.d} | 4 - .../extra-files/{xheader.di => header2.di} | 1 - .../{inlinexheader.di => header2i.di} | 1 - .../gdc.test/compilable/extra-files/json.out | 143 +- .../fail260.d | 13 +- .../gdc.test/compilable/fwdref12201.d | 38 +- .../gdc.test/compilable/fwdref12543.d | 7 + gcc/testsuite/gdc.test/compilable/header.d | 413 - gcc/testsuite/gdc.test/compilable/ice11777.d | 14 + gcc/testsuite/gdc.test/compilable/ice11906.d | 10 + gcc/testsuite/gdc.test/compilable/ice12002.d | 24 + gcc/testsuite/gdc.test/compilable/ice12956.d | 30 + gcc/testsuite/gdc.test/compilable/ice13071.d | 13 + gcc/testsuite/gdc.test/compilable/ice13088.d | 19 + gcc/testsuite/gdc.test/compilable/ice13245.d | 5 + gcc/testsuite/gdc.test/compilable/ice13323.d | 10 + gcc/testsuite/gdc.test/compilable/ice8392.d | 12 + .../gdc.test/compilable/imports/a12506.d | 3 + .../gdc.test/compilable/imports/a12567.d | 4 + .../gdc.test/compilable/imports/a8392.d | 15 + .../gdc.test/compilable/imports/imp12242a.d | 17 + .../gdc.test/compilable/imports/imp12242a1.d | 8 + .../gdc.test/compilable/imports/imp12242a2.d | 11 + .../gdc.test/compilable/imports/imp12242b.d | 17 + .../gdc.test/compilable/imports/imp12242b1.d | 8 + .../gdc.test/compilable/imports/imp12242b2.d | 11 + .../gdc.test/compilable/imports/test55a.d | 4 +- .../gdc.test/compilable/inlinexheader.d | 91 - .../gdc.test/compilable/interpret3.d | 450 +- gcc/testsuite/gdc.test/compilable/json.d | 12 + gcc/testsuite/gdc.test/compilable/nogc.d | 96 + .../gdc.test/compilable/parse10199.d | 17 + .../gdc.test/compilable/parse11957.d | 13 + .../gdc.test/compilable/parse13049.d | 3 + gcc/testsuite/gdc.test/compilable/test10150.d | 36 - gcc/testsuite/gdc.test/compilable/test10520.d | 11 + gcc/testsuite/gdc.test/compilable/test11471.d | 10 + gcc/testsuite/gdc.test/compilable/test12009.d | 36 + gcc/testsuite/gdc.test/compilable/test12523.d | 15 + .../gdc.test/compilable/test12567a.d | 11 + .../gdc.test/compilable/test12567b.d | 11 + .../gdc.test/compilable/test12567c.d | 11 + .../gdc.test/compilable/test12567d.d | 10 + gcc/testsuite/gdc.test/compilable/test12593.d | 12 + gcc/testsuite/gdc.test/compilable/test12967.d | 64 + .../gdc.test/compilable/test12979a.d | 5 + .../gdc.test/compilable/test12979b.d | 64 + gcc/testsuite/gdc.test/compilable/test13008.d | 10 + gcc/testsuite/gdc.test/compilable/test13053.d | 13 + gcc/testsuite/gdc.test/compilable/test13193.d | 126 + gcc/testsuite/gdc.test/compilable/test13194.d | 17 + gcc/testsuite/gdc.test/compilable/test55.d | 4 +- gcc/testsuite/gdc.test/compilable/test602.d | 25 +- gcc/testsuite/gdc.test/compilable/test8296.d | 33 + gcc/testsuite/gdc.test/compilable/test9570.d | 59 + .../gdc.test/compilable/testInference.d | 176 +- gcc/testsuite/gdc.test/compilable/testVRP.d | 23 + .../gdc.test/compilable/testheader1.d | 6 + .../gdc.test/compilable/testheader12567a.d | 7 + .../gdc.test/compilable/testheader12567b.d | 7 + .../gdc.test/compilable/testheader1i.d | 6 + .../gdc.test/compilable/testheader2.d | 6 + .../gdc.test/compilable/testheader2i.d | 6 + .../gdc.test/compilable/testimport12242.d | 26 + gcc/testsuite/gdc.test/compilable/vgc1.d | 85 + gcc/testsuite/gdc.test/compilable/vgc2.d | 104 + gcc/testsuite/gdc.test/compilable/vgc3.d | 68 + gcc/testsuite/gdc.test/compilable/warn3882.d | 66 + .../fail_compilation/deprecate12979a.d | 18 + .../fail_compilation/deprecate12979b.d | 17 + .../fail_compilation/deprecate12979c.d | 17 + .../fail_compilation/deprecate12979d.d | 16 + .../gdc.test/fail_compilation/diag10169.d | 12 + .../gdc.test/fail_compilation/diag10221.d | 11 + .../gdc.test/fail_compilation/diag10221a.d | 11 + .../gdc.test/fail_compilation/diag10415.d | 6 +- .../gdc.test/fail_compilation/diag11078.d | 4 +- .../gdc.test/fail_compilation/diag11769.d | 4 +- .../gdc.test/fail_compilation/diag11819a.d | 51 + .../gdc.test/fail_compilation/diag11819b.d | 47 + .../gdc.test/fail_compilation/diag12063.d | 15 + .../gdc.test/fail_compilation/diag12280.d | 18 + .../gdc.test/fail_compilation/diag12312.d | 11 + .../gdc.test/fail_compilation/diag12380.d | 13 + .../gdc.test/fail_compilation/diag12432.d | 61 + .../gdc.test/fail_compilation/diag12480.d | 12 + .../gdc.test/fail_compilation/diag12598.d | 21 + .../gdc.test/fail_compilation/diag12640.d | 30 + .../gdc.test/fail_compilation/diag12678.d | 27 + .../gdc.test/fail_compilation/diag12777.d | 23 + .../gdc.test/fail_compilation/diag13082.d | 24 + .../gdc.test/fail_compilation/diag13142.d | 27 + .../gdc.test/fail_compilation/diag13333.d | 40 + .../gdc.test/fail_compilation/diag1566.d | 30 + .../gdc.test/fail_compilation/diag3672.d | 57 + .../gdc.test/fail_compilation/diag3672a.d | 34 + .../gdc.test/fail_compilation/diag4479.d | 2 +- .../gdc.test/fail_compilation/diag4596.d | 21 + .../gdc.test/fail_compilation/diag6373.d | 2 +- .../gdc.test/fail_compilation/diag6677.d | 29 + .../gdc.test/fail_compilation/diag7747.d | 13 + .../gdc.test/fail_compilation/diag8101.d | 63 + .../gdc.test/fail_compilation/diag8101b.d | 34 + .../gdc.test/fail_compilation/diag8178.d | 9 +- .../gdc.test/fail_compilation/diag8648.d | 12 +- .../gdc.test/fail_compilation/diag8777.d | 44 + .../gdc.test/fail_compilation/diag8777a.d | 12 - .../gdc.test/fail_compilation/diag8777b.d | 12 - .../gdc.test/fail_compilation/diag8777c.d | 13 - .../gdc.test/fail_compilation/diag8777d.d | 13 - .../gdc.test/fail_compilation/diag8777e.d | 14 - .../gdc.test/fail_compilation/diag8777f.d | 14 - .../gdc.test/fail_compilation/diag9191.d | 2 +- .../gdc.test/fail_compilation/diag9247.d | 4 +- .../gdc.test/fail_compilation/diag9635.d | 2 +- .../gdc.test/fail_compilation/diag_err1.d | 25 + .../fail_compilation/extra-files/bar11453.d | 1 + .../fail_compilation/extra-files/foo11453.d | 1 + .../gdc.test/fail_compilation/fail10115.d | 6 +- .../gdc.test/fail_compilation/fail102.d | 25 - .../gdc.test/fail_compilation/fail10277.d | 1 - .../gdc.test/fail_compilation/fail10285.d | 10 + .../gdc.test/fail_compilation/fail10346.d | 4 +- .../gdc.test/fail_compilation/fail10481.d | 3 +- .../gdc.test/fail_compilation/fail10630.d | 2 +- .../gdc.test/fail_compilation/fail108.d | 3 +- .../gdc.test/fail_compilation/fail112.d | 13 - .../gdc.test/fail_compilation/fail113.d | 2 +- .../gdc.test/fail_compilation/fail114.d | 2 +- .../gdc.test/fail_compilation/fail11426.d | 2 +- .../gdc.test/fail_compilation/fail11453a.d | 10 + .../gdc.test/fail_compilation/fail11453b.d | 10 + .../gdc.test/fail_compilation/fail11503a.d | 21 + .../gdc.test/fail_compilation/fail11503b.d | 13 + .../gdc.test/fail_compilation/fail11503c.d | 15 + .../gdc.test/fail_compilation/fail11503d.d | 22 + .../gdc.test/fail_compilation/fail11542.d | 73 + .../gdc.test/fail_compilation/fail11545.d | 20 + .../gdc.test/fail_compilation/fail11591.d | 14 - .../gdc.test/fail_compilation/fail11591b.d | 34 + .../gdc.test/fail_compilation/fail120.d | 4 +- .../gdc.test/fail_compilation/fail12047.d | 2 +- .../gdc.test/fail_compilation/fail12236.d | 33 + .../gdc.test/fail_compilation/fail12255.d | 139 + .../gdc.test/fail_compilation/fail12378.d | 143 + .../gdc.test/fail_compilation/fail12390.d | 15 + .../gdc.test/fail_compilation/fail12436.d | 64 + .../gdc.test/fail_compilation/fail12485.d | 11 + .../gdc.test/fail_compilation/fail125.d | 9 +- .../gdc.test/fail_compilation/fail12567.d | 8 + .../gdc.test/fail_compilation/fail126.d | 2 +- .../gdc.test/fail_compilation/fail12604.d | 79 + .../gdc.test/fail_compilation/fail12622.d | 30 + .../gdc.test/fail_compilation/fail12749.d | 62 + .../gdc.test/fail_compilation/fail128.d | 8 - .../gdc.test/fail_compilation/fail12809.d | 80 + .../gdc.test/fail_compilation/fail12901.d | 14 + .../gdc.test/fail_compilation/fail12932.d | 19 + .../gdc.test/fail_compilation/fail130.d | 15 - .../gdc.test/fail_compilation/fail13187.d | 13 + .../gdc.test/fail_compilation/fail13424.d | 11 + .../gdc.test/fail_compilation/fail138.d | 16 - .../gdc.test/fail_compilation/fail139.d | 2 +- .../gdc.test/fail_compilation/fail156.d | 4 +- .../gdc.test/fail_compilation/fail163.d | 84 +- .../gdc.test/fail_compilation/fail164.d | 13 - .../gdc.test/fail_compilation/fail165.d | 14 - .../gdc.test/fail_compilation/fail166.d | 13 - .../gdc.test/fail_compilation/fail167.d | 13 - .../gdc.test/fail_compilation/fail168.d | 14 - .../gdc.test/fail_compilation/fail171.d | 12 - .../gdc.test/fail_compilation/fail172.d | 19 +- .../gdc.test/fail_compilation/fail174.d | 21 - .../gdc.test/fail_compilation/fail176.d | 10 +- .../gdc.test/fail_compilation/fail177.d | 4 +- .../gdc.test/fail_compilation/fail180.d | 8 +- .../gdc.test/fail_compilation/fail187.d | 4 +- .../gdc.test/fail_compilation/fail1900.d | 4 +- .../gdc.test/fail_compilation/fail218.d | 2 +- .../gdc.test/fail_compilation/fail236.d | 4 +- .../gdc.test/fail_compilation/fail238_m32.d | 11 +- .../gdc.test/fail_compilation/fail238_m64.d | 11 +- .../gdc.test/fail_compilation/fail243t.d | 2 +- .../gdc.test/fail_compilation/fail269.d | 4 +- .../gdc.test/fail_compilation/fail278.d | 7 +- .../gdc.test/fail_compilation/fail287.d | 2 +- .../gdc.test/fail_compilation/fail303.d | 2 +- .../gdc.test/fail_compilation/fail315.d | 10 +- .../gdc.test/fail_compilation/fail327.d | 2 +- .../gdc.test/fail_compilation/fail329.d | 2 +- .../gdc.test/fail_compilation/fail333.d | 2 +- .../gdc.test/fail_compilation/fail352.d | 2 +- .../gdc.test/fail_compilation/fail3672.d | 38 + .../gdc.test/fail_compilation/fail3882.d | 48 + .../gdc.test/fail_compilation/fail4.d | 12 - .../gdc.test/fail_compilation/fail4082.d | 34 + .../gdc.test/fail_compilation/fail4082a.d | 8 - .../gdc.test/fail_compilation/fail4082b.d | 4 - .../gdc.test/fail_compilation/fail52.d | 4 +- .../gdc.test/fail_compilation/fail6572.d | 4 +- .../gdc.test/fail_compilation/fail66.d | 80 +- .../gdc.test/fail_compilation/fail6652.d | 37 + .../gdc.test/fail_compilation/fail6652a.d | 25 - .../gdc.test/fail_compilation/fail6652b.d | 25 - .../gdc.test/fail_compilation/fail67.d | 26 - .../gdc.test/fail_compilation/fail68.d | 21 - .../gdc.test/fail_compilation/fail6889.d | 130 + .../gdc.test/fail_compilation/fail69.d | 21 - .../gdc.test/fail_compilation/fail70.d | 21 - .../gdc.test/fail_compilation/fail71.d | 19 - .../gdc.test/fail_compilation/fail8373.d | 23 + .../gdc.test/fail_compilation/fail8664.d | 3 +- .../gdc.test/fail_compilation/fail9368.d | 2 +- .../gdc.test/fail_compilation/fail9562.d | 4 +- .../gdc.test/fail_compilation/fail9665a.d | 50 +- .../gdc.test/fail_compilation/fail9665b.d | 40 +- .../gdc.test/fail_compilation/ice10076.d | 9 +- .../gdc.test/fail_compilation/ice10212.d | 4 +- .../gdc.test/fail_compilation/ice10259.d | 21 +- .../gdc.test/fail_compilation/ice10610.d | 15 - .../gdc.test/fail_compilation/ice10616.d | 3 +- .../gdc.test/fail_compilation/ice10624.d | 4 +- .../gdc.test/fail_compilation/ice10922.d | 3 +- .../gdc.test/fail_compilation/ice11518.d | 4 +- .../gdc.test/fail_compilation/ice11553.d | 3 +- .../gdc.test/fail_compilation/ice11822.d | 11 +- .../gdc.test/fail_compilation/ice11850.d | 10 +- .../gdc.test/fail_compilation/ice11919.d | 9 +- .../gdc.test/fail_compilation/ice11926.d | 4 +- .../gdc.test/fail_compilation/ice12174.d | 51 + .../gdc.test/fail_compilation/ice12179.d | 83 + .../gdc.test/fail_compilation/ice12235.d | 17 + .../gdc.test/fail_compilation/ice12350.d | 31 + .../gdc.test/fail_compilation/ice12362.d | 13 + .../gdc.test/fail_compilation/ice12397.d | 16 + .../gdc.test/fail_compilation/ice12497.d | 18 + .../gdc.test/fail_compilation/ice12501.d | 44 + .../gdc.test/fail_compilation/ice12534.d | 15 + .../gdc.test/fail_compilation/ice12539.d | 16 + .../gdc.test/fail_compilation/ice12554.d | 55 + .../gdc.test/fail_compilation/ice12574.d | 54 + .../gdc.test/fail_compilation/ice12581.d | 22 + .../gdc.test/fail_compilation/ice12673.d | 4 + .../gdc.test/fail_compilation/ice12727.d | 28 + .../gdc.test/fail_compilation/ice12836.d | 9 + .../gdc.test/fail_compilation/ice12838.d | 28 + .../gdc.test/fail_compilation/ice12841.d | 25 + .../gdc.test/fail_compilation/ice12850.d | 13 + .../gdc.test/fail_compilation/ice12902.d | 21 + .../gdc.test/fail_compilation/ice12907.d | 11 + .../gdc.test/fail_compilation/ice13024.d | 16 + .../gdc.test/fail_compilation/ice13027.d | 10 + .../gdc.test/fail_compilation/ice13081.d | 29 + .../gdc.test/fail_compilation/ice13131.d | 20 + .../gdc.test/fail_compilation/ice13220.d | 23 + .../gdc.test/fail_compilation/ice13221.d | 23 + .../gdc.test/fail_compilation/ice13225.d | 14 + .../gdc.test/fail_compilation/ice13259.d | 8 + .../gdc.test/fail_compilation/ice13311.d | 12 + .../gdc.test/fail_compilation/ice6538.d | 4 +- .../gdc.test/fail_compilation/ice7645.d | 2 +- .../gdc.test/fail_compilation/ice8100.d | 12 + .../gdc.test/fail_compilation/ice8309.d | 11 + .../gdc.test/fail_compilation/ice8499.d | 19 + .../gdc.test/fail_compilation/ice9806.d | 15 +- .../fail_compilation/imports/a10169.d | 6 + .../imports/a13131checkpoint.d | 17 + .../fail_compilation/imports/a13131elec.d | 10 + .../imports/a13131parameters.d | 13 + .../fail_compilation/imports/a13311.d | 9 + .../fail_compilation/imports/diag12598a.d | 3 + .../fail_compilation/imports/test13152a.d | 26 + .../fail_compilation/imports/test13152b.d | 26 + .../fail_compilation/imports/test13152c.d | 26 + .../fail_compilation/imports/test13152d.d | 26 + .../fail_compilation/imports/test13152e.d | 26 + .../fail_compilation/imports/test13152f.d | 26 + .../fail_compilation/imports/test13152g.d | 26 + .../fail_compilation/imports/test13152h.d | 26 + .../fail_compilation/imports/test13152i.d | 26 + .../fail_compilation/imports/test13152j.d | 26 + .../fail_compilation/imports/test13152k.d | 26 + .../fail_compilation/imports/test13152l.d | 26 + .../fail_compilation/imports/test13152m.d | 26 + .../fail_compilation/imports/test13152n.d | 26 + .../fail_compilation/imports/test13152o.d | 26 + .../fail_compilation/imports/test13152p.d | 26 + .../fail_compilation/imports/test13152q.d | 26 + .../fail_compilation/imports/test13152r.d | 26 + .../fail_compilation/imports/test13152s.d | 26 + .../fail_compilation/imports/test13152t.d | 26 + .../fail_compilation/imports/test13152u.d | 26 + .../fail_compilation/imports/test13152v.d | 26 + .../fail_compilation/imports/test13152w.d | 26 + .../fail_compilation/imports/test13152x.d | 26 + .../fail_compilation/imports/test13152y.d | 26 + .../fail_compilation/imports/test13152z.d | 26 + .../gdc.test/fail_compilation/nogc1.d | 85 + .../gdc.test/fail_compilation/nogc2.d | 104 + .../gdc.test/fail_compilation/nogc3.d | 68 + .../gdc.test/fail_compilation/parse12924.d | 20 + .../gdc.test/fail_compilation/parse12967a.d | 43 + .../gdc.test/fail_compilation/parse12967b.d | 41 + .../gdc.test/fail_compilation/parseStc2.d | 67 + .../gdc.test/fail_compilation/parseStc3.d | 62 + .../gdc.test/fail_compilation/spell9644.d | 2 +- .../gdc.test/fail_compilation/test12979.d | 16 + .../gdc.test/fail_compilation/test13152.d | 12 + .../gdc.test/fail_compilation/test64.d | 8 +- .../gdc.test/fail_compilation/test8556.d | 9 +- .../gdc.test/fail_compilation/test8793.d | 2 +- .../gdc.test/fail_compilation/test9176.d | 2 +- .../gdc.test/fail_compilation/testCols.d | 14 + .../gdc.test/fail_compilation/warn12809.d | 34 + .../gdc.test/fail_compilation/warn1553.d | 20 + gcc/testsuite/gdc.test/runnable/aliasthis.d | 64 +- gcc/testsuite/gdc.test/runnable/arrayop.d | 45 + gcc/testsuite/gdc.test/runnable/assignable.d | 264 +- gcc/testsuite/gdc.test/runnable/bitops.d | 1 + gcc/testsuite/gdc.test/runnable/bug12928.d | 13 + gcc/testsuite/gdc.test/runnable/bug7068.d | 11 + gcc/testsuite/gdc.test/runnable/circular.d | 4 +- gcc/testsuite/gdc.test/runnable/constfold.d | 21 + .../gdc.test/runnable/ctorpowtests.d | 8 +- gcc/testsuite/gdc.test/runnable/declaration.d | 18 + gcc/testsuite/gdc.test/runnable/delegate.d | 43 +- gcc/testsuite/gdc.test/runnable/deprecate1.d | 1320 - gcc/testsuite/gdc.test/runnable/eh.d | 51 + gcc/testsuite/gdc.test/runnable/foreach.d | 17 + gcc/testsuite/gdc.test/runnable/foreach5.d | 230 +- gcc/testsuite/gdc.test/runnable/funclit.d | 140 +- gcc/testsuite/gdc.test/runnable/iasm64.d | 51 + gcc/testsuite/gdc.test/runnable/implicit.d | 242 + .../gdc.test/runnable/imports/a12874.d | 13 + .../gdc.test/runnable/imports/argufile.d | 2 +- .../gdc.test/runnable/imports/link13043a.d | 17 + .../gdc.test/runnable/imports/link13394a.d | 24 + .../gdc.test/runnable/imports/link13400a.d | 16 + .../gdc.test/runnable/imports/link846a.d | 38 + .../gdc.test/runnable/imports/mangle10077.d | 9 + .../runnable/imports/template13478a.d | 8 + .../runnable/imports/template13478b.d | 7 + .../gdc.test/runnable/imports/template_ovs1.d | 5 + .../gdc.test/runnable/imports/template_ovs2.d | 5 + gcc/testsuite/gdc.test/runnable/inline.d | 53 + gcc/testsuite/gdc.test/runnable/interpret.d | 3 +- gcc/testsuite/gdc.test/runnable/lazy.d | 21 + gcc/testsuite/gdc.test/runnable/lexer.d | 4 + gcc/testsuite/gdc.test/runnable/link13043.d | 5 + gcc/testsuite/gdc.test/runnable/link13394.d | 13 + gcc/testsuite/gdc.test/runnable/link13400.d | 13 + gcc/testsuite/gdc.test/runnable/link846.d | 6 + gcc/testsuite/gdc.test/runnable/mangle.d | 541 +- gcc/testsuite/gdc.test/runnable/mars1.d | 135 + gcc/testsuite/gdc.test/runnable/mixin1.d | 4 +- gcc/testsuite/gdc.test/runnable/nested.d | 317 +- gcc/testsuite/gdc.test/runnable/nogc.d | 56 + gcc/testsuite/gdc.test/runnable/nulltype.d | 16 + gcc/testsuite/gdc.test/runnable/opover.d | 48 + gcc/testsuite/gdc.test/runnable/opover2.d | 317 +- gcc/testsuite/gdc.test/runnable/overload.d | 98 +- gcc/testsuite/gdc.test/runnable/sdtor.d | 220 +- gcc/testsuite/gdc.test/runnable/statictor.d | 24 + gcc/testsuite/gdc.test/runnable/structlit.d | 130 +- .../gdc.test/runnable/template13478.d | 10 + gcc/testsuite/gdc.test/runnable/template4.d | 218 +- gcc/testsuite/gdc.test/runnable/template9.d | 1226 +- gcc/testsuite/gdc.test/runnable/test12.d | 3 +- gcc/testsuite/gdc.test/runnable/test12874.d | 18 + gcc/testsuite/gdc.test/runnable/test22.d | 2 +- gcc/testsuite/gdc.test/runnable/test23.d | 17 + gcc/testsuite/gdc.test/runnable/test28.d | 22 +- gcc/testsuite/gdc.test/runnable/test4.d | 10 + gcc/testsuite/gdc.test/runnable/test42.d | 137 +- gcc/testsuite/gdc.test/runnable/test5854.d | 45 + gcc/testsuite/gdc.test/runnable/test7511.d | 15 +- gcc/testsuite/gdc.test/runnable/test9.d | 360 - gcc/testsuite/gdc.test/runnable/testaa3.d | 323 + gcc/testsuite/gdc.test/runnable/testappend.d | 29 +- gcc/testsuite/gdc.test/runnable/testbounds2.d | 13 + .../gdc.test/runnable/testbounds_off.d | 27 + .../gdc.test/runnable/testbounds_on.d | 27 + .../gdc.test/runnable/testbounds_safeonly.d | 27 + gcc/testsuite/gdc.test/runnable/testclass.d | 65 + gcc/testsuite/gdc.test/runnable/testconst.d | 169 +- gcc/testsuite/gdc.test/runnable/testdt.d | 50 +- .../gdc.test/runnable/testinvariant.d | 34 + .../gdc.test/runnable/testrightthis.d | 19 + gcc/testsuite/gdc.test/runnable/testsafe.d | 9 +- gcc/testsuite/gdc.test/runnable/testtypeid.d | 58 + gcc/testsuite/gdc.test/runnable/testxmm.d | 34 + gcc/testsuite/gdc.test/runnable/traits.d | 188 +- gcc/testsuite/gdc.test/runnable/uda.d | 65 + gcc/testsuite/gdc.test/runnable/ufcs.d | 16 + gcc/testsuite/gdc.test/runnable/uniformctor.d | 157 + gcc/testsuite/gdc.test/runnable/warning1.d | 31 + gcc/testsuite/gdc.test/runnable/xtest46.d | 297 +- libphobos/libdruntime/Makefile.am | 52 +- libphobos/libdruntime/Makefile.in | 51 +- libphobos/libdruntime/core/atomic.d | 68 +- libphobos/libdruntime/core/bitop.d | 1 + libphobos/libdruntime/core/checkedint.d | 500 + libphobos/libdruntime/core/cpuid.d | 3 +- libphobos/libdruntime/core/demangle.d | 27 +- libphobos/libdruntime/core/exception.d | 24 +- libphobos/libdruntime/core/internal/traits.d | 73 + libphobos/libdruntime/core/math.d | 1 + libphobos/libdruntime/core/memory.d | 81 +- libphobos/libdruntime/core/runtime.d | 20 +- libphobos/libdruntime/core/simd.d | 68 +- libphobos/libdruntime/core/stdc/complex.d | 11 +- libphobos/libdruntime/core/stdc/config.d | 13 +- libphobos/libdruntime/core/stdc/ctype.d | 11 +- libphobos/libdruntime/core/stdc/errno.d | 151 +- libphobos/libdruntime/core/stdc/errno_.c | 12 +- libphobos/libdruntime/core/stdc/fenv.d | 65 +- libphobos/libdruntime/core/stdc/float_.d | 11 +- libphobos/libdruntime/core/stdc/inttypes.d | 11 +- libphobos/libdruntime/core/stdc/limits.d | 11 +- libphobos/libdruntime/core/stdc/locale.d | 30 +- libphobos/libdruntime/core/stdc/math.d | 97 +- libphobos/libdruntime/core/stdc/signal.d | 11 +- libphobos/libdruntime/core/stdc/stdarg.d | 35 +- libphobos/libdruntime/core/stdc/stddef.d | 11 +- libphobos/libdruntime/core/stdc/stdint.d | 17 +- libphobos/libdruntime/core/stdc/stdio.d | 184 +- libphobos/libdruntime/core/stdc/stdlib.d | 22 +- libphobos/libdruntime/core/stdc/string.d | 11 +- libphobos/libdruntime/core/stdc/tgmath.d | 11 +- libphobos/libdruntime/core/stdc/time.d | 24 +- libphobos/libdruntime/core/stdc/wchar_.d | 12 +- libphobos/libdruntime/core/stdc/wctype.d | 11 +- libphobos/libdruntime/core/sync/config.d | 15 +- libphobos/libdruntime/core/sync/mutex.d | 25 +- libphobos/libdruntime/core/sync/semaphore.d | 7 +- .../libdruntime/core/sys/freebsd/dlfcn.d | 3 +- .../libdruntime/core/sys/freebsd/execinfo.d | 3 +- libphobos/libdruntime/core/sys/linux/dlfcn.d | 24 + libphobos/libdruntime/core/sys/linux/epoll.d | 7 +- .../libdruntime/core/sys/linux/execinfo.d | 1 + libphobos/libdruntime/core/sys/linux/link.d | 6 + .../libdruntime/core/sys/linux/sys/mman.d | 4 +- libphobos/libdruntime/core/sys/osx/execinfo.d | 1 + .../core/sys/osx/mach/thread_act.d | 1 + libphobos/libdruntime/core/sys/osx/pthread.d | 1 + .../libdruntime/core/sys/posix/arpa/inet.d | 47 + libphobos/libdruntime/core/sys/posix/dirent.d | 46 + libphobos/libdruntime/core/sys/posix/dlfcn.d | 33 + libphobos/libdruntime/core/sys/posix/fcntl.d | 125 +- libphobos/libdruntime/core/sys/posix/grp.d | 17 + .../libdruntime/core/sys/posix/net/if_.d | 7 + libphobos/libdruntime/core/sys/posix/netdb.d | 83 + .../libdruntime/core/sys/posix/netinet/in_.d | 174 +- .../libdruntime/core/sys/posix/netinet/tcp.d | 4 + libphobos/libdruntime/core/sys/posix/poll.d | 29 + .../libdruntime/core/sys/posix/pthread.d | 107 + libphobos/libdruntime/core/sys/posix/pwd.d | 29 +- libphobos/libdruntime/core/sys/posix/sched.d | 24 + .../libdruntime/core/sys/posix/semaphore.d | 15 + libphobos/libdruntime/core/sys/posix/setjmp.d | 46 + libphobos/libdruntime/core/sys/posix/signal.d | 445 +- libphobos/libdruntime/core/sys/posix/stdio.d | 41 +- libphobos/libdruntime/core/sys/posix/stdlib.d | 29 + .../libdruntime/core/sys/posix/sys/ioctl.d | 4 + .../libdruntime/core/sys/posix/sys/ipc.d | 32 + .../libdruntime/core/sys/posix/sys/mman.d | 58 + .../libdruntime/core/sys/posix/sys/resource.d | 56 +- .../libdruntime/core/sys/posix/sys/select.d | 49 + .../libdruntime/core/sys/posix/sys/socket.d | 209 + .../libdruntime/core/sys/posix/sys/stat.d | 232 +- .../libdruntime/core/sys/posix/sys/time.d | 29 + .../libdruntime/core/sys/posix/sys/types.d | 99 +- .../libdruntime/core/sys/posix/sys/uio.d | 18 + libphobos/libdruntime/core/sys/posix/sys/un.d | 10 + .../libdruntime/core/sys/posix/sys/utsname.d | 17 + .../libdruntime/core/sys/posix/sys/wait.d | 30 +- .../libdruntime/core/sys/posix/termios.d | 3 + libphobos/libdruntime/core/sys/posix/time.d | 59 + .../libdruntime/core/sys/posix/ucontext.d | 56 +- libphobos/libdruntime/core/sys/posix/unistd.d | 717 +- libphobos/libdruntime/core/sys/posix/utime.d | 12 + .../libdruntime/core/sys/windows/dbghelp.d | 11 +- libphobos/libdruntime/core/sys/windows/dll.d | 7 +- .../libdruntime/core/sys/windows/stacktrace.d | 29 +- .../libdruntime/core/sys/windows/threadaux.d | 14 +- .../libdruntime/core/sys/windows/windows.d | 202 +- libphobos/libdruntime/core/thread.d | 675 +- libphobos/libdruntime/core/thread.di | 1134 - libphobos/libdruntime/core/threadasm.S | 14 + libphobos/libdruntime/core/time.d | 792 +- libphobos/libdruntime/core/vararg.d | 10 +- libphobos/libdruntime/etc/linux/memoryerror.d | 4 +- libphobos/libdruntime/gc/bits.d | 24 +- libphobos/libdruntime/gc/gc.d | 875 +- libphobos/libdruntime/gc/os.d | 18 +- libphobos/libdruntime/gc/proxy.d | 104 +- libphobos/libdruntime/gcstub/gc.d | 42 +- libphobos/libdruntime/object.di | 398 +- libphobos/libdruntime/object_.d | 693 +- libphobos/libdruntime/rt/aApply.d | 565 +- libphobos/libdruntime/rt/aaA.d | 42 +- libphobos/libdruntime/rt/adi.d | 42 +- libphobos/libdruntime/rt/arraybyte.d | 1847 - libphobos/libdruntime/rt/arraydouble.d | 1668 - libphobos/libdruntime/rt/arrayfloat.d | 1387 - libphobos/libdruntime/rt/arrayint.d | 3575 -- libphobos/libdruntime/rt/arrayreal.d | 226 - libphobos/libdruntime/rt/arrayshort.d | 2259 -- libphobos/libdruntime/rt/critical_.d | 5 +- libphobos/libdruntime/rt/deh.d | 3 + libphobos/libdruntime/rt/dmain2.d | 28 +- libphobos/libdruntime/rt/image.d | 148 - libphobos/libdruntime/rt/lifetime.d | 375 +- libphobos/libdruntime/rt/memory.d | 3 + libphobos/libdruntime/rt/memory_osx.d | 171 - libphobos/libdruntime/rt/minfo.d | 345 +- libphobos/libdruntime/rt/monitor_.d | 5 +- libphobos/libdruntime/rt/tls.S | 45 - libphobos/libdruntime/rt/tlsgc.d | 18 +- libphobos/libdruntime/rt/typeinfo/ti_AC.d | 6 + libphobos/libdruntime/rt/typeinfo/ti_double.d | 9 + libphobos/libdruntime/rt/typeinfo/ti_float.d | 9 + libphobos/libdruntime/rt/util/array.d | 2 +- .../libdruntime/rt/util/container/array.d | 180 + .../libdruntime/rt/util/container/common.d | 62 + .../util/{container.d => container/hashtab.d} | 233 +- .../libdruntime/rt/util/container/treap.d | 337 + libphobos/libdruntime/rt/util/random.d | 48 + libphobos/libdruntime/rt/util/utf.d | 17 +- libphobos/src/Makefile.am | 11 +- libphobos/src/Makefile.in | 11 +- libphobos/src/etc/c/curl.d | 4 + libphobos/src/etc/c/zlib.d | 55 +- libphobos/src/index.d | 2 +- libphobos/src/std/algorithm.d | 1071 +- libphobos/src/std/array.d | 540 +- libphobos/src/std/ascii.d | 39 +- libphobos/src/std/base64.d | 12 +- libphobos/src/std/bigint.d | 113 +- libphobos/src/std/bitmanip.d | 422 +- libphobos/src/std/c/process.d | 2 +- libphobos/src/std/c/windows/com.d | 10 +- libphobos/src/std/c/windows/winsock.d | 281 +- libphobos/src/std/complex.d | 56 +- libphobos/src/std/concurrency.d | 38 +- libphobos/src/std/container.d | 6695 --- libphobos/src/std/container/array.d | 1995 + libphobos/src/std/container/binaryheap.d | 442 + libphobos/src/std/container/dlist.d | 813 + libphobos/src/std/container/package.d | 856 + libphobos/src/std/container/rbtree.d | 1759 + libphobos/src/std/container/slist.d | 660 + libphobos/src/std/container/util.d | 85 + libphobos/src/std/conv.d | 913 +- libphobos/src/std/csv.d | 8 +- libphobos/src/std/datetime.d | 33536 ++++++++-------- libphobos/src/std/digest/crc.d | 8 +- libphobos/src/std/digest/digest.d | 11 +- libphobos/src/std/digest/md.d | 4 +- libphobos/src/std/digest/ripemd.d | 4 +- libphobos/src/std/digest/sha.d | 774 +- libphobos/src/std/encoding.d | 102 +- libphobos/src/std/exception.d | 363 +- libphobos/src/std/file.d | 84 +- libphobos/src/std/format.d | 360 +- libphobos/src/std/functional.d | 316 +- libphobos/src/std/getopt.d | 250 +- libphobos/src/std/internal/math/biguintcore.d | 131 +- .../src/std/internal/math/biguintnoasm.d | 27 +- libphobos/src/std/internal/math/biguintx86.d | 9 +- .../src/std/internal/math/errorfunction.d | 5 + .../src/std/internal/math/gammafunction.d | 7 +- libphobos/src/std/internal/scopebuffer.d | 394 + libphobos/src/std/internal/uni.d | 805 - libphobos/src/std/internal/uni_tab.d | 7285 ---- libphobos/src/std/internal/unicode_tables.d | 24 +- libphobos/src/std/internal/windows/advapi32.d | 12 +- libphobos/src/std/json.d | 55 +- libphobos/src/std/math.d | 1180 +- libphobos/src/std/mathspecial.d | 7 +- libphobos/src/std/metastrings.d | 245 - libphobos/src/std/mmfile.d | 9 +- libphobos/src/std/net/curl.d | 93 +- libphobos/src/std/net/isemail.d | 64 +- libphobos/src/std/numeric.d | 87 +- libphobos/src/std/outbuffer.d | 29 + libphobos/src/std/parallelism.d | 179 +- libphobos/src/std/path.d | 247 +- libphobos/src/std/process.d | 413 +- libphobos/src/std/random.d | 326 +- libphobos/src/std/range.d | 1398 +- libphobos/src/std/regex.d | 516 +- libphobos/src/std/signals.d | 6 +- libphobos/src/std/socket.d | 381 +- libphobos/src/std/stdio.d | 515 +- libphobos/src/std/stream.d | 117 +- libphobos/src/std/string.d | 553 +- libphobos/src/std/traits.d | 1097 +- libphobos/src/std/typecons.d | 662 +- libphobos/src/std/typetuple.d | 160 +- libphobos/src/std/uni.d | 1579 +- libphobos/src/std/uri.d | 70 +- libphobos/src/std/utf.d | 954 +- libphobos/src/std/uuid.d | 44 +- libphobos/src/std/variant.d | 555 +- libphobos/src/std/xml.d | 26 +- libphobos/src/std/zip.d | 10 +- libphobos/src/std/zlib.d | 9 +- libphobos/src/unittest.d | 27 +- 781 files changed, 90406 insertions(+), 85239 deletions(-) delete mode 100644 gcc/d/d-asmstmt.cc delete mode 100644 gcc/d/d-intrinsics.def delete mode 100644 gcc/d/dfrontend/artistic.txt create mode 100644 gcc/d/dfrontend/boostlicense.txt delete mode 100644 gcc/d/dfrontend/dump.c delete mode 100644 gcc/d/dfrontend/gpl.txt create mode 100644 gcc/d/dfrontend/nogc.c create mode 100644 gcc/d/dfrontend/nspace.c create mode 100644 gcc/d/dfrontend/nspace.h create mode 100644 gcc/d/intrinsics.def create mode 100644 gcc/d/runtime.def create mode 100644 gcc/testsuite/gdc.test/compilable/ddoc10366.d create mode 100644 gcc/testsuite/gdc.test/compilable/ddoc11511.d create mode 100644 gcc/testsuite/gdc.test/compilable/ddoc11823.d create mode 100644 gcc/testsuite/gdc.test/compilable/ddoc12706.d create mode 100644 gcc/testsuite/gdc.test/compilable/ddoc198.d create mode 100644 gcc/testsuite/gdc.test/compilable/ddoc648.d create mode 100644 gcc/testsuite/gdc.test/compilable/ddoc7555.d create mode 100644 gcc/testsuite/gdc.test/compilable/deprecate12979a.d create mode 100644 gcc/testsuite/gdc.test/compilable/diag11066.d create mode 100644 gcc/testsuite/gdc.test/compilable/extra-files/ddoc10366.html create mode 100644 gcc/testsuite/gdc.test/compilable/extra-files/ddoc11511.html create mode 100644 gcc/testsuite/gdc.test/compilable/extra-files/ddoc11823.html create mode 100644 gcc/testsuite/gdc.test/compilable/extra-files/ddoc12706.html create mode 100644 gcc/testsuite/gdc.test/compilable/extra-files/ddoc198.ddoc create mode 100644 gcc/testsuite/gdc.test/compilable/extra-files/ddoc198.html create mode 100644 gcc/testsuite/gdc.test/compilable/extra-files/ddoc648.html create mode 100644 gcc/testsuite/gdc.test/compilable/extra-files/ddoc7555.html rename gcc/testsuite/gdc.test/compilable/{inlineheader.d => extra-files/header1.d} (95%) rename gcc/testsuite/gdc.test/compilable/extra-files/{header.di => header1.di} (92%) create mode 100644 gcc/testsuite/gdc.test/compilable/extra-files/header12567a.di create mode 100644 gcc/testsuite/gdc.test/compilable/extra-files/header12567b.di rename gcc/testsuite/gdc.test/compilable/extra-files/{inlineheader.di => header1i.di} (93%) rename gcc/testsuite/gdc.test/compilable/{xheader.d => extra-files/header2.d} (90%) rename gcc/testsuite/gdc.test/compilable/extra-files/{xheader.di => header2.di} (99%) rename gcc/testsuite/gdc.test/compilable/extra-files/{inlinexheader.di => header2i.di} (99%) rename gcc/testsuite/gdc.test/{fail_compilation => compilable}/fail260.d (54%) create mode 100644 gcc/testsuite/gdc.test/compilable/fwdref12543.d delete mode 100644 gcc/testsuite/gdc.test/compilable/header.d create mode 100644 gcc/testsuite/gdc.test/compilable/ice11777.d create mode 100644 gcc/testsuite/gdc.test/compilable/ice11906.d create mode 100644 gcc/testsuite/gdc.test/compilable/ice12002.d create mode 100644 gcc/testsuite/gdc.test/compilable/ice12956.d create mode 100644 gcc/testsuite/gdc.test/compilable/ice13071.d create mode 100644 gcc/testsuite/gdc.test/compilable/ice13088.d create mode 100644 gcc/testsuite/gdc.test/compilable/ice13245.d create mode 100644 gcc/testsuite/gdc.test/compilable/ice13323.d create mode 100644 gcc/testsuite/gdc.test/compilable/ice8392.d create mode 100644 gcc/testsuite/gdc.test/compilable/imports/a12506.d create mode 100644 gcc/testsuite/gdc.test/compilable/imports/a12567.d create mode 100644 gcc/testsuite/gdc.test/compilable/imports/a8392.d create mode 100644 gcc/testsuite/gdc.test/compilable/imports/imp12242a.d create mode 100644 gcc/testsuite/gdc.test/compilable/imports/imp12242a1.d create mode 100644 gcc/testsuite/gdc.test/compilable/imports/imp12242a2.d create mode 100644 gcc/testsuite/gdc.test/compilable/imports/imp12242b.d create mode 100644 gcc/testsuite/gdc.test/compilable/imports/imp12242b1.d create mode 100644 gcc/testsuite/gdc.test/compilable/imports/imp12242b2.d delete mode 100644 gcc/testsuite/gdc.test/compilable/inlinexheader.d create mode 100644 gcc/testsuite/gdc.test/compilable/nogc.d create mode 100644 gcc/testsuite/gdc.test/compilable/parse11957.d create mode 100644 gcc/testsuite/gdc.test/compilable/parse13049.d delete mode 100644 gcc/testsuite/gdc.test/compilable/test10150.d create mode 100644 gcc/testsuite/gdc.test/compilable/test10520.d create mode 100644 gcc/testsuite/gdc.test/compilable/test11471.d create mode 100644 gcc/testsuite/gdc.test/compilable/test12009.d create mode 100644 gcc/testsuite/gdc.test/compilable/test12523.d create mode 100644 gcc/testsuite/gdc.test/compilable/test12567a.d create mode 100644 gcc/testsuite/gdc.test/compilable/test12567b.d create mode 100644 gcc/testsuite/gdc.test/compilable/test12567c.d create mode 100644 gcc/testsuite/gdc.test/compilable/test12567d.d create mode 100644 gcc/testsuite/gdc.test/compilable/test12593.d create mode 100644 gcc/testsuite/gdc.test/compilable/test12967.d create mode 100644 gcc/testsuite/gdc.test/compilable/test12979a.d create mode 100644 gcc/testsuite/gdc.test/compilable/test12979b.d create mode 100644 gcc/testsuite/gdc.test/compilable/test13008.d create mode 100644 gcc/testsuite/gdc.test/compilable/test13053.d create mode 100644 gcc/testsuite/gdc.test/compilable/test13193.d create mode 100644 gcc/testsuite/gdc.test/compilable/test13194.d create mode 100644 gcc/testsuite/gdc.test/compilable/test8296.d create mode 100644 gcc/testsuite/gdc.test/compilable/test9570.d create mode 100644 gcc/testsuite/gdc.test/compilable/testheader1.d create mode 100644 gcc/testsuite/gdc.test/compilable/testheader12567a.d create mode 100644 gcc/testsuite/gdc.test/compilable/testheader12567b.d create mode 100644 gcc/testsuite/gdc.test/compilable/testheader1i.d create mode 100644 gcc/testsuite/gdc.test/compilable/testheader2.d create mode 100644 gcc/testsuite/gdc.test/compilable/testheader2i.d create mode 100644 gcc/testsuite/gdc.test/compilable/testimport12242.d create mode 100644 gcc/testsuite/gdc.test/compilable/vgc1.d create mode 100644 gcc/testsuite/gdc.test/compilable/vgc2.d create mode 100644 gcc/testsuite/gdc.test/compilable/vgc3.d create mode 100644 gcc/testsuite/gdc.test/compilable/warn3882.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/deprecate12979a.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/deprecate12979b.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/deprecate12979c.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/deprecate12979d.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag10169.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag10221.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag10221a.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag11819a.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag11819b.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag12063.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag12280.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag12312.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag12380.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag12432.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag12480.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag12598.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag12640.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag12678.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag12777.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag13082.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag13142.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag13333.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag1566.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag3672.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag3672a.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag4596.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag6677.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag7747.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag8101.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag8101b.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag8777.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag8777a.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag8777b.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag8777c.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag8777d.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag8777e.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag8777f.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/diag_err1.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/extra-files/bar11453.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/extra-files/foo11453.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail102.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail10285.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail112.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail11453a.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail11453b.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail11503a.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail11503b.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail11503c.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail11503d.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail11542.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail11545.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail11591.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail11591b.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail12236.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail12255.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail12378.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail12390.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail12436.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail12485.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail12567.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail12604.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail12622.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail12749.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail128.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail12809.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail12901.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail12932.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail130.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail13187.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail13424.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail138.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail164.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail165.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail166.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail167.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail168.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail171.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail174.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail3672.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail3882.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail4.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail4082.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail4082a.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail4082b.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail6652.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail6652a.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail6652b.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail67.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail68.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail6889.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail69.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail70.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail71.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/fail8373.d delete mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice10610.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12174.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12179.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12235.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12350.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12362.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12397.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12497.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12501.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12534.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12539.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12554.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12574.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12581.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12673.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12727.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12836.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12838.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12841.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12850.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12902.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice12907.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice13024.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice13027.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice13081.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice13131.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice13220.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice13221.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice13225.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice13259.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice13311.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice8100.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice8309.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/ice8499.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/a10169.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/a13131checkpoint.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/a13131elec.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/a13131parameters.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/a13311.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/diag12598a.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152a.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152b.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152c.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152d.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152e.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152f.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152g.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152h.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152i.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152j.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152k.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152l.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152m.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152n.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152o.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152p.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152q.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152r.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152s.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152t.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152u.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152v.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152w.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152x.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152y.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/imports/test13152z.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/nogc1.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/nogc2.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/nogc3.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/parse12924.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/parse12967a.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/parse12967b.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/parseStc2.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/parseStc3.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/test12979.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/test13152.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/testCols.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/warn12809.d create mode 100644 gcc/testsuite/gdc.test/fail_compilation/warn1553.d create mode 100644 gcc/testsuite/gdc.test/runnable/bug12928.d create mode 100644 gcc/testsuite/gdc.test/runnable/bug7068.d delete mode 100644 gcc/testsuite/gdc.test/runnable/deprecate1.d create mode 100644 gcc/testsuite/gdc.test/runnable/imports/a12874.d create mode 100644 gcc/testsuite/gdc.test/runnable/imports/link13043a.d create mode 100644 gcc/testsuite/gdc.test/runnable/imports/link13394a.d create mode 100644 gcc/testsuite/gdc.test/runnable/imports/link13400a.d create mode 100644 gcc/testsuite/gdc.test/runnable/imports/link846a.d create mode 100644 gcc/testsuite/gdc.test/runnable/imports/mangle10077.d create mode 100644 gcc/testsuite/gdc.test/runnable/imports/template13478a.d create mode 100644 gcc/testsuite/gdc.test/runnable/imports/template13478b.d create mode 100644 gcc/testsuite/gdc.test/runnable/link13043.d create mode 100644 gcc/testsuite/gdc.test/runnable/link13394.d create mode 100644 gcc/testsuite/gdc.test/runnable/link13400.d create mode 100644 gcc/testsuite/gdc.test/runnable/link846.d create mode 100644 gcc/testsuite/gdc.test/runnable/nogc.d create mode 100644 gcc/testsuite/gdc.test/runnable/template13478.d create mode 100644 gcc/testsuite/gdc.test/runnable/test12874.d create mode 100644 gcc/testsuite/gdc.test/runnable/test5854.d delete mode 100644 gcc/testsuite/gdc.test/runnable/test9.d create mode 100644 gcc/testsuite/gdc.test/runnable/testaa3.d create mode 100644 gcc/testsuite/gdc.test/runnable/testbounds_off.d create mode 100644 gcc/testsuite/gdc.test/runnable/testbounds_on.d create mode 100644 gcc/testsuite/gdc.test/runnable/testbounds_safeonly.d create mode 100644 gcc/testsuite/gdc.test/runnable/testclass.d create mode 100644 gcc/testsuite/gdc.test/runnable/uniformctor.d create mode 100644 libphobos/libdruntime/core/checkedint.d create mode 100644 libphobos/libdruntime/core/internal/traits.d delete mode 100644 libphobos/libdruntime/core/thread.di delete mode 100644 libphobos/libdruntime/rt/arraybyte.d delete mode 100644 libphobos/libdruntime/rt/arraydouble.d delete mode 100644 libphobos/libdruntime/rt/arrayfloat.d delete mode 100644 libphobos/libdruntime/rt/arrayint.d delete mode 100644 libphobos/libdruntime/rt/arrayreal.d delete mode 100644 libphobos/libdruntime/rt/arrayshort.d delete mode 100644 libphobos/libdruntime/rt/image.d delete mode 100644 libphobos/libdruntime/rt/memory_osx.d delete mode 100644 libphobos/libdruntime/rt/tls.S create mode 100644 libphobos/libdruntime/rt/util/container/array.d create mode 100644 libphobos/libdruntime/rt/util/container/common.d rename libphobos/libdruntime/rt/util/{container.d => container/hashtab.d} (58%) create mode 100644 libphobos/libdruntime/rt/util/container/treap.d create mode 100644 libphobos/libdruntime/rt/util/random.d delete mode 100644 libphobos/src/std/container.d create mode 100644 libphobos/src/std/container/array.d create mode 100644 libphobos/src/std/container/binaryheap.d create mode 100644 libphobos/src/std/container/dlist.d create mode 100644 libphobos/src/std/container/package.d create mode 100644 libphobos/src/std/container/rbtree.d create mode 100644 libphobos/src/std/container/slist.d create mode 100644 libphobos/src/std/container/util.d create mode 100644 libphobos/src/std/internal/scopebuffer.d delete mode 100644 libphobos/src/std/internal/uni.d delete mode 100644 libphobos/src/std/internal/uni_tab.d delete mode 100644 libphobos/src/std/metastrings.d diff --git a/gcc/d/Make-lang.in b/gcc/d/Make-lang.in index 97dc76adc..05beb37ba 100644 --- a/gcc/d/Make-lang.in +++ b/gcc/d/Make-lang.in @@ -43,15 +43,16 @@ D_DMD_H := \ d/dfrontend/import.h d/dfrontend/init.h d/dfrontend/intrange.h \ d/dfrontend/json.h d/dfrontend/lexer.h d/dfrontend/macro.h \ d/dfrontend/mars.h d/dfrontend/module.h d/dfrontend/mtype.h \ - d/dfrontend/object.h d/dfrontend/outbuffer.h d/dfrontend/parse.h \ - d/dfrontend/port.h d/dfrontend/rmem.h d/dfrontend/root.h \ - d/dfrontend/scope.h d/dfrontend/speller.h d/dfrontend/statement.h \ - d/dfrontend/staticassert.h d/dfrontend/stringtable.h \ - d/dfrontend/target.h d/dfrontend/template.h d/dfrontend/utf.h \ + d/dfrontend/nspace.h d/dfrontend/object.h d/dfrontend/outbuffer.h \ + d/dfrontend/parse.h d/dfrontend/port.h d/dfrontend/rmem.h \ + d/dfrontend/root.h d/dfrontend/scope.h d/dfrontend/speller.h \ + d/dfrontend/statement.h d/dfrontend/staticassert.h \ + d/dfrontend/stringtable.h d/dfrontend/target.h \ + d/dfrontend/template.h d/dfrontend/utf.h \ d/dfrontend/version.h d/dfrontend/visitor.h \ d/d-dmd-gcc.h d/longdouble.h d/id.h d/verstr.h -D_TREE_H = $(TREE_H) $(D_DMD_H) d/d-intrinsics.def d/d-tree.def \ +D_TREE_H = $(TREE_H) $(D_DMD_H) d/intrinsics.def d/runtime.def d/d-tree.def \ d/d-lang.h d/d-codegen.h d/d-objfile.h d/d-irstate.h \ d/d-dmd-gcc.h d/d-system.h d/longdouble.h coretypes.h function.h \ $(VARRAY_H) $(SYSTEM_H) $(CONFIG_H) $(TARGET_H) $(GGC_H) \ @@ -80,7 +81,7 @@ ALL_D_COMPILER_FLAGS = $(ALL_COMPILERFLAGS) $(ALL_CPPFLAGS) \ $(INCLUDES) $(D_INCLUDES) $(D_EXTRA_DEFINES) # D Frontend has slightly relaxed warnings compared to rest of GDC. -DMD_WARN_CFLAGS = -Wno-deprecated -Wstrict-aliasing +DMD_WARN_CFLAGS = -Wno-deprecated -Wstrict-aliasing -Wuninitialized DMD_CXXFLAGS = $(filter-out $(WARN_CXXFLAGS), $(ALL_COMPILERFLAGS)) $(DMD_WARN_CFLAGS) # %% Ditto @@ -107,17 +108,18 @@ D_DMD_OBJS := \ d/canthrow.dmd.o d/cast.dmd.o d/class.dmd.o \ d/clone.dmd.o d/cond.dmd.o d/constfold.dmd.o d/cppmangle.dmd.o \ d/ctfeexpr.dmd.o d/declaration.dmd.o d/delegatize.dmd.o d/doc.dmd.o \ - d/dsymbol.dmd.o d/dump.dmd.o d/entity.dmd.o d/enum.dmd.o \ + d/dsymbol.dmd.o d/entity.dmd.o d/enum.dmd.o \ d/expression.dmd.o d/file.dmd.o d/filename.dmd.o d/func.dmd.o \ d/hdrgen.dmd.o d/identifier.dmd.o d/imphint.dmd.o d/import.dmd.o \ d/init.dmd.o d/inline.dmd.o d/interpret.dmd.o d/intrange.dmd.o \ d/json.dmd.o d/lexer.dmd.o d/macro.dmd.o d/mangle.dmd.o \ - d/mtype.dmd.o d/module.dmd.o d/object.dmd.o d/opover.dmd.o \ - d/optimize.dmd.o d/outbuffer.dmd.o d/parse.dmd.o d/rmem.dmd.o \ - d/sapply.dmd.o d/scope.dmd.o d/sideeffect.dmd.o \ - d/speller.dmd.o d/statement.dmd.o d/staticassert.dmd.o \ - d/stringtable.dmd.o d/struct.dmd.o d/template.dmd.o d/traits.dmd.o \ - d/unittests.dmd.o d/utf.dmd.o d/version.dmd.o + d/mtype.dmd.o d/module.dmd.o d/nogc.dmd.o d/nspace.dmd.o \ + d/object.dmd.o d/opover.dmd.o d/optimize.dmd.o d/outbuffer.dmd.o \ + d/parse.dmd.o d/rmem.dmd.o d/sapply.dmd.o d/scope.dmd.o \ + d/sideeffect.dmd.o d/speller.dmd.o d/statement.dmd.o \ + d/staticassert.dmd.o d/stringtable.dmd.o d/struct.dmd.o \ + d/template.dmd.o d/traits.dmd.o d/unittests.dmd.o \ + d/utf.dmd.o d/version.dmd.o D_GENERATED_SRCS = d/id.c d/id.h d/impcnvtab.c D_GENERATED_OBJS = d/id.gen.o d/impcnvtab.gen.o @@ -126,7 +128,7 @@ D_GENERATED_OBJS = d/id.gen.o d/impcnvtab.gen.o D_GLUE_OBJS = d/d-lang.glue.o d/d-decls.glue.o d/d-codegen.glue.o \ d/d-objfile.glue.o d/d-irstate.glue.o \ d/d-convert.glue.o d/d-todt.glue.o d/d-longdouble.glue.o \ - d/d-gt.cglue.o d/d-builtins.glue.o d/d-asmstmt.glue.o \ + d/d-gt.cglue.o d/d-builtins.glue.o \ d/d-incpath.glue.o d/d-ctype.glue.o d/d-elem.glue.o \ d/toir.glue.o d/d-typinf.glue.o d/d-port.glue.o \ d/d-target.glue.o d/d-glue.glue.o @@ -187,7 +189,6 @@ d/d-todt.glue.o: d/d-todt.cc $(D_TREE_H) d/d-typinf.glue.o: d/d-typinf.cc $(D_TREE_H) d/d-longdouble.glue.o: d/d-longdouble.cc d/longdouble.h $(D_TREE_H) d/d-port.glue.o: d/d-port.cc d/dfrontend/port.h $(D_TREE_H) -d/d-asmstmt.glue.o: d/d-asmstmt.cc $(D_TREE_H) d/d-gt.cglue.o: d/d-gt.cc $(D_TREE_H) d/d-builtins.glue.o: d/d-builtins.cc $(D_TREE_H) diff --git a/gcc/d/VERSION b/gcc/d/VERSION index 73a7c0c0c..4447102fa 100644 --- a/gcc/d/VERSION +++ b/gcc/d/VERSION @@ -1 +1 @@ -"2.065" +"2.066.1" diff --git a/gcc/d/d-asmstmt.cc b/gcc/d/d-asmstmt.cc deleted file mode 100644 index 3009a378a..000000000 --- a/gcc/d/d-asmstmt.cc +++ /dev/null @@ -1,205 +0,0 @@ -// d-asmstmt.cc -- D frontend for GCC. -// Copyright (C) 2011-2013 Free Software Foundation, Inc. - -// GCC is free software; you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3, or (at your option) any later -// version. - -// GCC is distributed in the hope that it will be useful, but WITHOUT ANY -// WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -// for more details. - -// You should have received a copy of the GNU General Public License -// along with GCC; see the file COPYING3. If not see -// . - -#include "d-system.h" -#include "statement.h" -#include "scope.h" -#include "id.h" - -#include "d-lang.h" -#include "d-codegen.h" - - -// Semantically analyze AsmStatement where SC is the scope. - -Statement * -AsmStatement::semantic (Scope *sc) -{ - sc->func->hasReturnExp |= 8; - return Statement::semantic (sc); -} - -// Construct an ExtAsmStatement, whose components are an INSN, -// some ARGS, a list of all NAMES used, and their CONSTRAINTS, -// the number of OUTPUTARGS, and the list of CLOBBERS. - -ExtAsmStatement::ExtAsmStatement (Loc loc, Expression *insn, - Expressions *args, Identifiers *names, - Expressions *constraints, int outputargs, - Expressions *clobbers) - : Statement (loc) -{ - this->insn = insn; - this->args = args; - this->names = names; - this->constraints = constraints; - this->outputargs = outputargs; - this->clobbers = clobbers; -} - -// Create a copy of ExtAsmStatement. - -Statement * -ExtAsmStatement::syntaxCopy (void) -{ - Expression *insn = this->insn->syntaxCopy(); - Expressions *args = NULL; - Expressions *constraints = NULL; - Expressions *clobbers = NULL; - - args = Expression::arraySyntaxCopy (this->args); - constraints = Expression::arraySyntaxCopy (this->constraints); - clobbers = Expression::arraySyntaxCopy (this->clobbers); - - return new ExtAsmStatement (this->loc, insn, args, this->names, constraints, - this->outputargs, clobbers); -} - -// Semantically analyze ExtAsmStatement where SC is the scope of the statment. -// Determines types, fold constants, etc. - -Statement * -ExtAsmStatement::semantic (Scope *sc) -{ - this->insn->semantic (sc); - this->insn->optimize (WANTvalue); - - if (sc->func) - { - if (sc->func->setUnsafe()) - error ("extended assembler not allowed in @safe function %s", - sc->func->toChars()); - } - - if (this->insn->op != TOKstring || ((StringExp *) this->insn)->sz != 1) - error ("instruction template must be a constant char string"); - - if (this->args) - { - for (size_t i = 0; i < this->args->dim; i++) - { - Expression *e = (*this->args)[i]; - e = e->semantic (sc); - if (i < this->outputargs) - e = e->modifiableLvalue (sc, NULL); - else - e = e->optimize (WANTvalue); - (*this->args)[i] = e; - - e = (*this->constraints)[i]; - e = e->semantic (sc); - e = e->optimize (WANTvalue); - if (e->op != TOKstring || ((StringExp *) e)->sz != 1) - error ("constraint must be a constant char string"); - (*this->constraints)[i] = e; - } - } - - if (this->clobbers) - { - for (size_t i = 0; i < this->clobbers->dim; i++) - { - Expression *e = (*this->clobbers)[i]; - e = e->semantic (sc); - e = e->optimize (WANTvalue); - if (e->op != TOKstring || ((StringExp *) e)->sz != 1) - error ("clobber specification must be a constant char string"); - (*this->clobbers)[i] = e; - } - } - - return this; -} - -// Write C-style representation of ExtAsmStatement to BUF. - -void -ExtAsmStatement::toCBuffer (OutBuffer *buf, HdrGenState *hgs ATTRIBUTE_UNUSED) -{ - buf->writestring ("gcc asm { "); - - if (this->insn) - buf->writestring (this->insn->toChars()); - - buf->writestring (" : "); - - if (this->args) - { - for (size_t i = 0; i < this->args->dim; i++) - { - Identifier *name = (*this->names)[i]; - Expression *constr = (*this->constraints)[i]; - Expression *arg = (*this->args)[i]; - - if (name) - { - buf->writestring ("["); - buf->writestring (name->toChars()); - buf->writestring ("] "); - } - - if (constr) - { - buf->writestring (constr->toChars()); - buf->writestring (" "); - } - - if (arg) - buf->writestring (arg->toChars()); - - if (i < this->outputargs - 1) - buf->writestring (", "); - else if (i == this->outputargs - 1) - buf->writestring (" : "); - else if (i < this->args->dim - 1) - buf->writestring (", "); - } - } - - if (this->clobbers) - { - buf->writestring (" : "); - for (size_t i = 0; i < this->clobbers->dim; i++) - { - Expression *clobber = (*this->clobbers)[i]; - buf->writestring (clobber->toChars()); - if (i < this->clobbers->dim - 1) - buf->writestring (", "); - } - } - - buf->writestring ("; }"); - buf->writenl(); -} - -// TRUE if statement 'comes from' somewhere else, like a goto. - -bool -ExtAsmStatement::comeFromImpl() -{ - return true; -} - -// Return how an ExtAsmStatement exits. - -int -ExtAsmStatement::blockExit (bool) -{ - // Assume the worst - return BEany; -} - diff --git a/gcc/d/d-builtins.cc b/gcc/d/d-builtins.cc index 7eacb70c4..5c0e6031d 100644 --- a/gcc/d/d-builtins.cc +++ b/gcc/d/d-builtins.cc @@ -222,7 +222,6 @@ build_dtype (tree t) sdecl->sizeok = SIZEOKdone; sdecl->type = new TypeStruct (sdecl); sdecl->type->ctype = t; - sdecl->handle = sdecl->type; sdecl->type->merge(); // Does not seem necessary to convert fields, but the @@ -567,87 +566,6 @@ d_gcc_magic_module (Module *m) } } -// Perform a reinterpret cast of EXPR to type TYPE for use in CTFE. -// The front end should have already ensured that EXPR is a constant, -// so we just lower the value to GCC and return the converted CST. - -Expression * -d_gcc_paint_type (Expression *expr, Type *type) -{ - /* We support up to 512-bit values. */ - unsigned char buffer[64]; - tree cst; - - Type *tb = type->toBasetype(); - - if (expr->type->isintegral()) - cst = build_integer_cst (expr->toInteger(), expr->type->toCtype()); - else if (expr->type->isfloating()) - cst = build_float_cst (expr->toReal(), expr->type); - else if (expr->op == TOKarrayliteral) - { - // Build array as VECTOR_CST, assumes EXPR is constant. - Expressions *elements = ((ArrayLiteralExp *) expr)->elements; - vec *elms = NULL; - - vec_safe_reserve (elms, elements->dim); - for (size_t i = 0; i < elements->dim; i++) - { - Expression *e = (*elements)[i]; - if (e->type->isintegral()) - { - tree value = build_integer_cst (e->toInteger(), e->type->toCtype()); - CONSTRUCTOR_APPEND_ELT (elms, size_int (i), value); - } - else if (e->type->isfloating()) - { - tree value = build_float_cst (e->toReal(), e->type); - CONSTRUCTOR_APPEND_ELT (elms, size_int (i), value); - } - else - gcc_unreachable(); - } - - // Build vector type. - int nunits = ((TypeSArray *) expr->type)->dim->toUInteger(); - Type *telem = expr->type->nextOf(); - tree vectype = build_vector_type (telem->toCtype(), nunits); - - cst = build_vector_from_ctor (vectype, elms); - } - else - gcc_unreachable(); - - // Encode CST to buffer. - int len = native_encode_expr (cst, buffer, sizeof (buffer)); - - if (tb->ty == Tsarray) - { - // Interpret value as a vector of the same size, - // then return the array literal. - int nunits = ((TypeSArray *) type)->dim->toUInteger(); - Type *elem = type->nextOf(); - tree vectype = build_vector_type (elem->toCtype(), nunits); - - cst = native_interpret_expr (vectype, buffer, len); - - Expression *e = build_expression (cst); - gcc_assert (e != NULL && e->op == TOKvector); - - return ((VectorExp *) e)->e1; - } - else - { - // Normal interpret cast. - cst = native_interpret_expr (type->toCtype(), buffer, len); - - Expression *e = build_expression (cst); - gcc_assert (e != NULL); - - return e; - } -} - /* Used to help initialize the builtin-types.def table. When a type of the correct size doesn't exist, use error_mark_node instead of NULL. The later results in segfaults even when a decl using the type doesn't @@ -681,7 +599,7 @@ enum built_in_attribute static GTY(()) tree built_in_attributes[(int) ATTR_LAST]; static void -d_init_attributes (void) +d_init_attributes() { /* Fill in the built_in_attributes array. */ #define DEF_ATTR_NULL_TREE(ENUM) \ @@ -828,7 +746,7 @@ def_fn_type (builtin_type def, builtin_type ret, bool var, int n, ...) // Build builtin functions and types for the D language frontend. void -d_init_builtins (void) +d_init_builtins() { tree va_list_ref_type_node; tree va_list_arg_type_node; @@ -1460,7 +1378,7 @@ ignore_attribute (tree * ARG_UNUSED (node), tree ARG_UNUSED (name), // Backend init. void -d_backend_init (void) +d_backend_init() { init_global_binding_level(); @@ -1482,7 +1400,7 @@ d_backend_init (void) // Backend term. void -d_backend_term (void) +d_backend_term() { } diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc index c13759e32..822580b84 100644 --- a/gcc/d/d-codegen.cc +++ b/gcc/d/d-codegen.cc @@ -261,16 +261,11 @@ convert_expr (tree exp, Type *etype, Type *totype) if (totype->size() == etype->size()) { // Allowed to cast to structs with same type size. - result = build_vconvert (totype->toCtype(), exp); - } - else if (tbtype->ty == Taarray) - { - tbtype = ((TypeAArray *) tbtype)->getImpl()->type; - return convert_expr (exp, etype, tbtype); + result = build_vconvert(totype->toCtype(), exp); } else { - error ("can't convert struct %s to %s", etype->toChars(), totype->toChars()); + error("can't convert struct %s to %s", etype->toChars(), totype->toChars()); return error_mark_node; } } @@ -280,56 +275,54 @@ convert_expr (tree exp, Type *etype, Type *totype) case Tclass: if (tbtype->ty == Tclass) { - ClassDeclaration *cdfrom = tbtype->isClassHandle(); - ClassDeclaration *cdto = ebtype->isClassHandle(); + ClassDeclaration *cdfrom = ebtype->isClassHandle(); + ClassDeclaration *cdto = tbtype->isClassHandle(); int offset; - if (cdfrom->isBaseOf (cdto, &offset) && offset != OFFSET_RUNTIME) + if (cdfrom->cpp) + { + // Downcasting in C++ is a no-op. + if (cdto->cpp) + break; + + // Casting from a C++ interface to a class/non-C++ interface + // always results in null as there is no runtime information, + // and no way one can derive from the other. + warning(OPT_Wcast_result, "cast to %s will produce null result", totype->toChars()); + result = d_convert(totype->toCtype(), null_pointer_node); + + // Make sure the expression is still evaluated if necessary + if (TREE_SIDE_EFFECTS(exp)) + result = compound_expr(exp, result); + + return result; + } + + if (cdto->isBaseOf(cdfrom, &offset) && offset != OFFSET_RUNTIME) { // Casting up the inheritance tree: Don't do anything special. // Cast to an implemented interface: Handle at compile time. if (offset) { tree t = totype->toCtype(); - exp = maybe_make_temp (exp); - return build3 (COND_EXPR, t, - build_boolop (NE_EXPR, exp, null_pointer_node), - build_nop (t, build_offset (exp, size_int (offset))), - build_nop (t, null_pointer_node)); + exp = maybe_make_temp(exp); + return build3(COND_EXPR, t, + build_boolop(NE_EXPR, exp, null_pointer_node), + build_nop(t, build_offset(exp, size_int(offset))), + build_nop(t, null_pointer_node)); } // d_convert will make a no-op cast break; } - // More cases for no-op cast - if (cdfrom == cdto) - break; - - if (cdfrom->cpp && cdto->cpp) - break; - - // Casting from a C++ interface to a class/non-C++ interface - // always results in null as there is no runtime information, - // and no way one can derive from the other. - if (cdto->isCOMclass() || cdfrom->cpp != cdto->cpp) - { - warning (OPT_Wcast_result, "cast to %s will produce null result", totype->toChars()); - result = d_convert (totype->toCtype(), null_pointer_node); - // Make sure the expression is still evaluated if necessary - if (TREE_SIDE_EFFECTS (exp)) - result = compound_expr (exp, result); - - return result; - } - // The offset can only be determined at runtime, do dynamic cast tree args[2]; args[0] = exp; - args[1] = build_address (cdfrom->toSymbol()->Stree); + args[1] = build_address(cdto->toSymbol()->Stree); - return build_libcall (cdto->isInterfaceDeclaration() - ? LIBCALL_INTERFACE_CAST : LIBCALL_DYNAMIC_CAST, 2, args); + return build_libcall(cdfrom->isInterfaceDeclaration() + ? LIBCALL_INTERFACE_CAST : LIBCALL_DYNAMIC_CAST, 2, args); } // else default conversion break; @@ -426,20 +419,15 @@ convert_expr (tree exp, Type *etype, Type *totype) case Taarray: if (tbtype->ty == Taarray) return build_vconvert (totype->toCtype(), exp); - else if (tbtype->ty == Tstruct) - { - ebtype = ((TypeAArray *) ebtype)->getImpl()->type; - return convert_expr (exp, ebtype, totype); - } // Can convert associative arrays to void pointers. - else if (tbtype == Type::tvoidptr) + else if (tbtype->ty == Tpointer && tbtype->nextOf()->ty == Tvoid) return build_vconvert (totype->toCtype(), exp); // else, default conversion, which should product an error break; case Tpointer: // Can convert void pointers to associative arrays too... - if (tbtype->ty == Taarray && ebtype == Type::tvoidptr) + if (tbtype->ty == Taarray && ebtype->nextOf()->ty == Tvoid) return build_vconvert (totype->toCtype(), exp); break; @@ -447,9 +435,13 @@ convert_expr (tree exp, Type *etype, Type *totype) if (tbtype->ty == Tarray) { tree ptrtype = tbtype->nextOf()->pointerTo()->toCtype(); - return d_array_value (totype->toCtype(), size_int (0), - build_nop (ptrtype, exp)); + return d_array_value(totype->toCtype(), size_int(0), + build_nop(ptrtype, exp)); } + else if (tbtype->ty == Taarray) + return build_vconvert (totype->toCtype(), exp); + else if (tbtype->ty == Tdelegate) + return build_delegate_cst(exp, null_pointer_node, totype); break; case Tvector: @@ -1059,24 +1051,6 @@ get_array_length (tree exp, Type *type) } } -// Return TRUE if binary expression EXP is an unhandled array operation, -// in which case we error that it is not implemented. - -bool -unhandled_arrayop_p (BinExp *exp) -{ - TY ty1 = exp->e1->type->toBasetype()->ty; - TY ty2 = exp->e2->type->toBasetype()->ty; - - if ((ty1 == Tarray || ty1 == Tsarray - || ty2 == Tarray || ty2 == Tsarray)) - { - exp->error ("Array operation %s not implemented", exp->toChars()); - return true; - } - return false; -} - // Create BINFO for a ClassDeclaration's inheritance tree. // Interfaces are not included. @@ -1268,27 +1242,18 @@ get_object_method (tree thisexp, Expression *objexp, FuncDeclaration *func, Type // type we are building. N1 and N2 are the names of the two fields. tree -build_two_field_type (tree t1, tree t2, Type *type, const char *n1, const char *n2) +build_two_field_type(tree t1, tree t2, Type *type, const char *n1, const char *n2) { - tree rectype = make_node (RECORD_TYPE); - tree f0 = build_decl (BUILTINS_LOCATION, FIELD_DECL, get_identifier (n1), t1); - tree f1 = build_decl (BUILTINS_LOCATION, FIELD_DECL, get_identifier (n2), t2); - - DECL_CONTEXT (f0) = rectype; - DECL_CONTEXT (f1) = rectype; - TYPE_FIELDS (rectype) = chainon (f0, f1); - layout_type (rectype); - - if (type) - { - tree ident = get_identifier (type->toChars()); - tree stubdecl = build_decl (BUILTINS_LOCATION, TYPE_DECL, ident, rectype); + tree rectype = make_node(RECORD_TYPE); + tree f0 = build_decl(BUILTINS_LOCATION, FIELD_DECL, get_identifier(n1), t1); + tree f1 = build_decl(BUILTINS_LOCATION, FIELD_DECL, get_identifier(n2), t2); - TYPE_STUB_DECL (rectype) = stubdecl; - TYPE_NAME (rectype) = stubdecl; - DECL_ARTIFICIAL (stubdecl) = 1; - rest_of_decl_compilation (stubdecl, 1, 0); - } + DECL_CONTEXT(f0) = rectype; + DECL_CONTEXT(f1) = rectype; + TYPE_FIELDS(rectype) = chainon(f0, f1); + if (type != NULL) + TYPE_NAME(rectype) = get_identifier(type->toChars()); + layout_type(rectype); return rectype; } @@ -1763,6 +1728,51 @@ build_binary_op (tree_code code, tree type, tree arg0, tree arg1) return d_convert (type, t); } +// Build a binary expression of code CODE, assigning the result into E1. + +tree +build_binop_assignment(tree_code code, Expression *e1, Expression *e2) +{ + // Skip casts for lhs assignment. + Expression *e1b = e1; + while (e1b->op == TOKcast) + { + CastExp *ce = (CastExp *) e1b; + gcc_assert(d_types_same(ce->type, ce->to)); + e1b = ce->e1; + } + + // Prevent multiple evaluations of LHS, but watch out! + // The LHS expression could be an assignment, to which + // it's operation gets lost during gimplification. + tree lexpr = NULL_TREE; + tree lhs; + + if (e1b->op == TOKcomma) + { + CommaExp *ce = (CommaExp *) e1b; + lexpr = ce->e1->toElem(current_irstate); + lhs = ce->e2->toElem(current_irstate); + } + else + lhs = e1b->toElem(current_irstate); + + tree rhs = e2->toElem(current_irstate); + + // Build assignment expression. Stabilize lhs for assignment. + lhs = stabilize_reference(lhs); + + rhs = build_binary_op(code, e1->type->toCtype(), + convert_expr(lhs, e1b->type, e1->type), rhs); + + tree expr = modify_expr(lhs, convert_expr(rhs, e1->type, e1b->type)); + + if (lexpr) + expr = compound_expr(lexpr, expr); + + return expr; +} + // Builds an array bounds checking condition, returning INDEX if true, // else throws a RangeError exception. @@ -1801,7 +1811,7 @@ d_bounds_condition (tree index, tree upr, bool inclusive) // Returns TRUE if array bounds checking code generation is turned on. bool -array_bounds_check (void) +array_bounds_check() { int result = global.params.useArrayBounds; @@ -2071,353 +2081,68 @@ d_assert_call (Loc loc, LibCall libcall, tree msg) // Our internal list of library functions. -// Most are extern(C) - for those that are not, correct mangling must be ensured. -// List kept in ascii collating order to allow binary search - -static const char *libcall_ids[LIBCALL_count] = { - "_D9invariant12_d_invariantFC6ObjectZv", - "_aaDelX", "_aaEqual", - "_aaGetRvalueX", "_aaGetX", - "_aaInX", - "_adCmp2", "_adEq2", - "_d_allocmemory", "_d_arraybounds", - "_d_arrayappendT", "_d_arrayappendcTX", - "_d_arrayappendcd", "_d_arrayappendwd", - "_d_arrayassign", "_d_arraycast", - "_d_arraycatT", "_d_arraycatnT", - "_d_arraycopy", "_d_arrayctor", - "_d_arrayliteralTX", - "_d_arraysetassign", "_d_arraysetctor", - "_d_arraysetlengthT", "_d_arraysetlengthiT", - "_d_assert", "_d_assert_msg", - "_d_assocarrayliteralTX", - "_d_callfinalizer", "_d_callinterfacefinalizer", - "_d_delarray", "_d_delarray_t", "_d_delclass", - "_d_delinterface", "_d_delmemory", - "_d_dynamic_cast", "_d_hidden_func", "_d_interface_cast", - "_d_newarrayT", "_d_newarrayiT", - "_d_newarraymTX", "_d_newarraymiTX", - "_d_newclass", "_d_newitemT", "_d_newitemiT", - "_d_switch_dstring", "_d_switch_error", - "_d_switch_string", "_d_switch_ustring", - "_d_throw", "_d_unittest", "_d_unittest_msg", -}; static FuncDeclaration *libcall_decls[LIBCALL_count]; -// Library functions are generated as needed. -// This could probably be changed in the future to be -// more like GCC builtin trees. +// Build and return a function symbol to be used by libcall_decls. -FuncDeclaration * -get_libcall (LibCall libcall) +static FuncDeclaration * +get_libcall(const char *name, Type *type, int flags, int nparams, ...) { - FuncDeclaration *decl = libcall_decls[libcall]; - - static Type *aatype = NULL; - - if (!decl) - { - Types targs; - Type *treturn = Type::tvoid; - bool varargs = false; - bool fnmalloc = false; - bool fnthrows = false; - - // Build generic AA type void*[void*] - if (aatype == NULL) - aatype = new TypeAArray (Type::tvoidptr, Type::tvoidptr); - - switch (libcall) - { - case LIBCALL_ASSERT: - case LIBCALL_ARRAY_BOUNDS: - case LIBCALL_SWITCH_ERROR: - // need to spec chararray/string because internal code passes string constants - targs.push (Type::tchar->arrayOf()); - targs.push (Type::tuns32); - fnthrows = true; - break; - - case LIBCALL_ASSERT_MSG: - targs.push (Type::tchar->arrayOf()); - targs.push (Type::tchar->arrayOf()); - targs.push (Type::tuns32); - fnthrows = true; - break; - - case LIBCALL_UNITTEST: - targs.push (Type::tchar->arrayOf()); - targs.push (Type::tuns32); - fnthrows = true; - break; - - case LIBCALL_UNITTEST_MSG: - targs.push (Type::tchar->arrayOf()); - targs.push (Type::tchar->arrayOf()); - targs.push (Type::tuns32); - fnthrows = true; - break; - - case LIBCALL_NEWCLASS: - targs.push (Type::typeinfoclass->type->constOf()); - treturn = build_object_type(); - break; - - case LIBCALL_NEWARRAYT: - case LIBCALL_NEWARRAYIT: - targs.push (Type::dtypeinfo->type->constOf()); - targs.push (Type::tsize_t); - treturn = Type::tvoid->arrayOf(); - break; - - case LIBCALL_NEWARRAYMTX: - case LIBCALL_NEWARRAYMITX: - targs.push (Type::dtypeinfo->type->constOf()); - targs.push (Type::tsize_t); - targs.push (Type::tsize_t); - treturn = Type::tvoid->arrayOf(); - break; - - case LIBCALL_NEWITEMT: - case LIBCALL_NEWITEMIT: - targs.push (Type::dtypeinfo->type->constOf()); - treturn = Type::tvoidptr; - break; - - case LIBCALL_ALLOCMEMORY: - targs.push (Type::tsize_t); - treturn = Type::tvoidptr; - fnmalloc = true; - break; - - case LIBCALL_DELCLASS: - case LIBCALL_DELINTERFACE: - targs.push (Type::tvoidptr); - break; - - case LIBCALL_DELARRAY: - targs.push (Type::tvoid->arrayOf()->pointerTo()); - break; - - case LIBCALL_DELARRAYT: - targs.push (Type::tvoid->arrayOf()->pointerTo()); - targs.push (Type::dtypeinfo->type->constOf()); - break; - - case LIBCALL_DELMEMORY: - targs.push (Type::tvoidptr->pointerTo()); - break; - - case LIBCALL_CALLFINALIZER: - case LIBCALL_CALLINTERFACEFINALIZER: - targs.push (Type::tvoidptr); - break; - - case LIBCALL_ARRAYSETLENGTHT: - case LIBCALL_ARRAYSETLENGTHIT: - targs.push (Type::dtypeinfo->type->constOf()); - targs.push (Type::tsize_t); - targs.push (Type::tvoid->arrayOf()->pointerTo()); - treturn = Type::tvoid->arrayOf(); - break; - - case LIBCALL_DYNAMIC_CAST: - case LIBCALL_INTERFACE_CAST: - targs.push (build_object_type()); - targs.push (Type::typeinfoclass->type); - treturn = build_object_type(); - break; - - case LIBCALL_ADEQ2: - case LIBCALL_ADCMP2: - targs.push (Type::tvoid->arrayOf()); - targs.push (Type::tvoid->arrayOf()); - targs.push (Type::dtypeinfo->type->constOf()); - treturn = Type::tint32; - break; - - case LIBCALL_AAEQUAL: - targs.push (Type::dtypeinfo->type->constOf()); - targs.push (aatype); - targs.push (aatype); - treturn = Type::tint32; - break; - - case LIBCALL_AAINX: - targs.push (aatype); - targs.push (Type::dtypeinfo->type->constOf()); - targs.push (Type::tvoidptr); - treturn = Type::tvoidptr; - break; - - case LIBCALL_AAGETX: - targs.push (aatype->pointerTo()); - targs.push (Type::dtypeinfo->type->constOf()); - targs.push (Type::tsize_t); - targs.push (Type::tvoidptr); - treturn = Type::tvoidptr; - break; - - case LIBCALL_AAGETRVALUEX: - targs.push (aatype); - targs.push (Type::dtypeinfo->type->constOf()); - targs.push (Type::tsize_t); - targs.push (Type::tvoidptr); - treturn = Type::tvoidptr; - break; - - case LIBCALL_AADELX: - targs.push (aatype); - targs.push (Type::dtypeinfo->type->constOf()); - targs.push (Type::tvoidptr); - treturn = Type::tbool; - break; - - case LIBCALL_ARRAYCAST: - targs.push (Type::tsize_t); - targs.push (Type::tsize_t); - targs.push (Type::tvoid->arrayOf()); - treturn = Type::tvoid->arrayOf(); - break; - - case LIBCALL_ARRAYCOPY: - targs.push (Type::tsize_t); - targs.push (Type::tvoid->arrayOf()); - targs.push (Type::tvoid->arrayOf()); - treturn = Type::tvoid->arrayOf(); - break; - - case LIBCALL_ARRAYCATT: - targs.push (Type::dtypeinfo->type->constOf()); - targs.push (Type::tint8->arrayOf()); - targs.push (Type::tint8->arrayOf()); - treturn = Type::tint8->arrayOf(); - break; + // Add parameter types. + Parameters *args = new Parameters; + args->setDim(nparams); - case LIBCALL_ARRAYCATNT: - targs.push (Type::dtypeinfo->type->constOf()); - // Currently 'uint', even if 64-bit - targs.push (Type::tuns32); - treturn = Type::tvoid->arrayOf(); - varargs = true; - break; - - case LIBCALL_ARRAYAPPENDT: - targs.push (Type::dtypeinfo->type); //->constOf()); - targs.push (Type::tint8->arrayOf()->pointerTo()); - targs.push (Type::tint8->arrayOf()); - treturn = Type::tvoid->arrayOf(); - break; - - case LIBCALL_ARRAYAPPENDCTX: - targs.push (Type::dtypeinfo->type->constOf()); - targs.push (Type::tint8->arrayOf()->pointerTo()); - targs.push (Type::tsize_t); - treturn = Type::tint8->arrayOf(); - break; - - case LIBCALL_ARRAYAPPENDCD: - targs.push (Type::tint8->arrayOf()->pointerTo()); - targs.push (Type::tdchar); - treturn = Type::tvoid->arrayOf(); - break; - - case LIBCALL_ARRAYAPPENDWD: - targs.push (Type::tint8->arrayOf()->pointerTo()); - targs.push (Type::tdchar); - treturn = Type::tvoid->arrayOf(); - break; - - case LIBCALL_ARRAYASSIGN: - case LIBCALL_ARRAYCTOR: - targs.push (Type::dtypeinfo->type->constOf()); - targs.push (Type::tvoid->arrayOf()); - targs.push (Type::tvoid->arrayOf()); - treturn = Type::tvoid->arrayOf(); - break; - - case LIBCALL_ARRAYSETASSIGN: - case LIBCALL_ARRAYSETCTOR: - targs.push (Type::tvoidptr); - targs.push (Type::tvoidptr); - targs.push (Type::tsize_t); - targs.push (Type::dtypeinfo->type->constOf()); - treturn = Type::tvoidptr; - break; - - case LIBCALL_THROW: - case LIBCALL_INVARIANT: - targs.push (build_object_type()); - break; - - case LIBCALL_SWITCH_USTRING: - targs.push (Type::twchar->arrayOf()->arrayOf()); - targs.push (Type::twchar->arrayOf()); - treturn = Type::tint32; - break; - - case LIBCALL_SWITCH_DSTRING: - targs.push (Type::tdchar->arrayOf()->arrayOf()); - targs.push (Type::tdchar->arrayOf()); - treturn = Type::tint32; - break; - - case LIBCALL_SWITCH_STRING: - targs.push (Type::tchar->arrayOf()->arrayOf()); - targs.push (Type::tchar->arrayOf()); - treturn = Type::tint32; - break; - - case LIBCALL_ASSOCARRAYLITERALTX: - targs.push (Type::dtypeinfo->type->constOf()); - targs.push (Type::tvoid->arrayOf()); - targs.push (Type::tvoid->arrayOf()); - treturn = Type::tvoidptr; - break; - - case LIBCALL_ARRAYLITERALTX: - targs.push (Type::dtypeinfo->type->constOf()); - targs.push (Type::tsize_t); - treturn = Type::tvoidptr; - break; - - case LIBCALL_HIDDEN_FUNC: - /* Argument is an Object, but can't use that as - LIBCALL_HIDDEN_FUNC is needed before the Object type is - created. */ - targs.push (Type::tvoidptr); - break; - - default: - gcc_unreachable(); - } - - // Add parameter types. - Parameters *args = new Parameters; - args->setDim (targs.dim); - for (size_t i = 0; i < targs.dim; i++) - (*args)[i] = new Parameter (0, targs[i], NULL, NULL); + va_list ap; + va_start (ap, nparams); + for (int i = 0; i < nparams; i++) + (*args)[i] = new Parameter(0, va_arg(ap, Type *), NULL, NULL); + va_end(ap); + + // Build extern(C) function. + FuncDeclaration *decl = FuncDeclaration::genCfunc(args, type, name); + + // Apply flags to the decl. + tree t = decl->toSymbol()->Stree; + DECL_ARTIFICIAL(t) = 1; + + // Whether the function accepts a variable list of arguments. + TypeFunction *tf = (TypeFunction *) decl->type; + tf->varargs = (flags & LCFvarargs); + // Whether the function does not return except through catching a thrown exception. + TREE_THIS_VOLATILE(t) = (flags & LCFthrows); + // Whether the function performs a malloc-like operation. + DECL_IS_MALLOC(t) = (flags & LCFmalloc); - // Build extern(C) function. - decl = FuncDeclaration::genCfunc (args, treturn, libcall_ids[libcall]); + return decl; +} - TypeFunction *tf = (TypeFunction *) decl->type; - tf->varargs = varargs ? 1 : 0; - libcall_decls[libcall] = decl; +// Library functions are generated as needed. +// This could probably be changed in the future to be more like GCC builtin +// trees, but we depend on runtime initialisation of front-end types. - tree t = decl->toSymbol()->Stree; +FuncDeclaration * +get_libcall(LibCall libcall) +{ + // Build generic AA type void*[void*] for runtime.def + static Type *AA = NULL; + if (AA == NULL) + AA = new TypeAArray(Type::tvoidptr, Type::tvoidptr); - // Function does not return except through catching a thrown exception. - if (fnthrows) - TREE_THIS_VOLATILE (t) = 1; + switch (libcall) + { +#define DEF_D_RUNTIME(CODE, NAME, PARAMS, TYPE, FLAGS) \ + case LIBCALL_ ## CODE: \ + libcall_decls[libcall] = get_libcall(NAME, TYPE, FLAGS, PARAMS); \ + break; +#include "runtime.def" +#undef DEF_D_RUNTIME - // Function performs a malloc-like operation. - if (fnmalloc) - DECL_IS_MALLOC (t) = 1; + default: + gcc_unreachable(); } - return decl; + return libcall_decls[libcall]; } // Build call to LIBCALL. N_ARGS is the number of call arguments which are @@ -2430,9 +2155,10 @@ get_libcall (LibCall libcall) tree build_libcall (LibCall libcall, unsigned n_args, tree *args, tree force_type) { - FuncDeclaration *lib_decl = get_libcall (libcall); - Type *type = lib_decl->type->nextOf(); - tree callee = build_address (lib_decl->toSymbol()->Stree); + // Build the call expression to the runtime function. + FuncDeclaration *decl = get_libcall(libcall); + Type *type = decl->type->nextOf(); + tree callee = build_address (decl->toSymbol()->Stree); tree arg_list = NULL_TREE; for (int i = n_args - 1; i >= 0; i--) @@ -2484,9 +2210,9 @@ d_build_call_nary (tree callee, int n_args, ...) enum intrinsic_code { -#define DEF_INTRINSIC(CODE, A, N, M, D) CODE, -#include "d-intrinsics.def" -#undef DEF_INTRINSIC +#define DEF_D_INTRINSIC(CODE, A, N, M, D) INTRINSIC_ ## CODE, +#include "intrinsics.def" +#undef DEF_D_INTRINSIC INTRINSIC_LAST }; @@ -2509,9 +2235,10 @@ struct intrinsic_decl static const intrinsic_decl intrinsic_decls[] = { -#define DEF_INTRINSIC(CODE, ALIAS, NAME, MODULE, DECO) { ALIAS, NAME, MODULE, DECO }, -#include "d-intrinsics.def" -#undef DEF_INTRINSIC +#define DEF_D_INTRINSIC(CODE, ALIAS, NAME, MODULE, DECO) \ + { INTRINSIC_ ## ALIAS, NAME, MODULE, DECO }, +#include "intrinsics.def" +#undef DEF_D_INTRINSIC }; // Call an fold the intrinsic call CALLEE with the argument ARG @@ -2609,34 +2336,36 @@ expand_intrinsic_bt (intrinsic_code intrinsic, tree callee, tree arg1, tree arg2 // => return arg = (T) VA_ARG_EXP; static tree -expand_intrinsic_vaarg (tree callee, tree arg1, tree arg2) +expand_intrinsic_vaarg(tree callee, tree arg1, tree arg2) { tree type; - STRIP_NOPS (arg1); + STRIP_NOPS(arg1); - if (TREE_CODE (arg1) == ADDR_EXPR) - arg1 = TREE_OPERAND (arg1, 0); + if (TREE_CODE(arg1) == ADDR_EXPR) + arg1 = TREE_OPERAND(arg1, 0); + else if (TREE_CODE(TREE_TYPE(arg1)) == REFERENCE_TYPE) + arg1 = build_deref(arg1); if (arg2 == NULL_TREE) - type = TREE_TYPE (callee); + type = TREE_TYPE(callee); else { - STRIP_NOPS (arg2); - gcc_assert (TREE_CODE (arg2) == ADDR_EXPR); - arg2 = TREE_OPERAND (arg2, 0); - type = TREE_TYPE (arg2); + STRIP_NOPS(arg2); + gcc_assert(TREE_CODE(arg2) == ADDR_EXPR); + arg2 = TREE_OPERAND(arg2, 0); + type = TREE_TYPE(arg2); } // Silently convert promoted types. - tree ptype = lang_hooks.types.type_promotes_to (type); - tree exp = build1 (VA_ARG_EXPR, ptype, arg1); + tree ptype = lang_hooks.types.type_promotes_to(type); + tree exp = build1(VA_ARG_EXPR, ptype, arg1); if (type != ptype) - exp = fold_convert (type, exp); + exp = fold_convert(type, exp); if (arg2 != NULL_TREE) - exp = vmodify_expr (arg2, exp); + exp = vmodify_expr(arg2, exp); return exp; } @@ -2671,21 +2400,6 @@ maybe_set_intrinsic (FuncDeclaration *decl) if (!decl->ident || decl->builtin == BUILTINyes) return; - // It's a runtime library function, add to libcall_decls. - LibCall libcall = (LibCall) binary (decl->ident->string, libcall_ids, LIBCALL_count); - if (libcall != LIBCALL_NONE) - { - if (libcall_decls[libcall] == decl) - return; - - // This should have been done either by the front-end or get_libcall. - TypeFunction *tf = (TypeFunction *) decl->type; - gcc_assert (tf->parameters != NULL); - - libcall_decls[libcall] = decl; - return; - } - // Check if it's a compiler intrinsic. We only require that any // internally recognised intrinsics are declared in a module with // an explicit module declaration. @@ -2903,7 +2617,7 @@ build_typeinfo (Type *t) // Different from the generic exception pointer. tree -build_exception_object (void) +build_exception_object() { tree obj_type = build_object_type()->toCtype(); @@ -3254,11 +2968,81 @@ build_frame_type (FuncDeclaration *func) return frame_rec_type; } +// Closures are implemented by taking the local variables that +// need to survive the scope of the function, and copying them +// into a gc allocated chuck of memory. That chunk, called the +// closure here, is inserted into the linked list of stack +// frames instead of the usual stack frame. + +// If a closure is not required, but FD still needs a frame to lower +// nested refs, then instead build custom static chain decl on stack. + +void +build_closure(FuncDeclaration *fd, IRState *irs) +{ + FuncFrameInfo *ffi = get_frameinfo(fd); + + if (!ffi->creates_frame) + return; + + tree type = build_frame_type(fd); + gcc_assert(COMPLETE_TYPE_P(type)); + + tree decl, decl_ref; + + if (ffi->is_closure) + { + decl = build_local_temp(build_pointer_type(type)); + DECL_NAME(decl) = get_identifier("__closptr"); + decl_ref = build_deref(decl); + + // Allocate memory for closure. + tree arg = convert(Type::tsize_t->toCtype(), TYPE_SIZE_UNIT(type)); + tree init = build_libcall(LIBCALL_ALLOCMEMORY, 1, &arg); + + DECL_INITIAL(decl) = build_nop(TREE_TYPE(decl), init); + } + else + { + decl = build_local_temp(type); + DECL_NAME(decl) = get_identifier("__frame"); + decl_ref = decl; + } + + DECL_IGNORED_P(decl) = 0; + expand_decl(decl); + + // Set the first entry to the parent closure/frame, if any. + tree chain_field = component_ref(decl_ref, TYPE_FIELDS(type)); + tree chain_expr = vmodify_expr(chain_field, irs->sthis); + irs->addExp(chain_expr); + + // Copy parameters that are referenced nonlocally. + for (size_t i = 0; i < fd->closureVars.dim; i++) + { + VarDeclaration *v = fd->closureVars[i]; + + if (!v->isParameter()) + continue; + + Symbol *vsym = v->toSymbol(); + + tree field = component_ref (decl_ref, vsym->SframeField); + tree expr = vmodify_expr (field, vsym->Stree); + irs->addExp (expr); + } + + if (!ffi->is_closure) + decl = build_address (decl); + + irs->sthis = decl; +} + // Return the frame of FD. This could be a static chain or a closure // passed via the hidden 'this' pointer. FuncFrameInfo * -get_frameinfo (FuncDeclaration *fd) +get_frameinfo(FuncDeclaration *fd) { Symbol *fds = fd->toSymbol(); if (fds->frameInfo) @@ -3390,37 +3174,19 @@ get_framedecl (FuncDeclaration *inner, FuncDeclaration *outer) } } -// Construct a WrappedExp, whose components are an EXP_NODE, which contains -// a list of instructions in GCC to be passed through. - -WrappedExp::WrappedExp (Loc loc, TOK op, tree exp_node, Type *type) - : Expression (loc, op, sizeof (WrappedExp)) -{ - this->exp_node = exp_node; - this->type = type; -} - -// Write C-style representation of WrappedExp to BUF. - -void -WrappedExp::toCBuffer (OutBuffer *buf, HdrGenState *hgs ATTRIBUTE_UNUSED) -{ - buf->printf (""); -} - // Build and return expression tree for WrappedExp. elem * WrappedExp::toElem (IRState *) { - return exp_node; + return this->e1; } // Write out all fields for aggregate DECL. For classes, write // out base class fields first, and adds all interfaces last. void -layout_aggregate_type (AggLayout *al, AggregateDeclaration *decl) +layout_aggregate_type(AggLayout *al, AggregateDeclaration *decl) { ClassDeclaration *cd = decl->isClassDeclaration(); bool inherited_p = (al->decl != decl); @@ -3428,32 +3194,32 @@ layout_aggregate_type (AggLayout *al, AggregateDeclaration *decl) if (cd != NULL) { if (cd->baseClass) - layout_aggregate_type (al, cd->baseClass); + layout_aggregate_type(al, cd->baseClass); else { // This is the base class (Object) or interface. - tree objtype = TREE_TYPE (cd->type->toCtype()); + tree objtype = TREE_TYPE(cd->type->toCtype()); // Add the virtual table pointer, and optionally the monitor fields. - tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL, - get_identifier ("__vptr"), vtbl_ptr_type_node); - DECL_ARTIFICIAL (field) = 1; - DECL_IGNORED_P (field) = inherited_p; + tree field = build_decl(UNKNOWN_LOCATION, FIELD_DECL, + get_identifier("__vptr"), vtbl_ptr_type_node); + DECL_ARTIFICIAL(field) = 1; + DECL_IGNORED_P(field) = inherited_p; - insert_aggregate_field (al, field, 0); + insert_aggregate_field(al, field, 0); - DECL_VIRTUAL_P (field) = 1; - DECL_FCONTEXT (field) = objtype; - TYPE_VFIELD (al->type) = field; + DECL_VIRTUAL_P(field) = 1; + DECL_FCONTEXT(field) = objtype; + TYPE_VFIELD(al->type) = field; - if (cd->cpp == false) + if (!cd->cpp) { - field = build_decl (UNKNOWN_LOCATION, FIELD_DECL, - get_identifier ("__monitor"), ptr_type_node); - DECL_FCONTEXT (field) = objtype; - DECL_ARTIFICIAL (field) = 1; - DECL_IGNORED_P (field) = inherited_p; - insert_aggregate_field (al, field, Target::ptrsize); + field = build_decl(UNKNOWN_LOCATION, FIELD_DECL, + get_identifier("__monitor"), ptr_type_node); + DECL_FCONTEXT(field) = objtype; + DECL_ARTIFICIAL(field) = 1; + DECL_IGNORED_P(field) = inherited_p; + insert_aggregate_field(al, field, Target::ptrsize); } } } @@ -3462,42 +3228,42 @@ layout_aggregate_type (AggLayout *al, AggregateDeclaration *decl) { tree fcontext = decl->type->toCtype(); - if (POINTER_TYPE_P (fcontext)) - fcontext = TREE_TYPE (fcontext); + if (POINTER_TYPE_P(fcontext)) + fcontext = TREE_TYPE(fcontext); for (size_t i = 0; i < decl->fields.dim; i++) { // D anonymous unions just put the fields into the outer struct... // Does this cause problems? VarDeclaration *var = decl->fields[i]; - gcc_assert (var && var->isField()); + gcc_assert(var && var->isField()); - tree ident = var->ident ? get_identifier (var->ident->string) : NULL_TREE; - tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL, ident, - declaration_type (var)); - set_decl_location (field, var); + tree ident = var->ident ? get_identifier(var->ident->string) : NULL_TREE; + tree field = build_decl(UNKNOWN_LOCATION, FIELD_DECL, ident, + declaration_type(var)); + set_decl_location(field, var); var->csym = new Symbol; var->csym->Stree = field; - DECL_CONTEXT (field) = al->type; - DECL_FCONTEXT (field) = fcontext; - DECL_FIELD_OFFSET (field) = size_int (var->offset); - DECL_FIELD_BIT_OFFSET (field) = bitsize_zero_node; + DECL_CONTEXT(field) = al->type; + DECL_FCONTEXT(field) = fcontext; + DECL_FIELD_OFFSET(field) = size_int(var->offset); + DECL_FIELD_BIT_OFFSET(field) = bitsize_zero_node; - DECL_ARTIFICIAL (field) = inherited_p; - DECL_IGNORED_P (field) = inherited_p; - SET_DECL_OFFSET_ALIGN (field, TYPE_ALIGN (TREE_TYPE (field))); + DECL_ARTIFICIAL(field) = inherited_p; + DECL_IGNORED_P(field) = inherited_p; + SET_DECL_OFFSET_ALIGN(field, TYPE_ALIGN(TREE_TYPE(field))); - TREE_THIS_VOLATILE (field) = TYPE_VOLATILE (TREE_TYPE (field)); - layout_decl (field, 0); + TREE_THIS_VOLATILE(field) = TYPE_VOLATILE(TREE_TYPE(field)); + layout_decl(field, 0); - if (var->size (var->loc)) + if (var->size(var->loc)) { - gcc_assert (DECL_MODE (field) != VOIDmode); - gcc_assert (DECL_SIZE (field) != NULL_TREE); + gcc_assert(DECL_MODE(field) != VOIDmode); + gcc_assert(DECL_SIZE(field) != NULL_TREE); } - TYPE_FIELDS(al->type) = chainon (TYPE_FIELDS (al->type), field); + TYPE_FIELDS(al->type) = chainon(TYPE_FIELDS(al->type), field); } } @@ -3506,11 +3272,11 @@ layout_aggregate_type (AggLayout *al, AggregateDeclaration *decl) for (size_t i = 0; i < cd->vtblInterfaces->dim; i++) { BaseClass *bc = (*cd->vtblInterfaces)[i]; - tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, - Type::tvoidptr->pointerTo()->toCtype()); - DECL_ARTIFICIAL (field) = 1; - DECL_IGNORED_P (field) = 1; - insert_aggregate_field (al, field, bc->offset); + tree field = build_decl(UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, + Type::tvoidptr->pointerTo()->toCtype()); + DECL_ARTIFICIAL(field) = 1; + DECL_IGNORED_P(field) = 1; + insert_aggregate_field(al, field, bc->offset); } } } diff --git a/gcc/d/d-codegen.h b/gcc/d/d-codegen.h index a4779bb07..d0007b33a 100644 --- a/gcc/d/d-codegen.h +++ b/gcc/d/d-codegen.h @@ -21,62 +21,26 @@ #include "d-irstate.h" #include "d-objfile.h" -enum LibCall -{ - LIBCALL_NONE = -1, - LIBCALL_INVARIANT, - - LIBCALL_AADELX, LIBCALL_AAEQUAL, - LIBCALL_AAGETRVALUEX, LIBCALL_AAGETX, - LIBCALL_AAINX, - - LIBCALL_ADCMP2, LIBCALL_ADEQ2, - - LIBCALL_ALLOCMEMORY, - LIBCALL_ARRAY_BOUNDS, - - LIBCALL_ARRAYAPPENDT, LIBCALL_ARRAYAPPENDCTX, - LIBCALL_ARRAYAPPENDCD, LIBCALL_ARRAYAPPENDWD, - LIBCALL_ARRAYASSIGN, - LIBCALL_ARRAYCAST, - LIBCALL_ARRAYCATT, LIBCALL_ARRAYCATNT, - LIBCALL_ARRAYCOPY, - LIBCALL_ARRAYCTOR, - LIBCALL_ARRAYLITERALTX, - LIBCALL_ARRAYSETASSIGN, LIBCALL_ARRAYSETCTOR, - LIBCALL_ARRAYSETLENGTHT, LIBCALL_ARRAYSETLENGTHIT, - - LIBCALL_ASSERT, - LIBCALL_ASSERT_MSG, - - LIBCALL_ASSOCARRAYLITERALTX, - - LIBCALL_CALLFINALIZER, - LIBCALL_CALLINTERFACEFINALIZER, - - LIBCALL_DELARRAY, LIBCALL_DELARRAYT, - LIBCALL_DELCLASS, LIBCALL_DELINTERFACE, - LIBCALL_DELMEMORY, +// D library function flags. - LIBCALL_DYNAMIC_CAST, - LIBCALL_HIDDEN_FUNC, - LIBCALL_INTERFACE_CAST, - - LIBCALL_NEWARRAYT, LIBCALL_NEWARRAYIT, - LIBCALL_NEWARRAYMTX, LIBCALL_NEWARRAYMITX, - LIBCALL_NEWCLASS, LIBCALL_NEWITEMT, - LIBCALL_NEWITEMIT, +enum LibCallFlag +{ + LCFnone = 0, + LCFthrows = 1 << 1, + LCFmalloc = 1 << 2, + LCFvarargs = 1 << 4, +}; - LIBCALL_SWITCH_DSTRING, LIBCALL_SWITCH_ERROR, - LIBCALL_SWITCH_STRING, LIBCALL_SWITCH_USTRING, +// List of codes for internally recognised D library functions. - LIBCALL_THROW, - LIBCALL_UNITTEST, - LIBCALL_UNITTEST_MSG, +enum LibCall +{ +#define DEF_D_RUNTIME(CODE, N, P, T, F) LIBCALL_ ## CODE, +#include "runtime.def" +#undef DEF_D_RUNTIME LIBCALL_count }; - struct FuncFrameInfo { bool creates_frame; // Function creates nested frame. @@ -117,7 +81,7 @@ extern tree insert_type_modifiers (tree type, unsigned mod); extern tree build_two_field_type (tree t1, tree t2, Type *type, const char *n1, const char *n2); -extern tree build_exception_object (void); +extern tree build_exception_object(); extern tree build_float_modulus (tree type, tree t1, tree t2); extern tree indirect_ref (tree type, tree exp); @@ -158,6 +122,7 @@ extern tree void_okay_p (tree t); // Various expressions extern tree build_binary_op (tree_code code, tree type, tree arg0, tree arg1); +extern tree build_binop_assignment(tree_code code, Expression *e1, Expression *e2); extern tree build_array_index (tree ptr, tree index); extern tree build_offset_op (tree_code op, tree ptr, tree idx); extern tree build_offset (tree ptr_node, tree byte_offset); @@ -171,23 +136,24 @@ extern tree d_build_call_nary (tree callee, int n_args, ...); extern tree d_assert_call (Loc loc, LibCall libcall, tree msg = NULL_TREE); // Closures and frame generation. -extern tree build_frame_type (FuncDeclaration *func); -extern FuncFrameInfo *get_frameinfo (FuncDeclaration *fd); -extern tree get_framedecl (FuncDeclaration *inner, FuncDeclaration *outer); +extern tree build_frame_type(FuncDeclaration *func); +extern void build_closure(FuncDeclaration *fd, IRState *irs); +extern FuncFrameInfo *get_frameinfo(FuncDeclaration *fd); +extern tree get_framedecl(FuncDeclaration *inner, FuncDeclaration *outer); -extern tree build_vthis (AggregateDeclaration *decl, FuncDeclaration *fd); +extern tree build_vthis(AggregateDeclaration *decl, FuncDeclaration *fd); // Static chain for nested functions -extern tree get_frame_for_symbol (FuncDeclaration *func, Dsymbol *sym); +extern tree get_frame_for_symbol(FuncDeclaration *func, Dsymbol *sym); // Local variables -extern void build_local_var (VarDeclaration *vd, FuncDeclaration *fd); -extern tree build_local_temp (tree type); -extern tree create_temporary_var (tree type); -extern tree maybe_temporary_var (tree exp, tree *out_var); -extern void expand_decl (tree decl); +extern void build_local_var(VarDeclaration *vd, FuncDeclaration *fd); +extern tree build_local_temp(tree type); +extern tree create_temporary_var(tree type); +extern tree maybe_temporary_var(tree exp, tree *out_var); +extern void expand_decl(tree decl); -extern tree get_decl_tree (Declaration *decl, FuncDeclaration *func); +extern tree get_decl_tree(Declaration *decl, FuncDeclaration *func); // Temporaries (currently just SAVE_EXPRs) extern tree make_temp (tree t); @@ -195,9 +161,7 @@ extern tree maybe_make_temp (tree t); extern bool d_has_side_effects (tree t); // Array operations -extern bool unhandled_arrayop_p (BinExp *exp); - -extern bool array_bounds_check (void); +extern bool array_bounds_check(); extern tree d_checked_index (Loc loc, tree index, tree upr, bool inclusive); extern tree d_bounds_condition (tree index, tree upr, bool inclusive); @@ -254,7 +218,7 @@ lang_ddecl (tree t) // Returns D frontend type 'Object' which all classes are derived from. inline Type * -build_object_type (void) +build_object_type() { if (ClassDeclaration::object) return ClassDeclaration::object->type; @@ -353,15 +317,6 @@ extern IRState *current_irstate; // Various helpers that need extra state -class WrappedExp : public Expression -{ -public: - tree exp_node; - WrappedExp (Loc loc, TOK op, tree exp_node, Type *type); - void toCBuffer (OutBuffer *buf, HdrGenState *hgs); - elem *toElem (IRState *irs); -}; - struct AggLayout { AggLayout (AggregateDeclaration *indecl, tree intype) @@ -389,7 +344,7 @@ class ArrayScope class AddrOfExpr { public: - AddrOfExpr (void) + AddrOfExpr() { this->var_ = NULL_TREE; } tree set (tree exp) @@ -409,10 +364,10 @@ class CallExpr : ce_(ce), argi_(0) { } - tree callee (void) + tree callee() { return CALL_EXPR_FN (this->ce_); } - tree nextArg (void) + tree nextArg() { tree result = this->argi_ < call_expr_nargs (this->ce_) ? CALL_EXPR_ARG (this->ce_, this->argi_) : NULL_TREE; @@ -421,7 +376,7 @@ class CallExpr } private: - CallExpr (void) + CallExpr() { } tree ce_; diff --git a/gcc/d/d-convert.cc b/gcc/d/d-convert.cc index f608004b9..a9c11cc6e 100644 --- a/gcc/d/d-convert.cc +++ b/gcc/d/d-convert.cc @@ -23,98 +23,98 @@ // This function implements all reasonable scalar conversions. static tree -d_convert_basic (tree type, tree expr) +d_convert_basic(tree type, tree expr) { tree e = expr; - tree_code code = TREE_CODE (type); + tree_code code = TREE_CODE(type); if (type == error_mark_node || expr == error_mark_node - || TREE_TYPE (expr) == error_mark_node) + || TREE_TYPE(expr) == error_mark_node) return error_mark_node; const char *invalid_conv_diag - = targetm.invalid_conversion (TREE_TYPE (expr), type); + = targetm.invalid_conversion(TREE_TYPE(expr), type); if (invalid_conv_diag) { - error ("%s", invalid_conv_diag); + error("%s", invalid_conv_diag); return error_mark_node; } - if (type == TREE_TYPE (expr)) + if (type == TREE_TYPE(expr)) return expr; - if (TREE_CODE (type) == ARRAY_TYPE - && TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE - && TYPE_DOMAIN (type) == TYPE_DOMAIN (TREE_TYPE (expr))) + if (TREE_CODE(type) == ARRAY_TYPE + && TREE_CODE(TREE_TYPE(expr)) == ARRAY_TYPE + && TYPE_DOMAIN(type) == TYPE_DOMAIN(TREE_TYPE(expr))) return expr; - tree ret = targetm.convert_to_type (type, expr); + tree ret = targetm.convert_to_type(type, expr); if (ret) return ret; - STRIP_TYPE_NOPS (e); - tree etype = TREE_TYPE (e); + STRIP_TYPE_NOPS(e); + tree etype = TREE_TYPE(e); - if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (TREE_TYPE (expr))) - return fold_convert (type, expr); - if (TREE_CODE (TREE_TYPE (expr)) == ERROR_MARK) + if (TYPE_MAIN_VARIANT(type) == TYPE_MAIN_VARIANT(TREE_TYPE(expr))) + return fold_convert(type, expr); + if (TREE_CODE(TREE_TYPE(expr)) == ERROR_MARK) return error_mark_node; - if (TREE_CODE (TREE_TYPE (expr)) == VOID_TYPE) + if (TREE_CODE(TREE_TYPE(expr)) == VOID_TYPE) { - error ("void value not ignored as it ought to be"); + error("void value not ignored as it ought to be"); return error_mark_node; } switch (code) { case VOID_TYPE: - return fold_convert (type, e); + return fold_convert(type, e); case INTEGER_TYPE: case ENUMERAL_TYPE: - if (TREE_CODE (etype) == POINTER_TYPE - || TREE_CODE (etype) == REFERENCE_TYPE) + if (TREE_CODE(etype) == POINTER_TYPE + || TREE_CODE(etype) == REFERENCE_TYPE) { - if (integer_zerop (e)) - return build_int_cst (type, 0); + if (integer_zerop(e)) + return build_int_cst(type, 0); // Convert to an unsigned integer of the correct width first, and // from there widen/truncate to the required type. - tree utype = lang_hooks.types.type_for_size (TYPE_PRECISION (etype), 1); - ret = fold_build1 (CONVERT_EXPR, utype, e); - return fold_convert (type, ret); + tree utype = lang_hooks.types.type_for_size(TYPE_PRECISION(etype), 1); + ret = fold_build1(CONVERT_EXPR, utype, e); + return fold_convert(type, ret); } - ret = convert_to_integer (type, e); + ret = convert_to_integer(type, e); goto maybe_fold; case BOOLEAN_TYPE: - return fold_convert (type, d_truthvalue_conversion (expr)); + return fold_convert(type, d_truthvalue_conversion(expr)); case POINTER_TYPE: case REFERENCE_TYPE: - ret = convert_to_pointer (type, e); + ret = convert_to_pointer(type, e); goto maybe_fold; case REAL_TYPE: - ret = convert_to_real (type, e); + ret = convert_to_real(type, e); goto maybe_fold; case COMPLEX_TYPE: - ret = convert_to_complex (type, e); + ret = convert_to_complex(type, e); goto maybe_fold; case VECTOR_TYPE: - ret = convert_to_vector (type, e); + ret = convert_to_vector(type, e); goto maybe_fold; case RECORD_TYPE: case UNION_TYPE: - if (lang_hooks.types_compatible_p (type, TREE_TYPE (expr))) + if (lang_hooks.types_compatible_p(type, TREE_TYPE(expr))) { - ret = build1 (VIEW_CONVERT_EXPR, type, expr); + ret = build1(VIEW_CONVERT_EXPR, type, expr); goto maybe_fold; } break; @@ -123,12 +123,12 @@ d_convert_basic (tree type, tree expr) break; maybe_fold: - if (!TREE_CONSTANT (ret)) - ret = fold (ret); + if (!TREE_CONSTANT(ret)) + ret = fold(ret); return ret; } - error ("conversion to non-scalar type requested"); + error("conversion to non-scalar type requested"); return error_mark_node; } diff --git a/gcc/d/d-ctype.cc b/gcc/d/d-ctype.cc index 440bc5030..23dadfdca 100644 --- a/gcc/d/d-ctype.cc +++ b/gcc/d/d-ctype.cc @@ -24,7 +24,7 @@ #include "dfrontend/target.h" type * -Type::toCtype (void) +Type::toCtype() { if (!ctype) { @@ -162,52 +162,7 @@ Type::toCtype (void) } type * -Type::toCParamtype (void) -{ - return toCtype(); -} - -type * -TypeTypedef::toCtype (void) -{ - if (!ctype) - { - if (!isNaked()) - { - ctype = castMod(0)->toCtype(); - ctype = insert_type_modifiers (ctype, mod); - } - else - { - tree basetype = sym->basetype->toCtype(); - const char *name = toChars(); - - tree ident = get_identifier (name); - tree type_node = build_variant_type_copy (basetype); - tree type_decl = build_decl (UNKNOWN_LOCATION, TYPE_DECL, ident, type_node); - TYPE_NAME (type_node) = type_decl; - - if (sym->userAttribDecl) - { - Expressions *attrs = sym->userAttribDecl->getAttributes(); - decl_attributes (&type_node, build_attributes (attrs), 0); - } - - ctype = type_node; - } - } - - return ctype; -} - -type * -TypeTypedef::toCParamtype (void) -{ - return toCtype(); -} - -type * -TypeEnum::toCtype (void) +TypeEnum::toCtype() { if (!ctype) { @@ -278,7 +233,7 @@ TypeEnum::toCtype (void) } type * -TypeStruct::toCtype (void) +TypeStruct::toCtype() { if (!ctype) { @@ -315,14 +270,8 @@ TypeStruct::toCtype (void) return ctype; } -Symbol * -TypeClass::toSymbol (void) -{ - return sym->toSymbol(); -} - type * -TypeFunction::toCtype (void) +TypeFunction::toCtype() { if (!ctype) { @@ -407,21 +356,8 @@ TypeFunction::toCtype (void) return ctype; } -RET -TypeFunction::retStyle (void) -{ - /* Return by reference or pointer. */ - if (isref || next->ty == Tclass || next->ty == Tpointer) - return RETregs; - - /* Need the ctype to determine this, but this is called from - the front end before semantic processing is finished. An - accurate value is not currently needed anyway. */ - return RETstack; -} - type * -TypeVector::toCtype (void) +TypeVector::toCtype() { if (!ctype) { @@ -454,7 +390,7 @@ TypeVector::toCtype (void) } type * -TypeSArray::toCtype (void) +TypeSArray::toCtype() { if (!ctype) { @@ -482,13 +418,7 @@ TypeSArray::toCtype (void) } type * -TypeSArray::toCParamtype (void) -{ - return toCtype(); -} - -type * -TypeDArray::toCtype (void) +TypeDArray::toCtype() { if (!ctype) { @@ -513,7 +443,7 @@ TypeDArray::toCtype (void) } type * -TypeAArray::toCtype (void) +TypeAArray::toCtype() { if (!ctype) { @@ -544,7 +474,7 @@ TypeAArray::toCtype (void) } type * -TypePointer::toCtype (void) +TypePointer::toCtype() { if (!ctype) { @@ -563,7 +493,7 @@ TypePointer::toCtype (void) } type * -TypeDelegate::toCtype (void) +TypeDelegate::toCtype() { if (!ctype) { @@ -596,7 +526,7 @@ TypeDelegate::toCtype (void) } type * -TypeClass::toCtype (void) +TypeClass::toCtype() { if (!ctype) { @@ -657,24 +587,3 @@ TypeClass::toCtype (void) return ctype; } - -// These are not used for code generation in glue. - -Symbol * -Type::toSymbol (void) -{ - return NULL; -} - -unsigned -Type::totym (void) -{ - return 0; -} - -unsigned -TypeFunction::totym (void) -{ - return 0; -} - diff --git a/gcc/d/d-decls.cc b/gcc/d/d-decls.cc index 95147d3f9..1b1efdfd7 100644 --- a/gcc/d/d-decls.cc +++ b/gcc/d/d-decls.cc @@ -35,7 +35,7 @@ // Create the symbol with tree for struct initialisers. Symbol * -SymbolDeclaration::toSymbol (void) +SymbolDeclaration::toSymbol() { return dsym->toInitializer(); } @@ -47,7 +47,7 @@ SymbolDeclaration::toSymbol (void) Symbol * Dsymbol::toSymbolX (const char *prefix, int, type *, const char *suffix) { - const char *symbol = mangle(); + const char *symbol = mangle(this); unsigned prefixlen = strlen (prefix); size_t sz = (2 + strlen (symbol) + sizeof (size_t) * 3 + prefixlen + strlen (suffix) + 1); Symbol *s = new Symbol(); @@ -65,7 +65,7 @@ Dsymbol::toSymbolX (const char *prefix, int, type *, const char *suffix) Symbol * -Dsymbol::toSymbol (void) +Dsymbol::toSymbol() { fprintf (global.stdmsg, "Dsymbol::toSymbol() '%s', kind = '%s'\n", toChars(), kind()); gcc_unreachable(); // BUG: implement @@ -76,7 +76,7 @@ Dsymbol::toSymbol (void) // then build the chain of NAMESPACE_DECLs. Symbol * -Dsymbol::toImport (void) +Dsymbol::toImport() { if (!isym) { @@ -94,7 +94,7 @@ Dsymbol::toImport (void) isym->Stree = decl; d_keep (decl); - Loc loc = (m->md != NULL) ? m->md->loc : Loc (m, 1); + Loc loc = (m->md != NULL) ? m->md->loc : Loc(m, 1, 0); set_decl_location (decl, loc); if (output_module_p (m)) @@ -136,7 +136,7 @@ Dsymbol::toImport (Symbol *sym) // Create the symbol with VAR_DECL tree for static variables. Symbol * -VarDeclaration::toSymbol (void) +VarDeclaration::toSymbol() { if (!csym) { @@ -156,7 +156,7 @@ VarDeclaration::toSymbol (void) if (isDataseg()) { - csym->Sident = mangle(); + csym->Sident = mangle(this); csym->prettyIdent = toPrettyChars(); } else @@ -214,10 +214,6 @@ VarDeclaration::toSymbol (void) TREE_READONLY (decl) = 1; else csym->Sreadonly = true; - - // Const doesn't seem to matter for aggregates, so prevent problems. - if (isConst() && isDataseg()) - TREE_CONSTANT (decl) = 1; } // Propagate volatile. @@ -254,7 +250,7 @@ VarDeclaration::toSymbol (void) // Create the symbol with tree for classinfo decls. Symbol * -ClassInfoDeclaration::toSymbol (void) +ClassInfoDeclaration::toSymbol() { return cd->toSymbol(); } @@ -262,7 +258,7 @@ ClassInfoDeclaration::toSymbol (void) // Create the symbol with tree for typeinfo decls. Symbol * -TypeInfoDeclaration::toSymbol (void) +TypeInfoDeclaration::toSymbol() { if (!csym) { @@ -288,7 +284,7 @@ TypeInfoDeclaration::toSymbol (void) // Create the symbol with tree for typeinfoclass decls. Symbol * -TypeInfoClassDeclaration::toSymbol (void) +TypeInfoClassDeclaration::toSymbol() { gcc_assert (tinfo->ty == Tclass); TypeClass *tc = (TypeClass *) tinfo; @@ -299,7 +295,7 @@ TypeInfoClassDeclaration::toSymbol (void) // Create the symbol with tree for function aliases. Symbol * -FuncAliasDeclaration::toSymbol (void) +FuncAliasDeclaration::toSymbol() { return funcalias->toSymbol(); } @@ -307,7 +303,7 @@ FuncAliasDeclaration::toSymbol (void) // Create the symbol with FUNCTION_DECL tree for functions. Symbol * -FuncDeclaration::toSymbol (void) +FuncDeclaration::toSymbol() { if (!csym) { @@ -317,8 +313,15 @@ FuncDeclaration::toSymbol (void) tree fntype = NULL_TREE; tree vindex = NULL_TREE; + // Run full semantic on symbols we need to know about during compilation. + if (inferRetType && type && !type->nextOf() && !functionSemantic()) + { + csym->Stree = error_mark_node; + return csym; + } + // Save mangle/debug names for making thunks. - csym->Sident = mangleExact(); + csym->Sident = mangleExact(this); csym->prettyIdent = toPrettyChars(); tree id = get_identifier (this->isMain() @@ -343,7 +346,7 @@ FuncDeclaration::toSymbol (void) // Do this even if there is no debug info. It is needed to make // sure member functions are not called statically AggregateDeclaration *agg_decl = isMember2(); - tree handle = agg_decl->handle->toCtype(); + tree handle = agg_decl->handleType()->toCtype(); // If handle is a pointer type, get record type. if (!agg_decl->isStructDeclaration()) @@ -391,7 +394,7 @@ FuncDeclaration::toSymbol (void) DECL_NO_INLINE_WARNING_P (fndecl) = 1; } // Don't know what to do with this. - else if (flag_inline_functions && canInline (0, 1, 0)) + else if (flag_inline_functions) { DECL_DECLARED_INLINE_P (fndecl) = 1; DECL_NO_INLINE_WARNING_P (fndecl) = 1; @@ -422,18 +425,6 @@ FuncDeclaration::toSymbol (void) if (storage_class & STCfinal) DECL_FINAL_P (fndecl) = 1; - // Assert contracts in functions cause implicit side effects that could - // cause wrong codegen if pure/nothrow is thrown in the equation. - if (!global.params.useAssert) - { - // Cannot mark as pure as in 'no side effects' if the function either - // returns by ref, or has an internal state 'this'. - // Note, pure D functions don't imply nothrow. - if (isPure() == PUREstrong && vthis == NULL - && ftype->isnothrow && ftype->retStyle() == RETstack) - DECL_PURE_P (fndecl) = 1; - } - #if TARGET_DLLIMPORT_DECL_ATTRIBUTES // Have to test for import first if (isImportedSymbol()) @@ -545,7 +536,7 @@ FuncDeclaration::toThunkSymbol (int offset) // Create the "ClassInfo" symbol for classes. Symbol * -ClassDeclaration::toSymbol (void) +ClassDeclaration::toSymbol() { if (!csym) { @@ -562,7 +553,7 @@ ClassDeclaration::toSymbol (void) DECL_ARTIFICIAL (decl) = 1; // ClassInfo cannot be const data, because we use the monitor on it. - TREE_CONSTANT (decl) = 0; + TREE_READONLY (decl) = 0; } return csym; @@ -571,7 +562,7 @@ ClassDeclaration::toSymbol (void) // Create the "InterfaceInfo" symbol for interfaces. Symbol * -InterfaceDeclaration::toSymbol (void) +InterfaceDeclaration::toSymbol() { if (!csym) { @@ -587,7 +578,7 @@ InterfaceDeclaration::toSymbol (void) set_decl_location (decl, this); DECL_ARTIFICIAL (decl) = 1; - TREE_CONSTANT (decl) = 1; + TREE_READONLY (decl) = 1; } return csym; @@ -596,7 +587,7 @@ InterfaceDeclaration::toSymbol (void) // Create the "ModuleInfo" symbol for a given module. Symbol * -Module::toSymbol (void) +Module::toSymbol() { if (!csym) { @@ -613,7 +604,6 @@ Module::toSymbol (void) DECL_ARTIFICIAL (decl) = 1; // Not readonly, moduleinit depends on this. - TREE_CONSTANT (decl) = 0; TREE_READONLY (decl) = 0; } @@ -621,7 +611,7 @@ Module::toSymbol (void) } Symbol * -StructLiteralExp::toSymbol (void) +StructLiteralExp::toSymbol() { if (!sym) { @@ -650,7 +640,7 @@ StructLiteralExp::toSymbol (void) } Symbol * -ClassReferenceExp::toSymbol (void) +ClassReferenceExp::toSymbol() { if (!value->sym) { @@ -687,7 +677,7 @@ ClassReferenceExp::toSymbol (void) // needed directly (like for rtti comparisons), make it directly accessible. Symbol * -ClassDeclaration::toVtblSymbol (void) +ClassDeclaration::toVtblSymbol() { if (!vtblsym) { @@ -705,10 +695,9 @@ ClassDeclaration::toVtblSymbol (void) setup_symbol_storage (this, decl, true); set_decl_location (decl, this); - TREE_READONLY (decl) = 1; - TREE_CONSTANT (decl) = 1; - TREE_ADDRESSABLE (decl) = 1; - DECL_ARTIFICIAL (decl) = 1; + TREE_READONLY(decl) = 1; + TREE_ADDRESSABLE(decl) = 1; + DECL_ARTIFICIAL(decl) = 1; DECL_CONTEXT (decl) = d_decl_context (this); DECL_ALIGN (decl) = TARGET_VTABLE_ENTRY_ALIGN; @@ -728,7 +717,7 @@ ClassDeclaration::toVtblSymbol (void) // are in the toObjFile phase. Symbol * -AggregateDeclaration::toInitializer (void) +AggregateDeclaration::toInitializer() { if (!sinit) { @@ -757,7 +746,6 @@ AggregateDeclaration::toInitializer (void) TREE_ADDRESSABLE (sinit->Stree) = 1; TREE_READONLY (sinit->Stree) = 1; - TREE_CONSTANT (sinit->Stree) = 1; DECL_ARTIFICIAL (sinit->Stree) = 1; // These initialisers are always global. DECL_CONTEXT (sinit->Stree) = NULL_TREE; @@ -766,37 +754,10 @@ AggregateDeclaration::toInitializer (void) return sinit; } -// Create the static initializer for the typedef variable. - -Symbol * -TypedefDeclaration::toInitializer (void) -{ - if (!sinit) - sinit = toSymbolX ("__init", 0, 0, "Z"); - - if (!sinit->Stree && current_module_decl) - { - sinit->Stree = build_decl (UNKNOWN_LOCATION, VAR_DECL, - get_identifier (sinit->prettyIdent), type->toCtype()); - SET_DECL_ASSEMBLER_NAME (sinit->Stree, get_identifier (sinit->Sident)); - d_keep (sinit->Stree); - - setup_symbol_storage (this, sinit->Stree, true); - set_decl_location (sinit->Stree, this); - - TREE_CONSTANT (sinit->Stree) = 1; - TREE_READONLY (sinit->Stree) = 1; - DECL_ARTIFICIAL (sinit->Stree) = 1; - DECL_CONTEXT (sinit->Stree) = NULL_TREE; - } - - return sinit; -} - // Create the static initializer for the enum. Symbol * -EnumDeclaration::toInitializer (void) +EnumDeclaration::toInitializer() { if (!sinit) { @@ -817,7 +778,6 @@ EnumDeclaration::toInitializer (void) setup_symbol_storage (this, sinit->Stree, true); set_decl_location (sinit->Stree, this); - TREE_CONSTANT (sinit->Stree) = 1; TREE_READONLY (sinit->Stree) = 1; DECL_ARTIFICIAL (sinit->Stree) = 1; DECL_CONTEXT (sinit->Stree) = NULL_TREE; @@ -830,7 +790,7 @@ EnumDeclaration::toInitializer (void) // void -ClassDeclaration::toDebug (void) +ClassDeclaration::toDebug() { tree rec_type = TREE_TYPE (type->toCtype()); build_type_decl (rec_type, this); @@ -838,7 +798,7 @@ ClassDeclaration::toDebug (void) } void -EnumDeclaration::toDebug (void) +EnumDeclaration::toDebug() { TypeEnum *tc = (TypeEnum *) type; if (!tc->sym->defaultval || type->isZeroInit()) @@ -846,22 +806,17 @@ EnumDeclaration::toDebug (void) tree ctype = type->toCtype(); - if (TREE_CODE (ctype) == ENUMERAL_TYPE) - build_type_decl (ctype, this); - // The ctype is not necessarily enum, which doesn't sit well with - // rest_of_type_compilation. Can call this on structs though. - if (RECORD_OR_UNION_TYPE_P (ctype) || TREE_CODE (ctype) == ENUMERAL_TYPE) - rest_of_type_compilation (ctype, 1); -} - -void -TypedefDeclaration::toDebug (void) -{ + // rest_of_type_compilation. + if (TREE_CODE (ctype) == ENUMERAL_TYPE) + { + build_type_decl (ctype, this); + rest_of_type_compilation (ctype, 1); + } } void -StructDeclaration::toDebug (void) +StructDeclaration::toDebug() { tree ctype = type->toCtype(); build_type_decl (ctype, this); @@ -872,19 +827,19 @@ StructDeclaration::toDebug (void) // Stubs unused in GDC, but required for D front-end. Symbol * -Module::toModuleAssert (void) +Module::toModuleAssert() { return NULL; } Symbol * -Module::toModuleUnittest (void) +Module::toModuleUnittest() { return NULL; } Symbol * -Module::toModuleArray (void) +Module::toModuleArray() { return NULL; } @@ -895,33 +850,3 @@ TypeAArray::aaGetSymbol (const char *, int) return 0; } -int -Dsymbol::cvMember (unsigned char *) -{ - return 0; -} - -int -EnumDeclaration::cvMember (unsigned char *) -{ - return 0; -} - -int -FuncDeclaration::cvMember (unsigned char *) -{ - return 0; -} - -int -VarDeclaration::cvMember (unsigned char *) -{ - return 0; -} - -int -TypedefDeclaration::cvMember (unsigned char *) -{ - return 0; -} - diff --git a/gcc/d/d-dmd-gcc.h b/gcc/d/d-dmd-gcc.h index 3dc720a60..80c5f47f2 100644 --- a/gcc/d/d-dmd-gcc.h +++ b/gcc/d/d-dmd-gcc.h @@ -26,18 +26,21 @@ #include "mars.h" #include "arraytypes.h" +#include "aggregate.h" -/* used in module.c */ +// Functions defined in the glue interface, but used in the frontend. + +// Used in module.c extern void d_gcc_magic_module (Module *); -/* used in interpret.c */ -extern Expression *d_gcc_eval_builtin (Loc, FuncDeclaration *, Expressions *); -/* used in arrayop.c */ -extern int binary(const char *p , const char **tab, int high); +// Functions defined in the frontend, but used in the glue interface. + +// Used in init.cc +FuncDeclaration *search_toString(StructDeclaration *); -/* used in ctfeexpr.c */ -extern Expression *d_gcc_paint_type (Expression *, Type *); +// Used in d-lang.cc +void initTraitsStringTable(); #endif /* GCC_SAFE_DMD */ diff --git a/gcc/d/d-elem.cc b/gcc/d/d-elem.cc index 5f0467697..beec412e9 100644 --- a/gcc/d/d-elem.cc +++ b/gcc/d/d-elem.cc @@ -379,130 +379,103 @@ CmpExp::toElem (IRState *irs) } elem * -AndAndExp::toElem (IRState *irs) +AndAndExp::toElem(IRState *irs) { if (e2->type->toBasetype()->ty != Tvoid) { - tree t1 = convert_for_condition (e1->toElem (irs), e1->type); - tree t2 = convert_for_condition (e2->toElem (irs), e2->type); + tree t1 = convert_for_condition(e1->toElem(irs), e1->type); + tree t2 = convert_for_condition(e2->toElem(irs), e2->type); - return d_convert (type->toCtype(), build_boolop (TRUTH_ANDIF_EXPR, t1, t2)); + return d_convert(type->toCtype(), build_boolop(TRUTH_ANDIF_EXPR, t1, t2)); } else { - return build3 (COND_EXPR, type->toCtype(), - convert_for_condition (e1->toElem (irs), e1->type), - e2->toElemDtor (irs), void_node); + return build3(COND_EXPR, type->toCtype(), + convert_for_condition(e1->toElem(irs), e1->type), + e2->toElemDtor(irs), void_node); } } elem * -OrOrExp::toElem (IRState *irs) +OrOrExp::toElem(IRState *irs) { if (e2->type->toBasetype()->ty != Tvoid) { - tree t1 = convert_for_condition (e1->toElem (irs), e1->type); - tree t2 = convert_for_condition (e2->toElem (irs), e2->type); + tree t1 = convert_for_condition(e1->toElem(irs), e1->type); + tree t2 = convert_for_condition(e2->toElem(irs), e2->type); - return d_convert (type->toCtype(), build_boolop (TRUTH_ORIF_EXPR, t1, t2)); + return d_convert(type->toCtype(), build_boolop(TRUTH_ORIF_EXPR, t1, t2)); } else { - return build3 (COND_EXPR, type->toCtype(), - build1 (TRUTH_NOT_EXPR, bool_type_node, - convert_for_condition (e1->toElem (irs), e1->type)), - e2->toElemDtor (irs), void_node); + return build3(COND_EXPR, type->toCtype(), + build1(TRUTH_NOT_EXPR, bool_type_node, + convert_for_condition(e1->toElem(irs), e1->type)), + e2->toElemDtor(irs), void_node); } } elem * -XorExp::toElem (IRState *irs) +XorExp::toElem(IRState *irs) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - return build_binary_op (BIT_XOR_EXPR, type->toCtype(), - e1->toElem (irs), e2->toElem (irs)); + return build_binary_op(BIT_XOR_EXPR, type->toCtype(), + e1->toElem(irs), e2->toElem(irs)); } elem * -OrExp::toElem (IRState *irs) +OrExp::toElem(IRState *irs) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - return build_binary_op (BIT_IOR_EXPR, type->toCtype(), - e1->toElem (irs), e2->toElem (irs)); + return build_binary_op(BIT_IOR_EXPR, type->toCtype(), + e1->toElem(irs), e2->toElem(irs)); } elem * -AndExp::toElem (IRState *irs) +AndExp::toElem(IRState *irs) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - return build_binary_op (BIT_AND_EXPR, type->toCtype(), - e1->toElem (irs), e2->toElem (irs)); + return build_binary_op(BIT_AND_EXPR, type->toCtype(), + e1->toElem(irs), e2->toElem(irs)); } elem * -UshrExp::toElem (IRState *irs) +UshrExp::toElem(IRState *irs) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - return build_binary_op (UNSIGNED_RSHIFT_EXPR, type->toCtype(), - e1->toElem (irs), e2->toElem (irs)); + return build_binary_op(UNSIGNED_RSHIFT_EXPR, type->toCtype(), + e1->toElem(irs), e2->toElem(irs)); } elem * -ShrExp::toElem (IRState *irs) +ShrExp::toElem(IRState *irs) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - return build_binary_op (RSHIFT_EXPR, type->toCtype(), - e1->toElem (irs), e2->toElem (irs)); + return build_binary_op(RSHIFT_EXPR, type->toCtype(), + e1->toElem(irs), e2->toElem(irs)); } elem * -ShlExp::toElem (IRState *irs) +ShlExp::toElem(IRState *irs) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - return build_binary_op (LSHIFT_EXPR, type->toCtype(), - e1->toElem (irs), e2->toElem (irs)); + return build_binary_op(LSHIFT_EXPR, type->toCtype(), + e1->toElem(irs), e2->toElem(irs)); } elem * -ModExp::toElem (IRState *irs) +ModExp::toElem(IRState *irs) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - return build_binary_op (e1->type->isfloating() ? FLOAT_MOD_EXPR : TRUNC_MOD_EXPR, - type->toCtype(), e1->toElem (irs), e2->toElem (irs)); + return build_binary_op(e1->type->isfloating() ? FLOAT_MOD_EXPR : TRUNC_MOD_EXPR, + type->toCtype(), e1->toElem(irs), e2->toElem(irs)); } elem * -DivExp::toElem (IRState *irs) +DivExp::toElem(IRState *irs) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - return build_binary_op (e1->type->isintegral() ? TRUNC_DIV_EXPR : RDIV_EXPR, - type->toCtype(), e1->toElem (irs), e2->toElem (irs)); + return build_binary_op(e1->type->isintegral() ? TRUNC_DIV_EXPR : RDIV_EXPR, + type->toCtype(), e1->toElem(irs), e2->toElem(irs)); } elem * -MulExp::toElem (IRState *irs) +MulExp::toElem(IRState *irs) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - return build_binary_op (MULT_EXPR, type->toCtype(), - e1->toElem (irs), e2->toElem (irs)); + return build_binary_op(MULT_EXPR, type->toCtype(), + e1->toElem(irs), e2->toElem(irs)); } elem * @@ -597,7 +570,7 @@ CatExp::toElem(IRState *irs) size_int(1), build_address(expr)); if (var != NULL_TREE) - vec_safe_push (elemvars, var); + vec_safe_push(elemvars, var); } else arg = d_array_convert(oe); @@ -620,213 +593,135 @@ CatExp::toElem(IRState *irs) tree result = build_libcall(n_operands > 2 ? LIBCALL_ARRAYCATNT : LIBCALL_ARRAYCATT, n_args, args, type->toCtype()); - for (size_t i = 0; i < vec_safe_length (elemvars); ++i) - result = bind_expr ((*elemvars)[i], result); + for (size_t i = 0; i < vec_safe_length(elemvars); ++i) + result = bind_expr((*elemvars)[i], result); return result; } elem * -MinExp::toElem (IRState *irs) +MinExp::toElem(IRState *irs) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - // %% faster: check if result is complex if ((e1->type->isreal() && e2->type->isimaginary()) || (e1->type->isimaginary() && e2->type->isreal())) { // %%TODO: need to check size/modes - tree t1 = e1->toElem (irs); - tree t2 = e2->toElem (irs); + tree t1 = e1->toElem(irs); + tree t2 = e2->toElem(irs); - t2 = build1 (NEGATE_EXPR, TREE_TYPE (t2), t2); + t2 = build1(NEGATE_EXPR, TREE_TYPE(t2), t2); if (e1->type->isreal()) - return build2 (COMPLEX_EXPR, type->toCtype(), t1, t2); + return build2(COMPLEX_EXPR, type->toCtype(), t1, t2); else - return build2 (COMPLEX_EXPR, type->toCtype(), t2, t1); + return build2(COMPLEX_EXPR, type->toCtype(), t2, t1); } // The front end has already taken care of pointer-int and pointer-pointer - return build_binary_op (MINUS_EXPR, type->toCtype(), - e1->toElem (irs), e2->toElem (irs)); + return build_binary_op(MINUS_EXPR, type->toCtype(), + e1->toElem(irs), e2->toElem(irs)); } elem * -AddExp::toElem (IRState *irs) +AddExp::toElem(IRState *irs) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - // %% faster: check if result is complex if ((e1->type->isreal() && e2->type->isimaginary()) || (e1->type->isimaginary() && e2->type->isreal())) { // %%TODO: need to check size/modes - tree t1 = e1->toElem (irs); - tree t2 = e2->toElem (irs); + tree t1 = e1->toElem(irs); + tree t2 = e2->toElem(irs); if (e1->type->isreal()) - return build2 (COMPLEX_EXPR, type->toCtype(), t1, t2); + return build2(COMPLEX_EXPR, type->toCtype(), t1, t2); else - return build2 (COMPLEX_EXPR, type->toCtype(), t2, t1); + return build2(COMPLEX_EXPR, type->toCtype(), t2, t1); } // The front end has already taken care of (pointer + integer) - return build_binary_op (PLUS_EXPR, type->toCtype(), - e1->toElem (irs), e2->toElem (irs)); + return build_binary_op(PLUS_EXPR, type->toCtype(), + e1->toElem(irs), e2->toElem(irs)); } elem * -BinExp::toElemBin (IRState *irs, int op) +XorAssignExp::toElem(IRState *) { - tree_code code = (tree_code) op; - - // Skip casts for lhs assignment. - Expression *e1b = e1; - while (e1b->op == TOKcast) - { - CastExp *ce = (CastExp *) e1b; - gcc_assert (d_types_same (ce->type, ce->to)); - e1b = ce->e1; - } - - // Prevent multiple evaluations of LHS, but watch out! - // The LHS expression could be an assignment, to which - // it's operation gets lost during gimplification. - tree lexpr = NULL_TREE; - tree lhs; - - if (e1b->op == TOKcomma) - { - CommaExp *ce = (CommaExp *) e1b; - lexpr = ce->e1->toElem (irs); - lhs = ce->e2->toElem (irs); - } - else - lhs = e1b->toElem (irs); - - // Build assignment expression. Stabilize lhs for assignment. - lhs = stabilize_reference (lhs); - - tree rhs = build_binary_op (code, e1->type->toCtype(), - convert_expr (lhs, e1b->type, e1->type), e2->toElem (irs)); - - tree expr = modify_expr (lhs, convert_expr (rhs, e1->type, e1b->type)); - - if (lexpr) - expr = compound_expr (lexpr, expr); - - return expr; -} - -elem * -XorAssignExp::toElem (IRState *irs) -{ - if (unhandled_arrayop_p (this)) - return error_mark_node; - - tree exp = toElemBin (irs, BIT_XOR_EXPR); - return convert_expr (exp, e1->type, type); + tree exp = build_binop_assignment(BIT_XOR_EXPR, e1, e2); + return convert_expr(exp, e1->type, type); } elem * -OrAssignExp::toElem (IRState *irs) +OrAssignExp::toElem(IRState *) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - tree exp = toElemBin (irs, BIT_IOR_EXPR); - return convert_expr (exp, e1->type, type); + tree exp = build_binop_assignment(BIT_IOR_EXPR, e1, e2); + return convert_expr(exp, e1->type, type); } elem * -AndAssignExp::toElem (IRState *irs) +AndAssignExp::toElem(IRState *) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - tree exp = toElemBin (irs, BIT_AND_EXPR); - return convert_expr (exp, e1->type, type); + tree exp = build_binop_assignment(BIT_AND_EXPR, e1, e2); + return convert_expr(exp, e1->type, type); } elem * -UshrAssignExp::toElem (IRState *irs) +UshrAssignExp::toElem(IRState *) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - // Front-end integer promotions don't work here. - while (e1->op == TOKcast) + Expression *e1b = e1; + while (e1b->op == TOKcast) { - CastExp *ce = (CastExp *) e1; + CastExp *ce = (CastExp *) e1b; gcc_assert(d_types_same(ce->type, ce->to)); - e1 = ce->e1; + e1b = ce->e1; } - tree exp = toElemBin (irs, UNSIGNED_RSHIFT_EXPR); - return convert_expr(exp, e1->type, type); + tree exp = build_binop_assignment(UNSIGNED_RSHIFT_EXPR, e1b, e2); + return convert_expr(exp, e1b->type, type); } elem * -ShrAssignExp::toElem (IRState *irs) +ShrAssignExp::toElem(IRState *) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - tree exp = toElemBin (irs, RSHIFT_EXPR); - return convert_expr (exp, e1->type, type); + tree exp = build_binop_assignment(RSHIFT_EXPR, e1, e2); + return convert_expr(exp, e1->type, type); } elem * -ShlAssignExp::toElem (IRState *irs) +ShlAssignExp::toElem(IRState *) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - tree exp = toElemBin (irs, LSHIFT_EXPR); - return convert_expr (exp, e1->type, type); + tree exp = build_binop_assignment(LSHIFT_EXPR, e1, e2); + return convert_expr(exp, e1->type, type); } elem * -ModAssignExp::toElem (IRState *irs) +ModAssignExp::toElem(IRState *) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - tree exp = toElemBin (irs, e1->type->isfloating() ? - FLOAT_MOD_EXPR : TRUNC_MOD_EXPR); - return convert_expr (exp, e1->type, type); + tree exp = build_binop_assignment(e1->type->isfloating() + ? FLOAT_MOD_EXPR : TRUNC_MOD_EXPR, e1, e2); + return convert_expr(exp, e1->type, type); } elem * -DivAssignExp::toElem (IRState *irs) +DivAssignExp::toElem(IRState *) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - tree exp = toElemBin (irs, e1->type->isintegral() ? - TRUNC_DIV_EXPR : RDIV_EXPR); - return convert_expr (exp, e1->type, type); + tree exp = build_binop_assignment(e1->type->isintegral() + ? TRUNC_DIV_EXPR : RDIV_EXPR, e1, e2); + return convert_expr(exp, e1->type, type); } elem * -MulAssignExp::toElem (IRState *irs) +MulAssignExp::toElem(IRState *) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - tree exp = toElemBin (irs, MULT_EXPR); - return convert_expr (exp, e1->type, type); + tree exp = build_binop_assignment(MULT_EXPR, e1, e2); + return convert_expr(exp, e1->type, type); } elem * -PowAssignExp::toElem (IRState *) +PowAssignExp::toElem(IRState *) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - gcc_unreachable(); } @@ -920,27 +815,21 @@ CatAssignExp::toElem (IRState *irs) } elem * -MinAssignExp::toElem (IRState *irs) +MinAssignExp::toElem (IRState *) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - tree exp = toElemBin (irs, MINUS_EXPR); + tree exp = build_binop_assignment(MINUS_EXPR, e1, e2); return convert_expr (exp, e1->type, type); } elem * -AddAssignExp::toElem (IRState *irs) +AddAssignExp::toElem (IRState *) { - if (unhandled_arrayop_p (this)) - return error_mark_node; - - tree exp = toElemBin (irs, PLUS_EXPR); + tree exp = build_binop_assignment(PLUS_EXPR, e1, e2); return convert_expr (exp, e1->type, type); } elem * -AssignExp::toElem (IRState *irs) +AssignExp::toElem(IRState *irs) { // First, handle special assignment semantics @@ -954,13 +843,13 @@ AssignExp::toElem (IRState *irs) tree args[3]; LibCall libcall; - args[0] = build_typeinfo (ale->e1->type); - args[1] = convert_expr (e2->toElem (irs), e2->type, Type::tsize_t); - args[2] = build_address (ale->e1->toElem (irs)); + args[0] = build_typeinfo(ale->e1->type); + args[1] = convert_expr(e2->toElem(irs), e2->type, Type::tsize_t); + args[2] = build_address(ale->e1->toElem(irs)); libcall = etype->isZeroInit() ? LIBCALL_ARRAYSETLENGTHT : LIBCALL_ARRAYSETLENGTHIT; - tree result = build_libcall (libcall, 3, args); - return d_array_length (result); + tree result = build_libcall(libcall, 3, args); + return d_array_length(result); } // Look for array[] = n; @@ -968,127 +857,112 @@ AssignExp::toElem (IRState *irs) { SliceExp *se = (SliceExp *) e1; Type *stype = se->e1->type->toBasetype(); - Type *tb2 = e2->type->toBasetype(); Type *etype = stype->nextOf()->toBasetype(); - // Optimize static array assignment with array literal. - // Front-end writes these as an assignment of a dynamic - // array literal with a slice. - if (se->lwr == NULL && stype->ty == Tsarray - && e2->op == TOKarrayliteral - && tb2->nextOf()->mutableOf()->implicitConvTo(stype->nextOf())) - { - Expression *e1 = se->e1; - Type *t2save = e2->type; - - // Treat [e2] as a static array literal. - e2->type = stype; - tree t1 = e1->toElem (irs); - tree t2 = convert_for_assignment (e2->toElem (irs), e2->type, e1->type); - tree result = modify_expr (e1->type->toCtype(), t1, t2); - e2->type = t2save; - - return convert_expr (result, e1->type, type); - } - // Determine if we need to do postblit. - int postblit = 0; - - if (needsPostblit (etype) != NULL + bool postblit = false; + if (needsPostblit(etype) != NULL && ((e2->op != TOKslice && e2->isLvalue()) || (e2->op == TOKslice && ((UnaExp *) e2)->e1->isLvalue()) || (e2->op == TOKcast && ((UnaExp *) e2)->e1->isLvalue()))) - postblit = 1; + postblit = true; if (ismemset) { // Set a range of elements to one value. - tree t1 = maybe_make_temp (e1->toElem (irs)); + tree t1 = maybe_make_temp(e1->toElem(irs)); + tree t2 = e2->toElem(irs); - if (op != TOKblit) + if (postblit && op != TOKblit) { - if (postblit) - { - AddrOfExpr aoe; - tree args[4]; - LibCall libcall; - - args[0] = d_array_ptr (t1); - args[1] = aoe.set (e2->toElem (irs)); - args[2] = d_array_length (t1); - args[3] = build_typeinfo (etype); - libcall = (op == TOKconstruct) ? LIBCALL_ARRAYSETCTOR : LIBCALL_ARRAYSETASSIGN; - - tree call = build_libcall (libcall, 4, args); - return compound_expr (aoe.finish (call), t1); - } + AddrOfExpr aoe; + tree args[4]; + LibCall libcall; + + args[0] = d_array_ptr(t1); + args[1] = aoe.set(t2); + args[2] = d_array_length(t1); + args[3] = build_typeinfo(etype); + libcall = (op == TOKconstruct) ? LIBCALL_ARRAYSETCTOR : LIBCALL_ARRAYSETASSIGN; + + tree call = build_libcall(libcall, 4, args); + return compound_expr(aoe.finish(call), t1); } - tree set_exp = irs->doArraySet (d_array_ptr (t1), e2->toElem (irs), d_array_length (t1)); - return compound_expr (set_exp, t1); + tree result = irs->doArraySet(d_array_ptr(t1), t2, d_array_length(t1)); + return compound_expr(result, t1); } - - if (op != TOKblit && postblit) + else { - // Generate _d_arrayassign() or _d_arrayctor() - tree args[3]; - LibCall libcall; + // Perform a memcpy operation. + gcc_assert(e2->type->ty != Tpointer); - args[0] = build_typeinfo (etype); - args[1] = d_array_convert (e1); - args[2] = d_array_convert (e2); - libcall = (op == TOKconstruct) ? LIBCALL_ARRAYCTOR : LIBCALL_ARRAYASSIGN; + if (!postblit && !array_bounds_check()) + { + tree t1 = maybe_make_temp(d_array_convert(e1)); + tree t2 = d_array_convert(e2); + tree size = fold_build2(MULT_EXPR, size_type_node, + d_convert(size_type_node, d_array_length(t1)), + size_int(etype->size())); + + tree result = d_build_call_nary(builtin_decl_explicit(BUILT_IN_MEMCPY), 3, + d_array_ptr(t1), + d_array_ptr(t2), size); + return compound_expr(result, t1); + } + else if (postblit && op != TOKblit) + { + // Generate _d_arrayassign() or _d_arrayctor() + tree args[3]; + LibCall libcall; - return build_libcall (libcall, 3, args, type->toCtype()); - } + args[0] = build_typeinfo(etype); + args[1] = maybe_make_temp(d_array_convert(e1)); + args[2] = d_array_convert(e2); + libcall = (op == TOKconstruct) ? LIBCALL_ARRAYCTOR : LIBCALL_ARRAYASSIGN; - if (array_bounds_check()) - { - tree args[3]; + return build_libcall(libcall, 3, args, type->toCtype()); + } + else + { + // Generate _d_arraycopy() + tree args[3]; - args[0] = build_integer_cst (etype->size(), Type::tsize_t->toCtype()); - args[1] = d_array_convert (e2); - args[2] = d_array_convert (e1); + args[0] = build_integer_cst(etype->size(), Type::tsize_t->toCtype()); + args[1] = d_array_convert(e2); + args[2] = maybe_make_temp(d_array_convert(e1)); - return build_libcall (LIBCALL_ARRAYCOPY, 3, args, type->toCtype()); - } - else - { - tree t1 = maybe_make_temp (d_array_convert (e1)); - tree t2 = d_array_convert (e2); - tree size = fold_build2 (MULT_EXPR, size_type_node, - d_convert (size_type_node, d_array_length (t1)), - size_int (etype->size())); - - tree result = d_build_call_nary (builtin_decl_explicit (BUILT_IN_MEMCPY), 3, - d_array_ptr (t1), - d_array_ptr (t2), size); - return compound_expr (result, t1); + return build_libcall(LIBCALL_ARRAYCOPY, 3, args, type->toCtype()); + } } } - // Assignment that requires post construction. - if (op == TOKconstruct) + // Look for reference initializations + if (op == TOKconstruct && e1->op == TOKvar) { - tree lhs = e1->toElem (irs); - tree rhs = convert_for_assignment (e2->toElem (irs), e2->type, e1->type); - Type *tb1 = e1->type->toBasetype(); - - if (e1->op == TOKvar) + Declaration *decl = ((VarExp *) e1)->var; + if (decl->storage_class & (STCout | STCref)) { - Declaration *decl = ((VarExp *) e1)->var; - // Look for reference initializations - if (decl->storage_class & (STCout | STCref)) - { - // Want reference to lhs, not indirect ref. - lhs = TREE_OPERAND (lhs, 0); - rhs = build_address (rhs); - } + tree t1 = e1->toElem(irs); + tree t2 = convert_for_assignment(e2->toElem(irs), e2->type, e1->type); + // Want reference to lhs, not indirect ref. + t1 = TREE_OPERAND(t1, 0); + t2 = build_address(t2); + + return modify_expr(type->toCtype(), t1, t2); } + } - tree result = modify_expr (type->toCtype(), lhs, rhs); + // Other types of assignments that may require post construction. + Type *tb1 = e1->type->toBasetype(); + + if (tb1->ty == Tstruct) + { + tree t1 = e1->toElem(irs); + tree t2 = convert_for_assignment(e2->toElem(irs), e2->type, e1->type); + tree result = modify_expr(type->toCtype(), t1, t2); - if (tb1->ty == Tstruct && e2->op == TOKint64) + if (e2->op == TOKint64) { // Maybe set-up hidden pointer to outer scope context. StructDeclaration *sd = ((TypeStruct *) tb1)->sym; @@ -1096,23 +970,54 @@ AssignExp::toElem (IRState *irs) if (sd->isNested()) { tree vthis_field = sd->vthis->toSymbol()->Stree; - tree vthis_value = build_vthis (sd, irs->func); + tree vthis_value = build_vthis(sd, irs->func); - tree vthis_exp = modify_expr (component_ref (lhs, vthis_field), vthis_value); - result = compound_expr (result, vthis_exp); + tree vthis_exp = modify_expr(component_ref(t1, vthis_field), vthis_value); + result = compound_expr(result, vthis_exp); } } - return result; } + if (tb1->ty == Tsarray) + { + Type *etype = tb1->nextOf(); + gcc_assert(e2->type->toBasetype()->ty == Tsarray); + + // Determine if we need to do postblit. + bool postblit = false; + if (needsPostblit(etype) != NULL + && ((e2->op != TOKslice && e2->isLvalue()) + || (e2->op == TOKslice && ((UnaExp *) e2)->e1->isLvalue()) + || (e2->op == TOKcast && ((UnaExp *) e2)->e1->isLvalue()))) + postblit = true; + + if (postblit && op != TOKblit) + { + // Generate _d_arrayassign() or _d_arrayctor() + tree t1 = e1->toElem(irs); + tree args[3]; + LibCall libcall; + + args[0] = build_typeinfo(etype); + args[1] = d_array_convert(e1); + args[2] = d_array_convert(e2); + libcall = (op == TOKconstruct) ? LIBCALL_ARRAYCTOR : LIBCALL_ARRAYASSIGN; + + tree result = build_libcall(libcall, 3, args); + return compound_expr(result, t1); + } + } + // Simple assignment - return modify_expr (type->toCtype(), e1->toElem (irs), - convert_for_assignment (e2->toElem (irs), e2->type, e1->type)); + tree t1 = e1->toElem(irs); + tree t2 = convert_for_assignment(e2->toElem(irs), e2->type, e1->type); + + return modify_expr(type->toCtype(), t1, t2); } elem * -PostExp::toElem (IRState *irs) +PostExp::toElem(IRState *irs) { tree result; @@ -1263,6 +1168,20 @@ ArrayLengthExp::toElem (IRState *irs) } } +elem * +DelegatePtrExp::toElem(IRState *irs) +{ + tree t1 = e1->toElem(irs); + return delegate_object(t1); +} + +elem * +DelegateFuncptrExp::toElem(IRState *irs) +{ + tree t1 = e1->toElem(irs); + return delegate_method(t1); +} + elem * SliceExp::toElem (IRState *irs) { @@ -1446,7 +1365,7 @@ DeleteExp::toElem (IRState *irs) { TypeStruct *ts = (TypeStruct *) telem; if (ts->sym->dtor) - ti = tb1->nextOf()->getTypeInfo (NULL)->toElem (irs); + ti = tb1->nextOf()->getTypeInfo(NULL)->toElem(irs); } // call _delarray_t (&t1, ti); @@ -1506,7 +1425,7 @@ elem * ComExp::toElem (IRState *irs) { TY ty1 = e1->type->toBasetype()->ty; - gcc_assert ((ty1 != Tarray) && (ty1 != Tsarray)); + gcc_assert (ty1 != Tarray && ty1 != Tsarray); return build1 (BIT_NOT_EXPR, type->toCtype(), e1->toElem (irs)); } @@ -1515,7 +1434,7 @@ elem * NegExp::toElem (IRState *irs) { TY ty1 = e1->type->toBasetype()->ty; - gcc_assert ((ty1 != Tarray) && (ty1 != Tsarray)); + gcc_assert (ty1 != Tarray && ty1 != Tsarray); return build1 (NEGATE_EXPR, type->toCtype(), e1->toElem (irs)); } @@ -1638,7 +1557,7 @@ CallExp::toElem (IRState *irs) else if (e1b->op == TOKvar) { FuncDeclaration *fd = ((VarExp *) e1b)->var->isFuncDeclaration(); - gcc_assert (fd); + gcc_assert(fd != NULL); tf = get_function_type (fd->type); if (fd->isNested()) @@ -1669,7 +1588,7 @@ CallExp::toElem (IRState *irs) tree exp = d_build_call (tf, callee, object, arguments); if (tf->isref) - exp = build_deref (exp); + exp = build_deref(exp); // Some library calls are defined to return a generic type. // this->type is the real type we want to return. @@ -1763,7 +1682,7 @@ DelegateExp::toElem (IRState *irs) while (!owner->isTemplateInstance() && owner->toParent()) owner = owner->toParent(); if (owner->isTemplateInstance() || owner == irs->mod) - irs->func->deferred.push (func); + irs->deferred.safe_push(func); } if (t->ty == Tclass || t->ty == Tstruct) @@ -1980,7 +1899,7 @@ FuncExp::toElem (IRState *irs) // Emit after current function body has finished. if (irs->func) - irs->func->deferred.push (fd); + irs->deferred.safe_push(fd); // If nested, this will be a trampoline... if (fd->isNested()) @@ -2056,87 +1975,86 @@ SymbolExp::toElem (IRState *irs) } elem * -NewExp::toElem (IRState *irs) +NewExp::toElem(IRState *irs) { Type *tb = type->toBasetype(); LibCall libcall; tree result; if (allocator) - gcc_assert (newargs); + gcc_assert(newargs); // New'ing a class. if (tb->ty == Tclass) { tb = newtype->toBasetype(); - gcc_assert (tb->ty == Tclass); - TypeClass *class_type = (TypeClass *) tb; - ClassDeclaration *class_decl = class_type->sym; + gcc_assert(tb->ty == Tclass); + TypeClass *tclass = (TypeClass *) tb; + ClassDeclaration *cd = tclass->sym; tree new_call; tree setup_exp = NULL_TREE; // type->toCtype() is a REFERENCE_TYPE; we want the RECORD_TYPE - tree rec_type = TREE_TYPE (class_type->toCtype()); + tree rec_type = TREE_TYPE(tclass->toCtype()); // Call allocator (custom allocator or _d_newclass). if (onstack) { - tree stack_var = build_local_temp (rec_type); - expand_decl (stack_var); - new_call = build_address (stack_var); - setup_exp = modify_expr (indirect_ref (rec_type, new_call), - class_decl->toInitializer()->Stree); + tree stack_var = build_local_temp(rec_type); + expand_decl(stack_var); + new_call = build_address(stack_var); + setup_exp = modify_expr(stack_var, cd->toInitializer()->Stree); } else if (allocator) { - new_call = d_build_call (allocator, NULL_TREE, newargs); - new_call = maybe_make_temp (new_call); + new_call = d_build_call(allocator, NULL_TREE, newargs); + new_call = maybe_make_temp(new_call); // copy memory... - setup_exp = modify_expr (indirect_ref (rec_type, new_call), - class_decl->toInitializer()->Stree); + setup_exp = modify_expr(indirect_ref(rec_type, new_call), + cd->toInitializer()->Stree); } else { - tree arg = build_address (class_decl->toSymbol()->Stree); - new_call = build_libcall (LIBCALL_NEWCLASS, 1, &arg); + tree arg = build_address(cd->toSymbol()->Stree); + new_call = build_libcall(LIBCALL_NEWCLASS, 1, &arg); } - new_call = build_nop (tb->toCtype(), new_call); + new_call = build_nop(tb->toCtype(), new_call); // Set vthis for nested classes. - if (class_decl->isNested()) + if (cd->isNested()) { tree vthis_value = NULL_TREE; - tree vthis_field = class_decl->vthis->toSymbol()->Stree; + tree vthis_field = cd->vthis->toSymbol()->Stree; if (thisexp) { ClassDeclaration *thisexp_cd = thisexp->type->isClassHandle(); - Dsymbol *outer = class_decl->toParent2(); + Dsymbol *outer = cd->toParent2(); int offset = 0; - vthis_value = thisexp->toElem (irs); + vthis_value = thisexp->toElem(irs); if (outer != thisexp_cd) { ClassDeclaration *outer_cd = outer->isClassDeclaration(); - gcc_assert (outer_cd->isBaseOf (thisexp_cd, &offset)); + gcc_assert(outer_cd->isBaseOf(thisexp_cd, &offset)); // could just add offset - vthis_value = convert_expr (vthis_value, thisexp->type, outer_cd->type); + vthis_value = convert_expr(vthis_value, thisexp->type, outer_cd->type); } } else - vthis_value = build_vthis (class_decl, irs->func); + vthis_value = build_vthis(cd, irs->func); if (vthis_value) { - new_call = maybe_make_temp (new_call); - vthis_field = component_ref (indirect_ref (rec_type, new_call), vthis_field); - setup_exp = maybe_compound_expr (setup_exp, modify_expr (vthis_field, vthis_value)); + new_call = maybe_make_temp(new_call); + vthis_field = component_ref(indirect_ref(rec_type, new_call), vthis_field); + setup_exp = maybe_compound_expr(setup_exp, modify_expr(vthis_field, vthis_value)); } } - new_call = maybe_compound_expr (setup_exp, new_call); + new_call = maybe_compound_expr(setup_exp, new_call); // Call constructor. if (member) - result = d_build_call (member, new_call, arguments); + result = d_build_call(member, new_call, arguments); else result = new_call; } @@ -2144,8 +2062,8 @@ NewExp::toElem (IRState *irs) else if (tb->ty == Tpointer && tb->nextOf()->toBasetype()->ty == Tstruct) { Type *htype = newtype->toBasetype(); - gcc_assert (htype->ty == Tstruct); - gcc_assert (!onstack); + gcc_assert(htype->ty == Tstruct); + gcc_assert(!onstack); TypeStruct *stype = (TypeStruct *) htype; StructDeclaration *sd = stype->sym; @@ -2156,30 +2074,44 @@ NewExp::toElem (IRState *irs) return d_convert(type->toCtype(), integer_zero_node); if (allocator) - new_call = d_build_call (allocator, NULL_TREE, newargs); + new_call = d_build_call(allocator, NULL_TREE, newargs); else { libcall = htype->isZeroInit() ? LIBCALL_NEWITEMT : LIBCALL_NEWITEMIT; - tree arg = type->getTypeInfo(NULL)->toElem (irs); - new_call = build_libcall (libcall, 1, &arg); + tree arg = type->getTypeInfo(NULL)->toElem(irs); + new_call = build_libcall(libcall, 1, &arg); } - new_call = maybe_make_temp (new_call); - new_call = build_nop (tb->toCtype(), new_call); + new_call = maybe_make_temp(new_call); + new_call = build_nop(tb->toCtype(), new_call); - // Set vthis for nested structs/classes. - if (sd->isNested()) + if (member || !arguments) { - tree vthis_value = build_vthis (sd, irs->func); - tree vthis_field = component_ref (indirect_ref (stype->toCtype(), new_call), - sd->vthis->toSymbol()->Stree); - new_call = compound_expr (modify_expr (vthis_field, vthis_value), new_call); - } + // Set vthis for nested structs. + if (sd->isNested()) + { + tree vthis_value = build_vthis(sd, irs->func); + tree vthis_field = component_ref(indirect_ref(stype->toCtype(), new_call), + sd->vthis->toSymbol()->Stree); + new_call = compound_expr(modify_expr(vthis_field, vthis_value), new_call); + } - // Call constructor. - if (member) - result = d_build_call (member, new_call, arguments); + // Call constructor. + if (member) + result = d_build_call(member, new_call, arguments); + else + result = new_call; + } else - result = new_call; + { + // User supplied initialiser, set-up with a struct literal. + StructLiteralExp *se = StructLiteralExp::create(loc, sd, arguments, htype); + se->sym = new Symbol(); + se->sym->Stree = new_call; + se->type = sd->type; + se->fillHoles = 0; + + result = compound_expr(se->toElem(irs), new_call); + } } // New'ing a D array. else if (tb->ty == Tarray) @@ -2187,8 +2119,8 @@ NewExp::toElem (IRState *irs) tb = newtype->toBasetype(); gcc_assert (tb->ty == Tarray); TypeDArray *tarray = (TypeDArray *) tb; - gcc_assert (!allocator); - gcc_assert (arguments && arguments->dim >= 1); + gcc_assert(!allocator); + gcc_assert(arguments && arguments->dim >= 1); if (arguments->dim == 1) { @@ -2198,42 +2130,42 @@ NewExp::toElem (IRState *irs) // Elem size is unknown. if (tarray->next->size() == 0) - return d_array_value (type->toCtype(), size_int (0), null_pointer_node); + return d_array_value(type->toCtype(), size_int(0), null_pointer_node); libcall = tarray->next->isZeroInit() ? LIBCALL_NEWARRAYT : LIBCALL_NEWARRAYIT; - args[0] = type->getTypeInfo(NULL)->toElem (irs); - args[1] = arg->toElem (irs); - result = build_libcall (libcall, 2, args, tb->toCtype()); + args[0] = type->getTypeInfo(NULL)->toElem(irs); + args[1] = arg->toElem(irs); + result = build_libcall(libcall, 2, args, tb->toCtype()); } else { // Multidimensional array allocations. vec *elms = NULL; Type *telem = newtype->toBasetype(); - tree dims_var = create_temporary_var (d_array_type (Type::tsize_t, arguments->dim)); - tree dims_init = build_constructor (TREE_TYPE (dims_var), NULL); + tree dims_var = create_temporary_var(d_array_type(Type::tsize_t, arguments->dim)); + tree dims_init = build_constructor(TREE_TYPE(dims_var), NULL); tree args[3]; for (size_t i = 0; i < arguments->dim; i++) { Expression *arg = (*arguments)[i]; - tree index = build_integer_cst (i, size_type_node); - CONSTRUCTOR_APPEND_ELT (elms, index, arg->toElem (irs)); + tree index = build_integer_cst(i, size_type_node); + CONSTRUCTOR_APPEND_ELT(elms, index, arg->toElem(irs)); - gcc_assert (telem->ty == Tarray); + gcc_assert(telem->ty == Tarray); telem = telem->toBasetype()->nextOf(); - gcc_assert (telem); + gcc_assert(telem); } - CONSTRUCTOR_ELTS (dims_init) = elms; - DECL_INITIAL (dims_var) = dims_init; + CONSTRUCTOR_ELTS(dims_init) = elms; + DECL_INITIAL(dims_var) = dims_init; libcall = telem->isZeroInit() ? LIBCALL_NEWARRAYMTX : LIBCALL_NEWARRAYMITX; - args[0] = type->getTypeInfo(NULL)->toElem (irs); - args[1] = build_integer_cst (arguments->dim, Type::tint32->toCtype()); - args[2] = build_address (dims_var); - result = build_libcall (libcall, 3, args, tb->toCtype()); - result = bind_expr (dims_var, result); + args[0] = type->getTypeInfo(NULL)->toElem(irs); + args[1] = build_integer_cst(arguments->dim, Type::tint32->toCtype()); + args[2] = build_address(dims_var); + result = build_libcall(libcall, 3, args, tb->toCtype()); + result = bind_expr(dims_var, result); } } // New'ing a pointer @@ -2243,17 +2175,24 @@ NewExp::toElem (IRState *irs) // Elem size is unknown. if (tpointer->next->size() == 0) - return d_convert (type->toCtype(), integer_zero_node); + return d_convert(type->toCtype(), integer_zero_node); + + libcall = tpointer->next->isZeroInit(loc) ? LIBCALL_NEWITEMT : LIBCALL_NEWITEMIT; - libcall = tpointer->next->isZeroInit (loc) ? LIBCALL_NEWITEMT : LIBCALL_NEWITEMIT; + tree arg = type->getTypeInfo(NULL)->toElem(irs); + result = build_libcall(libcall, 1, &arg, tb->toCtype()); - tree arg = type->getTypeInfo(NULL)->toElem (irs); - result = build_libcall (libcall, 1, &arg, tb->toCtype()); + if (arguments && arguments->dim == 1) + { + result = make_temp(result); + tree init = modify_expr(build_deref(result), (*arguments)[0]->toElem(irs)); + result = compound_expr(init, result); + } } else gcc_unreachable(); - return convert_expr (result, tb, type); + return convert_expr(result, tb, type); } elem * @@ -2322,7 +2261,6 @@ StringExp::toElem (IRState *) // Array type doesn't match string length if null terminated. TREE_TYPE (value) = d_array_type (tb->nextOf(), len); TREE_CONSTANT (value) = 1; - TREE_READONLY (value) = 1; switch (tb->ty) { @@ -2436,80 +2374,63 @@ ArrayLiteralExp::toElem (IRState *irs) elem * AssocArrayLiteralExp::toElem (IRState *irs) { - Type *tb = type->toBasetype(); - // %% want mutable type for typeinfo reference. - tb = tb->mutableOf(); + // Want mutable type for typeinfo reference. + Type *tb = type->toBasetype()->mutableOf(); + gcc_assert(tb->ty == Taarray); - TypeAArray *aa_type; - - if (tb->ty == Taarray) - aa_type = (TypeAArray *) tb; - else - { - // It's the AssociativeArray type. - // Turn it back into a TypeAArray - aa_type = new TypeAArray ((*values)[0]->type, (*keys)[0]->type); - aa_type->semantic (loc, NULL); - } - - Type *index = aa_type->index; - Type *next = aa_type->next; - gcc_assert (keys != NULL); - gcc_assert (values != NULL); - - tree keys_var = create_temporary_var (d_array_type (index, keys->dim)); - tree vals_var = create_temporary_var (d_array_type (next, keys->dim)); - tree keys_ptr = build_nop (index->pointerTo()->toCtype(), - build_address (keys_var)); - tree vals_ptr = build_nop (next->pointerTo()->toCtype(), - build_address (vals_var)); - tree keys_offset = size_zero_node; - tree vals_offset = size_zero_node; - tree keys_size = size_int (index->size()); - tree vals_size = size_int (next->size()); - tree result = NULL_TREE; + // Handle empty assoc array literals. + TypeAArray *ta = (TypeAArray *) tb; + if (keys->dim == 0) + return build_constructor (ta->toCtype(), NULL); + // Build an expression that assigns the expressions in KEYS and VALUES to a constructor. + vec *ke = NULL; + vec_safe_reserve (ke, keys->dim); for (size_t i = 0; i < keys->dim; i++) { - Expression *e; - tree elemp_e, assgn_e; - - e = (*keys)[i]; - elemp_e = build_offset (keys_ptr, keys_offset); - assgn_e = vmodify_expr (build_deref (elemp_e), e->toElem (irs)); - keys_offset = size_binop (PLUS_EXPR, keys_offset, keys_size); - result = maybe_compound_expr (result, assgn_e); + Expression *e = (*keys)[i]; + tree t = e->toElem(irs); + t = maybe_make_temp(t); + CONSTRUCTOR_APPEND_ELT(ke, build_integer_cst(i, size_type_node), + convert_expr(t, e->type, ta->index)); + } + tree akeys = build_constructor(d_array_type(ta->index, keys->dim), ke); - e = (*values)[i]; - elemp_e = build_offset (vals_ptr, vals_offset); - assgn_e = vmodify_expr (build_deref (elemp_e), e->toElem (irs)); - vals_offset = size_binop (PLUS_EXPR, vals_offset, vals_size); - result = maybe_compound_expr (result, assgn_e); + vec *ve = NULL; + vec_safe_reserve(ve, values->dim); + for (size_t i = 0; i < values->dim; i++) + { + Expression *e = (*values)[i]; + tree t = e->toElem(irs); + t = maybe_make_temp(t); + CONSTRUCTOR_APPEND_ELT(ve, build_integer_cst(i, size_type_node), + convert_expr(t, e->type, ta->next)); } + tree avals = build_constructor(d_array_type(ta->next, values->dim), ve); + // Call _d_assocarrayliteralTX (ti, keys, vals); tree args[3]; + args[0] = build_typeinfo(ta); + args[1] = d_array_value(ta->index->arrayOf()->toCtype(), size_int(keys->dim), build_address(akeys)); + args[2] = d_array_value(ta->next->arrayOf()->toCtype(), size_int(values->dim), build_address(avals)); - args[0] = build_typeinfo (aa_type); - args[1] = d_array_value (index->arrayOf()->toCtype(), size_int (keys->dim), keys_ptr); - args[2] = d_array_value (next->arrayOf()->toCtype(), size_int (keys->dim), vals_ptr); - result = maybe_compound_expr (result, build_libcall (LIBCALL_ASSOCARRAYLITERALTX, 3, args)); + tree mem = build_libcall(LIBCALL_ASSOCARRAYLITERALTX, 3, args); - tree aat_type = aa_type->toCtype(); + // Returns an AA pointed to by MEM. + tree aatype = ta->toCtype(); vec *ce = NULL; - CONSTRUCTOR_APPEND_ELT (ce, TYPE_FIELDS (aat_type), result); - tree ctor = build_constructor (aat_type, ce); + CONSTRUCTOR_APPEND_ELT (ce, TYPE_FIELDS (aatype), mem); - result = bind_expr (keys_var, bind_expr (vals_var, ctor)); - return build_nop (type->toCtype(), result); + return build_nop (type->toCtype(), build_constructor(aatype, ce)); } elem * -StructLiteralExp::toElem (IRState *irs) +StructLiteralExp::toElem(IRState *irs) { vec *ce = NULL; Type *tb = type->toBasetype(); - gcc_assert (tb->ty == Tstruct); + gcc_assert(tb->ty == Tstruct); if (sinit) { @@ -2518,13 +2439,13 @@ StructLiteralExp::toElem (IRState *irs) if (sinit->Stree == NULL_TREE) sd->toInitializer(); - gcc_assert (sinit->Stree != NULL); + gcc_assert(sinit->Stree != NULL); return sinit->Stree; } // CTFE may fill the hidden pointer by NullExp. size_t dim = elements ? elements->dim : 0; - gcc_assert (dim <= sd->fields.dim); + gcc_assert(dim <= sd->fields.dim); for (size_t i = 0; i < dim; i++) { @@ -2540,34 +2461,33 @@ StructLiteralExp::toElem (IRState *irs) if (fld_type->ty == Tsarray) { - if (d_types_same (exp_type, fld_type)) + if (d_types_same(exp_type, fld_type)) { // %% This would call _d_newarrayT ... use memcpy? - exp_tree = convert_expr (exp->toElem (irs), exp->type, fld->type); + exp_tree = convert_expr(exp->toElem(irs), exp->type, fld->type); } else { - // %% Could use memset if is zero init... - exp_tree = build_local_temp (fld_type->toCtype()); + exp_tree = build_local_temp(fld_type->toCtype()); Type *etype = fld_type; while (etype->ty == Tsarray) etype = etype->nextOf(); - gcc_assert (fld_type->size() % etype->size() == 0); - tree size = fold_build2 (TRUNC_DIV_EXPR, size_type_node, - size_int (fld_type->size()), size_int (etype->size())); + gcc_assert(fld_type->size() % etype->size() == 0); + tree size = fold_build2(TRUNC_DIV_EXPR, size_type_node, + size_int(fld_type->size()), size_int(etype->size())); - tree ptr_tree = build_nop (etype->pointerTo()->toCtype(), - build_address (exp_tree)); - tree set_exp = irs->doArraySet (ptr_tree, exp->toElem (irs), size); - exp_tree = compound_expr (set_exp, exp_tree); + tree ptr_tree = build_nop(etype->pointerTo()->toCtype(), + build_address(exp_tree)); + tree set_exp = irs->doArraySet(ptr_tree, exp->toElem(irs), size); + exp_tree = compound_expr(set_exp, exp_tree); } } else - exp_tree = convert_expr (exp->toElem (irs), exp->type, fld->type); + exp_tree = convert_expr(exp->toElem(irs), exp->type, fld->type); - CONSTRUCTOR_APPEND_ELT (ce, fld->toSymbol()->Stree, exp_tree); + CONSTRUCTOR_APPEND_ELT(ce, fld->toSymbol()->Stree, exp_tree); // Unions only have one field that gets assigned. if (sd->isUnionDeclaration()) @@ -2578,30 +2498,31 @@ StructLiteralExp::toElem (IRState *irs) { // Maybe setup hidden pointer to outer scope context. tree vthis_field = sd->vthis->toSymbol()->Stree; - tree vthis_value = build_vthis (sd, irs->func); - CONSTRUCTOR_APPEND_ELT (ce, vthis_field, vthis_value); - gcc_assert (sinit == NULL); + tree vthis_value = build_vthis(sd, irs->func); + CONSTRUCTOR_APPEND_ELT(ce, vthis_field, vthis_value); + gcc_assert(sinit == NULL); } - tree ctor = build_constructor (type->toCtype(), ce); - tree var = build_local_temp (TREE_TYPE (ctor)); + tree ctor = build_constructor(type->toCtype(), ce); + tree var = (sym != NULL) + ? build_deref(sym->Stree) : build_local_temp(TREE_TYPE(ctor)); tree init = NULL_TREE; if (fillHoles) { // Initialize all alignment 'holes' to zero. - init = d_build_call_nary (builtin_decl_explicit (BUILT_IN_MEMSET), 3, - build_address (var), size_zero_node, - size_int (sd->structsize)); + init = d_build_call_nary(builtin_decl_explicit(BUILT_IN_MEMSET), 3, + build_address(var), size_zero_node, + size_int(sd->structsize)); } - init = maybe_compound_expr (init, modify_expr (var, ctor)); + init = maybe_compound_expr(init, modify_expr(var, ctor)); - return compound_expr (init, var); + return compound_expr(init, var); } elem * -NullExp::toElem (IRState *) +NullExp::toElem(IRState *) { TY base_ty = type->toBasetype()->ty; tree null_exp; diff --git a/gcc/d/d-glue.cc b/gcc/d/d-glue.cc index d19b180b2..47ee3e1c7 100644 --- a/gcc/d/d-glue.cc +++ b/gcc/d/d-glue.cc @@ -22,12 +22,13 @@ #include "mars.h" #include "module.h" +#include "scope.h" #include "declaration.h" Global global; void -Global::init (void) +Global::init() { this->mars_ext = "d"; this->sym_ext = "d"; @@ -50,18 +51,18 @@ Global::init (void) this->stdmsg = stdout; this->main_d = "__main.d"; - memset (&this->params, 0, sizeof (Param)); + memset(&this->params, 0, sizeof(Param)); } unsigned -Global::startGagging (void) +Global::startGagging() { this->gag++; return this->gaggedErrors; } bool -Global::endGagging (unsigned oldGagged) +Global::endGagging(unsigned oldGagged) { bool anyErrs = (this->gaggedErrors != oldGagged); this->gag--; @@ -74,20 +75,8 @@ Global::endGagging (unsigned oldGagged) return anyErrs; } -bool -Global::isSpeculativeGagging (void) -{ - if (!this->gag) - return false; - - if (this->gag != this->speculativeGag) - return false; - - return true; -} - void -Global::increaseErrorCount (void) +Global::increaseErrorCount() { if (gag) this->gaggedErrors++; @@ -95,52 +84,38 @@ Global::increaseErrorCount (void) this->errors++; } -Ungag -Dsymbol::ungagSpeculative (void) -{ - unsigned oldgag = global.gag; - - if (global.isSpeculativeGagging() && !isSpeculative()) - global.gag = 0; - - return Ungag (oldgag); -} - -Ungag::~Ungag (void) -{ - global.gag = this->oldgag; -} - - char * -Loc::toChars (void) +Loc::toChars() { OutBuffer buf; if (this->filename) - buf.printf ("%s", this->filename); + buf.printf("%s", this->filename); if (this->linnum) - buf.printf (":%u", this->linnum); - - buf.writeByte (0); + { + buf.printf(":%u", this->linnum); + if (this->charnum) + buf.printf(":%u", this->charnum); + } - return (char *)buf.extractData(); + return buf.extractString(); } -Loc::Loc (Module *mod, unsigned linnum) +Loc::Loc(Module *mod, unsigned linnum, unsigned charnum) { this->linnum = linnum; + this->charnum = charnum; this->filename = mod ? mod->srcfile->toChars() : NULL; } bool -Loc::equals (const Loc& loc) +Loc::equals(const Loc& loc) { - if (this->linnum != loc.linnum) + if (this->linnum != loc.linnum || this->charnum != loc.charnum) return false; - if (!FileName::equals (this->filename, loc.filename)) + if (!FileName::equals(this->filename, loc.filename)) return false; return true; @@ -150,47 +125,48 @@ Loc::equals (const Loc& loc) // Print a hard error message. void -error (Loc loc, const char *format, ...) +error(Loc loc, const char *format, ...) { va_list ap; - va_start (ap, format); - verror (loc, format, ap); - va_end (ap); + va_start(ap, format); + verror(loc, format, ap); + va_end(ap); } void -error (const char *filename, unsigned linnum, const char *format, ...) +error(const char *filename, unsigned linnum, unsigned charnum, const char *format, ...) { Loc loc; va_list ap; - loc.filename = CONST_CAST (char *, filename); + loc.filename = CONST_CAST(char *, filename); loc.linnum = linnum; + loc.charnum = charnum; - va_start (ap, format); - verror (loc, format, ap); - va_end (ap); + va_start(ap, format); + verror(loc, format, ap); + va_end(ap); } void -verror (Loc loc, const char *format, va_list ap, - const char *p1, const char *p2, const char *) +verror(Loc loc, const char *format, va_list ap, + const char *p1, const char *p2, const char *) { if (!global.gag) { - location_t location = get_linemap (loc); + location_t location = get_linemap(loc); char *msg; // Build string and emit. - if (vasprintf (&msg, format, ap) >= 0 && msg != NULL) + if (vasprintf(&msg, format, ap) >= 0 && msg != NULL) { if (p2) - msg = concat (p2, " ", msg, NULL); + msg = concat(p2, " ", msg, NULL); if (p1) - msg = concat (p1, " ", msg, NULL); + msg = concat(p1, " ", msg, NULL); - error_at (location, "%s", msg); + error_at(location, "%s", msg); } // Moderate blizzard of cascading messages @@ -207,48 +183,48 @@ verror (Loc loc, const char *format, va_list ap, // Doesn't increase error count. void -errorSupplemental (Loc loc, const char *format, ...) +errorSupplemental(Loc loc, const char *format, ...) { va_list ap; - va_start (ap, format); - verrorSupplemental (loc, format, ap); - va_end (ap); + va_start(ap, format); + verrorSupplemental(loc, format, ap); + va_end(ap); } void -verrorSupplemental (Loc loc, const char *format, va_list ap) +verrorSupplemental(Loc loc, const char *format, va_list ap) { if (!global.gag) { - location_t location = get_linemap (loc); + location_t location = get_linemap(loc); char *msg; - if (vasprintf (&msg, format, ap) >= 0 && msg != NULL) - inform (location, "%s", msg); + if (vasprintf(&msg, format, ap) >= 0 && msg != NULL) + inform(location, "%s", msg); } } // Print a warning message. void -warning (Loc loc, const char *format, ...) +warning(Loc loc, const char *format, ...) { va_list ap; - va_start (ap, format); - vwarning (loc, format, ap); - va_end (ap); + va_start(ap, format); + vwarning(loc, format, ap); + va_end(ap); } void -vwarning (Loc loc, const char *format, va_list ap) +vwarning(Loc loc, const char *format, va_list ap) { if (global.params.warnings && !global.gag) { - location_t location = get_linemap (loc); + location_t location = get_linemap(loc); char *msg; - if (vasprintf (&msg, format, ap) >= 0 && msg != NULL) - warning_at (location, 0, "%s", msg); + if (vasprintf(&msg, format, ap) >= 0 && msg != NULL) + warning_at(location, 0, "%s", msg); // Warnings don't count if gagged. if (global.params.warnings == 1) @@ -259,34 +235,34 @@ vwarning (Loc loc, const char *format, va_list ap) // Print a deprecation message. void -deprecation (Loc loc, const char *format, ...) +deprecation(Loc loc, const char *format, ...) { va_list ap; - va_start (ap, format); - vdeprecation (loc, format, ap); - va_end (ap); + va_start(ap, format); + vdeprecation(loc, format, ap); + va_end(ap); } void -vdeprecation (Loc loc, const char *format, va_list ap, +vdeprecation(Loc loc, const char *format, va_list ap, const char *p1, const char *p2) { if (global.params.useDeprecated == 0) - verror (loc, format, ap, p1, p2); + verror(loc, format, ap, p1, p2); else if (global.params.useDeprecated == 2 && !global.gag) { - location_t location = get_linemap (loc); + location_t location = get_linemap(loc); char *msg; // Build string and emit. if (p2) - format = concat (p2, " ", format, NULL); + format = concat(p2, " ", format, NULL); if (p1) - format = concat (p1, " ", format, NULL); + format = concat(p1, " ", format, NULL); - if (vasprintf (&msg, format, ap) >= 0 && msg != NULL) - warning_at (location, OPT_Wdeprecated, "%s", msg); + if (vasprintf(&msg, format, ap) >= 0 && msg != NULL) + warning_at(location, OPT_Wdeprecated, "%s", msg); } } @@ -294,14 +270,14 @@ vdeprecation (Loc loc, const char *format, va_list ap, // the compiler. void -fatal (void) +fatal() { - exit (FATAL_EXIT_CODE); + exit(FATAL_EXIT_CODE); } void -escapePath (OutBuffer *buf, const char *fname) +escapePath(OutBuffer *buf, const char *fname) { while (1) { @@ -313,10 +289,10 @@ escapePath (OutBuffer *buf, const char *fname) case '(': case ')': case '\\': - buf->writebyte('\\'); + buf->writeByte('\\'); default: - buf->writebyte(*fname); + buf->writeByte(*fname); break; } fname++; @@ -324,101 +300,101 @@ escapePath (OutBuffer *buf, const char *fname) } void -readFile (Loc loc, File *f) +readFile(Loc loc, File *f) { if (f->read()) { - error (loc, "Error reading file '%s'", f->name->toChars()); + error(loc, "Error reading file '%s'", f->name->toChars()); fatal(); } } void -writeFile (Loc loc, File *f) +writeFile(Loc loc, File *f) { if (f->write()) { - error (loc, "Error writing file '%s'", f->name->toChars()); + error(loc, "Error writing file '%s'", f->name->toChars()); fatal(); } } void -ensurePathToNameExists (Loc loc, const char *name) +ensurePathToNameExists(Loc loc, const char *name) { - const char *pt = FileName::path (name); + const char *pt = FileName::path(name); if (*pt) { if (FileName::ensurePathExists(pt)) { - error (loc, "cannot create directory %s", pt); + error(loc, "cannot create directory %s", pt); fatal(); } } } -// Binary search for P in TAB between the range 0 to HIGH. +// Semantically analyze AsmStatement where SC is the scope. -int binary(const char *p , const char **tab, int high) +Statement * +asmSemantic(AsmStatement *s, Scope *sc) { - int low = 0; - do - { - int pos = (low + high) / 2; - int cmp = strcmp(p, tab[pos]); - if (! cmp) - return pos; - else if (cmp < 0) - high = pos; - else - low = pos + 1; - } while (low != high); - - return -1; + sc->func->hasReturnExp |= 8; + return s; +} + +// Determine return style of function - whether in registers or through a +// hidden pointer to the caller's stack. + +RET +retStyle(TypeFunction *) +{ + // Need the backend type to determine this, but this is called from the + // front end before semantic processing is finished. An accurate value + // is not currently needed anyway. + return RETstack; } -// Determine if function is a builtin one that we can -// evaluate at compile time. +// Determine if function is a builtin one that we can evaluate at compile time. BUILTIN -FuncDeclaration::isBuiltin (void) +isBuiltin(FuncDeclaration *fd) { - if (this->builtin != BUILTINunknown) - return this->builtin; + if (fd->builtin != BUILTINunknown) + return fd->builtin; - this->builtin = BUILTINno; + fd->builtin = BUILTINno; // Intrinsics have no function body. - if (this->fbody) + if (fd->fbody) return BUILTINno; - maybe_set_intrinsic (this); - return this->builtin; + maybe_set_intrinsic(fd); + return fd->builtin; } // Evaluate builtin D function FD whose argument list is ARGUMENTS. // Return result; NULL if cannot evaluate it. Expression * -eval_builtin (Loc loc, FuncDeclaration *fd, Expressions *arguments) +eval_builtin(Loc loc, FuncDeclaration *fd, Expressions *arguments) { if (fd->builtin != BUILTINyes) return NULL; tree decl = fd->toSymbol()->Stree; - gcc_assert (DECL_BUILT_IN (decl)); + gcc_assert(DECL_BUILT_IN(decl)); TypeFunction *tf = (TypeFunction *) fd->type; Expression *e = NULL; - set_input_location (loc); + set_input_location(loc); - tree result = d_build_call (tf, decl, NULL, arguments); - result = fold (result); + tree result = d_build_call(tf, decl, NULL, arguments); + result = fold(result); // Builtin should be successfully evaluated. // Will only return NULL if we can't convert it. - if (TREE_CONSTANT (result) && TREE_CODE (result) != CALL_EXPR) - e = build_expression (result); + if (TREE_CONSTANT(result) && TREE_CODE(result) != CALL_EXPR) + e = build_expression(result); return e; } diff --git a/gcc/d/d-intrinsics.def b/gcc/d/d-intrinsics.def deleted file mode 100644 index 628c8d57d..000000000 --- a/gcc/d/d-intrinsics.def +++ /dev/null @@ -1,151 +0,0 @@ -// d-intrinsics.def -- D frontend for GCC. -// Copyright (C) 2014 Free Software Foundation, Inc. - -// GCC is free software; you can redistribute it and/or modify it under -// the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 3, or (at your option) any later -// version. - -// GCC is distributed in the hope that it will be useful, but WITHOUT ANY -// WARRANTY; without even the implied warranty of MERCHANTABILITY or -// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -// for more details. - -// You should have received a copy of the GNU General Public License -// along with GCC; see the file COPYING3. If not see -// . - -// Definitions for the D compiler intrinsics and runtime functions. - -// Define all D intrinsic functions. -// The first parameter: -// CODE - The enum code used to refer this intrinsic. -// The second parameter: -// ALIAS - The enum code used to refer the function DECL_FUNCTION_CODE, -// if there are multiple modules or decos for a single intrinsic, they -// would all refer to this code. -// The third parameter: -// NAME - The name of the intrinsic. -// The fourth parameter: -// MODULE - The list of modules which the intrinsic could belong to. -// The fifth parameter: -// DECO - the signature decoration of the intrinsic. -// Template intrinsics reserve this for the template parameter strings. - -#define DEF_D_INTRINSIC(CODE, ALIAS, NAME, MODULE, DECO) \ - DEF_INTRINSIC (INTRINSIC_ ## CODE, INTRINSIC_ ## ALIAS, NAME, MODULE, DECO) - -// Helper macros. - -// Modules -#define CORE_BITOP "core.bitop" -#define CORE_VARARG "core.stdc.stdarg" -#define CORE_MATH "core.math" -#define STD_MATH "std.math" - -// Signature decorations -#define FN_SAFE_PURE_NOTHROW "FNaNbNf" // @safe pure nothrow function -#define FN_PURE_NOTHROW "FNaNb" // @system pure nothrow function -#define FN_NONE NULL - -// Parameter and return types -#define INT_UINT "kZi" // int(uint) -#define INT_ULONG "mZi" // int(ulong) -#define UINT_UINT "kZk" // uint(uint) -#define INT_PTR_UINT_UINT "PkkZi" // int(uint*, uint) -#define INT_PTR_ULONG_ULONG "PmmZi" // int(ulong*, ulong) -#define REAL_REAL "eZe" // real(real) -#define REAL_REAL_INT "eiZe" // real(real, int) -#define LONG_REAL "eZl" // long(real) -#define FLOAT_FLOAT "fZf" // float(float) -#define DOUBLE_DOUBLE "dZd" // double(double) - -#define SAFE_PURE_NOTHROW_INT_UINT FN_SAFE_PURE_NOTHROW INT_UINT -#define SAFE_PURE_NOTHROW_INT_ULONG FN_SAFE_PURE_NOTHROW INT_ULONG -#define SAFE_PURE_NOTHROW_UINT_UINT FN_SAFE_PURE_NOTHROW UINT_UINT -#define SAFE_PURE_NOTHROW_LONG_REAL FN_SAFE_PURE_NOTHROW LONG_REAL -#define SAFE_PURE_NOTHROW_FLOAT_FLOAT FN_SAFE_PURE_NOTHROW FLOAT_FLOAT -#define SAFE_PURE_NOTHROW_DOUBLE_DOUBLE FN_SAFE_PURE_NOTHROW DOUBLE_DOUBLE -#define SAFE_PURE_NOTHROW_REAL_REAL FN_SAFE_PURE_NOTHROW REAL_REAL -#define SAFE_PURE_NOTHROW_REAL_REAL_INT FN_SAFE_PURE_NOTHROW REAL_REAL_INT -#define PURE_NOTHROW_INT_PTR_UINT_UINT FN_PURE_NOTHROW INT_PTR_UINT_UINT -#define PURE_NOTHROW_INT_PTR_ULONG_ULONG FN_PURE_NOTHROW INT_PTR_ULONG_ULONG -#define DECO_NONE FN_NONE - -// core.bitop intrinsics - -DEF_D_INTRINSIC (BSF, BSF, "bsf", CORE_BITOP, SAFE_PURE_NOTHROW_INT_UINT) -DEF_D_INTRINSIC (BSR, BSR, "bsr", CORE_BITOP, SAFE_PURE_NOTHROW_INT_UINT) -DEF_D_INTRINSIC (BTC, BTC, "btc", CORE_BITOP, PURE_NOTHROW_INT_PTR_UINT_UINT) -DEF_D_INTRINSIC (BTR, BTR, "btr", CORE_BITOP, PURE_NOTHROW_INT_PTR_UINT_UINT) -DEF_D_INTRINSIC (BTS, BTS, "bts", CORE_BITOP, PURE_NOTHROW_INT_PTR_UINT_UINT) -DEF_D_INTRINSIC (BSF64, BSF, "bsf", CORE_BITOP, SAFE_PURE_NOTHROW_INT_ULONG) -DEF_D_INTRINSIC (BSR64, BSR, "bsr", CORE_BITOP, SAFE_PURE_NOTHROW_INT_ULONG) -DEF_D_INTRINSIC (BTC64, BTC, "btc", CORE_BITOP, PURE_NOTHROW_INT_PTR_ULONG_ULONG) -DEF_D_INTRINSIC (BTR64, BTR, "btr", CORE_BITOP, PURE_NOTHROW_INT_PTR_ULONG_ULONG) -DEF_D_INTRINSIC (BTS64, BTS, "bts", CORE_BITOP, PURE_NOTHROW_INT_PTR_ULONG_ULONG) -DEF_D_INTRINSIC (BSWAP, BSWAP, "bswap", CORE_BITOP, SAFE_PURE_NOTHROW_UINT_UINT) - -// core.math intrinsics - -DEF_D_INTRINSIC (COS, COS, "cos", CORE_MATH, SAFE_PURE_NOTHROW_REAL_REAL) -DEF_D_INTRINSIC (FABS, FABS, "fabs", CORE_MATH, SAFE_PURE_NOTHROW_REAL_REAL) -DEF_D_INTRINSIC (LDEXP, LDEXP, "ldexp", CORE_MATH, SAFE_PURE_NOTHROW_REAL_REAL_INT) -DEF_D_INTRINSIC (RINT, RINT, "rint", CORE_MATH, SAFE_PURE_NOTHROW_REAL_REAL) -DEF_D_INTRINSIC (RNDTOL, RNDTOL, "rndtol", CORE_MATH, SAFE_PURE_NOTHROW_LONG_REAL) -DEF_D_INTRINSIC (SIN, SIN, "sin", CORE_MATH, SAFE_PURE_NOTHROW_REAL_REAL) -DEF_D_INTRINSIC (SQRTF, SQRTF, "sqrt", CORE_MATH, SAFE_PURE_NOTHROW_FLOAT_FLOAT) -DEF_D_INTRINSIC (SQRT, SQRT, "sqrt", CORE_MATH, SAFE_PURE_NOTHROW_DOUBLE_DOUBLE) -DEF_D_INTRINSIC (SQRTL, SQRTL, "sqrt", CORE_MATH, SAFE_PURE_NOTHROW_REAL_REAL) - -// std.math intrinsics - -DEF_D_INTRINSIC (STD_COS, COS, "cos", STD_MATH, SAFE_PURE_NOTHROW_REAL_REAL) -DEF_D_INTRINSIC (STD_FABS, FABS, "fabs", STD_MATH, SAFE_PURE_NOTHROW_REAL_REAL) -DEF_D_INTRINSIC (STD_LDEXP, LDEXP, "ldexp", STD_MATH, SAFE_PURE_NOTHROW_REAL_REAL_INT) -DEF_D_INTRINSIC (STD_RINT, RINT, "rint", STD_MATH, SAFE_PURE_NOTHROW_REAL_REAL) -DEF_D_INTRINSIC (STD_RNDTOL, RNDTOL, "rndtol", STD_MATH, SAFE_PURE_NOTHROW_LONG_REAL) -DEF_D_INTRINSIC (STD_SIN, SIN, "sin", STD_MATH, SAFE_PURE_NOTHROW_REAL_REAL) -DEF_D_INTRINSIC (STD_SQRTF, SQRTF, "sqrt", STD_MATH, SAFE_PURE_NOTHROW_FLOAT_FLOAT) -DEF_D_INTRINSIC (STD_SQRT, SQRT, "sqrt", STD_MATH, SAFE_PURE_NOTHROW_DOUBLE_DOUBLE) -DEF_D_INTRINSIC (STD_SQRTL, SQRTL, "sqrt", STD_MATH, SAFE_PURE_NOTHROW_REAL_REAL) - -// core.stdc.stdarg intrinsics - -DEF_D_INTRINSIC (VA_ARG, VA_ARG, "va_arg", CORE_VARARG, "void(ref va_list ap, ref T parmn)") -DEF_D_INTRINSIC (C_VA_ARG, C_VA_ARG, "va_arg", CORE_VARARG, "T(ref va_list ap)") -DEF_D_INTRINSIC (VASTART, VASTART, "va_start", CORE_VARARG, "void(out va_list ap, ref T parmn)") - - -// Remove helper macros -#undef DEF_D_INTRINSIC -#undef CORE_BITOP -#undef CORE_VARARG -#undef CORE_MATH -#undef STD_MATH -#undef FN_SAFE_PURE_NOTHROW -#undef FN_PURE_NOTHROW -#undef FN_NONE -#undef INT_UINT -#undef INT_ULONG -#undef UINT_UINT -#undef INT_PTR_UINT_UINT -#undef INT_PTR_ULONG_ULONG -#undef REAL_REAL -#undef REAL_REAL_INT -#undef LONG_REAL -#undef FLOAT_FLOAT -#undef DOUBLE_DOUBLE -#undef SAFE_PURE_NOTHROW_INT_UINT -#undef SAFE_PURE_NOTHROW_INT_ULONG -#undef SAFE_PURE_NOTHROW_UINT_UINT -#undef SAFE_PURE_NOTHROW_LONG_REAL -#undef SAFE_PURE_NOTHROW_FLOAT_FLOAT -#undef SAFE_PURE_NOTHROW_DOUBLE_DOUBLE -#undef SAFE_PURE_NOTHROW_REAL_REAL -#undef SAFE_PURE_NOTHROW_REAL_REAL_INT -#undef PURE_NOTHROW_INT_PTR_UINT_UINT -#undef PURE_NOTHROW_INT_PTR_ULONG_ULONG - -#undef DECO_NONE - diff --git a/gcc/d/d-irstate.cc b/gcc/d/d-irstate.cc index a04e6becd..3d62f915e 100644 --- a/gcc/d/d-irstate.cc +++ b/gcc/d/d-irstate.cc @@ -22,7 +22,7 @@ #include "init.h" -IRState::IRState (void) +IRState::IRState() { this->parent = NULL; this->func = NULL; @@ -74,7 +74,7 @@ IRState::startFunction (FuncDeclaration *decl) } void -IRState::endFunction (void) +IRState::endFunction() { gcc_assert (this->scopes_.is_empty()); current_irstate = (IRState *) this->parent; @@ -86,18 +86,12 @@ IRState::endFunction (void) void IRState::addExp (tree e) { - /* Need to check that this is actually an expression; it - could be an integer constant (statement with no effect.) - Maybe should filter those out anyway... */ - if (TREE_TYPE (e) && !VOID_TYPE_P (TREE_TYPE (e))) - { - if (warn_unused_value - && !TREE_NO_WARNING (e) - && !TREE_SIDE_EFFECTS (e)) - warning (OPT_Wunused_value, "statement has no effect"); - - e = build1 (CONVERT_EXPR, void_type_node, e); - } + // Need to check that this is actually an expression; it + // could be an integer constant (statement with no effect.) + if (TREE_TYPE(e) == void_type_node && + TREE_CODE(e) == NOP_EXPR && + TREE_OPERAND(e, 0) == size_zero_node) + return; if (EXPR_P (e) && !EXPR_HAS_LOCATION (e)) SET_EXPR_LOCATION (e, input_location); @@ -109,7 +103,7 @@ IRState::addExp (tree e) void -IRState::pushStatementList (void) +IRState::pushStatementList() { tree t = alloc_stmt_list(); this->statementList_.safe_push (t); @@ -117,7 +111,7 @@ IRState::pushStatementList (void) } tree -IRState::popStatementList (void) +IRState::popStatementList() { tree t = this->statementList_.pop(); @@ -235,7 +229,7 @@ IRState::beginFlow (Statement *stmt) } void -IRState::endFlow (void) +IRState::endFlow() { gcc_assert (!this->loops_.is_empty()); @@ -258,7 +252,7 @@ IRState::doLabel (tree label) void -IRState::startScope (void) +IRState::startScope() { unsigned *p_count = new unsigned; *p_count = 0; @@ -268,7 +262,7 @@ IRState::startScope (void) } void -IRState::endScope (void) +IRState::endScope() { unsigned *p_count = this->currentScope(); while (*p_count) @@ -279,7 +273,7 @@ IRState::endScope (void) void -IRState::startBindings (void) +IRState::startBindings() { tree block; @@ -293,7 +287,7 @@ IRState::startBindings (void) } void -IRState::endBindings (void) +IRState::endBindings() { tree block = pop_binding_level (1, 0); TREE_USED (block) = 1; @@ -323,7 +317,7 @@ IRState::startCond (Statement *stmt, tree cond) // Start a new statement list for the false condition branch. void -IRState::startElse (void) +IRState::startElse() { Flow *flow = this->currentFlow(); flow->trueBranch = this->popStatementList(); @@ -333,7 +327,7 @@ IRState::startElse (void) // Wrap up our constructed if condition into a COND_EXPR. void -IRState::endCond (void) +IRState::endCond() { Flow *flow = this->currentFlow(); tree branch = this->popStatementList(); @@ -366,7 +360,7 @@ IRState::startLoop (Statement *stmt) // Emit continue label for loop. void -IRState::continueHere (void) +IRState::continueHere() { Flow *flow = this->currentFlow(); this->doLabel (flow->continueLabel); @@ -418,7 +412,7 @@ IRState::exitLoop (Identifier *ident) // Wrap up constructed loop body in a LOOP_EXPR. void -IRState::endLoop (void) +IRState::endLoop() { // says must contain an EXIT_EXPR -- what about while (1)..goto;? something other thand LOOP_EXPR? tree body = this->popStatementList(); @@ -518,7 +512,7 @@ IRState::doCase (tree value, tree label) // Wrap up constructed body into a SWITCH_EXPR. void -IRState::endCase (void) +IRState::endCase() { Flow *flow = this->currentFlow(); tree body = this->popStatementList(); @@ -550,7 +544,7 @@ IRState::startTry (Statement *stmt) // Pops the try body and starts a new statement list for all catches. void -IRState::startCatches (void) +IRState::startCatches() { Flow *flow = this->currentFlow(); flow->tryBody = this->popStatementList(); @@ -570,7 +564,7 @@ IRState::startCatch (tree type) // Wrap up catch expression into a CATCH_EXPR. void -IRState::endCatch (void) +IRState::endCatch() { tree body = this->popStatementList(); this->addExp (build2 (CATCH_EXPR, void_type_node, @@ -580,7 +574,7 @@ IRState::endCatch (void) // Wrap up try/catch into a TRY_CATCH_EXPR. void -IRState::endCatches (void) +IRState::endCatches() { Flow *flow = this->currentFlow(); tree catches = this->popStatementList(); @@ -605,7 +599,7 @@ IRState::endCatches (void) // Start a new finally expression. void -IRState::startFinally (void) +IRState::startFinally() { Flow *flow = this->currentFlow(); flow->tryBody = this->popStatementList(); @@ -616,7 +610,7 @@ IRState::startFinally (void) // Wrap-up try/finally into a TRY_FINALLY_EXPR. void -IRState::endFinally (void) +IRState::endFinally() { Flow *flow = this->currentFlow(); tree finally = this->popStatementList(); diff --git a/gcc/d/d-irstate.h b/gcc/d/d-irstate.h index e6e6c2689..54b50a9f7 100644 --- a/gcc/d/d-irstate.h +++ b/gcc/d/d-irstate.h @@ -51,7 +51,7 @@ struct Label LevelKind kind; unsigned level; - Label (void) + Label() : label(NULL), block(NULL), from(NULL), kind(level_block), level(0) { } @@ -105,7 +105,7 @@ struct IRState public: IRState *parent; - IRState (void); + IRState(); // ** Functions FuncDeclaration *func; @@ -114,16 +114,18 @@ struct IRState // Static chain of function, for D2, this is a closure. tree sthis; + auto_vec deferred; + IRState *startFunction (FuncDeclaration *decl); - void endFunction (void); + void endFunction(); // Variables that are in scope that will need destruction later. auto_vec varsInScope; // ** Statement Lists void addExp (tree e); - void pushStatementList (void); - tree popStatementList (void); + void pushStatementList(); + tree popStatementList(); // ** Labels // It is only valid to call this while the function in which the label is defined @@ -139,9 +141,9 @@ struct IRState Flow *getLoopForLabel (Identifier *ident, bool want_continue = false); Flow *beginFlow (Statement *stmt); - void endFlow (void); + void endFlow(); - Flow *currentFlow (void) + Flow *currentFlow() { gcc_assert (!this->loops_.is_empty()); return this->loops_.last(); @@ -162,17 +164,17 @@ struct IRState where the variable is declared and ends at it's containing scope. */ - void startScope (void); - void endScope (void); + void startScope(); + void endScope(); - unsigned *currentScope (void) + unsigned *currentScope() { gcc_assert (!this->scopes_.is_empty()); return this->scopes_.last(); } - void startBindings (void); - void endBindings (void); + void startBindings(); + void endBindings(); // Update current source file location to LOC. void doLineNote (const Loc& loc) @@ -182,15 +184,15 @@ struct IRState // ** Conditional statements. void startCond (Statement *stmt, tree t_cond); - void startElse (void); - void endCond (void); + void startElse(); + void endCond(); // ** Loop statements. void startLoop (Statement *stmt); - void continueHere (void); + void continueHere(); void setContinueLabel (tree lbl); void exitIfFalse (tree t_cond); - void endLoop (void); + void endLoop(); void continueLoop (Identifier *ident); void exitLoop (Identifier *ident); @@ -207,16 +209,16 @@ struct IRState void startCase (Statement *stmt, tree t_cond, int has_vars = 0); void checkSwitchCase (Statement *stmt, int default_flag = 0); void doCase (tree t_value, tree t_label); - void endCase (void); + void endCase(); // ** Exception handling. void startTry (Statement *stmt); - void startCatches (void); + void startCatches(); void startCatch (tree t_type); - void endCatch (void); - void endCatches (void); - void startFinally (void); - void endFinally (void); + void endCatch(); + void endCatches(); + void startFinally(); + void endFinally(); // ** Return statement. void doReturn (tree t_value); diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc index c11d2a3e8..2b5c57994 100644 --- a/gcc/d/d-lang.cc +++ b/gcc/d/d-lang.cc @@ -29,18 +29,20 @@ #include "mars.h" #include "mtype.h" #include "cond.h" +#include "hdrgen.h" #include "id.h" +#include "doc.h" #include "json.h" #include "module.h" #include "scope.h" #include "root.h" #include "dfrontend/target.h" -static tree d_handle_noinline_attribute (tree *, tree, tree, int, bool *); -static tree d_handle_forceinline_attribute (tree *, tree, tree, int, bool *); -static tree d_handle_flatten_attribute (tree *, tree, tree, int, bool *); -static tree d_handle_target_attribute (tree *, tree, tree, int, bool *); -static tree d_handle_noclone_attribute (tree *, tree, tree, int, bool *); +static tree d_handle_noinline_attribute(tree *, tree, tree, int, bool *); +static tree d_handle_forceinline_attribute(tree *, tree, tree, int, bool *); +static tree d_handle_flatten_attribute(tree *, tree, tree, int, bool *); +static tree d_handle_target_attribute(tree *, tree, tree, int, bool *); +static tree d_handle_noclone_attribute(tree *, tree, tree, int, bool *); static char lang_name[6] = "GNU D"; @@ -66,7 +68,6 @@ static const attribute_spec d_attribute_table[] = #undef LANG_HOOKS_INIT_TS #undef LANG_HOOKS_INIT_OPTIONS #undef LANG_HOOKS_INIT_OPTIONS_STRUCT -#undef LANG_HOOKS_INITIALIZE_DIAGNOSTICS #undef LANG_HOOKS_OPTION_LANG_MASK #undef LANG_HOOKS_HANDLE_OPTION #undef LANG_HOOKS_POST_OPTIONS @@ -90,7 +91,6 @@ static const attribute_spec d_attribute_table[] = #define LANG_HOOKS_INIT_TS d_init_ts #define LANG_HOOKS_INIT_OPTIONS d_init_options #define LANG_HOOKS_INIT_OPTIONS_STRUCT d_init_options_struct -#define LANG_HOOKS_INITIALIZE_DIAGNOSTICS d_initialize_diagnostics #define LANG_HOOKS_OPTION_LANG_MASK d_option_lang_mask #define LANG_HOOKS_HANDLE_OPTION d_handle_option #define LANG_HOOKS_POST_OPTIONS d_post_options @@ -145,28 +145,27 @@ static bool std_inc = true; /* Common initialization before calling option handlers. */ static void -d_init_options (unsigned int, cl_decoded_option *decoded_options) +d_init_options(unsigned int, cl_decoded_option *decoded_options) { // Set default values global.init(); global.compiler.vendor = lang_name; - global.params.argv0 = xstrdup (decoded_options[0].arg); - global.params.link = 1; - global.params.useAssert = 1; - global.params.useInvariants = 1; - global.params.useIn = 1; - global.params.useOut = 1; + global.params.argv0 = xstrdup(decoded_options[0].arg); + global.params.link = true; + global.params.useAssert = true; + global.params.useInvariants = true; + global.params.useIn = true; + global.params.useOut = true; global.params.useArrayBounds = 2; - global.params.useSwitchError = 1; - global.params.useInline = 0; + global.params.useSwitchError = true; + global.params.useInline = false; global.params.warnings = 0; - global.params.obj = 1; - global.params.quiet = 1; + global.params.obj = true; global.params.useDeprecated = 1; - global.params.betterC = 0; - global.params.allInst = 0; + global.params.betterC = false; + global.params.allInst = false; global.params.linkswitches = new Strings(); global.params.libfiles = new Strings(); @@ -182,7 +181,7 @@ d_init_options (unsigned int, cl_decoded_option *decoded_options) /* Initialize options structure OPTS. */ static void -d_init_options_struct (gcc_options *opts) +d_init_options_struct(gcc_options *opts) { // GCC options opts->x_flag_exceptions = 1; @@ -204,18 +203,9 @@ d_init_options_struct (gcc_options *opts) opts->x_flag_wrapv = 1; } -static void -d_initialize_diagnostics (diagnostic_context *context) -{ - // We don't need any of these in error messages. - context->show_caret = false; - context->show_option_requested = false; - context->show_column = false; -} - /* Return language mask for option parsing. */ static unsigned int -d_option_lang_mask (void) +d_option_lang_mask() { return CL_D; } @@ -224,17 +214,17 @@ static void d_add_builtin_version(const char* ident) { if (strcmp (ident, "linux") == 0) - global.params.isLinux = 1; + global.params.isLinux = true; else if (strcmp (ident, "OSX") == 0) - global.params.isOSX = 1; + global.params.isOSX = true; else if (strcmp (ident, "Windows") == 0) - global.params.isWindows = 1; + global.params.isWindows = true; else if (strcmp (ident, "FreeBSD") == 0) - global.params.isFreeBSD = 1; + global.params.isFreeBSD = true; else if (strcmp (ident, "OpenBSD") == 0) - global.params.isOpenBSD = 1; + global.params.isOpenBSD = true; else if (strcmp (ident, "Solaris") == 0) - global.params.isSolaris = 1; + global.params.isSolaris = true; else if (strcmp (ident, "X86_64") == 0) global.params.is64bit = true; @@ -242,16 +232,17 @@ d_add_builtin_version(const char* ident) } static bool -d_init (void) +d_init() { if(POINTER_SIZE == 64) - global.params.isLP64 = 1; + global.params.isLP64 = true; Type::init(); Id::initialize(); Module::init(); Expression::init(); initPrecedence(); + initTraitsStringTable(); d_backend_init(); @@ -312,7 +303,7 @@ d_init (void) VersionCondition::addPredefinedGlobalIdent ("unittest"); if (global.params.useAssert) VersionCondition::addPredefinedGlobalIdent("assert"); - if (global.params.noboundscheck) + if (!global.params.useArrayBounds) VersionCondition::addPredefinedGlobalIdent("D_NoBoundsChecks"); VersionCondition::addPredefinedGlobalIdent ("all"); @@ -325,7 +316,7 @@ d_init (void) } void -d_init_ts (void) +d_init_ts() { MARK_TS_TYPED (IASM_EXPR); MARK_TS_TYPED (FLOAT_MOD_EXPR); @@ -383,7 +374,8 @@ d_handle_option (size_t scode, const char *arg, int value, break; case OPT_fbounds_check: - global.params.noboundscheck = !value; + global.params.useArrayBounds = value ? 2 : 0; + flag_bounds_check = value; break; case OPT_fdebug: @@ -422,12 +414,12 @@ d_handle_option (size_t scode, const char *arg, int value, break; case OPT_fdoc_dir_: - global.params.doDocComments = 1; + global.params.doDocComments = true; global.params.docdir = arg; break; case OPT_fdoc_file_: - global.params.doDocComments = 1; + global.params.doDocComments = true; global.params.docname = arg; break; @@ -435,6 +427,10 @@ d_handle_option (size_t scode, const char *arg, int value, global.params.ddocfiles->push (arg); break; + case OPT_fd_vgc: + global.params.vgc = value; + break; + case OPT_fd_verbose: global.params.verbose = value; break; @@ -465,12 +461,12 @@ d_handle_option (size_t scode, const char *arg, int value, break; case OPT_fintfc_dir_: - global.params.doHdrGeneration = 1; + global.params.doHdrGeneration = true; global.params.hdrdir = arg; break; case OPT_fintfc_file_: - global.params.doHdrGeneration = 1; + global.params.doHdrGeneration = true; global.params.hdrname = arg; break; @@ -520,7 +516,8 @@ d_handle_option (size_t scode, const char *arg, int value, global.params.useOut = !value; global.params.useAssert = !value; // release mode doesn't turn off bounds checking for safe functions. - global.params.useArrayBounds = !value ? 2 : 1; + if (global.params.useArrayBounds != 0) + global.params.useArrayBounds = !value ? 2 : 1; flag_bounds_check = !value; global.params.useSwitchError = !value; break; @@ -546,8 +543,8 @@ d_handle_option (size_t scode, const char *arg, int value, break; case OPT_fXf_: - global.params.doXGeneration = 1; - global.params.xfilename = arg; + global.params.doJsonGeneration = true; + global.params.jsonfilename = arg; break; case OPT_imultilib: @@ -612,10 +609,6 @@ d_post_options (const char ** fn) if (num_in_fnames > 1) flag_unit_at_a_time = 1; - /* Array bounds checking. */ - if (global.params.noboundscheck) - flag_bounds_check = global.params.useArrayBounds = 0; - /* Error about use of deprecated features. */ if (global.params.useDeprecated == 2 && global.params.warnings == 1) global.params.useDeprecated = 0; @@ -624,10 +617,10 @@ d_post_options (const char ** fn) flag_excess_precision_cmdline = EXCESS_PRECISION_STANDARD; if (global.params.useUnitTests) - global.params.useAssert = 1; + global.params.useAssert = true; global.params.symdebug = write_symbols != NO_DEBUG; - //global.params.useInline = flag_inline_functions; + global.params.useInline = flag_inline_functions; global.params.obj = !flag_syntax_only; // Has no effect yet. global.params.pic = flag_pic != 0; @@ -646,7 +639,7 @@ d_add_global_declaration (tree decl) // Write out globals. static void -d_write_global_declarations (void) +d_write_global_declarations() { if (vec_safe_length (global_declarations) != 0) { @@ -708,7 +701,7 @@ d_gimplify_expr (tree *expr_p, gimple_seq *pre_p ATTRIBUTE_UNUSED, Module * -d_gcc_get_output_module (void) +d_gcc_get_output_module() { return output_module; } @@ -823,7 +816,7 @@ deps_write (Module *m) } void -d_parse_file (void) +d_parse_file() { if (global.params.verbose) { @@ -952,7 +945,7 @@ d_parse_file (void) if (m->isDocFile) { - m->gendocfile(); + gendocfile(m); // Remove m from list of modules modules.remove (i); i--; @@ -978,7 +971,7 @@ d_parse_file (void) if (global.params.verbose) fprintf (global.stdmsg, "import %s\n", m->toChars()); - m->genhdrfile(); + genhdrfile(m); } } @@ -1013,9 +1006,20 @@ d_parse_file (void) if (global.errors) goto had_errors; + // Do deferred semantic analysis Module::dprogress = 1; Module::runDeferredSemantic(); + if (Module::deferred.dim) + { + for (size_t i = 0; i < Module::deferred.dim; i++) + { + Dsymbol *sd = Module::deferred[i]; + sd->error("unable to resolve forward reference in definition"); + } + goto had_errors; + } + // Do pass 2 semantic analysis for (size_t i = 0; i < modules.dim; i++) { @@ -1089,13 +1093,13 @@ d_parse_file (void) output_modules.append (&modules); // Generate output files - if (global.params.doXGeneration) + if (global.params.doJsonGeneration) { OutBuffer buf; json_generate(&buf, &modules); // Write buf to file - const char *name = global.params.xfilename; + const char *name = global.params.jsonfilename; if (name && name[0] == '-' && name[1] == 0) { @@ -1125,6 +1129,15 @@ d_parse_file (void) } } + if (global.params.doDocComments && !global.errors && !errorcount) + { + for (size_t i = 0; i < modules.dim; i++) + { + Module *m = modules[i]; + gendocfile(m); + } + } + for (size_t i = 0; i < modules.dim; i++) { Module *m = modules[i]; @@ -1141,12 +1154,6 @@ d_parse_file (void) m->genobjfile (false); } - - if (!global.errors && !errorcount) - { - if (global.params.doDocComments) - m->gendocfile(); - } } // And end the main input file, if the debug writer wants it. @@ -1164,7 +1171,7 @@ d_parse_file (void) } static tree -d_type_for_mode (machine_mode mode, int unsignedp) +d_type_for_mode(machine_mode mode, int unsignedp) { if (mode == QImode) return unsignedp ? ubyte_type_node : byte_type_node; @@ -1179,125 +1186,125 @@ d_type_for_mode (machine_mode mode, int unsignedp) return unsignedp ? ulong_type_node : long_type_node; #if HOST_BITS_PER_WIDE_INT >= 64 - if (mode == TYPE_MODE (cent_type_node)) + if (mode == TYPE_MODE(cent_type_node)) return unsignedp ? ucent_type_node : cent_type_node; #endif - if (mode == TYPE_MODE (float_type_node)) + if (mode == TYPE_MODE(float_type_node)) return float_type_node; - if (mode == TYPE_MODE (double_type_node)) + if (mode == TYPE_MODE(double_type_node)) return double_type_node; - if (mode == TYPE_MODE (long_double_type_node)) + if (mode == TYPE_MODE(long_double_type_node)) return long_double_type_node; - if (mode == TYPE_MODE (build_pointer_type (char8_type_node))) - return build_pointer_type (char8_type_node); + if (mode == TYPE_MODE(build_pointer_type(char8_type_node))) + return build_pointer_type(char8_type_node); - if (mode == TYPE_MODE (build_pointer_type (int_type_node))) - return build_pointer_type (int_type_node); + if (mode == TYPE_MODE(build_pointer_type(int_type_node))) + return build_pointer_type(int_type_node); - if (COMPLEX_MODE_P (mode)) + if (COMPLEX_MODE_P(mode)) { machine_mode inner_mode; tree inner_type; - if (mode == TYPE_MODE (complex_float_type_node)) + if (mode == TYPE_MODE(complex_float_type_node)) return complex_float_type_node; - if (mode == TYPE_MODE (complex_double_type_node)) + if (mode == TYPE_MODE(complex_double_type_node)) return complex_double_type_node; - if (mode == TYPE_MODE (complex_long_double_type_node)) + if (mode == TYPE_MODE(complex_long_double_type_node)) return complex_long_double_type_node; - inner_mode = (machine_mode) GET_MODE_INNER (mode); - inner_type = d_type_for_mode (inner_mode, unsignedp); + inner_mode = (machine_mode) GET_MODE_INNER(mode); + inner_type = d_type_for_mode(inner_mode, unsignedp); if (inner_type != NULL_TREE) - return build_complex_type (inner_type); + return build_complex_type(inner_type); } - else if (VECTOR_MODE_P (mode)) + else if (VECTOR_MODE_P(mode)) { - machine_mode inner_mode = (machine_mode) GET_MODE_INNER (mode); - tree inner_type = d_type_for_mode (inner_mode, unsignedp); + machine_mode inner_mode = (machine_mode) GET_MODE_INNER(mode); + tree inner_type = d_type_for_mode(inner_mode, unsignedp); if (inner_type != NULL_TREE) - return build_vector_type_for_mode (inner_type, mode); + return build_vector_type_for_mode(inner_type, mode); } return 0; } static tree -d_type_for_size (unsigned bits, int unsignedp) +d_type_for_size(unsigned bits, int unsignedp) { - if (bits <= TYPE_PRECISION (byte_type_node)) + if (bits <= TYPE_PRECISION(byte_type_node)) return unsignedp ? ubyte_type_node : byte_type_node; - if (bits <= TYPE_PRECISION (short_type_node)) + if (bits <= TYPE_PRECISION(short_type_node)) return unsignedp ? ushort_type_node : short_type_node; - if (bits <= TYPE_PRECISION (int_type_node)) + if (bits <= TYPE_PRECISION(int_type_node)) return unsignedp ? uint_type_node : int_type_node; - if (bits <= TYPE_PRECISION (long_type_node)) + if (bits <= TYPE_PRECISION(long_type_node)) return unsignedp ? ulong_type_node : long_type_node; return 0; } static tree -d_signed_or_unsigned_type (int unsignedp, tree type) +d_signed_or_unsigned_type(int unsignedp, tree type) { - if (!INTEGRAL_TYPE_P (type) - || TYPE_UNSIGNED (type) == (unsigned) unsignedp) + if (!INTEGRAL_TYPE_P(type) + || TYPE_UNSIGNED(type) == (unsigned) unsignedp) return type; #if HOST_BITS_PER_WIDE_INT >= 64 - if (TYPE_PRECISION (type) == TYPE_PRECISION (cent_type_node)) + if (TYPE_PRECISION(type) == TYPE_PRECISION(cent_type_node)) return unsignedp ? ucent_type_node : cent_type_node; #endif - if (TYPE_PRECISION (type) == TYPE_PRECISION (long_type_node)) + if (TYPE_PRECISION(type) == TYPE_PRECISION(long_type_node)) return unsignedp ? ulong_type_node : long_type_node; - if (TYPE_PRECISION (type) == TYPE_PRECISION (int_type_node)) + if (TYPE_PRECISION(type) == TYPE_PRECISION(int_type_node)) return unsignedp ? uint_type_node : int_type_node; - if (TYPE_PRECISION (type) == TYPE_PRECISION (short_type_node)) + if (TYPE_PRECISION(type) == TYPE_PRECISION(short_type_node)) return unsignedp ? ushort_type_node : short_type_node; - if (TYPE_PRECISION (type) == TYPE_PRECISION (byte_type_node)) + if (TYPE_PRECISION(type) == TYPE_PRECISION(byte_type_node)) return unsignedp ? ubyte_type_node : byte_type_node; return type; } tree -d_unsigned_type (tree type) +d_unsigned_type(tree type) { - return d_signed_or_unsigned_type (1, type); + return d_signed_or_unsigned_type(1, type); } tree -d_signed_type (tree type) +d_signed_type(tree type) { - return d_signed_or_unsigned_type (0, type); + return d_signed_or_unsigned_type(0, type); } // Type promotion for variable arguments. // This is needed for varargs to work on certain targets. static tree -d_type_promotes_to (tree type) +d_type_promotes_to(tree type) { - tree ptype = targetm.promoted_type (type); + tree ptype = targetm.promoted_type(type); if (ptype) return ptype; - if (TYPE_MAIN_VARIANT (type) == float_type_node) + if (TYPE_MAIN_VARIANT(type) == float_type_node) return double_type_node; - if (INTEGRAL_TYPE_P (type) - && (TYPE_PRECISION (type) <= TYPE_PRECISION (integer_type_node))) + if (INTEGRAL_TYPE_P(type) + && (TYPE_PRECISION(type) <= TYPE_PRECISION(integer_type_node))) { // Preserve unsignedness if not really getting any wider. - if (TYPE_UNSIGNED (type) - && (TYPE_PRECISION (type) == TYPE_PRECISION (integer_type_node))) + if (TYPE_UNSIGNED(type) + && (TYPE_PRECISION(type) == TYPE_PRECISION(integer_type_node))) return unsigned_type_node; return integer_type_node; @@ -1311,7 +1318,7 @@ struct binding_level *current_binding_level; struct binding_level *global_binding_level; static binding_level * -alloc_binding_level (void) +alloc_binding_level() { return ggc_cleared_alloc(); } @@ -1321,7 +1328,7 @@ alloc_binding_level (void) otherwise support the backend. */ void -push_binding_level (void) +push_binding_level() { binding_level *new_level = alloc_binding_level(); new_level->level_chain = current_binding_level; @@ -1403,7 +1410,7 @@ pop_binding_level (int keep, int routinebody) // loops forever. static bool -d_global_bindings_p (void) +d_global_bindings_p() { if (current_binding_level == global_binding_level) return true; @@ -1412,7 +1419,7 @@ d_global_bindings_p (void) } void -init_global_binding_level (void) +init_global_binding_level() { global_binding_level = alloc_binding_level(); current_binding_level = global_binding_level; @@ -1447,7 +1454,7 @@ set_decl_binding_chain (tree decl_chain) // Supports dbx and stabs. static tree -d_getdecls (void) +d_getdecls() { if (current_binding_level) return current_binding_level->names; @@ -1574,7 +1581,7 @@ tree d_eh_personality_decl; /* Return the GDC personality function decl. */ static tree -d_eh_personality (void) +d_eh_personality() { if (!d_eh_personality_decl) { @@ -1600,7 +1607,7 @@ d_build_eh_type_type (tree type) } void -d_init_exceptions (void) +d_init_exceptions() { using_eh_for_cleanups(); } diff --git a/gcc/d/d-lang.h b/gcc/d/d-lang.h index 39c4dbfff..b135676ab 100644 --- a/gcc/d/d-lang.h +++ b/gcc/d/d-lang.h @@ -164,7 +164,7 @@ tree d_truthvalue_conversion (tree); void d_add_global_declaration (tree); class Module; -Module *d_gcc_get_output_module (void); +Module *d_gcc_get_output_module(); struct lang_type *build_d_type_lang_specific (Type *t); struct lang_decl *build_d_decl_lang_specific (Declaration *d); @@ -176,20 +176,20 @@ tree d_build_asm_stmt (tree insn_tmpl, tree outputs, tree inputs, tree clobbers) extern const char *iprefix; extern const char *multilib_dir; extern void add_import_paths (bool stdinc); -extern void add_phobos_versyms (void); +extern void add_phobos_versyms(); /* In d-lang.cc */ extern tree d_pushdecl (tree); -extern void push_binding_level (void); +extern void push_binding_level(); extern tree pop_binding_level (int, int); -extern void init_global_binding_level (void); +extern void init_global_binding_level(); extern void set_decl_binding_chain (tree decl_chain); extern tree d_unsigned_type (tree); extern tree d_signed_type (tree); -extern void d_init_exceptions (void); +extern void d_init_exceptions(); extern void d_keep (tree t); extern void d_free (tree t); @@ -201,10 +201,10 @@ extern void set_block (tree); extern const attribute_spec d_builtins_attribute_table[]; extern const attribute_spec d_format_attribute_table[]; tree d_builtin_function (tree); -void d_init_builtins (void); +void d_init_builtins(); void d_register_builtin_type (tree, const char *); -void d_backend_init (void); -void d_backend_term (void); +void d_backend_init(); +void d_backend_term(); class Expression; extern Expression *build_expression (tree cst); diff --git a/gcc/d/d-longdouble.cc b/gcc/d/d-longdouble.cc index aeeb1e54b..329e9531b 100644 --- a/gcc/d/d-longdouble.cc +++ b/gcc/d/d-longdouble.cc @@ -53,7 +53,7 @@ real_properties real_limits[longdouble::NumModes]; // Initialise D floating point property values. void -longdouble::init (void) +longdouble::init() { gcc_assert (sizeof (longdouble) >= sizeof (real_value)); @@ -112,13 +112,13 @@ longdouble::init (void) // Return the hidden real_value from the longdouble type. const real_value & -longdouble::rv (void) const +longdouble::rv() const { return *(const real_value *) this; } real_value & -longdouble::rv (void) +longdouble::rv() { return *(real_value *) this; } @@ -176,7 +176,7 @@ longdouble::set (real_value& r) } longdouble::operator -real_value& (void) +real_value&() { return rv(); } @@ -202,13 +202,13 @@ longdouble::set (double d) // These functions should never be called. longdouble::operator -float (void) +float() { gcc_unreachable(); } longdouble::operator -double (void) +double() { gcc_unreachable(); } @@ -222,7 +222,7 @@ longdouble::set (bool d) } longdouble::operator -bool (void) +bool() { return rv().cl != rvc_zero; } @@ -234,20 +234,20 @@ void longdouble::set (int16_t d) { from_int (Type::tfloat32, d); } void longdouble::set (int32_t d) { from_int (Type::tfloat64, d); } void longdouble::set (int64_t d) { from_int (Type::tfloat80, d); } -longdouble::operator int8_t (void) { return to_int (Type::tint8); } -longdouble::operator int16_t (void) { return to_int (Type::tint16); } -longdouble::operator int32_t (void) { return to_int (Type::tint32); } -longdouble::operator int64_t (void) { return to_int (Type::tint64); } +longdouble::operator int8_t() { return to_int (Type::tint8); } +longdouble::operator int16_t() { return to_int (Type::tint16); } +longdouble::operator int32_t() { return to_int (Type::tint32); } +longdouble::operator int64_t() { return to_int (Type::tint64); } void longdouble::set (uint8_t d) { from_uint (Type::tfloat32, d); } void longdouble::set (uint16_t d) { from_uint (Type::tfloat32, d); } void longdouble::set (uint32_t d) { from_uint (Type::tfloat64, d); } void longdouble::set (uint64_t d) { from_uint (Type::tfloat80, d); } -longdouble::operator uint8_t (void) { return to_uint (Type::tuns8); } -longdouble::operator uint16_t (void) { return to_uint (Type::tuns16); } -longdouble::operator uint32_t (void) { return to_uint (Type::tuns32); } -longdouble::operator uint64_t (void) { return to_uint (Type::tuns64); } +longdouble::operator uint8_t() { return to_uint (Type::tuns8); } +longdouble::operator uint16_t() { return to_uint (Type::tuns16); } +longdouble::operator uint32_t() { return to_uint (Type::tuns32); } +longdouble::operator uint64_t() { return to_uint (Type::tuns64); } // Overload numeric operators for longdouble types. @@ -311,7 +311,7 @@ longdouble::operator % (const longdouble& r) } longdouble -longdouble::operator - (void) +longdouble::operator -() { real_value x = real_value_negate (&rv()); return ldouble (x); @@ -394,7 +394,7 @@ longdouble::formatHex (char fmt, char *buf, unsigned buf_size) const // Dump value of longdouble for debugging purposes. void -longdouble::dump (void) +longdouble::dump() { char buf[128]; format (buf, sizeof (buf)); diff --git a/gcc/d/d-objfile.cc b/gcc/d/d-objfile.cc index 5f6ae3d9a..d35d62d47 100644 --- a/gcc/d/d-objfile.cc +++ b/gcc/d/d-objfile.cc @@ -28,6 +28,7 @@ #include "init.h" #include "module.h" #include "template.h" +#include "nspace.h" #include "dfrontend/target.h" static FuncDeclaration *build_call_function (const char *, vec, bool); @@ -45,7 +46,7 @@ static vec static_dtor_list; // Construct a new Symbol. -Symbol::Symbol (void) +Symbol::Symbol() { this->Sident = NULL; this->prettyIdent = NULL; @@ -63,7 +64,7 @@ Symbol::Symbol (void) void -Dsymbol::toObjFile (int) +Dsymbol::toObjFile(bool) { // Emit the imported symbol to debug. Import *imp = this->isImport(); @@ -137,13 +138,13 @@ Dsymbol::toObjFile (int) { Declaration *d = ((DsymbolExp *) o)->s->isDeclaration(); if (d) - d->toObjFile (0); + d->toObjFile(false); } } } void -AttribDeclaration::toObjFile (int) +AttribDeclaration::toObjFile(bool) { Dsymbols *d = include (NULL, NULL); @@ -153,12 +154,12 @@ AttribDeclaration::toObjFile (int) for (size_t i = 0; i < d->dim; i++) { Dsymbol *s = (*d)[i]; - s->toObjFile (0); + s->toObjFile(false); } } void -PragmaDeclaration::toObjFile (int) +PragmaDeclaration::toObjFile(bool) { if (!global.params.ignoreUnsupportedPragmas) { @@ -168,11 +169,24 @@ PragmaDeclaration::toObjFile (int) warning (loc, "pragma(startaddress) not implemented"); } - AttribDeclaration::toObjFile (0); + AttribDeclaration::toObjFile(false); } void -StructDeclaration::toObjFile (int) +Nspace::toObjFile(bool) +{ + if (isError(this) || !members) + return; + + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + s->toObjFile(false); + } +} + +void +StructDeclaration::toObjFile(bool) { if (type->ty == Terror) { @@ -192,7 +206,7 @@ StructDeclaration::toObjFile (int) toDebug(); // Generate TypeInfo - type->getTypeInfo (NULL); + type->genTypeInfo(NULL); // Generate static initialiser toInitializer(); @@ -207,19 +221,22 @@ StructDeclaration::toObjFile (int) Dsymbol *member = (*members)[i]; // There might be static ctors in the members, and they cannot // be put in separate object files. - member->toObjFile (0); + member->toObjFile(false); } - // Put out xopEquals and xopCmp + // Put out xopEquals, xopCmp and xopHash if (xeq && xeq != xerreq) - xeq->toObjFile (0); + xeq->toObjFile(false); if (xcmp && xcmp != xerrcmp) - xcmp->toObjFile (0); + xcmp->toObjFile(false); + + if (xhash) + xhash->toObjFile(false); } void -ClassDeclaration::toObjFile (int) +ClassDeclaration::toObjFile(bool) { if (type->ty == Terror) { @@ -237,7 +254,7 @@ ClassDeclaration::toObjFile (int) for (size_t i = 0; i < members->dim; i++) { Dsymbol *member = (*members)[i]; - member->toObjFile (0); + member->toObjFile(false); } // Generate C symbols @@ -251,7 +268,7 @@ ClassDeclaration::toObjFile (int) d_finish_symbol (sinit); // Put out the TypeInfo - type->getTypeInfo (NULL); + type->genTypeInfo(NULL); // must be ClassInfo.size size_t offset = CLASSINFO_SIZE; @@ -518,9 +535,9 @@ ClassDeclaration::toObjFile (int) if (tf->ty == Tfunction) { deprecation ("use of %s%s hidden by %s is deprecated. " - "Use 'alias %s.%s %s;' to introduce base class overload set.", + "Use 'alias %s = %s.%s;' to introduce base class overload set.", fd->toPrettyChars(), Parameter::argsTypesToChars(tf->parameters, tf->varargs), toChars(), - fd->parent->toChars(), fd->toChars(), fd->toChars()); + fd->toChars(), fd->parent->toChars(), fd->toChars()); } else deprecation ("use of %s hidden by %s is deprecated", fd->toPrettyChars(), toChars()); @@ -577,7 +594,7 @@ ClassDeclaration::baseVtblOffset (BaseClass *bc) } void -InterfaceDeclaration::toObjFile (int) +InterfaceDeclaration::toObjFile(bool) { if (type->ty == Terror) { @@ -595,15 +612,15 @@ InterfaceDeclaration::toObjFile (int) for (size_t i = 0; i < members->dim; i++) { Dsymbol *member = (*members)[i]; - member->toObjFile (0); + member->toObjFile(false); } // Generate C symbols toSymbol(); // Put out the TypeInfo - type->getTypeInfo (NULL); - type->vtinfo->toObjFile (0); + type->genTypeInfo(NULL); + type->vtinfo->toObjFile(false); /* Put out the ClassInfo. * The layout is: @@ -713,7 +730,7 @@ InterfaceDeclaration::toObjFile (int) } void -EnumDeclaration::toObjFile (int) +EnumDeclaration::toObjFile(bool) { if (semanticRun >= PASSobj) return; @@ -731,7 +748,7 @@ EnumDeclaration::toObjFile (int) toDebug(); // Generate TypeInfo - type->getTypeInfo (NULL); + type->genTypeInfo(NULL); TypeEnum *tc = (TypeEnum *) type; if (tc->sym->members && !type->isZeroInit()) @@ -746,7 +763,7 @@ EnumDeclaration::toObjFile (int) } void -VarDeclaration::toObjFile (int) +VarDeclaration::toObjFile(bool) { if (type->ty == Terror) { @@ -756,7 +773,7 @@ VarDeclaration::toObjFile (int) if (aliassym) { - toAlias()->toObjFile (0); + toAlias()->toObjFile(false); return; } @@ -814,10 +831,9 @@ VarDeclaration::toObjFile (int) } else { - // This is needed for VarDeclarations in mixins that are to be - // local variables of a function. Otherwise, it would be - // enough to make a check for isVarDeclaration() in - // DeclarationExp::toElem. + // This is needed for VarDeclarations in mixins that are to be local + // variables of a function. Otherwise, it would be enough to make + // a check for isVarDeclaration() in DeclarationExp::toElem. if (!isDataseg() && !isMember()) { IRState *irs = current_irstate; @@ -844,33 +860,23 @@ VarDeclaration::toObjFile (int) } void -TypedefDeclaration::toObjFile (int) +TemplateInstance::toObjFile(bool) { - if (type->ty == Terror) - { - error ("had semantic errors when compiling"); - return; - } - - if (global.params.symdebug) - toDebug(); + if (isError (this)|| !members) + return; - // Generate TypeInfo - type->getTypeInfo (NULL); + if (!needsCodegen()) + return; - TypeTypedef *tc = (TypeTypedef *) type; - if (tc->sym->init && !type->isZeroInit()) + for (size_t i = 0; i < members->dim; i++) { - // Generate static initialiser - toInitializer(); - sinit->Sdt = tc->sym->init->toDt(); - sinit->Sreadonly = true; - d_finish_symbol (sinit); + Dsymbol *s = (*members)[i]; + s->toObjFile(false); } } void -TemplateInstance::toObjFile (int) +TemplateMixin::toObjFile(bool) { if (isError (this)|| !members) return; @@ -878,18 +884,12 @@ TemplateInstance::toObjFile (int) for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; - s->toObjFile (0); + s->toObjFile(false); } } void -TemplateMixin::toObjFile (int) -{ - TemplateInstance::toObjFile (0); -} - -void -TypeInfoDeclaration::toObjFile (int) +TypeInfoDeclaration::toObjFile(bool) { Symbol *s = toSymbol(); toDt (&s->Sdt); @@ -1027,6 +1027,18 @@ Module::genmoduleinfo() build_moduleinfo (msym); } +// For nested functions in particular, unnest DECL in the cgraph, +// as all static chain passing is handled by the front-end. + +static void +unnest_function(tree decl) +{ + struct cgraph_node *node = cgraph_node::get_create(decl); + + if (node->origin) + node->unnest(); +} + // Returns true if we want to compile the declaration DSYM. static bool @@ -1073,26 +1085,32 @@ output_declaration_p (Dsymbol *dsym) gcc_assert (global.errors); return false; } - } - - // Nested functions may not have its toObjFile called before the outer - // function is finished. GCC requires that nested functions be finished - // first so we need to arrange for toObjFile to be called earlier. - // If the parent never gets emitted, then neither will fd. - Dsymbol *outer = fd->toParent2(); - if (outer && outer->isFuncDeclaration()) - { - FuncDeclaration *fouter = (FuncDeclaration *) outer; - if (fouter->semanticRun < PASSobj) + FuncDeclaration *fdp = fd->toParent2()->isFuncDeclaration(); + if (fdp && fdp->semanticRun < PASSobj) { - fouter->deferred.push (fd); - return false; + // Parent failed to compile, but errors were gagged. + if (fdp->semantic3Errors) + return false; + + if (UnitTestDeclaration *udp = fdp->isUnitTestDeclaration()) + { + udp->deferredNested.push(fd); + return false; + } } } - if (!fd->needsCodegen()) - return false; + for (FuncDeclaration *fdp = fd; fdp != NULL;) + { + if (!fdp->isInstantiated() && fdp->inNonRoot()) + return false; + + if (!fdp->isNested()) + break; + + fdp = fdp->toParent2()->isFuncDeclaration(); + } } if (flag_emit_templates == TEnone) @@ -1105,37 +1123,44 @@ output_declaration_p (Dsymbol *dsym) // down to assembler language output. void -FuncDeclaration::toObjFile (int) +FuncDeclaration::toObjFile(bool) { - if (!global.params.useUnitTests && isUnitTestDeclaration()) - return; - // Already generated the function. if (semanticRun >= PASSobj) return; - if (!output_declaration_p (this)) + // Not emitting unittest functions. + if (!global.params.useUnitTests && this->isUnitTestDeclaration()) return; tree fndecl = toSymbol()->Stree; + // Do this even if we are not emitting the body. + // Such as when when -fno-emit-templates is in effect. + unnest_function(fndecl); + if (!fbody) { - if (!isNested()) - { - // %% Should set this earlier... - DECL_EXTERNAL (fndecl) = 1; - TREE_PUBLIC (fndecl) = 1; - } rest_of_decl_compilation (fndecl, 1, 0); return; } + if (!output_declaration_p(this)) + return; + if (global.errors) return; // Start generating code for this function. - gcc_assert(semanticRun == PASSsemantic3done); + gcc_assert(this->semanticRun == PASSsemantic3done); + this->semanticRun = PASSobj; + + // Nested functions may not have its toObjFile called before the outer + // function is finished. GCC requires that nested functions be finished + // first so we need to arrange for toObjFile to be called earlier. + FuncDeclaration *fdp = this->toParent2()->isFuncDeclaration(); + if (fdp && fdp->semanticRun < PASSobj) + fdp->toObjFile(false); if (global.params.verbose) fprintf (global.stdmsg, "function %s\n", this->toPrettyChars()); @@ -1240,7 +1265,7 @@ FuncDeclaration::toObjFile (int) } // May change irs->sthis. - this->buildClosure (irs); + build_closure(this, irs); if (vresult) build_local_var (vresult, this); @@ -1348,13 +1373,20 @@ FuncDeclaration::toObjFile (int) if (!errorcount && !global.errors) d_finish_function (this); - semanticRun = PASSobj; - // Process all deferred nested functions. - for (size_t i = 0; i < this->deferred.dim; ++i) + for (size_t i = 0; i < irs->deferred.length(); ++i) { - FuncDeclaration *fd = this->deferred[i]; - fd->toObjFile (0); + FuncDeclaration *fd = irs->deferred[i]; + fd->toObjFile(false); + } + + if (UnitTestDeclaration *ud = this->isUnitTestDeclaration()) + { + for (size_t i = 0; i < ud->deferredNested.dim; ++i) + { + FuncDeclaration *fd = ud->deferredNested[i]; + fd->toObjFile(false); + } } current_function_decl = old_current_function_decl; @@ -1363,80 +1395,10 @@ FuncDeclaration::toObjFile (int) irs->endFunction(); } - -// Closures are implemented by taking the local variables that -// need to survive the scope of the function, and copying them -// into a gc allocated chuck of memory. That chunk, called the -// closure here, is inserted into the linked list of stack -// frames instead of the usual stack frame. - -// If a closure is not required, but FUNC still needs a frame to lower -// nested refs, then instead build custom static chain decl on stack. - -void -FuncDeclaration::buildClosure (IRState *irs) -{ - FuncFrameInfo *ffi = get_frameinfo (this); - - if (!ffi->creates_frame) - return; - - tree type = build_frame_type (this); - gcc_assert(COMPLETE_TYPE_P (type)); - - tree decl, decl_ref; - - if (ffi->is_closure) - { - decl = build_local_temp (build_pointer_type (type)); - DECL_NAME (decl) = get_identifier ("__closptr"); - decl_ref = build_deref (decl); - - // Allocate memory for closure. - tree arg = convert (Type::tsize_t->toCtype(), - TYPE_SIZE_UNIT (type)); - tree init = build_libcall (LIBCALL_ALLOCMEMORY, 1, &arg); - - DECL_INITIAL (decl) = build_nop (TREE_TYPE (decl), init); - } - else - { - decl = build_local_temp (type); - DECL_NAME (decl) = get_identifier ("__frame"); - decl_ref = decl; - } - - DECL_IGNORED_P (decl) = 0; - expand_decl (decl); - - // Set the first entry to the parent closure/frame, if any. - tree chain_field = component_ref (decl_ref, TYPE_FIELDS (type)); - tree chain_expr = vmodify_expr (chain_field, irs->sthis); - irs->addExp (chain_expr); - - // Copy parameters that are referenced nonlocally. - for (size_t i = 0; i < closureVars.dim; i++) - { - VarDeclaration *v = closureVars[i]; - - if (!v->isParameter()) - continue; - - Symbol *vsym = v->toSymbol(); - - tree field = component_ref (decl_ref, vsym->SframeField); - tree expr = vmodify_expr (field, vsym->Stree); - irs->addExp (expr); - } - - if (!ffi->is_closure) - decl = build_address (decl); - - irs->sthis = decl; -} +// void -Module::genobjfile (int) +Module::genobjfile(bool) { // Normally would create an ObjFile here, but gcc is limited to one object // file per pass and there may be more than one module per object file. @@ -1448,7 +1410,7 @@ Module::genobjfile (int) for (size_t i = 0; i < members->dim; i++) { Dsymbol *dsym = (*members)[i]; - dsym->toObjFile (0); + dsym->toObjFile(false); } } @@ -1510,7 +1472,7 @@ output_module_p (Module *m) } void -d_finish_module (void) +d_finish_module() { /* If the target does not directly support static constructors, static_ctor_list contains a list of all static constructors defined @@ -1538,7 +1500,7 @@ get_linemap (const Loc loc) linemap_add (line_table, LC_ENTER, 0, loc.filename, loc.linnum); linemap_line_start (line_table, loc.linnum, 0); - gcc_location = linemap_position_for_column (line_table, 0); + gcc_location = linemap_position_for_column (line_table, loc.charnum); linemap_add (line_table, LC_LEAVE, 0, NULL, 0); return gcc_location; @@ -1907,14 +1869,6 @@ d_finish_function (FuncDeclaration *fd) static_dtor_list.safe_push (fd); } - // Build cgraph for function. - struct cgraph_node *node = cgraph_node::get_create (decl); - - // For nested functions update the cgraph to reflect unnesting, - // which is handled by the front-end. - if (node->origin) - node->unnest(); - cgraph_node::finalize_function (decl, true); } @@ -2011,7 +1965,7 @@ static vec deferred_thunks; // Process all deferred thunks in list DEFERRED_THUNKS. void -write_deferred_thunks (void) +write_deferred_thunks() { for (size_t i = 0; i < deferred_thunks.length(); i++) { @@ -2190,7 +2144,7 @@ build_simple_function (const char *name, tree expr, bool static_ctor) TypeFunction *func_type = new TypeFunction (0, Type::tvoid, 0, LINKc); FuncDeclaration *func = new FuncDeclaration (mod->loc, mod->loc, Lexer::idPool (name), STCstatic, func_type); - func->loc = Loc (mod, 1); + func->loc = Loc(mod, 1, 0); func->linkage = func_type->linkage; func->parent = mod; func->protection = PROTprivate; @@ -2207,9 +2161,9 @@ build_simple_function (const char *name, tree expr, bool static_ctor) TREE_USED (func_decl) = 1; // %% Maybe remove the identifier - WrappedExp *body = new WrappedExp (mod->loc, TOKcomma, expr, Type::tvoid); + WrappedExp *body = new WrappedExp (mod->loc, expr, Type::tvoid); func->fbody = new ExpStatement (mod->loc, body); - func->toObjFile (0); + func->toObjFile(false); return func; } @@ -2230,7 +2184,7 @@ build_call_function (const char *name, vec functions, bool fo Module *mod = current_module_decl; if (!mod) mod = d_gcc_get_output_module(); - set_input_location (Loc (mod, 1)); + set_input_location(Loc(mod, 1, 0)); // Shouldn't front end build these? for (size_t i = 0; i < functions.length(); i++) diff --git a/gcc/d/d-objfile.h b/gcc/d/d-objfile.h index fb81cdf78..835527f6b 100644 --- a/gcc/d/d-objfile.h +++ b/gcc/d/d-objfile.h @@ -46,7 +46,7 @@ typedef tree_node dt_t; struct Symbol { - Symbol (void); + Symbol(); const char *Sident; const char *prettyIdent; @@ -71,7 +71,7 @@ struct Symbol struct Thunk { - Thunk (void) + Thunk() { offset = 0; symbol = NULL; } int offset; @@ -126,7 +126,7 @@ extern void d_comdat_linkage (tree decl); extern void d_finish_symbol (Symbol *sym); extern void d_finish_function (FuncDeclaration *f); -extern void d_finish_module (void); +extern void d_finish_module(); extern void d_finish_compilation (tree *vec, int len); extern void build_type_decl (tree t, Dsymbol *dsym); @@ -134,7 +134,7 @@ extern void build_type_decl (tree t, Dsymbol *dsym); extern Modules output_modules; extern bool output_module_p (Module *mod); -extern void write_deferred_thunks (void); +extern void write_deferred_thunks(); extern void use_thunk (tree thunk_decl, tree target_decl, int offset); extern void finish_thunk (tree thunk_decl, tree target_decl, int offset); diff --git a/gcc/d/d-port.cc b/gcc/d/d-port.cc index aced42a89..97af114c4 100644 --- a/gcc/d/d-port.cc +++ b/gcc/d/d-port.cc @@ -25,7 +25,7 @@ longdouble Port::ldbl_infinity; longdouble Port::ldbl_max; void -Port::init (void) +Port::init() { char buf[128]; machine_mode mode = TYPE_MODE (long_double_type_node); diff --git a/gcc/d/d-spec.cc b/gcc/d/d-spec.cc index 18f0d185b..7ad767a20 100644 --- a/gcc/d/d-spec.cc +++ b/gcc/d/d-spec.cc @@ -561,7 +561,7 @@ lang_specific_driver (cl_decoded_option **in_decoded_options, /* Called before linking. Returns 0 on success and -1 on failure. */ -int lang_specific_pre_link (void) /* Not used for D. */ +int lang_specific_pre_link() /* Not used for D. */ { return 0; } diff --git a/gcc/d/d-target.cc b/gcc/d/d-target.cc index d1b11be18..dc6a5613f 100644 --- a/gcc/d/d-target.cc +++ b/gcc/d/d-target.cc @@ -16,6 +16,9 @@ // . #include "d-system.h" +#include "d-lang.h" +#include "d-codegen.h" + #include "aggregate.h" #include "mtype.h" #include "dfrontend/target.h" @@ -28,16 +31,16 @@ bool Target::reverseCppOverloads; void -Target::init (void) +Target::init() { // Map D frontend type and sizes to GCC backend types. - realsize = int_size_in_bytes (long_double_type_node); - realpad = TYPE_PRECISION (long_double_type_node) / BITS_PER_UNIT; - realalignsize = TYPE_ALIGN_UNIT (long_double_type_node); + realsize = int_size_in_bytes(long_double_type_node); + realpad = TYPE_PRECISION(long_double_type_node) / BITS_PER_UNIT; + realalignsize = TYPE_ALIGN_UNIT(long_double_type_node); reverseCppOverloads = false; // Define what type to use for size_t, ptrdiff_t. - size_t wordsize = int_size_in_bytes (size_type_node); + size_t wordsize = int_size_in_bytes(size_type_node); if (wordsize == 2) Tsize_t = Tuns16; else if (wordsize == 4) @@ -62,39 +65,39 @@ Target::init (void) // Return GCC memory alignment size for type TYPE. unsigned -Target::alignsize (Type *type) +Target::alignsize(Type *type) { - gcc_assert (type->isTypeBasic()); - return TYPE_ALIGN_UNIT (type->toCtype()); + gcc_assert(type->isTypeBasic()); + return TYPE_ALIGN_UNIT(type->toCtype()); } // Return GCC field alignment size for type TYPE. unsigned -Target::fieldalign (Type *type) +Target::fieldalign(Type *type) { // Work out the correct alignment for the field decl. - tree field = make_node (FIELD_DECL); - DECL_ALIGN (field) = type->alignsize() * BITS_PER_UNIT; + tree field = make_node(FIELD_DECL); + DECL_ALIGN(field) = type->alignsize() * BITS_PER_UNIT; #ifdef BIGGEST_FIELD_ALIGNMENT - DECL_ALIGN (field) - = MIN (DECL_ALIGN (field), (unsigned) BIGGEST_FIELD_ALIGNMENT); + DECL_ALIGN(field) + = MIN(DECL_ALIGN(field), (unsigned) BIGGEST_FIELD_ALIGNMENT); #endif #ifdef ADJUST_FIELD_ALIGN if (type->isTypeBasic()) { - TREE_TYPE (field) = type->toCtype(); - DECL_ALIGN (field) = ADJUST_FIELD_ALIGN (field, DECL_ALIGN (field)); + TREE_TYPE(field) = type->toCtype(); + DECL_ALIGN(field) = ADJUST_FIELD_ALIGN(field, DECL_ALIGN(field)); } #endif // Also controlled by -fpack-struct= if (maximum_field_alignment) - DECL_ALIGN (field) = MIN (DECL_ALIGN (field), maximum_field_alignment); + DECL_ALIGN(field) = MIN(DECL_ALIGN(field), maximum_field_alignment); - return DECL_ALIGN_UNIT (field); + return DECL_ALIGN_UNIT(field); } // Return size of OS critical section. @@ -102,7 +105,7 @@ Target::fieldalign (Type *type) // and would end up using the host sizes rather than the target sizes. unsigned -Target::critsecsize (void) +Target::critsecsize() { if (global.params.isWindows) { @@ -140,3 +143,93 @@ Target::critsecsize (void) gcc_unreachable(); } + +// Returns a Type for the va_list type of the target. + +Type * +Target::va_listType() +{ + return Type::tvalist; +} + +// Perform a reinterpret cast of EXPR to type TYPE for use in CTFE. +// The front end should have already ensured that EXPR is a constant, +// so we just lower the value to GCC and return the converted CST. + +Expression * +Target::paintAsType(Expression *expr, Type *type) +{ + /* We support up to 512-bit values. */ + unsigned char buffer[64]; + tree cst; + + Type *tb = type->toBasetype(); + + if (expr->type->isintegral()) + cst = build_integer_cst(expr->toInteger(), expr->type->toCtype()); + else if (expr->type->isfloating()) + cst = build_float_cst(expr->toReal(), expr->type); + else if (expr->op == TOKarrayliteral) + { + // Build array as VECTOR_CST, assumes EXPR is constant. + Expressions *elements = ((ArrayLiteralExp *) expr)->elements; + vec *elms = NULL; + + vec_safe_reserve(elms, elements->dim); + for (size_t i = 0; i < elements->dim; i++) + { + Expression *e = (*elements)[i]; + if (e->type->isintegral()) + { + tree value = build_integer_cst(e->toInteger(), e->type->toCtype()); + CONSTRUCTOR_APPEND_ELT(elms, size_int(i), value); + } + else if (e->type->isfloating()) + { + tree value = build_float_cst(e->toReal(), e->type); + CONSTRUCTOR_APPEND_ELT(elms, size_int(i), value); + } + else + gcc_unreachable(); + } + + // Build vector type. + int nunits = ((TypeSArray *) expr->type)->dim->toUInteger(); + Type *telem = expr->type->nextOf(); + tree vectype = build_vector_type(telem->toCtype(), nunits); + + cst = build_vector_from_ctor(vectype, elms); + } + else + gcc_unreachable(); + + // Encode CST to buffer. + int len = native_encode_expr(cst, buffer, sizeof(buffer)); + + if (tb->ty == Tsarray) + { + // Interpret value as a vector of the same size, + // then return the array literal. + int nunits = ((TypeSArray *) type)->dim->toUInteger(); + Type *elem = type->nextOf(); + tree vectype = build_vector_type(elem->toCtype(), nunits); + + cst = native_interpret_expr(vectype, buffer, len); + + Expression *e = build_expression(cst); + gcc_assert(e != NULL && e->op == TOKvector); + + return ((VectorExp *) e)->e1; + } + else + { + // Normal interpret cast. + cst = native_interpret_expr(type->toCtype(), buffer, len); + + Expression *e = build_expression(cst); + gcc_assert(e != NULL); + + return e; + } +} + diff --git a/gcc/d/d-todt.cc b/gcc/d/d-todt.cc index fe171dab3..b38f67391 100644 --- a/gcc/d/d-todt.cc +++ b/gcc/d/d-todt.cc @@ -27,20 +27,17 @@ #include "dfrontend/target.h" -extern FuncDeclaration *search_toHash(StructDeclaration *sd); -extern FuncDeclaration *search_toString(StructDeclaration *sd); - // Append VAL to constructor PDT. Create a new constructor // of generic type if PDT is not already pointing to one. dt_t ** -dt_cons (dt_t **pdt, tree val) +dt_cons(dt_t **pdt, tree val) { if (*pdt == NULL_TREE) - *pdt = build_constructor (d_unknown_type_node, NULL); + *pdt = build_constructor(d_unknown_type_node, NULL); - CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (*pdt), 0, val); + CONSTRUCTOR_APPEND_ELT(CONSTRUCTOR_ELTS(*pdt), 0, val); return pdt; } @@ -48,16 +45,16 @@ dt_cons (dt_t **pdt, tree val) // values of DT to PDT. dt_t ** -dt_chainon (dt_t **pdt, dt_t *dt) +dt_chainon(dt_t **pdt, dt_t *dt) { - vec *elts = CONSTRUCTOR_ELTS (dt); + vec *elts = CONSTRUCTOR_ELTS(dt); tree value; size_t i; - gcc_assert (*pdt != dt); + gcc_assert(*pdt != dt); - FOR_EACH_CONSTRUCTOR_VALUE (elts, i, value) - dt_cons (pdt, value); + FOR_EACH_CONSTRUCTOR_VALUE(elts, i, value) + dt_cons(pdt, value); return pdt; } @@ -65,11 +62,11 @@ dt_chainon (dt_t **pdt, dt_t *dt) // Add zero padding of size SIZE onto end of PDT. dt_t ** -dt_zeropad (dt_t **pdt, size_t size) +dt_zeropad(dt_t **pdt, size_t size) { - tree type = d_array_type (Type::tuns8, size); - gcc_assert (size != 0); - return dt_cons (pdt, build_constructor (type, NULL)); + tree type = d_array_type(Type::tuns8, size); + gcc_assert(size != 0); + return dt_cons(pdt, build_constructor(type, NULL)); } // It is necessary to give static array data its original @@ -81,13 +78,13 @@ dt_zeropad (dt_t **pdt, size_t size) // be a CONSTRUCTOR, or the CCP pass may use it incorrectly. static tree -dt_container2 (dt_t *dt) +dt_container2(dt_t *dt) { // Generate type on the fly vec *elts = NULL; tree fields = NULL_TREE; - tree aggtype = make_node (RECORD_TYPE); + tree aggtype = make_node(RECORD_TYPE); tree offset = size_zero_node; if (dt != NULL_TREE) @@ -95,37 +92,36 @@ dt_container2 (dt_t *dt) tree value; size_t i; - FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (dt), i, value) + FOR_EACH_CONSTRUCTOR_VALUE(CONSTRUCTOR_ELTS(dt), i, value) { - tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, TREE_TYPE (value)); - tree size = TYPE_SIZE_UNIT (TREE_TYPE (value)); - - DECL_CONTEXT (field) = aggtype; - DECL_FIELD_OFFSET (field) = offset; - DECL_FIELD_BIT_OFFSET (field) = bitsize_zero_node; - SET_DECL_OFFSET_ALIGN (field, TYPE_ALIGN (TREE_TYPE (value))); - DECL_ARTIFICIAL (field) = 1; - DECL_IGNORED_P (field) = 1; - - layout_decl (field, 0); - fields = chainon (fields, field); - CONSTRUCTOR_APPEND_ELT (elts, field, value); - offset = size_binop (PLUS_EXPR, offset, size); + tree field = build_decl(UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, TREE_TYPE(value)); + tree size = TYPE_SIZE_UNIT(TREE_TYPE(value)); + + DECL_CONTEXT(field) = aggtype; + DECL_FIELD_OFFSET(field) = offset; + DECL_FIELD_BIT_OFFSET(field) = bitsize_zero_node; + SET_DECL_OFFSET_ALIGN(field, TYPE_ALIGN(TREE_TYPE(value))); + DECL_ARTIFICIAL(field) = 1; + DECL_IGNORED_P(field) = 1; + + layout_decl(field, 0); + fields = chainon(fields, field); + CONSTRUCTOR_APPEND_ELT(elts, field, value); + offset = size_binop(PLUS_EXPR, offset, size); } } else - dt = build_constructor (aggtype, NULL); + dt = build_constructor(aggtype, NULL); - TYPE_FIELDS (aggtype) = fields; - TYPE_SIZE (aggtype) = size_binop (MULT_EXPR, offset, size_int (BITS_PER_UNIT)); - TYPE_SIZE_UNIT (aggtype) = offset; - compute_record_mode (aggtype); + TYPE_FIELDS(aggtype) = fields; + TYPE_SIZE(aggtype) = size_binop(MULT_EXPR, offset, size_int(BITS_PER_UNIT)); + TYPE_SIZE_UNIT(aggtype) = offset; + compute_record_mode(aggtype); - TREE_TYPE (dt) = aggtype; - CONSTRUCTOR_ELTS (dt) = elts; - TREE_READONLY (dt) = 1; - TREE_STATIC (dt) = 1; - TREE_CONSTANT (dt) = 1; + TREE_TYPE(dt) = aggtype; + CONSTRUCTOR_ELTS(dt) = elts; + TREE_STATIC(dt) = 1; + TREE_CONSTANT(dt) = 1; return dt; } @@ -134,7 +130,7 @@ dt_container2 (dt_t *dt) // DT and append to the dt_t node list PDT. dt_t ** -dt_container (dt_t **pdt, Type *type, dt_t *dt) +dt_container(dt_t **pdt, Type *type, dt_t *dt) { Type *tb = type->toBasetype(); @@ -147,57 +143,62 @@ dt_container (dt_t **pdt, Type *type, dt_t *dt) size_t i; if (dt == NULL) - dt = dt_container2 (dt); + dt = dt_container2(dt); else { - gcc_assert (CONSTRUCTOR_NELTS (dt) == tsa->dim->toInteger()); + gcc_assert(CONSTRUCTOR_NELTS(dt) == tsa->dim->toInteger()); - FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (dt), i, value) - CONSTRUCTOR_APPEND_ELT (elts, size_int (i), value); + FOR_EACH_CONSTRUCTOR_VALUE(CONSTRUCTOR_ELTS(dt), i, value) + CONSTRUCTOR_APPEND_ELT(elts, size_int(i), value); - CONSTRUCTOR_ELTS (dt) = elts; + CONSTRUCTOR_ELTS(dt) = elts; } - TREE_TYPE (dt) = type->toCtype(); - TREE_CONSTANT (dt) = 1; - TREE_READONLY (dt) = 1; - TREE_STATIC (dt) = 1; + TREE_TYPE(dt) = type->toCtype(); + TREE_CONSTANT(dt) = 1; + TREE_STATIC(dt) = 1; - return dt_cons (pdt, dt); + return dt_cons(pdt, dt); } else if (tb->ty == Tstruct) { - dt = dt_container2 (dt); - TREE_TYPE (dt) = type->toCtype(); - return dt_cons (pdt, dt); + dt = dt_container2(dt); + TREE_TYPE(dt) = type->toCtype(); + return dt_cons(pdt, dt); + } + else if (tb->ty == Tclass) + { + dt = dt_container2(dt); + TREE_TYPE(dt) = TREE_TYPE(type->toCtype()); + return dt_cons(pdt, dt); } - return dt_cons (pdt, dtvector_to_tree (dt)); + return dt_cons(pdt, dtvector_to_tree(dt)); } // Return a new CONSTRUCTOR whose values are in a dt_t // list pointed to by DT. tree -dtvector_to_tree (dt_t *dt) +dtvector_to_tree(dt_t *dt) { - if (dt && CONSTRUCTOR_NELTS (dt) == 1) - return CONSTRUCTOR_ELT (dt, 0)->value; + if (dt && CONSTRUCTOR_NELTS(dt) == 1) + return CONSTRUCTOR_ELT(dt, 0)->value; - return dt_container2 (dt); + return dt_container2(dt); } // Put out __vptr and __monitor of class CD into PDT. dt_t ** -build_vptr_monitor (dt_t **pdt, ClassDeclaration *cd) +build_vptr_monitor(dt_t **pdt, ClassDeclaration *cd) { - gcc_assert (cd != NULL); + gcc_assert(cd != NULL); Symbol *s = cd->toVtblSymbol(); - dt_cons (pdt, build_address (s->Stree)); + dt_cons(pdt, build_address(s->Stree)); if (!cd->cpp) - dt_cons (pdt, size_int (0)); + dt_cons(pdt, size_int(0)); return pdt; } @@ -207,14 +208,14 @@ build_vptr_monitor (dt_t **pdt, ClassDeclaration *cd) // Build constructors for front-end Initialisers to be written to data segment. dt_t * -Initializer::toDt (void) +Initializer::toDt() { gcc_unreachable(); return NULL_TREE; } dt_t * -VoidInitializer::toDt (void) +VoidInitializer::toDt() { // void initialisers are set to 0, just because we need something // to set them to in the static data segment. @@ -224,14 +225,14 @@ VoidInitializer::toDt (void) } dt_t * -StructInitializer::toDt (void) +StructInitializer::toDt() { ::error ("StructInitializer::toDt: we shouldn't emit this (%s)", toChars()); gcc_unreachable(); } dt_t * -ArrayInitializer::toDt (void) +ArrayInitializer::toDt() { Type *tb = type->toBasetype(); if (tb->ty == Tvector) @@ -309,7 +310,7 @@ ArrayInitializer::toDt (void) } dt_t * -ExpInitializer::toDt (void) +ExpInitializer::toDt() { tree dt = NULL_TREE; exp = exp->optimize (WANTvalue); @@ -593,7 +594,7 @@ AddrExp::toDt (dt_t **pdt) } dt_t ** -ClassReferenceExp::toDt (dt_t **pdt) +ClassReferenceExp::toDt(dt_t **pdt) { InterfaceDeclaration *to = ((TypeClass *) type)->sym->isInterfaceDeclaration(); @@ -603,32 +604,32 @@ ClassReferenceExp::toDt (dt_t **pdt) // We must add offset to symbol. ClassDeclaration *from = originalClass(); int off = 0; - int isbase = to->isBaseOf (from, &off); - gcc_assert (isbase); + int isbase = to->isBaseOf(from, &off); + gcc_assert(isbase); - return toDtI (pdt, off); + return toDtI(pdt, off); } - return toDtI (pdt, 0); + return toDtI(pdt, 0); } dt_t ** -ClassReferenceExp::toDtI (dt_t **pdt, int off) +ClassReferenceExp::toDtI(dt_t **pdt, int off) { - tree dt = build_address (toSymbol()->Stree); + tree dt = build_address(toSymbol()->Stree); if (off != 0) - dt = build_offset (dt, size_int (off)); + dt = build_offset(dt, size_int(off)); - return dt_cons (pdt, dt); + return dt_cons(pdt, dt); } dt_t ** -ClassReferenceExp::toInstanceDt (dt_t **pdt) +ClassReferenceExp::toInstanceDt(dt_t **pdt) { ClassDeclaration *cd = originalClass(); Dts dts; - dts.setDim (value->elements->dim); + dts.setDim(value->elements->dim); dts.zero(); for (size_t i = 0; i < value->elements->dim; i++) @@ -637,7 +638,7 @@ ClassReferenceExp::toInstanceDt (dt_t **pdt) if (!e) continue; tree dt = NULL_TREE; - e->toDt (&dt); + e->toDt(&dt); dts[i] = dt; } @@ -645,10 +646,13 @@ ClassReferenceExp::toInstanceDt (dt_t **pdt) * void **vptr; * monitor_t monitor; */ - build_vptr_monitor (pdt, cd); + tree cdt = NULL_TREE; + build_vptr_monitor(&cdt, cd); // Put out rest of class fields. - return toDt2 (pdt, cd, &dts); + toDt2(&cdt, cd, &dts); + + return dt_container(pdt, cd->type, cdt); } // Generates the data for the static initializer of class variable. @@ -657,24 +661,30 @@ ClassReferenceExp::toInstanceDt (dt_t **pdt) // this function, being alike to ClassDeclaration::toDt2, recursively builds the dt for all base classes. dt_t ** -ClassReferenceExp::toDt2 (dt_t **pdt, ClassDeclaration *cd, Dts *dts) +ClassReferenceExp::toDt2(dt_t **pdt, ClassDeclaration *cd, Dts *dts) { // Note equivalence of this implementation to class's size_t offset; if (cd->baseClass) { - toDt2 (pdt, cd->baseClass, dts); + toDt2(pdt, cd->baseClass, dts); offset = cd->baseClass->structsize; } else - offset = Target::ptrsize * 2; + { + // Allow room for __vptr and __monitor. + if (cd->cpp) + offset = Target::ptrsize; + else + offset = Target::ptrsize * 2; + } for (size_t i = 0; i < cd->fields.dim; i++) { VarDeclaration *v = cd->fields[i]; - int index = findFieldIndexByName (v); - gcc_assert (index != -1); + int index = findFieldIndexByName(v); + gcc_assert(index != -1); tree fdt = (*dts)[index]; @@ -690,23 +700,23 @@ ClassReferenceExp::toDt2 (dt_t **pdt, ClassDeclaration *cd, Dts *dts) if (!init->isVoidInitializer()) { if (ei && tb->ty == Tsarray) - ((TypeSArray *) tb)->toDtElem (&dt, ei->exp); + ((TypeSArray *) tb)->toDtElem(&dt, ei->exp); else dt = init->toDt(); } } else if (v->offset >= offset) - v->type->toDt (&dt); + v->type->toDt(&dt); if (dt != NULL_TREE) { if (v->offset < offset) - error ("duplicated union initialization for %s", v->toChars()); + error("duplicated union initialization for %s", v->toChars()); else { if (offset < v->offset) - dt_zeropad (pdt, v->offset - offset); - dt_chainon (pdt, dt); + dt_zeropad(pdt, v->offset - offset); + dt_chainon(pdt, dt); offset = v->offset + v->type->size(); } } @@ -715,18 +725,18 @@ ClassReferenceExp::toDt2 (dt_t **pdt, ClassDeclaration *cd, Dts *dts) if (fdt != NULL_TREE) { if (v->offset < offset) - error ("duplicate union initialization for %s", v->toChars()); + error("duplicate union initialization for %s", v->toChars()); else { - size_t sz = int_size_in_bytes (TREE_TYPE (CONSTRUCTOR_ELT (fdt, 0)->value)); + size_t sz = int_size_in_bytes(TREE_TYPE(CONSTRUCTOR_ELT(fdt, 0)->value)); size_t vsz = v->type->size(); size_t voffset = v->offset; size_t dim = 1; if (sz > vsz) { - gcc_assert (v->type->ty == Tsarray && vsz == 0); - error ("zero length array %s has non-zero length initializer", v->toChars()); + gcc_assert(v->type->ty == Tsarray && vsz == 0); + error("zero length array %s has non-zero length initializer", v->toChars()); } for (Type *vt = v->type->toBasetype(); @@ -736,22 +746,22 @@ ClassReferenceExp::toDt2 (dt_t **pdt, ClassDeclaration *cd, Dts *dts) dim *= tsa->dim->toInteger(); } - gcc_assert (sz == vsz || sz * dim <= vsz); + gcc_assert(sz == vsz || sz * dim <= vsz); for (size_t i = 0; i < dim; i++) { if (offset < voffset) - dt_zeropad (pdt, voffset - offset); + dt_zeropad(pdt, voffset - offset); if (fdt == NULL_TREE) { if (v->init) fdt = v->init->toDt(); else - v->type->toDt (&fdt); + v->type->toDt(&fdt); } - dt_chainon (pdt, fdt); + dt_chainon(pdt, fdt); fdt = NULL_TREE; offset = voffset + sz; @@ -772,14 +782,14 @@ ClassReferenceExp::toDt2 (dt_t **pdt, ClassDeclaration *cd, Dts *dts) for (ClassDeclaration *cd2 = originalClass(); 1; cd2 = cd2->baseClass) { - gcc_assert (cd2); - unsigned csymoffset = cd2->baseVtblOffset (b); + gcc_assert(cd2); + unsigned csymoffset = cd2->baseVtblOffset(b); if (csymoffset != (unsigned) ~0) { - tree dt = build_address (cd2->toSymbol()->Stree); + tree dt = build_address(cd2->toSymbol()->Stree); if (offset < (size_t) b->offset) - dt_zeropad (pdt, b->offset - offset); - dt_cons (pdt, build_offset (dt, size_int (csymoffset))); + dt_zeropad(pdt, b->offset - offset); + dt_cons(pdt, build_offset(dt, size_int(csymoffset))); break; } } @@ -788,7 +798,7 @@ ClassReferenceExp::toDt2 (dt_t **pdt, ClassDeclaration *cd, Dts *dts) } if (offset < cd->structsize) - dt_zeropad (pdt, cd->structsize - offset); + dt_zeropad(pdt, cd->structsize - offset); return pdt; } @@ -798,30 +808,39 @@ ClassReferenceExp::toDt2 (dt_t **pdt, ClassDeclaration *cd, Dts *dts) // Generate the data for the static initialiser. void -ClassDeclaration::toDt (dt_t **pdt) +ClassDeclaration::toDt(dt_t **pdt) { /* Put out: * void **vptr; * monitor_t monitor; */ - build_vptr_monitor (pdt, this); + tree cdt = NULL_TREE; + build_vptr_monitor(&cdt, this); // Put out rest of class fields. - toDt2 (pdt, this); + toDt2(&cdt, this); + + dt_container(pdt, type, cdt); } void -ClassDeclaration::toDt2 (dt_t **pdt, ClassDeclaration *cd) +ClassDeclaration::toDt2(dt_t **pdt, ClassDeclaration *cd) { size_t offset; if (baseClass) { - baseClass->toDt2 (pdt, cd); + baseClass->toDt2(pdt, cd); offset = baseClass->structsize; } else - offset = Target::ptrsize * 2; + { + // Allow room for __vptr and __monitor + if (cd->cpp) + offset = Target::ptrsize; + else + offset = Target::ptrsize * 2; + } // Note equivalence of this loop to struct's for (size_t i = 0; i < fields.dim; i++) @@ -837,23 +856,23 @@ ClassDeclaration::toDt2 (dt_t **pdt, ClassDeclaration *cd) if (!init->isVoidInitializer()) { if (ei && tb->ty == Tsarray) - ((TypeSArray *) tb)->toDtElem (&dt, ei->exp); + ((TypeSArray *) tb)->toDtElem(&dt, ei->exp); else dt = init->toDt(); } } else if (v->offset >= offset) - v->type->toDt (&dt); + v->type->toDt(&dt); if (dt != NULL_TREE) { if (v->offset < offset) - error ("duplicated union initialization for %s", v->toChars()); + error("duplicated union initialization for %s", v->toChars()); else { if (offset < v->offset) - dt_zeropad (pdt, v->offset - offset); - dt_chainon (pdt, dt); + dt_zeropad(pdt, v->offset - offset); + dt_chainon(pdt, dt); offset = v->offset + v->type->size(); } } @@ -868,14 +887,14 @@ ClassDeclaration::toDt2 (dt_t **pdt, ClassDeclaration *cd) for (ClassDeclaration *cd2 = cd; 1; cd2 = cd2->baseClass) { - gcc_assert (cd2); - unsigned csymoffset = cd2->baseVtblOffset (b); + gcc_assert(cd2); + unsigned csymoffset = cd2->baseVtblOffset(b); if (csymoffset != (unsigned) ~0) { - tree dt = build_address (cd2->toSymbol()->Stree); + tree dt = build_address(cd2->toSymbol()->Stree); if (offset < (size_t) b->offset) - dt_zeropad (pdt, b->offset - offset); - dt_cons (pdt, build_offset (dt, size_int (csymoffset))); + dt_zeropad(pdt, b->offset - offset); + dt_cons(pdt, build_offset(dt, size_int(csymoffset))); break; } } @@ -884,19 +903,19 @@ ClassDeclaration::toDt2 (dt_t **pdt, ClassDeclaration *cd) } if (offset < structsize) - dt_zeropad (pdt, structsize - offset); + dt_zeropad(pdt, structsize - offset); } void -StructDeclaration::toDt (dt_t **pdt) +StructDeclaration::toDt(dt_t **pdt) { - StructLiteralExp *sle = new StructLiteralExp (loc, this, NULL); + StructLiteralExp *sle = new StructLiteralExp(loc, this, NULL); if (!fill(loc, sle->elements, true)) gcc_unreachable(); sle->type = type; - sle->toDt (pdt); + sle->toDt(pdt); } /* ================================================================ */ @@ -986,17 +1005,6 @@ TypeStruct::toDt (dt_t **pdt) return pdt; } -dt_t ** -TypeTypedef::toDt (dt_t **pdt) -{ - if (sym->init) - dt_chainon (pdt, sym->init->toDt()); - else - sym->basetype->toDt (pdt); - - return pdt; -} - /* ================================================================ */ // Verify the runtime TypeInfo sizes. @@ -1037,7 +1045,7 @@ TypeInfoConstDeclaration::toDt (dt_t **pdt) */ Type *tm = tinfo->mutableOf(); tm = tm->merge(); - tm->getTypeInfo (NULL); + tm->genTypeInfo(NULL); // vtbl and monitor for TypeInfo_Const build_vptr_monitor (pdt, Type::typeinfoconst); @@ -1058,7 +1066,7 @@ TypeInfoInvariantDeclaration::toDt (dt_t **pdt) */ Type *tm = tinfo->mutableOf(); tm = tm->merge(); - tm->getTypeInfo (NULL); + tm->genTypeInfo(NULL); // vtbl and monitor for TypeInfo_Invariant build_vptr_monitor (pdt, Type::typeinfoinvariant); @@ -1079,7 +1087,7 @@ TypeInfoSharedDeclaration::toDt (dt_t **pdt) */ Type *tm = tinfo->unSharedOf(); tm = tm->merge(); - tm->getTypeInfo (NULL); + tm->genTypeInfo(NULL); // vtbl and monitor for TypeInfo_Shared build_vptr_monitor (pdt, Type::typeinfoshared); @@ -1100,7 +1108,7 @@ TypeInfoWildDeclaration::toDt (dt_t **pdt) */ Type *tm = tinfo->mutableOf(); tm = tm->merge(); - tm->getTypeInfo (NULL); + tm->genTypeInfo(NULL); // vtbl and monitor for TypeInfo_Wild build_vptr_monitor (pdt, Type::typeinfowild); @@ -1110,46 +1118,6 @@ TypeInfoWildDeclaration::toDt (dt_t **pdt) } -void -TypeInfoTypedefDeclaration::toDt (dt_t **pdt) -{ - verify_structsize (Type::typeinfotypedef, 7 * Target::ptrsize); - - /* Put out: - * void **vptr; - * monitor_t monitor; - * TypeInfo base; - * char[] name; - * void[] m_init; - */ - gcc_assert (tinfo->ty == Ttypedef); - - TypedefDeclaration *sd = ((TypeTypedef *) tinfo)->sym; - sd->basetype = sd->basetype->merge(); - // Generate vtinfo. - sd->basetype->getTypeInfo (NULL); - gcc_assert (sd->basetype->vtinfo); - - // vtbl and monitor for TypeInfo_Typedef - build_vptr_monitor (pdt, Type::typeinfotypedef); - - // Typeinfo for basetype. - dt_cons (pdt, build_address (sd->basetype->vtinfo->toSymbol()->Stree)); - - // Name of the typedef declaration. - dt_cons (pdt, d_array_string (sd->toPrettyChars())); - - // Default initialiser for typedef. - tree tarray = Type::tvoid->arrayOf()->toCtype(); - if (tinfo->isZeroInit() || !sd->init) - dt_cons (pdt, d_array_value (tarray, size_int (0), null_pointer_node)); - else - { - tree sinit = build_address (sd->toInitializer()->Stree); - dt_cons (pdt, d_array_value (tarray, size_int (sd->type->size()), sinit)); - } -} - void TypeInfoEnumDeclaration::toDt (dt_t **pdt) { @@ -1173,7 +1141,7 @@ TypeInfoEnumDeclaration::toDt (dt_t **pdt) // TypeInfo for enum members. if (sd->memtype) { - sd->memtype->getTypeInfo (NULL); + sd->memtype->genTypeInfo(NULL); dt_cons (pdt, build_address (sd->memtype->vtinfo->toSymbol()->Stree)); } else @@ -1209,7 +1177,7 @@ TypeInfoPointerDeclaration::toDt (dt_t **pdt) gcc_assert (tinfo->ty == Tpointer); TypePointer *tc = (TypePointer *) tinfo; - tc->next->getTypeInfo (NULL); + tc->next->genTypeInfo(NULL); // vtbl and monitor for TypeInfo_Pointer build_vptr_monitor (pdt, Type::typeinfopointer); @@ -1231,7 +1199,7 @@ TypeInfoArrayDeclaration::toDt (dt_t **pdt) gcc_assert (tinfo->ty == Tarray); TypeDArray *tc = (TypeDArray *) tinfo; - tc->next->getTypeInfo (NULL); + tc->next->genTypeInfo(NULL); // vtbl and monitor for TypeInfo_Array build_vptr_monitor (pdt, Type::typeinfoarray); @@ -1254,7 +1222,7 @@ TypeInfoStaticArrayDeclaration::toDt (dt_t **pdt) gcc_assert (tinfo->ty == Tsarray); TypeSArray *tc = (TypeSArray *) tinfo; - tc->next->getTypeInfo (NULL); + tc->next->genTypeInfo(NULL); // vtbl and monitor for TypeInfo_StaticArray build_vptr_monitor (pdt, Type::typeinfostaticarray); @@ -1279,7 +1247,7 @@ TypeInfoVectorDeclaration::toDt (dt_t **pdt) gcc_assert (tinfo->ty == Tvector); TypeVector *tc = (TypeVector *) tinfo; - tc->basetype->getTypeInfo (NULL); + tc->basetype->genTypeInfo(NULL); // vtbl and monitor for TypeInfo_Vector build_vptr_monitor (pdt, Type::typeinfovector); @@ -1291,7 +1259,7 @@ TypeInfoVectorDeclaration::toDt (dt_t **pdt) void TypeInfoAssociativeArrayDeclaration::toDt (dt_t **pdt) { - verify_structsize (Type::typeinfoassociativearray, 5 * Target::ptrsize); + verify_structsize (Type::typeinfoassociativearray, 4 * Target::ptrsize); /* Put out: * void **vptr; @@ -1303,9 +1271,8 @@ TypeInfoAssociativeArrayDeclaration::toDt (dt_t **pdt) gcc_assert (tinfo->ty == Taarray); TypeAArray *tc = (TypeAArray *) tinfo; - tc->next->getTypeInfo (NULL); - tc->index->getTypeInfo (NULL); - tc->getImpl()->type->getTypeInfo (NULL); + tc->next->genTypeInfo(NULL); + tc->index->genTypeInfo(NULL); // vtbl and monitor for TypeInfo_AssociativeArray build_vptr_monitor (pdt, Type::typeinfoassociativearray); @@ -1315,9 +1282,6 @@ TypeInfoAssociativeArrayDeclaration::toDt (dt_t **pdt) // TypeInfo for index of type. dt_cons (pdt, build_address (tc->index->vtinfo->toSymbol()->Stree)); - - // TypeInfo impl; - dt_cons (pdt, build_address (tc->getImpl()->type->vtinfo->toSymbol()->Stree)); } void @@ -1335,7 +1299,7 @@ TypeInfoFunctionDeclaration::toDt (dt_t **pdt) gcc_assert (tinfo->deco); TypeFunction *tc = (TypeFunction *) tinfo; - tc->next->getTypeInfo (NULL); + tc->next->genTypeInfo(NULL); // vtbl and monitor for TypeInfo_Function build_vptr_monitor (pdt, Type::typeinfofunction); @@ -1362,7 +1326,7 @@ TypeInfoDelegateDeclaration::toDt (dt_t **pdt) gcc_assert (tinfo->deco); TypeDelegate *tc = (TypeDelegate *) tinfo; - tc->next->nextOf()->getTypeInfo (NULL); + tc->next->nextOf()->genTypeInfo(NULL); // vtbl and monitor for TypeInfo_Delegate build_vptr_monitor (pdt, Type::typeinfodelegate); @@ -1422,7 +1386,7 @@ TypeInfoStructDeclaration::toDt (dt_t **pdt) dt_cons (pdt, build_address (sd->toInitializer()->Stree)); // hash_t function(in void*) xtoHash; - FuncDeclaration *fdx = search_toHash(sd); + FuncDeclaration *fdx = sd->xhash; if (fdx) { TypeFunction *tf = (TypeFunction *) fdx->type; @@ -1484,7 +1448,7 @@ TypeInfoStructDeclaration::toDt (dt_t **pdt) // TypeInfo m_arg1; if (sd->arg1type) { - sd->arg1type->getTypeInfo (NULL); + sd->arg1type->genTypeInfo(NULL); dt_cons (pdt, build_address (sd->arg1type->vtinfo->toSymbol()->Stree)); } else @@ -1493,7 +1457,7 @@ TypeInfoStructDeclaration::toDt (dt_t **pdt) // TypeInfo m_arg2; if (sd->arg2type) { - sd->arg2type->getTypeInfo (NULL); + sd->arg2type->genTypeInfo(NULL); dt_cons (pdt, build_address (sd->arg2type->vtinfo->toSymbol()->Stree)); } else @@ -1564,7 +1528,7 @@ TypeInfoTupleDeclaration::toDt (dt_t **pdt) for (size_t i = 0; i < tu->arguments->dim; i++) { Parameter *arg = (*tu->arguments)[i]; - Expression *e = arg->type->getTypeInfo (NULL); + Expression *e = arg->type->getTypeInfo(NULL); e = e->optimize (WANTvalue); e->toDt (&dt); } diff --git a/gcc/d/d-typinf.cc b/gcc/d/d-typinf.cc index 314c02c22..ed0e5e3ed 100644 --- a/gcc/d/d-typinf.cc +++ b/gcc/d/d-typinf.cc @@ -23,9 +23,6 @@ #include "declaration.h" #include "aggregate.h" -extern FuncDeclaration *search_toHash(StructDeclaration *sd); -extern FuncDeclaration *search_toString(StructDeclaration *sd); - /******************************************* * Get a canonicalized form of the TypeInfo for use with the internal @@ -72,7 +69,7 @@ Type::getInternalTypeInfo (Scope *sc) internalTI[t->ty] = tid; } e = VarExp::create (Loc(), tid); - e = e->addressOf (sc); + e = e->addressOf(); // do this so we don't get redundant dereference e->type = tid->type; return e; @@ -80,7 +77,7 @@ Type::getInternalTypeInfo (Scope *sc) default: break; } - return t->getTypeInfo (sc); + return t->getTypeInfo(sc); } @@ -88,16 +85,16 @@ Type::getInternalTypeInfo (Scope *sc) * Get the exact TypeInfo. */ -Expression * -Type::getTypeInfo (Scope *sc) +void +Type::genTypeInfo(Scope *sc) { if (!Type::dtypeinfo) { - error (Loc(), "TypeInfo not found. object.d may be incorrectly installed or corrupt"); + error(Loc(), "TypeInfo not found. object.d may be incorrectly installed or corrupt"); fatal(); } - gcc_assert (ty != Terror); + gcc_assert(ty != Terror); // do this since not all Type's are merge'd Type *t = merge2(); @@ -105,17 +102,17 @@ Type::getTypeInfo (Scope *sc) { // does both 'shared' and 'shared const' if (t->isShared()) - t->vtinfo = TypeInfoSharedDeclaration::create (t); + t->vtinfo = TypeInfoSharedDeclaration::create(t); else if (t->isConst()) - t->vtinfo = TypeInfoConstDeclaration::create (t); + t->vtinfo = TypeInfoConstDeclaration::create(t); else if (t->isImmutable()) - t->vtinfo = TypeInfoInvariantDeclaration::create (t); + t->vtinfo = TypeInfoInvariantDeclaration::create(t); else if (t->isWild()) - t->vtinfo = TypeInfoWildDeclaration::create (t); + t->vtinfo = TypeInfoWildDeclaration::create(t); else t->vtinfo = t->getTypeInfoDeclaration(); - gcc_assert (t->vtinfo); + gcc_assert(t->vtinfo); vtinfo = t->vtinfo; /* If this has a custom implementation in std/typeinfo, then @@ -125,80 +122,75 @@ Type::getTypeInfo (Scope *sc) { if (sc) { - // Find module that will go all the way to an object file - Module *m = sc->module->importedFrom; - m->members->push (t->vtinfo); - - if (ty == Tstruct) + if (!sc->func || sc->func->isInstantiated() || !sc->func->inNonRoot()) { - StructDeclaration *sd = ((TypeStruct *) this)->sym; - - if (((sd->xeq && sd->xeq != sd->xerreq) - || (sd->xcmp && sd->xcmp != sd->xerrcmp) - || search_toHash (sd) || search_toString (sd)) - && sd->inNonRoot()) - Module::addDeferredSemantic3 (sd); + // Find module that will go all the way to an object file + Module *m = sc->module->importedFrom; + m->members->push(t->vtinfo); + semanticTypeInfo(sc, t); } } else - t->vtinfo->toObjFile (0); + t->vtinfo->toObjFile(0); } } // Types aren't merged, but we can share the vtinfo's if (!vtinfo) vtinfo = t->vtinfo; - Expression *e = VarExp::create (Loc(), t->vtinfo); - e = e->addressOf (sc); - // do this so we don't get redundant dereference - e->type = t->vtinfo->type; - return e; + gcc_assert(vtinfo != NULL); } -TypeInfoDeclaration * -Type::getTypeInfoDeclaration (void) +Expression * +Type::getTypeInfo(Scope *sc) { - return TypeInfoDeclaration::create (this, 0); + gcc_assert(this->ty != Terror); + this->genTypeInfo(sc); + Expression *e = VarExp::create(Loc(), this->vtinfo); + e = e->addressOf(); + // do this so we don't get redundant dereference + e->type = this->vtinfo->type; + return e; } TypeInfoDeclaration * -TypeTypedef::getTypeInfoDeclaration (void) +Type::getTypeInfoDeclaration() { - return TypeInfoTypedefDeclaration::create (this); + return TypeInfoDeclaration::create (this, 0); } TypeInfoDeclaration * -TypePointer::getTypeInfoDeclaration (void) +TypePointer::getTypeInfoDeclaration() { return TypeInfoPointerDeclaration::create (this); } TypeInfoDeclaration * -TypeDArray::getTypeInfoDeclaration (void) +TypeDArray::getTypeInfoDeclaration() { return TypeInfoArrayDeclaration::create (this); } TypeInfoDeclaration * -TypeSArray::getTypeInfoDeclaration (void) +TypeSArray::getTypeInfoDeclaration() { return TypeInfoStaticArrayDeclaration::create (this); } TypeInfoDeclaration * -TypeAArray::getTypeInfoDeclaration (void) +TypeAArray::getTypeInfoDeclaration() { return TypeInfoAssociativeArrayDeclaration::create (this); } TypeInfoDeclaration * -TypeStruct::getTypeInfoDeclaration (void) +TypeStruct::getTypeInfoDeclaration() { return TypeInfoStructDeclaration::create (this); } TypeInfoDeclaration * -TypeClass::getTypeInfoDeclaration (void) +TypeClass::getTypeInfoDeclaration() { if (sym->isInterfaceDeclaration()) return TypeInfoInterfaceDeclaration::create (this); @@ -207,31 +199,31 @@ TypeClass::getTypeInfoDeclaration (void) } TypeInfoDeclaration * -TypeVector::getTypeInfoDeclaration (void) +TypeVector::getTypeInfoDeclaration() { return TypeInfoVectorDeclaration::create (this); } TypeInfoDeclaration * -TypeEnum::getTypeInfoDeclaration (void) +TypeEnum::getTypeInfoDeclaration() { return TypeInfoEnumDeclaration::create (this); } TypeInfoDeclaration * -TypeFunction::getTypeInfoDeclaration (void) +TypeFunction::getTypeInfoDeclaration() { return TypeInfoFunctionDeclaration::create (this); } TypeInfoDeclaration * -TypeDelegate::getTypeInfoDeclaration (void) +TypeDelegate::getTypeInfoDeclaration() { return TypeInfoDelegateDeclaration::create (this); } TypeInfoDeclaration * -TypeTuple::getTypeInfoDeclaration (void) +TypeTuple::getTypeInfoDeclaration() { return TypeInfoTupleDeclaration::create (this); } @@ -243,19 +235,19 @@ TypeTuple::getTypeInfoDeclaration (void) */ int -Type::builtinTypeInfo (void) +Type::builtinTypeInfo() { return 0; } int -TypeBasic::builtinTypeInfo (void) +TypeBasic::builtinTypeInfo() { return mod ? 0 : 1; } int -TypeDArray::builtinTypeInfo (void) +TypeDArray::builtinTypeInfo() { // Strings are so common, make them builtin. return !mod @@ -265,7 +257,7 @@ TypeDArray::builtinTypeInfo (void) } int -TypeClass::builtinTypeInfo (void) +TypeClass::builtinTypeInfo() { /* This is statically put out with the ClassInfo, so * claim it is built in so it isn't regenerated by each module. @@ -273,35 +265,3 @@ TypeClass::builtinTypeInfo (void) return mod ? 0 : 1; } -/* ========================================================================= */ - -/*************************************** - * Create a static array of TypeInfo references - * corresponding to an array of Expression's. - * Used to supply hidden _arguments[] value for variadic D functions. - */ - -Expression * -createTypeInfoArray (Scope *sc, Expression *exps[], size_t dim) -{ - /* - * Pass a reference to the TypeInfo_Tuple corresponding to the types of the - * arguments. Source compatibility is maintained by computing _arguments[] - * at the start of the called function by offseting into the TypeInfo_Tuple - * reference. - */ - Parameters *args = new Parameters; - args->setDim (dim); - for (size_t i = 0; i < dim; i++) - { - Parameter *arg = Parameter::create (STCin, exps[i]->type, NULL, NULL); - (*args)[i] = arg; - } - TypeTuple *tup = TypeTuple::create (args); - Expression *e = tup->getTypeInfo (sc); - e = e->optimize (WANTvalue); - gcc_assert (e->op == TOKsymoff); - - return e; -} - diff --git a/gcc/d/dfrontend/aav.c b/gcc/d/dfrontend/aav.c index 170549381..08748525e 100644 --- a/gcc/d/dfrontend/aav.c +++ b/gcc/d/dfrontend/aav.c @@ -1,11 +1,11 @@ -// Copyright (c) 2010-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Copyright (c) 2010-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/aav.c + */ /** * Implementation of associative arrays. @@ -47,7 +47,7 @@ struct AA * Determine number of entries in associative array. */ -size_t _aaLen(AA* aa) +size_t dmd_aaLen(AA* aa) { return aa ? aa->nodes : 0; } @@ -58,7 +58,7 @@ size_t _aaLen(AA* aa) * Add entry for key if it is not already there. */ -Value* _aaGet(AA** paa, Key key) +Value* dmd_aaGet(AA** paa, Key key) { //printf("paa = %p\n", paa); @@ -102,7 +102,7 @@ Value* _aaGet(AA** paa, Key key) if (nodes > (*paa)->b_length * 2) { //printf("rehash\n"); - _aaRehash(paa); + dmd_aaRehash(paa); } return &e->value; @@ -114,7 +114,7 @@ Value* _aaGet(AA** paa, Key key) * Returns NULL if it is not already there. */ -Value _aaGetRvalue(AA* aa, Key key) +Value dmd_aaGetRvalue(AA* aa, Key key) { //printf("_aaGetRvalue(key = %p)\n", key); if (aa) @@ -138,7 +138,7 @@ Value _aaGetRvalue(AA* aa, Key key) * Rehash an array. */ -void _aaRehash(AA** paa) +void dmd_aaRehash(AA** paa) { //printf("Rehash\n"); if (*paa) @@ -179,12 +179,12 @@ void _aaRehash(AA** paa) void unittest_aa() { AA* aa = NULL; - Value v = _aaGetRvalue(aa, NULL); + Value v = dmd_aaGetRvalue(aa, NULL); assert(!v); - Value *pv = _aaGet(&aa, NULL); + Value *pv = dmd_aaGet(&aa, NULL); assert(pv); *pv = (void *)3; - v = _aaGetRvalue(aa, NULL); + v = dmd_aaGetRvalue(aa, NULL); assert(v == (void *)3); } diff --git a/gcc/d/dfrontend/aav.h b/gcc/d/dfrontend/aav.h index 201ca48f1..d41825034 100644 --- a/gcc/d/dfrontend/aav.h +++ b/gcc/d/dfrontend/aav.h @@ -1,19 +1,19 @@ -// Copyright (c) 2010-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Copyright (c) 2010-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/aav.h + */ typedef void* Value; typedef void* Key; struct AA; -size_t _aaLen(AA* aa); -Value* _aaGet(AA** aa, Key key); -Value _aaGetRvalue(AA* aa, Key key); -void _aaRehash(AA** paa); +size_t dmd_aaLen(AA* aa); +Value* dmd_aaGet(AA** aa, Key key); +Value dmd_aaGetRvalue(AA* aa, Key key); +void dmd_aaRehash(AA** paa); diff --git a/gcc/d/dfrontend/access.c b/gcc/d/dfrontend/access.c index 8c2bd628c..f16a3f3b5 100644 --- a/gcc/d/dfrontend/access.c +++ b/gcc/d/dfrontend/access.c @@ -1,12 +1,12 @@ - -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. - +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/access.c + */ #include #include @@ -32,26 +32,25 @@ /* Code to do access checks */ -int hasPackageAccess(Scope *sc, Dsymbol *s); +bool hasPackageAccess(Scope *sc, Dsymbol *s); +bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember); +bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd); /**************************************** * Return PROT access for Dsymbol smember in this declaration. */ -PROT AggregateDeclaration::getAccess(Dsymbol *smember) -{ - return PROTpublic; -} - -PROT StructDeclaration::getAccess(Dsymbol *smember) +PROT getAccess(AggregateDeclaration *ad, Dsymbol *smember) { PROT access_ret = PROTnone; #if LOG - printf("+StructDeclaration::getAccess(this = '%s', smember = '%s')\n", - toChars(), smember->toChars()); + printf("+AggregateDeclaration::getAccess(this = '%s', smember = '%s')\n", + ad->toChars(), smember->toChars()); #endif - if (smember->toParent() == this) + + assert(ad->isStructDeclaration() || ad->isClassDeclaration()); + if (smember->toParent() == ad) { access_ret = smember->prot(); } @@ -59,32 +58,13 @@ PROT StructDeclaration::getAccess(Dsymbol *smember) { access_ret = smember->prot(); } - return access_ret; -} - -PROT ClassDeclaration::getAccess(Dsymbol *smember) -{ - PROT access_ret = PROTnone; - -#if LOG - printf("+ClassDeclaration::getAccess(this = '%s', smember = '%s')\n", - toChars(), smember->toChars()); -#endif - if (smember->toParent() == this) - { - access_ret = smember->prot(); - } - else + if (ClassDeclaration *cd = ad->isClassDeclaration()) { - if (smember->isDeclaration()->isStatic()) + for (size_t i = 0; i < cd->baseclasses->dim; i++) { - access_ret = smember->prot(); - } + BaseClass *b = (*cd->baseclasses)[i]; - for (size_t i = 0; i < baseclasses->dim; i++) - { BaseClass *b = (*baseclasses)[i]; - - PROT access = b->base->getAccess(smember); + PROT access = getAccess(b->base, smember); switch (access) { case PROTnone: @@ -112,9 +92,10 @@ PROT ClassDeclaration::getAccess(Dsymbol *smember) } } } + #if LOG - printf("-ClassDeclaration::getAccess(this = '%s', smember = '%s') = %d\n", - toChars(), smember->toChars(), access_ret); + printf("-AggregateDeclaration::getAccess(this = '%s', smember = '%s') = %d\n", + ad->toChars(), smember->toChars(), access_ret); #endif return access_ret; } @@ -140,8 +121,8 @@ static int accessCheckX( sfunc ? sfunc->toChars() : "NULL", cdscope ? cdscope->toChars() : "NULL"); #endif - if (dthis->hasPrivateAccess(sfunc) || - dthis->isFriendOf(cdscope)) + if (hasPrivateAccess(dthis, sfunc) || + isFriendOf(dthis, cdscope)) { if (smember->toParent() == dthis) return 1; @@ -152,7 +133,7 @@ static int accessCheckX( { for (size_t i = 0; i < cdthis->baseclasses->dim; i++) { BaseClass *b = (*cdthis->baseclasses)[i]; - PROT access = b->base->getAccess(smember); + PROT access = getAccess(b->base, smember); if (access >= PROTprotected || accessCheckX(smember, sfunc, b->base, cdscope) ) @@ -186,7 +167,7 @@ static int accessCheckX( * type of the 'this' pointer used to access smember. */ -void AggregateDeclaration::accessCheck(Loc loc, Scope *sc, Dsymbol *smember) +void accessCheck(AggregateDeclaration *ad, Loc loc, Scope *sc, Dsymbol *smember) { int result; @@ -196,7 +177,7 @@ void AggregateDeclaration::accessCheck(Loc loc, Scope *sc, Dsymbol *smember) #if LOG printf("AggregateDeclaration::accessCheck() for %s.%s in function %s() in scope %s\n", - toChars(), smember->toChars(), + ad->toChars(), smember->toChars(), f ? f->toChars() : NULL, cdscope ? cdscope->toChars() : NULL); #endif @@ -213,26 +194,26 @@ void AggregateDeclaration::accessCheck(Loc loc, Scope *sc, Dsymbol *smember) // BUG: should enable this check //assert(smember->parent->isBaseOf(this, NULL)); - if (smemberparent == this) - { PROT access2 = smember->prot(); - + if (smemberparent == ad) + { + PROT access2 = smember->prot(); result = access2 >= PROTpublic || - hasPrivateAccess(f) || - isFriendOf(cdscope) || - (access2 == PROTpackage && hasPackageAccess(sc, this)) || - getAccessModule() == sc->module; + hasPrivateAccess(ad, f) || + isFriendOf(ad, cdscope) || + (access2 == PROTpackage && hasPackageAccess(sc, ad)) || + ad->getAccessModule() == sc->module; #if LOG printf("result1 = %d\n", result); #endif } - else if ((access = this->getAccess(smember)) >= PROTpublic) + else if ((access = getAccess(ad, smember)) >= PROTpublic) { result = 1; #if LOG printf("result2 = %d\n", result); #endif } - else if (access == PROTpackage && hasPackageAccess(sc, this)) + else if (access == PROTpackage && hasPackageAccess(sc, ad)) { result = 1; #if LOG @@ -241,14 +222,14 @@ void AggregateDeclaration::accessCheck(Loc loc, Scope *sc, Dsymbol *smember) } else { - result = accessCheckX(smember, f, this, cdscope); + result = accessCheckX(smember, f, ad, cdscope); #if LOG printf("result4 = %d\n", result); #endif } if (!result) { - error(loc, "member %s is not accessible", smember->toChars()); + ad->error(loc, "member %s is not accessible", smember->toChars()); } } @@ -256,35 +237,35 @@ void AggregateDeclaration::accessCheck(Loc loc, Scope *sc, Dsymbol *smember) * Determine if this is the same or friend of cd. */ -int AggregateDeclaration::isFriendOf(AggregateDeclaration *cd) +bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd) { #if LOG - printf("AggregateDeclaration::isFriendOf(this = '%s', cd = '%s')\n", toChars(), cd ? cd->toChars() : "null"); + printf("AggregateDeclaration::isFriendOf(this = '%s', cd = '%s')\n", ad->toChars(), cd ? cd->toChars() : "null"); #endif - if (this == cd) - return 1; + if (ad == cd) + return true; // Friends if both are in the same module //if (toParent() == cd->toParent()) - if (cd && getAccessModule() == cd->getAccessModule()) + if (cd && ad->getAccessModule() == cd->getAccessModule()) { #if LOG printf("\tin same module\n"); #endif - return 1; + return true; } #if LOG printf("\tnot friend\n"); #endif - return 0; + return false; } /**************************************** * Determine if scope sc has package level access to s. */ -int hasPackageAccess(Scope *sc, Dsymbol *s) +bool hasPackageAccess(Scope *sc, Dsymbol *s) { #if LOG printf("hasPackageAccess(s = '%s', sc = '%p')\n", s->toChars(), sc); @@ -300,9 +281,8 @@ int hasPackageAccess(Scope *sc, Dsymbol *s) Dsymbol *s2 = dst->lookup(m->ident); assert(s2); Package *p = s2->isPackage(); - if (p && p->isPkgMod == PKGmodule) + if (p && p->isPackageMod()) { - assert(p->mod == m); pkg = p; break; } @@ -322,14 +302,14 @@ int hasPackageAccess(Scope *sc, Dsymbol *s) #if LOG printf("\ts is in same package as sc\n"); #endif - return 1; + return true; } - if (pkg->isPkgMod == PKGmodule && pkg->mod == sc->module) + if (pkg->isPackageMod() == sc->module) { #if LOG printf("\ts is in same package.d module as sc\n"); #endif - return 1; + return true; } s = sc->module->parent; for (; s; s = s->parent) @@ -339,7 +319,7 @@ int hasPackageAccess(Scope *sc, Dsymbol *s) #if LOG printf("\ts is in ancestor package of sc\n"); #endif - return 1; + return true; } } } @@ -347,61 +327,63 @@ int hasPackageAccess(Scope *sc, Dsymbol *s) #if LOG printf("\tno package access\n"); #endif - return 0; + return false; } /********************************** * Determine if smember has access to private members of this declaration. */ -int AggregateDeclaration::hasPrivateAccess(Dsymbol *smember) +bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember) { if (smember) - { AggregateDeclaration *cd = NULL; + { + AggregateDeclaration *cd = NULL; Dsymbol *smemberparent = smember->toParent(); if (smemberparent) cd = smemberparent->isAggregateDeclaration(); #if LOG printf("AggregateDeclaration::hasPrivateAccess(class %s, member %s)\n", - toChars(), smember->toChars()); + ad->toChars(), smember->toChars()); #endif - if (this == cd) // smember is a member of this class + if (ad == cd) // smember is a member of this class { #if LOG printf("\tyes 1\n"); #endif - return 1; // so we get private access + return true; // so we get private access } // If both are members of the same module, grant access while (1) - { Dsymbol *sp = smember->toParent(); + { + Dsymbol *sp = smember->toParent(); if (sp->isFuncDeclaration() && smember->isFuncDeclaration()) smember = sp; else break; } - if (!cd && toParent() == smember->toParent()) + if (!cd && ad->toParent() == smember->toParent()) { #if LOG printf("\tyes 2\n"); #endif - return 1; + return true; } - if (!cd && getAccessModule() == smember->getAccessModule()) + if (!cd && ad->getAccessModule() == smember->getAccessModule()) { #if LOG printf("\tyes 3\n"); #endif - return 1; + return true; } } #if LOG printf("\tno\n"); #endif - return 0; + return false; } /**************************************** @@ -415,7 +397,8 @@ void accessCheck(Loc loc, Scope *sc, Expression *e, Declaration *d) #if LOG if (e) - { printf("accessCheck(%s . %s)\n", e->toChars(), d->toChars()); + { + printf("accessCheck(%s . %s)\n", e->toChars(), d->toChars()); printf("\te->type = %s\n", e->type->toChars()); } else @@ -447,12 +430,12 @@ void accessCheck(Loc loc, Scope *sc, Expression *e, Declaration *d) if (cd2) cd = cd2; } - cd->accessCheck(loc, sc, d); + accessCheck(cd, loc, sc, d); } else if (e->type->ty == Tstruct) { // Do access check StructDeclaration *cd = (StructDeclaration *)(((TypeStruct *)e->type)->sym); - cd->accessCheck(loc, sc, d); + accessCheck(cd, loc, sc, d); } } diff --git a/gcc/d/dfrontend/aggregate.h b/gcc/d/dfrontend/aggregate.h index 30818e207..c9f948831 100644 --- a/gcc/d/dfrontend/aggregate.h +++ b/gcc/d/dfrontend/aggregate.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/aggregate.h + */ #ifndef DMD_AGGREGATE_H #define DMD_AGGREGATE_H @@ -53,19 +54,30 @@ enum StructPOD ISPODfwd, // POD not yet computed }; +FuncDeclaration *hasIdentityOpAssign(AggregateDeclaration *ad, Scope *sc); +FuncDeclaration *buildOpAssign(StructDeclaration *sd, Scope *sc); +bool needOpEquals(StructDeclaration *sd); +FuncDeclaration *buildOpEquals(StructDeclaration *sd, Scope *sc); +FuncDeclaration *buildXopEquals(StructDeclaration *sd, Scope *sc); +FuncDeclaration *buildXopCmp(StructDeclaration *sd, Scope *sc); +FuncDeclaration *buildXtoHash(StructDeclaration *ad, Scope *sc); +FuncDeclaration *buildCpCtor(StructDeclaration *sd, Scope *sc); +FuncDeclaration *buildPostBlit(StructDeclaration *sd, Scope *sc); +FuncDeclaration *buildDtor(AggregateDeclaration *ad, Scope *sc); +FuncDeclaration *buildInv(AggregateDeclaration *ad, Scope *sc); + class AggregateDeclaration : public ScopeDsymbol { public: Type *type; StorageClass storage_class; PROT protection; - Type *handle; // 'this' type unsigned structsize; // size of struct unsigned alignsize; // size of struct for alignment purposes VarDeclarations fields; // VarDeclaration fields Sizeok sizeok; // set when structsize contains valid data Dsymbol *deferred; // any deferred semantic2() or semantic3() symbol - bool isdeprecated; // !=0 if deprecated + bool isdeprecated; // true if deprecated Dsymbol *enclosing; /* !=NULL if is nested * pointing to the dsymbol that directly enclosing it. @@ -96,7 +108,6 @@ class AggregateDeclaration : public ScopeDsymbol void setScope(Scope *sc); void semantic2(Scope *sc); void semantic3(Scope *sc); - void inlineScan(); unsigned size(Loc loc); static void alignmember(structalign_t salign, unsigned size, unsigned *poffset); static unsigned placeField(unsigned *nextoffset, @@ -106,29 +117,15 @@ class AggregateDeclaration : public ScopeDsymbol int firstFieldInUnion(int indx); // first field in union that includes indx int numFieldsInUnion(int firstIndex); // #fields in union starting at index bool isDeprecated(); // is aggregate deprecated? - FuncDeclaration *buildDtor(Scope *sc); - FuncDeclaration *buildInv(Scope *sc); bool isNested(); void makeNested(); bool isExport(); - void searchCtor(); - - void emitComment(Scope *sc); - void toDocBuffer(OutBuffer *buf, Scope *sc); - - FuncDeclaration *hasIdentityOpAssign(Scope *sc); - FuncDeclaration *hasIdentityOpEquals(Scope *sc); - - const char *mangle(bool isv = false); - - // For access checking - virtual PROT getAccess(Dsymbol *smember); // determine access to smember - int isFriendOf(AggregateDeclaration *cd); - int hasPrivateAccess(Dsymbol *smember); // does smember have private access to members of this class? - void accessCheck(Loc loc, Scope *sc, Dsymbol *smember); + Dsymbol *searchCtor(); PROT prot(); + Type *handleType() { return type; } // 'this' type + // Back end Symbol *stag; // tag symbol for debug data Symbol *sinit; @@ -151,14 +148,15 @@ class StructDeclaration : public AggregateDeclaration { public: int zeroInit; // !=0 if initialize with 0 fill - int hasIdentityAssign; // !=0 if has identity opAssign - int hasIdentityEquals; // !=0 if has identity opEquals + bool hasIdentityAssign; // true if has identity opAssign + bool hasIdentityEquals; // true if has identity opEquals FuncDeclaration *cpctor; // generated copy-constructor, if any FuncDeclarations postblits; // Array of postblit functions FuncDeclaration *postblit; // aggregate postblit FuncDeclaration *xeq; // TypeInfo_Struct.xopEquals FuncDeclaration *xcmp; // TypeInfo_Struct.xopCmp + FuncDeclaration *xhash; // TypeInfo_Struct.xtoHash static FuncDeclaration *xerreq; // object.xopEquals static FuncDeclaration *xerrcmp; // object.xopCmp @@ -172,26 +170,16 @@ class StructDeclaration : public AggregateDeclaration StructDeclaration(Loc loc, Identifier *id); Dsymbol *syntaxCopy(Dsymbol *s); void semantic(Scope *sc); + void semanticTypeInfoMembers(); Dsymbol *search(Loc, Identifier *ident, int flags = IgnoreNone); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - const char *mangle(bool isv = false); const char *kind(); void finalizeSize(Scope *sc); + bool fit(Loc loc, Scope *sc, Expressions *elements, Type *stype); bool fill(Loc loc, Expressions *elements, bool ctorinit); bool isPOD(); - int needOpAssign(); - int needOpEquals(); - FuncDeclaration *buildOpAssign(Scope *sc); - FuncDeclaration *buildPostBlit(Scope *sc); - FuncDeclaration *buildCpCtor(Scope *sc); - FuncDeclaration *buildOpEquals(Scope *sc); - FuncDeclaration *buildXopEquals(Scope *sc); - FuncDeclaration *buildXopCmp(Scope *sc); - void toDocBuffer(OutBuffer *buf, Scope *sc); - - PROT getAccess(Dsymbol *smember); // determine access to smember - - void toObjFile(int multiobj); // compile to .obj file + + void toObjFile(bool multiobj); // compile to .obj file void toDt(dt_t **pdt); void toDebug(); // to symbolic debug info @@ -221,13 +209,14 @@ struct BaseClass // making up the vtbl[] size_t baseInterfaces_dim; - BaseClass *baseInterfaces; // if BaseClass is an interface, these - // are a copy of the InterfaceDeclaration::interfaces + // if BaseClass is an interface, these + // are a copy of the InterfaceDeclaration::interfaces + BaseClass *baseInterfaces; BaseClass(); BaseClass(Type *type, PROT protection); - int fillVtbl(ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance); + bool fillVtbl(ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance); void copyBaseInterfaces(BaseClasses *); }; @@ -279,13 +268,12 @@ class ClassDeclaration : public AggregateDeclaration // their own vtbl[] TypeInfoClassDeclaration *vclassinfo; // the ClassInfo object for this ClassDeclaration - int com; // !=0 if this is a COM class (meaning - // it derives from IUnknown) - int cpp; // !=0 if this is a C++ interface - int isscope; // !=0 if this is an auto class - int isabstract; // !=0 if abstract class + bool com; // true if this is a COM class (meaning it derives from IUnknown) + bool cpp; // true if this is a C++ interface + bool isscope; // true if this is a scope class + bool isabstract; // true if abstract class int inuse; // to prevent recursive attempts - Semantic doAncestorsSemantic; // Before searching symbol, whole ancestors should finish + Semantic doAncestorsSemantic; // Before searching symbol, whole ancestors should finish // calling semantic() at least once, due to fill symtab // and do addMember(). [== Semantic(Start,In,Done)] @@ -293,33 +281,29 @@ class ClassDeclaration : public AggregateDeclaration Dsymbol *syntaxCopy(Dsymbol *s); void semantic(Scope *sc); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - int isBaseOf2(ClassDeclaration *cd); + bool isBaseOf2(ClassDeclaration *cd); #define OFFSET_RUNTIME 0x76543210 - virtual int isBaseOf(ClassDeclaration *cd, int *poffset); + virtual bool isBaseOf(ClassDeclaration *cd, int *poffset); - virtual int isBaseInfoComplete(); + bool isBaseInfoComplete(); Dsymbol *search(Loc, Identifier *ident, int flags = IgnoreNone); ClassDeclaration *searchBase(Loc, Identifier *ident); - int isFuncHidden(FuncDeclaration *fd); + bool isFuncHidden(FuncDeclaration *fd); FuncDeclaration *findFunc(Identifier *ident, TypeFunction *tf); void interfaceSemantic(Scope *sc); - int isCOMclass(); - virtual int isCOMinterface(); - int isCPPclass(); - virtual int isCPPinterface(); + bool isCOMclass(); + virtual bool isCOMinterface(); + bool isCPPclass(); + virtual bool isCPPinterface(); bool isAbstract(); virtual int vtblOffset(); const char *kind(); - const char *mangle(bool isv = false); - void toDocBuffer(OutBuffer *buf, Scope *sc); - - PROT getAccess(Dsymbol *smember); // determine access to smember void addLocalClass(ClassDeclarations *); // Back end - void toObjFile(int multiobj); // compile to .obj file + void toObjFile(bool multiobj); // compile to .obj file void toDebug(); unsigned baseVtblOffset(BaseClass *bc); Symbol *toSymbol(); @@ -339,15 +323,14 @@ class InterfaceDeclaration : public ClassDeclaration InterfaceDeclaration(Loc loc, Identifier *id, BaseClasses *baseclasses); Dsymbol *syntaxCopy(Dsymbol *s); void semantic(Scope *sc); - int isBaseOf(ClassDeclaration *cd, int *poffset); - int isBaseOf(BaseClass *bc, int *poffset); + bool isBaseOf(ClassDeclaration *cd, int *poffset); + bool isBaseOf(BaseClass *bc, int *poffset); const char *kind(); - int isBaseInfoComplete(); int vtblOffset(); - int isCPPinterface(); - virtual int isCOMinterface(); + bool isCPPinterface(); + bool isCOMinterface(); - void toObjFile(int multiobj); // compile to .obj file + void toObjFile(bool multiobj); // compile to .obj file Symbol *toSymbol(); InterfaceDeclaration *isInterfaceDeclaration() { return this; } diff --git a/gcc/d/dfrontend/aliasthis.c b/gcc/d/dfrontend/aliasthis.c index ba9e40b16..afee9874c 100644 --- a/gcc/d/dfrontend/aliasthis.c +++ b/gcc/d/dfrontend/aliasthis.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 2009-2009 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 2009-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/aliasthis.c + */ #include #include diff --git a/gcc/d/dfrontend/aliasthis.h b/gcc/d/dfrontend/aliasthis.h index 4c0dcbb7c..40ec13eee 100644 --- a/gcc/d/dfrontend/aliasthis.h +++ b/gcc/d/dfrontend/aliasthis.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 2009-2009 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 2009-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/aliasthis.h + */ #ifndef DMD_ALIASTHIS_H #define DMD_ALIASTHIS_H diff --git a/gcc/d/dfrontend/apply.c b/gcc/d/dfrontend/apply.c index afd454568..0eaf61ed3 100644 --- a/gcc/d/dfrontend/apply.c +++ b/gcc/d/dfrontend/apply.c @@ -1,18 +1,20 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2011 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/apply.c + */ #include #include #include "mars.h" #include "expression.h" +#include "visitor.h" /************************************** @@ -26,127 +28,118 @@ * Creating an iterator for this would be much more complex. */ -int Expression::apply(apply_fp_t fp, void *param) -{ - return (*fp)(this, param); -} - -/****************************** - * Perform apply() on an t if not null - */ -#define condApply(t, fp, param) (t ? t->apply(fp, param) : 0) - -int NewExp::apply(apply_fp_t fp, void *param) -{ - //printf("NewExp::apply(): %s\n", toChars()); - - return condApply(thisexp, fp, param) || - condApply(newargs, fp, param) || - condApply(arguments, fp, param) || - (*fp)(this, param); -} - -int NewAnonClassExp::apply(apply_fp_t fp, void *param) -{ - //printf("NewAnonClassExp::apply(): %s\n", toChars()); - - return condApply(thisexp, fp, param) || - condApply(newargs, fp, param) || - condApply(arguments, fp, param) || - (*fp)(this, param); -} - -int UnaExp::apply(apply_fp_t fp, void *param) -{ - return e1->apply(fp, param) || - (*fp)(this, param); -} - -int BinExp::apply(apply_fp_t fp, void *param) -{ - return e1->apply(fp, param) || - e2->apply(fp, param) || - (*fp)(this, param); -} - -int AssertExp::apply(apply_fp_t fp, void *param) -{ - //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); - return e1->apply(fp, param) || - condApply(msg, fp, param) || - (*fp)(this, param); -} - - -int CallExp::apply(apply_fp_t fp, void *param) -{ - //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); - return e1->apply(fp, param) || - condApply(arguments, fp, param) || - (*fp)(this, param); -} - - -int ArrayExp::apply(apply_fp_t fp, void *param) -{ - //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); - return e1->apply(fp, param) || - condApply(arguments, fp, param) || - (*fp)(this, param); -} - - -int SliceExp::apply(apply_fp_t fp, void *param) -{ - return e1->apply(fp, param) || - condApply(lwr, fp, param) || - condApply(upr, fp, param) || - (*fp)(this, param); -} - - -int ArrayLiteralExp::apply(apply_fp_t fp, void *param) +class PostorderExpressionVisitor : public StoppableVisitor { - return condApply(elements, fp, param) || - (*fp)(this, param); -} - - -int AssocArrayLiteralExp::apply(apply_fp_t fp, void *param) +public: + StoppableVisitor *v; + PostorderExpressionVisitor(StoppableVisitor *v) : v(v) {} + + bool doCond(Expression *e) + { + if (!stop && e) + e->accept(this); + return stop; + } + bool doCond(Expressions *e) + { + if (!e) + return false; + for (size_t i = 0; i < e->dim && !stop; i++) + doCond((*e)[i]); + return stop; + } + bool applyTo(Expression *e) + { + e->accept(v); + stop = v->stop; + return true; + } + + void visit(Expression *e) + { + applyTo(e); + } + + void visit(NewExp *e) + { + //printf("NewExp::apply(): %s\n", toChars()); + + doCond(e->thisexp) || doCond(e->newargs) || doCond(e->arguments) || applyTo(e); + } + + void visit(NewAnonClassExp *e) + { + //printf("NewAnonClassExp::apply(): %s\n", toChars()); + + doCond(e->thisexp) || doCond(e->newargs) || doCond(e->arguments) || applyTo(e); + } + + void visit(UnaExp *e) + { + doCond(e->e1) || applyTo(e); + } + + void visit(BinExp *e) + { + doCond(e->e1) || doCond(e->e2) || applyTo(e); + } + + void visit(AssertExp *e) + { + //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); + doCond(e->e1) || doCond(e->msg) || applyTo(e); + } + + void visit(CallExp *e) + { + //printf("CallExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); + doCond(e->e1) || doCond(e->arguments) || applyTo(e); + } + + void visit(ArrayExp *e) + { + //printf("ArrayExp::apply(apply_fp_t fp, void *param): %s\n", toChars()); + doCond(e->e1) || doCond(e->arguments) || applyTo(e); + } + + void visit(SliceExp *e) + { + doCond(e->e1) || doCond(e->lwr) || doCond(e->upr) || applyTo(e); + } + + void visit(ArrayLiteralExp *e) + { + doCond(e->elements) || applyTo(e); + } + + void visit(AssocArrayLiteralExp *e) + { + doCond(e->keys) || doCond(e->values) || applyTo(e); + } + + void visit(StructLiteralExp *e) + { + if (e->stageflags & stageApply) return; + int old = e->stageflags; + e->stageflags |= stageApply; + doCond(e->elements) || applyTo(e); + e->stageflags = old; + } + + void visit(TupleExp *e) + { + doCond(e->e0) || doCond(e->exps) || applyTo(e); + } + + void visit(CondExp *e) + { + doCond(e->econd) || doCond(e->e1) || doCond(e->e2) || applyTo(e); + } +}; + +bool walkPostorder(Expression *e, StoppableVisitor *v) { - return condApply(keys, fp, param) || - condApply(values, fp, param) || - (*fp)(this, param); + PostorderExpressionVisitor pv(v); + e->accept(&pv); + return v->stop; } - - -int StructLiteralExp::apply(apply_fp_t fp, void *param) -{ - if(stageflags & stageApply) return 0; - int old = stageflags; - stageflags |= stageApply; - int ret = condApply(elements, fp, param) || - (*fp)(this, param); - stageflags = old; - return ret; -} - - -int TupleExp::apply(apply_fp_t fp, void *param) -{ - return (e0 ? (*fp)(e0, param) : 0) || - condApply(exps, fp, param) || - (*fp)(this, param); -} - - -int CondExp::apply(apply_fp_t fp, void *param) -{ - return econd->apply(fp, param) || - e1->apply(fp, param) || - e2->apply(fp, param) || - (*fp)(this, param); -} - - - diff --git a/gcc/d/dfrontend/argtypes.c b/gcc/d/dfrontend/argtypes.c index 4ed5ddf7c..ab133e014 100644 --- a/gcc/d/dfrontend/argtypes.c +++ b/gcc/d/dfrontend/argtypes.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 2010-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 2010-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/argtypes.c + */ #include #include @@ -33,417 +34,455 @@ * Returning a tuple of zero length means the type cannot be passed/returned in registers. */ - -TypeTuple *Type::toArgTypes() -{ - return NULL; // not valid for a parameter -} - -TypeTuple *TypeError::toArgTypes() +TypeTuple *toArgTypes(Type *t) { - return new TypeTuple(Type::terror); -} - -TypeTuple *TypeBasic::toArgTypes() -{ Type *t1 = NULL; - Type *t2 = NULL; - switch (ty) + class ToArgTypes : public Visitor { - case Tvoid: - return NULL; - - case Tbool: - case Tint8: - case Tuns8: - case Tint16: - case Tuns16: - case Tint32: - case Tuns32: - case Tfloat32: - case Tint64: - case Tuns64: - case Tfloat64: - case Tfloat80: - t1 = this; - break; - - case Timaginary32: - t1 = Type::tfloat32; - break; - - case Timaginary64: - t1 = Type::tfloat64; - break; - - case Timaginary80: - t1 = Type::tfloat80; - break; - - case Tcomplex32: - if (global.params.is64bit) - t1 = Type::tfloat64; - else - { - t1 = Type::tfloat64; - t2 = Type::tfloat64; - } - break; + public: + TypeTuple *result; - case Tcomplex64: - t1 = Type::tfloat64; - t2 = Type::tfloat64; - break; + ToArgTypes() + { + result = NULL; + } - case Tcomplex80: - t1 = Type::tfloat80; - t2 = Type::tfloat80; - break; + void visit(Type *) + { + // not valid for a parameter + } - case Tchar: - t1 = Type::tuns8; - break; + void visit(TypeError *) + { + result = new TypeTuple(Type::terror); + } - case Twchar: - t1 = Type::tuns16; - break; + void visit(TypeBasic *t) + { + Type *t1 = NULL; + Type *t2 = NULL; + switch (t->ty) + { + case Tvoid: + return; + + case Tbool: + case Tint8: + case Tuns8: + case Tint16: + case Tuns16: + case Tint32: + case Tuns32: + case Tfloat32: + case Tint64: + case Tuns64: + case Tfloat64: + case Tfloat80: + t1 = t; + break; - case Tdchar: - t1 = Type::tuns32; - break; + case Timaginary32: + t1 = Type::tfloat32; + break; - default: assert(0); - } + case Timaginary64: + t1 = Type::tfloat64; + break; - TypeTuple *t; - if (t1) - { - if (t2) - t = new TypeTuple(t1, t2); - else - t = new TypeTuple(t1); - } - else - t = new TypeTuple(); - return t; -} + case Timaginary80: + t1 = Type::tfloat80; + break; -TypeTuple *TypeVector::toArgTypes() -{ - return new TypeTuple(this); -} + case Tcomplex32: + if (global.params.is64bit) + t1 = Type::tfloat64; + else + { + t1 = Type::tfloat64; + t2 = Type::tfloat64; + } + break; -TypeTuple *TypeSArray::toArgTypes() -{ - if (dim) - { - /* Should really be done as if it were a struct with dim members - * of the array's elements. - * I.e. int[2] should be done like struct S { int a; int b; } - */ - dinteger_t sz = dim->toInteger(); - if (sz == 1) - // T[1] should be passed like T - return next->toArgTypes(); - } - return new TypeTuple(); // pass on the stack for efficiency -} + case Tcomplex64: + t1 = Type::tfloat64; + t2 = Type::tfloat64; + break; -TypeTuple *TypeAArray::toArgTypes() -{ - return new TypeTuple(Type::tvoidptr); -} + case Tcomplex80: + t1 = Type::tfloat80; + t2 = Type::tfloat80; + break; -TypeTuple *TypePointer::toArgTypes() -{ - return new TypeTuple(Type::tvoidptr); -} + case Tchar: + t1 = Type::tuns8; + break; -/************************************* - * Convert a floating point type into the equivalent integral type. - */ + case Twchar: + t1 = Type::tuns16; + break; -Type *mergeFloatToInt(Type *t) -{ - switch (t->ty) - { - case Tfloat32: - case Timaginary32: - t = Type::tint32; - break; - case Tfloat64: - case Timaginary64: - case Tcomplex32: - t = Type::tint64; - break; - default: -#ifdef DEBUG - printf("mergeFloatToInt() %s\n", t->toChars()); -#endif - assert(0); - } - return t; -} + case Tdchar: + t1 = Type::tuns32; + break; -/************************************* - * This merges two types into an 8byte type. - */ + default: + assert(0); + } -Type *argtypemerge(Type *t1, Type *t2, unsigned offset2) -{ - //printf("argtypemerge(%s, %s, %d)\n", t1 ? t1->toChars() : "", t2 ? t2->toChars() : "", offset2); - if (!t1) - { assert(!t2 || offset2 == 0); - return t2; - } - if (!t2) - return t1; - - unsigned sz1 = (unsigned)t1->size(Loc()); - unsigned sz2 = (unsigned)t2->size(Loc()); - - if (t1->ty != t2->ty && - (t1->ty == Tfloat80 || t2->ty == Tfloat80)) - return NULL; - - // [float,float] => [cfloat] - if (t1->ty == Tfloat32 && t2->ty == Tfloat32 && offset2 == 4) - return Type::tfloat64; - - // Merging floating and non-floating types produces the non-floating type - if (t1->isfloating()) - { - if (!t2->isfloating()) - t1 = mergeFloatToInt(t1); - } - else if (t2->isfloating()) - t2 = mergeFloatToInt(t2); - - Type *t; - - // Pick type with larger size - if (sz1 < sz2) - t = t2; - else - t = t1; - - // If t2 does not lie within t1, need to increase the size of t to enclose both - if (offset2 && sz1 < offset2 + sz2) - { - switch (offset2 + sz2) + if (t1) + { + if (t2) + result = new TypeTuple(t1, t2); + else + result = new TypeTuple(t1); + } + else + result = new TypeTuple(); + } + + void visit(TypeVector *t) { - case 2: - t = Type::tint16; - break; - case 3: - case 4: - t = Type::tint32; - break; - case 5: - case 6: - case 7: - case 8: - t = Type::tint64; - break; - default: - assert(0); + result = new TypeTuple(t); } - } - return t; -} -TypeTuple *TypeDArray::toArgTypes() -{ - /* Should be done as if it were: - * struct S { size_t length; void* ptr; } - */ - if (global.params.is64bit && !global.params.isLP64) - { - // For AMD64 ILP32 ABI, D arrays fit into a single integer register. - unsigned offset = (unsigned)Type::tsize_t->size(Loc()); - Type *t = argtypemerge(Type::tsize_t, Type::tvoidptr, offset); - if (t) - return new TypeTuple(t); - } - return new TypeTuple(Type::tsize_t, Type::tvoidptr); -} + void visit(TypeSArray *t) + { + if (t->dim) + { + /* Should really be done as if it were a struct with dim members + * of the array's elements. + * I.e. int[2] should be done like struct S { int a; int b; } + */ + dinteger_t sz = t->dim->toInteger(); + // T[1] should be passed like T + if (sz == 1) + { + t->next->accept(this); + return; + } + } + result = new TypeTuple(); // pass on the stack for efficiency + } -TypeTuple *TypeDelegate::toArgTypes() -{ - /* Should be done as if it were: - * struct S { size_t length; void* ptr; } - */ - if (global.params.is64bit && !global.params.isLP64) - { - // For AMD64 ILP32 ABI, delegates fit into a single integer register. - unsigned offset = (unsigned)Type::tsize_t->size(Loc()); - Type *t = argtypemerge(Type::tsize_t, Type::tvoidptr, offset); - if (t) - return new TypeTuple(t); - } - return new TypeTuple(Type::tvoidptr, Type::tvoidptr); -} + void visit(TypeAArray *) + { + result = new TypeTuple(Type::tvoidptr); + } -TypeTuple *TypeStruct::toArgTypes() -{ - //printf("TypeStruct::toArgTypes() %s\n", toChars()); - if (!sym->isPOD()) - { - Lmemory: - //printf("\ttoArgTypes() %s => [ ]\n", toChars()); - return new TypeTuple(); // pass on the stack - } - Type *t1 = NULL; - Type *t2 = NULL; - d_uns64 sz = size(Loc()); - assert(sz < 0xFFFFFFFF); - switch ((unsigned)sz) - { - case 1: - t1 = Type::tint8; - break; - case 2: - t1 = Type::tint16; - break; - case 4: - t1 = Type::tint32; - break; - case 8: - t1 = Type::tint64; - break; - case 16: - t1 = NULL; // could be a TypeVector - break; - default: - goto Lmemory; - } - if (global.params.is64bit && sym->fields.dim) - { -#if 1 - t1 = NULL; - for (size_t i = 0; i < sym->fields.dim; i++) + void visit(TypePointer *) { - VarDeclaration *f = sym->fields[i]; - //printf("f->type = %s\n", f->type->toChars()); + result = new TypeTuple(Type::tvoidptr); + } - TypeTuple *tup = f->type->toArgTypes(); - if (!tup) - goto Lmemory; - size_t dim = tup->arguments->dim; - Type *ft1 = NULL; - Type *ft2 = NULL; - switch (dim) + /************************************* + * Convert a floating point type into the equivalent integral type. + */ + + static Type *mergeFloatToInt(Type *t) + { + switch (t->ty) { - case 2: - ft1 = (*tup->arguments)[0]->type; - ft2 = (*tup->arguments)[1]->type; + case Tfloat32: + case Timaginary32: + t = Type::tint32; break; - case 1: - if (f->offset < 8) - ft1 = (*tup->arguments)[0]->type; - else - ft2 = (*tup->arguments)[0]->type; + case Tfloat64: + case Timaginary64: + case Tcomplex32: + t = Type::tint64; break; default: - goto Lmemory; + #ifdef DEBUG + printf("mergeFloatToInt() %s\n", t->toChars()); + #endif + assert(0); } + return t; + } - if (f->offset & 7) - { - // Misaligned fields goto Lmemory - unsigned alignsz = f->type->alignsize(); - if (f->offset & (alignsz - 1)) - goto Lmemory; + /************************************* + * This merges two types into an 8byte type. + */ - // Fields that overlap the 8byte boundary goto Lmemory - d_uns64 fieldsz = f->type->size(Loc()); - if (f->offset < 8 && (f->offset + fieldsz) > 8) - goto Lmemory; + static Type *argtypemerge(Type *t1, Type *t2, unsigned offset2) + { + //printf("argtypemerge(%s, %s, %d)\n", t1 ? t1->toChars() : "", t2 ? t2->toChars() : "", offset2); + if (!t1) + { assert(!t2 || offset2 == 0); + return t2; } + if (!t2) + return t1; - // First field in 8byte must be at start of 8byte - assert(t1 || f->offset == 0); + unsigned sz1 = (unsigned)t1->size(Loc()); + unsigned sz2 = (unsigned)t2->size(Loc()); - if (ft1) + if (t1->ty != t2->ty && + (t1->ty == Tfloat80 || t2->ty == Tfloat80)) + return NULL; + + // [float,float] => [cfloat] + if (t1->ty == Tfloat32 && t2->ty == Tfloat32 && offset2 == 4) + return Type::tfloat64; + + // Merging floating and non-floating types produces the non-floating type + if (t1->isfloating()) { - t1 = argtypemerge(t1, ft1, f->offset); - if (!t1) - goto Lmemory; + if (!t2->isfloating()) + t1 = mergeFloatToInt(t1); } + else if (t2->isfloating()) + t2 = mergeFloatToInt(t2); + + Type *t; - if (ft2) + // Pick type with larger size + if (sz1 < sz2) + t = t2; + else + t = t1; + + // If t2 does not lie within t1, need to increase the size of t to enclose both + if (offset2 && sz1 < offset2 + sz2) { - unsigned off2 = f->offset; - if (ft1) - off2 = 8; - if (!t2 && off2 != 8) - goto Lmemory; - assert(t2 || off2 == 8); - t2 = argtypemerge(t2, ft2, off2 - 8); - if (!t2) - goto Lmemory; + switch (offset2 + sz2) + { + case 2: + t = Type::tint16; + break; + case 3: + case 4: + t = Type::tint32; + break; + case 5: + case 6: + case 7: + case 8: + t = Type::tint64; + break; + default: + assert(0); + } } + return t; } - if (t2) + void visit(TypeDArray *) { - if (t1->isfloating() && t2->isfloating()) + /* Should be done as if it were: + * struct S { size_t length; void* ptr; } + */ + if (global.params.is64bit && !global.params.isLP64) { - if (t1->ty == Tfloat64 && t2->ty == Tfloat64) - ; - else - goto Lmemory; + // For AMD64 ILP32 ABI, D arrays fit into a single integer register. + unsigned offset = (unsigned)Type::tsize_t->size(Loc()); + Type *t = argtypemerge(Type::tsize_t, Type::tvoidptr, offset); + if (t) + { + result = new TypeTuple(t); + return; + } } - else if (t1->isfloating()) - goto Lmemory; - else if (t2->isfloating()) - goto Lmemory; - else - ; + result = new TypeTuple(Type::tsize_t, Type::tvoidptr); } -#else - if (sym->fields.dim == 1) + + void visit(TypeDelegate *) { - VarDeclaration *f = sym->fields[0]; - //printf("f->type = %s\n", f->type->toChars()); - TypeTuple *tup = f->type->toArgTypes(); - if (tup) + /* Should be done as if it were: + * struct S { size_t length; void* ptr; } + */ + if (global.params.is64bit && !global.params.isLP64) { - size_t dim = tup->arguments->dim; - if (dim == 1) - t1 = (*tup->arguments)[0]->type; + // For AMD64 ILP32 ABI, delegates fit into a single integer register. + unsigned offset = (unsigned)Type::tsize_t->size(Loc()); + Type *t = argtypemerge(Type::tsize_t, Type::tvoidptr, offset); + if (t) + { + result = new TypeTuple(t); + return; + } } + result = new TypeTuple(Type::tvoidptr, Type::tvoidptr); } -#endif - } - //printf("\ttoArgTypes() %s => [%s,%s]\n", toChars(), t1 ? t1->toChars() : "", t2 ? t2->toChars() : ""); + void visit(TypeStruct *t) + { + //printf("TypeStruct::toArgTypes() %s\n", t->toChars()); + if (!t->sym->isPOD()) + { + Lmemory: + //printf("\ttoArgTypes() %s => [ ]\n", t->toChars()); + result = new TypeTuple(); // pass on the stack + return; + } + Type *t1 = NULL; + Type *t2 = NULL; + d_uns64 sz = t->size(Loc()); + assert(sz < 0xFFFFFFFF); + switch ((unsigned)sz) + { + case 1: + t1 = Type::tint8; + break; + case 2: + t1 = Type::tint16; + break; + case 3: + if (!global.params.is64bit) + goto Lmemory; + case 4: + t1 = Type::tint32; + break; + case 5: + case 6: + case 7: + if (!global.params.is64bit) + goto Lmemory; + case 8: + t1 = Type::tint64; + break; + case 16: + t1 = NULL; // could be a TypeVector + break; + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + if (!global.params.is64bit) + goto Lmemory; + t1 = NULL; + break; + default: + goto Lmemory; + } + if (global.params.is64bit && t->sym->fields.dim) + { + #if 1 + t1 = NULL; + for (size_t i = 0; i < t->sym->fields.dim; i++) + { + VarDeclaration *f = t->sym->fields[i]; + //printf("f->type = %s\n", f->type->toChars()); + + TypeTuple *tup = toArgTypes(f->type); + if (!tup) + goto Lmemory; + size_t dim = tup->arguments->dim; + Type *ft1 = NULL; + Type *ft2 = NULL; + switch (dim) + { + case 2: + ft1 = (*tup->arguments)[0]->type; + ft2 = (*tup->arguments)[1]->type; + break; + case 1: + if (f->offset < 8) + ft1 = (*tup->arguments)[0]->type; + else + ft2 = (*tup->arguments)[0]->type; + break; + default: + goto Lmemory; + } + + if (f->offset & 7) + { + // Misaligned fields goto Lmemory + unsigned alignsz = f->type->alignsize(); + if (f->offset & (alignsz - 1)) + goto Lmemory; + + // Fields that overlap the 8byte boundary goto Lmemory + d_uns64 fieldsz = f->type->size(Loc()); + if (f->offset < 8 && (f->offset + fieldsz) > 8) + goto Lmemory; + } + + // First field in 8byte must be at start of 8byte + assert(t1 || f->offset == 0); + + if (ft1) + { + t1 = argtypemerge(t1, ft1, f->offset); + if (!t1) + goto Lmemory; + } + + if (ft2) + { + unsigned off2 = f->offset; + if (ft1) + off2 = 8; + if (!t2 && off2 != 8) + goto Lmemory; + assert(t2 || off2 == 8); + t2 = argtypemerge(t2, ft2, off2 - 8); + if (!t2) + goto Lmemory; + } + } + + if (t2) + { + if (t1->isfloating() && t2->isfloating()) + { + if (t1->ty == Tfloat64 && t2->ty == Tfloat64) + ; + else + goto Lmemory; + } + else if (t1->isfloating()) + goto Lmemory; + else if (t2->isfloating()) + goto Lmemory; + else + ; + } + #else + if (t->sym->fields.dim == 1) + { + VarDeclaration *f = t->sym->fields[0]; + //printf("f->type = %s\n", f->type->toChars()); + TypeTuple *tup = toArgTypes(f->type); + if (tup) + { + size_t dim = tup->arguments->dim; + if (dim == 1) + t1 = (*tup->arguments)[0]->type; + } + } + #endif + } - TypeTuple *t; - if (t1) - { - //if (t1) printf("test1: %s => %s\n", toChars(), t1->toChars()); - if (t2) - t = new TypeTuple(t1, t2); - else - t = new TypeTuple(t1); - } - else - goto Lmemory; - return t; -} + //printf("\ttoArgTypes() %s => [%s,%s]\n", t->toChars(), t1 ? t1->toChars() : "", t2 ? t2->toChars() : ""); -TypeTuple *TypeEnum::toArgTypes() -{ - return toBasetype()->toArgTypes(); -} + if (t1) + { + //if (t1) printf("test1: %s => %s\n", toChars(), t1->toChars()); + if (t2) + result = new TypeTuple(t1, t2); + else + result = new TypeTuple(t1); + } + else + goto Lmemory; + } -TypeTuple *TypeTypedef::toArgTypes() -{ - return sym->basetype->toArgTypes(); -} + void visit(TypeEnum *t) + { + t->toBasetype()->accept(this); + } -TypeTuple *TypeClass::toArgTypes() -{ - return new TypeTuple(Type::tvoidptr); -} + void visit(TypeClass *) + { + result = new TypeTuple(Type::tvoidptr); + } + }; + ToArgTypes v; + t->accept(&v); + return v.result; +} diff --git a/gcc/d/dfrontend/array.h b/gcc/d/dfrontend/array.h index b234e6852..04edefd39 100644 --- a/gcc/d/dfrontend/array.h +++ b/gcc/d/dfrontend/array.h @@ -1,11 +1,10 @@ - -// Copyright (c) 1999-2011 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Copyright (c) 2011-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/array.h + */ #ifndef ARRAY_H #define ARRAY_H @@ -19,6 +18,7 @@ #include #include +#include "object.h" #ifdef IN_GCC // rmem uses functions poisoned by GCC. void *mem_malloc(size_t size); @@ -27,7 +27,6 @@ void mem_free(void *p); #else #include "rmem.h" #endif -#include "object.h" template struct Array @@ -276,21 +275,6 @@ struct Array memcpy(a->data, data, dim * sizeof(*data)); return a; } - - typedef int (*Array_apply_ft_t)(TYPE, void *); - int apply(Array_apply_ft_t fp, void *param) - { - for (size_t i = 0; i < dim; i++) - { TYPE e = (*this)[i]; - - if (e) - { - if (e->apply(fp, param)) - return 1; - } - } - return 0; - } }; #endif diff --git a/gcc/d/dfrontend/arrayop.c b/gcc/d/dfrontend/arrayop.c index 733663e28..a427ce2bc 100644 --- a/gcc/d/dfrontend/arrayop.c +++ b/gcc/d/dfrontend/arrayop.c @@ -1,11 +1,13 @@ -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/arrayop.c + */ #include #include @@ -25,6 +27,8 @@ #include "init.h" extern int binary(const char *p , const char **tab, int high); +void buildArrayIdent(Expression *e, OutBuffer *buf, Expressions *arguments); +Expression *buildArrayLoop(Expression *e, Parameters *fparams); /************************************** * Hash table of array op functions already generated or known about. @@ -36,208 +40,10 @@ AA *arrayfuncs; * Structure to contain information needed to insert an array op call */ -struct ArrayOp -{ - FuncDeclaration *cFunc; // Stub for optimized druntime version - FuncDeclaration *dFunc; // Full D version for ctfe -}; - -/************************************** - * Search for a druntime array op - */ -int isDruntimeArrayOp(Identifier *ident) -{ - /* Some of the array op functions are written as library functions, - * presumably to optimize them with special CPU vector instructions. - * List those library functions here, in alpha order. - */ - static const char *libArrayopFuncs[] = - { - "_arrayExpSliceAddass_a", - "_arrayExpSliceAddass_d", // T[]+=T - "_arrayExpSliceAddass_f", // T[]+=T - "_arrayExpSliceAddass_g", - "_arrayExpSliceAddass_h", - "_arrayExpSliceAddass_i", - "_arrayExpSliceAddass_k", - "_arrayExpSliceAddass_s", - "_arrayExpSliceAddass_t", - "_arrayExpSliceAddass_u", - "_arrayExpSliceAddass_w", - - "_arrayExpSliceDivass_d", // T[]/=T - "_arrayExpSliceDivass_f", // T[]/=T - - "_arrayExpSliceMinSliceAssign_a", - "_arrayExpSliceMinSliceAssign_d", // T[]=T-T[] - "_arrayExpSliceMinSliceAssign_f", // T[]=T-T[] - "_arrayExpSliceMinSliceAssign_g", - "_arrayExpSliceMinSliceAssign_h", - "_arrayExpSliceMinSliceAssign_i", - "_arrayExpSliceMinSliceAssign_k", - "_arrayExpSliceMinSliceAssign_s", - "_arrayExpSliceMinSliceAssign_t", - "_arrayExpSliceMinSliceAssign_u", - "_arrayExpSliceMinSliceAssign_w", - - "_arrayExpSliceMinass_a", - "_arrayExpSliceMinass_d", // T[]-=T - "_arrayExpSliceMinass_f", // T[]-=T - "_arrayExpSliceMinass_g", - "_arrayExpSliceMinass_h", - "_arrayExpSliceMinass_i", - "_arrayExpSliceMinass_k", - "_arrayExpSliceMinass_s", - "_arrayExpSliceMinass_t", - "_arrayExpSliceMinass_u", - "_arrayExpSliceMinass_w", - - "_arrayExpSliceMulass_d", // T[]*=T - "_arrayExpSliceMulass_f", // T[]*=T - "_arrayExpSliceMulass_i", - "_arrayExpSliceMulass_k", - "_arrayExpSliceMulass_s", - "_arrayExpSliceMulass_t", - "_arrayExpSliceMulass_u", - "_arrayExpSliceMulass_w", - - "_arraySliceExpAddSliceAssign_a", - "_arraySliceExpAddSliceAssign_d", // T[]=T[]+T - "_arraySliceExpAddSliceAssign_f", // T[]=T[]+T - "_arraySliceExpAddSliceAssign_g", - "_arraySliceExpAddSliceAssign_h", - "_arraySliceExpAddSliceAssign_i", - "_arraySliceExpAddSliceAssign_k", - "_arraySliceExpAddSliceAssign_s", - "_arraySliceExpAddSliceAssign_t", - "_arraySliceExpAddSliceAssign_u", - "_arraySliceExpAddSliceAssign_w", - - "_arraySliceExpDivSliceAssign_d", // T[]=T[]/T - "_arraySliceExpDivSliceAssign_f", // T[]=T[]/T - - "_arraySliceExpMinSliceAssign_a", - "_arraySliceExpMinSliceAssign_d", // T[]=T[]-T - "_arraySliceExpMinSliceAssign_f", // T[]=T[]-T - "_arraySliceExpMinSliceAssign_g", - "_arraySliceExpMinSliceAssign_h", - "_arraySliceExpMinSliceAssign_i", - "_arraySliceExpMinSliceAssign_k", - "_arraySliceExpMinSliceAssign_s", - "_arraySliceExpMinSliceAssign_t", - "_arraySliceExpMinSliceAssign_u", - "_arraySliceExpMinSliceAssign_w", - - "_arraySliceExpMulSliceAddass_d", // T[] += T[]*T - "_arraySliceExpMulSliceAddass_f", - "_arraySliceExpMulSliceAddass_r", - - "_arraySliceExpMulSliceAssign_d", // T[]=T[]*T - "_arraySliceExpMulSliceAssign_f", // T[]=T[]*T - "_arraySliceExpMulSliceAssign_i", - "_arraySliceExpMulSliceAssign_k", - "_arraySliceExpMulSliceAssign_s", - "_arraySliceExpMulSliceAssign_t", - "_arraySliceExpMulSliceAssign_u", - "_arraySliceExpMulSliceAssign_w", - - "_arraySliceExpMulSliceMinass_d", // T[] -= T[]*T - "_arraySliceExpMulSliceMinass_f", - "_arraySliceExpMulSliceMinass_r", - - "_arraySliceSliceAddSliceAssign_a", - "_arraySliceSliceAddSliceAssign_d", // T[]=T[]+T[] - "_arraySliceSliceAddSliceAssign_f", // T[]=T[]+T[] - "_arraySliceSliceAddSliceAssign_g", - "_arraySliceSliceAddSliceAssign_h", - "_arraySliceSliceAddSliceAssign_i", - "_arraySliceSliceAddSliceAssign_k", - "_arraySliceSliceAddSliceAssign_r", // T[]=T[]+T[] - "_arraySliceSliceAddSliceAssign_s", - "_arraySliceSliceAddSliceAssign_t", - "_arraySliceSliceAddSliceAssign_u", - "_arraySliceSliceAddSliceAssign_w", - - "_arraySliceSliceAddass_a", - "_arraySliceSliceAddass_d", // T[]+=T[] - "_arraySliceSliceAddass_f", // T[]+=T[] - "_arraySliceSliceAddass_g", - "_arraySliceSliceAddass_h", - "_arraySliceSliceAddass_i", - "_arraySliceSliceAddass_k", - "_arraySliceSliceAddass_s", - "_arraySliceSliceAddass_t", - "_arraySliceSliceAddass_u", - "_arraySliceSliceAddass_w", - - "_arraySliceSliceMinSliceAssign_a", - "_arraySliceSliceMinSliceAssign_d", // T[]=T[]-T[] - "_arraySliceSliceMinSliceAssign_f", // T[]=T[]-T[] - "_arraySliceSliceMinSliceAssign_g", - "_arraySliceSliceMinSliceAssign_h", - "_arraySliceSliceMinSliceAssign_i", - "_arraySliceSliceMinSliceAssign_k", - "_arraySliceSliceMinSliceAssign_r", // T[]=T[]-T[] - "_arraySliceSliceMinSliceAssign_s", - "_arraySliceSliceMinSliceAssign_t", - "_arraySliceSliceMinSliceAssign_u", - "_arraySliceSliceMinSliceAssign_w", - - "_arraySliceSliceMinass_a", - "_arraySliceSliceMinass_d", // T[]-=T[] - "_arraySliceSliceMinass_f", // T[]-=T[] - "_arraySliceSliceMinass_g", - "_arraySliceSliceMinass_h", - "_arraySliceSliceMinass_i", - "_arraySliceSliceMinass_k", - "_arraySliceSliceMinass_s", - "_arraySliceSliceMinass_t", - "_arraySliceSliceMinass_u", - "_arraySliceSliceMinass_w", - - "_arraySliceSliceMulSliceAssign_d", // T[]=T[]*T[] - "_arraySliceSliceMulSliceAssign_f", // T[]=T[]*T[] - "_arraySliceSliceMulSliceAssign_i", - "_arraySliceSliceMulSliceAssign_k", - "_arraySliceSliceMulSliceAssign_s", - "_arraySliceSliceMulSliceAssign_t", - "_arraySliceSliceMulSliceAssign_u", - "_arraySliceSliceMulSliceAssign_w", - - "_arraySliceSliceMulass_d", // T[]*=T[] - "_arraySliceSliceMulass_f", // T[]*=T[] - "_arraySliceSliceMulass_i", - "_arraySliceSliceMulass_k", - "_arraySliceSliceMulass_s", - "_arraySliceSliceMulass_t", - "_arraySliceSliceMulass_u", - "_arraySliceSliceMulass_w", - }; - char *name = ident->toChars(); - int i = binary(name, libArrayopFuncs, sizeof(libArrayopFuncs) / sizeof(char *)); - if (i != -1) - return 1; - -#ifdef DEBUG // Make sure our array is alphabetized - for (i = 0; i < sizeof(libArrayopFuncs) / sizeof(char *); i++) - { - if (strcmp(name, libArrayopFuncs[i]) == 0) - assert(0); - } -#endif - return 0; -} - -ArrayOp *buildArrayOp(Identifier *ident, BinExp *exp, Scope *sc, Loc loc) +FuncDeclaration *buildArrayOp(Identifier *ident, BinExp *exp, Scope *sc, Loc loc) { Parameters *fparams = new Parameters(); - Expression *loopbody = exp->buildArrayLoop(fparams); - - ArrayOp *op = new ArrayOp; - if (isDruntimeArrayOp(ident)) - op->cFunc = FuncDeclaration::genCfunc(fparams, exp->type, ident); - else - op->cFunc = NULL; + Expression *loopbody = buildArrayLoop(exp, fparams); /* Construct the function body: * foreach (i; 0 .. p.length) for (size_t i = 0; i < p.length; i++) @@ -245,7 +51,7 @@ ArrayOp *buildArrayOp(Identifier *ident, BinExp *exp, Scope *sc, Loc loc) * return p; */ - Parameter *p = (*fparams)[0 /*fparams->dim - 1*/]; + Parameter *p = (*fparams)[0]; // foreach (i; 0 .. p.length) Statement *s1 = new ForeachRangeStatement(Loc(), TOKforeach, new Parameter(0, NULL, Id::p, NULL), @@ -257,8 +63,8 @@ ArrayOp *buildArrayOp(Identifier *ident, BinExp *exp, Scope *sc, Loc loc) //printf("s2: %s\n", s2->toChars()); Statement *fbody = new CompoundStatement(Loc(), s1, s2); - // Built-in array ops should be @trusted, pure and nothrow - StorageClass stc = STCtrusted | STCpure | STCnothrow; + // Built-in array ops should be @trusted, pure, nothrow and nogc + StorageClass stc = STCtrusted | STCpure | STCnothrow | STCnogc; /* Construct the function */ @@ -270,8 +76,7 @@ ArrayOp *buildArrayOp(Identifier *ident, BinExp *exp, Scope *sc, Loc loc) fd->linkage = LINKc; fd->isArrayOp = 1; - if (!op->cFunc) - sc->module->importedFrom->members->push(fd); + sc->module->importedFrom->members->push(fd); sc = sc->push(); sc->parent = sc->module->importedFrom; @@ -289,13 +94,7 @@ ArrayOp *buildArrayOp(Identifier *ident, BinExp *exp, Scope *sc, Loc loc) } sc->pop(); - if (op->cFunc) - { - op->cFunc->dArrayOp = fd; - op->cFunc->type = fd->type; - } - op->dFunc = fd; - return op; + return fd; } /********************************************** @@ -359,25 +158,55 @@ bool isArrayOpValid(Expression *e) return true; } +bool isNonAssignmentArrayOp(Expression *e) +{ + if (e->op == TOKslice) + return isNonAssignmentArrayOp(((SliceExp *)e)->e1); + Type *tb = e->type->toBasetype(); + + if (tb->ty == Tarray || tb->ty == Tsarray) + { + switch (e->op) + { + case TOKadd: + case TOKmin: + case TOKmul: + case TOKdiv: + case TOKmod: + case TOKxor: + case TOKand: + case TOKor: + case TOKpow: + case TOKneg: + case TOKtilde: + return true; + + default: + return false; + } + } + return false; +} + /*********************************** * Construct the array operation expression. */ -Expression *BinExp::arrayOp(Scope *sc) +Expression *arrayOp(BinExp *e, Scope *sc) { //printf("BinExp::arrayOp() %s\n", toChars()); - Type *tb = type->toBasetype(); + Type *tb = e->type->toBasetype(); assert(tb->ty == Tarray || tb->ty == Tsarray); Type *tbn = tb->nextOf()->toBasetype(); if (tbn->ty == Tvoid) { - error("Cannot perform array operations on void[] arrays"); + e->error("Cannot perform array operations on void[] arrays"); return new ErrorExp(); } - if (!isArrayOpValid(this)) + if (!isArrayOpValid(e)) { - error("invalid array operation %s (did you forget a [] ?)", toChars()); + e->error("invalid array operation %s (did you forget a [] ?)", e->toChars()); return new ErrorExp(); } @@ -389,24 +218,23 @@ Expression *BinExp::arrayOp(Scope *sc) */ OutBuffer buf; buf.writestring("_array"); - buildArrayIdent(&buf, arguments); + buildArrayIdent(e, &buf, arguments); buf.writeByte('_'); /* Append deco of array element type */ - buf.writestring(type->toBasetype()->nextOf()->toBasetype()->mutableOf()->deco); + buf.writestring(e->type->toBasetype()->nextOf()->toBasetype()->mutableOf()->deco); - buf.writeByte(0); - char *name = buf.toChars(); + char *name = buf.peekString(); Identifier *ident = Lexer::idPool(name); - ArrayOp **pOp = (ArrayOp **)_aaGet(&arrayfuncs, ident); - ArrayOp *op = *pOp; + FuncDeclaration **pFd = (FuncDeclaration **)dmd_aaGet(&arrayfuncs, (void *)ident); + FuncDeclaration *fd = *pFd; - if (!op) - op = buildArrayOp(ident, this, sc, loc); + if (!fd) + fd = buildArrayOp(ident, e, sc, e->loc); - if (op->dFunc && op->dFunc->errors) + if (fd && fd->errors) { const char *fmt; if (tbn->ty == Tstruct || tbn->ty == Tclass) @@ -416,38 +244,37 @@ Expression *BinExp::arrayOp(Scope *sc) else fmt = "invalid array operation '%s' for element type %s"; - error(fmt, toChars(), tbn->toChars()); + e->error(fmt, e->toChars(), tbn->toChars()); return new ErrorExp(); } - *pOp = op; + *pFd = fd; - FuncDeclaration *fd = op->cFunc ? op->cFunc : op->dFunc; - Expression *ec = new VarExp(loc, fd); - Expression *e = new CallExp(loc, ec, arguments); + Expression *ev = new VarExp(e->loc, fd); + Expression *ec = new CallExp(e->loc, ev, arguments); - return e->semantic(sc); + return ec->semantic(sc); } -Expression *BinAssignExp::arrayOp(Scope *sc) +Expression *arrayOp(BinAssignExp *e, Scope *sc) { //printf("BinAssignExp::arrayOp() %s\n", toChars()); /* Check that the elements of e1 can be assigned to */ - Type *tn = e1->type->toBasetype()->nextOf(); + Type *tn = e->e1->type->toBasetype()->nextOf(); if (tn && (!tn->isMutable() || !tn->isAssignable())) { - error("slice %s is not mutable", e1->toChars()); + e->error("slice %s is not mutable", e->e1->toChars()); return new ErrorExp(); } - if (e1->op == TOKarrayliteral) + if (e->e1->op == TOKarrayliteral) { - return e1->modifiableLvalue(sc, e1); + return e->e1->modifiableLvalue(sc, e->e1); } - return BinExp::arrayOp(sc); + return arrayOp((BinExp *)e, sc); } /****************************************** @@ -455,105 +282,122 @@ Expression *BinAssignExp::arrayOp(Scope *sc) * and build the argument list to pass to it. */ -void Expression::buildArrayIdent(OutBuffer *buf, Expressions *arguments) -{ - buf->writestring("Exp"); - arguments->shift(this); -} - -void CastExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) +void buildArrayIdent(Expression *e, OutBuffer *buf, Expressions *arguments) { - Type *tb = type->toBasetype(); - if (tb->ty == Tarray || tb->ty == Tsarray) + class BuildArrayIdentVisitor : public Visitor { - e1->buildArrayIdent(buf, arguments); - } - else - Expression::buildArrayIdent(buf, arguments); -} + OutBuffer *buf; + Expressions *arguments; + public: + BuildArrayIdentVisitor(OutBuffer *buf, Expressions *arguments) + : buf(buf), arguments(arguments) + { + } -void ArrayLiteralExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) -{ - buf->writestring("Slice"); - arguments->shift(this); -} + void visit(Expression *e) + { + buf->writestring("Exp"); + arguments->shift(e); + } -void SliceExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) -{ - buf->writestring("Slice"); - arguments->shift(this); -} + void visit(CastExp *e) + { + Type *tb = e->type->toBasetype(); + if (tb->ty == Tarray || tb->ty == Tsarray) + { + e->e1->accept(this); + } + else + visit((Expression *)e); + } -void AssignExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) -{ - /* Evaluate assign expressions right to left - */ - e2->buildArrayIdent(buf, arguments); - e1->buildArrayIdent(buf, arguments); - buf->writestring("Assign"); -} + void visit(ArrayLiteralExp *e) + { + buf->writestring("Slice"); + arguments->shift(e); + } -void BinAssignExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) -{ - /* Evaluate assign expressions right to left - */ - e2->buildArrayIdent(buf, arguments); - e1->buildArrayIdent(buf, arguments); - const char *s; - switch(op) - { - case TOKaddass: s = "Addass"; break; - case TOKminass: s = "Subass"; break; - case TOKmulass: s = "Mulass"; break; - case TOKdivass: s = "Divass"; break; - case TOKmodass: s = "Modass"; break; - case TOKxorass: s = "Xorass"; break; - case TOKandass: s = "Andass"; break; - case TOKorass: s = "Orass"; break; - case TOKpowass: s = "Powass"; break; - default: assert(0); - } - buf->writestring(s); -} + void visit(SliceExp *e) + { + buf->writestring("Slice"); + arguments->shift(e); + } -void NegExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) -{ - e1->buildArrayIdent(buf, arguments); - buf->writestring("Neg"); -} + void visit(AssignExp *e) + { + /* Evaluate assign expressions right to left + */ + e->e2->accept(this); + e->e1->accept(this); + buf->writestring("Assign"); + } -void ComExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) -{ - e1->buildArrayIdent(buf, arguments); - buf->writestring("Com"); -} + void visit(BinAssignExp *e) + { + /* Evaluate assign expressions right to left + */ + e->e2->accept(this); + e->e1->accept(this); + const char *s; + switch(e->op) + { + case TOKaddass: s = "Addass"; break; + case TOKminass: s = "Subass"; break; + case TOKmulass: s = "Mulass"; break; + case TOKdivass: s = "Divass"; break; + case TOKmodass: s = "Modass"; break; + case TOKxorass: s = "Xorass"; break; + case TOKandass: s = "Andass"; break; + case TOKorass: s = "Orass"; break; + case TOKpowass: s = "Powass"; break; + default: assert(0); + } + buf->writestring(s); + } -void BinExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) -{ - /* Evaluate assign expressions left to right - */ - const char *s = NULL; - switch(op) - { - case TOKadd: s = "Add"; break; - case TOKmin: s = "Sub"; break; - case TOKmul: s = "Mul"; break; - case TOKdiv: s = "Div"; break; - case TOKmod: s = "Mod"; break; - case TOKxor: s = "Xor"; break; - case TOKand: s = "And"; break; - case TOKor: s = "Or"; break; - case TOKpow: s = "Pow"; break; - default: break; - } - if (s) - { - e1->buildArrayIdent(buf, arguments); - e2->buildArrayIdent(buf, arguments); - buf->writestring(s); - } - else - Expression::buildArrayIdent(buf, arguments); + void visit(NegExp *e) + { + e->e1->accept(this); + buf->writestring("Neg"); + } + + void visit(ComExp *e) + { + e->e1->accept(this); + buf->writestring("Com"); + } + + void visit(BinExp *e) + { + /* Evaluate assign expressions left to right + */ + const char *s = NULL; + switch(e->op) + { + case TOKadd: s = "Add"; break; + case TOKmin: s = "Sub"; break; + case TOKmul: s = "Mul"; break; + case TOKdiv: s = "Div"; break; + case TOKmod: s = "Mod"; break; + case TOKxor: s = "Xor"; break; + case TOKand: s = "And"; break; + case TOKor: s = "Or"; break; + case TOKpow: s = "Pow"; break; + default: break; + } + if (s) + { + e->e1->accept(this); + e->e2->accept(this); + buf->writestring(s); + } + else + visit((Expression *)e); + } + }; + + BuildArrayIdentVisitor v(buf, arguments); + e->accept(&v); } /****************************************** @@ -561,183 +405,201 @@ void BinExp::buildArrayIdent(OutBuffer *buf, Expressions *arguments) * and build the parameter list. */ -Expression *Expression::buildArrayLoop(Parameters *fparams) +Expression *buildArrayLoop(Expression *e, Parameters *fparams) { - Identifier *id = Identifier::generateId("c", fparams->dim); - Parameter *param = new Parameter(0, type, id, NULL); - fparams->shift(param); - Expression *e = new IdentifierExp(Loc(), id); - return e; -} - -Expression *CastExp::buildArrayLoop(Parameters *fparams) -{ - Type *tb = type->toBasetype(); - if (tb->ty == Tarray || tb->ty == Tsarray) + class BuildArrayLoopVisitor : public Visitor { - return e1->buildArrayLoop(fparams); - } - else - return Expression::buildArrayLoop(fparams); -} + Parameters *fparams; + Expression *result; -Expression *ArrayLiteralExp::buildArrayLoop(Parameters *fparams) -{ - Identifier *id = Identifier::generateId("p", fparams->dim); - Parameter *param = new Parameter(STCconst, type, id, NULL); - fparams->shift(param); - Expression *e = new IdentifierExp(Loc(), id); - Expressions *arguments = new Expressions(); - Expression *index = new IdentifierExp(Loc(), Id::p); - arguments->push(index); - e = new ArrayExp(Loc(), e, arguments); - return e; -} + public: + BuildArrayLoopVisitor(Parameters *fparams) + : fparams(fparams), result(NULL) + { + } -Expression *SliceExp::buildArrayLoop(Parameters *fparams) -{ - Identifier *id = Identifier::generateId("p", fparams->dim); - Parameter *param = new Parameter(STCconst, type, id, NULL); - fparams->shift(param); - Expression *e = new IdentifierExp(Loc(), id); - Expressions *arguments = new Expressions(); - Expression *index = new IdentifierExp(Loc(), Id::p); - arguments->push(index); - e = new ArrayExp(Loc(), e, arguments); - return e; -} + void visit(Expression *e) + { + Identifier *id = Identifier::generateId("c", fparams->dim); + Parameter *param = new Parameter(0, e->type, id, NULL); + fparams->shift(param); + result = new IdentifierExp(Loc(), id); + } -Expression *AssignExp::buildArrayLoop(Parameters *fparams) -{ - /* Evaluate assign expressions right to left - */ - Expression *ex2 = e2->buildArrayLoop(fparams); - /* Need the cast because: - * b = c + p[i]; - * where b is a byte fails because (c + p[i]) is an int - * which cannot be implicitly cast to byte. - */ - ex2 = new CastExp(Loc(), ex2, e1->type->nextOf()); - Expression *ex1 = e1->buildArrayLoop(fparams); - Parameter *param = (*fparams)[0]; - param->storageClass = 0; - Expression *e = new AssignExp(Loc(), ex1, ex2); - return e; -} + void visit(CastExp *e) + { + Type *tb = e->type->toBasetype(); + if (tb->ty == Tarray || tb->ty == Tsarray) + { + e->e1->accept(this); + } + else + visit((Expression *)e); + } -Expression *BinAssignExp::buildArrayLoop(Parameters *fparams) -{ - /* Evaluate assign expressions right to left - */ - Expression *ex2 = e2->buildArrayLoop(fparams); - Expression *ex1 = e1->buildArrayLoop(fparams); - Parameter *param = (*fparams)[0]; - param->storageClass = 0; - Expression *e; - switch(op) - { - case TOKaddass: return new AddAssignExp(loc, ex1, ex2); - case TOKminass: return new MinAssignExp(loc, ex1, ex2); - case TOKmulass: return new MulAssignExp(loc, ex1, ex2); - case TOKdivass: return new DivAssignExp(loc, ex1, ex2); - case TOKmodass: return new ModAssignExp(loc, ex1, ex2); - case TOKxorass: return new XorAssignExp(loc, ex1, ex2); - case TOKandass: return new AndAssignExp(loc, ex1, ex2); - case TOKorass: return new OrAssignExp(loc, ex1, ex2); - case TOKpowass: return new PowAssignExp(loc, ex1, ex2); - default: - assert(0); - return NULL; - } -} + void visit(ArrayLiteralExp *e) + { + Identifier *id = Identifier::generateId("p", fparams->dim); + Parameter *param = new Parameter(STCconst, e->type, id, NULL); + fparams->shift(param); + Expression *ie = new IdentifierExp(Loc(), id); + Expressions *arguments = new Expressions(); + Expression *index = new IdentifierExp(Loc(), Id::p); + arguments->push(index); + result = new ArrayExp(Loc(), ie, arguments); + } -Expression *NegExp::buildArrayLoop(Parameters *fparams) -{ - Expression *ex1 = e1->buildArrayLoop(fparams); - Expression *e = new NegExp(Loc(), ex1); - return e; -} + void visit(SliceExp *e) + { + Identifier *id = Identifier::generateId("p", fparams->dim); + Parameter *param = new Parameter(STCconst, e->type, id, NULL); + fparams->shift(param); + Expression *ie = new IdentifierExp(Loc(), id); + Expressions *arguments = new Expressions(); + Expression *index = new IdentifierExp(Loc(), Id::p); + arguments->push(index); + result = new ArrayExp(Loc(), ie, arguments); + } -Expression *ComExp::buildArrayLoop(Parameters *fparams) -{ - Expression *ex1 = e1->buildArrayLoop(fparams); - Expression *e = new ComExp(Loc(), ex1); - return e; -} + void visit(AssignExp *e) + { + /* Evaluate assign expressions right to left + */ + Expression *ex2 = buildArrayLoop(e->e2); + /* Need the cast because: + * b = c + p[i]; + * where b is a byte fails because (c + p[i]) is an int + * which cannot be implicitly cast to byte. + */ + ex2 = new CastExp(Loc(), ex2, e->e1->type->nextOf()); + Expression *ex1 = buildArrayLoop(e->e1); + Parameter *param = (*fparams)[0]; + param->storageClass = 0; + result = new AssignExp(Loc(), ex1, ex2); + } -Expression *BinExp::buildArrayLoop(Parameters *fparams) -{ - switch(op) - { - case TOKadd: - case TOKmin: - case TOKmul: - case TOKdiv: - case TOKmod: - case TOKxor: - case TOKand: - case TOKor: - case TOKpow: - { - /* Evaluate assign expressions left to right - */ - BinExp *e = (BinExp *)copy(); - e->e1 = e->e1->buildArrayLoop(fparams); - e->e2 = e->e2->buildArrayLoop(fparams); - e->type = NULL; - return e; - } - default: - return Expression::buildArrayLoop(fparams); - } + void visit(BinAssignExp *e) + { + /* Evaluate assign expressions right to left + */ + Expression *ex2 = buildArrayLoop(e->e2); + Expression *ex1 = buildArrayLoop(e->e1); + Parameter *param = (*fparams)[0]; + param->storageClass = 0; + switch(e->op) + { + case TOKaddass: result = new AddAssignExp(e->loc, ex1, ex2); return; + case TOKminass: result = new MinAssignExp(e->loc, ex1, ex2); return; + case TOKmulass: result = new MulAssignExp(e->loc, ex1, ex2); return; + case TOKdivass: result = new DivAssignExp(e->loc, ex1, ex2); return; + case TOKmodass: result = new ModAssignExp(e->loc, ex1, ex2); return; + case TOKxorass: result = new XorAssignExp(e->loc, ex1, ex2); return; + case TOKandass: result = new AndAssignExp(e->loc, ex1, ex2); return; + case TOKorass: result = new OrAssignExp(e->loc, ex1, ex2); return; + case TOKpowass: result = new PowAssignExp(e->loc, ex1, ex2); return; + default: + assert(0); + } + } + + void visit(NegExp *e) + { + Expression *ex1 = buildArrayLoop(e->e1); + result = new NegExp(Loc(), ex1); + } + + void visit(ComExp *e) + { + Expression *ex1 = buildArrayLoop(e->e1); + result = new ComExp(Loc(), ex1); + } + + void visit(BinExp *e) + { + switch(e->op) + { + case TOKadd: + case TOKmin: + case TOKmul: + case TOKdiv: + case TOKmod: + case TOKxor: + case TOKand: + case TOKor: + case TOKpow: + { + /* Evaluate assign expressions left to right + */ + BinExp *be = (BinExp *)e->copy(); + be->e1 = buildArrayLoop(be->e1); + be->e2 = buildArrayLoop(be->e2); + be->type = NULL; + result = be; + return; + } + default: + visit((Expression *)e); + return; + } + } + + Expression *buildArrayLoop(Expression *e) + { + e->accept(this); + return result; + } + }; + + BuildArrayLoopVisitor v(fparams); + return v.buildArrayLoop(e); } /*********************************************** * Test if operand is a valid array op operand. */ -int Expression::isArrayOperand() +bool isArrayOperand(Expression *e) { - //printf("Expression::isArrayOperand() %s\n", toChars()); - if (op == TOKslice) - return 1; - if (op == TOKarrayliteral) + //printf("Expression::isArrayOperand() %s\n", e->toChars()); + if (e->op == TOKslice) + return true; + if (e->op == TOKarrayliteral) { - Type *t = type->toBasetype(); + Type *t = e->type->toBasetype(); while (t->ty == Tarray || t->ty == Tsarray) t = t->nextOf()->toBasetype(); return (t->ty != Tvoid); } - if (type->toBasetype()->ty == Tarray) + if (e->type->toBasetype()->ty == Tarray) { - switch (op) + switch (e->op) { - case TOKadd: - case TOKmin: - case TOKmul: - case TOKdiv: - case TOKmod: - case TOKxor: - case TOKand: - case TOKor: - case TOKassign: - case TOKaddass: - case TOKminass: - case TOKmulass: - case TOKdivass: - case TOKmodass: - case TOKxorass: - case TOKandass: - case TOKorass: - case TOKpow: - case TOKpowass: - case TOKneg: - case TOKtilde: - return 1; - - default: - break; + case TOKadd: + case TOKmin: + case TOKmul: + case TOKdiv: + case TOKmod: + case TOKxor: + case TOKand: + case TOKor: + case TOKassign: + case TOKaddass: + case TOKminass: + case TOKmulass: + case TOKdivass: + case TOKmodass: + case TOKxorass: + case TOKandass: + case TOKorass: + case TOKpow: + case TOKpowass: + case TOKneg: + case TOKtilde: + return true; + + default: + break; } } - return 0; + return false; } diff --git a/gcc/d/dfrontend/arraytypes.h b/gcc/d/dfrontend/arraytypes.h index 0cce38659..79f26a9e2 100644 --- a/gcc/d/dfrontend/arraytypes.h +++ b/gcc/d/dfrontend/arraytypes.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 2006-2007 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 2006-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/arraytypes.h + */ #ifndef DMD_ARRAYTYPES_H #define DMD_ARRAYTYPES_H @@ -43,9 +44,6 @@ typedef Array Initializers; typedef Array VarDeclarations; typedef Array Types; - -typedef Array ScopeDsymbols; - typedef Array Catches; typedef Array StaticDtorDeclarations; @@ -66,8 +64,6 @@ typedef Array GotoCaseStatements; typedef Array GotoStatements; -typedef Array ReturnStatements; - typedef Array TemplateInstances; #ifdef IN_GCC diff --git a/gcc/d/dfrontend/artistic.txt b/gcc/d/dfrontend/artistic.txt deleted file mode 100644 index cae432b7c..000000000 --- a/gcc/d/dfrontend/artistic.txt +++ /dev/null @@ -1,117 +0,0 @@ - - - - - The "Artistic License" - - Preamble - -The intent of this document is to state the conditions under which a -Package may be copied, such that the Copyright Holder maintains some -semblance of artistic control over the development of the package, -while giving the users of the package the right to use and distribute -the Package in a more-or-less customary fashion, plus the right to make -reasonable modifications. - -Definitions: - - "Package" refers to the collection of files distributed by the - Copyright Holder, and derivatives of that collection of files - created through textual modification. - - "Standard Version" refers to such a Package if it has not been - modified, or has been modified in accordance with the wishes - of the Copyright Holder as specified below. - - "Copyright Holder" is whoever is named in the copyright or - copyrights for the package. - - "You" is you, if you're thinking about copying or distributing - this Package. - - "Reasonable copying fee" is whatever you can justify on the - basis of media cost, duplication charges, time of people involved, - and so on. (You will not be required to justify it to the - Copyright Holder, but only to the computing community at large - as a market that must bear the fee.) - - "Freely Available" means that no fee is charged for the item - itself, though there may be fees involved in handling the item. - It also means that recipients of the item may redistribute it - under the same conditions they received it. - -1. You may make and give away verbatim copies of the source form of the -Standard Version of this Package without restriction, provided that you -duplicate all of the original copyright notices and associated disclaimers. - -2. You may apply bug fixes, portability fixes and other modifications -derived from the Public Domain or from the Copyright Holder. A Package -modified in such a way shall still be considered the Standard Version. - -3. You may otherwise modify your copy of this Package in any way, provided -that you insert a prominent notice in each changed file stating how and -when you changed that file, and provided that you do at least ONE of the -following: - - a) place your modifications in the Public Domain or otherwise make them - Freely Available, such as by posting said modifications to Usenet or - an equivalent medium, or placing the modifications on a major archive - site such as uunet.uu.net, or by allowing the Copyright Holder to include - your modifications in the Standard Version of the Package. - - b) use the modified Package only within your corporation or organization. - - c) rename any non-standard executables so the names do not conflict - with standard executables, which must also be provided, and provide - a separate manual page for each non-standard executable that clearly - documents how it differs from the Standard Version. - - d) make other distribution arrangements with the Copyright Holder. - -4. You may distribute the programs of this Package in object code or -executable form, provided that you do at least ONE of the following: - - a) distribute a Standard Version of the executables and library files, - together with instructions (in the manual page or equivalent) on where - to get the Standard Version. - - b) accompany the distribution with the machine-readable source of - the Package with your modifications. - - c) give non-standard executables non-standard names, and clearly - document the differences in manual pages (or equivalent), together - with instructions on where to get the Standard Version. - - d) make other distribution arrangements with the Copyright Holder. - -5. You may charge a reasonable copying fee for any distribution of this -Package. You may charge any fee you choose for support of this -Package. You may not charge a fee for this Package itself. However, -you may distribute this Package in aggregate with other (possibly -commercial) programs as part of a larger (possibly commercial) software -distribution provided that you do not advertise this Package as a -product of your own. You may embed this Package's interpreter within -an executable of yours (by linking); this shall be construed as a mere -form of aggregation, provided that the complete Standard Version of the -interpreter is so embedded. - -6. The source code and object code supplied as input to or produced as -output from the programs of this Package do not automatically fall -under the copyright of this Package, but belong to whoever generated -them, and may be sold commercially, and may be aggregated with this -Package. - -7. Aggregation of this Package with a commercial distribution is always -permitted provided that the use of this Package is embedded; that is, -when no overt attempt is made to make this Package's interfaces visible -to the end user of the commercial distribution. Such use shall not be -construed as a distribution of this Package. - -8. The name of the Copyright Holder may not be used to endorse or promote -products derived from this software without specific prior written permission. - -9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED -WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. - - The End diff --git a/gcc/d/dfrontend/attrib.c b/gcc/d/dfrontend/attrib.c index 8e1aa8809..4d4ac5649 100644 --- a/gcc/d/dfrontend/attrib.c +++ b/gcc/d/dfrontend/attrib.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/attrib.c + */ #include #include @@ -39,7 +40,7 @@ AttribDeclaration::AttribDeclaration(Dsymbols *decl) this->decl = decl; } -Dsymbols *AttribDeclaration::include(Scope *sc, ScopeDsymbol *sd) +Dsymbols *AttribDeclaration::include(Scope *sc, ScopeDsymbol *sds) { return decl; } @@ -51,7 +52,8 @@ int AttribDeclaration::apply(Dsymbol_apply_ft_t fp, void *param) if (d) { for (size_t i = 0; i < d->dim; i++) - { Dsymbol *s = (*d)[i]; + { + Dsymbol *s = (*d)[i]; if (s) { if (s->apply(fp, param)) @@ -62,89 +64,103 @@ int AttribDeclaration::apply(Dsymbol_apply_ft_t fp, void *param) return 0; } -int AttribDeclaration::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) +/**************************************** + * Create a new scope if one or more given attributes + * are different from the sc's. + * If the returned scope != sc, the caller should pop + * the scope after it used. + */ +Scope *AttribDeclaration::createNewScope(Scope *sc, + StorageClass stc, LINK linkage, PROT protection, int explicitProtection, + structalign_t structalign) +{ + Scope *sc2 = sc; + if (stc != sc->stc || + linkage != sc->linkage || + protection != sc->protection || + explicitProtection != sc->explicitProtection || + structalign != sc->structalign) + { + // create new one for changes + sc2 = sc->copy(); + sc2->flags &= ~SCOPEfree; + sc2->stc = stc; + sc2->linkage = linkage; + sc2->protection = protection; + sc2->explicitProtection = explicitProtection; + sc2->structalign = structalign; + } + return sc2; +} + +/**************************************** + * A hook point to supply scope for members. + * addMember, setScope, importAll, semantic, semantic2 and semantic3 will use this. + */ +Scope *AttribDeclaration::newScope(Scope *sc) +{ + return sc; +} + +int AttribDeclaration::addMember(Scope *sc, ScopeDsymbol *sds, int memnum) { int m = 0; - Dsymbols *d = include(sc, sd); + Dsymbols *d = include(sc, sds); if (d) { + Scope *sc2 = newScope(sc); + for (size_t i = 0; i < d->dim; i++) - { Dsymbol *s = (*d)[i]; - //printf("\taddMember %s to %s\n", s->toChars(), sd->toChars()); - m |= s->addMember(sc, sd, m | memnum); + { + Dsymbol *s = (*d)[i]; + //printf("\taddMember %s to %s\n", s->toChars(), sds->toChars()); + m |= s->addMember(sc2, sds, m | memnum); } + + if (sc2 != sc) + sc2->pop(); } return m; } -void AttribDeclaration::setScopeNewSc(Scope *sc, - StorageClass stc, LINK linkage, PROT protection, int explicitProtection, - structalign_t structalign) +void AttribDeclaration::setScope(Scope *sc) { - if (decl) + Dsymbols *d = include(sc, NULL); + + //printf("\tAttribDeclaration::setScope '%s', d = %p\n",toChars(), d); + if (d) { - Scope *newsc = sc; - if (stc != sc->stc || - linkage != sc->linkage || - protection != sc->protection || - explicitProtection != sc->explicitProtection || - structalign != sc->structalign) - { - // create new one for changes - newsc = new Scope(*sc); - newsc->flags &= ~SCOPEfree; - newsc->stc = stc; - newsc->linkage = linkage; - newsc->protection = protection; - newsc->explicitProtection = explicitProtection; - newsc->structalign = structalign; - } - for (size_t i = 0; i < decl->dim; i++) - { Dsymbol *s = (*decl)[i]; + Scope *sc2 = newScope(sc); - s->setScope(newsc); // yes, the only difference from semanticNewSc() - } - if (newsc != sc) + for (size_t i = 0; i < d->dim; i++) { - sc->offset = newsc->offset; - newsc->pop(); + Dsymbol *s = (*d)[i]; + s->setScope(sc2); } + + if (sc2 != sc) + sc2->pop(); } } -void AttribDeclaration::semanticNewSc(Scope *sc, - StorageClass stc, LINK linkage, PROT protection, int explicitProtection, - structalign_t structalign) +void AttribDeclaration::importAll(Scope *sc) { - if (decl) + Dsymbols *d = include(sc, NULL); + + //printf("\tAttribDeclaration::importAll '%s', d = %p\n", toChars(), d); + if (d) { - Scope *newsc = sc; - if (stc != sc->stc || - linkage != sc->linkage || - protection != sc->protection || - explicitProtection != sc->explicitProtection || - structalign != sc->structalign) - { - // create new one for changes - newsc = new Scope(*sc); - newsc->flags &= ~SCOPEfree; - newsc->stc = stc; - newsc->linkage = linkage; - newsc->protection = protection; - newsc->explicitProtection = explicitProtection; - newsc->structalign = structalign; - } - for (size_t i = 0; i < decl->dim; i++) - { Dsymbol *s = (*decl)[i]; + Scope *sc2 = newScope(sc); - s->semantic(newsc); - } - if (newsc != sc) + for (size_t i = 0; i < d->dim; i++) { - sc->offset = newsc->offset; - newsc->pop(); + Dsymbol *s = (*d)[i]; + s->importAll(sc2); } + + if (sc2 != sc) + sc2->pop(); } } @@ -155,12 +171,16 @@ void AttribDeclaration::semantic(Scope *sc) //printf("\tAttribDeclaration::semantic '%s', d = %p\n",toChars(), d); if (d) { + Scope *sc2 = newScope(sc); + for (size_t i = 0; i < d->dim; i++) { Dsymbol *s = (*d)[i]; - - s->semantic(sc); + s->semantic(sc2); } + + if (sc2 != sc) + sc2->pop(); } } @@ -170,10 +190,16 @@ void AttribDeclaration::semantic2(Scope *sc) if (d) { + Scope *sc2 = newScope(sc); + for (size_t i = 0; i < d->dim; i++) - { Dsymbol *s = (*d)[i]; - s->semantic2(sc); + { + Dsymbol *s = (*d)[i]; + s->semantic2(sc2); } + + if (sc2 != sc) + sc2->pop(); } } @@ -183,24 +209,16 @@ void AttribDeclaration::semantic3(Scope *sc) if (d) { - for (size_t i = 0; i < d->dim; i++) - { Dsymbol *s = (*d)[i]; - s->semantic3(sc); - } - } -} - -void AttribDeclaration::inlineScan() -{ - Dsymbols *d = include(NULL, NULL); + Scope *sc2 = newScope(sc); - if (d) - { for (size_t i = 0; i < d->dim; i++) - { Dsymbol *s = (*d)[i]; - //printf("AttribDeclaration::inlineScan %s\n", s->toChars()); - s->inlineScan(); + { + Dsymbol *s = (*d)[i]; + s->semantic3(sc2); } + + if (sc2 != sc) + sc2->pop(); } } @@ -214,7 +232,8 @@ void AttribDeclaration::addComment(const utf8_t *comment) if (d) { for (size_t i = 0; i < d->dim; i++) - { Dsymbol *s = (*d)[i]; + { + Dsymbol *s = (*d)[i]; //printf("AttribDeclaration::addComment %s\n", s->toChars()); s->addComment(comment); } @@ -222,18 +241,8 @@ void AttribDeclaration::addComment(const utf8_t *comment) } } -void AttribDeclaration::emitComment(Scope *sc) +void AttribDeclaration::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion) { - //printf("AttribDeclaration::emitComment(sc = %p)\n", sc); - - /* A general problem with this, illustrated by BUGZILLA 2516, - * is that attributes are not transmitted through to the underlying - * member declarations for template bodies, because semantic analysis - * is not done for template declaration bodies - * (only template instantiations). - * Hence, Ddoc omits attributes from template members. - */ - Dsymbols *d = include(NULL, NULL); if (d) @@ -241,20 +250,6 @@ void AttribDeclaration::emitComment(Scope *sc) for (size_t i = 0; i < d->dim; i++) { Dsymbol *s = (*d)[i]; - //printf("AttribDeclaration::emitComment %s\n", s->toChars()); - s->emitComment(sc); - } - } -} - -void AttribDeclaration::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion) -{ - Dsymbols *d = include(NULL, NULL); - - if (d) - { - for (size_t i = 0; i < d->dim; i++) - { Dsymbol *s = (*d)[i]; s->setFieldOffset(ad, poffset, isunion); } } @@ -311,7 +306,8 @@ void AttribDeclaration::checkCtorConstInit() if (d) { for (size_t i = 0; i < d->dim; i++) - { Dsymbol *s = (*d)[i]; + { + Dsymbol *s = (*d)[i]; s->checkCtorConstInit(); } } @@ -327,7 +323,8 @@ void AttribDeclaration::addLocalClass(ClassDeclarations *aclasses) if (d) { for (size_t i = 0; i < d->dim; i++) - { Dsymbol *s = (*d)[i]; + { + Dsymbol *s = (*d)[i]; s->addLocalClass(aclasses); } } @@ -341,7 +338,8 @@ void AttribDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) if (decl->dim == 0) buf->writestring("{}"); else if (hgs->hdrgen && decl->dim == 1 && (*decl)[0]->isUnitTestDeclaration()) - { // hack for bugzilla 8081 + { + // hack for bugzilla 8081 buf->writestring("{}"); } else if (decl->dim == 1) @@ -412,58 +410,29 @@ bool StorageClassDeclaration::oneMember(Dsymbol **ps, Identifier *ident) return t; } -void StorageClassDeclaration::setScope(Scope *sc) +Scope *StorageClassDeclaration::newScope(Scope *sc) { - if (decl) - { - StorageClass scstc = sc->stc; - - /* These sets of storage classes are mutually exclusive, - * so choose the innermost or most recent one. - */ - if (stc & (STCauto | STCscope | STCstatic | STCextern | STCmanifest)) - scstc &= ~(STCauto | STCscope | STCstatic | STCextern | STCmanifest); - if (stc & (STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared)) - scstc &= ~(STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared); - if (stc & (STCconst | STCimmutable | STCmanifest)) - scstc &= ~(STCconst | STCimmutable | STCmanifest); - if (stc & (STCgshared | STCshared | STCtls)) - scstc &= ~(STCgshared | STCshared | STCtls); - if (stc & (STCsafe | STCtrusted | STCsystem)) - scstc &= ~(STCsafe | STCtrusted | STCsystem); - scstc |= stc; - //printf("scstc = x%llx\n", scstc); - - setScopeNewSc(sc, scstc, sc->linkage, sc->protection, sc->explicitProtection, sc->structalign); - } -} + StorageClass scstc = sc->stc; -void StorageClassDeclaration::semantic(Scope *sc) -{ - if (decl) - { - StorageClass scstc = sc->stc; - - /* These sets of storage classes are mutually exclusive, - * so choose the innermost or most recent one. - */ - if (stc & (STCauto | STCscope | STCstatic | STCextern | STCmanifest)) - scstc &= ~(STCauto | STCscope | STCstatic | STCextern | STCmanifest); - if (stc & (STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared)) - scstc &= ~(STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared); - if (stc & (STCconst | STCimmutable | STCmanifest)) - scstc &= ~(STCconst | STCimmutable | STCmanifest); - if (stc & (STCgshared | STCshared | STCtls)) - scstc &= ~(STCgshared | STCshared | STCtls); - if (stc & (STCsafe | STCtrusted | STCsystem)) - scstc &= ~(STCsafe | STCtrusted | STCsystem); - scstc |= stc; - - semanticNewSc(sc, scstc, sc->linkage, sc->protection, sc->explicitProtection, sc->structalign); - } + /* These sets of storage classes are mutually exclusive, + * so choose the innermost or most recent one. + */ + if (stc & (STCauto | STCscope | STCstatic | STCextern | STCmanifest)) + scstc &= ~(STCauto | STCscope | STCstatic | STCextern | STCmanifest); + if (stc & (STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared)) + scstc &= ~(STCauto | STCscope | STCstatic | STCtls | STCmanifest | STCgshared); + if (stc & (STCconst | STCimmutable | STCmanifest)) + scstc &= ~(STCconst | STCimmutable | STCmanifest); + if (stc & (STCgshared | STCshared | STCtls)) + scstc &= ~(STCgshared | STCshared | STCtls); + if (stc & (STCsafe | STCtrusted | STCsystem)) + scstc &= ~(STCsafe | STCtrusted | STCsystem); + scstc |= stc; + //printf("scstc = x%llx\n", scstc); + + return createNewScope(sc, scstc, sc->linkage, sc->protection, sc->explicitProtection, sc->structalign); } - /************************************************* * Pick off one of the storage classes from stc, * and return a pointer to a string representation of it. @@ -504,6 +473,7 @@ const char *StorageClassDeclaration::stcToChars(char tmp[], StorageClass& stc) { STCref, TOKref }, { STCtls }, { STCgshared, TOKgshared }, + { STCnogc, TOKat, "nogc" }, { STCproperty, TOKat, "property" }, { STCsafe, TOKat, "safe" }, { STCtrusted, TOKat, "trusted" }, @@ -576,7 +546,7 @@ void DeprecatedDeclaration::setScope(Scope *sc) { assert(msg); char *depmsg = NULL; - StringExp *se = msg->toString(); + StringExp *se = msg->toStringExp(); if (se) depmsg = (char *)se->string; else @@ -614,47 +584,14 @@ Dsymbol *LinkDeclaration::syntaxCopy(Dsymbol *s) return ld; } -void LinkDeclaration::setScope(Scope *sc) -{ - //printf("LinkDeclaration::setScope(linkage = %d, decl = %p)\n", linkage, decl); - if (decl) - { - setScopeNewSc(sc, sc->stc, linkage, sc->protection, sc->explicitProtection, sc->structalign); - } -} - -void LinkDeclaration::semantic(Scope *sc) -{ - //printf("LinkDeclaration::semantic(linkage = %d, decl = %p)\n", linkage, decl); - if (decl) - { - semanticNewSc(sc, sc->stc, linkage, sc->protection, sc->explicitProtection, sc->structalign); - } -} - -void LinkDeclaration::semantic3(Scope *sc) +Scope *LinkDeclaration::newScope(Scope *sc) { - //printf("LinkDeclaration::semantic3(linkage = %d, decl = %p)\n", linkage, decl); - if (decl) - { LINK linkage_save = sc->linkage; - - sc->linkage = linkage; - for (size_t i = 0; i < decl->dim; i++) - { - Dsymbol *s = (*decl)[i]; - - s->semantic3(sc); - } - sc->linkage = linkage_save; - } - else - { - sc->linkage = linkage; - } + return createNewScope(sc, sc->stc, this->linkage, sc->protection, sc->explicitProtection, sc->structalign); } void LinkDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ const char *p; +{ + const char *p; switch (linkage) { @@ -696,71 +633,15 @@ Dsymbol *ProtDeclaration::syntaxCopy(Dsymbol *s) return pd; } -void ProtDeclaration::setScope(Scope *sc) -{ - if (decl) - { - setScopeNewSc(sc, sc->stc, sc->linkage, protection, 1, sc->structalign); - } -} - -void ProtDeclaration::importAll(Scope *sc) +Scope *ProtDeclaration::newScope(Scope *sc) { - Scope *newsc = sc; - if (sc->protection != protection || - sc->explicitProtection != 1) - { - // create new one for changes - newsc = new Scope(*sc); - newsc->flags &= ~SCOPEfree; - newsc->protection = protection; - newsc->explicitProtection = 1; - } - - for (size_t i = 0; i < decl->dim; i++) - { - Dsymbol *s = (*decl)[i]; - s->importAll(newsc); - } - - if (newsc != sc) - newsc->pop(); -} - -void ProtDeclaration::semantic(Scope *sc) -{ - if (decl) - { - semanticNewSc(sc, sc->stc, sc->linkage, protection, 1, sc->structalign); - } -} - -void ProtDeclaration::emitComment(Scope *sc) -{ - if (decl) - { - sc = sc->push(); - sc->protection = protection; - AttribDeclaration::emitComment(sc); - sc = sc->pop(); - } -} - -void ProtDeclaration::protectionToCBuffer(OutBuffer *buf, PROT protection) -{ - const char *p; - - p = Pprotectionnames[protection]; - - assert(p); - - buf->writestring(p); - buf->writeByte(' '); + return createNewScope(sc, sc->stc, sc->linkage, this->protection, 1, sc->structalign); } void ProtDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { - protectionToCBuffer(buf, protection); + protectionToBuffer(buf, protection); + buf->writeByte(' '); AttribDeclaration::toCBuffer(buf, hgs); } @@ -781,25 +662,11 @@ Dsymbol *AlignDeclaration::syntaxCopy(Dsymbol *s) return ad; } -void AlignDeclaration::setScope(Scope *sc) -{ - //printf("\tAlignDeclaration::setScope '%s'\n",toChars()); - if (decl) - { - setScopeNewSc(sc, sc->stc, sc->linkage, sc->protection, sc->explicitProtection, salign); - } -} - -void AlignDeclaration::semantic(Scope *sc) +Scope *AlignDeclaration::newScope(Scope *sc) { - //printf("\tAlignDeclaration::semantic '%s'\n",toChars()); - if (decl) - { - semanticNewSc(sc, sc->stc, sc->linkage, sc->protection, sc->explicitProtection, salign); - } + return createNewScope(sc, sc->stc, sc->linkage, sc->protection, sc->explicitProtection, this->salign); } - void AlignDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { if (salign == STRUCTALIGN_DEFAULT) @@ -850,7 +717,6 @@ void AnonDeclaration::semantic(Scope *sc) sc = sc->push(); sc->stc &= ~(STCauto | STCscope | STCstatic | STCtls | STCgshared); sc->inunion = isunion; - sc->offset = 0; sc->flags = 0; for (size_t i = 0; i < decl->dim; i++) @@ -1002,7 +868,8 @@ static unsigned setMangleOverride(Dsymbol *s, char *sym) } void PragmaDeclaration::semantic(Scope *sc) -{ // Should be merged with PragmaStatement +{ + // Should be merged with PragmaStatement //printf("\tPragmaDeclaration::semantic '%s'\n",toChars()); if (ident == Id::msg) @@ -1021,10 +888,11 @@ void PragmaDeclaration::semantic(Scope *sc) // pragma(msg) is allowed to contain types as well as expressions e = ctfeInterpretForPragmaMsg(e); if (e->op == TOKerror) - { errorSupplemental(loc, "while evaluating pragma(msg, %s)", (*args)[i]->toChars()); + { + errorSupplemental(loc, "while evaluating pragma(msg, %s)", (*args)[i]->toChars()); return; } - StringExp *se = e->toString(); + StringExp *se = e->toStringExp(); if (se) { se = se->toUTF8(sc); @@ -1054,7 +922,7 @@ void PragmaDeclaration::semantic(Scope *sc) (*args)[0] = e; if (e->op == TOKerror) goto Lnodecl; - StringExp *se = e->toString(); + StringExp *se = e->toStringExp(); if (!se) error("string expected for library name, not '%s'", e->toChars()); else @@ -1118,7 +986,7 @@ void PragmaDeclaration::semantic(Scope *sc) if (e->op == TOKerror) goto Lnodecl; - StringExp *se = e->toString(); + StringExp *se = e->toStringExp(); if (!se) { @@ -1220,7 +1088,7 @@ void PragmaDeclaration::semantic(Scope *sc) if (ident == Id::mangle) { - StringExp *e = (*args)[0]->toString(); + StringExp *e = (*args)[0]->toStringExp(); char *name = (char *)mem.malloc(e->len + 1); memcpy(name, e->string, e->len); @@ -1243,12 +1111,6 @@ void PragmaDeclaration::semantic(Scope *sc) } } -bool PragmaDeclaration::oneMember(Dsymbol **ps, Identifier *ident) -{ - *ps = NULL; - return true; -} - const char *PragmaDeclaration::kind() { return "pragma"; @@ -1306,34 +1168,13 @@ bool ConditionalDeclaration::oneMember(Dsymbol **ps, Identifier *ident) } } -void ConditionalDeclaration::emitComment(Scope *sc) -{ - //printf("ConditionalDeclaration::emitComment(sc = %p)\n", sc); - if (condition->inc) - { - AttribDeclaration::emitComment(sc); - } - else if (sc->docbuf) - { - /* If generating doc comment, be careful because if we're inside - * a template, then include(NULL, NULL) will fail. - */ - Dsymbols *d = decl ? decl : elsedecl; - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - s->emitComment(sc); - } - } -} - // Decide if 'then' or 'else' code should be included -Dsymbols *ConditionalDeclaration::include(Scope *sc, ScopeDsymbol *sd) +Dsymbols *ConditionalDeclaration::include(Scope *sc, ScopeDsymbol *sds) { //printf("ConditionalDeclaration::include(sc = %p) scope = %p\n", sc, scope); assert(condition); - return condition->include(scope ? scope : sc, sd) ? decl : elsedecl; + return condition->include(scope ? scope : sc, sds) ? decl : elsedecl; } void ConditionalDeclaration::setScope(Scope *sc) @@ -1346,28 +1187,11 @@ void ConditionalDeclaration::setScope(Scope *sc) for (size_t i = 0; i < d->dim; i++) { Dsymbol *s = (*d)[i]; - s->setScope(sc); } } } -void ConditionalDeclaration::importAll(Scope *sc) -{ - Dsymbols *d = include(sc, NULL); - - //printf("\tConditionalDeclaration::importAll '%s', d = %p\n",toChars(), d); - if (d) - { - for (size_t i = 0; i < d->dim; i++) - { - Dsymbol *s = (*d)[i]; - - s->importAll(sc); - } - } -} - void ConditionalDeclaration::addComment(const utf8_t *comment) { /* Because addComment is called by the parser, if we called @@ -1385,7 +1209,8 @@ void ConditionalDeclaration::addComment(const utf8_t *comment) if (d) { for (size_t i = 0; i < d->dim; i++) - { Dsymbol *s = (*d)[i]; + { + Dsymbol *s = (*d)[i]; //printf("ConditionalDeclaration::addComment %s\n", s->toChars()); s->addComment(comment); } @@ -1443,7 +1268,7 @@ StaticIfDeclaration::StaticIfDeclaration(Condition *condition, : ConditionalDeclaration(condition, decl, elsedecl) { //printf("StaticIfDeclaration::StaticIfDeclaration()\n"); - sd = NULL; + sds = NULL; addisdone = 0; } @@ -1459,7 +1284,7 @@ Dsymbol *StaticIfDeclaration::syntaxCopy(Dsymbol *s) return dd; } -Dsymbols *StaticIfDeclaration::include(Scope *sc, ScopeDsymbol *sd) +Dsymbols *StaticIfDeclaration::include(Scope *sc, ScopeDsymbol *sds) { //printf("StaticIfDeclaration::include(sc = %p) scope = %p\n", sc, scope); @@ -1470,7 +1295,7 @@ Dsymbols *StaticIfDeclaration::include(Scope *sc, ScopeDsymbol *sd) */ bool x = !scope && sc; if (x) scope = sc; - Dsymbols *d = ConditionalDeclaration::include(sc, sd); + Dsymbols *d = ConditionalDeclaration::include(sc, sds); if (x) scope = NULL; // Set the scopes lazily. @@ -1487,11 +1312,11 @@ Dsymbols *StaticIfDeclaration::include(Scope *sc, ScopeDsymbol *sd) } else { - return ConditionalDeclaration::include(sc, sd); + return ConditionalDeclaration::include(sc, sds); } } -int StaticIfDeclaration::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) +int StaticIfDeclaration::addMember(Scope *sc, ScopeDsymbol *sds, int memnum) { //printf("StaticIfDeclaration::addMember() '%s'\n",toChars()); /* This is deferred until semantic(), so that @@ -1505,11 +1330,12 @@ int StaticIfDeclaration::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) * const int k; * } */ - this->sd = sd; + this->sds = sds; int m = 0; - if (memnum == 0) - { m = AttribDeclaration::addMember(sc, sd, memnum); + if (0 && memnum == 0) + { + m = AttribDeclaration::addMember(sc, sds, memnum); addisdone = 1; } return m; @@ -1531,20 +1357,20 @@ void StaticIfDeclaration::setScope(Scope *sc) void StaticIfDeclaration::semantic(Scope *sc) { - Dsymbols *d = include(sc, sd); + Dsymbols *d = include(sc, sds); //printf("\tStaticIfDeclaration::semantic '%s', d = %p\n",toChars(), d); if (d) { if (!addisdone) - { AttribDeclaration::addMember(sc, sd, 1); + { + AttribDeclaration::addMember(sc, sds, 1); addisdone = 1; } for (size_t i = 0; i < d->dim; i++) { Dsymbol *s = (*d)[i]; - s->semantic(sc); } } @@ -1566,7 +1392,7 @@ CompileDeclaration::CompileDeclaration(Loc loc, Expression *exp) //printf("CompileDeclaration(loc = %d)\n", loc.linnum); this->loc = loc; this->exp = exp; - this->sd = NULL; + this->sds = NULL; this->compiled = 0; } @@ -1577,24 +1403,29 @@ Dsymbol *CompileDeclaration::syntaxCopy(Dsymbol *s) return sc; } -int CompileDeclaration::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) +int CompileDeclaration::addMember(Scope *sc, ScopeDsymbol *sds, int memnum) { - //printf("CompileDeclaration::addMember(sc = %p, sd = %p, memnum = %d)\n", sc, sd, memnum); + //printf("CompileDeclaration::addMember(sc = %p, sds = %p, memnum = %d)\n", sc, sds, memnum); if (compiled) return 1; - this->sd = sd; + this->sds = sds; if (memnum == 0) { /* No members yet, so parse the mixin now */ compileIt(sc); - memnum |= AttribDeclaration::addMember(sc, sd, memnum); + memnum |= AttribDeclaration::addMember(sc, sds, memnum); compiled = 1; } return memnum; } +void CompileDeclaration::setScope(Scope *sc) +{ + Dsymbol::setScope(sc); +} + void CompileDeclaration::compileIt(Scope *sc) { //printf("CompileDeclaration::compileIt(loc = %d) %s\n", loc.linnum, exp->toChars()); @@ -1602,23 +1433,26 @@ void CompileDeclaration::compileIt(Scope *sc) exp = exp->semantic(sc); exp = resolveProperties(sc, exp); sc = sc->endCTFE(); - exp = exp->ctfeInterpret(); - StringExp *se = exp->toString(); - if (!se) - { - exp->error("argument to mixin must be a string, not (%s)", exp->toChars()); - } - else + + if (exp->op != TOKerror) { - se = se->toUTF8(sc); - Parser p(loc, sc->module, (utf8_t *)se->string, se->len, 0); - p.nextToken(); - unsigned errors = global.errors; - decl = p.parseDeclDefs(0); - if (p.token.value != TOKeof) - exp->error("incomplete mixin declaration (%s)", se->toChars()); - if (global.errors != errors) - decl = NULL; + Expression *e = exp->ctfeInterpret(); + StringExp *se = e->toStringExp(); + if (!se) + exp->error("argument to mixin must be a string, not (%s) of type %s", exp->toChars(), exp->type->toChars()); + else + { + se = se->toUTF8(sc); + Parser p(loc, sc->module, (utf8_t *)se->string, se->len, 0); + p.nextToken(); + + unsigned errors = global.errors; + decl = p.parseDeclDefs(0); + if (p.token.value != TOKeof) + exp->error("incomplete mixin declaration (%s)", se->toChars()); + if (global.errors != errors) + decl = NULL; + } } } @@ -1629,7 +1463,7 @@ void CompileDeclaration::semantic(Scope *sc) if (!compiled) { compileIt(sc); - AttribDeclaration::addMember(sc, sd, 0); + AttribDeclaration::addMember(sc, sds, 0); compiled = 1; if (scope && decl) @@ -1675,89 +1509,48 @@ Dsymbol *UserAttributeDeclaration::syntaxCopy(Dsymbol *s) return new UserAttributeDeclaration(atts, Dsymbol::arraySyntaxCopy(decl)); } +Scope *UserAttributeDeclaration::newScope(Scope *sc) +{ + Scope *sc2 = sc; + if (atts && atts->dim) + { + // create new one for changes + sc2 = sc->copy(); + sc2->userAttribDecl = this; + } + return sc2; +} + void UserAttributeDeclaration::setScope(Scope *sc) { //printf("UserAttributeDeclaration::setScope() %p\n", this); if (decl) - { Dsymbol::setScope(sc); // for forward reference of UDAs - Scope *newsc = sc; - if (atts && atts->dim) - { - // create new one for changes - newsc = sc->push(); - newsc->userAttribDecl = this; - } - for (size_t i = 0; i < decl->dim; i++) - { - Dsymbol *s = (*decl)[i]; - s->setScope(newsc); // yes, the only difference from semantic() - } - if (newsc != sc) - { - sc->offset = newsc->offset; - newsc->pop(); - } - } + return AttribDeclaration::setScope(sc); } void UserAttributeDeclaration::semantic(Scope *sc) { //printf("UserAttributeDeclaration::semantic() %p\n", this); - if (decl) - { - if (!scope) - Dsymbol::setScope(sc); // for function local symbols + if (decl && !scope) + Dsymbol::setScope(sc); // for function local symbols - Scope *newsc = sc; - if (atts && atts->dim) - { - // create new one for changes - newsc = sc->push(); - newsc->userAttribDecl = this; - } - for (size_t i = 0; i < decl->dim; i++) - { - Dsymbol *s = (*decl)[i]; - s->semantic(newsc); - } - if (newsc != sc) - { - sc->offset = newsc->offset; - newsc->pop(); - } - } + return AttribDeclaration::semantic(sc); } void UserAttributeDeclaration::semantic2(Scope *sc) { - if (decl) + if (decl && atts && atts->dim) { - Scope *newsc = sc; - if (atts && atts->dim) - { - if (scope) - { - scope = NULL; - arrayExpressionSemantic(atts, sc); // run semantic - } - - // create new one for changes - newsc = sc->push(); - newsc->userAttribDecl = this; - } - for (size_t i = 0; i < decl->dim; i++) + if (atts && atts->dim && scope) { - Dsymbol *s = (*decl)[i]; - s->semantic2(newsc); - } - if (newsc != sc) - { - sc->offset = newsc->offset; - newsc->pop(); + scope = NULL; + arrayExpressionSemantic(atts, sc); // run semantic } } + + AttribDeclaration::semantic2(sc); } Expressions *UserAttributeDeclaration::concat(Expressions *udas1, Expressions *udas2) diff --git a/gcc/d/dfrontend/attrib.h b/gcc/d/dfrontend/attrib.h index affe2cc8e..825956447 100644 --- a/gcc/d/dfrontend/attrib.h +++ b/gcc/d/dfrontend/attrib.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/attrib.h + */ #ifndef DMD_ATTRIB_H #define DMD_ATTRIB_H @@ -33,21 +34,19 @@ class AttribDeclaration : public Dsymbol Dsymbols *decl; // array of Dsymbol's AttribDeclaration(Dsymbols *decl); - virtual Dsymbols *include(Scope *sc, ScopeDsymbol *s); + virtual Dsymbols *include(Scope *sc, ScopeDsymbol *sds); int apply(Dsymbol_apply_ft_t fp, void *param); - int addMember(Scope *sc, ScopeDsymbol *s, int memnum); - void setScopeNewSc(Scope *sc, - StorageClass newstc, LINK linkage, PROT protection, int explictProtection, - structalign_t structalign); - void semanticNewSc(Scope *sc, + static Scope *createNewScope(Scope *sc, StorageClass newstc, LINK linkage, PROT protection, int explictProtection, structalign_t structalign); + virtual Scope *newScope(Scope *sc); + int addMember(Scope *sc, ScopeDsymbol *sds, int memnum); + void setScope(Scope *sc); + void importAll(Scope *sc); void semantic(Scope *sc); void semantic2(Scope *sc); void semantic3(Scope *sc); - void inlineScan(); void addComment(const utf8_t *comment); - void emitComment(Scope *sc); const char *kind(); bool oneMember(Dsymbol **ps, Identifier *ident); void setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion); @@ -58,7 +57,7 @@ class AttribDeclaration : public Dsymbol void toCBuffer(OutBuffer *buf, HdrGenState *hgs); AttribDeclaration *isAttribDeclaration() { return this; } - void toObjFile(int multiobj); // compile to .obj file + void toObjFile(bool multiobj); // compile to .obj file void accept(Visitor *v) { v->visit(this); } }; @@ -69,8 +68,7 @@ class StorageClassDeclaration : public AttribDeclaration StorageClassDeclaration(StorageClass stc, Dsymbols *decl); Dsymbol *syntaxCopy(Dsymbol *s); - void setScope(Scope *sc); - void semantic(Scope *sc); + Scope *newScope(Scope *sc); bool oneMember(Dsymbol **ps, Identifier *ident); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); @@ -98,9 +96,7 @@ class LinkDeclaration : public AttribDeclaration LinkDeclaration(LINK p, Dsymbols *decl); Dsymbol *syntaxCopy(Dsymbol *s); - void setScope(Scope *sc); - void semantic(Scope *sc); - void semantic3(Scope *sc); + Scope *newScope(Scope *sc); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); char *toChars(); void accept(Visitor *v) { v->visit(this); } @@ -113,13 +109,8 @@ class ProtDeclaration : public AttribDeclaration ProtDeclaration(PROT p, Dsymbols *decl); Dsymbol *syntaxCopy(Dsymbol *s); - void importAll(Scope *sc); - void setScope(Scope *sc); - void semantic(Scope *sc); - void emitComment(Scope *sc); + Scope *newScope(Scope *sc); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - - static void protectionToCBuffer(OutBuffer *buf, PROT protection); void accept(Visitor *v) { v->visit(this); } }; @@ -130,8 +121,7 @@ class AlignDeclaration : public AttribDeclaration AlignDeclaration(unsigned sa, Dsymbols *decl); Dsymbol *syntaxCopy(Dsymbol *s); - void setScope(Scope *sc); - void semantic(Scope *sc); + Scope *newScope(Scope *sc); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void accept(Visitor *v) { v->visit(this); } }; @@ -161,10 +151,9 @@ class PragmaDeclaration : public AttribDeclaration Dsymbol *syntaxCopy(Dsymbol *s); void semantic(Scope *sc); void setScope(Scope *sc); - bool oneMember(Dsymbol **ps, Identifier *ident); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); const char *kind(); - void toObjFile(int multiobj); // compile to .obj file + void toObjFile(bool multiobj); // compile to .obj file void accept(Visitor *v) { v->visit(this); } }; @@ -177,11 +166,9 @@ class ConditionalDeclaration : public AttribDeclaration ConditionalDeclaration(Condition *condition, Dsymbols *decl, Dsymbols *elsedecl); Dsymbol *syntaxCopy(Dsymbol *s); bool oneMember(Dsymbol **ps, Identifier *ident); - void emitComment(Scope *sc); - Dsymbols *include(Scope *sc, ScopeDsymbol *s); + Dsymbols *include(Scope *sc, ScopeDsymbol *sds); void addComment(const utf8_t *comment); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - void importAll(Scope *sc); void setScope(Scope *sc); void accept(Visitor *v) { v->visit(this); } }; @@ -189,13 +176,13 @@ class ConditionalDeclaration : public AttribDeclaration class StaticIfDeclaration : public ConditionalDeclaration { public: - ScopeDsymbol *sd; + ScopeDsymbol *sds; int addisdone; StaticIfDeclaration(Condition *condition, Dsymbols *decl, Dsymbols *elsedecl); Dsymbol *syntaxCopy(Dsymbol *s); - Dsymbols *include(Scope *sc, ScopeDsymbol *s); - int addMember(Scope *sc, ScopeDsymbol *s, int memnum); + Dsymbols *include(Scope *sc, ScopeDsymbol *sds); + int addMember(Scope *sc, ScopeDsymbol *sds, int memnum); void semantic(Scope *sc); void importAll(Scope *sc); void setScope(Scope *sc); @@ -210,12 +197,13 @@ class CompileDeclaration : public AttribDeclaration public: Expression *exp; - ScopeDsymbol *sd; + ScopeDsymbol *sds; int compiled; CompileDeclaration(Loc loc, Expression *exp); Dsymbol *syntaxCopy(Dsymbol *s); - int addMember(Scope *sc, ScopeDsymbol *sd, int memnum); + int addMember(Scope *sc, ScopeDsymbol *sds, int memnum); + void setScope(Scope *sc); void compileIt(Scope *sc); void semantic(Scope *sc); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); @@ -234,6 +222,7 @@ class UserAttributeDeclaration : public AttribDeclaration UserAttributeDeclaration(Expressions *atts, Dsymbols *decl); Dsymbol *syntaxCopy(Dsymbol *s); + Scope *newScope(Scope *sc); void semantic(Scope *sc); void semantic2(Scope *sc); void setScope(Scope *sc); diff --git a/gcc/d/dfrontend/boostlicense.txt b/gcc/d/dfrontend/boostlicense.txt new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/gcc/d/dfrontend/boostlicense.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/gcc/d/dfrontend/canthrow.c b/gcc/d/dfrontend/canthrow.c index defbcbaec..3fb266c38 100644 --- a/gcc/d/dfrontend/canthrow.c +++ b/gcc/d/dfrontend/canthrow.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2011 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/canthrow.c + */ #include #include @@ -23,122 +24,136 @@ #include "scope.h" #include "attrib.h" -bool Dsymbol_canThrow(Dsymbol *s, bool mustNotThrow); -int lambdaCanThrow(Expression *e, void *param); +bool Dsymbol_canThrow(Dsymbol *s, FuncDeclaration *func, bool mustNotThrow); +bool walkPostorder(Expression *e, StoppableVisitor *v); /******************************************** - * Convert from expression to delegate that returns the expression, - * i.e. convert: - * expr - * to: - * t delegate() { return expr; } + * Returns true if the expression may throw exceptions. + * If 'mustNotThrow' is true, generate an error if it throws */ -struct CanThrow -{ - bool can; - bool mustnot; -}; - -bool Expression::canThrow(bool mustNotThrow) +bool canThrow(Expression *e, FuncDeclaration *func, bool mustNotThrow) { //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars()); - CanThrow ct; - ct.can = false; - ct.mustnot = mustNotThrow; - apply(&lambdaCanThrow, &ct); - return ct.can; -} -int lambdaCanThrow(Expression *e, void *param) -{ - CanThrow *pct = (CanThrow *)param; - switch (e->op) + // stop walking if we determine this expression can throw + class CanThrow : public StoppableVisitor { - case TOKdeclaration: + FuncDeclaration *func; + bool mustNotThrow; + + public: + CanThrow(FuncDeclaration *func, bool mustNotThrow) + : func(func), mustNotThrow(mustNotThrow) { - DeclarationExp *de = (DeclarationExp *)e; - pct->can = Dsymbol_canThrow(de->declaration, pct->mustnot); - break; } - case TOKcall: + void visit(Expression *) + { + } + + void visit(DeclarationExp *de) + { + stop = Dsymbol_canThrow(de->declaration, func, mustNotThrow); + } + + void visit(CallExp *ce) { - CallExp *ce = (CallExp *)e; if (global.errors && !ce->e1->type) - break; // error recovery + return; // error recovery /* If calling a function or delegate that is typed as nothrow, * then this expression cannot throw. * Note that pure functions can throw. */ Type *t = ce->e1->type->toBasetype(); - if (t->ty == Tfunction && ((TypeFunction *)t)->isnothrow) + if (ce->f && ce->f == func) + ; + else if (t->ty == Tfunction && ((TypeFunction *)t)->isnothrow) ; else if (t->ty == Tdelegate && ((TypeFunction *)((TypeDelegate *)t)->next)->isnothrow) ; else { - if (pct->mustnot) - e->error("'%s' is not nothrow", ce->f ? ce->f->toPrettyChars() : ce->e1->toChars()); - pct->can = true; + if (mustNotThrow) + { + const char *s; + if (ce->f) + s = ce->f->toPrettyChars(); + else if (ce->e1->op == TOKstar) + { + // print 'fp' if ce->e1 is (*fp) + s = ((PtrExp *)ce->e1)->e1->toChars(); + } + else + s = ce->e1->toChars(); + ce->error("'%s' is not nothrow", s); + } + stop = true; } - break; } - case TOKnew: + void visit(NewExp *ne) { - NewExp *ne = (NewExp *)e; if (ne->member) { // See if constructor call can throw Type *t = ne->member->type->toBasetype(); if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow) { - if (pct->mustnot) - e->error("constructor %s is not nothrow", ne->member->toChars()); - pct->can = true; + if (mustNotThrow) + ne->error("constructor %s is not nothrow", ne->member->toChars()); + stop = true; } } // regard storage allocation failures as not recoverable - break; } - case TOKassign: - case TOKconstruct: + void visit(AssignExp *ae) { + // blit-init cannot throw + if (ae->op == TOKblit) + return; + /* Element-wise assignment could invoke postblits. */ - AssignExp *ae = (AssignExp *)e; - if (ae->e1->op != TOKslice) - break; + Type *t; + if (ae->type->toBasetype()->ty == Tsarray) + { + if (!ae->e2->isLvalue()) + return; + t = ae->type; + } + else if (ae->e1->op == TOKslice) + t = ((SliceExp *)ae->e1)->e1->type; + else + return; - Type *tv = ae->e1->type->toBasetype()->nextOf()->baseElemOf(); + Type *tv = t->baseElemOf(); if (tv->ty != Tstruct) - break; + return; StructDeclaration *sd = ((TypeStruct *)tv)->sym; if (!sd->postblit || sd->postblit->type->ty != Tfunction) - break; + return; if (((TypeFunction *)sd->postblit->type)->isnothrow) ; else { - if (pct->mustnot) - e->error("'%s' is not nothrow", sd->postblit->toPrettyChars()); - pct->can = true; + if (mustNotThrow) + ae->error("'%s' is not nothrow", sd->postblit->toPrettyChars()); + stop = true; } - break; } - case TOKnewanonclass: + void visit(NewAnonClassExp *) + { assert(0); // should have been lowered by semantic() - break; + } + }; - default: - break; - } - return pct->can; // stop walking if we determine this expression can throw + CanThrow ct(func, mustNotThrow); + return walkPostorder(e, &ct); } /************************************** @@ -146,7 +161,7 @@ int lambdaCanThrow(Expression *e, void *param) * Mirrors logic in Dsymbol_toElem(). */ -bool Dsymbol_canThrow(Dsymbol *s, bool mustNotThrow) +bool Dsymbol_canThrow(Dsymbol *s, FuncDeclaration *func, bool mustNotThrow) { AttribDeclaration *ad; VarDeclaration *vd; @@ -163,7 +178,7 @@ bool Dsymbol_canThrow(Dsymbol *s, bool mustNotThrow) for (size_t i = 0; i < decl->dim; i++) { s = (*decl)[i]; - if (Dsymbol_canThrow(s, mustNotThrow)) + if (Dsymbol_canThrow(s, func, mustNotThrow)) return true; } } @@ -172,7 +187,7 @@ bool Dsymbol_canThrow(Dsymbol *s, bool mustNotThrow) { s = s->toAlias(); if (s != vd) - return Dsymbol_canThrow(s, mustNotThrow); + return Dsymbol_canThrow(s, func, mustNotThrow); if (vd->storage_class & STCmanifest) ; else if (vd->isStatic() || vd->storage_class & (STCextern | STCtls | STCgshared)) @@ -180,12 +195,13 @@ bool Dsymbol_canThrow(Dsymbol *s, bool mustNotThrow) else { if (vd->init) - { ExpInitializer *ie = vd->init->isExpInitializer(); - if (ie && ie->exp->canThrow(mustNotThrow)) + { + ExpInitializer *ie = vd->init->isExpInitializer(); + if (ie && canThrow(ie->exp, func, mustNotThrow)) return true; } if (vd->edtor && !vd->noscope) - return vd->edtor->canThrow(mustNotThrow); + return canThrow(vd->edtor, func, mustNotThrow); } } else if ((tm = s->isTemplateMixin()) != NULL) @@ -196,7 +212,7 @@ bool Dsymbol_canThrow(Dsymbol *s, bool mustNotThrow) for (size_t i = 0; i < tm->members->dim; i++) { Dsymbol *sm = (*tm->members)[i]; - if (Dsymbol_canThrow(sm, mustNotThrow)) + if (Dsymbol_canThrow(sm, func, mustNotThrow)) return true; } } @@ -204,12 +220,15 @@ bool Dsymbol_canThrow(Dsymbol *s, bool mustNotThrow) else if ((td = s->isTupleDeclaration()) != NULL) { for (size_t i = 0; i < td->objects->dim; i++) - { RootObject *o = (*td->objects)[i]; + { + RootObject *o = (*td->objects)[i]; if (o->dyncast() == DYNCAST_EXPRESSION) - { Expression *eo = (Expression *)o; + { + Expression *eo = (Expression *)o; if (eo->op == TOKdsymbol) - { DsymbolExp *se = (DsymbolExp *)eo; - if (Dsymbol_canThrow(se->s, mustNotThrow)) + { + DsymbolExp *se = (DsymbolExp *)eo; + if (Dsymbol_canThrow(se->s, func, mustNotThrow)) return true; } } diff --git a/gcc/d/dfrontend/cast.c b/gcc/d/dfrontend/cast.c index d8372aabd..127b2149e 100644 --- a/gcc/d/dfrontend/cast.c +++ b/gcc/d/dfrontend/cast.c @@ -1,11 +1,13 @@ -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/cast.c + */ #include #include @@ -21,6 +23,9 @@ #include "template.h" #include "scope.h" #include "id.h" +#include "init.h" + +bool isCommutative(Expression *e); /* ==================== implicitCast ====================== */ @@ -29,1021 +34,1301 @@ * Issue error if it can't be done. */ -Expression *Expression::implicitCastTo(Scope *sc, Type *t) -{ - //printf("Expression::implicitCastTo(%s of type %s) => %s\n", toChars(), type->toChars(), t->toChars()); - MATCH match = implicitConvTo(t); - if (match) +Expression *implicitCastTo(Expression *e, Scope *sc, Type *t) +{ + class ImplicitCastTo : public Visitor { - if (match == MATCHconst && type->constConv(t)) + public: + Type *t; + Scope *sc; + Expression *result; + + ImplicitCastTo(Scope *sc, Type *t) + : sc(sc), t(t) { - Expression *e = copy(); - e->type = t; - return e; + result = NULL; } - return castTo(sc, t); - } - Expression *e = optimize(WANTflags | WANTvalue); - if (e != this) - return e->implicitCastTo(sc, t); + void visit(Expression *e) + { + //printf("Expression::implicitCastTo(%s of type %s) => %s\n", e->toChars(), e->type->toChars(), t->toChars()); -#if 0 -printf("ty = %d\n", type->ty); -print(); -type->print(); -printf("to:\n"); -t->print(); -printf("%p %p type: %s to: %s\n", type->deco, t->deco, type->deco, t->deco); -//printf("%p %p %p\n", type->nextOf()->arrayOf(), type, t); -fflush(stdout); -#endif - if (t->ty != Terror && type->ty != Terror) - { - if (!t->deco) - { /* Can happen with: - * enum E { One } - * class A - * { static void fork(EDG dg) { dg(E.One); } - * alias void delegate(E) EDG; - * } - * Should eventually make it work. - */ - error("forward reference to type %s", t->toChars()); + MATCH match = e->implicitConvTo(t); + if (match) + { + if (match == MATCHconst && + (e->type->constConv(t) || + !e->isLvalue() && e->type->immutableOf()->equals(t->immutableOf()))) + { + /* Do not emit CastExp for const conversions and + * unique conversions on rvalue. + */ + result = e->copy(); + result->type = t; + return; + } + result = e->castTo(sc, t); + return; + } + + result = e->optimize(WANTflags | WANTvalue); + if (result != e) + { + result->accept(this); + return; + } + + if (t->ty != Terror && e->type->ty != Terror) + { + if (!t->deco) + { + /* Can happen with: + * enum E { One } + * class A + * { static void fork(EDG dg) { dg(E.One); } + * alias void delegate(E) EDG; + * } + * Should eventually make it work. + */ + e->error("forward reference to type %s", t->toChars()); + } + else if (Type *tx = reliesOnTident(t)) + e->error("forward reference to type %s", tx->toChars()); + + //printf("type %p ty %d deco %p\n", type, type->ty, type->deco); + //type = type->semantic(loc, sc); + //printf("type %s t %s\n", type->deco, t->deco); + e->error("cannot implicitly convert expression (%s) of type %s to %s", + e->toChars(), e->type->toChars(), t->toChars()); + } + result = new ErrorExp(); } - else if (t->reliesOnTident()) - error("forward reference to type %s", t->reliesOnTident()->toChars()); -//printf("type %p ty %d deco %p\n", type, type->ty, type->deco); -//type = type->semantic(loc, sc); -//printf("type %s t %s\n", type->deco, t->deco); - error("cannot implicitly convert expression (%s) of type %s to %s", - toChars(), type->toChars(), t->toChars()); - } - return new ErrorExp(); -} + void visit(StringExp *e) + { + //printf("StringExp::implicitCastTo(%s of type %s) => %s\n", e->toChars(), e->type->toChars(), t->toChars()); + visit((Expression *)e); + if (result->op == TOKstring) + { + // Retain polysemous nature if it started out that way + ((StringExp *)result)->committed = e->committed; + } + } -Expression *StringExp::implicitCastTo(Scope *sc, Type *t) -{ - //printf("StringExp::implicitCastTo(%s of type %s) => %s\n", toChars(), type->toChars(), t->toChars()); - unsigned char committed = this->committed; - Expression *e = Expression::implicitCastTo(sc, t); - if (e->op == TOKstring) - { - // Retain polysemous nature if it started out that way - ((StringExp *)e)->committed = committed; - } - return e; -} + void visit(ErrorExp *e) + { + result = e; + } -Expression *ErrorExp::implicitCastTo(Scope *sc, Type *t) -{ - return this; -} + void visit(FuncExp *e) + { + //printf("FuncExp::implicitCastTo type = %p %s, t = %s\n", e->type, e->type ? e->type->toChars() : NULL, t->toChars()); + FuncExp *fe; + if (e->matchType(t, sc, &fe) > MATCHnomatch) + { + result = fe; + return; + } + visit((Expression *)e); + } -Expression *FuncExp::implicitCastTo(Scope *sc, Type *t) -{ - //printf("FuncExp::implicitCastTo type = %p %s, t = %s\n", type, type ? type->toChars() : NULL, t->toChars()); - return inferType(t)->Expression::implicitCastTo(sc, t); -} + void visit(ArrayLiteralExp *e) + { + visit((Expression *)e); -Expression *ArrayLiteralExp::implicitCastTo(Scope *sc, Type *t) -{ - Expression *result = Expression::implicitCastTo(sc, t); + Type *tb = result->type->toBasetype(); + if (tb->ty == Tarray) + semanticTypeInfo(sc, ((TypeDArray *)tb)->next); + } + + void visit(SliceExp *e) + { + visit((Expression *)e); + if (result->op != TOKslice) + return; - Type *tb = result->type->toBasetype(); - if (tb->ty == Tarray) - semanticTypeInfo(sc, ((TypeDArray *)tb)->next); + e = (SliceExp *)result; + if (e->e1->op == TOKarrayliteral) + { + ArrayLiteralExp *ale = (ArrayLiteralExp *)e->e1; + Type *tb = t->toBasetype(); + Type *tx; + if (tb->ty == Tsarray) + tx = tb->nextOf()->sarrayOf(ale->elements ? ale->elements->dim : 0); + else + tx = tb->nextOf()->arrayOf(); + e->e1 = ale->implicitCastTo(sc, tx); + } + } + }; - return result; + ImplicitCastTo v(sc, t); + e->accept(&v); + return v.result; } /******************************************* - * Return !=0 if we can implicitly convert this to type t. - * Don't do the actual cast. + * Return MATCH level of implicitly converting e to type t. + * Don't do the actual cast; don't change e. */ -MATCH Expression::implicitConvTo(Type *t) +MATCH implicitConvTo(Expression *e, Type *t) { -#if 0 - printf("Expression::implicitConvTo(this=%s, type=%s, t=%s)\n", - toChars(), type->toChars(), t->toChars()); -#endif - //static int nest; if (++nest == 10) halt(); - if (t == Type::terror) - return MATCHnomatch; - if (!type) - { error("%s is not an expression", toChars()); - type = Type::terror; - } - Expression *e = optimize(WANTvalue | WANTflags); - if (e->type->equals(t)) - return MATCHexact; - if (e != this) - { //printf("\toptimized to %s of type %s\n", e->toChars(), e->type->toChars()); - return e->implicitConvTo(t); - } - MATCH match = type->implicitConvTo(t); - if (match != MATCHnomatch) - return match; - - /* See if we can do integral narrowing conversions - */ - if (type->isintegral() && t->isintegral() && - type->isTypeBasic() && t->isTypeBasic()) - { IntRange src = this->getIntRange(); - IntRange target = IntRange::fromType(t); - if (target.contains(src)) - return MATCHconvert; - } - -#if 0 - Type *tb = t->toBasetype(); - if (tb->ty == Tdelegate) - { TypeDelegate *td = (TypeDelegate *)tb; - TypeFunction *tf = (TypeFunction *)td->nextOf(); + class ImplicitConvTo : public Visitor + { + public: + Type *t; + MATCH result; - if (!tf->varargs && - !(tf->arguments && tf->arguments->dim) - ) + ImplicitConvTo(Type *t) + : t(t) { - match = type->implicitConvTo(tf->nextOf()); - if (match) - return match; - if (tf->nextOf()->toBasetype()->ty == Tvoid) - return MATCHconvert; + result = MATCHnomatch; } - } -#endif - return MATCHnomatch; -} - - -MATCH IntegerExp::implicitConvTo(Type *t) -{ -#if 0 - printf("IntegerExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - toChars(), type->toChars(), t->toChars()); -#endif - MATCH m = type->implicitConvTo(t); - if (m >= MATCHconst) - return m; - - TY ty = type->toBasetype()->ty; - TY toty = t->toBasetype()->ty; - TY oldty = ty; - - if (m == MATCHnomatch && t->ty == Tenum) - goto Lno; - - if (t->ty == Tvector) - { TypeVector *tv = (TypeVector *)t; - TypeBasic *tb = tv->elementType(); - if (tb->ty == Tvoid) - goto Lno; - toty = tb->ty; - } - - switch (ty) - { - case Tbool: - value &= 1; - ty = Tint32; - break; - - case Tint8: - value = (signed char)value; - ty = Tint32; - break; - - case Tchar: - case Tuns8: - value &= 0xFF; - ty = Tint32; - break; - - case Tint16: - value = (short)value; - ty = Tint32; - break; - - case Tuns16: - case Twchar: - value &= 0xFFFF; - ty = Tint32; - break; - case Tint32: - value = (int)value; - break; - - case Tuns32: - case Tdchar: - value &= 0xFFFFFFFF; - ty = Tuns32; - break; - - default: - break; - } + void visit(Expression *e) + { + #if 0 + printf("Expression::implicitConvTo(this=%s, type=%s, t=%s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + //static int nest; if (++nest == 10) halt(); + if (t == Type::terror) + return; + if (!e->type) + { + e->error("%s is not an expression", e->toChars()); + e->type = Type::terror; + } + Expression *ex = e->optimize(WANTvalue | WANTflags); + if (ex->type->equals(t)) + { + result = MATCHexact; + return; + } + if (ex != e) + { + //printf("\toptimized to %s of type %s\n", e->toChars(), e->type->toChars()); + result = ex->implicitConvTo(t); + return; + } + MATCH match = e->type->implicitConvTo(t); + if (match != MATCHnomatch) + { + result = match; + return; + } - // Only allow conversion if no change in value - switch (toty) - { - case Tbool: - if ((value & 1) != value) - goto Lno; - goto Lyes; + /* See if we can do integral narrowing conversions + */ + if (e->type->isintegral() && t->isintegral() && + e->type->isTypeBasic() && t->isTypeBasic()) + { + IntRange src = getIntRange(e); + IntRange target = IntRange::fromType(t); + if (target.contains(src)) + { + result = MATCHconvert; + return; + } + } + } - case Tint8: - if (ty == Tuns64 && value & ~0x7FUL) - goto Lno; - //else if (ty == Tint64 && 0x7FUL < value && value < ~0x7FUL) - // goto Lno; - else if ((signed char)value != value) - goto Lno; - goto Lyes; + /****** + * Given expression e of type t, see if we can implicitly convert e + * to type tprime, where tprime is type t with mod bits added. + * Returns: + * match level + */ + static MATCH implicitMod(Expression *e, Type *t, MOD mod) + { + Type *tprime; + if (t->ty == Tpointer) + tprime = t->nextOf()->castMod(mod)->pointerTo(); + else if (t->ty == Tarray) + tprime = t->nextOf()->castMod(mod)->arrayOf(); + else if (t->ty == Tsarray) + tprime = t->nextOf()->castMod(mod)->sarrayOf(t->size() / t->nextOf()->size()); + else + tprime = t->castMod(mod); - case Tchar: - if ((oldty == Twchar || oldty == Tdchar) && value > 0x7F) - goto Lno; - case Tuns8: - //printf("value = %llu %llu\n", (dinteger_t)(unsigned char)value, value); - if ((unsigned char)value != value) - goto Lno; - goto Lyes; + return e->implicitConvTo(tprime); + } - case Tint16: - if (ty == Tuns64 && value & ~0x7FFFUL) - goto Lno; - //else if (ty == Tint64 && 0x7FFFUL < value && value < ~0x7FFFUL) - // goto Lno; - else if ((short)value != value) - goto Lno; - goto Lyes; + static MATCH implicitConvToAddMin(BinExp *e, Type *t) + { + /* Is this (ptr +- offset)? If so, then ask ptr + * if the conversion can be done. + * This is to support doing things like implicitly converting a mutable unique + * pointer to an immutable pointer. + */ - case Twchar: - if (oldty == Tdchar && value > 0xD7FF && value < 0xE000) - goto Lno; - case Tuns16: - if ((unsigned short)value != value) - goto Lno; - goto Lyes; + Type *typeb = e->type->toBasetype(); + Type *tb = t->toBasetype(); + if (typeb->ty != Tpointer || tb->ty != Tpointer) + return MATCHnomatch; - case Tint32: - if (ty == Tuns32) + Type *t1b = e->e1->type->toBasetype(); + Type *t2b = e->e2->type->toBasetype(); + if (t1b->ty == Tpointer && t2b->isintegral() && + t1b->immutableOf()->equals(tb->immutableOf())) { + // ptr + offset + // ptr - offset + MATCH m = e->e1->implicitConvTo(t); + return (m > MATCHconst) ? MATCHconst : m; } - else if (ty == Tuns64 && value & ~0x7FFFFFFFUL) - goto Lno; - //else if (ty == Tint64 && 0x7FFFFFFFUL < value && value < ~0x7FFFFFFFUL) - // goto Lno; - else if ((int)value != value) - goto Lno; - goto Lyes; - - case Tuns32: - if (ty == Tint32) + if (t2b->ty == Tpointer && t1b->isintegral() && + t2b->immutableOf()->equals(tb->immutableOf())) { + // offset + ptr + MATCH m = e->e2->implicitConvTo(t); + return (m > MATCHconst) ? MATCHconst : m; } - else if ((unsigned)value != value) - goto Lno; - goto Lyes; - case Tdchar: - if (value > 0x10FFFFUL) - goto Lno; - goto Lyes; + return MATCHnomatch; + } - case Tfloat32: + void visit(AddExp *e) { - volatile float f; - if (type->isunsigned()) - { - f = (float)value; - if (f != value) - goto Lno; - } - else - { - f = (float)(sinteger_t)value; - if (f != (sinteger_t)value) - goto Lno; - } - goto Lyes; + #if 0 + printf("AddExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + visit((Expression *)e); + if (result == MATCHnomatch) + result = implicitConvToAddMin(e, t); } - case Tfloat64: + void visit(MinExp *e) { - volatile double f; - if (type->isunsigned()) - { - f = (double)value; - if (f != value) - goto Lno; - } - else - { - f = (double)(sinteger_t)value; - if (f != (sinteger_t)value) - goto Lno; - } - goto Lyes; + #if 0 + printf("MinExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + visit((Expression *)e); + if (result == MATCHnomatch) + result = implicitConvToAddMin(e, t); } - case Tfloat80: + void visit(IntegerExp *e) { - volatile_longdouble f; - if (type->isunsigned()) + #if 0 + printf("IntegerExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + MATCH m = e->type->implicitConvTo(t); + if (m >= MATCHconst) { - f = ldouble(value); - if (f != value) // isn't this a noop, because the compiler prefers ld - goto Lno; + result = m; + return; } - else + + TY ty = e->type->toBasetype()->ty; + TY toty = t->toBasetype()->ty; + TY oldty = ty; + + if (m == MATCHnomatch && t->ty == Tenum) + return; + + if (t->ty == Tvector) { - f = ldouble((sinteger_t)value); - if (f != (sinteger_t)value) - goto Lno; + TypeVector *tv = (TypeVector *)t; + TypeBasic *tb = tv->elementType(); + if (tb->ty == Tvoid) + return; + toty = tb->ty; } - goto Lyes; - } - case Tpointer: -//printf("type = %s\n", type->toBasetype()->toChars()); -//printf("t = %s\n", t->toBasetype()->toChars()); - if (ty == Tpointer && - type->toBasetype()->nextOf()->ty == t->toBasetype()->nextOf()->ty) - { /* Allow things like: - * const char* P = cast(char *)3; - * char* q = P; - */ - goto Lyes; + switch (ty) + { + case Tbool: + case Tint8: + case Tchar: + case Tuns8: + case Tint16: + case Tuns16: + case Twchar: + ty = Tint32; + break; + + case Tdchar: + ty = Tuns32; + break; + + default: + break; } - break; - } - return Expression::implicitConvTo(t); -Lyes: - //printf("MATCHconvert\n"); - return MATCHconvert; + // Only allow conversion if no change in value + dinteger_t value = e->toInteger(); + switch (toty) + { + case Tbool: + if ((value & 1) != value) + return; + break; -Lno: - //printf("MATCHnomatch\n"); - return MATCHnomatch; -} + case Tint8: + if (ty == Tuns64 && value & ~0x7FUL) + return; + else if ((signed char)value != value) + return; + break; -MATCH ErrorExp::implicitConvTo(Type *t) -{ - return MATCHnomatch; -} + case Tchar: + if ((oldty == Twchar || oldty == Tdchar) && value > 0x7F) + return; + case Tuns8: + //printf("value = %llu %llu\n", (dinteger_t)(unsigned char)value, value); + if ((unsigned char)value != value) + return; + break; -MATCH NullExp::implicitConvTo(Type *t) -{ -#if 0 - printf("NullExp::implicitConvTo(this=%s, type=%s, t=%s, committed = %d)\n", - toChars(), type->toChars(), t->toChars(), committed); -#endif - if (this->type->equals(t)) - return MATCHexact; + case Tint16: + if (ty == Tuns64 && value & ~0x7FFFUL) + return; + else if ((short)value != value) + return; + break; - /* Allow implicit conversions from immutable to mutable|const, - * and mutable to immutable. It works because, after all, a null - * doesn't actually point to anything. - */ - if (t->immutableOf()->equals(type->immutableOf())) - return MATCHconst; + case Twchar: + if (oldty == Tdchar && value > 0xD7FF && value < 0xE000) + return; + case Tuns16: + if ((unsigned short)value != value) + return; + break; - return Expression::implicitConvTo(t); -} + case Tint32: + if (ty == Tuns32) + { + } + else if (ty == Tuns64 && value & ~0x7FFFFFFFUL) + return; + else if ((int)value != value) + return; + break; -MATCH StructLiteralExp::implicitConvTo(Type *t) -{ -#if 0 - printf("StructLiteralExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - toChars(), type->toChars(), t->toChars()); -#endif - MATCH m = Expression::implicitConvTo(t); - if (m != MATCHnomatch) - return m; - if (type->ty == t->ty && type->ty == Tstruct && - ((TypeStruct *)type)->sym == ((TypeStruct *)t)->sym) - { - m = MATCHconst; - for (size_t i = 0; i < elements->dim; i++) - { - Expression *e = (*elements)[i]; - if (!e) - continue; - Type *te = e->type; - te = sd->fields[i]->type->addMod(t->mod); - MATCH m2 = e->implicitConvTo(te); - //printf("\t%s => %s, match = %d\n", e->toChars(), te->toChars(), m2); - if (m2 < m) - m = m2; - } - } - return m; -} + case Tuns32: + if (ty == Tint32) + { + } + else if ((unsigned)value != value) + return; + break; -MATCH StringExp::implicitConvTo(Type *t) -{ -#if 0 - printf("StringExp::implicitConvTo(this=%s, committed=%d, type=%s, t=%s)\n", - toChars(), committed, type->toChars(), t->toChars()); -#endif - if (!committed && t->ty == Tpointer && t->nextOf()->ty == Tvoid) - { - return MATCHnomatch; - } - if (type->ty == Tsarray || type->ty == Tarray || type->ty == Tpointer) - { - TY tyn = type->nextOf()->ty; - if (tyn == Tchar || tyn == Twchar || tyn == Tdchar) - { Type *tn; - MATCH m; + case Tdchar: + if (value > 0x10FFFFUL) + return; + break; - switch (t->ty) - { - case Tsarray: - if (type->ty == Tsarray) + case Tfloat32: + { + volatile float f; + if (e->type->isunsigned()) { - if (((TypeSArray *)type)->dim->toInteger() != - ((TypeSArray *)t)->dim->toInteger()) - return MATCHnomatch; - TY tynto = t->nextOf()->ty; - if (tynto == tyn) - return MATCHexact; - if (!committed && (tynto == Tchar || tynto == Twchar || tynto == Tdchar)) - return MATCHexact; + f = (float)value; + if (f != value) + return; } - else if (type->ty == Tarray) + else { - if (length() > - ((TypeSArray *)t)->dim->toInteger()) - return MATCHnomatch; - TY tynto = t->nextOf()->ty; - if (tynto == tyn) - return MATCHexact; - if (!committed && (tynto == Tchar || tynto == Twchar || tynto == Tdchar)) - return MATCHexact; + f = (float)(sinteger_t)value; + if (f != (sinteger_t)value) + return; } - case Tarray: - case Tpointer: - tn = t->nextOf(); - m = MATCHexact; - if (type->nextOf()->mod != tn->mod) - { if (!tn->isConst()) - return MATCHnomatch; - m = MATCHconst; + break; + } + + case Tfloat64: + { + volatile double f; + if (e->type->isunsigned()) + { + f = (double)value; + if (f != value) + return; } - if (!committed) + else { - switch (tn->ty) - { - case Tchar: - return (postfix != 'w' && postfix != 'd' ? m : MATCHconvert); - case Twchar: - return (postfix == 'w' ? m : MATCHconvert); - case Tdchar: - return (postfix == 'd' ? m : MATCHconvert); - } + f = (double)(sinteger_t)value; + if (f != (sinteger_t)value) + return; } break; - } - } - } - return Expression::implicitConvTo(t); -#if 0 - m = (MATCH)type->implicitConvTo(t); - if (m) - { - return m; - } + } - return MATCHnomatch; -#endif -} + case Tfloat80: + { + volatile_longdouble f; + if (e->type->isunsigned()) + { + f = ldouble(value); + if (f != value) // isn't this a noop, because the compiler prefers ld + return; + } + else + { + f = ldouble((sinteger_t)value); + if (f != (sinteger_t)value) + return; + } + break; + } -MATCH ArrayLiteralExp::implicitConvTo(Type *t) -{ MATCH result = MATCHexact; + case Tpointer: + //printf("type = %s\n", type->toBasetype()->toChars()); + //printf("t = %s\n", t->toBasetype()->toChars()); + if (ty == Tpointer && + e->type->toBasetype()->nextOf()->ty == t->toBasetype()->nextOf()->ty) + { + /* Allow things like: + * const char* P = cast(char *)3; + * char* q = P; + */ + break; + } -#if 0 - printf("ArrayLiteralExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - toChars(), type->toChars(), t->toChars()); -#endif - Type *typeb = type->toBasetype(); - Type *tb = t->toBasetype(); - if ((tb->ty == Tarray || tb->ty == Tsarray) && - (typeb->ty == Tarray || typeb->ty == Tsarray)) - { - Type *typen = typeb->nextOf()->toBasetype(); + default: + visit((Expression *)e); + return; + } - if (tb->ty == Tsarray) - { TypeSArray *tsa = (TypeSArray *)tb; - if (elements->dim != tsa->dim->toInteger()) - result = MATCHnomatch; + //printf("MATCHconvert\n"); + result = MATCHconvert; } - Type *telement = tb->nextOf(); - if (!elements->dim) - { if (typen->ty != Tvoid) - result = typen->implicitConvTo(telement); - } - else - { for (size_t i = 0; i < elements->dim; i++) - { Expression *e = (*elements)[i]; - if (result == MATCHnomatch) - break; // no need to check for worse - MATCH m = (MATCH)e->implicitConvTo(telement); - if (m < result) - result = m; // remember worst match - } + void visit(ErrorExp *e) + { + // no match } - if (!result) - result = type->implicitConvTo(t); + void visit(NullExp *e) + { + #if 0 + printf("NullExp::implicitConvTo(this=%s, type=%s, t=%s, committed = %d)\n", + e->toChars(), e->type->toChars(), t->toChars(), e->committed); + #endif + if (e->type->equals(t)) + { + result = MATCHexact; + return; + } - return result; - } - else if (tb->ty == Tvector && - (typeb->ty == Tarray || typeb->ty == Tsarray)) - { - // Convert array literal to vector type - TypeVector *tv = (TypeVector *)tb; - TypeSArray *tbase = (TypeSArray *)tv->basetype; - assert(tbase->ty == Tsarray); - if (elements->dim != tbase->dim->toInteger()) - return MATCHnomatch; + /* Allow implicit conversions from immutable to mutable|const, + * and mutable to immutable. It works because, after all, a null + * doesn't actually point to anything. + */ + if (t->immutableOf()->equals(e->type->immutableOf())) + { + result = MATCHconst; + return; + } - Type *telement = tv->elementType(); - for (size_t i = 0; i < elements->dim; i++) - { Expression *e = (*elements)[i]; - MATCH m = (MATCH)e->implicitConvTo(telement); - if (m < result) - result = m; // remember worst match - if (result == MATCHnomatch) - break; // no need to check for worse + visit((Expression *)e); } - return result; - } - else - return Expression::implicitConvTo(t); -} -MATCH AssocArrayLiteralExp::implicitConvTo(Type *t) -{ - Type *typeb = type->toBasetype(); - Type *tb = t->toBasetype(); - if (tb->ty == Taarray && typeb->ty == Taarray) - { - MATCH result = MATCHexact; - for (size_t i = 0; i < keys->dim; i++) - { Expression *e = (*keys)[i]; - MATCH m = (MATCH)e->implicitConvTo(((TypeAArray *)tb)->index); - if (m < result) - result = m; // remember worst match - if (result == MATCHnomatch) - break; // no need to check for worse - e = (*values)[i]; - m = (MATCH)e->implicitConvTo(tb->nextOf()); - if (m < result) - result = m; // remember worst match - if (result == MATCHnomatch) - break; // no need to check for worse + void visit(StructLiteralExp *e) + { + #if 0 + printf("StructLiteralExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + visit((Expression *)e); + if (result != MATCHnomatch) + return; + if (e->type->ty == t->ty && e->type->ty == Tstruct && + ((TypeStruct *)e->type)->sym == ((TypeStruct *)t)->sym) + { + result = MATCHconst; + for (size_t i = 0; i < e->elements->dim; i++) + { + Expression *el = (*e->elements)[i]; + if (!el) + continue; + Type *te = el->type; + te = e->sd->fields[i]->type->addMod(t->mod); + MATCH m2 = el->implicitConvTo(te); + //printf("\t%s => %s, match = %d\n", el->toChars(), te->toChars(), m2); + if (m2 < result) + result = m2; + } + } } - return result; - } - else - return Expression::implicitConvTo(t); -} -Expression *CallExp::implicitCastTo(Scope *sc, Type *t) -{ - //printf("CallExp::implicitCastTo(%s of type %s) => %s\n", toChars(), type->toChars(), t->toChars()); + void visit(StringExp *e) + { + #if 0 + printf("StringExp::implicitConvTo(this=%s, committed=%d, type=%s, t=%s)\n", + e->toChars(), e->committed, e->type->toChars(), t->toChars()); + #endif + if (!e->committed && t->ty == Tpointer && t->nextOf()->ty == Tvoid) + return; + + if (e->type->ty == Tsarray || e->type->ty == Tarray || e->type->ty == Tpointer) + { + TY tyn = e->type->nextOf()->ty; + if (tyn == Tchar || tyn == Twchar || tyn == Tdchar) + { + switch (t->ty) + { + case Tsarray: + if (e->type->ty == Tsarray) + { + if (((TypeSArray *)e->type)->dim->toInteger() != + ((TypeSArray *)t)->dim->toInteger()) + return; + TY tynto = t->nextOf()->ty; + if (tynto == tyn) + { + result = MATCHexact; + return; + } + if (!e->committed && (tynto == Tchar || tynto == Twchar || tynto == Tdchar)) + { + result = MATCHexact; + return; + } + } + else if (e->type->ty == Tarray) + { + if (e->length() > + ((TypeSArray *)t)->dim->toInteger()) + return; + TY tynto = t->nextOf()->ty; + if (tynto == tyn) + { + result = MATCHexact; + return; + } + if (!e->committed && (tynto == Tchar || tynto == Twchar || tynto == Tdchar)) + { + result = MATCHexact; + return; + } + } + case Tarray: + case Tpointer: + Type *tn = t->nextOf(); + MATCH m = MATCHexact; + if (e->type->nextOf()->mod != tn->mod) + { + if (!tn->isConst()) + return; + m = MATCHconst; + } + if (!e->committed) + { + switch (tn->ty) + { + case Tchar: + if (e->postfix == 'w' || e->postfix == 'd') + m = MATCHconvert; + result = m; + return; + case Twchar: + if (e->postfix != 'w') + m = MATCHconvert; + result = m; + return; + case Tdchar: + if (e->postfix != 'd') + m = MATCHconvert; + result = m; + return; + } + } + break; + } + } + } - /* Allow the result of strongly pure functions to - * convert to immutable - */ - if (f && f->isolateReturn() && - type->immutableOf()->equals(t->immutableOf())) - { - /* Avoid emitting CastExp for: - * T[] make() pure { ... } - * immutable T[] arr = make(); // unique return - */ - Expression *e = copy(); - e->type = t; - return e; - } - return Expression::implicitCastTo(sc, t); -} + visit((Expression *)e); + } -MATCH CallExp::implicitConvTo(Type *t) -{ -#if 0 - printf("CalLExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - toChars(), type->toChars(), t->toChars()); -#endif + void visit(ArrayLiteralExp *e) + { + #if 0 + printf("ArrayLiteralExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + Type *typeb = e->type->toBasetype(); + Type *tb = t->toBasetype(); + if ((tb->ty == Tarray || tb->ty == Tsarray) && + (typeb->ty == Tarray || typeb->ty == Tsarray)) + { + result = MATCHexact; + Type *typen = typeb->nextOf()->toBasetype(); - MATCH m = Expression::implicitConvTo(t); - if (m) - return m; + if (tb->ty == Tsarray) + { + TypeSArray *tsa = (TypeSArray *)tb; + if (e->elements->dim != tsa->dim->toInteger()) + result = MATCHnomatch; + } - /* Allow the result of strongly pure functions to - * convert to immutable - */ - if (f && f->isolateReturn()) - return type->immutableOf()->implicitConvTo(t); + Type *telement = tb->nextOf(); + if (!e->elements->dim) + { + if (typen->ty != Tvoid) + result = typen->implicitConvTo(telement); + } + else + { + for (size_t i = 0; i < e->elements->dim; i++) + { + Expression *el = (*e->elements)[i]; + if (result == MATCHnomatch) + break; // no need to check for worse + MATCH m = el->implicitConvTo(telement); + if (m < result) + result = m; // remember worst match + } + } - /* The result of arr.dup and arr.idup can be unique essentially. - * So deal with this case specially. - */ - if (!f && e1->op == TOKvar && ((VarExp *)e1)->var->ident == Id::adDup && - t->toBasetype()->ty == Tarray) - { - assert(type->toBasetype()->ty == Tarray); - assert(arguments->dim == 2); - Expression *eorg = (*arguments)[1]; - Type *tn = t->nextOf(); - if (type->nextOf()->implicitConvTo(tn) < MATCHconst) + if (!result) + result = e->type->implicitConvTo(t); + + return; + } + else if (tb->ty == Tvector && + (typeb->ty == Tarray || typeb->ty == Tsarray)) + { + result = MATCHexact; + // Convert array literal to vector type + TypeVector *tv = (TypeVector *)tb; + TypeSArray *tbase = (TypeSArray *)tv->basetype; + assert(tbase->ty == Tsarray); + if (e->elements->dim != tbase->dim->toInteger()) + { + result = MATCHnomatch; + return; + } + + Type *telement = tv->elementType(); + for (size_t i = 0; i < e->elements->dim; i++) + { + Expression *el = (*e->elements)[i]; + MATCH m = el->implicitConvTo(telement); + if (m < result) + result = m; // remember worst match + if (result == MATCHnomatch) + break; // no need to check for worse + } + return; + } + + visit((Expression *)e); + } + + void visit(AssocArrayLiteralExp *e) { - /* If the operand is an unique array literal, then allow conversion. - */ - if (eorg->op != TOKarrayliteral) - return MATCHnomatch; - Expressions *elements = ((ArrayLiteralExp *)eorg)->elements; - for (size_t i = 0; i < elements->dim; i++) + Type *typeb = e->type->toBasetype(); + Type *tb = t->toBasetype(); + if (tb->ty == Taarray && typeb->ty == Taarray) { - if (!(*elements)[i]->implicitConvTo(tn)) - return MATCHnomatch; + result = MATCHexact; + for (size_t i = 0; i < e->keys->dim; i++) + { + Expression *el = (*e->keys)[i]; + MATCH m = el->implicitConvTo(((TypeAArray *)tb)->index); + if (m < result) + result = m; // remember worst match + if (result == MATCHnomatch) + break; // no need to check for worse + el = (*e->values)[i]; + m = el->implicitConvTo(tb->nextOf()); + if (m < result) + result = m; // remember worst match + if (result == MATCHnomatch) + break; // no need to check for worse + } + return; } + else + visit((Expression *)e); } - m = type->immutableOf()->implicitConvTo(t); - return m; - } - return MATCHnomatch; -} + void visit(CallExp *e) + { +#define LOG 0 + #if LOG + printf("CallExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + + visit((Expression *)e); + if (result != MATCHnomatch) + return; + + /* Allow the result of strongly pure functions to + * convert to immutable + */ + if (e->f && e->f->isolateReturn()) + { + result = e->type->immutableOf()->implicitConvTo(t); + if (result > MATCHconst) // Match level is MATCHconst at best. + result = MATCHconst; + return; + } -MATCH AddrExp::implicitConvTo(Type *t) -{ -#if 0 - printf("AddrExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - toChars(), type->toChars(), t->toChars()); + /* Conversion is 'const' conversion if: + * 1. function is pure (weakly pure is ok) + * 2. implicit conversion only fails because of mod bits + * 3. each function parameter can be implicitly converted to the mod bits + */ + Type *tx = e->f ? e->f->type : e->e1->type; + tx = tx->toBasetype(); + if (tx->ty != Tfunction) + return; + TypeFunction *tf = (TypeFunction *)tx; + + if (tf->purity == PUREimpure) + return; + + /* See if fail only because of mod bits + */ + if (e->type->immutableOf()->implicitConvTo(t->immutableOf()) == MATCHnomatch) + return; + + /* Get mod bits of what we're converting to + */ + Type *tb = t->toBasetype(); + MOD mod = tb->mod; + if (tf->isref) + ; + else + { + Type *ti = getIndirection(t); + if (ti) + mod = ti->mod; + } +#if LOG + printf("mod = x%x\n", mod); #endif - MATCH result; + if (mod & MODwild) + return; // not sure what to do with this - result = type->implicitConvTo(t); - //printf("\tresult = %d\n", result); + /* Apply mod bits to each function parameter, + * and see if we can convert the function argument to the modded type + */ - if (result == MATCHnomatch) - { - // Look for pointers to functions where the functions are overloaded. - - t = t->toBasetype(); - - if (e1->op == TOKoverloadset && - (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction) - { OverExp *eo = (OverExp *)e1; - FuncDeclaration *f = NULL; - for (size_t i = 0; i < eo->vars->a.dim; i++) - { Dsymbol *s = eo->vars->a[i]; - FuncDeclaration *f2 = s->isFuncDeclaration(); - assert(f2); - if (f2->overloadExactMatch(t->nextOf())) - { if (f) - /* Error if match in more than one overload set, - * even if one is a 'better' match than the other. + size_t nparams = Parameter::dim(tf->parameters); + size_t j = (tf->linkage == LINKd && tf->varargs == 1); // if TypeInfoArray was prepended + for (size_t i = j; i <= e->arguments->dim; ++i) + { + if (i == e->arguments->dim) + { + if (e->e1->op == TOKdotvar) + { + /* Treat 'this' as just another function argument */ - ScopeDsymbol::multiplyDefined(loc, f, f2); - else - f = f2; - result = MATCHexact; + DotVarExp *dve = (DotVarExp *)e->e1; + Type *targ = dve->e1->type; + if (targ->toBasetype()->ty == Tstruct) + targ = targ->pointerTo(); + if (targ->implicitConvTo(targ->addMod(mod)) == MATCHnomatch) + return; + } + continue; + } + Expression *earg = (*e->arguments)[i]; + Type *targ = earg->type->toBasetype(); +#if LOG + printf("[%d] earg: %s, targ: %s\n", (int)i, earg->toChars(), targ->toChars()); +#endif + if (i - j < nparams) + { + Parameter *fparam = Parameter::getNth(tf->parameters, i - j); + if (fparam->storageClass & STClazy) + return; // not sure what to do with this + Type *tparam = fparam->type; + if (!tparam) + continue; + if (fparam->storageClass & (STCout | STCref)) + { + tparam = tparam->pointerTo(); + targ = targ->pointerTo(); + if (targ->implicitConvTo(tparam->addMod(mod)) == MATCHnomatch) + return; + continue; + } } + +#if LOG + printf("[%d] earg: %s, targm: %s\n", (int)i, earg->toChars(), targ->addMod(mod)->toChars()); +#endif + if (implicitMod(earg, targ, mod) == MATCHnomatch) + return; } - } - if (type->ty == Tpointer && type->nextOf()->ty == Tfunction && - t->ty == Tpointer && t->nextOf()->ty == Tfunction && - e1->op == TOKvar) - { - /* I don't think this can ever happen - - * it should have been - * converted to a SymOffExp. + /* Success */ - assert(0); - VarExp *ve = (VarExp *)e1; - FuncDeclaration *f = ve->var->isFuncDeclaration(); - if (f && f->overloadExactMatch(t->nextOf())) - result = MATCHexact; + result = MATCHconst; +#undef LOG } - } - //printf("\tresult = %d\n", result); - return result; -} -MATCH SymOffExp::implicitConvTo(Type *t) -{ -#if 0 - printf("SymOffExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - toChars(), type->toChars(), t->toChars()); -#endif - MATCH result; + void visit(AddrExp *e) + { + #if 0 + printf("AddrExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + result = e->type->implicitConvTo(t); + //printf("\tresult = %d\n", result); - result = type->implicitConvTo(t); - //printf("\tresult = %d\n", result); + if (result != MATCHnomatch) + return; - if (result == MATCHnomatch) - { - // Look for pointers to functions where the functions are overloaded. - FuncDeclaration *f; + // Look for pointers to functions where the functions are overloaded. - t = t->toBasetype(); - if (type->ty == Tpointer && type->nextOf()->ty == Tfunction && - (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction) - { - f = var->isFuncDeclaration(); - if (f) - { f = f->overloadExactMatch(t->nextOf()); - if (f) - { if ((t->ty == Tdelegate && (f->needThis() || f->isNested())) || - (t->ty == Tpointer && !(f->needThis() || f->isNested()))) + t = t->toBasetype(); + + if (e->e1->op == TOKoverloadset && + (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction) + { + OverExp *eo = (OverExp *)e->e1; + FuncDeclaration *f = NULL; + for (size_t i = 0; i < eo->vars->a.dim; i++) + { + Dsymbol *s = eo->vars->a[i]; + FuncDeclaration *f2 = s->isFuncDeclaration(); + assert(f2); + if (f2->overloadExactMatch(t->nextOf())) { + if (f) + { + /* Error if match in more than one overload set, + * even if one is a 'better' match than the other. + */ + ScopeDsymbol::multiplyDefined(e->loc, f, f2); + } + else + f = f2; result = MATCHexact; } } } - } - } - //printf("\tresult = %d\n", result); - return result; -} -MATCH DelegateExp::implicitConvTo(Type *t) -{ -#if 0 - printf("DelegateExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - toChars(), type->toChars(), t->toChars()); -#endif - MATCH result; - - result = type->implicitConvTo(t); + if (e->type->ty == Tpointer && e->type->nextOf()->ty == Tfunction && + t->ty == Tpointer && t->nextOf()->ty == Tfunction && + e->e1->op == TOKvar) + { + /* I don't think this can ever happen - + * it should have been + * converted to a SymOffExp. + */ + assert(0); + } - if (result == MATCHnomatch) - { - // Look for pointers to functions where the functions are overloaded. + //printf("\tresult = %d\n", result); + } - t = t->toBasetype(); - if (type->ty == Tdelegate && - t->ty == Tdelegate) + void visit(SymOffExp *e) { - if (func && func->overloadExactMatch(t->nextOf())) - result = MATCHexact; + #if 0 + printf("SymOffExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + result = e->type->implicitConvTo(t); + //printf("\tresult = %d\n", result); + if (result != MATCHnomatch) + return; + + // Look for pointers to functions where the functions are overloaded. + t = t->toBasetype(); + if (e->type->ty == Tpointer && e->type->nextOf()->ty == Tfunction && + (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction) + { + if (FuncDeclaration *f = e->var->isFuncDeclaration()) + { + f = f->overloadExactMatch(t->nextOf()); + if (f) + { + if ((t->ty == Tdelegate && (f->needThis() || f->isNested())) || + (t->ty == Tpointer && !(f->needThis() || f->isNested()))) + { + result = MATCHexact; + } + } + } + } + //printf("\tresult = %d\n", result); } - } - return result; -} -MATCH FuncExp::implicitConvTo(Type *t) -{ - //printf("FuncExp::implicitConvTo type = %p %s, t = %s\n", type, type ? type->toChars() : NULL, t->toChars()); - Expression *e = inferType(t, 1); - if (e && - (t->ty == Tdelegate || - t->ty == Tpointer && t->nextOf()->ty == Tfunction)) - { - if (e != this) - return e->implicitConvTo(t); + void visit(DelegateExp *e) + { + #if 0 + printf("DelegateExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + result = e->type->implicitConvTo(t); + if (result != MATCHnomatch) + return; + + // Look for pointers to functions where the functions are overloaded. + t = t->toBasetype(); + if (e->type->ty == Tdelegate && + t->ty == Tdelegate) + { + if (e->func && e->func->overloadExactMatch(t->nextOf())) + result = MATCHexact; + } + } - /* MATCHconst: Conversion from implicit to explicit function pointer - * MATCHconvert: Conversion from impliict funciton pointer to delegate - */ - if (fd->tok == TOKreserved && // fbody doesn't have a frame pointer - (type->equals(t) || type->nextOf()->covariant(t->nextOf()) == 1)) + void visit(FuncExp *e) { - return t->ty == Tpointer ? MATCHconst : MATCHconvert; + //printf("FuncExp::implicitConvTo type = %p %s, t = %s\n", e->type, e->type ? e->type->toChars() : NULL, t->toChars()); + MATCH m = e->matchType(t, NULL, NULL, 1); + if (m > MATCHnomatch) + { + result = m; + return; + } + visit((Expression *)e); } - } - return Expression::implicitConvTo(t); -} -MATCH OrExp::implicitConvTo(Type *t) -{ - MATCH result = Expression::implicitConvTo(t); + void visit(OrExp *e) + { + visit((Expression *)e); + if (result != MATCHnomatch) + return; - if (result == MATCHnomatch) - { - MATCH m1 = e1->implicitConvTo(t); - MATCH m2 = e2->implicitConvTo(t); + MATCH m1 = e->e1->implicitConvTo(t); + MATCH m2 = e->e2->implicitConvTo(t); - // Pick the worst match - result = (m1 < m2) ? m1 : m2; - } - return result; -} + // Pick the worst match + result = (m1 < m2) ? m1 : m2; + } -MATCH XorExp::implicitConvTo(Type *t) -{ - MATCH result = Expression::implicitConvTo(t); + void visit(XorExp *e) + { + visit((Expression *)e); + if (result != MATCHnomatch) + return; - if (result == MATCHnomatch) - { - MATCH m1 = e1->implicitConvTo(t); - MATCH m2 = e2->implicitConvTo(t); + MATCH m1 = e->e1->implicitConvTo(t); + MATCH m2 = e->e2->implicitConvTo(t); - // Pick the worst match - result = (m1 < m2) ? m1 : m2; - } - return result; -} + // Pick the worst match + result = (m1 < m2) ? m1 : m2; + } -MATCH CondExp::implicitConvTo(Type *t) -{ - MATCH m1 = e1->implicitConvTo(t); - MATCH m2 = e2->implicitConvTo(t); - //printf("CondExp: m1 %d m2 %d\n", m1, m2); + void visit(CondExp *e) + { + MATCH m1 = e->e1->implicitConvTo(t); + MATCH m2 = e->e2->implicitConvTo(t); + //printf("CondExp: m1 %d m2 %d\n", m1, m2); - // Pick the worst match - return (m1 < m2) ? m1 : m2; -} + // Pick the worst match + result = (m1 < m2) ? m1 : m2; + } -MATCH CommaExp::implicitConvTo(Type *t) -{ - return e2->implicitConvTo(t); -} + void visit(CommaExp *e) + { + e->e2->accept(this); + } -MATCH CastExp::implicitConvTo(Type *t) -{ -#if 0 - printf("CastExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - toChars(), type->toChars(), t->toChars()); -#endif - MATCH result; + void visit(CastExp *e) + { + #if 0 + printf("CastExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + result = e->type->implicitConvTo(t); + if (result != MATCHnomatch) + return; + + if (t->isintegral() && + e->e1->type->isintegral() && + e->e1->implicitConvTo(t) != MATCHnomatch) + result = MATCHconvert; + else + visit((Expression *)e); + } - result = type->implicitConvTo(t); + void visit(NewExp *e) + { + #if 0 + printf("NewExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + visit((Expression *)e); + if (result != MATCHnomatch) + return; + + /* Calling new() is like calling a pure function. We can implicitly convert the + * return from new() to t using the same algorithm as in CallExp, with the function + * 'arguments' being: + * thisexp + * newargs + * arguments + * .init + * 'member' and 'allocator' need to be pure. + */ - if (result == MATCHnomatch) - { - if (t->isintegral() && - e1->type->isintegral() && - e1->implicitConvTo(t) != MATCHnomatch) - result = MATCHconvert; - else - result = Expression::implicitConvTo(t); - } - return result; -} + /* See if fail only because of mod bits + */ + if (e->type->immutableOf()->implicitConvTo(t->immutableOf()) == MATCHnomatch) + return; -MATCH NewExp::implicitConvTo(Type *t) -{ -#if 0 - printf("NewExp::implicitConvTo(this=%s, type=%s, t=%s)\n", - toChars(), type->toChars(), t->toChars()); + /* Get mod bits of what we're converting to + */ + Type *tb = t->toBasetype(); + MOD mod = tb->mod; + if (Type *ti = getIndirection(t)) + mod = ti->mod; +#if LOG + printf("mod = x%x\n", mod); #endif - MATCH match = Expression::implicitConvTo(t); - if (match != MATCHnomatch) - return match; - - /* The return from new() is special in that it might be a unique pointer. - * If we can prove it is, allow the following implicit conversions: - * mutable => immutable - * non-shared => shared - * shared => non-shared - */ + if (mod & MODwild) + return; // not sure what to do with this - Type *typeb = type->toBasetype(); - Type *tb = t->toBasetype(); - - if (tb->ty == Tclass) - { - //printf("%s => %s\n", type->castMod(0)->toChars(), t->castMod(0)->toChars()); - match = type->castMod(0)->implicitConvTo(t->castMod(0)); - if (!match) - goto Lnomatch; + /* Apply mod bits to each argument, + * and see if we can convert the argument to the modded type + */ - // Regardless, don't allow immutable to be implicitly converted to mutable - if (tb->isMutable() && !typeb->isMutable()) - goto Lnomatch; + if (e->thisexp) + { + /* Treat 'this' as just another function argument + */ + Type *targ = e->thisexp->type; + if (targ->toBasetype()->ty == Tstruct) + targ = targ->pointerTo(); + if (targ->implicitConvTo(targ->addMod(mod)) == MATCHnomatch) + return; + } - // All the fields must be convertible as well - ClassDeclaration *cd = ((TypeClass *)tb)->sym; + /* Check call to 'allocator', then 'member' + */ + FuncDeclaration *fd = e->allocator; + for (int count = 0; count < 2; ++count, (fd = e->member)) + { + if (!fd) + continue; + TypeFunction *tf = (TypeFunction *)fd->type->toBasetype(); + if (tf->ty != Tfunction || ((TypeFunction *)tf)->purity == PUREimpure) + return; // error or impure - cd->size(loc); // resolve any forward references + Expressions *args = (fd == e->allocator) ? e->newargs : e->arguments; - /* The following is excessively conservative, but be very - * careful in loosening them up. - */ - if (cd->isNested() || - cd->isInterfaceDeclaration() || - cd->ctor || - cd->baseClass != ClassDeclaration::object) - goto Lnomatch; + size_t nparams = Parameter::dim(tf->parameters); + size_t j = (tf->linkage == LINKd && tf->varargs == 1); // if TypeInfoArray was prepended + for (size_t i = j; i < e->arguments->dim; ++i) + { + Expression *earg = (*args)[i]; + Type *targ = earg->type->toBasetype(); +#if LOG + printf("[%d] earg: %s, targ: %s\n", (int)i, earg->toChars(), targ->toChars()); +#endif + if (i - j < nparams) + { + Parameter *fparam = Parameter::getNth(tf->parameters, i - j); + if (fparam->storageClass & STClazy) + return; // not sure what to do with this + Type *tparam = fparam->type; + if (!tparam) + continue; + if (fparam->storageClass & (STCout | STCref)) + { + tparam = tparam->pointerTo(); + targ = targ->pointerTo(); + if (targ->implicitConvTo(tparam->addMod(mod)) == MATCHnomatch) + return; + continue; + } + } - for (size_t i = 0; i < cd->fields.dim; i++) - { - Declaration *d = cd->fields[i]; - if (d->storage_class & STCref || d->hasPointers()) - goto Lnomatch; - } - return (match == MATCHexact) ? MATCHconst : match; - } - else if ((tb->ty == Tpointer || tb->ty == Tarray) && - (typeb->ty == Tpointer || typeb->ty == Tarray)) - { - Type *typen = type->nextOf()->toBasetype(); - Type *tn = tb->nextOf()->toBasetype(); +#if LOG + printf("[%d] earg: %s, targm: %s\n", (int)i, earg->toChars(), targ->addMod(mod)->toChars()); +#endif + if (implicitMod(earg, targ, mod) == MATCHnomatch) + return; + } + } - //printf("%s => %s\n", typen->castMod(0)->toChars(), tn->castMod(0)->toChars()); - { - /* Determine if the match failure was solely due to a difference - * in the mod bits, by rebuilding type and t without mod bits and - * retrying the implicit conversion. + /* If no 'member', then construction is by simple assignment, + * and just straight check 'arguments' */ - Type *tn2 = tn->castMod(0); // cast off mod bits - Type *typen2 = typen->castMod(0); - Type *t2 = (tb->ty == Tpointer) ? tn2->pointerTo() : tn2->arrayOf(); - Type *type2 = (typeb->ty == Tpointer) ? typen2->pointerTo() : typen2->arrayOf(); - match = type2->implicitConvTo(t2); - if (!match) - goto Lnomatch; - } + if (!e->member && e->arguments) + { + for (size_t i = 0; i < e->arguments->dim; ++i) + { + Expression *earg = (*e->arguments)[i]; + Type *targ = earg->type->toBasetype(); +#if LOG + printf("[%d] earg: %s, targ: %s\n", (int)i, earg->toChars(), targ->toChars()); + printf("[%d] earg: %s, targm: %s\n", (int)i, earg->toChars(), targ->addMod(mod)->toChars()); +#endif + if (implicitMod(earg, targ, mod) == MATCHnomatch) + return; + } + } - // Regardless, don't allow immutable to be implicitly converted to mutable - if (tn->isMutable() && !typen->isMutable()) - goto Lnomatch; + /* Consider the .init expression as an argument + */ + Type *ntb = e->newtype->toBasetype(); + if (ntb->ty == Tarray) + ntb = ntb->nextOf()->toBasetype(); + if (ntb->ty == Tstruct) + { + // Don't allow nested structs - uplevel reference may not be convertible + StructDeclaration *sd = ((TypeStruct *)ntb)->sym; + sd->size(e->loc); // resolve any forward references + if (sd->isNested()) + return; + } + if (ntb->isZeroInit(e->loc)) + { + /* Zeros are implicitly convertible, except for special cases. + */ + if (ntb->ty == Tclass) + { + /* With new() must look at the class instance initializer. + */ + ClassDeclaration *cd = ((TypeClass *)ntb)->sym; - if (tn->isTypeBasic()) - ; - else if (tn->ty == Tstruct) - { - // All the fields must be convertible as well - StructDeclaration *sd = ((TypeStruct *)tn)->sym; + cd->size(e->loc); // resolve any forward references - sd->size(loc); // resolve any forward references + if (cd->isNested()) + return; // uplevel reference may not be convertible - /* The following is excessively conservative, but be very - * careful in loosening them up. - */ + assert(!cd->isInterfaceDeclaration()); - if (sd->isNested() || - sd->ctor) - goto Lnomatch; + struct ClassCheck + { + static bool convertible(Loc loc, ClassDeclaration *cd, MOD mod) + { + for (size_t i = 0; i < cd->fields.dim; i++) + { + VarDeclaration *v = cd->fields[i]; + Initializer *init = v->init; + if (init) + { + if (init->isVoidInitializer()) + ; + else if (ExpInitializer *ei = init->isExpInitializer()) + { + Type *tb = v->type->toBasetype(); + if (implicitMod(ei->exp, tb, mod) == MATCHnomatch) + return false; + } + else + { + /* Enhancement: handle StructInitializer and ArrayInitializer + */ + return false; + } + } + else if (!v->type->isZeroInit(loc)) + return false; + } + return cd->baseClass ? convertible(loc, cd->baseClass, mod) : true; + } + }; - for (size_t i = 0; i < sd->fields.dim; i++) + if (!ClassCheck::convertible(e->loc, cd, mod)) + return; + } + } + else { - Declaration *d = sd->fields[i]; - if (d->storage_class & STCref || d->hasPointers()) - goto Lnomatch; + Expression *earg = e->newtype->defaultInitLiteral(e->loc); + Type *targ = e->newtype->toBasetype(); + + if (implicitMod(earg, targ, mod) == MATCHnomatch) + return; } + + /* Success + */ + result = MATCHconst; } - else + + void visit(SliceExp *e) { - /* More fruit left on the table, such as pointers to immutable. + visit((Expression *)e); + if (result != MATCHnomatch) + return; + + Type *tb = t->toBasetype(); + Type *typeb = e->type->toBasetype(); + if (tb->ty == Tsarray && typeb->ty == Tarray && + e->lwr && e->upr) + { + typeb = toStaticArrayType(e); + if (typeb) + result = typeb->implicitConvTo(t); + return; + } + + /* If the only reason it won't convert is because of the mod bits, + * then test for conversion by seeing if e1 can be converted with those + * same mod bits. */ - goto Lnomatch; - } + Type *t1b = e->e1->type->toBasetype(); + if (tb->ty == Tarray && + typeb->immutableOf()->equals(tb->immutableOf())) + { + Type *tbn = tb->nextOf(); + Type *tx = NULL; - return (match == MATCHexact) ? MATCHconst : match; - } + /* If e->e1 is dynamic array or pointer, the uniqueness of e->e1 + * is equivalent with the uniqueness of the referred data. And in here + * we can have arbitrary typed reference for that. + */ + if (t1b->ty == Tarray) + tx = tbn->arrayOf(); + if (t1b->ty == Tpointer) + tx = tbn->pointerTo(); + + /* If e->e1 is static array, at least it should be an rvalue. + * If not, e->e1 is a reference, and its uniqueness does not link + * to the uniqueness of the referred data. + */ + if (t1b->ty == Tsarray && !e->e1->isLvalue()) + tx = tbn->sarrayOf(t1b->size() / tbn->size()); - Lnomatch: - return MATCHnomatch; + if (tx) + { + result = e->e1->implicitConvTo(tx); + if (result > MATCHconst) // Match level is MATCHconst at best. + result = MATCHconst; + } + } + } + }; + + ImplicitConvTo v(t); + e->accept(&v); + return v.result; } -Type *SliceExp::toStaticArrayType() +Type *toStaticArrayType(SliceExp *e) { - if (lwr && upr) + if (e->lwr && e->upr) { - Expression *lwr = this->lwr->optimize(WANTvalue); - Expression *upr = this->upr->optimize(WANTvalue); + // For the following code to work, e should be optimized beforehand. + // (eg. $ in lwr and upr should be already resolved, if possible) + Expression *lwr = e->lwr->optimize(WANTvalue); + Expression *upr = e->upr->optimize(WANTvalue); if (lwr->isConst() && upr->isConst()) { size_t len = (size_t)(upr->toUInteger() - lwr->toUInteger()); - return type->toBasetype()->nextOf()->sarrayOf(len); + return e->type->toBasetype()->nextOf()->sarrayOf(len); } } + else + { + Type *t1b = e->e1->type->toBasetype(); + if (t1b->ty == Tsarray) + return t1b; + } return NULL; } -MATCH SliceExp::implicitConvTo(Type *t) -{ - MATCH result = Expression::implicitConvTo(t); - - Type *tb = t->toBasetype(); - Type *typeb = type->toBasetype(); - if (result == MATCHnomatch && - tb->ty == Tsarray && typeb->ty == Tarray && - lwr && upr) - { - typeb = toStaticArrayType(); - if (typeb) - result = typeb->implicitConvTo(t); - } - return result; -} - /* ==================== castTo ====================== */ /************************************** @@ -1051,827 +1336,855 @@ MATCH SliceExp::implicitConvTo(Type *t) * Assume that the 'this' expression does not have any indirections. */ -Expression *Expression::castTo(Scope *sc, Type *t) +Expression *castTo(Expression *e, Scope *sc, Type *t) { - //printf("Expression::castTo(this=%s, t=%s)\n", toChars(), t->toChars()); -#if 0 - printf("Expression::castTo(this=%s, type=%s, t=%s)\n", - toChars(), type->toChars(), t->toChars()); -#endif - if (type->equals(t)) - return this; - if (op == TOKvar) + + class CastTo : public Visitor { - VarDeclaration *v = ((VarExp *)this)->var->isVarDeclaration(); - if (v && v->storage_class & STCmanifest) + public: + Type *t; + Scope *sc; + Expression *result; + + CastTo(Scope *sc, Type *t) + : sc(sc), t(t) { - Expression *e = ctfeInterpret(); - return e->castTo(sc, t); + result = NULL; } - } - Expression *e = this; - Type *tb = t->toBasetype(); - Type *typeb = type->toBasetype(); - if (!tb->equals(typeb)) - { - // Do (type *) cast of (type [dim]) - if (tb->ty == Tpointer && - typeb->ty == Tsarray - ) - { - //printf("Converting [dim] to *\n"); - e = new AddrExp(loc, e); - } - else + void visit(Expression *e) { - if (typeb->ty == Tstruct) + //printf("Expression::castTo(this=%s, t=%s)\n", e->toChars(), t->toChars()); + #if 0 + printf("Expression::castTo(this=%s, type=%s, t=%s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + if (e->type->equals(t)) { - TypeStruct *ts = (TypeStruct *)typeb; - if (!(tb->ty == Tstruct && ts->sym == ((TypeStruct *)tb)->sym) && - ts->sym->aliasthis) + result = e; + return; + } + if (e->op == TOKvar) + { + VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); + if (v && v->storage_class & STCmanifest) { - /* Forward the cast to our alias this member, rewrite to: - * cast(to)e1.aliasthis - */ - Expression *ex = resolveAliasThis(sc, this); - return ex->castTo(sc, t); + result = e->ctfeInterpret(); + result = result->castTo(sc, t); + return; } } - else if (typeb->ty == Tclass) + result = e; + Type *tb = t->toBasetype(); + Type *typeb = e->type->toBasetype(); + if (!tb->equals(typeb)) { - TypeClass *ts = (TypeClass *)typeb; - if (ts->sym->aliasthis) + // Do (type *) cast of (type [dim]) + if (tb->ty == Tpointer && + typeb->ty == Tsarray) { - if (tb->ty == Tclass) + //printf("Converting [dim] to *\n"); + result = new AddrExp(e->loc, result); + } + else + { + if (typeb->ty == Tstruct) { - ClassDeclaration *cdfrom = typeb->isClassHandle(); - ClassDeclaration *cdto = tb->isClassHandle(); - int offset; - if (cdto->isBaseOf(cdfrom, &offset)) - goto L1; + TypeStruct *ts = (TypeStruct *)typeb; + if (!(tb->ty == Tstruct && ts->sym == ((TypeStruct *)tb)->sym) && + ts->sym->aliasthis) + { + /* Forward the cast to our alias this member, rewrite to: + * cast(to)e1.aliasthis + */ + Expression *ex = resolveAliasThis(sc, e); + result = ex->castTo(sc, t); + return; + } } - /* Forward the cast to our alias this member, rewrite to: - * cast(to)e1.aliasthis - */ - Expression *e1 = resolveAliasThis(sc, this); - Expression *e2 = new CastExp(loc, e1, tb); - e2 = e2->semantic(sc); - return e2; + else if (typeb->ty == Tclass) + { + TypeClass *ts = (TypeClass *)typeb; + if (ts->sym->aliasthis) + { + if (tb->ty == Tclass) + { + ClassDeclaration *cdfrom = typeb->isClassHandle(); + ClassDeclaration *cdto = tb->isClassHandle(); + int offset; + if (cdto->isBaseOf(cdfrom, &offset)) + goto L1; + } + /* Forward the cast to our alias this member, rewrite to: + * cast(to)e1.aliasthis + */ + Expression *e1 = resolveAliasThis(sc, e); + Expression *e2 = new CastExp(e->loc, e1, tb); + e2 = e2->semantic(sc); + result = e2; + return; + } + } + else if (tb->ty == Tvector && typeb->ty != Tvector) + { + //printf("test1 e = %s, e->type = %s, tb = %s\n", e->toChars(), e->type->toChars(), tb->toChars()); + TypeVector *tv = (TypeVector *)tb; + result = new CastExp(e->loc, result, tv->elementType()); + result = new VectorExp(e->loc, result, tb); + result = result->semantic(sc); + return; + } + else if (typeb->implicitConvTo(tb) == MATCHconst && t->equals(e->type->constOf())) + { + result = e->copy(); + result->type = t; + return; + } + L1: + result = new CastExp(e->loc, result, tb); } - L1: ; - } - else if (tb->ty == Tvector && typeb->ty != Tvector) - { - //printf("test1 e = %s, e->type = %s, tb = %s\n", e->toChars(), e->type->toChars(), tb->toChars()); - TypeVector *tv = (TypeVector *)tb; - e = new CastExp(loc, e, tv->elementType()); - e = new VectorExp(loc, e, tb); - e = e->semantic(sc); - return e; } - else if (typeb->implicitConvTo(tb) == MATCHconst && t->equals(type->constOf())) + else { - e = copy(); - e->type = t; - return e; + result = result->copy(); // because of COW for assignment to e->type } - e = new CastExp(loc, e, tb); - } - } - else - { - e = e->copy(); // because of COW for assignment to e->type - } - assert(e != this); - e->type = t; - //printf("Returning: %s\n", e->toChars()); - return e; -} - - -Expression *ErrorExp::castTo(Scope *sc, Type *t) -{ - return this; -} - - -Expression *RealExp::castTo(Scope *sc, Type *t) -{ - Expression *e = this; - if (!type->equals(t)) - { - if ((type->isreal() && t->isreal()) || - (type->isimaginary() && t->isimaginary()) - ) - { e = copy(); - e->type = t; + assert(result != e); + result->type = t; + //printf("Returning: %s\n", result->toChars()); } - else - e = Expression::castTo(sc, t); - } - return e; -} - -Expression *ComplexExp::castTo(Scope *sc, Type *t) -{ - Expression *e = this; - if (!type->equals(t)) - { - if (type->iscomplex() && t->iscomplex()) - { e = copy(); - e->type = t; + void visit(ErrorExp *e) + { + result = e; } - else - e = Expression::castTo(sc, t); - } - return e; -} - - -Expression *NullExp::castTo(Scope *sc, Type *t) -{ - //printf("NullExp::castTo(t = %s) %s\n", t->toChars(), toChars()); - if (type->equals(t)) - { - committed = 1; - return this; - } - NullExp *e = (NullExp *)copy(); - e->committed = 1; - Type *tb = t->toBasetype(); -#if 0 - e->type = type->toBasetype(); - if (!tb->equals(e->type)) - { - // NULL implicitly converts to any pointer type or dynamic array - if (e->type->ty == Tpointer && e->type->nextOf()->ty == Tvoid && - (tb->ty == Tpointer || tb->ty == Tarray || tb->ty == Taarray || - tb->ty == Tdelegate)) + void visit(RealExp *e) { - if (tb->ty == Tdelegate) - { TypeDelegate *td = (TypeDelegate *)tb; - TypeFunction *tf = (TypeFunction *)td->nextOf(); - - if (!tf->varargs && - !(tf->arguments && tf->arguments->dim) + if (!e->type->equals(t)) + { + if ((e->type->isreal() && t->isreal()) || + (e->type->isimaginary() && t->isimaginary()) ) { - return Expression::castTo(sc, t); + result = e->copy(); + result->type = t; } + else + visit((Expression *)e); + return; } + result = e; } - else + + void visit(ComplexExp *e) { - //return e->Expression::castTo(sc, t); + if (!e->type->equals(t)) + { + if (e->type->iscomplex() && t->iscomplex()) + { + result = e->copy(); + result->type = t; + } + else + visit((Expression *)e); + return; + } + result = e; } - } -#else - if (tb->ty == Tvoid) - { - e->type = type->toBasetype(); - return e->Expression::castTo(sc, t); - } -#endif - if (tb->ty == Tsarray || tb->ty == Tstruct) - { - error("cannot cast null to %s", t->toChars()); - } - e->type = t; - return e; -} - -Expression *StructLiteralExp::castTo(Scope *sc, Type *t) -{ - Expression *e = Expression::castTo(sc, t); - if (e->op == TOKstructliteral) - ((StructLiteralExp *)e)->stype = t; // commit type - return e; -} - -Expression *StringExp::castTo(Scope *sc, Type *t) -{ - /* This follows copy-on-write; any changes to 'this' - * will result in a copy. - * The this->string member is considered immutable. - */ - int copied = 0; - - //printf("StringExp::castTo(t = %s), '%s' committed = %d\n", t->toChars(), toChars(), committed); - - if (!committed && t->ty == Tpointer && t->nextOf()->ty == Tvoid) - { - error("cannot convert string literal to void*"); - return new ErrorExp(); - } - - StringExp *se = this; - if (!committed) - { se = (StringExp *)copy(); - se->committed = 1; - copied = 1; - } - if (type->equals(t)) - { - return se; - } + void visit(NullExp *e) + { + //printf("NullExp::castTo(t = %s) %s\n", t->toChars(), toChars()); + if (e->type->equals(t)) + { + e->committed = 1; + result = e; + return; + } - Type *tb = t->toBasetype(); - //printf("\ttype = %s\n", type->toChars()); - if (tb->ty == Tdelegate && type->toBasetype()->ty != Tdelegate) - return Expression::castTo(sc, t); + NullExp *ex = (NullExp *)e->copy(); + ex->committed = 1; + Type *tb = t->toBasetype(); - Type *typeb = type->toBasetype(); - if (typeb->equals(tb)) - { - if (!copied) - { se = (StringExp *)copy(); - copied = 1; + if (tb->ty == Tvoid) + { + ex->type = e->type->toBasetype(); + visit((Expression *)ex); + return; + } + if (tb->ty == Tsarray || tb->ty == Tstruct) + { + e->error("cannot cast null to %s", t->toChars()); + } + ex->type = t; + result = ex; } - se->type = t; - return se; - } - if (committed && tb->ty == Tsarray && typeb->ty == Tarray) - { - se = (StringExp *)copy(); - d_uns64 szx = tb->nextOf()->size(); - assert(szx <= 255); - se->sz = (unsigned char)szx; - se->len = (len * sz) / se->sz; - se->committed = 1; - se->type = t; - - /* Assure space for terminating 0 - */ - if ((se->len + 1) * se->sz > (len + 1) * sz) + void visit(StructLiteralExp *e) { - void *s = (void *)mem.malloc((se->len + 1) * se->sz); - memcpy(s, se->string, se->len * se->sz); - memset((char *)s + se->len * se->sz, 0, se->sz); - se->string = s; + visit((Expression *)e); + if (result->op == TOKstructliteral) + ((StructLiteralExp *)result)->stype = t; // commit type } - return se; - } - if (tb->ty != Tsarray && tb->ty != Tarray && tb->ty != Tpointer) - { if (!copied) - { se = (StringExp *)copy(); - copied = 1; - } - goto Lcast; - } - if (typeb->ty != Tsarray && typeb->ty != Tarray && typeb->ty != Tpointer) - { if (!copied) - { se = (StringExp *)copy(); - copied = 1; - } - goto Lcast; - } + void visit(StringExp *e) + { + /* This follows copy-on-write; any changes to 'this' + * will result in a copy. + * The this->string member is considered immutable. + */ + int copied = 0; - if (typeb->nextOf()->size() == tb->nextOf()->size()) - { - if (!copied) - { se = (StringExp *)copy(); - copied = 1; - } - if (tb->ty == Tsarray) - goto L2; // handle possible change in static array dimension - se->type = t; - return se; - } + //printf("StringExp::castTo(t = %s), '%s' committed = %d\n", t->toChars(), e->toChars(), e->committed); - if (committed) - goto Lcast; + if (!e->committed && t->ty == Tpointer && t->nextOf()->ty == Tvoid) + { + e->error("cannot convert string literal to void*"); + result = new ErrorExp(); + return; + } -#define X(tf,tt) ((int)(tf) * 256 + (int)(tt)) - { - OutBuffer buffer; - size_t newlen = 0; - int tfty = typeb->nextOf()->toBasetype()->ty; - int ttty = tb->nextOf()->toBasetype()->ty; - switch (X(tfty, ttty)) - { - case X(Tchar, Tchar): - case X(Twchar,Twchar): - case X(Tdchar,Tdchar): - break; + StringExp *se = e; + if (!e->committed) + { + se = (StringExp *)e->copy(); + se->committed = 1; + copied = 1; + } - case X(Tchar, Twchar): - for (size_t u = 0; u < len;) - { unsigned c; - const char *p = utf_decodeChar((utf8_t *)se->string, len, &u, &c); - if (p) - error("%s", p); - else - buffer.writeUTF16(c); - } - newlen = buffer.offset / 2; - buffer.writeUTF16(0); - goto L1; - - case X(Tchar, Tdchar): - for (size_t u = 0; u < len;) - { unsigned c; - const char *p = utf_decodeChar((utf8_t *)se->string, len, &u, &c); - if (p) - error("%s", p); - buffer.write4(c); - newlen++; - } - buffer.write4(0); - goto L1; - - case X(Twchar,Tchar): - for (size_t u = 0; u < len;) - { unsigned c; - const char *p = utf_decodeWchar((unsigned short *)se->string, len, &u, &c); - if (p) - error("%s", p); - else - buffer.writeUTF8(c); - } - newlen = buffer.offset; - buffer.writeUTF8(0); - goto L1; - - case X(Twchar,Tdchar): - for (size_t u = 0; u < len;) - { unsigned c; - const char *p = utf_decodeWchar((unsigned short *)se->string, len, &u, &c); - if (p) - error("%s", p); - buffer.write4(c); - newlen++; - } - buffer.write4(0); - goto L1; - - case X(Tdchar,Tchar): - for (size_t u = 0; u < len; u++) - { - unsigned c = ((unsigned *)se->string)[u]; - if (!utf_isValidDchar(c)) - error("invalid UCS-32 char \\U%08x", c); - else - buffer.writeUTF8(c); - newlen++; + if (e->type->equals(t)) + { + result = se; + return; } - newlen = buffer.offset; - buffer.writeUTF8(0); - goto L1; - case X(Tdchar,Twchar): - for (size_t u = 0; u < len; u++) + Type *tb = t->toBasetype(); + //printf("\ttype = %s\n", e->type->toChars()); + if (tb->ty == Tdelegate && e->type->toBasetype()->ty != Tdelegate) { - unsigned c = ((unsigned *)se->string)[u]; - if (!utf_isValidDchar(c)) - error("invalid UCS-32 char \\U%08x", c); - else - buffer.writeUTF16(c); - newlen++; + visit((Expression *)e); + return; } - newlen = buffer.offset / 2; - buffer.writeUTF16(0); - goto L1; - L1: - if (!copied) - { se = (StringExp *)copy(); - copied = 1; + Type *typeb = e->type->toBasetype(); + if (typeb->equals(tb)) + { + if (!copied) + { + se = (StringExp *)e->copy(); + copied = 1; + } + se->type = t; + result = se; + return; } - se->string = buffer.extractData(); - se->len = newlen; + if (e->committed && tb->ty == Tsarray && typeb->ty == Tarray) { + se = (StringExp *)e->copy(); d_uns64 szx = tb->nextOf()->size(); assert(szx <= 255); se->sz = (unsigned char)szx; + se->len = (e->len * e->sz) / se->sz; + se->committed = 1; + se->type = t; + + /* Assure space for terminating 0 + */ + if ((se->len + 1) * se->sz > (e->len + 1) * e->sz) + { + void *s = (void *)mem.malloc((se->len + 1) * se->sz); + memcpy(s, se->string, se->len * se->sz); + memset((char *)s + se->len * se->sz, 0, se->sz); + se->string = s; + } + result = se; + return; } - break; - default: - assert(typeb->nextOf()->size() != tb->nextOf()->size()); - goto Lcast; - } - } -#undef X -L2: - assert(copied); + if (tb->ty != Tsarray && tb->ty != Tarray && tb->ty != Tpointer) + { + if (!copied) + { + se = (StringExp *)e->copy(); + copied = 1; + } + goto Lcast; + } + if (typeb->ty != Tsarray && typeb->ty != Tarray && typeb->ty != Tpointer) + { + if (!copied) + { + se = (StringExp *)e->copy(); + copied = 1; + } + goto Lcast; + } - // See if need to truncate or extend the literal - if (tb->ty == Tsarray) - { - size_t dim2 = (size_t)((TypeSArray *)tb)->dim->toInteger(); + if (typeb->nextOf()->size() == tb->nextOf()->size()) + { + if (!copied) + { + se = (StringExp *)e->copy(); + copied = 1; + } + if (tb->ty == Tsarray) + goto L2; // handle possible change in static array dimension + se->type = t; + result = se; + return; + } - //printf("dim from = %d, to = %d\n", (int)se->len, (int)dim2); + if (e->committed) + goto Lcast; - // Changing dimensions - if (dim2 != se->len) - { - // Copy when changing the string literal - size_t newsz = se->sz; - size_t d = (dim2 < se->len) ? dim2 : se->len; - void *s = (void *)mem.malloc((dim2 + 1) * newsz); - memcpy(s, se->string, d * newsz); - // Extend with 0, add terminating 0 - memset((char *)s + d * newsz, 0, (dim2 + 1 - d) * newsz); - se->string = s; - se->len = dim2; - } - } - se->type = t; - return se; + #define X(tf,tt) ((int)(tf) * 256 + (int)(tt)) + { + OutBuffer buffer; + size_t newlen = 0; + int tfty = typeb->nextOf()->toBasetype()->ty; + int ttty = tb->nextOf()->toBasetype()->ty; + switch (X(tfty, ttty)) + { + case X(Tchar, Tchar): + case X(Twchar,Twchar): + case X(Tdchar,Tdchar): + break; -Lcast: - Expression *e = new CastExp(loc, se, t); - e->type = t; // so semantic() won't be run on e - return e; -} + case X(Tchar, Twchar): + for (size_t u = 0; u < e->len;) + { + unsigned c; + const char *p = utf_decodeChar((utf8_t *)se->string, e->len, &u, &c); + if (p) + e->error("%s", p); + else + buffer.writeUTF16(c); + } + newlen = buffer.offset / 2; + buffer.writeUTF16(0); + goto L1; -Expression *AddrExp::castTo(Scope *sc, Type *t) -{ - Type *tb; + case X(Tchar, Tdchar): + for (size_t u = 0; u < e->len;) + { + unsigned c; + const char *p = utf_decodeChar((utf8_t *)se->string, e->len, &u, &c); + if (p) + e->error("%s", p); + buffer.write4(c); + newlen++; + } + buffer.write4(0); + goto L1; -#if 0 - printf("AddrExp::castTo(this=%s, type=%s, t=%s)\n", - toChars(), type->toChars(), t->toChars()); -#endif - Expression *e = this; + case X(Twchar,Tchar): + for (size_t u = 0; u < e->len;) + { + unsigned c; + const char *p = utf_decodeWchar((unsigned short *)se->string, e->len, &u, &c); + if (p) + e->error("%s", p); + else + buffer.writeUTF8(c); + } + newlen = buffer.offset; + buffer.writeUTF8(0); + goto L1; - tb = t->toBasetype(); - type = type->toBasetype(); - if (!tb->equals(type)) - { - // Look for pointers to functions where the functions are overloaded. - - if (e1->op == TOKoverloadset && - (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction) - { OverExp *eo = (OverExp *)e1; - FuncDeclaration *f = NULL; - for (size_t i = 0; i < eo->vars->a.dim; i++) - { Dsymbol *s = eo->vars->a[i]; - FuncDeclaration *f2 = s->isFuncDeclaration(); - assert(f2); - if (f2->overloadExactMatch(t->nextOf())) - { if (f) - /* Error if match in more than one overload set, - * even if one is a 'better' match than the other. - */ - ScopeDsymbol::multiplyDefined(loc, f, f2); - else - f = f2; - } + case X(Twchar,Tdchar): + for (size_t u = 0; u < e->len;) + { + unsigned c; + const char *p = utf_decodeWchar((unsigned short *)se->string, e->len, &u, &c); + if (p) + e->error("%s", p); + buffer.write4(c); + newlen++; + } + buffer.write4(0); + goto L1; + + case X(Tdchar,Tchar): + for (size_t u = 0; u < e->len; u++) + { + unsigned c = ((unsigned *)se->string)[u]; + if (!utf_isValidDchar(c)) + e->error("invalid UCS-32 char \\U%08x", c); + else + buffer.writeUTF8(c); + newlen++; + } + newlen = buffer.offset; + buffer.writeUTF8(0); + goto L1; + + case X(Tdchar,Twchar): + for (size_t u = 0; u < e->len; u++) + { + unsigned c = ((unsigned *)se->string)[u]; + if (!utf_isValidDchar(c)) + e->error("invalid UCS-32 char \\U%08x", c); + else + buffer.writeUTF16(c); + newlen++; + } + newlen = buffer.offset / 2; + buffer.writeUTF16(0); + goto L1; + + L1: + if (!copied) + { + se = (StringExp *)e->copy(); + copied = 1; + } + se->string = buffer.extractData(); + se->len = newlen; + + { + d_uns64 szx = tb->nextOf()->size(); + assert(szx <= 255); + se->sz = (unsigned char)szx; + } + break; + + default: + assert(typeb->nextOf()->size() != tb->nextOf()->size()); + goto Lcast; } - if (f) - { f->tookAddressOf++; - SymOffExp *se = new SymOffExp(loc, f, 0, 0); - se->semantic(sc); - // Let SymOffExp::castTo() do the heavy lifting - return se->castTo(sc, t); } - } - + #undef X + L2: + assert(copied); - if (type->ty == Tpointer && type->nextOf()->ty == Tfunction && - tb->ty == Tpointer && tb->nextOf()->ty == Tfunction && - e1->op == TOKvar) - { - VarExp *ve = (VarExp *)e1; - FuncDeclaration *f = ve->var->isFuncDeclaration(); - if (f) + // See if need to truncate or extend the literal + if (tb->ty == Tsarray) { - assert(0); // should be SymOffExp instead - f = f->overloadExactMatch(tb->nextOf()); - if (f) + size_t dim2 = (size_t)((TypeSArray *)tb)->dim->toInteger(); + + //printf("dim from = %d, to = %d\n", (int)se->len, (int)dim2); + + // Changing dimensions + if (dim2 != se->len) { - e = new VarExp(loc, f); - e->type = f->type; - e = new AddrExp(loc, e); - e->type = t; - return e; + // Copy when changing the string literal + size_t newsz = se->sz; + size_t d = (dim2 < se->len) ? dim2 : se->len; + void *s = (void *)mem.malloc((dim2 + 1) * newsz); + memcpy(s, se->string, d * newsz); + // Extend with 0, add terminating 0 + memset((char *)s + d * newsz, 0, (dim2 + 1 - d) * newsz); + se->string = s; + se->len = dim2; } } + se->type = t; + result = se; + return; + + Lcast: + result = new CastExp(e->loc, se, t); + result->type = t; // so semantic() won't be run on e } - e = Expression::castTo(sc, t); - } - e->type = t; - return e; -} + void visit(AddrExp *e) + { + Type *tb; -Expression *TupleExp::castTo(Scope *sc, Type *t) -{ - if (type->equals(t)) - return this; - - TupleExp *e = (TupleExp *)copy(); - e->e0 = e0 ? e0->copy() : NULL; - e->exps = (Expressions *)exps->copy(); - for (size_t i = 0; i < e->exps->dim; i++) - { Expression *ex = (*e->exps)[i]; - ex = ex->castTo(sc, t); - (*e->exps)[i] = ex; - } - return e; -} + #if 0 + printf("AddrExp::castTo(this=%s, type=%s, t=%s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + result = e; + tb = t->toBasetype(); + e->type = e->type->toBasetype(); + if (!tb->equals(e->type)) + { + // Look for pointers to functions where the functions are overloaded. -Expression *ArrayLiteralExp::castTo(Scope *sc, Type *t) -{ -#if 0 - printf("ArrayLiteralExp::castTo(this=%s, type=%s, => %s)\n", - toChars(), type->toChars(), t->toChars()); -#endif - if (type == t) - return this; - ArrayLiteralExp *e = this; - Type *typeb = type->toBasetype(); - Type *tb = t->toBasetype(); - if ((tb->ty == Tarray || tb->ty == Tsarray) && - (typeb->ty == Tarray || typeb->ty == Tsarray) && - // Not trying to convert non-void[] to void[] - !(tb->nextOf()->toBasetype()->ty == Tvoid && typeb->nextOf()->toBasetype()->ty != Tvoid) && - // Not trying to convert void[n] to others - !(typeb->ty == Tsarray && typeb->nextOf()->toBasetype()->ty == Tvoid)) - { - if (tb->ty == Tsarray) - { TypeSArray *tsa = (TypeSArray *)tb; - if (elements->dim != tsa->dim->toInteger()) - goto L1; - } + if (e->e1->op == TOKoverloadset && + (t->ty == Tpointer || t->ty == Tdelegate) && t->nextOf()->ty == Tfunction) + { + OverExp *eo = (OverExp *)e->e1; + FuncDeclaration *f = NULL; + for (size_t i = 0; i < eo->vars->a.dim; i++) + { + Dsymbol *s = eo->vars->a[i]; + FuncDeclaration *f2 = s->isFuncDeclaration(); + assert(f2); + if (f2->overloadExactMatch(t->nextOf())) + { + if (f) + { + /* Error if match in more than one overload set, + * even if one is a 'better' match than the other. + */ + ScopeDsymbol::multiplyDefined(e->loc, f, f2); + } + else + f = f2; + } + } + if (f) + { + f->tookAddressOf++; + SymOffExp *se = new SymOffExp(e->loc, f, 0, 0); + se->semantic(sc); + // Let SymOffExp::castTo() do the heavy lifting + visit(se); + return; + } + } - e = (ArrayLiteralExp *)copy(); - e->elements = (Expressions *)elements->copy(); - for (size_t i = 0; i < elements->dim; i++) - { Expression *ex = (*elements)[i]; - ex = ex->castTo(sc, tb->nextOf()); - (*e->elements)[i] = ex; + if (e->type->ty == Tpointer && e->type->nextOf()->ty == Tfunction && + tb->ty == Tpointer && tb->nextOf()->ty == Tfunction && + e->e1->op == TOKvar) + { + VarExp *ve = (VarExp *)e->e1; + FuncDeclaration *f = ve->var->isFuncDeclaration(); + if (f) + { + assert(f->isImportedSymbol()); + f = f->overloadExactMatch(tb->nextOf()); + if (f) + { + result = new VarExp(e->loc, f); + result->type = f->type; + result = new AddrExp(e->loc, result); + result->type = t; + return; + } + } + } + visit((Expression *)e); + } + result->type = t; } - e->type = t; - return e; - } - if (tb->ty == Tpointer && typeb->ty == Tsarray) - { - Type *tp = typeb->nextOf()->pointerTo(); - if (!tp->equals(e->type)) - { e = (ArrayLiteralExp *)copy(); - e->type = tp; + + void visit(TupleExp *e) + { + if (e->type->equals(t)) + { + result = e; + return; + } + + TupleExp *te = (TupleExp *)e->copy(); + te->e0 = e->e0 ? e->e0->copy() : NULL; + te->exps = (Expressions *)e->exps->copy(); + for (size_t i = 0; i < te->exps->dim; i++) + { + Expression *ex = (*te->exps)[i]; + ex = ex->castTo(sc, t); + (*te->exps)[i] = ex; + } + result = te; } - } - else if (tb->ty == Tvector && - (typeb->ty == Tarray || typeb->ty == Tsarray)) - { - // Convert array literal to vector type - TypeVector *tv = (TypeVector *)tb; - TypeSArray *tbase = (TypeSArray *)tv->basetype; - assert(tbase->ty == Tsarray); - if (elements->dim != tbase->dim->toInteger()) - goto L1; - - e = (ArrayLiteralExp *)copy(); - e->elements = (Expressions *)elements->copy(); - Type *telement = tv->elementType(); - for (size_t i = 0; i < elements->dim; i++) - { Expression *ex = (*elements)[i]; - ex = ex->castTo(sc, telement); - (*e->elements)[i] = ex; - } - Expression *ev = new VectorExp(loc, e, tb); - ev = ev->semantic(sc); - return ev; - } -L1: - return e->Expression::castTo(sc, t); -} -Expression *AssocArrayLiteralExp::castTo(Scope *sc, Type *t) -{ - if (type == t) - return this; - AssocArrayLiteralExp *e = this; - Type *typeb = type->toBasetype(); - Type *tb = t->toBasetype(); - if (tb->ty == Taarray && typeb->ty == Taarray && - tb->nextOf()->toBasetype()->ty != Tvoid) - { - e = (AssocArrayLiteralExp *)copy(); - e->keys = (Expressions *)keys->copy(); - e->values = (Expressions *)values->copy(); - assert(keys->dim == values->dim); - for (size_t i = 0; i < keys->dim; i++) - { Expression *ex = (*values)[i]; - ex = ex->castTo(sc, tb->nextOf()); - (*e->values)[i] = ex; - - ex = (*keys)[i]; - ex = ex->castTo(sc, ((TypeAArray *)tb)->index); - (*e->keys)[i] = ex; + void visit(ArrayLiteralExp *e) + { + #if 0 + printf("ArrayLiteralExp::castTo(this=%s, type=%s, => %s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + if (e->type == t) + { + result = e; + return; + } + ArrayLiteralExp *ae = e; + Type *typeb = e->type->toBasetype(); + Type *tb = t->toBasetype(); + if ((tb->ty == Tarray || tb->ty == Tsarray) && + (typeb->ty == Tarray || typeb->ty == Tsarray)) + { + if (tb->nextOf()->toBasetype()->ty == Tvoid && typeb->nextOf()->toBasetype()->ty != Tvoid) + { + // Don't do anything to cast non-void[] to void[] + } + else if (typeb->ty == Tsarray && typeb->nextOf()->toBasetype()->ty == Tvoid) + { + // Don't do anything for casting void[n] to others + } + else + { + if (tb->ty == Tsarray) + { + TypeSArray *tsa = (TypeSArray *)tb; + if (e->elements->dim != tsa->dim->toInteger()) + goto L1; + } + + ae = (ArrayLiteralExp *)e->copy(); + ae->elements = e->elements->copy(); + for (size_t i = 0; i < e->elements->dim; i++) + { + Expression *ex = (*e->elements)[i]; + ex = ex->castTo(sc, tb->nextOf()); + (*ae->elements)[i] = ex; + } + ae->type = t; + result = ae; + return; + } + } + else if (tb->ty == Tpointer && typeb->ty == Tsarray) + { + Type *tp = typeb->nextOf()->pointerTo(); + if (!tp->equals(ae->type)) + { + ae = (ArrayLiteralExp *)e->copy(); + ae->type = tp; + } + } + else if (tb->ty == Tvector && + (typeb->ty == Tarray || typeb->ty == Tsarray)) + { + // Convert array literal to vector type + TypeVector *tv = (TypeVector *)tb; + TypeSArray *tbase = (TypeSArray *)tv->basetype; + assert(tbase->ty == Tsarray); + if (e->elements->dim != tbase->dim->toInteger()) + goto L1; + + ae = (ArrayLiteralExp *)e->copy(); + ae->type = tbase; // Bugzilla 12642 + ae->elements = e->elements->copy(); + Type *telement = tv->elementType(); + for (size_t i = 0; i < e->elements->dim; i++) + { + Expression *ex = (*e->elements)[i]; + ex = ex->castTo(sc, telement); + (*ae->elements)[i] = ex; + } + Expression *ev = new VectorExp(e->loc, ae, tb); + ev = ev->semantic(sc); + result = ev; + return; + } + L1: + visit((Expression *)ae); } - e->type = t; - return e; - } - return e->Expression::castTo(sc, t); -} -Expression *SymOffExp::castTo(Scope *sc, Type *t) -{ -#if 0 - printf("SymOffExp::castTo(this=%s, type=%s, t=%s)\n", - toChars(), type->toChars(), t->toChars()); -#endif - if (type == t && !hasOverloads) - return this; - Expression *e; - Type *tb = t->toBasetype(); - Type *typeb = type->toBasetype(); - if (!tb->equals(typeb)) - { - // Look for pointers to functions where the functions are overloaded. - FuncDeclaration *f; + void visit(AssocArrayLiteralExp *e) + { + if (e->type == t) + { + result = e; + return; + } + Type *typeb = e->type->toBasetype(); + Type *tb = t->toBasetype(); + if (tb->ty == Taarray && typeb->ty == Taarray && + tb->nextOf()->toBasetype()->ty != Tvoid) + { + AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)e->copy(); + ae->keys = e->keys->copy(); + ae->values = e->values->copy(); + assert(e->keys->dim == e->values->dim); + for (size_t i = 0; i < e->keys->dim; i++) + { + Expression *ex = (*e->values)[i]; + ex = ex->castTo(sc, tb->nextOf()); + (*ae->values)[i] = ex; + + ex = (*e->keys)[i]; + ex = ex->castTo(sc, ((TypeAArray *)tb)->index); + (*ae->keys)[i] = ex; + } + ae->type = t; + result = ae; + return; + } + visit((Expression *)e); + } - if (hasOverloads && - typeb->ty == Tpointer && typeb->nextOf()->ty == Tfunction && - (tb->ty == Tpointer || tb->ty == Tdelegate) && tb->nextOf()->ty == Tfunction) + void visit(SymOffExp *e) { - f = var->isFuncDeclaration(); - if (f) + #if 0 + printf("SymOffExp::castTo(this=%s, type=%s, t=%s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + if (e->type == t && !e->hasOverloads) + { + result = e; + return; + } + Type *tb = t->toBasetype(); + Type *typeb = e->type->toBasetype(); + + if (tb->equals(typeb)) + { + result = e->copy(); + result->type = t; + ((SymOffExp *)result)->hasOverloads = false; + return; + } + + // Look for pointers to functions where the functions are overloaded. + if (e->hasOverloads && + typeb->ty == Tpointer && typeb->nextOf()->ty == Tfunction && + (tb->ty == Tpointer || tb->ty == Tdelegate) && tb->nextOf()->ty == Tfunction) { - f = f->overloadExactMatch(tb->nextOf()); + FuncDeclaration *f = e->var->isFuncDeclaration(); + f = f ? f->overloadExactMatch(tb->nextOf()) : NULL; if (f) { if (tb->ty == Tdelegate) { if (f->needThis() && hasThis(sc)) { - e = new DelegateExp(loc, new ThisExp(loc), f); - e = e->semantic(sc); + result = new DelegateExp(e->loc, new ThisExp(e->loc), f); + result = result->semantic(sc); } else if (f->isNested()) { - e = new DelegateExp(loc, new IntegerExp(0), f); - e = e->semantic(sc); + result = new DelegateExp(e->loc, new IntegerExp(0), f); + result = result->semantic(sc); } else if (f->needThis()) - { error("no 'this' to create delegate for %s", f->toChars()); - return new ErrorExp(); + { + e->error("no 'this' to create delegate for %s", f->toChars()); + result = new ErrorExp(); + return; } else - { error("cannot cast from function pointer to delegate"); - return new ErrorExp(); + { + e->error("cannot cast from function pointer to delegate"); + result = new ErrorExp(); + return; } } else { - e = new SymOffExp(loc, f, 0); - e->type = t; + result = new SymOffExp(e->loc, f, 0); + result->type = t; } f->tookAddressOf++; - return e; + return; } } + visit((Expression *)e); } - e = Expression::castTo(sc, t); - } - else - { e = copy(); - e->type = t; - ((SymOffExp *)e)->hasOverloads = false; - } - return e; -} -Expression *DelegateExp::castTo(Scope *sc, Type *t) -{ -#if 0 - printf("DelegateExp::castTo(this=%s, type=%s, t=%s)\n", - toChars(), type->toChars(), t->toChars()); -#endif - static const char msg[] = "cannot form delegate due to covariant return type"; - - Expression *e = this; - Type *tb = t->toBasetype(); - Type *typeb = type->toBasetype(); - if (!tb->equals(typeb) || hasOverloads) - { - // Look for delegates to functions where the functions are overloaded. - FuncDeclaration *f; + void visit(DelegateExp *e) + { + #if 0 + printf("DelegateExp::castTo(this=%s, type=%s, t=%s)\n", + e->toChars(), e->type->toChars(), t->toChars()); + #endif + static const char msg[] = "cannot form delegate due to covariant return type"; + + Type *tb = t->toBasetype(); + Type *typeb = e->type->toBasetype(); + if (!tb->equals(typeb) || e->hasOverloads) + { + // Look for delegates to functions where the functions are overloaded. + if (typeb->ty == Tdelegate && + tb->ty == Tdelegate) + { + if (e->func) + { + FuncDeclaration *f = e->func->overloadExactMatch(tb->nextOf()); + if (f) + { + int offset; + if (f->tintro && f->tintro->nextOf()->isBaseOf(f->type->nextOf(), &offset) && offset) + e->error("%s", msg); + f->tookAddressOf++; + result = new DelegateExp(e->loc, e->e1, f); + result->type = t; + return; + } + if (e->func->tintro) + e->error("%s", msg); + } + } + visit((Expression *)e); + } + else + { + int offset; + e->func->tookAddressOf++; + if (e->func->tintro && e->func->tintro->nextOf()->isBaseOf(e->func->type->nextOf(), &offset) && offset) + e->error("%s", msg); + result = e->copy(); + result->type = t; + } + } - if (typeb->ty == Tdelegate && - tb->ty == Tdelegate) + void visit(FuncExp *e) { - if (func) + //printf("FuncExp::castTo type = %s, t = %s\n", e->type->toChars(), t->toChars()); + FuncExp *fe; + if (e->matchType(t, sc, &fe, 1) > MATCHnomatch) { - f = func->overloadExactMatch(tb->nextOf()); - if (f) - { int offset; - if (f->tintro && f->tintro->nextOf()->isBaseOf(f->type->nextOf(), &offset) && offset) - error("%s", msg); - f->tookAddressOf++; - e = new DelegateExp(loc, e1, f); - e->type = t; - return e; - } - if (func->tintro) - error("%s", msg); + result = fe; + return; } + visit((Expression *)e); } - e = Expression::castTo(sc, t); - } - else - { int offset; - - func->tookAddressOf++; - if (func->tintro && func->tintro->nextOf()->isBaseOf(func->type->nextOf(), &offset) && offset) - error("%s", msg); - e = copy(); - e->type = t; - } - return e; -} -Expression *FuncExp::castTo(Scope *sc, Type *t) -{ - //printf("FuncExp::castTo type = %s, t = %s\n", type->toChars(), t->toChars()); - Expression *e = inferType(t, 1); - if (e) - { - if (e != this) - return e->castTo(sc, t); - if (!e->type->equals(t) && e->type->implicitConvTo(t)) + void visit(CondExp *e) { - assert(t->ty == Tpointer && t->nextOf()->ty == Tvoid || // Bugzilla 9928 - e->type->nextOf()->covariant(t->nextOf()) == 1); - e = e->copy(); - e->type = t; - return e; + if (!e->type->equals(t)) + { + result = new CondExp(e->loc, e->econd, e->e1->castTo(sc, t), e->e2->castTo(sc, t)); + result->type = t; + return; + } + result = e; } - } - return Expression::castTo(sc, t); -} -Expression *CondExp::castTo(Scope *sc, Type *t) -{ - Expression *e = this; + void visit(CommaExp *e) + { + Expression *e2c = e->e2->castTo(sc, t); - if (!type->equals(t)) - { - if (1 || e1->op == TOKstring || e2->op == TOKstring) - { e = new CondExp(loc, econd, e1->castTo(sc, t), e2->castTo(sc, t)); - e->type = t; + if (e2c != e->e2) + { + result = new CommaExp(e->loc, e->e1, e2c); + result->type = e2c->type; + } + else + { + result = e; + result->type = e->e2->type; + } } - else - e = Expression::castTo(sc, t); - } - return e; -} -Expression *CommaExp::castTo(Scope *sc, Type *t) -{ - Expression *e2c = e2->castTo(sc, t); - Expression *e; + void visit(SliceExp *e) + { + Type *typeb = e->type->toBasetype(); + Type *tb = t->toBasetype(); - if (e2c != e2) - { - e = new CommaExp(loc, e1, e2c); - e->type = e2c->type; - } - else - { e = this; - e->type = e2->type; - } - return e; -} + if (typeb->ty == Tarray && tb->ty == Tsarray) + { + /* If a SliceExp has Tsarray, it will become lvalue. + * That's handled in SliceExp::isLvalue and toLvalue + */ + result = e->copy(); + result->type = t; + } + else if (typeb->ty == Tarray && tb->ty == Tarray && + typeb->nextOf()->constConv(tb->nextOf()) == MATCHconst) + { + // immutable(T)[] to const(T)[] + // T [] to const(T)[] + result = e->copy(); + result->type = t; + } + else + { + visit((Expression *)e); + } + } + }; -Expression *SliceExp::castTo(Scope *sc, Type *t) -{ - Type *typeb = type->toBasetype(); - Type *tb = t->toBasetype(); - Expression *e; - if (typeb->ty == Tarray && tb->ty == Tsarray) - { - /* If a SliceExp has Tsarray, it will become lvalue. - * That's handled in SliceExp::isLvalue and toLvalue - */ - e = copy(); - e->type = t; - } - else if (typeb->ty == Tarray && tb->ty == Tarray && - typeb->nextOf()->constConv(tb->nextOf()) == MATCHconst) - { - // immutable(T)[] to const(T)[] - // T [] to const(T)[] - e = copy(); - e->type = t; - } - else - { - e = Expression::castTo(sc, t); - } - return e; + CastTo v(sc, t); + e->accept(&v); + return v.result; } /* ==================== inferType ====================== */ @@ -1880,195 +2193,104 @@ Expression *SliceExp::castTo(Scope *sc, Type *t) * Set type inference target * t Target type * flag 1: don't put an error when inference fails - * sc it is used for the semantic of t, when != NULL - * tparams template parameters should be inferred */ -Expression *Expression::inferType(Type *t, int flag, Scope *sc, TemplateParameters *tparams) -{ - return this; -} - -Expression *ArrayLiteralExp::inferType(Type *t, int flag, Scope *sc, TemplateParameters *tparams) +Expression *inferType(Expression *e, Type *t, int flag) { - if (t) + class InferType : public Visitor { - t = t->toBasetype(); - if (t->ty == Tarray || t->ty == Tsarray) - { - Type *tn = t->nextOf(); - for (size_t i = 0; i < elements->dim; i++) - { Expression *e = (*elements)[i]; - if (e) - { e = e->inferType(tn, flag, sc, tparams); - (*elements)[i] = e; - } - } - } - } - return this; -} + public: + Type *t; + int flag; + Expression *result; -Expression *AssocArrayLiteralExp::inferType(Type *t, int flag, Scope *sc, TemplateParameters *tparams) -{ - if (t) - { - t = t->toBasetype(); - if (t->ty == Taarray) - { TypeAArray *taa = (TypeAArray *)t; - Type *ti = taa->index; - Type *tv = taa->nextOf(); - for (size_t i = 0; i < keys->dim; i++) - { Expression *e = (*keys)[i]; - if (e) - { e = e->inferType(ti, flag, sc, tparams); - (*keys)[i] = e; - } - } - for (size_t i = 0; i < values->dim; i++) - { Expression *e = (*values)[i]; - if (e) - { e = e->inferType(tv, flag, sc, tparams); - (*values)[i] = e; - } - } + InferType(Type *t, int flag) + : t(t), flag(flag) + { + result = NULL; } - } - return this; -} - -Expression *FuncExp::inferType(Type *to, int flag, Scope *sc, TemplateParameters *tparams) -{ - if (!to) - return this; - //printf("FuncExp::interType('%s'), to=%s\n", type?type->toChars():"null", to->toChars()); - if (!type) // semantic is not yet done - { - if (to->ty == Tdelegate || - to->ty == Tpointer && to->nextOf()->ty == Tfunction) + void visit(Expression *e) { - fd->treq = to; + result = e; } - return this; - } - - Expression *e = NULL; - - Type *t = to; - if (t->ty == Tdelegate) - { if (tok == TOKfunction) - goto L1; - t = t->nextOf(); - } - else if (t->ty == Tpointer && t->nextOf()->ty == Tfunction) - { if (tok == TOKdelegate) - goto L1; - t = t->nextOf(); - } - if (td) - { - // Parameter types inference from 'to' - assert(td->scope); - if (t->ty == Tfunction) + void visit(ArrayLiteralExp *ale) { - TypeFunction *tfv = (TypeFunction *)t; - TypeFunction *tfl = (TypeFunction *)fd->type; - //printf("\ttfv = %s\n", tfv->toChars()); - //printf("\ttfl = %s\n", tfl->toChars()); - size_t dim = Parameter::dim(tfl->parameters); - - if (Parameter::dim(tfv->parameters) == dim && - tfv->varargs == tfl->varargs) + Type *tb = t->toBasetype(); + if (tb->ty == Tarray || tb->ty == Tsarray) { - Objects *tiargs = new Objects(); - tiargs->reserve(td->parameters->dim); - - for (size_t i = 0; i < td->parameters->dim; i++) + Type *tn = tb->nextOf(); + for (size_t i = 0; i < ale->elements->dim; i++) { - TemplateParameter *tp = (*td->parameters)[i]; - for (size_t u = 0; u < dim; u++) + Expression *e = (*ale->elements)[i]; + if (e) { - Parameter *p = Parameter::getNth(tfl->parameters, u); - if (p->type->ty == Tident && - ((TypeIdentifier *)p->type)->ident == tp->ident) - { - p = Parameter::getNth(tfv->parameters, u); - Type *tprm = p->type; - if (tprm->reliesOnTident(tparams)) - goto L1; - if (sc) - tprm = tprm->semantic(loc, sc); - if (tprm->ty == Terror) - goto L1; - tiargs->push(tprm); - u = dim; // break inner loop - } + e = inferType(e, tn, flag); + (*ale->elements)[i] = e; } } + } + result = ale; + } - // Set target of return type inference - assert(td->onemember); - FuncLiteralDeclaration *fld = td->onemember->isFuncLiteralDeclaration(); - assert(fld); - if (!fld->type->nextOf() && tfv->next) - fld->treq = to; - - TemplateInstance *ti = new TemplateInstance(loc, td, tiargs); - e = (new ScopeExp(loc, ti))->semantic(td->scope); - - // Reset inference target for the later re-semantic - fld->treq = NULL; - - if (e->op == TOKfunction) + void visit(AssocArrayLiteralExp *aale) + { + Type *tb = t->toBasetype(); + if (tb->ty == Taarray) + { + TypeAArray *taa = (TypeAArray *)tb; + Type *ti = taa->index; + Type *tv = taa->nextOf(); + for (size_t i = 0; i < aale->keys->dim; i++) + { + Expression *e = (*aale->keys)[i]; + if (e) + { + e = inferType(e, ti, flag); + (*aale->keys)[i] = e; + } + } + for (size_t i = 0; i < aale->values->dim; i++) { - FuncExp *fe = (FuncExp *)e; - assert(fe->td == NULL); - e = fe->inferType(to, flag); + Expression *e = (*aale->values)[i]; + if (e) + { + e = inferType(e, tv, flag); + (*aale->values)[i] = e; + } } } + result = aale; } - } - else if (type) - { - assert(type != Type::tvoid); // semantic is already done - // Allow conversion from implicit function pointer to delegate - if (tok == TOKreserved && type->ty == Tpointer && - to->ty == Tdelegate) + void visit(FuncExp *fe) { - Type *typen = type->nextOf(); - if (typen->deco) + //printf("FuncExp::inferType('%s'), to=%s\n", fe->type ? fe->type->toChars() : "null", t->toChars()); + if (t->ty == Tdelegate || + t->ty == Tpointer && t->nextOf()->ty == Tfunction) { - FuncExp *fe = (FuncExp *)copy(); - fe->tok = TOKdelegate; - fe->type = (new TypeDelegate(typen))->merge(); - e = fe; + fe->fd->treq = t; } + result = fe; } - else - e = this; - } -L1: - if (!flag && !e) - { - error("cannot infer function literal type from %s", to->toChars()); - e = new ErrorExp(); - } - return e; -} -Expression *CondExp::inferType(Type *t, int flag, Scope *sc, TemplateParameters *tparams) -{ - if (t) - { - t = t->toBasetype(); - e1 = e1->inferType(t, flag, sc, tparams); - e2 = e2->inferType(t, flag, sc, tparams); - } - return this; + void visit(CondExp *ce) + { + Type *tb = t->toBasetype(); + ce->e1 = inferType(ce->e1, tb, flag); + ce->e2 = inferType(ce->e2, tb, flag); + result = ce; + } + }; + + if (!t) + return e; + + InferType v(t, flag); + e->accept(&v); + return v.result; } /* ==================== ====================== */ @@ -2077,43 +2299,44 @@ Expression *CondExp::inferType(Type *t, int flag, Scope *sc, TemplateParameters * Scale addition/subtraction to/from pointer. */ -Expression *BinExp::scaleFactor(Scope *sc) +Expression *scaleFactor(BinExp *be, Scope *sc) { - d_uns64 stride; - Type *t1b = e1->type->toBasetype(); - Type *t2b = e2->type->toBasetype(); + Type *t1b = be->e1->type->toBasetype(); + Type *t2b = be->e2->type->toBasetype(); Expression *eoff; if (t1b->ty == Tpointer && t2b->isintegral()) - { // Need to adjust operator by the stride + { + // Need to adjust operator by the stride // Replace (ptr + int) with (ptr + (int * stride)) Type *t = Type::tptrdiff_t; - stride = t1b->nextOf()->size(loc); + d_uns64 stride = t1b->nextOf()->size(be->loc); if (!t->equals(t2b)) - e2 = e2->castTo(sc, t); - eoff = e2; - e2 = new MulExp(loc, e2, new IntegerExp(Loc(), stride, t)); - e2->type = t; - type = e1->type; + be->e2 = be->e2->castTo(sc, t); + eoff = be->e2; + be->e2 = new MulExp(be->loc, be->e2, new IntegerExp(Loc(), stride, t)); + be->e2->type = t; + be->type = be->e1->type; } else if (t2b->ty == Tpointer && t1b->isintegral()) - { // Need to adjust operator by the stride + { + // Need to adjust operator by the stride // Replace (int + ptr) with (ptr + (int * stride)) Type *t = Type::tptrdiff_t; Expression *e; - stride = t2b->nextOf()->size(loc); + d_uns64 stride = t2b->nextOf()->size(be->loc); if (!t->equals(t1b)) - e = e1->castTo(sc, t); + e = be->e1->castTo(sc, t); else - e = e1; + e = be->e1; eoff = e; - e = new MulExp(loc, e, new IntegerExp(Loc(), stride, t)); + e = new MulExp(be->loc, e, new IntegerExp(Loc(), stride, t)); e->type = t; - type = e2->type; - e1 = e2; - e2 = e; + be->type = be->e2->type; + be->e1 = be->e2; + be->e2 = e; } else assert(0); @@ -2125,12 +2348,12 @@ Expression *BinExp::scaleFactor(Scope *sc) ; else if (sc->func->setUnsafe()) { - error("pointer arithmetic not allowed in @safe functions"); + be->error("pointer arithmetic not allowed in @safe functions"); return new ErrorExp(); } } - return this; + return be; } /************************************** @@ -2174,7 +2397,7 @@ bool isVoidArrayLiteral(Expression *e, Type *other) int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression **pe2) { //printf("typeMerge() %s op %s\n", (*pe1)->toChars(), (*pe2)->toChars()); - //e->dump(0); + //e->print(); MATCH m; Expression *e1 = *pe1; @@ -2185,8 +2408,8 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression if (e->op != TOKquestion || t1b->ty != t2b->ty && (t1b->isTypeBasic() && t2b->isTypeBasic())) { - e1 = e1->integralPromotions(sc); - e2 = e2->integralPromotions(sc); + e1 = integralPromotions(e1, sc); + e2 = integralPromotions(e2, sc); } Type *t1 = e1->type; @@ -2241,7 +2464,7 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression e1 = e1->castTo(sc, t1); e2 = e2->castTo(sc, t2); //printf("after typeCombine():\n"); - //dump(0); + //print(); //printf("ty = %d, ty1 = %d, ty2 = %d\n", ty, ty1, ty2); goto Lret; } @@ -2283,6 +2506,9 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression { TypeFunction *tf1 = (TypeFunction *)t1n; TypeFunction *tf2 = (TypeFunction *)t2n; + tf1->purityLevel(); + tf2->purityLevel(); + TypeFunction *d = (TypeFunction *)tf1->syntaxCopy(); if (tf1->purity != tf2->purity) @@ -2290,6 +2516,7 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression assert(d->purity != PUREfwdref); d->isnothrow = (tf1->isnothrow && tf2->isnothrow); + d->isnogc = (tf1->isnogc && tf2->isnogc); if (tf1->trust == tf2->trust) d->trust = tf1->trust; @@ -2328,7 +2555,8 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression goto Lagain; } else if (t1n->ty == Tclass && t2n->ty == Tclass) - { ClassDeclaration *cd1 = t1n->isClassHandle(); + { + ClassDeclaration *cd1 = t1n->isClassHandle(); ClassDeclaration *cd2 = t2n->isClassHandle(); int offset; @@ -2363,11 +2591,11 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression } else if ((t1->ty == Tsarray || t1->ty == Tarray) && (e2->op == TOKnull && t2->ty == Tpointer && t2->nextOf()->ty == Tvoid || - // if e2 is void[] e2->op == TOKarrayliteral && t2->ty == Tsarray && t2->nextOf()->ty == Tvoid && ((TypeSArray *)t2)->dim->toInteger() == 0 || isVoidArrayLiteral(e2, t1)) ) - { /* (T[n] op void*) => T[] + { + /* (T[n] op void*) => T[] * (T[] op void*) => T[] * (T[n] op void[0]) => T[] * (T[] op void[0]) => T[] @@ -2381,7 +2609,8 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression e1->op == TOKarrayliteral && t1->ty == Tsarray && t1->nextOf()->ty == Tvoid && ((TypeSArray *)t1)->dim->toInteger() == 0 || isVoidArrayLiteral(e1, t2)) ) - { /* (void* op T[n]) => T[] + { + /* (void* op T[n]) => T[] * (void* op T[]) => T[] * (void[0] op T[n]) => T[] * (void[0] op T[]) => T[] @@ -2400,7 +2629,8 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression e->op == TOKdivass || e->op == TOKmodass || e->op == TOKpowass || e->op == TOKandass || e->op == TOKorass || e->op == TOKxorass) ) - { // Don't make the lvalue const + { + // Don't make the lvalue const t = t2; goto Lret; } @@ -2412,14 +2642,14 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression goto Lt2; goto Lt1; } - /* If one is mutable and the other invariant, then retry - * with both of them as const - */ else if ((t1->ty == Tsarray || t1->ty == Tarray || t1->ty == Tpointer) && (t2->ty == Tsarray || t2->ty == Tarray || t2->ty == Tpointer) && t1->nextOf()->mod != t2->nextOf()->mod ) { + /* If one is mutable and the other invariant, then retry + * with both of them as const + */ Type *t1n = t1->nextOf(); Type *t2n = t2->nextOf(); unsigned char mod; @@ -2483,14 +2713,17 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression if (i2) { + e2 = e2->castTo(sc, t2); goto Lt2; } else if (i1) { + e1 = e1->castTo(sc, t1); goto Lt1; } else if (t1->ty == Tclass && t2->ty == Tclass) - { TypeClass *tc1 = (TypeClass *)t1; + { + TypeClass *tc1 = (TypeClass *)t1; TypeClass *tc2 = (TypeClass *)t2; /* Pick 'tightest' type @@ -2499,7 +2732,8 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression ClassDeclaration *cd2 = tc2->sym->baseClass; if (cd1 && cd2) - { t1 = cd1->type; + { + t1 = cd1->type; t2 = cd2->type; } else if (cd1) @@ -2589,11 +2823,13 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression goto Lt2; if (e1b) - { e1 = e1b; + { + e1 = e1b; t1 = e1b->type->toBasetype(); } if (e2b) - { e2 = e2b; + { + e2 = e2b; t2 = e2b->type->toBasetype(); } t = t1; @@ -2672,8 +2908,8 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression { if (t1->ty != t2->ty) { - e1 = e1->integralPromotions(sc); - e2 = e2->integralPromotions(sc); + e1 = integralPromotions(e1, sc); + e2 = integralPromotions(e2, sc); t1 = e1->type; t1b = t1->toBasetype(); t2 = e2->type; t2b = t2->toBasetype(); } @@ -2698,22 +2934,35 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression e2 = e2->castTo(sc, t); goto Lret; } - else if (e1->isArrayOperand() && t1->ty == Tarray && - e2->implicitConvTo(t1->nextOf())) - { // T[] op T + else if (t2->ty == Tnull && + (t1->ty == Tpointer || t1->ty == Taarray || t1->ty == Tarray)) + { + goto Lt1; + } + else if (t1->ty == Tnull && + (t2->ty == Tpointer || t2->ty == Taarray || t2->ty == Tarray)) + { + goto Lt2; + } + else if (e->op != TOKquestion && t1->ty == Tarray && + isArrayOperand(e1) && e2->implicitConvTo(t1->nextOf())) + { + // T[] op T e2 = e2->castTo(sc, t1->nextOf()); t = t1->nextOf()->arrayOf(); } - else if (e2->isArrayOperand() && t2->ty == Tarray && - e1->implicitConvTo(t2->nextOf())) - { // T op T[] + else if (e->op != TOKquestion && t2->ty == Tarray && + isArrayOperand(e2) && e1->implicitConvTo(t2->nextOf())) + { + // T op T[] e1 = e1->castTo(sc, t2->nextOf()); t = t2->nextOf()->arrayOf(); //printf("test %s\n", e->toChars()); e1 = e1->optimize(WANTvalue); - if (e && e->isCommutative() && e1->isConst()) - { /* Swap operands to minimize number of functions generated + if (e && isCommutative(e) && e1->isConst()) + { + /* Swap operands to minimize number of functions generated */ //printf("swap %s\n", e->toChars()); Expression *tmp = e1; @@ -2737,7 +2986,7 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression if (e2->type) printf("\tt2 = %s\n", e2->type->toChars()); printf("\ttype = %s\n", t->toChars()); #endif - //dump(0); + //print(); return 1; @@ -2754,14 +3003,15 @@ int typeMerge(Scope *sc, Expression *e, Type **pt, Expression **pe1, Expression /************************************ * Bring leaves to common type. + * Returns ErrorExp if error occurs. otherwise returns NULL. */ -Expression *BinExp::typeCombine(Scope *sc) +Expression *typeCombine(BinExp *be, Scope *sc) { - Type *t1 = e1->type->toBasetype(); - Type *t2 = e2->type->toBasetype(); + Type *t1 = be->e1->type->toBasetype(); + Type *t2 = be->e2->type->toBasetype(); - if (op == TOKmin || op == TOKadd) + if (be->op == TOKmin || be->op == TOKadd) { // struct+struct, and class+class are errors if (t1->ty == Tstruct && t2->ty == Tstruct) @@ -2772,20 +3022,19 @@ Expression *BinExp::typeCombine(Scope *sc) goto Lerror; } - if (!typeMerge(sc, this, &type, &e1, &e2)) + if (!typeMerge(sc, be, &be->type, &be->e1, &be->e2)) goto Lerror; // If the types have no value, return an error - if (e1->op == TOKerror) - return e1; - if (e2->op == TOKerror) - return e2; - return this; + if (be->e1->op == TOKerror) + return be->e1; + if (be->e2->op == TOKerror) + return be->e2; + return NULL; Lerror: - incompatibleTypes(); - type = Type::terror; - e1 = new ErrorExp(); - e2 = new ErrorExp(); + Expression *ex = be->incompatibleTypes(); + if (ex->op == TOKerror) + return ex; return new ErrorExp(); } @@ -2794,15 +3043,13 @@ Expression *BinExp::typeCombine(Scope *sc) * Don't convert to */ -Expression *Expression::integralPromotions(Scope *sc) +Expression *integralPromotions(Expression *e, Scope *sc) { - Expression *e = this; - //printf("integralPromotions %s %s\n", e->toChars(), e->type->toChars()); - switch (type->toBasetype()->ty) + switch (e->type->toBasetype()->ty) { case Tvoid: - error("void has no value"); + e->error("void has no value"); return new ErrorExp(); case Tint8: @@ -2872,7 +3119,6 @@ int arrayTypeCompatibleWithoutCasting(Loc loc, Type *t1, Type *t2) return 0; } - /******************************************************************/ /* Determine the integral ranges of an expression. @@ -2880,309 +3126,353 @@ int arrayTypeCompatibleWithoutCasting(Loc loc, Type *t1, Type *t2) * be allowed. */ -uinteger_t getMask(uinteger_t v) +IntRange getIntRange(Expression *e) { - // Ref: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v |= v >> 32; - return v /* | 0xff*/; -} -IntRange Expression::getIntRange() -{ - return IntRange::fromType(type); -} + class IntRangeVisitor : public Visitor + { + private: + static uinteger_t getMask(uinteger_t v) + { + // Ref: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + return v; + } + + // The algorithms for &, |, ^ are not yet the best! Sometimes they will produce + // not the tightest bound. See + // https://github.com/D-Programming-Language/dmd/pull/116 + // for detail. + static IntRange unsignedBitwiseAnd(const IntRange& a, const IntRange& b) + { + // the DiffMasks stores the mask of bits which are variable in the range. + uinteger_t aDiffMask = getMask(a.imin.value ^ a.imax.value); + uinteger_t bDiffMask = getMask(b.imin.value ^ b.imax.value); + // Since '&' computes the digitwise-minimum, the we could set all varying + // digits to 0 to get a lower bound, and set all varying digits to 1 to get + // an upper bound. + IntRange result; + result.imin.value = (a.imin.value & ~aDiffMask) & (b.imin.value & ~bDiffMask); + result.imax.value = (a.imax.value | aDiffMask) & (b.imax.value | bDiffMask); + // Sometimes the upper bound is overestimated. The upper bound will never + // exceed the input. + if (result.imax.value > a.imax.value) + result.imax.value = a.imax.value; + if (result.imax.value > b.imax.value) + result.imax.value = b.imax.value; + result.imin.negative = result.imax.negative = a.imin.negative && b.imin.negative; + return result; + } + static IntRange unsignedBitwiseOr(const IntRange& a, const IntRange& b) + { + // the DiffMasks stores the mask of bits which are variable in the range. + uinteger_t aDiffMask = getMask(a.imin.value ^ a.imax.value); + uinteger_t bDiffMask = getMask(b.imin.value ^ b.imax.value); + // The imax algorithm by Adam D. Ruppe. + // http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=108796 + IntRange result; + result.imin.value = (a.imin.value & ~aDiffMask) | (b.imin.value & ~bDiffMask); + result.imax.value = a.imax.value | b.imax.value | getMask(a.imax.value & b.imax.value); + // Sometimes the lower bound is underestimated. The lower bound will never + // less than the input. + if (result.imin.value < a.imin.value) + result.imin.value = a.imin.value; + if (result.imin.value < b.imin.value) + result.imin.value = b.imin.value; + result.imin.negative = result.imax.negative = a.imin.negative || b.imin.negative; + return result; + } + static IntRange unsignedBitwiseXor(const IntRange& a, const IntRange& b) + { + // the DiffMasks stores the mask of bits which are variable in the range. + uinteger_t aDiffMask = getMask(a.imin.value ^ a.imax.value); + uinteger_t bDiffMask = getMask(b.imin.value ^ b.imax.value); + IntRange result; + result.imin.value = (a.imin.value ^ b.imin.value) & ~(aDiffMask | bDiffMask); + result.imax.value = (a.imax.value ^ b.imax.value) | (aDiffMask | bDiffMask); + result.imin.negative = result.imax.negative = a.imin.negative != b.imin.negative; + return result; + } -IntRange IntegerExp::getIntRange() -{ - return IntRange(SignExtendedNumber(value)).cast(type); -} + public: + IntRange range; -IntRange CastExp::getIntRange() -{ - return e1->getIntRange().cast(type); -} + void visit(Expression *e) + { + range = IntRange::fromType(e->type); + } -IntRange AddExp::getIntRange() -{ - IntRange ir1 = e1->getIntRange(); - IntRange ir2 = e2->getIntRange(); - return IntRange(ir1.imin + ir2.imin, ir1.imax + ir2.imax).cast(type); -} + void visit(IntegerExp *e) + { + range = IntRange(SignExtendedNumber(e->getInteger())).cast(e->type); + } -IntRange MinExp::getIntRange() -{ - IntRange ir1 = e1->getIntRange(); - IntRange ir2 = e2->getIntRange(); - return IntRange(ir1.imin - ir2.imax, ir1.imax - ir2.imin).cast(type); -} + void visit(CastExp *e) + { + range = getIntRange(e->e1).cast(e->type); + } -IntRange DivExp::getIntRange() -{ - IntRange ir1 = e1->getIntRange(); - IntRange ir2 = e2->getIntRange(); - - // Should we ignore the possibility of div-by-0??? - if (ir2.containsZero()) - return Expression::getIntRange(); - - // [a,b] / [c,d] = [min (a/c, a/d, b/c, b/d), max (a/c, a/d, b/c, b/d)] - SignExtendedNumber bdy[4]; - bdy[0] = ir1.imin / ir2.imin; - bdy[1] = ir1.imin / ir2.imax; - bdy[2] = ir1.imax / ir2.imin; - bdy[3] = ir1.imax / ir2.imax; - return IntRange::fromNumbers4(bdy).cast(type); -} + void visit(AddExp *e) + { + IntRange ir1 = getIntRange(e->e1); + IntRange ir2 = getIntRange(e->e2); + range = IntRange(ir1.imin + ir2.imin, ir1.imax + ir2.imax).cast(e->type); + } -IntRange MulExp::getIntRange() -{ - IntRange ir1 = e1->getIntRange(); - IntRange ir2 = e2->getIntRange(); - - // [a,b] * [c,d] = [min (ac, ad, bc, bd), max (ac, ad, bc, bd)] - SignExtendedNumber bdy[4]; - bdy[0] = ir1.imin * ir2.imin; - bdy[1] = ir1.imin * ir2.imax; - bdy[2] = ir1.imax * ir2.imin; - bdy[3] = ir1.imax * ir2.imax; - return IntRange::fromNumbers4(bdy).cast(type); -} + void visit(MinExp *e) + { + IntRange ir1 = getIntRange(e->e1); + IntRange ir2 = getIntRange(e->e2); + range = IntRange(ir1.imin - ir2.imax, ir1.imax - ir2.imin).cast(e->type); + } -IntRange ModExp::getIntRange() -{ - IntRange irNum = e1->getIntRange(); - IntRange irDen = e2->getIntRange().absNeg(); + void visit(DivExp *e) + { + IntRange ir1 = getIntRange(e->e1); + IntRange ir2 = getIntRange(e->e2); + + // Should we ignore the possibility of div-by-0??? + if (ir2.containsZero()) + { + visit((Expression *)e); + return; + } + + // [a,b] / [c,d] = [min (a/c, a/d, b/c, b/d), max (a/c, a/d, b/c, b/d)] + SignExtendedNumber bdy[4]; + bdy[0] = ir1.imin / ir2.imin; + bdy[1] = ir1.imin / ir2.imax; + bdy[2] = ir1.imax / ir2.imin; + bdy[3] = ir1.imax / ir2.imax; + range = IntRange::fromNumbers4(bdy).cast(e->type); + } + + void visit(MulExp *e) + { + IntRange ir1 = getIntRange(e->e1); + IntRange ir2 = getIntRange(e->e2); - /* - due to the rules of D (C)'s % operator, we need to consider the cases - separately in different range of signs. + // [a,b] * [c,d] = [min (ac, ad, bc, bd), max (ac, ad, bc, bd)] + SignExtendedNumber bdy[4]; + bdy[0] = ir1.imin * ir2.imin; + bdy[1] = ir1.imin * ir2.imax; + bdy[2] = ir1.imax * ir2.imin; + bdy[3] = ir1.imax * ir2.imax; + range = IntRange::fromNumbers4(bdy).cast(e->type); + } - case 1. [500, 1700] % [7, 23] (numerator is always positive) - = [0, 22] - case 2. [-500, 1700] % [7, 23] (numerator can be negative) - = [-22, 22] - case 3. [-1700, -500] % [7, 23] (numerator is always negative) - = [-22, 0] + void visit(ModExp *e) + { + IntRange irNum = getIntRange(e->e1); + IntRange irDen = getIntRange(e->e2).absNeg(); + + /* + due to the rules of D (C)'s % operator, we need to consider the cases + separately in different range of signs. + + case 1. [500, 1700] % [7, 23] (numerator is always positive) + = [0, 22] + case 2. [-500, 1700] % [7, 23] (numerator can be negative) + = [-22, 22] + case 3. [-1700, -500] % [7, 23] (numerator is always negative) + = [-22, 0] + + the number 22 is the maximum absolute value in the denomator's range. We + don't care about divide by zero. + */ + + // Modding on 0 is invalid anyway. + if (!irDen.imin.negative) + { + visit((Expression *)e); + return; + } - the number 22 is the maximum absolute value in the denomator's range. We - don't care about divide by zero. - */ + ++ irDen.imin; + irDen.imax = -irDen.imin; - // Modding on 0 is invalid anyway. - if (!irDen.imin.negative) - return Expression::getIntRange(); + if (!irNum.imin.negative) + irNum.imin.value = 0; + else if (irNum.imin < irDen.imin) + irNum.imin = irDen.imin; - ++ irDen.imin; - irDen.imax = -irDen.imin; + if (irNum.imax.negative) + { + irNum.imax.negative = false; + irNum.imax.value = 0; + } + else if (irNum.imax > irDen.imax) + irNum.imax = irDen.imax; - if (!irNum.imin.negative) - irNum.imin.value = 0; - else if (irNum.imin < irDen.imin) - irNum.imin = irDen.imin; + range = irNum.cast(e->type); + } - if (irNum.imax.negative) - { - irNum.imax.negative = false; - irNum.imax.value = 0; - } - else if (irNum.imax > irDen.imax) - irNum.imax = irDen.imax; + void visit(AndExp *e) + { + IntRange ir1 = getIntRange(e->e1); + IntRange ir2 = getIntRange(e->e2); + + IntRange ir1neg, ir1pos, ir2neg, ir2pos; + bool has1neg, has1pos, has2neg, has2pos; + + ir1.splitBySign(ir1neg, has1neg, ir1pos, has1pos); + ir2.splitBySign(ir2neg, has2neg, ir2pos, has2pos); + + IntRange result; + bool hasResult = false; + if (has1pos && has2pos) + result.unionOrAssign(unsignedBitwiseAnd(ir1pos, ir2pos), hasResult); + if (has1pos && has2neg) + result.unionOrAssign(unsignedBitwiseAnd(ir1pos, ir2neg), hasResult); + if (has1neg && has2pos) + result.unionOrAssign(unsignedBitwiseAnd(ir1neg, ir2pos), hasResult); + if (has1neg && has2neg) + result.unionOrAssign(unsignedBitwiseAnd(ir1neg, ir2neg), hasResult); + assert(hasResult); + range = result.cast(e->type); + } + + void visit(OrExp *e) + { + IntRange ir1 = getIntRange(e->e1); + IntRange ir2 = getIntRange(e->e2); - return irNum.cast(type); -} + IntRange ir1neg, ir1pos, ir2neg, ir2pos; + bool has1neg, has1pos, has2neg, has2pos; -// The algorithms for &, |, ^ are not yet the best! Sometimes they will produce -// not the tightest bound. See -// https://github.com/D-Programming-Language/dmd/pull/116 -// for detail. -static IntRange unsignedBitwiseAnd(const IntRange& a, const IntRange& b) -{ - // the DiffMasks stores the mask of bits which are variable in the range. - uinteger_t aDiffMask = getMask(a.imin.value ^ a.imax.value); - uinteger_t bDiffMask = getMask(b.imin.value ^ b.imax.value); - // Since '&' computes the digitwise-minimum, the we could set all varying - // digits to 0 to get a lower bound, and set all varying digits to 1 to get - // an upper bound. - IntRange result; - result.imin.value = (a.imin.value & ~aDiffMask) & (b.imin.value & ~bDiffMask); - result.imax.value = (a.imax.value | aDiffMask) & (b.imax.value | bDiffMask); - // Sometimes the upper bound is overestimated. The upper bound will never - // exceed the input. - if (result.imax.value > a.imax.value) - result.imax.value = a.imax.value; - if (result.imax.value > b.imax.value) - result.imax.value = b.imax.value; - result.imin.negative = result.imax.negative = a.imin.negative && b.imin.negative; - return result; -} -static IntRange unsignedBitwiseOr(const IntRange& a, const IntRange& b) -{ - // the DiffMasks stores the mask of bits which are variable in the range. - uinteger_t aDiffMask = getMask(a.imin.value ^ a.imax.value); - uinteger_t bDiffMask = getMask(b.imin.value ^ b.imax.value); - // The imax algorithm by Adam D. Ruppe. - // http://www.digitalmars.com/pnews/read.php?server=news.digitalmars.com&group=digitalmars.D&artnum=108796 - IntRange result; - result.imin.value = (a.imin.value & ~aDiffMask) | (b.imin.value & ~bDiffMask); - result.imax.value = a.imax.value | b.imax.value | getMask(a.imax.value & b.imax.value); - // Sometimes the lower bound is underestimated. The lower bound will never - // less than the input. - if (result.imin.value < a.imin.value) - result.imin.value = a.imin.value; - if (result.imin.value < b.imin.value) - result.imin.value = b.imin.value; - result.imin.negative = result.imax.negative = a.imin.negative || b.imin.negative; - return result; -} -static IntRange unsignedBitwiseXor(const IntRange& a, const IntRange& b) -{ - // the DiffMasks stores the mask of bits which are variable in the range. - uinteger_t aDiffMask = getMask(a.imin.value ^ a.imax.value); - uinteger_t bDiffMask = getMask(b.imin.value ^ b.imax.value); - IntRange result; - result.imin.value = (a.imin.value ^ b.imin.value) & ~(aDiffMask | bDiffMask); - result.imax.value = (a.imax.value ^ b.imax.value) | (aDiffMask | bDiffMask); - result.imin.negative = result.imax.negative = a.imin.negative != b.imin.negative; - return result; -} + ir1.splitBySign(ir1neg, has1neg, ir1pos, has1pos); + ir2.splitBySign(ir2neg, has2neg, ir2pos, has2pos); + IntRange result; + bool hasResult = false; + if (has1pos && has2pos) + result.unionOrAssign(unsignedBitwiseOr(ir1pos, ir2pos), hasResult); + if (has1pos && has2neg) + result.unionOrAssign(unsignedBitwiseOr(ir1pos, ir2neg), hasResult); + if (has1neg && has2pos) + result.unionOrAssign(unsignedBitwiseOr(ir1neg, ir2pos), hasResult); + if (has1neg && has2neg) + result.unionOrAssign(unsignedBitwiseOr(ir1neg, ir2neg), hasResult); -IntRange AndExp::getIntRange() -{ - IntRange ir1 = e1->getIntRange(); - IntRange ir2 = e2->getIntRange(); - - IntRange ir1neg, ir1pos, ir2neg, ir2pos; - bool has1neg, has1pos, has2neg, has2pos; - - ir1.splitBySign(ir1neg, has1neg, ir1pos, has1pos); - ir2.splitBySign(ir2neg, has2neg, ir2pos, has2pos); - - IntRange result; - bool hasResult = false; - if (has1pos && has2pos) - result.unionOrAssign(unsignedBitwiseAnd(ir1pos, ir2pos), /*ref*/hasResult); - if (has1pos && has2neg) - result.unionOrAssign(unsignedBitwiseAnd(ir1pos, ir2neg), /*ref*/hasResult); - if (has1neg && has2pos) - result.unionOrAssign(unsignedBitwiseAnd(ir1neg, ir2pos), /*ref*/hasResult); - if (has1neg && has2neg) - result.unionOrAssign(unsignedBitwiseAnd(ir1neg, ir2neg), /*ref*/hasResult); - assert(hasResult); - return result.cast(type); -} + assert(hasResult); + range = result.cast(e->type); + } -IntRange OrExp::getIntRange() -{ - IntRange ir1 = e1->getIntRange(); - IntRange ir2 = e2->getIntRange(); - - IntRange ir1neg, ir1pos, ir2neg, ir2pos; - bool has1neg, has1pos, has2neg, has2pos; - - ir1.splitBySign(ir1neg, has1neg, ir1pos, has1pos); - ir2.splitBySign(ir2neg, has2neg, ir2pos, has2pos); - - IntRange result; - bool hasResult = false; - if (has1pos && has2pos) - result.unionOrAssign(unsignedBitwiseOr(ir1pos, ir2pos), /*ref*/hasResult); - if (has1pos && has2neg) - result.unionOrAssign(unsignedBitwiseOr(ir1pos, ir2neg), /*ref*/hasResult); - if (has1neg && has2pos) - result.unionOrAssign(unsignedBitwiseOr(ir1neg, ir2pos), /*ref*/hasResult); - if (has1neg && has2neg) - result.unionOrAssign(unsignedBitwiseOr(ir1neg, ir2neg), /*ref*/hasResult); - - assert(hasResult); - return result.cast(type); -} + void visit(XorExp *e) + { + IntRange ir1 = getIntRange(e->e1); + IntRange ir2 = getIntRange(e->e2); -IntRange XorExp::getIntRange() -{ - IntRange ir1 = e1->getIntRange(); - IntRange ir2 = e2->getIntRange(); - - IntRange ir1neg, ir1pos, ir2neg, ir2pos; - bool has1neg, has1pos, has2neg, has2pos; - - ir1.splitBySign(ir1neg, has1neg, ir1pos, has1pos); - ir2.splitBySign(ir2neg, has2neg, ir2pos, has2pos); - - IntRange result; - bool hasResult = false; - if (has1pos && has2pos) - result.unionOrAssign(unsignedBitwiseXor(ir1pos, ir2pos), /*ref*/hasResult); - if (has1pos && has2neg) - result.unionOrAssign(unsignedBitwiseXor(ir1pos, ir2neg), /*ref*/hasResult); - if (has1neg && has2pos) - result.unionOrAssign(unsignedBitwiseXor(ir1neg, ir2pos), /*ref*/hasResult); - if (has1neg && has2neg) - result.unionOrAssign(unsignedBitwiseXor(ir1neg, ir2neg), /*ref*/hasResult); - - assert(hasResult); - return result.cast(type); -} + IntRange ir1neg, ir1pos, ir2neg, ir2pos; + bool has1neg, has1pos, has2neg, has2pos; -IntRange ShlExp::getIntRange() -{ - IntRange ir1 = e1->getIntRange(); - IntRange ir2 = e2->getIntRange(); + ir1.splitBySign(ir1neg, has1neg, ir1pos, has1pos); + ir2.splitBySign(ir2neg, has2neg, ir2pos, has2pos); + + IntRange result; + bool hasResult = false; + if (has1pos && has2pos) + result.unionOrAssign(unsignedBitwiseXor(ir1pos, ir2pos), hasResult); + if (has1pos && has2neg) + result.unionOrAssign(unsignedBitwiseXor(ir1pos, ir2neg), hasResult); + if (has1neg && has2pos) + result.unionOrAssign(unsignedBitwiseXor(ir1neg, ir2pos), hasResult); + if (has1neg && has2neg) + result.unionOrAssign(unsignedBitwiseXor(ir1neg, ir2neg), hasResult); - if (ir2.imin.negative) - ir2 = IntRange(SignExtendedNumber(0), SignExtendedNumber(64)); + assert(hasResult); + range = result.cast(e->type); + } - SignExtendedNumber lower = ir1.imin << (ir1.imin.negative ? ir2.imax : ir2.imin); - SignExtendedNumber upper = ir1.imax << (ir1.imax.negative ? ir2.imin : ir2.imax); + void visit(ShlExp *e) + { + IntRange ir1 = getIntRange(e->e1); + IntRange ir2 = getIntRange(e->e2); - return IntRange(lower, upper).cast(type); -} + if (ir2.imin.negative) + ir2 = IntRange(SignExtendedNumber(0), SignExtendedNumber(64)); -IntRange ShrExp::getIntRange() -{ - IntRange ir1 = e1->getIntRange(); - IntRange ir2 = e2->getIntRange(); + SignExtendedNumber lower = ir1.imin << (ir1.imin.negative ? ir2.imax : ir2.imin); + SignExtendedNumber upper = ir1.imax << (ir1.imax.negative ? ir2.imin : ir2.imax); - if (ir2.imin.negative) - ir2 = IntRange(SignExtendedNumber(0), SignExtendedNumber(64)); + range = IntRange(lower, upper).cast(e->type); + } - SignExtendedNumber lower = ir1.imin >> (ir1.imin.negative ? ir2.imin : ir2.imax); - SignExtendedNumber upper = ir1.imax >> (ir1.imax.negative ? ir2.imax : ir2.imin); + void visit(ShrExp *e) + { + IntRange ir1 = getIntRange(e->e1); + IntRange ir2 = getIntRange(e->e2); - return IntRange(lower, upper).cast(type); -} + if (ir2.imin.negative) + ir2 = IntRange(SignExtendedNumber(0), SignExtendedNumber(64)); -IntRange UshrExp::getIntRange() -{ - IntRange ir1 = e1->getIntRange().castUnsigned(e1->type); - IntRange ir2 = e2->getIntRange(); + SignExtendedNumber lower = ir1.imin >> (ir1.imin.negative ? ir2.imin : ir2.imax); + SignExtendedNumber upper = ir1.imax >> (ir1.imax.negative ? ir2.imax : ir2.imin); - if (ir2.imin.negative) - ir2 = IntRange(SignExtendedNumber(0), SignExtendedNumber(64)); + range = IntRange(lower, upper).cast(e->type); + } - return IntRange(ir1.imin >> ir2.imax, ir1.imax >> ir2.imin).cast(type); + void visit(UshrExp *e) + { + IntRange ir1 = getIntRange(e->e1).castUnsigned(e->e1->type); + IntRange ir2 = getIntRange(e->e2); -} + if (ir2.imin.negative) + ir2 = IntRange(SignExtendedNumber(0), SignExtendedNumber(64)); -IntRange CommaExp::getIntRange() -{ - return e2->getIntRange(); -} + range = IntRange(ir1.imin >> ir2.imax, ir1.imax >> ir2.imin).cast(e->type); + } -IntRange ComExp::getIntRange() -{ - IntRange ir = e1->getIntRange(); - return IntRange(SignExtendedNumber(~ir.imax.value, !ir.imax.negative), - SignExtendedNumber(~ir.imin.value, !ir.imin.negative)).cast(type); -} + void visit(AssignExp *e) + { + range = getIntRange(e->e2).cast(e->type); + } -IntRange NegExp::getIntRange() -{ - IntRange ir = e1->getIntRange(); - return IntRange(-ir.imax, -ir.imin).cast(type); -} + void visit(CondExp *e) + { + // No need to check e->econd; assume caller has called optimize() + IntRange ir1 = getIntRange(e->e1); + IntRange ir2 = getIntRange(e->e2); + range = ir1.unionWith(ir2).cast(e->type); + } + + void visit(VarExp *e) + { + Expression *ie; + VarDeclaration* vd = e->var->isVarDeclaration(); + if (vd && vd->range) + range = vd->range->cast(e->type); + else if (vd && vd->init && !vd->type->isMutable() && + (ie = vd->getConstInitializer()) != NULL) + ie->accept(this); + else + visit((Expression *)e); + } + + void visit(CommaExp *e) + { + e->e2->accept(this); + } + + void visit(ComExp *e) + { + IntRange ir = getIntRange(e->e1); + range = IntRange(SignExtendedNumber(~ir.imax.value, !ir.imax.negative), + SignExtendedNumber(~ir.imin.value, !ir.imin.negative)).cast(e->type); + } + void visit(NegExp *e) + { + IntRange ir = getIntRange(e->e1); + range = IntRange(-ir.imax, -ir.imin).cast(e->type); + } + }; + + IntRangeVisitor v; + e->accept(&v); + return v.range; +} diff --git a/gcc/d/dfrontend/class.c b/gcc/d/dfrontend/class.c index 7a0197597..b8df92cab 100644 --- a/gcc/d/dfrontend/class.c +++ b/gcc/d/dfrontend/class.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/class.c + */ #include #include @@ -43,8 +44,10 @@ ClassDeclaration::ClassDeclaration(Loc loc, Identifier *id, BaseClasses *basecla static const char msg[] = "only object.d can define this reserved class name"; if (baseclasses) + { // Actually, this is a transfer this->baseclasses = baseclasses; + } else this->baseclasses = new BaseClasses(); baseClass = NULL; @@ -58,7 +61,6 @@ ClassDeclaration::ClassDeclaration(Loc loc, Identifier *id, BaseClasses *basecla // For forward references type = new TypeClass(this); - handle = type; staticCtor = NULL; staticDtor = NULL; @@ -67,7 +69,8 @@ ClassDeclaration::ClassDeclaration(Loc loc, Identifier *id, BaseClasses *basecla vclassinfo = NULL; if (id) - { // Look for special class names + { + // Look for special class names if (id == Id::__sizeof || id == Id::__xalignof || id == Id::mangleof) error("illegal class name"); @@ -76,143 +79,158 @@ ClassDeclaration::ClassDeclaration(Loc loc, Identifier *id, BaseClasses *basecla if (id->toChars()[0] == 'T') { if (id == Id::TypeInfo) - { if (!inObject) + { + if (!inObject) error("%s", msg); Type::dtypeinfo = this; } if (id == Id::TypeInfo_Class) - { if (!inObject) + { + if (!inObject) error("%s", msg); Type::typeinfoclass = this; } if (id == Id::TypeInfo_Interface) - { if (!inObject) + { + if (!inObject) error("%s", msg); Type::typeinfointerface = this; } if (id == Id::TypeInfo_Struct) - { if (!inObject) + { + if (!inObject) error("%s", msg); Type::typeinfostruct = this; } - if (id == Id::TypeInfo_Typedef) - { if (!inObject) - error("%s", msg); - Type::typeinfotypedef = this; - } - if (id == Id::TypeInfo_Pointer) - { if (!inObject) + { + if (!inObject) error("%s", msg); Type::typeinfopointer = this; } if (id == Id::TypeInfo_Array) - { if (!inObject) + { + if (!inObject) error("%s", msg); Type::typeinfoarray = this; } if (id == Id::TypeInfo_StaticArray) - { //if (!inObject) - //Type::typeinfostaticarray->error("%s", msg); + { + //if (!inObject) + // Type::typeinfostaticarray->error("%s", msg); Type::typeinfostaticarray = this; } if (id == Id::TypeInfo_AssociativeArray) - { if (!inObject) + { + if (!inObject) error("%s", msg); Type::typeinfoassociativearray = this; } if (id == Id::TypeInfo_Enum) - { if (!inObject) + { + if (!inObject) error("%s", msg); Type::typeinfoenum = this; } if (id == Id::TypeInfo_Function) - { if (!inObject) + { + if (!inObject) error("%s", msg); Type::typeinfofunction = this; } if (id == Id::TypeInfo_Delegate) - { if (!inObject) + { + if (!inObject) error("%s", msg); Type::typeinfodelegate = this; } if (id == Id::TypeInfo_Tuple) - { if (!inObject) + { + if (!inObject) error("%s", msg); Type::typeinfotypelist = this; } if (id == Id::TypeInfo_Const) - { if (!inObject) + { + if (!inObject) error("%s", msg); Type::typeinfoconst = this; } if (id == Id::TypeInfo_Invariant) - { if (!inObject) + { + if (!inObject) error("%s", msg); Type::typeinfoinvariant = this; } if (id == Id::TypeInfo_Shared) - { if (!inObject) + { + if (!inObject) error("%s", msg); Type::typeinfoshared = this; } if (id == Id::TypeInfo_Wild) - { if (!inObject) + { + if (!inObject) error("%s", msg); Type::typeinfowild = this; } if (id == Id::TypeInfo_Vector) - { if (!inObject) + { + if (!inObject) error("%s", msg); Type::typeinfovector = this; } } if (id == Id::Object) - { if (!inObject) + { + if (!inObject) error("%s", msg); object = this; } if (id == Id::Throwable) - { if (!inObject) + { + if (!inObject) error("%s", msg); throwable = this; } if (id == Id::Exception) - { if (!inObject) + { + if (!inObject) error("%s", msg); exception = this; } if (id == Id::Error) - { if (!inObject) + { + if (!inObject) error("%s", msg); errorException = this; } } - com = 0; - cpp = 0; - isscope = 0; - isabstract = 0; + com = false; + cpp = false; + isscope = false; + isabstract = false; inuse = 0; doAncestorsSemantic = SemanticStart; } @@ -249,32 +267,12 @@ void ClassDeclaration::semantic(Scope *sc) //{ static int n; if (++n == 20) *(char*)0=0; } - if (!ident) // if anonymous class - { const char *id = "__anonclass"; - - ident = Identifier::generateId(id); - } - - if (!sc) - sc = scope; - if (!parent && sc->parent && !sc->parent->isModule()) - parent = sc->parent; - - type = type->semantic(loc, sc); - handle = type; - - if (!members) // if opaque declaration - { //printf("\tclass '%s' is forward referenced\n", toChars()); + if (semanticRun >= PASSsemanticdone) return; - } - if (symtab) - { if (sizeok == SIZEOKdone || !scope) - { //printf("\tsemantic for '%s' is already completed\n", toChars()); - return; // semantic() already completed - } - } - else - symtab = new DsymbolTable(); + unsigned dprogress_save = Module::dprogress; + int errors = global.errors; + + //printf("+ClassDeclaration::semantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this); Scope *scx = NULL; if (scope) @@ -283,62 +281,116 @@ void ClassDeclaration::semantic(Scope *sc) scx = scope; // save so we don't make redundant copies scope = NULL; } - unsigned dprogress_save = Module::dprogress; - int errors = global.errors; - if (sc->stc & STCdeprecated) + if (!parent) { - isdeprecated = true; + assert(sc->parent && (sc->func || !ident)); + parent = sc->parent; + + if (!ident) // if anonymous class + { + const char *id = "__anonclass"; + ident = Identifier::generateId(id); + } } - userAttribDecl = sc->userAttribDecl; + assert(parent && !isAnonymous()); + type = type->semantic(loc, sc); - if (sc->linkage == LINKcpp) - cpp = 1; + if (type->ty == Tclass && ((TypeClass *)type)->sym != this) + { + TemplateInstance *ti = ((TypeClass *)type)->sym->isInstantiated(); + if (ti && isError(ti)) + ((TypeClass *)type)->sym = this; + } + + // Ungag errors when not speculative + Ungag ungag = ungagSpeculative(); - // Expand any tuples in baseclasses[] - for (size_t i = 0; i < baseclasses->dim; ) + if (semanticRun == PASSinit) { - // Ungag errors when not speculative - Ungag ungag = ungagSpeculative(); + protection = sc->protection; - BaseClass *b = (*baseclasses)[i]; - b->type = b->type->semantic(loc, sc); + storage_class |= sc->stc; + if (storage_class & STCdeprecated) + isdeprecated = true; + if (storage_class & STCauto) + error("storage class 'auto' is invalid when declaring a class, did you mean to use 'scope'?"); + if (storage_class & STCscope) + isscope = true; + if (storage_class & STCabstract) + isabstract = true; - Type *tb = b->type->toBasetype(); - if (tb->ty == Ttuple) - { TypeTuple *tup = (TypeTuple *)tb; - PROT protection = b->protection; - baseclasses->remove(i); - size_t dim = Parameter::dim(tup->arguments); - for (size_t j = 0; j < dim; j++) - { Parameter *arg = Parameter::getNth(tup->arguments, j); - b = new BaseClass(arg->type, protection); - baseclasses->insert(i + j, b); - } + userAttribDecl = sc->userAttribDecl; + + if (sc->linkage == LINKcpp) + cpp = true; + } + else if (symtab) + { + if (sizeok == SIZEOKdone || !scx) + { + semanticRun = PASSsemanticdone; + return; } - else - i++; } + semanticRun = PASSsemantic; - // See if there's a base class as first in baseclasses[] - if (baseclasses->dim) + if (doAncestorsSemantic != SemanticDone) { - // Ungag errors when not speculative - Ungag ungag = ungagSpeculative(); + doAncestorsSemantic = SemanticIn; - BaseClass *b = (*baseclasses)[0]; - //b->type = b->type->semantic(loc, sc); + // Expand any tuples in baseclasses[] + for (size_t i = 0; i < baseclasses->dim; ) + { + scope = scx ? scx : sc->copy(); + scope->setNoFree(); - Type *tb = b->type->toBasetype(); - if (tb->ty != Tclass) + BaseClass *b = (*baseclasses)[i]; + //printf("+ %s [%d] b->type = %s\n", toChars(), i, b->type->toChars()); + b->type = b->type->semantic(loc, sc); + //printf("- %s [%d] b->type = %s\n", toChars(), i, b->type->toChars()); + + scope = NULL; + + Type *tb = b->type->toBasetype(); + if (tb->ty == Ttuple) + { + TypeTuple *tup = (TypeTuple *)tb; + PROT protection = b->protection; + baseclasses->remove(i); + size_t dim = Parameter::dim(tup->arguments); + for (size_t j = 0; j < dim; j++) + { + Parameter *arg = Parameter::getNth(tup->arguments, j); + b = new BaseClass(arg->type, protection); + baseclasses->insert(i + j, b); + } + } + else + i++; + } + + if (doAncestorsSemantic == SemanticDone) { - if (b->type != Type::terror) - error("base type must be class or interface, not %s", b->type->toChars()); - baseclasses->remove(0); + //printf("%s already semantic analyzed, semanticRun = %d\n", toChars(), semanticRun); + if (semanticRun >= PASSsemanticdone) + return; + goto Lancestorsdone; } - else + + // See if there's a base class as first in baseclasses[] + if (baseclasses->dim) { - TypeClass *tc = (TypeClass *)(tb); + BaseClass *b = (*baseclasses)[0]; + Type *tb = b->type->toBasetype(); + TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : NULL; + if (!tc) + { + if (b->type != Type::terror) + error("base type must be class or interface, not %s", b->type->toChars()); + baseclasses->remove(0); + goto L7; + } if (tc->sym->isDeprecated()) { @@ -352,82 +404,51 @@ void ClassDeclaration::semantic(Scope *sc) } if (tc->sym->isInterfaceDeclaration()) - ; - else - { - for (ClassDeclaration *cdb = tc->sym; cdb; cdb = cdb->baseClass) - { - if (cdb == this) - { - error("circular inheritance"); - baseclasses->remove(0); - goto L7; - } - } - if (tc->sym->scope) - { - // Try to resolve forward reference - tc->sym->semantic(NULL); - } + goto L7; - if (tc->sym->symtab && tc->sym->scope == NULL) - { - /* Bugzilla 11034: Essentailly, class inheritance hierarchy - * and instance size of each classes are orthogonal information. - * Therefore, even if tc->sym->sizeof == SIZEOKnone, - * we need to set baseClass field for class covariance check. - */ - baseClass = tc->sym; - b->base = baseClass; - } - if (!tc->sym->symtab || tc->sym->scope || tc->sym->sizeok == SIZEOKnone) + for (ClassDeclaration *cdb = tc->sym; cdb; cdb = cdb->baseClass) + { + if (cdb == this) { - //printf("%s: forward reference of base class %s\n", toChars(), tc->sym->toChars()); - //error("forward reference of base class %s", baseClass->toChars()); - // Forward reference of base class, try again later - //printf("\ttry later, forward reference of base class %s\n", tc->sym->toChars()); - scope = scx ? scx : new Scope(*sc); - scope->setNoFree(); - if (tc->sym->scope) - tc->sym->scope->module->addDeferredSemantic(tc->sym); - scope->module->addDeferredSemantic(this); - return; + error("circular inheritance"); + baseclasses->remove(0); + goto L7; } - L7: ; } - } - } - - // Treat the remaining entries in baseclasses as interfaces - // Check for errors, handle forward references - for (size_t i = (baseClass ? 1 : 0); i < baseclasses->dim; ) - { - // Ungag errors when not speculative - Ungag ungag = ungagSpeculative(); - BaseClass *b = (*baseclasses)[i]; - b->type = b->type->semantic(loc, sc); + /* Bugzilla 11034: Essentially, class inheritance hierarchy + * and instance size of each classes are orthogonal information. + * Therefore, even if tc->sym->sizeof == SIZEOKnone, + * we need to set baseClass field for class covariance check. + */ + baseClass = tc->sym; + b->base = baseClass; - Type *tb = b->type->toBasetype(); - TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : NULL; - if (!tc || !tc->sym->isInterfaceDeclaration()) - { - if (b->type != Type::terror) - error("base type must be interface, not %s", b->type->toChars()); - baseclasses->remove(i); - continue; + if (tc->sym->scope && tc->sym->doAncestorsSemantic != SemanticDone) + tc->sym->semantic(NULL); // Try to resolve forward reference + if (tc->sym->doAncestorsSemantic != SemanticDone) + { + //printf("\ttry later, forward reference of base class %s\n", tc->sym->toChars()); + if (tc->sym->scope) + tc->sym->scope->module->addDeferredSemantic(tc->sym); + doAncestorsSemantic = SemanticStart; + } + L7: ; } - else + + // Treat the remaining entries in baseclasses as interfaces + // Check for errors, handle forward references + for (size_t i = (baseClass ? 1 : 0); i < baseclasses->dim; ) { - if (tc->sym->isDeprecated()) + BaseClass *b = (*baseclasses)[i]; + Type *tb = b->type->toBasetype(); + TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : NULL; + if (!tc || !tc->sym->isInterfaceDeclaration()) { - if (!isDeprecated()) - { - // Deriving from deprecated class makes this one deprecated too - isdeprecated = true; - - tc->checkDeprecated(loc, sc); - } + if (b->type != Type::terror) + error("base type must be interface, not %s", b->type->toChars()); + baseclasses->remove(i); + continue; } // Check for duplicate interfaces @@ -435,37 +456,48 @@ void ClassDeclaration::semantic(Scope *sc) { BaseClass *b2 = (*baseclasses)[j]; if (b2->base == tc->sym) + { error("inherits from duplicate interface %s", b2->base->toChars()); + baseclasses->remove(i); + continue; + } } - if (tc->sym->scope) + if (tc->sym->isDeprecated()) { - // Try to resolve forward reference - tc->sym->semantic(NULL); + if (!isDeprecated()) + { + // Deriving from deprecated class makes this one deprecated too + isdeprecated = true; + + tc->checkDeprecated(loc, sc); + } } b->base = tc->sym; - if (!b->base->symtab || b->base->scope) + + if (tc->sym->scope && tc->sym->doAncestorsSemantic != SemanticDone) + tc->sym->semantic(NULL); // Try to resolve forward reference + if (tc->sym->doAncestorsSemantic != SemanticDone) { - //error("forward reference of base class %s", baseClass->toChars()); - // Forward reference of base, try again later - //printf("\ttry later, forward reference of base %s\n", baseClass->toChars()); - scope = scx ? scx : new Scope(*sc); - scope->setNoFree(); + //printf("\ttry later, forward reference of base %s\n", tc->sym->toChars()); if (tc->sym->scope) tc->sym->scope->module->addDeferredSemantic(tc->sym); - scope->module->addDeferredSemantic(this); - return; + doAncestorsSemantic = SemanticStart; } + i++; + } + if (doAncestorsSemantic == SemanticStart) + { + // Forward referencee of one or more bases, try again later + scope = scx ? scx : sc->copy(); + scope->setNoFree(); + scope->module->addDeferredSemantic(this); + //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars()); + return; } - i++; - } - if (doAncestorsSemantic == SemanticIn) doAncestorsSemantic = SemanticDone; - - if (sizeok == SIZEOKnone) - { // If no base class, and this is not an Object, use Object as base class if (!baseClass && ident != Id::Object && !cpp) { @@ -487,30 +519,74 @@ void ClassDeclaration::semantic(Scope *sc) assert(!baseClass->isInterfaceDeclaration()); b->base = baseClass; } - - interfaces_dim = baseclasses->dim; - interfaces = baseclasses->tdata(); - if (baseClass) { if (baseClass->storage_class & STCfinal) error("cannot inherit from final class %s", baseClass->toChars()); - interfaces_dim--; - interfaces++; + // Inherit properties from base class + if (baseClass->isCOMclass()) + com = true; + if (baseClass->isCPPclass()) + cpp = true; + if (baseClass->isscope) + isscope = true; + enclosing = baseClass->enclosing; + storage_class |= baseClass->storage_class & STC_TYPECTOR; + } + + interfaces_dim = baseclasses->dim - (baseClass ? 1 : 0); + interfaces = baseclasses->tdata() + (baseClass ? 1 : 0); + + for (size_t i = 0; i < interfaces_dim; i++) + { + BaseClass *b = interfaces[i]; + // If this is an interface, and it derives from a COM interface, + // then this is a COM interface too. + if (b->base->isCOMinterface()) + com = true; + } + } +Lancestorsdone: + + if (!members) // if opaque declaration + { + semanticRun = PASSsemanticdone; + return; + } + if (!symtab) + symtab = new DsymbolTable(); + for (size_t i = 0; i < baseclasses->dim; i++) + { + BaseClass *b = (*baseclasses)[i]; + Type *tb = b->type->toBasetype(); + assert(tb->ty == Tclass); + TypeClass *tc = (TypeClass *)tb; + + if (tc->sym->semanticRun < PASSsemanticdone) + { + // Forward referencee of one or more bases, try again later + scope = scx ? scx : sc->copy(); + scope->setNoFree(); + if (tc->sym->scope) + tc->sym->scope->module->addDeferredSemantic(tc->sym); + scope->module->addDeferredSemantic(this); + //printf("\tL%d semantic('%s') failed due to forward references\n", __LINE__, toChars()); + return; + } + } + + if (sizeok == SIZEOKnone) + { + // initialize vtbl + if (baseClass) + { // Copy vtbl[] from base class vtbl.setDim(baseClass->vtbl.dim); memcpy(vtbl.tdata(), baseClass->vtbl.tdata(), sizeof(void *) * vtbl.dim); - // Inherit properties from base class - com = baseClass->isCOMclass(); - if (baseClass->isCPPclass()) - cpp = 1; - isscope = baseClass->isscope; vthis = baseClass->vthis; - enclosing = baseClass->enclosing; - storage_class |= baseClass->storage_class & STC_TYPECTOR; } else { @@ -519,10 +595,6 @@ void ClassDeclaration::semantic(Scope *sc) if (vtblOffset()) vtbl.push(this); // leave room for classinfo as first member } - - protection = sc->protection; - storage_class |= sc->stc; - interfaceSemantic(sc); for (size_t i = 0; i < members->dim; i++) @@ -562,53 +634,46 @@ void ClassDeclaration::semantic(Scope *sc) } else makeNested(); - - if (storage_class & STCauto) - error("storage class 'auto' is invalid when declaring a class, did you mean to use 'scope'?"); - if (storage_class & STCscope) - isscope = 1; - if (storage_class & STCabstract) - isabstract = 1; } - sc = sc->push(this); - //sc->stc &= ~(STCfinal | STCauto | STCscope | STCstatic | STCabstract | STCdeprecated | STC_TYPECTOR | STCtls | STCgshared); - //sc->stc |= storage_class & STC_TYPECTOR; - sc->stc &= STCsafe | STCtrusted | STCsystem; - sc->parent = this; - sc->inunion = 0; + Scope *sc2 = sc->push(this); + //sc2->stc &= ~(STCfinal | STCauto | STCscope | STCstatic | STCabstract | STCdeprecated | STC_TYPECTOR | STCtls | STCgshared); + //sc2->stc |= storage_class & STC_TYPECTOR; + sc2->stc &= STCsafe | STCtrusted | STCsystem; + sc2->parent = this; + sc2->inunion = 0; if (isCOMclass()) { if (global.params.isWindows) - sc->linkage = LINKwindows; + sc2->linkage = LINKwindows; else + { /* This enables us to use COM objects under Linux and * work with things like XPCOM */ - sc->linkage = LINKc; + sc2->linkage = LINKc; + } } - sc->protection = PROTpublic; - sc->explicitProtection = 0; - sc->structalign = STRUCTALIGN_DEFAULT; + sc2->protection = PROTpublic; + sc2->explicitProtection = 0; + sc2->structalign = STRUCTALIGN_DEFAULT; + sc2->userAttribDecl = NULL; + if (baseClass) { - sc->offset = baseClass->structsize; alignsize = baseClass->alignsize; - sc->offset = (sc->offset + alignsize - 1) & ~(alignsize - 1); -// if (enclosing) -// sc->offset += Target::ptrsize; // room for uplevel context pointer + structsize = baseClass->structsize; + if (cpp && global.params.isWindows) + structsize = (structsize + alignsize - 1) & ~(alignsize - 1); } else { + alignsize = Target::ptrsize; if (cpp) - sc->offset = Target::ptrsize; // allow room for __vptr + structsize = Target::ptrsize; // allow room for __vptr else - sc->offset = Target::ptrsize * 2; // allow room for __vptr and __monitor - alignsize = Target::ptrsize; + structsize = Target::ptrsize * 2; // allow room for __vptr and __monitor } - sc->userAttribDecl = NULL; - structsize = sc->offset; - Scope scsave = *sc; size_t members_dim = members->dim; sizeok = SIZEOKnone; @@ -618,17 +683,20 @@ void ClassDeclaration::semantic(Scope *sc) for (size_t i = 0; i < members_dim; i++) { Dsymbol *s = (*members)[i]; - //printf("[%d] setScope %s %s, sc = %p\n", i, s->kind(), s->toChars(), sc); - s->setScope(sc); + //printf("[%d] setScope %s %s, sc2 = %p\n", i, s->kind(), s->toChars(), sc2); + s->setScope(sc2); } - for (size_t i = 0; i < members_dim; i++) + for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; + s->importAll(sc2); + } - // Ungag errors when not speculative - Ungag ungag = ungagSpeculative(); - s->semantic(sc); + for (size_t i = 0; i < members_dim; i++) + { + Dsymbol *s = (*members)[i]; + s->semantic(sc2); } // Set the offsets of the fields and determine the size of the class @@ -639,12 +707,14 @@ void ClassDeclaration::semantic(Scope *sc) Dsymbol *s = (*members)[i]; s->setFieldOffset(this, &offset, false); } - sc->offset = structsize; if (global.errors != errors) { // The type is no good. type = Type::terror; + this->errors = true; + if (deferred) + deferred->errors = true; } if (sizeok == SIZEOKfwd) // failed due to forward references @@ -659,11 +729,10 @@ void ClassDeclaration::semantic(Scope *sc) fields.setDim(0); structsize = 0; alignsize = 0; -// structalign = 0; - sc = sc->pop(); + sc2->pop(); - scope = scx ? scx : new Scope(*sc); + scope = scx ? scx : sc->copy(); scope->setNoFree(); scope->module->addDeferredSemantic(this); @@ -680,7 +749,7 @@ void ClassDeclaration::semantic(Scope *sc) /* Look for special member functions. * They must be in this class, not in a base class. */ - searchCtor(); + ctor = searchCtor(); if (ctor && (ctor->toParent() != this || !(ctor->isCtorDeclaration() || ctor->isTemplateDeclaration()))) ctor = NULL; // search() looks through ancestor classes if (!ctor && noDefaultCtor) @@ -694,7 +763,7 @@ void ClassDeclaration::semantic(Scope *sc) } } - inv = buildInv(sc); + inv = buildInv(this, sc2); // Can be in base class aggNew = (NewDeclaration *)search(Loc(), Id::classNew); @@ -705,20 +774,21 @@ void ClassDeclaration::semantic(Scope *sc) // this() { } if (!ctor && baseClass && baseClass->ctor) { - if (FuncDeclaration *fd = resolveFuncCall(loc, sc, baseClass->ctor, NULL, NULL, NULL, 1)) + FuncDeclaration *fd = resolveFuncCall(loc, sc2, baseClass->ctor, NULL, NULL, NULL, 1); + if (fd && !fd->errors) { //printf("Creating default this(){} for class %s\n", toChars()); TypeFunction *btf = (TypeFunction *)fd->type; TypeFunction *tf = new TypeFunction(NULL, NULL, 0, LINKd, fd->storage_class); tf->purity = btf->purity; tf->isnothrow = btf->isnothrow; + tf->isnogc = btf->isnogc; tf->trust = btf->trust; CtorDeclaration *ctor = new CtorDeclaration(loc, Loc(), 0, tf); ctor->fbody = new CompoundStatement(Loc(), new Statements()); members->push(ctor); ctor->addMember(sc, this, 1); - *sc = scsave; // why? What about sc->nofree? - ctor->semantic(sc); + ctor->semantic(sc2); this->ctor = ctor; defaultCtor = ctor; } @@ -728,58 +798,42 @@ void ClassDeclaration::semantic(Scope *sc) } } -#if 0 - if (baseClass) - { if (!aggDelete) - aggDelete = baseClass->aggDelete; - if (!aggNew) - aggNew = baseClass->aggNew; - } -#endif - // Allocate instance of each new interface - sc->offset = structsize; + offset = structsize; for (size_t i = 0; i < vtblInterfaces->dim; i++) { BaseClass *b = (*vtblInterfaces)[i]; unsigned thissize = Target::ptrsize; - alignmember(STRUCTALIGN_DEFAULT, thissize, &sc->offset); + alignmember(STRUCTALIGN_DEFAULT, thissize, &offset); assert(b->offset == 0); - b->offset = sc->offset; + b->offset = offset; // Take care of single inheritance offsets while (b->baseInterfaces_dim) { b = &b->baseInterfaces[0]; - b->offset = sc->offset; + b->offset = offset; } - sc->offset += thissize; + offset += thissize; if (alignsize < thissize) alignsize = thissize; } - structsize = sc->offset; + structsize = offset; sizeok = SIZEOKdone; + Module::dprogress++; + semanticRun = PASSsemanticdone; - dtor = buildDtor(sc); - if (FuncDeclaration *f = hasIdentityOpAssign(sc)) + dtor = buildDtor(this, sc2); + if (FuncDeclaration *f = hasIdentityOpAssign(this, sc2)) { if (!(f->storage_class & STCdisable)) error(f->loc, "identity assignment operator overload is illegal"); } - sc->pop(); - -#if 0 // Do not call until toObjfile() because of forward references - // Fill in base class vtbl[]s - for (i = 0; i < vtblInterfaces->dim; i++) - { - BaseClass *b = (*vtblInterfaces)[i]; + sc2->pop(); - //b->fillVtbl(this, &b->vtbl, 1); - } -#endif //printf("-ClassDeclaration::semantic(%s), type = %p\n", toChars(), type); if (deferred && !global.gag) @@ -788,12 +842,16 @@ void ClassDeclaration::semantic(Scope *sc) deferred->semantic3(sc); } +#if 0 if (type->ty == Tclass && ((TypeClass *)type)->sym != this) { - error("failed semantic analysis"); - this->errors = true; - type = Type::terror; - } + printf("this = %p %s\n", this, this->toChars()); + printf("type = %d sym = %p\n", type->ty, ((TypeClass *)type)->sym); + } +#endif + assert(type->ty != Tclass || ((TypeClass *)type)->sym == this); + + //printf("-ClassDeclaration::semantic(%s), type = %p, sizeok = %d, this = %p\n", toChars(), type, sizeok, this); } void ClassDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) @@ -833,42 +891,30 @@ void ClassDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writenl(); } -#if 0 -void ClassDeclaration::defineRef(Dsymbol *s) -{ - ClassDeclaration *cd; - - AggregateDeclaration::defineRef(s); - cd = s->isClassDeclaration(); - baseType = cd->baseType; - cd->baseType = NULL; -} -#endif - /********************************************* * Determine if 'this' is a base class of cd. * This is used to detect circular inheritance only. */ -int ClassDeclaration::isBaseOf2(ClassDeclaration *cd) +bool ClassDeclaration::isBaseOf2(ClassDeclaration *cd) { if (!cd) - return 0; + return false; //printf("ClassDeclaration::isBaseOf2(this = '%s', cd = '%s')\n", toChars(), cd->toChars()); for (size_t i = 0; i < cd->baseclasses->dim; i++) - { BaseClass *b = (*cd->baseclasses)[i]; - + { + BaseClass *b = (*cd->baseclasses)[i]; if (b->base == this || isBaseOf2(b->base)) - return 1; + return true; } - return 0; + return false; } /******************************************* * Determine if 'this' is a base class of cd. */ -int ClassDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset) +bool ClassDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset) { //printf("ClassDeclaration::isBaseOf(this = '%s', cd = '%s')\n", toChars(), cd->toChars()); if (poffset) @@ -885,11 +931,11 @@ int ClassDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset) } if (this == cd->baseClass) - return 1; + return true; cd = cd->baseClass; } - return 0; + return false; } /********************************************* @@ -897,16 +943,9 @@ int ClassDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset) * This is used to detect forward references in covariant overloads. */ -int ClassDeclaration::isBaseInfoComplete() +bool ClassDeclaration::isBaseInfoComplete() { - if (!baseClass) - return ident == Id::Object; - for (size_t i = 0; i < baseclasses->dim; i++) - { BaseClass *b = (*baseclasses)[i]; - if (!b->base || !b->base->isBaseInfoComplete()) - return 0; - } - return 1; + return doAncestorsSemantic == SemanticDone; } Dsymbol *ClassDeclaration::search(Loc loc, Identifier *ident, int flags) @@ -918,13 +957,10 @@ Dsymbol *ClassDeclaration::search(Loc loc, Identifier *ident, int flags) if (scope && doAncestorsSemantic == SemanticStart) { // must semantic on base class/interfaces - doAncestorsSemantic = SemanticIn; - semantic(scope); - if (doAncestorsSemantic != SemanticDone) - doAncestorsSemantic = SemanticStart; + semantic(NULL); } - if (!members || !symtab) // opaque or semantic() is not yet called + if (!members || !symtab) // opaque or addMember is not yet done { error("is forward referenced when looking for '%s'", ident->toChars()); //*(char*)0=0; @@ -991,7 +1027,7 @@ int isf(void *param, Dsymbol *s) return (RootObject *)param == fd; } -int ClassDeclaration::isFuncHidden(FuncDeclaration *fd) +bool ClassDeclaration::isFuncHidden(FuncDeclaration *fd) { //printf("ClassDeclaration::isFuncHidden(class = %s, fd = %s)\n", toChars(), fd->toChars()); Dsymbol *s = search(Loc(), fd->ident, IgnoreAmbiguous | IgnoreErrors); @@ -1001,7 +1037,7 @@ int ClassDeclaration::isFuncHidden(FuncDeclaration *fd) /* Because, due to a hack, if there are multiple definitions * of fd->ident, NULL is returned. */ - return 0; + return false; } s = s->toAlias(); OverloadSet *os = s->isOverloadSet(); @@ -1012,16 +1048,16 @@ int ClassDeclaration::isFuncHidden(FuncDeclaration *fd) Dsymbol *s2 = os->a[i]; FuncDeclaration *f2 = s2->isFuncDeclaration(); if (f2 && overloadApply(f2, (void *)fd, &isf)) - return 0; + return false; } - return 1; + return true; } else { FuncDeclaration *fdstart = s->isFuncDeclaration(); //printf("%s fdstart = %p\n", s->kind(), fdstart); if (overloadApply(fdstart, (void *)fd, &isf)) - return 0; + return false; return !fd->parent->isTemplateMixin(); } @@ -1051,7 +1087,8 @@ FuncDeclaration *ClassDeclaration::findFunc(Identifier *ident, TypeFunction *tf) //printf("\t[%d] = %s\n", i, fd->toChars()); if (ident == fd->ident && fd->type->covariant(tf) == 1) - { //printf("fd->parent->isClassDeclaration() = %p\n", fd->parent->isClassDeclaration()); + { + //printf("fd->parent->isClassDeclaration() = %p\n", fd->parent->isClassDeclaration()); if (!fdmatch) goto Lfd; if (fd == fdmatch) @@ -1086,7 +1123,6 @@ FuncDeclaration *ClassDeclaration::findFunc(Identifier *ident, TypeFunction *tf) goto Lfdmatch; } - Lambig: fdambig = fd; //printf("Lambig fdambig = %s %s [%s]\n", fdambig->toChars(), fdambig->type->toChars(), fdambig->loc.toChars()); continue; @@ -1114,27 +1150,12 @@ FuncDeclaration *ClassDeclaration::findFunc(Identifier *ident, TypeFunction *tf) void ClassDeclaration::interfaceSemantic(Scope *sc) { - InterfaceDeclaration *id = isInterfaceDeclaration(); - vtblInterfaces = new BaseClasses(); vtblInterfaces->reserve(interfaces_dim); for (size_t i = 0; i < interfaces_dim; i++) { BaseClass *b = interfaces[i]; - - // If this is an interface, and it derives from a COM interface, - // then this is a COM interface too. - if (b->base->isCOMinterface()) - com = 1; - -#if 1 - if (b->base->isCPPinterface() && id) - id->cpp = 1; -#else - if (b->base->isCPPinterface()) - cpp = 1; -#endif vtblInterfaces->push(b); b->copyBaseInterfaces(vtblInterfaces); } @@ -1143,24 +1164,24 @@ void ClassDeclaration::interfaceSemantic(Scope *sc) /**************************************** */ -int ClassDeclaration::isCOMclass() +bool ClassDeclaration::isCOMclass() { return com; } -int ClassDeclaration::isCOMinterface() +bool ClassDeclaration::isCOMinterface() { - return 0; + return false; } -int ClassDeclaration::isCPPclass() +bool ClassDeclaration::isCPPclass() { return cpp; } -int ClassDeclaration::isCPPinterface() +bool ClassDeclaration::isCPPinterface() { - return 0; + return false; } @@ -1178,7 +1199,7 @@ bool ClassDeclaration::isAbstract() //printf("\tvtbl[%d] = %p\n", i, fd); if (!fd || fd->isAbstract()) { - isabstract |= 1; + isabstract = true; return true; } } @@ -1223,8 +1244,9 @@ InterfaceDeclaration::InterfaceDeclaration(Loc loc, Identifier *id, BaseClasses : ClassDeclaration(loc, id, baseclasses) { if (id == Id::IUnknown) // IUnknown is the root of all COM interfaces - { com = 1; - cpp = 1; // IUnknown is also a C++ interface + { + com = true; + cpp = true; // IUnknown is also a C++ interface } } @@ -1247,24 +1269,9 @@ void InterfaceDeclaration::semantic(Scope *sc) if (inuse) return; - if (!sc) - sc = scope; - if (!parent && sc->parent && !sc->parent->isModule()) - parent = sc->parent; - - type = type->semantic(loc, sc); - handle = type; - - if (!members) // if forward reference - { //printf("\tinterface '%s' is forward referenced\n", toChars()); + if (semanticRun >= PASSsemanticdone) return; - } - if (symtab) // if already done - { if (!scope) - return; - } - else - symtab = new DsymbolTable(); + int errors = global.errors; Scope *scx = NULL; if (scope) @@ -1274,142 +1281,237 @@ void InterfaceDeclaration::semantic(Scope *sc) scope = NULL; } - int errors = global.errors; + if (!parent) + { + assert(sc->parent && sc->func); + parent = sc->parent; + } + assert(parent && !isAnonymous()); + type = type->semantic(loc, sc); - if (sc->stc & STCdeprecated) + if (type->ty == Tclass && ((TypeClass *)type)->sym != this) { - isdeprecated = true; + TemplateInstance *ti = ((TypeClass *)type)->sym->isInstantiated(); + if (ti && isError(ti)) + ((TypeClass *)type)->sym = this; } - userAttribDecl = sc->userAttribDecl; - // Expand any tuples in baseclasses[] - for (size_t i = 0; i < baseclasses->dim; ) + // Ungag errors when not speculative + Ungag ungag = ungagSpeculative(); + + if (semanticRun == PASSinit) { - // Ungag errors when not speculative - Ungag ungag = ungagSpeculative(); + protection = sc->protection; - BaseClass *b = (*baseclasses)[i]; - b->type = b->type->semantic(loc, sc); + storage_class |= sc->stc; + if (storage_class & STCdeprecated) + isdeprecated = true; - Type *tb = b->type->toBasetype(); - if (tb->ty == Ttuple) - { TypeTuple *tup = (TypeTuple *)tb; - PROT protection = b->protection; - baseclasses->remove(i); - size_t dim = Parameter::dim(tup->arguments); - for (size_t j = 0; j < dim; j++) - { Parameter *arg = Parameter::getNth(tup->arguments, j); - b = new BaseClass(arg->type, protection); - baseclasses->insert(i + j, b); - } + userAttribDecl = sc->userAttribDecl; + } + else if (symtab) + { + if (sizeok == SIZEOKdone || !scx) + { + semanticRun = PASSsemanticdone; + return; } - else - i++; } + semanticRun = PASSsemantic; - if (!baseclasses->dim && sc->linkage == LINKcpp) - cpp = 1; - - // Check for errors, handle forward references - for (size_t i = 0; i < baseclasses->dim; ) + if (doAncestorsSemantic != SemanticDone) { - // Ungag errors when not speculative - Ungag ungag = ungagSpeculative(); + doAncestorsSemantic = SemanticIn; - BaseClass *b = (*baseclasses)[i]; - b->type = b->type->semantic(loc, sc); + // Expand any tuples in baseclasses[] + for (size_t i = 0; i < baseclasses->dim; ) + { + scope = scx ? scx : sc->copy(); + scope->setNoFree(); - Type *tb = b->type->toBasetype(); - TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : NULL; - if (!tc || !tc->sym->isInterfaceDeclaration()) + BaseClass *b = (*baseclasses)[i]; + b->type = b->type->semantic(loc, sc); + + scope = NULL; + + Type *tb = b->type->toBasetype(); + if (tb->ty == Ttuple) + { + TypeTuple *tup = (TypeTuple *)tb; + PROT protection = b->protection; + baseclasses->remove(i); + size_t dim = Parameter::dim(tup->arguments); + for (size_t j = 0; j < dim; j++) + { + Parameter *arg = Parameter::getNth(tup->arguments, j); + b = new BaseClass(arg->type, protection); + baseclasses->insert(i + j, b); + } + } + else + i++; + } + + if (doAncestorsSemantic == SemanticDone) { - if (b->type != Type::terror) - error("base type must be interface, not %s", b->type->toChars()); - baseclasses->remove(i); - continue; + //printf("%s already semantic analyzed, semanticRun = %d\n", toChars(), semanticRun); + if (semanticRun >= PASSsemanticdone) + return; + goto Lancestorsdone; } - else + + if (!baseclasses->dim && sc->linkage == LINKcpp) + cpp = true; + + // Check for errors, handle forward references + for (size_t i = 0; i < baseclasses->dim; ) { + BaseClass *b = (*baseclasses)[i]; + Type *tb = b->type->toBasetype(); + TypeClass *tc = (tb->ty == Tclass) ? (TypeClass *)tb : NULL; + if (!tc || !tc->sym->isInterfaceDeclaration()) + { + if (b->type != Type::terror) + error("base type must be interface, not %s", b->type->toChars()); + baseclasses->remove(i); + continue; + } + // Check for duplicate interfaces for (size_t j = 0; j < i; j++) { BaseClass *b2 = (*baseclasses)[j]; if (b2->base == tc->sym) + { error("inherits from duplicate interface %s", b2->base->toChars()); + baseclasses->remove(i); + continue; + } } - b->base = tc->sym; - if (b->base == this || isBaseOf2(b->base)) + if (tc->sym == this || isBaseOf2(tc->sym)) { error("circular inheritance of interface"); baseclasses->remove(i); continue; } - if (b->base->scope) + + if (tc->sym->isDeprecated()) { - // Try to resolve forward reference - b->base->semantic(NULL); + if (!isDeprecated()) + { + // Deriving from deprecated class makes this one deprecated too + isdeprecated = true; + + tc->checkDeprecated(loc, sc); + } } - if (!b->base->symtab || b->base->scope || b->base->inuse) + + b->base = tc->sym; + + if (tc->sym->scope && tc->sym->doAncestorsSemantic != SemanticDone) + tc->sym->semantic(NULL); // Try to resolve forward reference + if (tc->sym->inuse || tc->sym->doAncestorsSemantic != SemanticDone) { - //error("forward reference of base class %s", baseClass->toChars()); - // Forward reference of base, try again later - //printf("\ttry later, forward reference of base %s\n", b->base->toChars()); - scope = scx ? scx : new Scope(*sc); - scope->setNoFree(); - scope->module->addDeferredSemantic(this); - return; + //printf("\ttry later, forward reference of base %s\n", tc->sym->toChars()); + if (tc->sym->scope) + tc->sym->scope->module->addDeferredSemantic(tc->sym); + doAncestorsSemantic = SemanticStart; } + i++; + } + if (doAncestorsSemantic == SemanticStart) + { + // Forward referencee of one or more bases, try again later + scope = scx ? scx : sc->copy(); + scope->setNoFree(); + scope->module->addDeferredSemantic(this); + return; } -#if 0 - // Inherit const/invariant from base class - storage_class |= b->base->storage_class & STC_TYPECTOR; -#endif - i++; - } - if (doAncestorsSemantic == SemanticIn) doAncestorsSemantic = SemanticDone; - interfaces_dim = baseclasses->dim; - interfaces = baseclasses->tdata(); + interfaces_dim = baseclasses->dim; + interfaces = baseclasses->tdata(); - interfaceSemantic(sc); + for (size_t i = 0; i < interfaces_dim; i++) + { + BaseClass *b = interfaces[i]; + // If this is an interface, and it derives from a COM interface, + // then this is a COM interface too. + if (b->base->isCOMinterface()) + com = true; + if (b->base->isCPPinterface()) + cpp = true; + } + } +Lancestorsdone: - if (vtblOffset()) - vtbl.push(this); // leave room at vtbl[0] for classinfo + if (!members) // if opaque declaration + { + semanticRun = PASSsemanticdone; + return; + } + if (!symtab) + symtab = new DsymbolTable(); - // Cat together the vtbl[]'s from base interfaces - for (size_t i = 0; i < interfaces_dim; i++) - { BaseClass *b = interfaces[i]; + for (size_t i = 0; i < baseclasses->dim; i++) + { + BaseClass *b = (*baseclasses)[i]; + Type *tb = b->type->toBasetype(); + assert(tb->ty == Tclass); + TypeClass *tc = (TypeClass *)tb; - // Skip if b has already appeared - for (size_t k = 0; k < i; k++) + if (tc->sym->semanticRun < PASSsemanticdone) { - if (b == interfaces[k]) - goto Lcontinue; + // Forward referencee of one or more bases, try again later + scope = scx ? scx : sc->copy(); + scope->setNoFree(); + if (tc->sym->scope) + tc->sym->scope->module->addDeferredSemantic(tc->sym); + scope->module->addDeferredSemantic(this); + return; } + } + + { + // initialize vtbl + interfaceSemantic(sc); + + if (vtblOffset()) + vtbl.push(this); // leave room at vtbl[0] for classinfo - // Copy vtbl[] from base class - if (b->base->vtblOffset()) - { size_t d = b->base->vtbl.dim; - if (d > 1) + // Cat together the vtbl[]'s from base interfaces + for (size_t i = 0; i < interfaces_dim; i++) + { + BaseClass *b = interfaces[i]; + + // Skip if b has already appeared + for (size_t k = 0; k < i; k++) { - vtbl.reserve(d - 1); - for (size_t j = 1; j < d; j++) - vtbl.push(b->base->vtbl[j]); + if (b == interfaces[k]) + goto Lcontinue; } - } - else - { - vtbl.append(&b->base->vtbl); - } - Lcontinue: - ; - } + // Copy vtbl[] from base class + if (b->base->vtblOffset()) + { + size_t d = b->base->vtbl.dim; + if (d > 1) + { + vtbl.reserve(d - 1); + for (size_t j = 1; j < d; j++) + vtbl.push(b->base->vtbl[j]); + } + } + else + { + vtbl.append(&b->base->vtbl); + } - protection = sc->protection; - storage_class |= sc->stc & STC_TYPECTOR; + Lcontinue: + ; + } + } for (size_t i = 0; i < members->dim; i++) { @@ -1417,62 +1519,71 @@ void InterfaceDeclaration::semantic(Scope *sc) s->addMember(sc, this, 1); } - sc = sc->push(this); - sc->stc &= STCsafe | STCtrusted | STCsystem; - sc->parent = this; + Scope *sc2 = sc->push(this); + sc2->stc &= STCsafe | STCtrusted | STCsystem; + sc2->parent = this; + sc2->inunion = 0; if (com) - sc->linkage = LINKwindows; + sc2->linkage = LINKwindows; else if (cpp) - sc->linkage = LINKcpp; - sc->structalign = STRUCTALIGN_DEFAULT; - sc->protection = PROTpublic; - sc->explicitProtection = 0; -// structalign = sc->structalign; - sc->offset = Target::ptrsize * 2; - sc->userAttribDecl = NULL; - structsize = sc->offset; + sc2->linkage = LINKcpp; + sc2->protection = PROTpublic; + sc2->explicitProtection = 0; + sc2->structalign = STRUCTALIGN_DEFAULT; + sc2->userAttribDecl = NULL; + + structsize = Target::ptrsize * 2; inuse++; /* Set scope so if there are forward references, we still might be able to * resolve individual members like enums. */ for (size_t i = 0; i < members->dim; i++) - { Dsymbol *s = (*members)[i]; + { + Dsymbol *s = (*members)[i]; /* There are problems doing this in the general case because * Scope keeps track of things like 'offset' */ if (s->isEnumDeclaration() || (s->isAggregateDeclaration() && s->ident)) { //printf("setScope %s %s\n", s->kind(), s->toChars()); - s->setScope(sc); + s->setScope(sc2); } } for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; + s->importAll(sc2); + } - // Ungag errors when not speculative - Ungag ungag = ungagSpeculative(); - s->semantic(sc); + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + s->semantic(sc2); } + semanticRun = PASSsemanticdone; + if (global.errors != errors) - { // The type is no good. + { + // The type is no good. type = Type::terror; } inuse--; //members->print(); - sc->pop(); + sc2->pop(); //printf("-InterfaceDeclaration::semantic(%s), type = %p\n", toChars(), type); +#if 0 if (type->ty == Tclass && ((TypeClass *)type)->sym != this) { - error("failed semantic analysis"); - this->errors = true; - type = Type::terror; - } + printf("this = %p %s\n", this, this->toChars()); + printf("type = %d sym = %p\n", type->ty, ((TypeClass *)type)->sym); + } +#endif + assert(type->ty != Tclass || ((TypeClass *)type)->sym == this); } @@ -1483,11 +1594,11 @@ void InterfaceDeclaration::semantic(Scope *sc) * *poffset offset to start of class * OFFSET_RUNTIME must determine offset at runtime * Returns: - * 0 not a base - * 1 is a base + * false not a base + * true is a base */ -int InterfaceDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset) +bool InterfaceDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset) { //printf("%s.InterfaceDeclaration::isBaseOf(cd = '%s')\n", toChars(), cd->toChars()); assert(!baseClass); @@ -1500,29 +1611,31 @@ int InterfaceDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset) { //printf("\tfound at offset %d\n", b->offset); if (poffset) - { *poffset = b->offset; + { + *poffset = b->offset; if (j && cd->isInterfaceDeclaration()) *poffset = OFFSET_RUNTIME; } - return 1; + return true; } if (isBaseOf(b, poffset)) - { if (j && poffset && cd->isInterfaceDeclaration()) + { + if (j && poffset && cd->isInterfaceDeclaration()) *poffset = OFFSET_RUNTIME; - return 1; + return true; } } if (cd->baseClass && isBaseOf(cd->baseClass, poffset)) - return 1; + return true; if (poffset) *poffset = 0; - return 0; + return false; } -int InterfaceDeclaration::isBaseOf(BaseClass *bc, int *poffset) +bool InterfaceDeclaration::isBaseOf(BaseClass *bc, int *poffset) { //printf("%s.InterfaceDeclaration::isBaseOf(bc = '%s')\n", toChars(), bc->base->toChars()); for (size_t j = 0; j < bc->baseInterfaces_dim; j++) @@ -1532,37 +1645,23 @@ int InterfaceDeclaration::isBaseOf(BaseClass *bc, int *poffset) if (this == b->base) { if (poffset) - { *poffset = b->offset; + { + *poffset = b->offset; if (j && bc->base->isInterfaceDeclaration()) *poffset = OFFSET_RUNTIME; } - return 1; + return true; } if (isBaseOf(b, poffset)) - { if (j && poffset && bc->base->isInterfaceDeclaration()) + { + if (j && poffset && bc->base->isInterfaceDeclaration()) *poffset = OFFSET_RUNTIME; - return 1; + return true; } } if (poffset) *poffset = 0; - return 0; -} - -/********************************************* - * Determine if 'this' has clomplete base class information. - * This is used to detect forward references in covariant overloads. - */ - -int InterfaceDeclaration::isBaseInfoComplete() -{ - assert(!baseClass); - for (size_t i = 0; i < baseclasses->dim; i++) - { BaseClass *b = (*baseclasses)[i]; - if (!b->base || !b->base->isBaseInfoComplete ()) - return 0; - } - return 1; + return false; } /**************************************** @@ -1579,12 +1678,12 @@ int InterfaceDeclaration::vtblOffset() return 1; } -int InterfaceDeclaration::isCOMinterface() +bool InterfaceDeclaration::isCOMinterface() { return com; } -int InterfaceDeclaration::isCPPinterface() +bool InterfaceDeclaration::isCPPinterface() { return cpp; } @@ -1624,13 +1723,13 @@ BaseClass::BaseClass(Type *type, PROT protection) * newinstance !=0 means all entries must be filled in by members * of cd, not members of any base classes of cd. * Returns: - * !=0 if any entries were filled in by members of cd (not exclusively + * true if any entries were filled in by members of cd (not exclusively * by base classes) */ -int BaseClass::fillVtbl(ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance) +bool BaseClass::fillVtbl(ClassDeclaration *cd, FuncDeclarations *vtbl, int newinstance) { - int result = 0; + bool result = false; //printf("BaseClass::fillVtbl(this='%s', cd='%s')\n", base->toChars(), cd->toChars()); if (vtbl) @@ -1664,7 +1763,7 @@ int BaseClass::fillVtbl(ClassDeclaration *cd, FuncDeclarations *vtbl, int newins cd->error("interface function '%s' is not implemented", ifd->toFullSignature()); if (fd->toParent() == cd) - result = 1; + result = true; } else { diff --git a/gcc/d/dfrontend/clone.c b/gcc/d/dfrontend/clone.c index 82b99458b..5a35023c1 100644 --- a/gcc/d/dfrontend/clone.c +++ b/gcc/d/dfrontend/clone.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2011 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/clone.c + */ #include #include @@ -27,8 +28,23 @@ /******************************************* * Merge function attributes pure, nothrow, @safe, and @disable */ -StorageClass mergeFuncAttrs(StorageClass s1, StorageClass s2) +StorageClass mergeFuncAttrs(StorageClass s1, FuncDeclaration *f) { + StorageClass s2 = (f->storage_class & STCdisable); + TypeFunction *tf = (TypeFunction *)f->type; + if (tf->trust == TRUSTsafe) + s2 |= STCsafe; + else if (tf->trust == TRUSTsystem) + s2 |= STCsystem; + else if (tf->trust == TRUSTtrusted) + s2 |= STCtrusted; + if (tf->purity != PUREimpure) + s2 |= STCpure; + if (tf->isnothrow) + s2 |= STCnothrow; + if (tf->isnogc) + s2 |= STCnogc; + StorageClass stc = 0; StorageClass sa = s1 & s2; StorageClass so = s1 | s2; @@ -48,6 +64,9 @@ StorageClass mergeFuncAttrs(StorageClass s1, StorageClass s2) if (sa & STCnothrow) stc |= STCnothrow; + if (sa & STCnogc) + stc |= STCnogc; + if (so & STCdisable) stc |= STCdisable; @@ -58,46 +77,45 @@ StorageClass mergeFuncAttrs(StorageClass s1, StorageClass s2) * Check given opAssign symbol is really identity opAssign or not. */ -FuncDeclaration *AggregateDeclaration::hasIdentityOpAssign(Scope *sc) +FuncDeclaration *hasIdentityOpAssign(AggregateDeclaration *ad, Scope *sc) { - Dsymbol *assign = search_function(this, Id::assign); + Dsymbol *assign = search_function(ad, Id::assign); if (assign) { /* check identity opAssign exists */ - Expression *er = new NullExp(loc, type); // dummy rvalue - Expression *el = new IdentifierExp(loc, Id::p); // dummy lvalue - el->type = type; + Expression *er = new NullExp(ad->loc, ad->type); // dummy rvalue + Expression *el = new IdentifierExp(ad->loc, Id::p); // dummy lvalue + el->type = ad->type; Expressions *a = new Expressions(); a->setDim(1); FuncDeclaration *f = NULL; unsigned errors = global.startGagging(); // Do not report errors, even if the - unsigned oldspec = global.speculativeGag; // template opAssign fbody makes it. - global.speculativeGag = global.gag; sc = sc->push(); sc->speculative = true; for (size_t i = 0; i < 2; i++) { (*a)[0] = (i == 0 ? er : el); - f = resolveFuncCall(loc, sc, assign, NULL, type, a, 1); + f = resolveFuncCall(ad->loc, sc, assign, NULL, ad->type, a, 1); if (f) break; } sc = sc->pop(); - global.speculativeGag = oldspec; global.endGagging(errors); if (f) { + if (f->errors) + return NULL; int varargs; Parameters *fparams = f->getParameters(&varargs); if (fparams->dim >= 1) { Parameter *arg0 = Parameter::getNth(fparams, 0); - if (arg0->type->toDsymbol(NULL) != this) + if (arg0->type->toDsymbol(NULL) != ad) f = NULL; } } @@ -114,42 +132,38 @@ FuncDeclaration *AggregateDeclaration::hasIdentityOpAssign(Scope *sc) * We need to generate one if a user-specified one does not exist. */ -int StructDeclaration::needOpAssign() +bool needOpAssign(StructDeclaration *sd) { -#define X 0 - if (X) printf("StructDeclaration::needOpAssign() %s\n", toChars()); + //printf("StructDeclaration::needOpAssign() %s\n", sd->toChars()); - if (hasIdentityAssign) + if (sd->hasIdentityAssign) goto Lneed; // because has identity==elaborate opAssign - if (dtor || postblit) + if (sd->dtor || sd->postblit) goto Lneed; /* If any of the fields need an opAssign, then we * need it too. */ - for (size_t i = 0; i < fields.dim; i++) + for (size_t i = 0; i < sd->fields.dim; i++) { - VarDeclaration *v = fields[i]; + VarDeclaration *v = sd->fields[i]; if (v->storage_class & STCref) continue; Type *tv = v->type->baseElemOf(); if (tv->ty == Tstruct) { TypeStruct *ts = (TypeStruct *)tv; - StructDeclaration *sd = ts->sym; - if (sd->needOpAssign()) + if (needOpAssign(ts->sym)) goto Lneed; } } -Ldontneed: - if (X) printf("\tdontneed\n"); - return 0; + //printf("\tdontneed\n"); + return false; Lneed: - if (X) printf("\tneed\n"); - return 1; -#undef X + //printf("\tneed\n"); + return true; } /****************************************** @@ -173,54 +187,55 @@ int StructDeclaration::needOpAssign() * ...; */ -FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc) +FuncDeclaration *buildOpAssign(StructDeclaration *sd, Scope *sc) { - if (FuncDeclaration *f = hasIdentityOpAssign(sc)) + if (FuncDeclaration *f = hasIdentityOpAssign(sd, sc)) { - hasIdentityAssign = 1; + sd->hasIdentityAssign = true; return f; } // Even if non-identity opAssign is defined, built-in identity opAssign // will be defined. - if (!needOpAssign()) + if (!needOpAssign(sd)) return NULL; //printf("StructDeclaration::buildOpAssign() %s\n", toChars()); - StorageClass stc = STCsafe | STCnothrow | STCpure; - Loc declLoc = this->loc; + StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; + Loc declLoc = sd->loc; Loc loc = Loc(); // internal code should have no loc to prevent coverage - if (dtor || postblit) + if (sd->dtor || sd->postblit) { - if (dtor) + if (!sd->type->isAssignable()) // Bugzilla 13044 + return NULL; + if (sd->dtor) { - stc = mergeFuncAttrs(stc, dtor->storage_class); + stc = mergeFuncAttrs(stc, sd->dtor); if (stc & STCsafe) stc = (stc & ~STCsafe) | STCtrusted; } } else { - for (size_t i = 0; i < fields.dim; i++) + for (size_t i = 0; i < sd->fields.dim; i++) { - VarDeclaration *v = fields[i]; + VarDeclaration *v = sd->fields[i]; if (v->storage_class & STCref) continue; Type *tv = v->type->baseElemOf(); if (tv->ty == Tstruct) { TypeStruct *ts = (TypeStruct *)tv; - StructDeclaration *sd = ts->sym; - if (FuncDeclaration *f = sd->hasIdentityOpAssign(sc)) - stc = mergeFuncAttrs(stc, f->storage_class); + if (FuncDeclaration *f = hasIdentityOpAssign(ts->sym, sc)) + stc = mergeFuncAttrs(stc, f); } } } Parameters *fparams = new Parameters; - fparams->push(new Parameter(STCnodtor, type, Id::p, NULL)); - Type *tf = new TypeFunction(fparams, handle, 0, LINKd, stc | STCref); + fparams->push(new Parameter(STCnodtor, sd->type, Id::p, NULL)); + Type *tf = new TypeFunction(fparams, sd->handleType(), 0, LINKd, stc | STCref); FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), Id::assign, stc, tf); @@ -228,39 +243,32 @@ FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc) if (stc & STCdisable) { } - else if (dtor || postblit) + else if (sd->dtor || sd->postblit) { /* Do swap this and rhs * tmp = this; this = s; tmp.dtor(); */ //printf("\tswap copy\n"); Identifier *idtmp = Lexer::uniqueId("__tmp"); - VarDeclaration *tmp; + VarDeclaration *tmp = NULL; AssignExp *ec = NULL; - if (dtor) + if (sd->dtor) { - tmp = new VarDeclaration(loc, type, idtmp, new VoidInitializer(loc)); + tmp = new VarDeclaration(loc, sd->type, idtmp, new VoidInitializer(loc)); tmp->noscope = 1; tmp->storage_class |= STCtemp | STCctfe; e = new DeclarationExp(loc, tmp); - ec = new AssignExp(loc, - new VarExp(loc, tmp), - new ThisExp(loc) - ); - ec->op = TOKblit; + ec = new BlitExp(loc, new VarExp(loc, tmp), new ThisExp(loc)); e = Expression::combine(e, ec); } - ec = new AssignExp(loc, - new ThisExp(loc), - new IdentifierExp(loc, Id::p)); - ec->op = TOKblit; + ec = new BlitExp(loc, new ThisExp(loc), new IdentifierExp(loc, Id::p)); e = Expression::combine(e, ec); - if (dtor) + if (sd->dtor) { /* Instead of running the destructor on s, run it * on tmp. This avoids needing to copy tmp back in to s. */ - Expression *ec2 = new DotVarExp(loc, new VarExp(loc, tmp), dtor, 0); + Expression *ec2 = new DotVarExp(loc, new VarExp(loc, tmp), sd->dtor, 0); ec2 = new CallExp(loc, ec2); e = Expression::combine(e, ec2); } @@ -270,9 +278,9 @@ FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc) /* Do memberwise copy */ //printf("\tmemberwise copy\n"); - for (size_t i = 0; i < fields.dim; i++) + for (size_t i = 0; i < sd->fields.dim; i++) { - VarDeclaration *v = fields[i]; + VarDeclaration *v = sd->fields[i]; // this.v = s.v; AssignExp *ec = new AssignExp(loc, new DotVarExp(loc, new ThisExp(loc), v, 0), @@ -293,45 +301,28 @@ FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc) fop->fbody = new CompoundStatement(loc, s1, s2); } - Dsymbol *s = fop; -#if 1 // workaround until fixing issue 1528 - Dsymbol *assign = search_function(this, Id::assign); - if (assign && assign->isTemplateDeclaration()) - { - // Wrap a template around the function declaration - TemplateParameters *tpl = new TemplateParameters(); - Dsymbols *decldefs = new Dsymbols(); - decldefs->push(s); - TemplateDeclaration *tempdecl = - new TemplateDeclaration(assign->loc, fop->ident, tpl, NULL, decldefs); - s = tempdecl; - } -#endif - members->push(s); - s->addMember(sc, this, 1); - this->hasIdentityAssign = 1; // temporary mark identity assignable + sd->members->push(fop); + fop->addMember(sc, sd, 1); + sd->hasIdentityAssign = true; // temporary mark identity assignable unsigned errors = global.startGagging(); // Do not report errors, even if the - unsigned oldspec = global.speculativeGag; // template opAssign fbody makes it. - global.speculativeGag = global.gag; Scope *sc2 = sc->push(); sc2->stc = 0; sc2->linkage = LINKd; - sc2->speculative = true; - s->semantic(sc2); - s->semantic2(sc2); - s->semantic3(sc2); + fop->semantic(sc2); + fop->semantic2(sc2); + fop->semantic3(sc2); sc2->pop(); - global.speculativeGag = oldspec; if (global.endGagging(errors)) // if errors happened - { // Disable generated opAssign, because some members forbid identity assignment. + { + // Disable generated opAssign, because some members forbid identity assignment. fop->storage_class |= STCdisable; fop->fbody = NULL; // remove fbody which contains the error } - //printf("-StructDeclaration::buildOpAssign() %s %s, errors = %d\n", toChars(), s->kind(), (fop->storage_class & STCdisable) != 0); + //printf("-StructDeclaration::buildOpAssign() %s, errors = %d\n", sd->toChars(), (fop->storage_class & STCdisable) != 0); return fop; } @@ -342,28 +333,32 @@ FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc) * Generate one if a user-specified one does not exist. */ -int StructDeclaration::needOpEquals() +bool needOpEquals(StructDeclaration *sd) { -#define X 0 - if (X) printf("StructDeclaration::needOpEquals() %s\n", toChars()); + //printf("StructDeclaration::needOpEquals() %s\n", sd->toChars()); - if (hasIdentityEquals) + if (sd->hasIdentityEquals) goto Lneed; - if (isUnionDeclaration()) + if (sd->isUnionDeclaration()) goto Ldontneed; /* If any of the fields has an opEquals, then we * need it too. */ - for (size_t i = 0; i < fields.dim; i++) + for (size_t i = 0; i < sd->fields.dim; i++) { - VarDeclaration *v = fields[i]; + VarDeclaration *v = sd->fields[i]; if (v->storage_class & STCref) continue; Type *tv = v->type->toBasetype(); if (tv->isfloating()) + { + // This is necessray for: + // 1. comparison of +0.0 and -0.0 should be true. + // 2. comparison of NANs should be false always. goto Lneed; + } if (tv->ty == Tarray) goto Lneed; if (tv->ty == Taarray) @@ -374,46 +369,42 @@ int StructDeclaration::needOpEquals() if (tv->ty == Tstruct) { TypeStruct *ts = (TypeStruct *)tv; - StructDeclaration *sd = ts->sym; - if (sd->needOpEquals()) + if (needOpEquals(ts->sym)) goto Lneed; } } Ldontneed: - if (X) printf("\tdontneed\n"); - return 0; + //printf("\tdontneed\n"); + return false; Lneed: - if (X) printf("\tneed\n"); - return 1; -#undef X + //printf("\tneed\n"); + return true; } -FuncDeclaration *AggregateDeclaration::hasIdentityOpEquals(Scope *sc) +FuncDeclaration *hasIdentityOpEquals(AggregateDeclaration *ad, Scope *sc) { - Dsymbol *eq = search_function(this, Id::eq); + Dsymbol *eq = search_function(ad, Id::eq); if (eq) { /* check identity opEquals exists */ - Expression *er = new NullExp(loc, NULL); // dummy rvalue - Expression *el = new IdentifierExp(loc, Id::p); // dummy lvalue + Expression *er = new NullExp(ad->loc, NULL); // dummy rvalue + Expression *el = new IdentifierExp(ad->loc, Id::p); // dummy lvalue Expressions *a = new Expressions(); a->setDim(1); for (size_t i = 0; ; i++) { Type *tthis; - if (i == 0) tthis = type; - if (i == 1) tthis = type->constOf(); - if (i == 2) tthis = type->immutableOf(); - if (i == 3) tthis = type->sharedOf(); - if (i == 4) tthis = type->sharedConstOf(); + if (i == 0) tthis = ad->type; + if (i == 1) tthis = ad->type->constOf(); + if (i == 2) tthis = ad->type->immutableOf(); + if (i == 3) tthis = ad->type->sharedOf(); + if (i == 4) tthis = ad->type->sharedConstOf(); if (i == 5) break; FuncDeclaration *f = NULL; unsigned errors = global.startGagging(); // Do not report errors, even if the - unsigned oldspec = global.speculativeGag; // template opAssign fbody makes it. - global.speculativeGag = global.gag; sc = sc->push(); sc->speculative = true; @@ -421,17 +412,20 @@ FuncDeclaration *AggregateDeclaration::hasIdentityOpEquals(Scope *sc) { (*a)[0] = (j == 0 ? er : el); (*a)[0]->type = tthis; - f = resolveFuncCall(loc, sc, eq, NULL, tthis, a, 1); + f = resolveFuncCall(ad->loc, sc, eq, NULL, tthis, a, 1); if (f) break; } sc = sc->pop(); - global.speculativeGag = oldspec; global.endGagging(errors); if (f) + { + if (f->errors) + return NULL; return f; + } } } return NULL; @@ -447,11 +441,11 @@ FuncDeclaration *AggregateDeclaration::hasIdentityOpEquals(Scope *sc) * to calculate structural equality. See EqualExp::semantic. */ -FuncDeclaration *StructDeclaration::buildOpEquals(Scope *sc) +FuncDeclaration *buildOpEquals(StructDeclaration *sd, Scope *sc) { - if (FuncDeclaration *f = hasIdentityOpEquals(sc)) + if (hasIdentityOpEquals(sd, sc)) { - hasIdentityEquals = 1; + sd->hasIdentityEquals = true; } return NULL; } @@ -467,13 +461,13 @@ FuncDeclaration *StructDeclaration::buildOpEquals(Scope *sc) * const objects comparison, it will throw "not implemented" Error in runtime. */ -FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc) +FuncDeclaration *buildXopEquals(StructDeclaration *sd, Scope *sc) { - if (!needOpEquals()) + if (!needOpEquals(sd)) return NULL; // bitwise comparison would work - //printf("StructDeclaration::buildXopEquals() %s\n", toChars()); - if (Dsymbol *eq = search_function(this, Id::eq)) + //printf("StructDeclaration::buildXopEquals() %s\n", sd->toChars()); + if (Dsymbol *eq = search_function(sd, Id::eq)) { if (FuncDeclaration *fd = eq->isFuncDeclaration()) { @@ -484,7 +478,7 @@ FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc) /* const bool opEquals(ref const S s); */ Parameters *parameters = new Parameters; - parameters->push(new Parameter(STCref | STCconst, type, NULL, NULL)); + parameters->push(new Parameter(STCref | STCconst, sd->type, NULL, NULL)); tfeqptr = new TypeFunction(parameters, Type::tbool, 0, LINKd); tfeqptr->mod = MODconst; tfeqptr = (TypeFunction *)tfeqptr->semantic(Loc(), &scx); @@ -495,13 +489,13 @@ FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc) } } - if (!xerreq) + if (!sd->xerreq) { // object._xopEquals Identifier *id = Lexer::idPool("_xopEquals"); - Expression *e = new IdentifierExp(loc, Id::empty); - e = new DotIdExp(loc, e, Id::object); - e = new DotIdExp(loc, e, id); + Expression *e = new IdentifierExp(sd->loc, Id::empty); + e = new DotIdExp(sd->loc, e, Id::object); + e = new DotIdExp(sd->loc, e, id); e = e->semantic(sc); Dsymbol *s = getDsymbol(e); if (!s) @@ -510,17 +504,16 @@ FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc) fatal(); } assert(s); - xerreq = s->isFuncDeclaration(); + sd->xerreq = s->isFuncDeclaration(); } Loc declLoc = Loc(); // loc is unnecessary so __xopEquals is never called directly Loc loc = Loc(); // loc is unnecessary so errors are gagged Parameters *parameters = new Parameters; - parameters->push(new Parameter(STCref | STCconst, type, Id::p, NULL)); - parameters->push(new Parameter(STCref | STCconst, type, Id::q, NULL)); + parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL)); + parameters->push(new Parameter(STCref | STCconst, sd->type, Id::q, NULL)); TypeFunction *tf = new TypeFunction(parameters, Type::tbool, 0, LINKd); - tf = (TypeFunction *)tf->semantic(loc, sc); Identifier *id = Id::xopEquals; FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); @@ -541,7 +534,7 @@ FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc) sc2->pop(); if (global.endGagging(errors)) // if errors happened - fop = xerreq; + fop = sd->xerreq; return fop; } @@ -557,10 +550,10 @@ FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc) * const objects comparison, it will throw "not implemented" Error in runtime. */ -FuncDeclaration *StructDeclaration::buildXopCmp(Scope *sc) +FuncDeclaration *buildXopCmp(StructDeclaration *sd, Scope *sc) { //printf("StructDeclaration::buildXopCmp() %s\n", toChars()); - if (Dsymbol *cmp = search_function(this, Id::cmp)) + if (Dsymbol *cmp = search_function(sd, Id::cmp)) { if (FuncDeclaration *fd = cmp->isFuncDeclaration()) { @@ -571,7 +564,7 @@ FuncDeclaration *StructDeclaration::buildXopCmp(Scope *sc) /* const int opCmp(ref const S s); */ Parameters *parameters = new Parameters; - parameters->push(new Parameter(STCref | STCconst, type, NULL, NULL)); + parameters->push(new Parameter(STCref | STCconst, sd->type, NULL, NULL)); tfcmpptr = new TypeFunction(parameters, Type::tint32, 0, LINKd); tfcmpptr->mod = MODconst; tfcmpptr = (TypeFunction *)tfcmpptr->semantic(Loc(), &scx); @@ -587,8 +580,8 @@ FuncDeclaration *StructDeclaration::buildXopCmp(Scope *sc) /* Check opCmp member exists. * Consider 'alias this', but except opDispatch. */ - Expression *e = new DsymbolExp(loc, this); - e = new DotIdExp(loc, e, Id::cmp); + Expression *e = new DsymbolExp(sd->loc, sd); + e = new DotIdExp(sd->loc, e, Id::cmp); Scope *sc2 = sc->push(); e = e->trySemantic(sc2); sc2->pop(); @@ -622,13 +615,13 @@ FuncDeclaration *StructDeclaration::buildXopCmp(Scope *sc) #endif } - if (!xerrcmp) + if (!sd->xerrcmp) { // object._xopCmp Identifier *id = Lexer::idPool("_xopCmp"); - Expression *e = new IdentifierExp(loc, Id::empty); - e = new DotIdExp(loc, e, Id::object); - e = new DotIdExp(loc, e, id); + Expression *e = new IdentifierExp(sd->loc, Id::empty); + e = new DotIdExp(sd->loc, e, Id::object); + e = new DotIdExp(sd->loc, e, id); e = e->semantic(sc); Dsymbol *s = getDsymbol(e); if (!s) @@ -637,24 +630,27 @@ FuncDeclaration *StructDeclaration::buildXopCmp(Scope *sc) fatal(); } assert(s); - xerrcmp = s->isFuncDeclaration(); + sd->xerrcmp = s->isFuncDeclaration(); } Loc declLoc = Loc(); // loc is unnecessary so __xopCmp is never called directly Loc loc = Loc(); // loc is unnecessary so errors are gagged Parameters *parameters = new Parameters; - parameters->push(new Parameter(STCref | STCconst, type, Id::p, NULL)); - parameters->push(new Parameter(STCref | STCconst, type, Id::q, NULL)); + parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL)); + parameters->push(new Parameter(STCref | STCconst, sd->type, Id::q, NULL)); TypeFunction *tf = new TypeFunction(parameters, Type::tint32, 0, LINKd); - tf = (TypeFunction *)tf->semantic(loc, sc); Identifier *id = Id::xopCmp; FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); Expression *e1 = new IdentifierExp(loc, Id::p); Expression *e2 = new IdentifierExp(loc, Id::q); +#ifdef IN_GCC Expression *e = new CallExp(loc, new DotIdExp(loc, e1, Id::cmp), e2); +#else + Expression *e = new CallExp(loc, new DotIdExp(loc, e2, Id::cmp), e1); +#endif fop->fbody = new ReturnStatement(loc, e); @@ -668,17 +664,130 @@ FuncDeclaration *StructDeclaration::buildXopCmp(Scope *sc) sc2->pop(); if (global.endGagging(errors)) // if errors happened - fop = xerrcmp; + fop = sd->xerrcmp; + + return fop; +} + +/******************************************* + * We need a toHash for the struct if + * any fields has a toHash. + * Generate one if a user-specified one does not exist. + */ + +bool needToHash(StructDeclaration *sd) +{ + //printf("StructDeclaration::needToHash() %s\n", sd->toChars()); + + if (sd->xhash) + goto Lneed; + + if (sd->isUnionDeclaration()) + goto Ldontneed; + + /* If any of the fields has an opEquals, then we + * need it too. + */ + for (size_t i = 0; i < sd->fields.dim; i++) + { + VarDeclaration *v = sd->fields[i]; + if (v->storage_class & STCref) + continue; + Type *tv = v->type->toBasetype(); + if (tv->isfloating()) + { + // This is necessray for: + // 1. comparison of +0.0 and -0.0 should be true. + goto Lneed; + } + if (tv->ty == Tarray) + goto Lneed; + if (tv->ty == Taarray) + goto Lneed; + if (tv->ty == Tclass) + goto Lneed; + tv = tv->baseElemOf(); + if (tv->ty == Tstruct) + { + TypeStruct *ts = (TypeStruct *)tv; + if (needToHash(ts->sym)) + goto Lneed; + } + } +Ldontneed: + //printf("\tdontneed\n"); + return false; + +Lneed: + //printf("\tneed\n"); + return true; +} +/****************************************** + * Build __xtoHash for non-bitwise hashing + * static hash_t xtoHash(ref const S p) nothrow @trusted; + */ + +FuncDeclaration *buildXtoHash(StructDeclaration *sd, Scope *sc) +{ + if (Dsymbol *s = search_function(sd, Id::tohash)) + { + static TypeFunction *tftohash; + if (!tftohash) + { + tftohash = new TypeFunction(NULL, Type::thash_t, 0, LINKd); + tftohash->mod = MODconst; + tftohash = (TypeFunction *)tftohash->merge(); + } + + if (FuncDeclaration *fd = s->isFuncDeclaration()) + { + fd = fd->overloadExactMatch(tftohash); + if (fd) + return fd; + } + } + + if (!needToHash(sd)) + return NULL; + + //printf("StructDeclaration::buildXtoHash() %s\n", sd->toPrettyChars()); + Loc declLoc = Loc(); // loc is unnecessary so __xtoHash is never called directly + Loc loc = Loc(); // internal code should have no loc to prevent coverage + + Parameters *parameters = new Parameters(); + parameters->push(new Parameter(STCref | STCconst, sd->type, Id::p, NULL)); + TypeFunction *tf = new TypeFunction(parameters, Type::thash_t, 0, LINKd, STCnothrow | STCtrusted); + + Identifier *id = Id::xtoHash; + FuncDeclaration *fop = new FuncDeclaration(declLoc, Loc(), id, STCstatic, tf); + + const char *code = + "size_t h = 0;" + "foreach (i, T; typeof(p.tupleof))" + " h += typeid(T).getHash(cast(const void*)&p.tupleof[i]);" + "return h;"; + fop->fbody = new CompileStatement(loc, new StringExp(loc, (char *)code)); + + Scope *sc2 = sc->push(); + sc2->stc = 0; + sc2->linkage = LINKd; + + fop->semantic(sc2); + fop->semantic2(sc2); + + sc2->pop(); + + //printf("%s fop = %s %s\n", sd->toChars(), fop->toChars(), fop->type->toChars()); return fop; } /******************************************* * Build copy constructor for struct. - * void __cpctpr(ref const S s) const [pure nothrow @trusted] + * void __cpctpr(ref const S s) [pure nothrow @trusted] * { - * (*cast(S*)&this) = *cast(S*)s; - * (*cast(S*)&this).postBlit(); + * this = s; // blit copy + * this.postBlit(); * } * * Copy constructors are compiler generated only, and are only @@ -690,52 +799,49 @@ FuncDeclaration *StructDeclaration::buildXopCmp(Scope *sc) * - no fields are overlooked */ -FuncDeclaration *StructDeclaration::buildCpCtor(Scope *sc) +FuncDeclaration *buildCpCtor(StructDeclaration *sd, Scope *sc) { /* Copy constructor is only necessary if there is a postblit function, * otherwise the code generator will just do a bit copy. */ - if (!postblit) + if (!sd->postblit) return NULL; //printf("StructDeclaration::buildCpCtor() %s\n", toChars()); - StorageClass stc = STCsafe | STCnothrow | STCpure; - Loc declLoc = postblit->loc; + StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; + Loc declLoc = sd->postblit->loc; Loc loc = Loc(); // internal code should have no loc to prevent coverage - stc = mergeFuncAttrs(stc, postblit->storage_class); + stc = mergeFuncAttrs(stc, sd->postblit); if (stc & STCsafe) // change to @trusted for unsafe casts stc = (stc & ~STCsafe) | STCtrusted; Parameters *fparams = new Parameters; - fparams->push(new Parameter(STCref, type->constOf(), Id::p, NULL)); + fparams->push(new Parameter(STCref, sd->type->constOf(), Id::p, NULL)); Type *tf = new TypeFunction(fparams, Type::tvoid, 0, LINKd, stc); - tf->mod = MODconst; FuncDeclaration *fcp = new FuncDeclaration(declLoc, Loc(), Id::cpctor, stc, tf); if (!(stc & STCdisable)) { - // Build *this = p; - Expression *e = new ThisExp(loc); - AssignExp *ea = new AssignExp(loc, - new PtrExp(loc, new CastExp(loc, new AddrExp(loc, e), type->mutableOf()->pointerTo())), - new PtrExp(loc, new CastExp(loc, new AddrExp(loc, new IdentifierExp(loc, Id::p)), type->mutableOf()->pointerTo())) + Expression *e; + + // Build this = p; + e = new BlitExp(loc, + new PtrExp(loc, new AddrExp(loc, new ThisExp(loc))), + new PtrExp(loc, new AddrExp(loc, new IdentifierExp(loc, Id::p))) ); - ea->op = TOKblit; - Statement *s = new ExpStatement(loc, ea); + Statement *s = new ExpStatement(loc, e); // Build postBlit(); - e = new ThisExp(loc); - e = new PtrExp(loc, new CastExp(loc, new AddrExp(loc, e), type->mutableOf()->pointerTo())); - e = new DotVarExp(loc, e, postblit, 0); + e = new DotVarExp(loc, new ThisExp(loc), sd->postblit, 0); e = new CallExp(loc, e); s = new CompoundStatement(loc, s, new ExpStatement(loc, e)); fcp->fbody = s; } - members->push(fcp); + sd->members->push(fcp); sc = sc->push(); sc->stc = 0; @@ -756,17 +862,17 @@ FuncDeclaration *StructDeclaration::buildCpCtor(Scope *sc) * and the ordering changes (runs forward instead of backwards). */ -FuncDeclaration *StructDeclaration::buildPostBlit(Scope *sc) +FuncDeclaration *buildPostBlit(StructDeclaration *sd, Scope *sc) { - //printf("StructDeclaration::buildPostBlit() %s\n", toChars()); - StorageClass stc = STCsafe | STCnothrow | STCpure; - Loc declLoc = postblits.dim ? postblits[0]->loc : this->loc; + //printf("StructDeclaration::buildPostBlit() %s\n", sd->toChars()); + StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; + Loc declLoc = sd->postblits.dim ? sd->postblits[0]->loc : sd->loc; Loc loc = Loc(); // internal code should have no loc to prevent coverage Expression *e = NULL; - for (size_t i = 0; i < fields.dim; i++) + for (size_t i = 0; i < sd->fields.dim; i++) { - VarDeclaration *v = fields[i]; + VarDeclaration *v = sd->fields[i]; if (v->storage_class & STCref) continue; Type *tv = v->type->toBasetype(); @@ -780,10 +886,10 @@ FuncDeclaration *StructDeclaration::buildPostBlit(Scope *sc) if (tv->ty == Tstruct) { TypeStruct *ts = (TypeStruct *)tv; - StructDeclaration *sd = ts->sym; - if (sd->postblit && dim) + StructDeclaration *sd2 = ts->sym; + if (sd2->postblit && dim) { - stc = mergeFuncAttrs(stc, sd->postblit->storage_class); + stc = mergeFuncAttrs(stc, sd2->postblit); if (stc & STCdisable) { e = NULL; @@ -796,7 +902,7 @@ FuncDeclaration *StructDeclaration::buildPostBlit(Scope *sc) if (v->type->toBasetype()->ty == Tstruct) { // this.v.postblit() - ex = new DotVarExp(loc, ex, sd->postblit, 0); + ex = new DotVarExp(loc, ex, sd2->postblit, 0); ex = new CallExp(loc, ex); } else @@ -818,29 +924,30 @@ FuncDeclaration *StructDeclaration::buildPostBlit(Scope *sc) /* Build our own "postblit" which executes e */ if (e || (stc & STCdisable)) - { //printf("Building __fieldPostBlit()\n"); + { + //printf("Building __fieldPostBlit()\n"); PostBlitDeclaration *dd = new PostBlitDeclaration(declLoc, Loc(), stc, Lexer::idPool("__fieldPostBlit")); dd->fbody = new ExpStatement(loc, e); - postblits.shift(dd); - members->push(dd); + sd->postblits.shift(dd); + sd->members->push(dd); dd->semantic(sc); } - switch (postblits.dim) + switch (sd->postblits.dim) { case 0: return NULL; case 1: - return postblits[0]; + return sd->postblits[0]; default: e = NULL; - stc = STCsafe | STCnothrow | STCpure; - for (size_t i = 0; i < postblits.dim; i++) + stc = STCsafe | STCnothrow | STCpure | STCnogc; + for (size_t i = 0; i < sd->postblits.dim; i++) { - FuncDeclaration *fd = postblits[i]; - stc = mergeFuncAttrs(stc, fd->storage_class); + FuncDeclaration *fd = sd->postblits[i]; + stc = mergeFuncAttrs(stc, fd); if (stc & STCdisable) { e = NULL; @@ -853,7 +960,7 @@ FuncDeclaration *StructDeclaration::buildPostBlit(Scope *sc) } PostBlitDeclaration *dd = new PostBlitDeclaration(declLoc, Loc(), stc, Lexer::idPool("__aggrPostBlit")); dd->fbody = new ExpStatement(loc, e); - members->push(dd); + sd->members->push(dd); dd->semantic(sc); return dd; } @@ -867,17 +974,17 @@ FuncDeclaration *StructDeclaration::buildPostBlit(Scope *sc) * and the ordering changes (runs backward instead of forwards). */ -FuncDeclaration *AggregateDeclaration::buildDtor(Scope *sc) +FuncDeclaration *buildDtor(AggregateDeclaration *ad, Scope *sc) { - //printf("AggregateDeclaration::buildDtor() %s\n", toChars()); - StorageClass stc = STCsafe | STCnothrow | STCpure; - Loc declLoc = dtors.dim ? dtors[0]->loc : this->loc; + //printf("AggregateDeclaration::buildDtor() %s\n", ad->toChars()); + StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; + Loc declLoc = ad->dtors.dim ? ad->dtors[0]->loc : ad->loc; Loc loc = Loc(); // internal code should have no loc to prevent coverage Expression *e = NULL; - for (size_t i = 0; i < fields.dim; i++) + for (size_t i = 0; i < ad->fields.dim; i++) { - VarDeclaration *v = fields[i]; + VarDeclaration *v = ad->fields[i]; if (v->storage_class & STCref) continue; Type *tv = v->type->toBasetype(); @@ -894,7 +1001,7 @@ FuncDeclaration *AggregateDeclaration::buildDtor(Scope *sc) StructDeclaration *sd = ts->sym; if (sd->dtor && dim) { - stc = mergeFuncAttrs(stc, sd->dtor->storage_class); + stc = mergeFuncAttrs(stc, sd->dtor); if (stc & STCdisable) { e = NULL; @@ -906,7 +1013,8 @@ FuncDeclaration *AggregateDeclaration::buildDtor(Scope *sc) ex = new DotVarExp(loc, ex, v, 0); if (v->type->toBasetype()->ty == Tstruct) - { // this.v.dtor() + { + // this.v.dtor() ex = new DotVarExp(loc, ex, sd->dtor, 0); ex = new CallExp(loc, ex); } @@ -929,29 +1037,30 @@ FuncDeclaration *AggregateDeclaration::buildDtor(Scope *sc) /* Build our own "destructor" which executes e */ if (e || (stc & STCdisable)) - { //printf("Building __fieldDtor()\n"); + { + //printf("Building __fieldDtor()\n"); DtorDeclaration *dd = new DtorDeclaration(declLoc, Loc(), stc, Lexer::idPool("__fieldDtor")); dd->fbody = new ExpStatement(loc, e); - dtors.shift(dd); - members->push(dd); + ad->dtors.shift(dd); + ad->members->push(dd); dd->semantic(sc); } - switch (dtors.dim) + switch (ad->dtors.dim) { case 0: return NULL; case 1: - return dtors[0]; + return ad->dtors[0]; default: e = NULL; - stc = STCsafe | STCnothrow | STCpure; - for (size_t i = 0; i < dtors.dim; i++) + stc = STCsafe | STCnothrow | STCpure | STCnogc; + for (size_t i = 0; i < ad->dtors.dim; i++) { - FuncDeclaration *fd = dtors[i]; - stc = mergeFuncAttrs(stc, fd->storage_class); + FuncDeclaration *fd = ad->dtors[i]; + stc = mergeFuncAttrs(stc, fd); if (stc & STCdisable) { e = NULL; @@ -964,7 +1073,7 @@ FuncDeclaration *AggregateDeclaration::buildDtor(Scope *sc) } DtorDeclaration *dd = new DtorDeclaration(declLoc, Loc(), stc, Lexer::idPool("__aggrDtor")); dd->fbody = new ExpStatement(loc, e); - members->push(dd); + ad->members->push(dd); dd->semantic(sc); return dd; } @@ -979,13 +1088,13 @@ FuncDeclaration *AggregateDeclaration::buildDtor(Scope *sc) * } */ -FuncDeclaration *AggregateDeclaration::buildInv(Scope *sc) +FuncDeclaration *buildInv(AggregateDeclaration *ad, Scope *sc) { - StorageClass stc = STCsafe | STCnothrow | STCpure; - Loc declLoc = this->loc; + StorageClass stc = STCsafe | STCnothrow | STCpure | STCnogc; + Loc declLoc = ad->loc; Loc loc = Loc(); // internal code should have no loc to prevent coverage - switch (invs.dim) + switch (ad->invs.dim) { case 0: return NULL; @@ -997,32 +1106,32 @@ FuncDeclaration *AggregateDeclaration::buildInv(Scope *sc) default: Expression *e = NULL; StorageClass stcx = 0; - for (size_t i = 0; i < invs.dim; i++) + for (size_t i = 0; i < ad->invs.dim; i++) { - stc = mergeFuncAttrs(stc, invs[i]->storage_class); + stc = mergeFuncAttrs(stc, ad->invs[i]); if (stc & STCdisable) { // What should do? } - StorageClass stcy = invs[i]->storage_class & (STCshared | STCsynchronized); + StorageClass stcy = (ad->invs[i]->storage_class & STCsynchronized) | + (ad->invs[i]->type->mod & MODshared ? STCshared : 0); if (i == 0) stcx = stcy; else if (stcx ^ stcy) { #if 1 // currently rejects - error(invs[i]->loc, "mixing invariants with shared/synchronized differene is not supported"); + ad->error(ad->invs[i]->loc, "mixing invariants with shared/synchronized differene is not supported"); e = NULL; break; #endif } - e = Expression::combine(e, new CallExp(loc, new VarExp(loc, invs[i]))); + e = Expression::combine(e, new CallExp(loc, new VarExp(loc, ad->invs[i]))); } InvariantDeclaration *inv; inv = new InvariantDeclaration(declLoc, Loc(), stc | stcx, Id::classInvariant); inv->fbody = new ExpStatement(loc, e); - members->push(inv); + ad->members->push(inv); inv->semantic(sc); return inv; } } - diff --git a/gcc/d/dfrontend/complex_t.h b/gcc/d/dfrontend/complex_t.h index fa39271eb..12f2048be 100644 --- a/gcc/d/dfrontend/complex_t.h +++ b/gcc/d/dfrontend/complex_t.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2006 by Digital Mars -// All Rights Reserved -// written by Walter Bright and Burton Radons -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/complex_t.h + */ #ifndef DMD_COMPLEX_T_H #define DMD_COMPLEX_T_H diff --git a/gcc/d/dfrontend/cond.c b/gcc/d/dfrontend/cond.c index 1265f6ae5..9a36a01f1 100644 --- a/gcc/d/dfrontend/cond.c +++ b/gcc/d/dfrontend/cond.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/cond.c + */ #include #include @@ -105,7 +106,7 @@ void printDepsConditional(Scope *sc, DVCondition* condition, const char* depType } -int DebugCondition::include(Scope *sc, ScopeDsymbol *s) +int DebugCondition::include(Scope *sc, ScopeDsymbol *sds) { //printf("DebugCondition::include() level = %d, debuglevel = %d\n", level, global.params.debuglevel); if (inc == 0) @@ -202,6 +203,8 @@ bool VersionCondition::isPredefined(const char *ident) "MIPS_EABI", "MIPS_SoftFloat", "MIPS_HardFloat", + "NVPTX", + "NVPTX64", "SPARC", "SPARC_V8Plus", "SPARC_SoftFloat", @@ -267,7 +270,7 @@ VersionCondition::VersionCondition(Module *mod, unsigned level, Identifier *iden { } -int VersionCondition::include(Scope *sc, ScopeDsymbol *s) +int VersionCondition::include(Scope *sc, ScopeDsymbol *sds) { //printf("VersionCondition::include() level = %d, versionlevel = %d\n", level, global.params.versionlevel); //if (ident) printf("\tident = '%s'\n", ident->toChars()); @@ -322,13 +325,13 @@ Condition *StaticIfCondition::syntaxCopy() return new StaticIfCondition(loc, exp->syntaxCopy()); } -int StaticIfCondition::include(Scope *sc, ScopeDsymbol *s) +int StaticIfCondition::include(Scope *sc, ScopeDsymbol *sds) { #if 0 - printf("StaticIfCondition::include(sc = %p, s = %p) this=%p inc = %d\n", sc, s, this, inc); - if (s) + printf("StaticIfCondition::include(sc = %p, sds = %p) this=%p inc = %d\n", sc, sds, this, inc); + if (sds) { - printf("\ts = '%s', kind = %s\n", s->toChars(), s->kind()); + printf("\ts = '%s', kind = %s\n", sds->toChars(), sds->kind()); } #endif if (inc == 0) @@ -349,7 +352,8 @@ int StaticIfCondition::include(Scope *sc, ScopeDsymbol *s) ++nest; sc = sc->push(sc->scopesym); - sc->sd = s; // s gets any addMember() + sc->sds = sds; // sds gets any addMember() + //sc->speculative = true; // TODO: static if (is(T U)) { /* U is available */ } sc->flags |= SCOPEstaticif; sc = sc->startCTFE(); diff --git a/gcc/d/dfrontend/cond.h b/gcc/d/dfrontend/cond.h index 9394f72e5..359d67622 100644 --- a/gcc/d/dfrontend/cond.h +++ b/gcc/d/dfrontend/cond.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/cond.h + */ #ifndef DMD_DEBCOND_H #define DMD_DEBCOND_H @@ -18,7 +19,7 @@ class Module; struct Scope; class ScopeDsymbol; class DebugCondition; -#include "lexer.h" // dmdhg +#include "lexer.h" enum TOK; struct HdrGenState; @@ -28,14 +29,15 @@ class Condition { public: Loc loc; - int inc; // 0: not computed yet - // 1: include - // 2: do not include + // 0: not computed yet + // 1: include + // 2: do not include + int inc; Condition(Loc loc); virtual Condition *syntaxCopy() = 0; - virtual int include(Scope *sc, ScopeDsymbol *s) = 0; + virtual int include(Scope *sc, ScopeDsymbol *sds) = 0; virtual void toCBuffer(OutBuffer *buf, HdrGenState *hgs) = 0; virtual DebugCondition *isDebugCondition() { return NULL; } }; @@ -60,7 +62,7 @@ class DebugCondition : public DVCondition DebugCondition(Module *mod, unsigned level, Identifier *ident); - int include(Scope *sc, ScopeDsymbol *s); + int include(Scope *sc, ScopeDsymbol *sds); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); DebugCondition *isDebugCondition() { return this; } }; @@ -80,7 +82,7 @@ class VersionCondition : public DVCondition VersionCondition(Module *mod, unsigned level, Identifier *ident); - int include(Scope *sc, ScopeDsymbol *s); + int include(Scope *sc, ScopeDsymbol *sds); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); }; @@ -92,7 +94,7 @@ class StaticIfCondition : public Condition StaticIfCondition(Loc loc, Expression *exp); Condition *syntaxCopy(); - int include(Scope *sc, ScopeDsymbol *s); + int include(Scope *sc, ScopeDsymbol *sds); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); }; diff --git a/gcc/d/dfrontend/constfold.c b/gcc/d/dfrontend/constfold.c index c8fab792a..262c1e2c1 100644 --- a/gcc/d/dfrontend/constfold.c +++ b/gcc/d/dfrontend/constfold.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/constfold.c + */ #include #include @@ -40,37 +41,26 @@ Expression *expType(Type *type, Expression *e) /* ================================== isConst() ============================== */ -int Expression::isConst() -{ - //printf("Expression::isConst(): %s\n", toChars()); - return 0; -} - -int IntegerExp::isConst() -{ - return 1; -} - -int RealExp::isConst() -{ - return 1; -} - -int ComplexExp::isConst() -{ - return 1; -} - -int NullExp::isConst() +int isConst(Expression *e) { + //printf("Expression::isConst(): %s\n", e->toChars()); + switch(e->op) + { + case TOKint64: + case TOKfloat64: + case TOKcomplex80: + return 1; + case TOKnull: + return 0; + case TOKsymoff: + return 2; + default: + return 0; + } + assert(0); return 0; } -int SymOffExp::isConst() -{ - return 2; -} - /* =============================== constFold() ============================== */ /* The constFold() functions were redundant with the optimize() ones, @@ -144,12 +134,12 @@ Expression *Add(Type *type, Expression *e1, Expression *e2) // This rigamarole is necessary so that -0.0 doesn't get // converted to +0.0 by doing an extraneous add with +0.0 complex_t c1; - real_t r1; - real_t i1; + real_t r1 = ldouble (0.0); + real_t i1 = ldouble (0.0); complex_t c2; - real_t r2; - real_t i2; + real_t r2 = ldouble (0.0); + real_t i2 = ldouble (0.0); complex_t v; int x; @@ -229,12 +219,12 @@ Expression *Min(Type *type, Expression *e1, Expression *e2) // This rigamarole is necessary so that -0.0 doesn't get // converted to +0.0 by doing an extraneous add with +0.0 complex_t c1; - real_t r1; - real_t i1; + real_t r1 = ldouble (0.0); + real_t i1 = ldouble (0.0); complex_t c2; - real_t r2; - real_t i2; + real_t r2 = ldouble (0.0); + real_t i2 = ldouble (0.0); complex_t v; int x; @@ -677,7 +667,7 @@ Expression *Xor(Type *type, Expression *e1, Expression *e2) Expression *Equal(TOK op, Type *type, Expression *e1, Expression *e2) { Expression *e; Loc loc = e1->loc; - int cmp; + int cmp = 0; real_t r1; real_t r2; @@ -1260,7 +1250,7 @@ Expression *Index(Type *type, Expression *e1, Expression *e2) e = (*ale->elements)[(size_t)i]; e->type = type; e->loc = loc; - if (e->hasSideEffect()) + if (hasSideEffect(e)) e = EXP_CANT_INTERPRET; } } @@ -1279,7 +1269,7 @@ Expression *Index(Type *type, Expression *e1, Expression *e2) { e = (*ale->elements)[(size_t)i]; e->type = type; e->loc = loc; - if (e->hasSideEffect()) + if (hasSideEffect(e)) e = EXP_CANT_INTERPRET; } } @@ -1300,7 +1290,7 @@ Expression *Index(Type *type, Expression *e1, Expression *e2) { e = (*ae->values)[i]; e->type = type; e->loc = loc; - if (e->hasSideEffect()) + if (hasSideEffect(e)) e = EXP_CANT_INTERPRET; break; } @@ -1353,7 +1343,7 @@ Expression *Slice(Type *type, Expression *e1, Expression *lwr, Expression *upr) } else if (e1->op == TOKarrayliteral && lwr->op == TOKint64 && upr->op == TOKint64 && - !e1->hasSideEffect()) + !hasSideEffect(e1)) { ArrayLiteralExp *es1 = (ArrayLiteralExp *)e1; uinteger_t ilwr = lwr->toInteger(); uinteger_t iupr = upr->toInteger(); @@ -1377,7 +1367,7 @@ Expression *Slice(Type *type, Expression *e1, Expression *lwr, Expression *upr) return e; } -/* Set a slice of char array literal 'existingAE' from a string 'newval'. +/* Set a slice of char/integer array literal 'existingAE' from a string 'newval'. * existingAE[firstIndex..firstIndex+newval.length] = newval. */ void sliceAssignArrayLiteralFromString(ArrayLiteralExp *existingAE, StringExp *newval, size_t firstIndex) @@ -1714,11 +1704,13 @@ Expression *Cat(Type *type, Expression *e1, Expression *e2) e->type = type; } else if ((e1->op == TOKarrayliteral || e1->op == TOKnull) && + e1->type->toBasetype()->nextOf() && e1->type->toBasetype()->nextOf()->equals(e2->type)) { ArrayLiteralExp *es1; if (e1->op == TOKarrayliteral) - { es1 = (ArrayLiteralExp *)e1; + { + es1 = (ArrayLiteralExp *)e1; es1 = new ArrayLiteralExp(es1->loc, (Expressions *)es1->elements->copy()); es1->elements->push(e2); } @@ -1795,4 +1787,3 @@ Expression *Ptr(Type *type, Expression *e1) } return EXP_CANT_INTERPRET; } - diff --git a/gcc/d/dfrontend/cppmangle.c b/gcc/d/dfrontend/cppmangle.c index 16ae34863..f612a4e98 100644 --- a/gcc/d/dfrontend/cppmangle.c +++ b/gcc/d/dfrontend/cppmangle.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/cppmangle.c + */ #include #include @@ -25,6 +26,7 @@ #include "enum.h" #include "import.h" #include "aggregate.h" +#include "target.h" #if IN_GCC || TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS @@ -74,19 +76,6 @@ class CppMangleVisitor : public Visitor return 1; } } - components.push(p); - return 0; - } - - int exist(RootObject *p) - { - for (size_t i = 0; i < components.dim; i++) - { - if (p == components[i]) - { - return 1; - } - } return 0; } @@ -103,15 +92,13 @@ class CppMangleVisitor : public Visitor void prefix_name(Dsymbol *s) { - if (!substitute(s)) - { - Dsymbol *p = s->toParent(); - if (p && !p->isModule()) - { - prefix_name(p); - } - source_name(s); - } + if (substitute(s)) + return; + Dsymbol *p = s->toParent(); + if (p && !p->isModule()) + prefix_name(p); + source_name(s); + store(s); } public: @@ -123,8 +110,7 @@ class CppMangleVisitor : public Visitor char *finish() { - buf.writeByte(0); - return (char *)buf.extractData(); + return buf.extractString(); } void cpp_mangle_name(Dsymbol *s) @@ -165,10 +151,11 @@ class CppMangleVisitor : public Visitor * C++ analog. * u */ - if (!substitute(t)) - { assert(t->deco); - buf.printf("u%d%s", strlen(t->deco), t->deco); - } + if (substitute(t)) + return; + assert(t->deco); + buf.printf("u%d%s", strlen(t->deco), t->deco); + store(t); } void visit(TypeBasic *t) @@ -210,10 +197,15 @@ class CppMangleVisitor : public Visitor case Tint32: c = 'i'; break; case Tuns32: c = 'j'; break; case Tfloat32: c = 'f'; break; +#ifdef IN_GCC case Tint64: c = 'x'; break; case Tuns64: c = 'y'; break; +#else + case Tint64: c = (Target::longsize == 8 ? 'l' : 'x'); break; + case Tuns64: c = (Target::longsize == 8 ? 'm' : 'y'); break; +#endif case Tfloat64: c = 'd'; break; - case Tfloat80: c = 'e'; break; + case Tfloat80: c = (Target::realsize - Target::realpad == 16) ? 'g' : 'e'; break; case Tbool: c = 'b'; break; case Tchar: c = 'c'; break; case Twchar: c = 't'; break; @@ -232,6 +224,7 @@ class CppMangleVisitor : public Visitor { if (substitute(t)) return; + store(t); } if (t->isConst()) @@ -246,20 +239,20 @@ class CppMangleVisitor : public Visitor void visit(TypeVector *t) { - if (!substitute(t)) - { - buf.writestring("U8__vector"); - t->basetype->accept(this); - } + if (substitute(t)) + return; + buf.writestring("U8__vector"); + t->basetype->accept(this); + store(t); } void visit(TypeSArray *t) { - if (!substitute(t)) - { - buf.printf("A%llu_", t->dim ? t->dim->toInteger() : 0); - t->next->accept(this); - } + if (substitute(t)) + return; + buf.printf("A%llu_", t->dim ? t->dim->toInteger() : 0); + t->next->accept(this); + store(t); } void visit(TypeDArray *t) @@ -274,26 +267,20 @@ class CppMangleVisitor : public Visitor void visit(TypePointer *t) { - if (!exist(t)) - { - buf.writeByte('P'); - t->next->accept(this); - store(t); - } - else - substitute(t); + if (substitute(t)) + return; + buf.writeByte('P'); + t->next->accept(this); + store(t); } void visit(TypeReference *t) { - if (!exist(t)) - { - buf.writeByte('R'); - t->next->accept(this); - store(t); - } - else - substitute(t); + if (substitute(t)) + return; + buf.writeByte('R'); + t->next->accept(this); + store(t); } void visit(TypeFunction *t) @@ -320,18 +307,15 @@ class CppMangleVisitor : public Visitor TypeFunctions for non-static member functions, and non-static member functions of different classes. */ - if (!exist(t)) - { - buf.writeByte('F'); - if (t->linkage == LINKc) - buf.writeByte('Y'); - t->next->accept(this); - argsCppMangle(t->parameters, t->varargs); - buf.writeByte('E'); - store(t); - } - else - substitute(t); + if (substitute(t)) + return; + buf.writeByte('F'); + if (t->linkage == LINKc) + buf.writeByte('Y'); + t->next->accept(this); + argsCppMangle(t->parameters, t->varargs); + buf.writeByte('E'); + store(t); } void visit(TypeDelegate *t) @@ -341,56 +325,45 @@ class CppMangleVisitor : public Visitor void visit(TypeStruct *t) { - if (!exist(t)) + if (substitute(t)) + return; + if (t->isConst()) + buf.writeByte('K'); + if (!substitute(t->sym)) { - if (t->isConst()) - buf.writeByte('K'); - - if (!substitute(t->sym)) - cpp_mangle_name(t->sym); - - if (t->isConst()) - store(t); + cpp_mangle_name(t->sym); + store(t->sym); } - else - substitute(t); + if (t->isConst()) + store(t); } void visit(TypeEnum *t) { - if (!exist(t)) + if (substitute(t)) + return; + if (t->isConst()) + buf.writeByte('K'); + if (!substitute(t->sym)) { - if (t->isConst()) - buf.writeByte('K'); - - if (!substitute(t->sym)) - cpp_mangle_name(t->sym); - - if (t->isConst()) - store(t); + cpp_mangle_name(t->sym); + store(t->sym); } - else - substitute(t); - } - - void visit(TypeTypedef *t) - { - visit((Type *)t); + if (t->isConst()) + store(t); } void visit(TypeClass *t) { - if (!exist(t)) + if (substitute(t)) + return; + buf.writeByte('P'); + if (!substitute(t->sym)) { - buf.writeByte('P'); - - if (!substitute(t->sym)) - cpp_mangle_name(t->sym); - - store(t); + cpp_mangle_name(t->sym); + store(t->sym); } - else - substitute(t); + store(t); } struct ArgsCppMangleCtx diff --git a/gcc/d/dfrontend/ctfe.h b/gcc/d/dfrontend/ctfe.h index ff93ba02e..2a8300f11 100644 --- a/gcc/d/dfrontend/ctfe.h +++ b/gcc/d/dfrontend/ctfe.h @@ -1,11 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. + +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/ctfe.h + */ #ifndef DMD_CTFE_H #define DMD_CTFE_H @@ -39,8 +41,6 @@ class ClassReferenceExp : public Expression public: StructLiteralExp *value; ClassReferenceExp(Loc loc, StructLiteralExp *lit, Type *type); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); ClassDeclaration *originalClass(); VarDeclaration *getFieldAt(unsigned index); @@ -55,8 +55,16 @@ class ClassReferenceExp : public Expression dt_t **toInstanceDt(dt_t **pdt); dt_t **toDt2(dt_t **pdt, ClassDeclaration *cd, Dts *dts); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; +// The various functions are used only to detect compiler CTFE bugs +Expression *getValue(VarDeclaration *vd); +bool hasValue(VarDeclaration *vd); +void setValueNull(VarDeclaration *vd); +void setValueWithoutChecking(VarDeclaration *vd, Expression *newval); +void setValue(VarDeclaration *vd, Expression *newval); + /// Return index of the field, or -1 if not found /// Same as getFieldIndex, but checks for a direct match with the VarDeclaration int findFieldIndexByName(StructDeclaration *sd, VarDeclaration *v); @@ -71,9 +79,11 @@ class VoidInitExp : public Expression VoidInitExp(VarDeclaration *var, Type *type); char *toChars(); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + void accept(Visitor *v) { v->visit(this); } }; +// Create an appropriate void initializer +Expression *voidInitLiteral(Type *t, VarDeclaration *var); /** Fake class which holds the thrown exception. Used for implementing exception handling. @@ -83,10 +93,10 @@ class ThrownExceptionExp : public Expression public: ClassReferenceExp *thrown; // the thing being tossed ThrownExceptionExp(Loc loc, ClassReferenceExp *victim); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); char *toChars(); /// Generate an error message when this exception is not caught void generateUncaughtError(); + void accept(Visitor *v) { v->visit(this); } }; @@ -193,7 +203,7 @@ bool isFloatIntPaint(Type *to, Type *from); // Reinterpret float/int value 'fromVal' as a float/integer of type 'to'. Expression *paintFloatInt(Expression *fromVal, Type *to); -/// Return true if t is an AA, or AssociativeArray!(key, value) +/// Return true if t is an AA bool isAssocArray(Type *t); /// Given a template AA type, extract the corresponding built-in AA type diff --git a/gcc/d/dfrontend/ctfeexpr.c b/gcc/d/dfrontend/ctfeexpr.c index 7ae7c4015..8df1390c6 100644 --- a/gcc/d/dfrontend/ctfeexpr.c +++ b/gcc/d/dfrontend/ctfeexpr.c @@ -1,11 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. + +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/ctfeexpr.c + */ #include #include @@ -21,10 +23,7 @@ #include "id.h" #include "template.h" #include "ctfe.h" - -#ifdef IN_GCC -#include "d-dmd-gcc.h" -#endif +#include "target.h" int RealEquals(real_t x1, real_t x2); @@ -38,17 +37,6 @@ ClassReferenceExp::ClassReferenceExp(Loc loc, StructLiteralExp *lit, Type *type) this->type = type; } -Expression *ClassReferenceExp::interpret(InterState *istate, CtfeGoal goal) -{ - //printf("ClassReferenceExp::interpret() %s\n", value->toChars()); - return this; -} - -void ClassReferenceExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring(value->toChars()); -} - ClassDeclaration *ClassReferenceExp::originalClass() { return value->sd->isClassDeclaration(); @@ -124,21 +112,14 @@ char *VoidInitExp::toChars() return (char *)"void"; } -Expression *VoidInitExp::interpret(InterState *istate, CtfeGoal goal) -{ - error("CTFE internal error: trying to read uninitialized variable"); - assert(0); - return EXP_CANT_INTERPRET; -} - // Return index of the field, or -1 if not found // Same as getFieldIndex, but checks for a direct match with the VarDeclaration int findFieldIndexByName(StructDeclaration *sd, VarDeclaration *v) { - for (int i = 0; i < sd->fields.dim; ++i) + for (size_t i = 0; i < sd->fields.dim; ++i) { if (sd->fields[i] == v) - return i; + return (int)i; } return -1; } @@ -151,12 +132,6 @@ ThrownExceptionExp::ThrownExceptionExp(Loc loc, ClassReferenceExp *victim) : Exp this->type = victim->type; } -Expression *ThrownExceptionExp::interpret(InterState *istate, CtfeGoal) -{ - assert(0); // This should never be interpreted - return this; -} - char *ThrownExceptionExp::toChars() { return (char *)"CTFE ThrownException"; @@ -166,7 +141,7 @@ char *ThrownExceptionExp::toChars() void ThrownExceptionExp::generateUncaughtError() { Expression *e = (*thrown->value->elements)[0]; - StringExp* se = e->toString(); + StringExp* se = e->toStringExp(); thrown->error("Uncaught CTFE exception %s(%s)", thrown->type->toChars(), se ? se->toChars() : e->toChars()); /* Also give the line where the throw statement was. We won't have it @@ -277,12 +252,12 @@ Expression *copyLiteral(Expression *e) r->ownedByCtfe = true; return r; } - /* syntaxCopy doesn't work for struct literals, because of a nasty special - * case: block assignment is permitted inside struct literals, eg, - * an int[4] array can be initialized with a single int. - */ else if (e->op == TOKstructliteral) { + /* syntaxCopy doesn't work for struct literals, because of a nasty special + * case: block assignment is permitted inside struct literals, eg, + * an int[4] array can be initialized with a single int. + */ StructLiteralExp *se = (StructLiteralExp *)e; Expressions *oldelems = se->elements; Expressions * newelems = new Expressions(); @@ -295,9 +270,7 @@ Expression *copyLiteral(Expression *e) VarDeclaration *v = sd->fields[i]; // If it is a void assignment, use the default initializer if (!m) - m = v->type->voidInitLiteral(v); - if (m->op == TOKslice) - m = resolveSlice(m); + m = voidInitLiteral(v->type, v); if ((v->type->ty != m->type->ty) && v->type->ty == Tsarray) { // Block assignment from inside struct literals @@ -309,11 +282,7 @@ Expression *copyLiteral(Expression *e) m = copyLiteral(m); (*newelems)[i] = m; } -#if DMDV2 StructLiteralExp *r = new StructLiteralExp(e->loc, se->sd, newelems, se->stype); -#else - StructLiteralExp *r = new StructLiteralExp(e->loc, se->sd, newelems); -#endif r->type = e->type; r->ownedByCtfe = true; r->origin = ((StructLiteralExp*)e)->origin; @@ -331,8 +300,9 @@ Expression *copyLiteral(Expression *e) r->type = e->type; return r; } - else if ( isPointer(e->type) ) - { // For pointers, we only do a shallow copy. + else if (isPointer(e->type)) + { + // For pointers, we only do a shallow copy. Expression *r; if (e->op == TOKaddress) r = new AddrExp(e->loc, ((AddrExp *)e)->e1); @@ -340,12 +310,8 @@ Expression *copyLiteral(Expression *e) r = new IndexExp(e->loc, ((IndexExp *)e)->e1, ((IndexExp *)e)->e2); else if (e->op == TOKdotvar) { -#if DMDV2 r = new DotVarExp(e->loc, ((DotVarExp *)e)->e1, ((DotVarExp *)e)->var, ((DotVarExp *)e)->hasOverloads); -#else - r = new DotVarExp(e->loc, ((DotVarExp *)e)->e1, ((DotVarExp *)e)->var); -#endif } else assert(0); @@ -353,9 +319,10 @@ Expression *copyLiteral(Expression *e) return r; } else if (e->op == TOKslice) - { // Array slices only do a shallow copy + { + // Array slices only do a shallow copy Expression *r = new SliceExp(e->loc, ((SliceExp *)e)->e1, - ((SliceExp *)e)->lwr, ((SliceExp *)e)->upr); + ((SliceExp *)e)->lwr, ((SliceExp *)e)->upr); r->type = e->type; return r; } @@ -382,9 +349,13 @@ Expression *paintTypeOntoLiteral(Type *type, Expression *lit) if (lit->type->equals(type)) return lit; - // If it is a cast to inout, retain the original type. - if (type->hasWild()) + // If it is a cast to inout, retain the original type of the referenced part. + if (type->hasWild() && type->hasPointers()) + { + lit = lit->copy(); + lit->type = type; return lit; + } Expression *e; if (lit->op == TOKslice) @@ -419,7 +390,8 @@ Expression *paintTypeOntoLiteral(Type *type, Expression *lit) e = aae; } else - { // Can't type paint from struct to struct*; this needs another + { + // Can't type paint from struct to struct*; this needs another // level of indirection if (lit->op == TOKstructliteral && isPointer(type) ) lit->error("CTFE internal error painting %s", type->toChars()); @@ -520,23 +492,12 @@ StringExp *createBlockDuplicatedStringLiteral(Loc loc, Type *type, return se; } -// Return true if t is an AA, or AssociativeArray!(key, value) +// Return true if t is an AA bool isAssocArray(Type *t) { t = t->toBasetype(); if (t->ty == Taarray) return true; -#if DMDV2 - if (t->ty != Tstruct) - return false; - StructDeclaration *sym = ((TypeStruct *)t)->sym; - if (sym->ident == Id::AssociativeArray && sym->parent && - sym->parent->parent && - sym->parent->parent->ident == Id::object) - { - return true; - } -#endif return false; } @@ -546,17 +507,8 @@ TypeAArray *toBuiltinAAType(Type *t) t = t->toBasetype(); if (t->ty == Taarray) return (TypeAArray *)t; -#if DMDV2 - assert(t->ty == Tstruct); - StructDeclaration *sym = ((TypeStruct *)t)->sym; - assert(sym->ident == Id::AssociativeArray); - TemplateInstance *ti = sym->parent->isTemplateInstance(); - assert(ti); - return new TypeAArray((Type *)(*ti->tiargs)[1], (Type *)(*ti->tiargs)[0]); -#else assert(0); return NULL; -#endif } /************** TypeInfo operations ************************************/ @@ -598,12 +550,8 @@ bool isSafePointerCast(Type *srcPointee, Type *destPointee) destPointee = destPointee->nextOf(); } -#if DMDV2 - // It's OK if both are the same (modulo const) - srcPointee = srcPointee->castMod(0); - destPointee = destPointee->castMod(0); -#endif - if (srcPointee == destPointee) + // It's OK if both are the same (modulo const) + if (srcPointee->constConv(destPointee)) return true; // It's OK if function pointers differ only in safe/pure/nothrow @@ -614,6 +562,10 @@ bool isSafePointerCast(Type *srcPointee, Type *destPointee) if (destPointee->ty == Tvoid) return true; + // It's OK to cast from V[K] to void* + if (srcPointee->ty == Taarray && destPointee == Type::tvoidptr) + return true; + // It's OK if they are the same size (static array of) integers, eg: // int* --> uint* // int[5][] --> uint[5][] @@ -853,18 +805,6 @@ int comparePointers(Loc loc, TOK op, Type *type, Expression *agg1, dinteger_t of return cmp; } -union UnionFloatInt -{ - float f; - d_int32 x; -}; - -union UnionDoubleLong -{ - double f; - d_int64 x; -}; - // True if conversion from type 'from' to 'to' involves a reinterpret_cast // floating point -> integer or integer -> floating point bool isFloatIntPaint(Type *to, Type *from) @@ -880,41 +820,8 @@ Expression *paintFloatInt(Expression *fromVal, Type *to) if (exceptionOrCantInterpret(fromVal)) return fromVal; -#ifdef IN_GCC - assert (to->size() == 4 || to->size() == 8); - return d_gcc_paint_type (fromVal, to); -#else - if (to->size() == 4) - { - UnionFloatInt u; - if (to->isintegral()) - { - u.f = fromVal->toReal(); - return new IntegerExp(fromVal->loc, (dinteger_t)ldouble(u.x), to); - } - else - { - u.x = (d_int32)fromVal->toInteger(); - return new RealExp(fromVal->loc, ldouble(u.f), to); - } - } - else if (to->size() == 8) - { - UnionDoubleLong v; - if (to->isintegral()) - { - v.f = fromVal->toReal(); - return new IntegerExp(fromVal->loc, v.x, to); - } - else - { - v.x = fromVal->toInteger(); - return new RealExp(fromVal->loc, ldouble(v.f), to); - } - } - assert(0); - return NULL; // avoid warning -#endif + assert(to->size() == 4 || to->size() == 8); + return Target::paintAsType(fromVal, to); } @@ -929,10 +836,10 @@ void intUnary(TOK op, IntegerExp *e) switch (op) { case TOKneg: - e->value = -e->value; + e->setInteger(-e->getInteger()); break; case TOKtilde: - e->value = ~e->value; + e->setInteger(~e->getInteger()); break; default: assert(0); @@ -948,26 +855,27 @@ void intBinary(TOK op, IntegerExp *dest, Type *type, IntegerExp *e1, IntegerExp switch (op) { case TOKand: - result = e1->value & e2->value; + result = e1->getInteger() & e2->getInteger(); break; case TOKor: - result = e1->value | e2->value; + result = e1->getInteger() | e2->getInteger(); break; case TOKxor: - result = e1->value ^ e2->value; + result = e1->getInteger() ^ e2->getInteger(); break; case TOKadd: - result = e1->value + e2->value; + result = e1->getInteger() + e2->getInteger(); break; case TOKmin: - result = e1->value - e2->value; + result = e1->getInteger() - e2->getInteger(); break; case TOKmul: - result = e1->value * e2->value; + result = e1->getInteger() * e2->getInteger(); break; case TOKdiv: - { sinteger_t n1 = e1->value; - sinteger_t n2 = e2->value; + { + sinteger_t n1 = e1->getInteger(); + sinteger_t n2 = e2->getInteger(); if (n2 == 0) { e2->error("divide by 0"); @@ -980,8 +888,8 @@ void intBinary(TOK op, IntegerExp *dest, Type *type, IntegerExp *e1, IntegerExp } break; case TOKmod: - { sinteger_t n1 = e1->value; - sinteger_t n2 = e2->value; + { sinteger_t n1 = e1->getInteger(); + sinteger_t n2 = e2->getInteger(); if (n2 == 0) { e2->error("divide by 0"); @@ -1007,13 +915,13 @@ void intBinary(TOK op, IntegerExp *dest, Type *type, IntegerExp *e1, IntegerExp } break; case TOKpow: - { dinteger_t n = e2->value; + { dinteger_t n = e2->getInteger(); if (!e2->type->isunsigned() && (sinteger_t)n < 0) { e2->error("integer ^^ -integer: total loss of precision"); n = 1; } - uinteger_t r = e1->value; + uinteger_t r = e1->getInteger(); result = 1; while (n != 0) { @@ -1025,11 +933,11 @@ void intBinary(TOK op, IntegerExp *dest, Type *type, IntegerExp *e1, IntegerExp } break; case TOKshl: - result = e1->value << e2->value; + result = e1->getInteger() << e2->getInteger(); break; case TOKshr: - { dinteger_t value = e1->value; - dinteger_t dcount = e2->value; + { dinteger_t value = e1->getInteger(); + dinteger_t dcount = e2->getInteger(); assert(dcount <= 0xFFFFFFFF); unsigned count = (unsigned)dcount; switch (e1->type->toBasetype()->ty) @@ -1074,8 +982,8 @@ void intBinary(TOK op, IntegerExp *dest, Type *type, IntegerExp *e1, IntegerExp } break; case TOKushr: - { dinteger_t value = e1->value; - dinteger_t dcount = e2->value; + { dinteger_t value = e1->getInteger(); + dinteger_t dcount = e2->getInteger(); assert(dcount <= 0xFFFFFFFF); unsigned count = (unsigned)dcount; switch (e1->type->toBasetype()->ty) @@ -1112,16 +1020,16 @@ void intBinary(TOK op, IntegerExp *dest, Type *type, IntegerExp *e1, IntegerExp break; case TOKequal: case TOKidentity: - result = (e1->value == e2->value); + result = (e1->getInteger() == e2->getInteger()); break; case TOKnotequal: case TOKnotidentity: - result = (e1->value != e2->value); + result = (e1->getInteger() != e2->getInteger()); break; default: assert(0); } - dest->value = result; + dest->setInteger(result); dest->type = type; } @@ -1735,10 +1643,12 @@ Expression *findKeyInAA(Loc loc, AssocArrayLiteralExp *ae, Expression *e2) * interpreted CTFE expression, so it cannot have side-effects. */ Expression *ctfeIndex(Loc loc, Type *type, Expression *e1, uinteger_t indx) -{ //printf("ctfeIndex(e1 = %s)\n", e1->toChars()); +{ + //printf("ctfeIndex(e1 = %s)\n", e1->toChars()); assert(e1->type); if (e1->op == TOKstring) - { StringExp *es1 = (StringExp *)e1; + { + StringExp *es1 = (StringExp *)e1; if (indx >= es1->len) { error(loc, "string index %llu is out of bounds [0 .. %llu]", indx, (ulonglong)es1->len); @@ -1775,12 +1685,10 @@ Expression *ctfeCast(Loc loc, Type *type, Type *to, Expression *e) // Allow TypeInfo type painting if (isTypeInfo_Class(e->type) && e->type->implicitConvTo(to)) return paintTypeOntoLiteral(to, e); -#if DMDV2 // Allow casting away const for struct literals if (e->op == TOKstructliteral && e->type->toBasetype()->castMod(0) == to->toBasetype()->castMod(0)) return paintTypeOntoLiteral(to, e); -#endif Expression *r = Cast(type, to, e); if (r == EXP_CANT_INTERPRET) error(loc, "cannot cast %s to %s at compile time", e->toChars(), to->toChars()); @@ -1810,6 +1718,8 @@ void assignInPlace(Expression *dest, Expression *src) assert(dest->op == src->op); oldelems = ((StructLiteralExp *)dest)->elements; newelems = ((StructLiteralExp *)src)->elements; + if (((StructLiteralExp *)dest)->sd->isNested() && oldelems->dim == newelems->dim - 1) + oldelems->push(NULL); } else if (dest->op == TOKarrayliteral && src->op==TOKarrayliteral) { @@ -1858,13 +1768,8 @@ void assignInPlace(Expression *dest, Expression *src) void recursiveBlockAssign(ArrayLiteralExp *ae, Expression *val, bool wantRef) { assert( ae->type->ty == Tsarray || ae->type->ty == Tarray); -#if DMDV2 Type *desttype = ((TypeArray *)ae->type)->next->toBasetype()->castMod(0); bool directblk = (val->type->toBasetype()->castMod(0))->equals(desttype); -#else - Type *desttype = ((TypeArray *)ae->type)->next; - bool directblk = (val->type->toBasetype())->equals(desttype); -#endif bool cow = !(val->op == TOKstructliteral || val->op == TOKarrayliteral || val->op == TOKstring); @@ -2017,12 +1922,7 @@ Expression *changeArrayLiteralLength(Loc loc, TypeArray *arrayType, bool isCtfeValueValid(Expression *newval) { -#if DMDV2 - bool isnull = newval->type->ty == Tnull; -#else - bool isnull = false; -#endif - if (isnull || isPointer(newval->type)) + if (newval->type->ty == Tnull || isPointer(newval->type)) { if (newval->op == TOKaddress || newval->op == TOKnull || newval->op == TOKstring) @@ -2049,7 +1949,7 @@ bool isCtfeValueValid(Expression *newval) VarExp *ve = (VarExp *)newval; VarDeclaration *vv = ve->var->isVarDeclaration(); // Must not be a reference to a reference - if (!(vv && vv->getValue() && vv->getValue()->op == TOKvar)) + if (!(vv && getValue(vv) && getValue(vv)->op == TOKvar)) return true; } if (newval->op == TOKdotvar) @@ -2183,8 +2083,8 @@ void showCtfeExpr(Expression *e, int level) { printf("VAR %p %s\n", e, e->toChars()); VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); - if (v && v->getValue()) - showCtfeExpr(v->getValue(), level + 1); + if (v && getValue(v)) + showCtfeExpr(getValue(v), level + 1); } else if (isPointer(e->type)) { @@ -2253,43 +2153,47 @@ void showCtfeExpr(Expression *e, int level) /*************************** Void initialization ***************************/ -Expression *Type::voidInitLiteral(VarDeclaration *var) +Expression *voidInitLiteral(Type *t, VarDeclaration *var) { - return new VoidInitExp(var, this); -} - -Expression *TypeSArray::voidInitLiteral(VarDeclaration *var) -{ - Expression *elem = next->voidInitLiteral(var); + if (t->ty == Tsarray) + { + TypeSArray *tsa = (TypeSArray *)t; + Expression *elem = voidInitLiteral(tsa->next, var); - // For aggregate value types (structs, static arrays) we must - // create an a separate copy for each element. - bool mustCopy = (elem->op == TOKarrayliteral || elem->op == TOKstructliteral); + // For aggregate value types (structs, static arrays) we must + // create an a separate copy for each element. + bool mustCopy = (elem->op == TOKarrayliteral || elem->op == TOKstructliteral); - Expressions *elements = new Expressions(); - size_t d = (size_t)dim->toInteger(); - elements->setDim(d); - for (size_t i = 0; i < d; i++) - { if (mustCopy && i > 0) - elem = copyLiteral(elem); - (*elements)[i] = elem; + Expressions *elements = new Expressions(); + size_t d = (size_t)tsa->dim->toInteger(); + elements->setDim(d); + for (size_t i = 0; i < d; i++) + { + if (mustCopy && i > 0) + elem = copyLiteral(elem); + (*elements)[i] = elem; + } + ArrayLiteralExp *ae = new ArrayLiteralExp(var->loc, elements); + ae->type = tsa; + ae->ownedByCtfe = true; + return ae; } - ArrayLiteralExp *ae = new ArrayLiteralExp(var->loc, elements); - ae->type = this; - ae->ownedByCtfe = true; - return ae; -} - -Expression *TypeStruct::voidInitLiteral(VarDeclaration *var) -{ - Expressions *exps = new Expressions(); - exps->setDim(sym->fields.dim); - for (size_t i = 0; i < sym->fields.dim; i++) + else if (t->ty == Tstruct) { - (*exps)[i] = sym->fields[i]->type->voidInitLiteral(sym->fields[i]); + TypeStruct *ts = (TypeStruct *)t; + Expressions *exps = new Expressions(); + exps->setDim(ts->sym->fields.dim); + for (size_t i = 0; i < ts->sym->fields.dim; i++) + { + (*exps)[i] = voidInitLiteral(ts->sym->fields[i]->type, ts->sym->fields[i]); + } + StructLiteralExp *se = new StructLiteralExp(var->loc, ts->sym, exps); + se->type = ts; + se->ownedByCtfe = true; + return se; + } + else + { + return new VoidInitExp(var, t); } - StructLiteralExp *se = new StructLiteralExp(var->loc, sym, exps); - se->type = this; - se->ownedByCtfe = true; - return se; } diff --git a/gcc/d/dfrontend/declaration.c b/gcc/d/dfrontend/declaration.c index f28ed3fa2..6dbb5ab3f 100644 --- a/gcc/d/dfrontend/declaration.c +++ b/gcc/d/dfrontend/declaration.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/declaration.c + */ #include #include @@ -27,10 +28,10 @@ #include "ctfe.h" #include "target.h" -void checkFrameAccess(Loc loc, Scope *sc, AggregateDeclaration *ad) +bool checkFrameAccess(Loc loc, Scope *sc, AggregateDeclaration *ad, size_t iStart = 0) { if (!ad->isNested()) - return; + return true; Dsymbol *s = sc->func; if (s) @@ -43,16 +44,20 @@ void checkFrameAccess(Loc loc, Scope *sc, AggregateDeclaration *ad) { if (s == sparent) // hit! { - // Is it better moving this check to AggregateDeclaration:semantic? - for (size_t i = 0; i < ad->fields.dim; i++) + bool result = true; + for (size_t i = iStart; i < ad->fields.dim; i++) { VarDeclaration *vd = ad->fields[i]; - if (vd) - if (AggregateDeclaration *ad2 = isAggregate(vd->type)) - if (ad2->isStructDeclaration()) - checkFrameAccess(loc, sc, ad2); + if (AggregateDeclaration *ad2 = isAggregate(vd->type)) + { + if (ad2->isStructDeclaration()) + { + bool r = checkFrameAccess(loc, sc, ad2); + result = result && r; + } + } } - return; + return result; } if (FuncDeclaration *fd = s->isFuncDeclaration()) @@ -69,6 +74,7 @@ void checkFrameAccess(Loc loc, Scope *sc, AggregateDeclaration *ad) } } error(loc, "cannot access frame pointer of %s", ad->toPrettyChars()); + return false; } /********************************* Declaration ****************************/ @@ -151,7 +157,8 @@ int Declaration::checkModify(Loc loc, Scope *sc, Type *t, Expression *e1, int fl } if (v && (isCtorinit() || isField())) - { // It's only modifiable if inside the right constructor + { + // It's only modifiable if inside the right constructor if ((storage_class & (STCforeach | STCref)) == (STCforeach | STCref)) return 2; return modifyFieldVar(loc, sc, v, e1) ? 2 : 1; @@ -270,144 +277,6 @@ bool TupleDeclaration::needThis() } -/********************************* TypedefDeclaration ****************************/ - -TypedefDeclaration::TypedefDeclaration(Loc loc, Identifier *id, Type *basetype, Initializer *init) - : Declaration(id) -{ - this->type = new TypeTypedef(this); - this->basetype = basetype->toBasetype(); - this->init = init; - this->htype = NULL; - this->hbasetype = NULL; - this->loc = loc; - this->sinit = NULL; -} - -Dsymbol *TypedefDeclaration::syntaxCopy(Dsymbol *s) -{ - Type *basetype = this->basetype->syntaxCopy(); - - Initializer *init = NULL; - if (this->init) - init = this->init->syntaxCopy(); - - assert(!s); - TypedefDeclaration *st; - st = new TypedefDeclaration(loc, ident, basetype, init); - - // Syntax copy for header file - if (!htype) // Don't overwrite original - { if (type) // Make copy for both old and new instances - { htype = type->syntaxCopy(); - st->htype = type->syntaxCopy(); - } - } - else // Make copy of original for new instance - st->htype = htype->syntaxCopy(); - if (!hbasetype) - { if (basetype) - { hbasetype = basetype->syntaxCopy(); - st->hbasetype = basetype->syntaxCopy(); - } - } - else - st->hbasetype = hbasetype->syntaxCopy(); - - return st; -} - -void TypedefDeclaration::semantic(Scope *sc) -{ - //printf("TypedefDeclaration::semantic(%s) sem = %d\n", toChars(), sem); - if (sem == SemanticStart) - { - sem = SemanticIn; - parent = sc->parent; - int errors = global.errors; - Type *savedbasetype = basetype; - basetype = basetype->semantic(loc, sc); - if (errors != global.errors) - { - basetype = savedbasetype; - sem = SemanticStart; - return; - } - sem = SemanticDone; - type = type->addStorageClass(storage_class); - Type *savedtype = type; - type = type->semantic(loc, sc); - if (sc->parent->isFuncDeclaration() && init) - semantic2(sc); - if (errors != global.errors) - { - basetype = savedbasetype; - type = savedtype; - sem = SemanticStart; - return; - } - storage_class |= sc->stc & STCdeprecated; - userAttribDecl = sc->userAttribDecl; - } - else if (sem == SemanticIn) - { - error("circular definition"); - basetype = Type::terror; - errors = true; - } -} - -void TypedefDeclaration::semantic2(Scope *sc) -{ - //printf("TypedefDeclaration::semantic2(%s) sem = %d\n", toChars(), sem); - if (sem == SemanticDone) - { - sem = Semantic2Done; - basetype->alignment(); // used to detect circular typedef declarations - if (init) - { - Initializer *savedinit = init; - int errors = global.errors; - init = init->semantic(sc, basetype, INITinterpret); - if (errors != global.errors || init->isErrorInitializer()) - { - init = savedinit; - return; - } - - ExpInitializer *ie = init->isExpInitializer(); - if (ie) - { - if (ie->exp->type == basetype) - ie->exp->type = type; - } - } - } -} - -const char *TypedefDeclaration::kind() -{ - return "typedef"; -} - -Type *TypedefDeclaration::getType() -{ - return type; -} - -void TypedefDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("typedef "); - basetype->toCBuffer(buf, ident, hgs); - if (init) - { - buf->writestring(" = "); - init->toCBuffer(buf, hgs); - } - buf->writeByte(';'); - buf->writenl(); -} - /********************************* AliasDeclaration ****************************/ AliasDeclaration::AliasDeclaration(Loc loc, Identifier *id, Type *type) @@ -422,7 +291,7 @@ AliasDeclaration::AliasDeclaration(Loc loc, Identifier *id, Type *type) this->htype = NULL; this->haliassym = NULL; this->overnext = NULL; - this->inSemantic = false; + this->inSemantic = 0; assert(type); } @@ -438,7 +307,7 @@ AliasDeclaration::AliasDeclaration(Loc loc, Identifier *id, Dsymbol *s) this->htype = NULL; this->haliassym = NULL; this->overnext = NULL; - this->inSemantic = false; + this->inSemantic = 0; assert(s); } @@ -483,7 +352,7 @@ void AliasDeclaration::semantic(Scope *sc) aliassym->semantic(sc); return; } - this->inSemantic = true; + this->inSemantic = 1; storage_class |= sc->stc & STCdeprecated; protection = sc->protection; @@ -498,13 +367,22 @@ void AliasDeclaration::semantic(Scope *sc) // type. If it is a symbol, then aliassym is set and type is NULL - // toAlias() will return aliasssym. - int errors = global.errors; + unsigned int errors = global.errors; Type *savedtype = type; Dsymbol *s; Type *t; Expression *e; + // Ungag errors when not instantiated DeclDefs scope alias + Ungag ungag(global.gag); + //printf("%s parent = %s, gag = %d, instantiated = %d\n", toChars(), parent, global.gag, isInstantiated()); + if (parent && global.gag && !isInstantiated() && !toParent2()->isFuncDeclaration()) + { + //printf("%s type = %s\n", toPrettyChars(), type->toChars()); + global.gag = 0; + } + /* This section is needed because resolve() will: * const x = 3; * alias x y; @@ -521,11 +399,12 @@ void AliasDeclaration::semantic(Scope *sc) goto L2; // it's a symbolic alias type = type->addStorageClass(storage_class); - if (storage_class & (STCref | STCnothrow | STCpure | STCdisable)) - { // For 'ref' to be attached to function types, and picked + if (storage_class & (STCref | STCnothrow | STCnogc | STCpure | STCdisable)) + { + // For 'ref' to be attached to function types, and picked // up by Type::resolve(), it has to go into sc. sc = sc->push(); - sc->stc |= storage_class & (STCref | STCnothrow | STCpure | STCshared | STCdisable); + sc->stc |= storage_class & (STCref | STCnothrow | STCnogc | STCpure | STCshared | STCdisable); type->resolve(loc, sc, &e, &t, &s); sc = sc->pop(); } @@ -553,7 +432,7 @@ void AliasDeclaration::semantic(Scope *sc) } if (overnext) ScopeDsymbol::multiplyDefined(Loc(), overnext, this); - this->inSemantic = false; + this->inSemantic = 0; if (global.gag && errors != global.errors) type = savedtype; @@ -571,27 +450,50 @@ void AliasDeclaration::semantic(Scope *sc) else { Dsymbol *savedovernext = overnext; - FuncDeclaration *f = s->toAlias()->isFuncDeclaration(); - if (f) + Dsymbol *sa = s->toAlias(); + if (FuncDeclaration *fd = sa->isFuncDeclaration()) { if (overnext) { - FuncAliasDeclaration *fa = new FuncAliasDeclaration(f); + FuncAliasDeclaration *fa = new FuncAliasDeclaration(fd); if (!fa->overloadInsert(overnext)) - ScopeDsymbol::multiplyDefined(Loc(), overnext, f); + ScopeDsymbol::multiplyDefined(Loc(), overnext, fd); overnext = NULL; s = fa; s->parent = sc->parent; } } - OverloadSet *o = s->toAlias()->isOverloadSet(); - if (o) + else if (TemplateDeclaration *td = sa->isTemplateDeclaration()) + { + if (overnext) + { + OverDeclaration *od = new OverDeclaration(td); + if (!od->overloadInsert(overnext)) + ScopeDsymbol::multiplyDefined(Loc(), overnext, td); + overnext = NULL; + s = od; + s->parent = sc->parent; + } + } + else if (OverDeclaration *od = sa->isOverDeclaration()) { if (overnext) { - o->push(overnext); + OverDeclaration *od2 = new OverDeclaration(od); + if (!od2->overloadInsert(overnext)) + ScopeDsymbol::multiplyDefined(Loc(), overnext, od); overnext = NULL; - s = o; + s = od2; + s->parent = sc->parent; + } + } + else if (OverloadSet *os = sa->isOverloadSet()) + { + if (overnext) + { + os->push(overnext); + overnext = NULL; + s = os; s->parent = sc->parent; } } @@ -606,14 +508,12 @@ void AliasDeclaration::semantic(Scope *sc) { type = savedtype; overnext = savedovernext; - aliassym = NULL; - inSemantic = false; - return; + s = NULL; } } //printf("setting aliassym %s to %s %s\n", toChars(), s->kind(), s->toChars()); aliassym = s; - this->inSemantic = false; + this->inSemantic = 0; } bool AliasDeclaration::overloadInsert(Dsymbol *s) @@ -625,14 +525,19 @@ bool AliasDeclaration::overloadInsert(Dsymbol *s) //printf("AliasDeclaration::overloadInsert('%s')\n", s->toChars()); if (aliassym) // see test/test56.d { - Dsymbol *a = aliassym->toAlias(); - FuncDeclaration *f = a->isFuncDeclaration(); - if (f) // BUG: what if it's a template? + Dsymbol *sa = aliassym->toAlias(); + if (FuncDeclaration *fd = sa->isFuncDeclaration()) { - FuncAliasDeclaration *fa = new FuncAliasDeclaration(f); + FuncAliasDeclaration *fa = new FuncAliasDeclaration(fd); aliassym = fa; return fa->overloadInsert(s); } + if (TemplateDeclaration *td = sa->isTemplateDeclaration()) + { + OverDeclaration *od = new OverDeclaration(td); + aliassym = od; + return od->overloadInsert(s); + } } if (overnext == NULL) @@ -667,18 +572,36 @@ Dsymbol *AliasDeclaration::toAlias() //printf("AliasDeclaration::toAlias('%s', this = %p, aliassym = %p, kind = '%s')\n", toChars(), this, aliassym, aliassym ? aliassym->kind() : ""); assert(this != aliassym); //static int count; if (++count == 10) *(char*)0=0; + if (inSemantic == 1 && type && scope) + { + inSemantic = 2; + unsigned olderrors = global.errors; + Dsymbol *s = type->toDsymbol(scope); + //printf("[%s] %s, s = %p, this = %p\n", loc.toChars(), type->toChars(), s, this); + if (!s || global.errors != olderrors) + goto Lerr; + s = s->toAlias(); + if (global.errors != olderrors) + goto Lerr; + + aliassym = s; + inSemantic = 0; + } if (inSemantic) { error("recursive alias declaration"); + Lerr: // Avoid breaking "recursive alias" state during errors gagged - if (global.isSpeculativeGagging()) + if (global.gag) return this; aliassym = new AliasDeclaration(loc, ident, Type::terror); type = Type::terror; + return aliassym; } - else if (aliassym || type->deco) + + if (aliassym || type->deco) ; // semantic is already done. else if (import && import->scope) { @@ -689,7 +612,9 @@ Dsymbol *AliasDeclaration::toAlias() } else if (scope) semantic(scope); + inSemantic = 1; Dsymbol *s = aliassym ? aliassym->toAlias() : this; + inSemantic = 0; return s; } @@ -708,6 +633,122 @@ void AliasDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writenl(); } +/****************************** OverDeclaration **************************/ + +OverDeclaration::OverDeclaration(Dsymbol *s, bool hasOverloads) + : Declaration(s->ident) +{ + this->aliassym = s; + + this->hasOverloads = hasOverloads; + if (hasOverloads) + { + if (OverDeclaration *od = aliassym->isOverDeclaration()) + this->hasOverloads = od->hasOverloads; + } + else + { + // for internal use + assert(!aliassym->isOverDeclaration()); + } +} + +const char *OverDeclaration::kind() +{ + return "overload alias"; // todo +} + +void OverDeclaration::semantic(Scope *sc) +{ +} + +bool OverDeclaration::equals(RootObject *o) +{ + if (this == o) + return true; + + Dsymbol *s = isDsymbol(o); + if (!s) + return false; + + OverDeclaration *od1 = this; + if (OverDeclaration *od2 = s->isOverDeclaration()) + { + return od1->aliassym->equals(od2->aliassym) && + od1->hasOverloads == od2->hasOverloads; + } + if (aliassym == s) + { + if (hasOverloads) + return true; + if (FuncDeclaration *fd = s->isFuncDeclaration()) + { + return fd->isUnique() != NULL; + } + if (TemplateDeclaration *td = s->isTemplateDeclaration()) + { + return td->overnext == NULL; + } + } + return false; +} + +bool OverDeclaration::overloadInsert(Dsymbol *s) +{ + //printf("OverDeclaration::overloadInsert('%s') aliassym = %p, overnext = %p\n", s->toChars(), aliassym, overnext); + if (overnext == NULL) + { + if (s == this) + { + return true; + } + overnext = s; + return true; + } + else + { + return overnext->overloadInsert(s); + } +} + +Dsymbol *OverDeclaration::toAlias() +{ + return this; +} + +Dsymbol *OverDeclaration::isUnique() +{ + if (!hasOverloads) + { + if (aliassym->isFuncDeclaration() || + aliassym->isTemplateDeclaration()) + { + return aliassym; + } + } + + struct ParamUniqueSym + { + static int fp(void *param, Dsymbol *s) + { + Dsymbol **ps = (Dsymbol **)param; + if (*ps) + { + *ps = NULL; + return 1; // ambiguous, done + } + else + { + *ps = s; + return 0; + } + } + }; + Dsymbol *result = NULL; + overloadApply(aliassym, &result, &ParamUniqueSym::fp); + return result; +} + /********************************* VarDeclaration ****************************/ VarDeclaration::VarDeclaration(Loc loc, Type *type, Identifier *id, Initializer *init) @@ -740,6 +781,7 @@ VarDeclaration::VarDeclaration(Loc loc, Type *type, Identifier *id, Initializer ctfeAdrOnStack = -1; rundtor = NULL; edtor = NULL; + range = NULL; } Dsymbol *VarDeclaration::syntaxCopy(Dsymbol *s) @@ -756,7 +798,6 @@ Dsymbol *VarDeclaration::syntaxCopy(Dsymbol *s) if (this->init) { init = this->init->syntaxCopy(); //init->isExpInitializer()->exp->print(); - //init->isExpInitializer()->exp->dump(0); } sv = new VarDeclaration(loc, type ? type->syntaxCopy() : NULL, ident, init); @@ -835,41 +876,14 @@ void VarDeclaration::semantic(Scope *sc) if (needctfe) sc = sc->startCTFE(); //printf("inferring type for %s with init %s\n", toChars(), init->toChars()); - ArrayInitializer *ai = init->isArrayInitializer(); - if (ai) - { - Expression *e; - if (ai->isAssociativeArray()) - e = ai->toAssocArrayLiteral(); - else - e = init->toExpression(); - if (!e) - { - error("cannot infer type from initializer"); - e = new ErrorExp(); - } - init = new ExpInitializer(e->loc, e); - type = init->inferType(sc); - if (type->ty == Tsarray) - type = type->nextOf()->arrayOf(); - } - else - { - type = init->inferType(sc); - } + init = init->inferType(sc); + type = init->toExpression()->type; if (needctfe) sc = sc->endCTFE(); -// type = type->semantic(loc, sc); inuse--; inferred = 1; - if (init->isArrayInitializer() && type->toBasetype()->ty == Tsarray) - { - // Prefer array literals to give a T[] type rather than a T[dim] - type = type->toBasetype()->nextOf()->arrayOf(); - } - /* This is a kludge to support the existing syntax for RAII * declarations. */ @@ -903,7 +917,7 @@ void VarDeclaration::semantic(Scope *sc) //printf("storage_class = x%x\n", storage_class); // Calculate type size + safety checks - if (sc->func && !sc->intypeof) + if (sc->func && !sc->intypeof && !isMember()) { if (storage_class & STCgshared) { @@ -1071,8 +1085,7 @@ void VarDeclaration::semantic(Scope *sc) OutBuffer buf; buf.printf("_%s_field_%llu", ident->toChars(), (ulonglong)i); - buf.writeByte(0); - const char *name = (const char *)buf.extractData(); + const char *name = buf.extractString(); Identifier *id = Lexer::idPool(name); Initializer *ti; @@ -1112,6 +1125,7 @@ void VarDeclaration::semantic(Scope *sc) v2->parent = this->parent; v2->isexp = true; aliassym = v2; + sem = SemanticDone; return; } @@ -1172,8 +1186,7 @@ void VarDeclaration::semantic(Scope *sc) else { storage_class |= STCfield; - if ((tbn->ty == Tstruct && ((TypeStruct *)tbn)->sym->noDefaultCtor) || - (tbn->ty == Tclass && ((TypeClass *)tbn)->sym->noDefaultCtor)) + if ((tbn->ty == Tstruct && ((TypeStruct *)tbn)->sym->noDefaultCtor)) { if (!isThisDeclaration() && !init) aad->noDefaultCtor = true; @@ -1216,8 +1229,7 @@ void VarDeclaration::semantic(Scope *sc) error("only parameters or foreach declarations can be ref"); } - if (type->hasWild() && - !(type->ty == Tpointer && type->nextOf()->ty == Tfunction || type->ty == Tdelegate)) + if (type->hasWild()) { if (storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCmanifest | STCfield) || isDataseg() @@ -1252,9 +1264,11 @@ void VarDeclaration::semantic(Scope *sc) if (!init) { if (isField()) + { /* For fields, we'll check the constructor later to make sure it is initialized */ storage_class |= STCnodefaultctor; + } else if (storage_class & STCparameter) ; else @@ -1287,7 +1301,7 @@ void VarDeclaration::semantic(Scope *sc) else if (storage_class & STCmanifest) error("manifest constants must have initializers"); - TOK op = TOKconstruct; + bool isBlit = false; if (!init && !sc->inunion && !(storage_class & (STCstatic | STCgshared | STCextern)) && fd && (!(storage_class & (STCfield | STCin | STCforeach | STCparameter | STCresult)) || (storage_class & STCout)) && @@ -1310,7 +1324,7 @@ void VarDeclaration::semantic(Scope *sc) Expression *e = tv->defaultInitLiteral(loc); Expression *e1 = new VarExp(loc, this); - e = new ConstructExp(loc, e1, e); + e = new BlitExp(loc, e1, e); e = e->semantic(sc); init = new ExpInitializer(loc, e); goto Ldtor; @@ -1327,60 +1341,31 @@ void VarDeclaration::semantic(Scope *sc) Expression *e = new IntegerExp(loc, 0, Type::tint32); Expression *e1; e1 = new VarExp(loc, this); - e = new ConstructExp(loc, e1, e); + e = new BlitExp(loc, e1, e); e->type = e1->type; // don't type check this, it would fail init = new ExpInitializer(loc, e); goto Ldtor; } - else if (type->ty == Ttypedef) + else if (type->baseElemOf()->ty == Tvoid) { - TypeTypedef *td = (TypeTypedef *)type; - if (td->sym->init) - { - init = td->sym->init; - ExpInitializer *ie = init->isExpInitializer(); - if (ie) - // Make copy so we can modify it - init = new ExpInitializer(ie->loc, ie->exp); - } - else - init = getExpInitializer(); + error("%s does not have a default initializer", type->toChars()); } else { init = getExpInitializer(); } // Default initializer is always a blit - op = TOKblit; + isBlit = true; } if (init) { sc = sc->push(); - sc->stc &= ~(STC_TYPECTOR | STCpure | STCnothrow | STCref | STCdisable); + sc->stc &= ~(STC_TYPECTOR | STCpure | STCnothrow | STCnogc | STCref | STCdisable); ExpInitializer *ei = init->isExpInitializer(); - if (ei && isScope()) - { - // See if initializer is a NewExp that can be allocated on the stack - if (ei->exp->op == TOKnew) - { - NewExp *ne = (NewExp *)ei->exp; - if (!(ne->newargs && ne->newargs->dim)) - { - ne->onstack = 1; - onstack = 1; - if (type->isBaseOf(ne->newtype->semantic(loc, sc), NULL)) - onstack = 2; - } - } - // or a delegate that doesn't escape a reference to the function - else if (ei->exp->op == TOKfunction) - { - FuncDeclaration *f = ((FuncExp *)ei->exp)->fd; - f->tookAddressOf--; - } - } + if (ei) // Bugzilla 13424: Preset the required type to fail in FuncLiteralDeclaration::semantic3 + ei->exp = inferType(ei->exp, type); // If inside function, there is no semantic3() call if (sc->func) @@ -1416,12 +1401,41 @@ void VarDeclaration::semantic(Scope *sc) } Expression *e1 = new VarExp(loc, this); - ei->exp = new AssignExp(loc, e1, ei->exp); - ei->exp->op = op; + if (isBlit) + ei->exp = new BlitExp(loc, e1, ei->exp); + else + ei->exp = new ConstructExp(loc, e1, ei->exp); canassign++; ei->exp = ei->exp->semantic(sc); canassign--; ei->exp->optimize(WANTvalue); + + if (isScope()) + { + Expression *ex = ei->exp; + while (ex->op == TOKcomma) + ex = ((CommaExp *)ex)->e2; + if (ex->op == TOKblit || ex->op == TOKconstruct) + ex = ((AssignExp *)ex)->e2; + if (ex->op == TOKnew) + { + // See if initializer is a NewExp that can be allocated on the stack + NewExp *ne = (NewExp *)ex; + if (!(ne->newargs && ne->newargs->dim) && type->toBasetype()->ty == Tclass) + { + ne->onstack = 1; + onstack = 1; + if (type->isBaseOf(ne->newtype->semantic(loc, sc), NULL)) + onstack = 2; + } + } + else if (ex->op == TOKfunction) + { + // or a delegate that doesn't escape a reference to the function + FuncDeclaration *f = ((FuncExp *)ex)->fd; + f->tookAddressOf--; + } + } } else { @@ -1430,7 +1444,7 @@ void VarDeclaration::semantic(Scope *sc) } else if (parent->isAggregateDeclaration()) { - scope = scx ? scx : new Scope(*sc); + scope = scx ? scx : sc->copy(); scope->setNoFree(); } else if (storage_class & (STCconst | STCimmutable | STCmanifest) || @@ -1476,8 +1490,10 @@ void VarDeclaration::semantic(Scope *sc) /* Look to see if initializer involves a copy constructor * (which implies a postblit) */ - if (sd->cpctor && // there is a copy constructor - tb2->toDsymbol(NULL) == sd) // exp is the same struct + // there is a copy constructor + // and exp is the same struct + if (sd->cpctor && + tb2->toDsymbol(NULL) == sd) { // The only allowable initializer is a (non-copy) constructor if (exp->isLvalue()) @@ -1496,7 +1512,7 @@ void VarDeclaration::semantic(Scope *sc) } else { - scope = scx ? scx : new Scope(*sc); + scope = scx ? scx : sc->copy(); scope->setNoFree(); } } @@ -1561,7 +1577,7 @@ void VarDeclaration::semantic2(Scope *sc) ExpInitializer *ei = init->isExpInitializer(); if (ei) { - ei->exp->dump(0); + ei->exp->print(); printf("type = %p\n", ei->exp->type); } #endif @@ -1602,10 +1618,10 @@ void VarDeclaration::semantic2(Scope *sc) } else if (init && isThreadlocal()) { - if ((type->ty == Tclass)&&type->isMutable()&&!type->isShared()) + if ((type->ty == Tclass) && type->isMutable() && !type->isShared()) { ExpInitializer *ei = init->isExpInitializer(); - if (ei->exp->op == TOKclassreference) + if (ei && ei->exp->op == TOKclassreference) error("is mutable. Only const or immutable class thread local variable are allowed, not %s", type->toChars()); } else if (type->ty == Tpointer && type->nextOf()->ty == Tstruct && type->nextOf()->isMutable() &&!type->nextOf()->isShared()) @@ -1685,6 +1701,8 @@ void VarDeclaration::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, ad->sizeok = SIZEOKfwd; // cannot finish; flag as forward referenced return; } + if (t->ty == Terror) + return; unsigned memsize = (unsigned)t->size(loc); // size of member @@ -1835,9 +1853,12 @@ void VarDeclaration::checkNestedReference(Scope *sc, Loc loc) /* This is necessary to avoid breaking tests for 8751 & 8793. * See: compilable/testInference.d */ - if (type->isMutable() || // mutable variable - !type->implicitConvTo(type->immutableOf()) || // has any mutable indirections - !fdv->isPureBypassingInference()) // does not belong to pure function + // if is a mutable variable or + // has any mutable indirections or + // does not belong to pure function + if (type->isMutable() || + !type->implicitConvTo(type->immutableOf()) || + !fdv->isPureBypassingInference()) { fld->setImpure(); // Bugzilla 9415 } @@ -1899,7 +1920,7 @@ Expression *VarDeclaration::getConstInitializer(bool needFullType) // Ungag errors when not speculative unsigned oldgag = global.gag; - if (global.isSpeculativeGagging()) + if (global.gag) { Dsymbol *sym = toParent()->isAggregateDeclaration(); if (sym && !sym->isSpeculative()) @@ -1925,25 +1946,7 @@ Expression *VarDeclaration::getConstInitializer(bool needFullType) bool VarDeclaration::canTakeAddressOf() { -#if 0 - /* Global variables and struct/class fields of the form: - * const int x = 3; - * are not stored and hence cannot have their address taken. - */ - if ((isConst() || isImmutable()) && - storage_class & STCinit && - (!(storage_class & (STCstatic | STCextern)) || isField()) && - (!parent || toParent()->isModule() || toParent()->isTemplateInstance()) && - type->toBasetype()->isTypeBasic() - ) - { - return false; - } -#else - if (storage_class & STCmanifest) - return false; -#endif - return true; + return !(storage_class & STCmanifest); } @@ -1959,18 +1962,18 @@ bool VarDeclaration::isDataseg() printf("%llx, isModule: %p, isTemplateInstance: %p\n", storage_class & (STCstatic | STCconst), parent->isModule(), parent->isTemplateInstance()); printf("parent = '%s'\n", parent->toChars()); #endif - if (storage_class & STCmanifest) + if (!canTakeAddressOf()) return false; - Dsymbol *parent = this->toParent(); + Dsymbol *parent = toParent(); if (!parent && !(storage_class & STCstatic)) - { error("forward referenced"); + { + error("forward referenced"); type = Type::terror; return false; } - return canTakeAddressOf() && - (storage_class & (STCstatic | STCextern | STCtls | STCgshared) || - toParent()->isModule() || - toParent()->isTemplateInstance()); + return (storage_class & (STCstatic | STCextern | STCtls | STCgshared) || + parent->isModule() || + parent->isTemplateInstance()); } /************************************ @@ -1980,13 +1983,6 @@ bool VarDeclaration::isDataseg() bool VarDeclaration::isThreadlocal() { //printf("VarDeclaration::isThreadlocal(%p, '%s')\n", this, toChars()); -#if 0 //|| TARGET_OSX - /* To be thread-local, must use the __thread storage class. - * BUG: OSX doesn't support thread local yet. - */ - return isDataseg() && - (storage_class & (STCtls | STCconst | STCimmutable | STCshared | STCgshared)) == STCtls; -#else /* Data defaults to being thread-local. It is not thread-local * if it is immutable, const or shared. */ @@ -1994,7 +1990,6 @@ bool VarDeclaration::isThreadlocal() !(storage_class & (STCimmutable | STCconst | STCshared | STCgshared)); //printf("\treturn %d\n", i); return i; -#endif } /******************************************** @@ -2092,6 +2087,12 @@ Expression *VarDeclaration::callScopeDtor(Scope *sc) */ //if (cd->isInterfaceDeclaration()) //error("interface %s cannot be scope", cd->toChars()); + + if (cd->cpp) + { + // Destructors are not supported on extern(C++) classes + break; + } if (1 || onstack || cd->dtors.dim) // if any destructors { // delete this; @@ -2179,8 +2180,7 @@ char *TypeInfoDeclaration::toChars() buf.writestring("typeid("); buf.writestring(tinfo->toChars()); buf.writeByte(')'); - buf.writeByte(0); - return buf.extractData(); + return buf.extractString(); } /***************************** TypeInfoConstDeclaration **********************/ @@ -2302,23 +2302,6 @@ TypeInfoInterfaceDeclaration *TypeInfoInterfaceDeclaration::create(Type *tinfo) return new TypeInfoInterfaceDeclaration(tinfo); } -/***************************** TypeInfoTypedefDeclaration *********************/ - -TypeInfoTypedefDeclaration::TypeInfoTypedefDeclaration(Type *tinfo) - : TypeInfoDeclaration(tinfo, 0) -{ - if (!Type::typeinfotypedef) - { - ObjectNotFound(Id::TypeInfo_Typedef); - } - type = Type::typeinfotypedef->type; -} - -TypeInfoTypedefDeclaration *TypeInfoTypedefDeclaration::create(Type *tinfo) -{ - return new TypeInfoTypedefDeclaration(tinfo); -} - /***************************** TypeInfoPointerDeclaration *********************/ TypeInfoPointerDeclaration::TypeInfoPointerDeclaration(Type *tinfo) diff --git a/gcc/d/dfrontend/declaration.h b/gcc/d/dfrontend/declaration.h index 3292b3d59..0ae66c57b 100644 --- a/gcc/d/dfrontend/declaration.h +++ b/gcc/d/dfrontend/declaration.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2011 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/declaration.h + */ #ifndef DMD_DECLARATION_H #define DMD_DECLARATION_H @@ -24,7 +25,6 @@ class Statement; class LabelDsymbol; class Initializer; class Module; -struct InlineScanState; class ForeachStatement; class FuncDeclaration; class ExpInitializer; @@ -74,7 +74,7 @@ enum PURE; // but not typed as "shared" #define STCwild 0x80000000LL // for "wild" type constructor #define STC_TYPECTOR (STCconst | STCimmutable | STCshared | STCwild) -#define STC_FUNCATTR (STCref | STCnothrow | STCpure | STCproperty | STCsafe | STCtrusted | STCsystem) +#define STC_FUNCATTR (STCref | STCnothrow | STCnogc | STCpure | STCproperty | STCsafe | STCtrusted | STCsystem) #define STCproperty 0x100000000LL #define STCsafe 0x200000000LL @@ -86,11 +86,12 @@ enum PURE; #define STCnodefaultctor 0x8000000000LL // must be set inside constructor #define STCtemp 0x10000000000LL // temporary variable #define STCrvalue 0x20000000000LL // force rvalue for variables +#define STCnogc 0x40000000000LL // @nogc const StorageClass STCStorageClass = (STCauto | STCscope | STCstatic | STCextern | STCconst | STCfinal | STCabstract | STCsynchronized | STCdeprecated | STCoverride | STClazy | STCalias | STCout | STCin | - STCmanifest | STCimmutable | STCshared | STCwild | STCnothrow | STCpure | STCref | STCtls | + STCmanifest | STCimmutable | STCshared | STCwild | STCnothrow | STCnogc | STCpure | STCref | STCtls | STCgshared | STCproperty | STCsafe | STCtrusted | STCsystem | STCdisable); struct Match @@ -137,10 +138,6 @@ class Declaration : public Dsymbol Dsymbol *search(Loc loc, Identifier *ident, int flags = IgnoreNone); - void emitComment(Scope *sc); - void toDocBuffer(OutBuffer *buf, Scope *sc); - - const char *mangle(bool isv = false); bool isStatic() { return (storage_class & STCstatic) != 0; } virtual bool isDelete(); virtual bool isDataseg(); @@ -193,61 +190,49 @@ class TupleDeclaration : public Declaration /**************************************************************/ -class TypedefDeclaration : public Declaration +class AliasDeclaration : public Declaration { public: - Type *basetype; - Initializer *init; + Dsymbol *aliassym; + Dsymbol *overnext; // next in overload list + Dsymbol *import; // !=NULL if unresolved internal alias for selective import + int inSemantic; - TypedefDeclaration(Loc loc, Identifier *ident, Type *basetype, Initializer *init); + AliasDeclaration(Loc loc, Identifier *ident, Type *type); + AliasDeclaration(Loc loc, Identifier *ident, Dsymbol *s); Dsymbol *syntaxCopy(Dsymbol *); void semantic(Scope *sc); - void semantic2(Scope *sc); - const char *mangle(bool isv = false); + bool overloadInsert(Dsymbol *s); const char *kind(); Type *getType(); + Dsymbol *toAlias(); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); Type *htype; - Type *hbasetype; - - void toDocBuffer(OutBuffer *buf, Scope *sc); - - void toObjFile(int multiobj); // compile to .obj file - void toDebug(); - int cvMember(unsigned char *p); - - TypedefDeclaration *isTypedefDeclaration() { return this; } + Dsymbol *haliassym; - Symbol *sinit; - Symbol *toInitializer(); + AliasDeclaration *isAliasDeclaration() { return this; } void accept(Visitor *v) { v->visit(this); } }; /**************************************************************/ -class AliasDeclaration : public Declaration +class OverDeclaration : public Declaration { public: - Dsymbol *aliassym; Dsymbol *overnext; // next in overload list - Dsymbol *import; // !=NULL if unresolved internal alias for selective import - bool inSemantic; + Dsymbol *aliassym; + bool hasOverloads; - AliasDeclaration(Loc loc, Identifier *ident, Type *type); - AliasDeclaration(Loc loc, Identifier *ident, Dsymbol *s); - Dsymbol *syntaxCopy(Dsymbol *); + OverDeclaration(Dsymbol *s, bool hasOverloads = true); + const char *kind(); void semantic(Scope *sc); + bool equals(RootObject *o); bool overloadInsert(Dsymbol *s); - const char *kind(); - Type *getType(); - Dsymbol *toAlias(); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - Type *htype; - Dsymbol *haliassym; - void toDocBuffer(OutBuffer *buf, Scope *sc); + Dsymbol *toAlias(); + Dsymbol *isUnique(); - AliasDeclaration *isAliasDeclaration() { return this; } + OverDeclaration *isOverDeclaration() { return this; } void accept(Visitor *v) { v->visit(this); } }; @@ -273,17 +258,12 @@ class VarDeclaration : public Declaration // When interpreting, these point to the value (NULL if value not determinable) // The index of this variable on the CTFE stack, -1 if not allocated int ctfeAdrOnStack; - // The various functions are used only to detect compiler CTFE bugs - Expression *getValue(); - bool hasValue(); - void setValueNull(); - void setValueWithoutChecking(Expression *newval); - void setValue(Expression *newval); VarDeclaration *rundtor; // if !NULL, rundtor is tested at runtime to see // if the destructor should be run. Used to prevent // dtor calls on postblitted vars Expression *edtor; // if !=NULL, does the destruction of the variable + IntRange *range; // if !NULL, the variable is known to be within the range VarDeclaration(Loc loc, Type *t, Identifier *id, Initializer *init); Dsymbol *syntaxCopy(Dsymbol *); @@ -311,9 +291,7 @@ class VarDeclaration : public Declaration void checkNestedReference(Scope *sc, Loc loc); Dsymbol *toAlias(); Symbol *toSymbol(); - void toObjFile(int multiobj); // compile to .obj file - int cvMember(unsigned char *p); - const char *mangle(bool isv = false); + void toObjFile(bool multiobj); // compile to .obj file // Eliminate need for dynamic_cast VarDeclaration *isVarDeclaration() { return (VarDeclaration *)this; } void accept(Visitor *v) { v->visit(this); } @@ -346,8 +324,6 @@ class ClassInfoDeclaration : public VarDeclaration Dsymbol *syntaxCopy(Dsymbol *); void semantic(Scope *sc); - void emitComment(Scope *sc); - Symbol *toSymbol(); void accept(Visitor *v) { v->visit(this); } }; @@ -363,10 +339,8 @@ class TypeInfoDeclaration : public VarDeclaration void semantic(Scope *sc); char *toChars(); - void emitComment(Scope *sc); - Symbol *toSymbol(); - void toObjFile(int multiobj); // compile to .obj file + void toObjFile(bool multiobj); // compile to .obj file virtual void toDt(dt_t **pdt); TypeInfoDeclaration *isTypeInfoDeclaration() { return this; } @@ -404,16 +378,6 @@ class TypeInfoInterfaceDeclaration : public TypeInfoDeclaration void accept(Visitor *v) { v->visit(this); } }; -class TypeInfoTypedefDeclaration : public TypeInfoDeclaration -{ -public: - TypeInfoTypedefDeclaration(Type *tinfo); - static TypeInfoTypedefDeclaration *create(Type *tinfo); - - void toDt(dt_t **pdt); - void accept(Visitor *v) { v->visit(this); } -}; - class TypeInfoPointerDeclaration : public TypeInfoDeclaration { public: @@ -572,10 +536,17 @@ enum BUILTIN }; Expression *eval_builtin(Loc loc, FuncDeclaration *fd, Expressions *arguments); +BUILTIN isBuiltin(FuncDeclaration *fd); typedef Expression *(*builtin_fp)(Loc loc, FuncDeclaration *fd, Expressions *arguments); void add_builtin(const char *mangle, builtin_fp fp); void builtin_init(); +void buildClosure(FuncDeclaration *fd, IRState *irs); + +#define FUNCFLAGpurityInprocess 1 // working on determining purity +#define FUNCFLAGsafetyInprocess 2 // working on determining safety +#define FUNCFLAGnothrowInprocess 4 // working on determining nothrow +#define FUNCFLAGnogcInprocess 8 // working on determining @nogc class FuncDeclaration : public Declaration { @@ -616,7 +587,6 @@ class FuncDeclaration : public Declaration CompiledCtfeFunction *ctfeCode; // Compiled code for interpreter int inlineNest; // !=0 if nested inline bool isArrayOp; // true if array operation - FuncDeclaration *dArrayOp; // D version of array op for ctfe bool semantic3Errors; // true if errors in semantic3 // this function's frame ptr ForeachStatement *fes; // if foreach body, this is the foreach @@ -638,7 +608,6 @@ class FuncDeclaration : public Declaration VarDeclaration *nrvo_var; // variable to replace with shidden Symbol *shidden; // hidden pointer passed to function - ReturnStatements *returns; GotoStatements *gotos; // Gotos with forward references BUILTIN builtin; // set if this is a known, builtin @@ -653,12 +622,8 @@ class FuncDeclaration : public Declaration // functions FuncDeclarations siblingCallers; // Sibling nested functions which // called this one - FuncDeclarations deferred; // toObjFile() these functions after this one unsigned flags; - #define FUNCFLAGpurityInprocess 1 // working on determining purity - #define FUNCFLAGsafetyInprocess 2 // working on determining safety - #define FUNCFLAGnothrowInprocess 4 // working on determining nothrow FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageClass storage_class, Type *type); Dsymbol *syntaxCopy(Dsymbol *); @@ -678,6 +643,7 @@ class FuncDeclaration : public Declaration bool overloadInsert(Dsymbol *s); FuncDeclaration *overloadExactMatch(Type *t); TemplateDeclaration *findTemplateDeclRoot(); + bool inUnittest(); MATCH leastAsSpecialized(FuncDeclaration *g); LabelDsymbol *searchLabel(Identifier *ident); AggregateDeclaration *isThis(); @@ -685,14 +651,11 @@ class FuncDeclaration : public Declaration int getLevel(Loc loc, Scope *sc, FuncDeclaration *fd); // lexical nesting level difference void appendExp(Expression *e); void appendState(Statement *s); - const char *mangle(bool isv = false); - const char *mangleExact(bool isv = false); - const char *toPrettyChars(); + const char *toPrettyChars(bool QualifyTypes = false); const char *toFullSignature(); // for diagnostics, e.g. 'int foo(int x, int y) pure' bool isMain(); bool isWinMain(); bool isDllMain(); - BUILTIN isBuiltin(); bool isExport(); bool isImportedSymbol(); bool isCodeseg(); @@ -700,11 +663,18 @@ class FuncDeclaration : public Declaration bool hasOverloads(); PURE isPure(); PURE isPureBypassingInference(); + bool isPureBypassingInferenceX(); bool setImpure(); bool isSafe(); bool isSafeBypassingInference(); bool isTrusted(); bool setUnsafe(); + + bool isNogc(); + bool isNogcBypassingInference(); + bool setGC(); + + void printGCUsage(Loc loc, const char *warn); bool isolateReturn(); bool parametersIntersect(Type *t); virtual bool isNested(); @@ -714,13 +684,7 @@ class FuncDeclaration : public Declaration virtual bool isFinalFunc(); virtual bool addPreInvariant(); virtual bool addPostInvariant(); - Expression *interpret(InterState *istate, Expressions *arguments, Expression *thisexp = NULL); - void ctfeCompile(); - void inlineScan(); - int canInline(int hasthis, int hdrscan, int statementsToo); - Expression *expandInline(InlineScanState *iss, Expression *eret, Expression *ethis, Expressions *arguments, Statement **ps); const char *kind(); - void toDocBuffer(OutBuffer *buf, Scope *sc); FuncDeclaration *isUnique(); void checkNestedReference(Scope *sc, Loc loc); bool needsClosure(); @@ -735,10 +699,7 @@ class FuncDeclaration : public Declaration Symbol *toSymbol(); Symbol *toThunkSymbol(int offset); // thunk version - void toObjFile(int multiobj); // compile to .obj file - int cvMember(unsigned char *p); - void buildClosure(IRState *irs); - bool needsCodegen(); + void toObjFile(bool multiobj); // compile to .obj file FuncDeclaration *isFuncDeclaration() { return this; } @@ -763,7 +724,6 @@ class FuncAliasDeclaration : public FuncDeclaration FuncAliasDeclaration *isFuncAliasDeclaration() { return this; } const char *kind(); Symbol *toSymbol(); - const char *mangle(bool isv = false); FuncDeclaration *toAliasFunc(); void accept(Visitor *v) { v->visit(this); } @@ -781,10 +741,14 @@ class FuncLiteralDeclaration : public FuncDeclaration Dsymbol *syntaxCopy(Dsymbol *); bool isNested(); bool isVirtual(); + bool addPreInvariant(); + bool addPostInvariant(); + + void modifyReturns(Scope *sc, Type *tret); FuncLiteralDeclaration *isFuncLiteralDeclaration() { return this; } const char *kind(); - const char *toPrettyChars(); + const char *toPrettyChars(bool QualifyTypes = false); void accept(Visitor *v) { v->visit(this); } }; @@ -815,7 +779,6 @@ class PostBlitDeclaration : public FuncDeclaration bool addPreInvariant(); bool addPostInvariant(); bool overloadInsert(Dsymbol *s); - void emitComment(Scope *sc); PostBlitDeclaration *isPostBlitDeclaration() { return this; } void accept(Visitor *v) { v->visit(this); } @@ -835,7 +798,6 @@ class DtorDeclaration : public FuncDeclaration bool addPreInvariant(); bool addPostInvariant(); bool overloadInsert(Dsymbol *s); - void emitComment(Scope *sc); DtorDeclaration *isDtorDeclaration() { return this; } void accept(Visitor *v) { v->visit(this); } @@ -844,8 +806,8 @@ class DtorDeclaration : public FuncDeclaration class StaticCtorDeclaration : public FuncDeclaration { public: - StaticCtorDeclaration(Loc loc, Loc endloc); - StaticCtorDeclaration(Loc loc, Loc endloc, const char *name); + StaticCtorDeclaration(Loc loc, Loc endloc, StorageClass stc); + StaticCtorDeclaration(Loc loc, Loc endloc, const char *name, StorageClass stc); Dsymbol *syntaxCopy(Dsymbol *); void semantic(Scope *sc); AggregateDeclaration *isThis(); @@ -853,7 +815,6 @@ class StaticCtorDeclaration : public FuncDeclaration bool addPreInvariant(); bool addPostInvariant(); bool hasStaticCtorOrDtor(); - void emitComment(Scope *sc); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); StaticCtorDeclaration *isStaticCtorDeclaration() { return this; } @@ -863,7 +824,7 @@ class StaticCtorDeclaration : public FuncDeclaration class SharedStaticCtorDeclaration : public StaticCtorDeclaration { public: - SharedStaticCtorDeclaration(Loc loc, Loc endloc); + SharedStaticCtorDeclaration(Loc loc, Loc endloc, StorageClass stc); Dsymbol *syntaxCopy(Dsymbol *); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); @@ -885,7 +846,6 @@ class StaticDtorDeclaration : public FuncDeclaration bool hasStaticCtorOrDtor(); bool addPreInvariant(); bool addPostInvariant(); - void emitComment(Scope *sc); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); StaticDtorDeclaration *isStaticDtorDeclaration() { return this; } @@ -912,7 +872,6 @@ class InvariantDeclaration : public FuncDeclaration bool isVirtual(); bool addPreInvariant(); bool addPostInvariant(); - void emitComment(Scope *sc); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); InvariantDeclaration *isInvariantDeclaration() { return this; } @@ -923,6 +882,10 @@ class UnitTestDeclaration : public FuncDeclaration { public: char *codedoc; /** For documented unittest. */ + + // toObjFile() these nested functions after this one + FuncDeclarations deferredNested; + UnitTestDeclaration(Loc loc, Loc endloc, char *codedoc); Dsymbol *syntaxCopy(Dsymbol *); void semantic(Scope *sc); @@ -930,8 +893,6 @@ class UnitTestDeclaration : public FuncDeclaration bool isVirtual(); bool addPreInvariant(); bool addPostInvariant(); - void emitComment(Scope *sc); - void inlineScan(); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); UnitTestDeclaration *isUnitTestDeclaration() { return this; } diff --git a/gcc/d/dfrontend/delegatize.c b/gcc/d/dfrontend/delegatize.c index 4d89d4186..27652c771 100644 --- a/gcc/d/dfrontend/delegatize.c +++ b/gcc/d/dfrontend/delegatize.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2011 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/delegatize.c + */ #include #include @@ -26,35 +27,38 @@ * i.e. convert: * expr * to: - * t delegate() { return expr; } + * typeof(expr) delegate() { return expr; } */ -int lambdaSetParent(Expression *e, void *param); -int lambdaCheckForNestedRef(Expression *e, void *param); +bool walkPostorder(Expression *e, StoppableVisitor *v); +void lambdaSetParent(Expression *e, Scope *sc); +void lambdaCheckForNestedRef(Expression *e, Scope *sc); -Expression *Expression::toDelegate(Scope *sc, Type *t) +Expression *toDelegate(Expression *e, Scope *sc) { - //printf("Expression::toDelegate(t = %s) %s\n", t->toChars(), toChars()); - Type *tw = t->semantic(loc, sc); - Type *tc = t->substWildTo(MODconst)->semantic(loc, sc); - TypeFunction *tf = new TypeFunction(NULL, tc, 0, LINKd); - if (tw != tc) tf->mod = MODwild; // hack for bug7757 - (tf = (TypeFunction *)tf->semantic(loc, sc))->next = tw; // hack for bug7757 + //printf("Expression::toDelegate(t = %s) %s\n", e->type->toChars(), e->toChars()); + Loc loc = e->loc; + Type *t = e->type; + + TypeFunction *tf = new TypeFunction(NULL, t, 0, LINKd); + if (t->hasWild()) + tf->mod = MODwild; FuncLiteralDeclaration *fld = new FuncLiteralDeclaration(loc, loc, tf, TOKdelegate, NULL); - Expression *e; + sc = sc->push(); sc->parent = fld; // set current function to be the delegate - e = this; - e->apply(&lambdaSetParent, sc); - e->apply(&lambdaCheckForNestedRef, sc); + lambdaSetParent(e, sc); + lambdaCheckForNestedRef(e, sc); sc = sc->pop(); + Statement *s; if (t->ty == Tvoid) s = new ExpStatement(loc, e); else s = new ReturnStatement(loc, e); fld->fbody = s; + e = new FuncExp(loc, fld); e = e->semantic(sc); return e; @@ -63,83 +67,85 @@ Expression *Expression::toDelegate(Scope *sc, Type *t) /****************************************** * Patch the parent of declarations to be the new function literal. */ -int lambdaSetParent(Expression *e, void *param) +void lambdaSetParent(Expression *e, Scope *sc) { - Scope *sc = (Scope *)param; - /* We could use virtual functions instead of a switch, - * but it doesn't seem worth the bother. - */ - switch (e->op) + class LambdaSetParent : public StoppableVisitor { - case TOKdeclaration: - { DeclarationExp *de = (DeclarationExp *)e; - de->declaration->parent = sc->parent; - break; + Scope *sc; + public: + LambdaSetParent(Scope *sc) : sc(sc) {} + + void visit(Expression *) + { } - case TOKindex: - { IndexExp *de = (IndexExp *)e; - if (de->lengthVar) - { //printf("lengthVar\n"); - de->lengthVar->parent = sc->parent; + void visit(DeclarationExp *e) + { + e->declaration->parent = sc->parent; + } + + void visit(IndexExp *e) + { + if (e->lengthVar) + { + //printf("lengthVar\n"); + e->lengthVar->parent = sc->parent; } - break; } - case TOKslice: - { SliceExp *se = (SliceExp *)e; - if (se->lengthVar) - { //printf("lengthVar\n"); - se->lengthVar->parent = sc->parent; + void visit(SliceExp *e) + { + if (e->lengthVar) + { + //printf("lengthVar\n"); + e->lengthVar->parent = sc->parent; } - break; } + }; - default: - break; - } - return 0; + LambdaSetParent lsp(sc); + walkPostorder(e, &lsp); } /******************************************* * Look for references to variables in a scope enclosing the new function literal. */ -int lambdaCheckForNestedRef(Expression *e, void *param) +void lambdaCheckForNestedRef(Expression *e, Scope *sc) { - Scope *sc = (Scope *)param; - /* We could use virtual functions instead of a switch, - * but it doesn't seem worth the bother. - */ - switch (e->op) + class LambdaCheckForNestedRef : public StoppableVisitor { - case TOKsymoff: - { SymOffExp *se = (SymOffExp *)e; - VarDeclaration *v = se->var->isVarDeclaration(); + Scope *sc; + public: + LambdaCheckForNestedRef(Scope *sc) : sc(sc) {} + + void visit(Expression *) + { + } + + void visit(SymOffExp *e) + { + VarDeclaration *v = e->var->isVarDeclaration(); if (v) v->checkNestedReference(sc, Loc()); - break; } - case TOKvar: - { VarExp *ve = (VarExp *)e; - VarDeclaration *v = ve->var->isVarDeclaration(); + void visit(VarExp *e) + { + VarDeclaration *v = e->var->isVarDeclaration(); if (v) v->checkNestedReference(sc, Loc()); - break; } - case TOKthis: - case TOKsuper: - { ThisExp *te = (ThisExp *)e; - VarDeclaration *v = te->var->isVarDeclaration(); + void visit(ThisExp *e) + { + VarDeclaration *v = e->var->isVarDeclaration(); if (v) v->checkNestedReference(sc, Loc()); - break; } - case TOKdeclaration: - { DeclarationExp *de = (DeclarationExp *)e; - VarDeclaration *v = de->declaration->isVarDeclaration(); + void visit(DeclarationExp *e) + { + VarDeclaration *v = e->declaration->isVarDeclaration(); if (v) { v->checkNestedReference(sc, Loc()); @@ -154,16 +160,15 @@ int lambdaCheckForNestedRef(Expression *e, void *param) * checking the declaration initializer too. */ if (v->init && v->init->isExpInitializer()) - { Expression *ie = v->init->toExpression(); - ie->apply (&lambdaCheckForNestedRef, param); + { + Expression *ie = v->init->toExpression(); + lambdaCheckForNestedRef(ie, sc); } } - break; } + }; - default: - break; - } - return 0; + LambdaCheckForNestedRef lcnr(sc); + walkPostorder(e, &lcnr); } diff --git a/gcc/d/dfrontend/doc.c b/gcc/d/dfrontend/doc.c index f24f91bab..e7120219e 100644 --- a/gcc/d/dfrontend/doc.c +++ b/gcc/d/dfrontend/doc.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/doc.c + */ // This implements the Ddoc capability. @@ -20,6 +21,8 @@ #include "root.h" #include "port.h" +#include "attrib.h" +#include "cond.h" #include "mars.h" #include "dsymbol.h" #include "macro.h" @@ -37,6 +40,10 @@ #include "mtype.h" #include "utf.h" +void emitMemberComments(ScopeDsymbol *sds, Scope *sc); +void toDocBuffer(Dsymbol *s, OutBuffer *buf, Scope *sc); +void emitComment(Dsymbol *s, Scope *sc); + struct Escape { const char *strings[256]; @@ -110,10 +117,18 @@ Parameter *isFunctionParameter(Dsymbol *s, const utf8_t *p, size_t len); TemplateParameter *isTemplateParameter(Dsymbol *s, const utf8_t *p, size_t len); int isIdStart(const utf8_t *p); +bool isCVariadicArg(const utf8_t *p, size_t len); int isIdTail(const utf8_t *p); int isIndentWS(const utf8_t *p); int utfStride(const utf8_t *p); +// Workaround for missing Parameter instance for variadic params. (it's unnecessary to instantiate one). +bool isCVariadicParameter(Dsymbol *s, const utf8_t *p, size_t len) +{ + TypeFunction *tf = isTypeFunction(s); + return tf && tf->varargs == 1 && cmp("...", p, len) == 0; +} + static const char ddoc_default[] = "\ DDOC = \n\ \n\ @@ -199,6 +214,7 @@ DDOC_BLANKLINE = $(BR)$(BR)\n\ \n\ DDOC_ANCHOR = \n\ DDOC_PSYMBOL = $(U $0)\n\ +DDOC_PSUPER_SYMBOL = $(U $0)\n\ DDOC_KEYWORD = $(B $0)\n\ DDOC_PARAM = $(I $0)\n\ \n\ @@ -217,7 +233,7 @@ static const char ddoc_decl_dd_e[] = ")\n"; /**************************************************** */ -void Module::gendocfile() +void gendocfile(Module *m) { static OutBuffer mbuf; static int mbuf_done; @@ -227,7 +243,8 @@ void Module::gendocfile() //printf("Module::gendocfile()\n"); if (!mbuf_done) // if not already read the ddoc files - { mbuf_done = 1; + { + mbuf_done = 1; // Use our internal default mbuf.write(ddoc_default, strlen(ddoc_default)); @@ -242,75 +259,77 @@ void Module::gendocfile() { FileName f((*global.params.ddocfiles)[i]); File file(&f); - readFile(loc, &file); + readFile(m->loc, &file); // BUG: convert file contents to UTF-8 before use //printf("file: '%.*s'\n", file.len, file.buffer); mbuf.write(file.buffer, file.len); } } - DocComment::parseMacros(&escapetable, ¯otable, (utf8_t *)mbuf.data, mbuf.offset); + DocComment::parseMacros(&m->escapetable, &m->macrotable, (utf8_t *)mbuf.data, mbuf.offset); - Scope *sc = Scope::createGlobal(this); // create root scope + Scope *sc = Scope::createGlobal(m); // create root scope sc->docbuf = &buf; - DocComment *dc = DocComment::parse(sc, this, comment); - dc->pmacrotable = ¯otable; - dc->pescapetable = &escapetable; + DocComment *dc = DocComment::parse(sc, m, m->comment); + dc->pmacrotable = &m->macrotable; + dc->pescapetable = &m->escapetable; // Generate predefined macros // Set the title to be the name of the module - { const char *p = toPrettyChars(); - Macro::define(¯otable, (utf8_t *)"TITLE", 5, (utf8_t *)p, strlen(p)); + { + const char *p = m->toPrettyChars(); + Macro::define(&m->macrotable, (utf8_t *)"TITLE", 5, (utf8_t *)p, strlen(p)); } // Set time macros - { time_t t; + { + time_t t; time(&t); char *p = ctime(&t); p = mem.strdup(p); - Macro::define(¯otable, (utf8_t *)"DATETIME", 8, (utf8_t *)p, strlen(p)); - Macro::define(¯otable, (utf8_t *)"YEAR", 4, (utf8_t *)p + 20, 4); + Macro::define(&m->macrotable, (utf8_t *)"DATETIME", 8, (utf8_t *)p, strlen(p)); + Macro::define(&m->macrotable, (utf8_t *)"YEAR", 4, (utf8_t *)p + 20, 4); } - char *srcfilename = srcfile->toChars(); - Macro::define(¯otable, (utf8_t *)"SRCFILENAME", 11, (utf8_t *)srcfilename, strlen(srcfilename)); + char *srcfilename = m->srcfile->toChars(); + Macro::define(&m->macrotable, (utf8_t *)"SRCFILENAME", 11, (utf8_t *)srcfilename, strlen(srcfilename)); - char *docfilename = docfile->toChars(); - Macro::define(¯otable, (utf8_t *)"DOCFILENAME", 11, (utf8_t *)docfilename, strlen(docfilename)); + char *docfilename = m->docfile->toChars(); + Macro::define(&m->macrotable, (utf8_t *)"DOCFILENAME", 11, (utf8_t *)docfilename, strlen(docfilename)); if (dc->copyright) { dc->copyright->nooutput = 1; - Macro::define(¯otable, (utf8_t *)"COPYRIGHT", 9, dc->copyright->body, dc->copyright->bodylen); + Macro::define(&m->macrotable, (utf8_t *)"COPYRIGHT", 9, dc->copyright->body, dc->copyright->bodylen); } - buf.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", srcfile->toChars()); - if (isDocFile) + buf.printf("$(DDOC_COMMENT Generated by Ddoc from %s)\n", m->srcfile->toChars()); + if (m->isDocFile) { - size_t commentlen = strlen((char *)comment); + size_t commentlen = strlen((char *)m->comment); if (dc->macros) { - commentlen = dc->macros->name - comment; - dc->macros->write(dc, sc, this, sc->docbuf); + commentlen = dc->macros->name - m->comment; + dc->macros->write(dc, sc, m, sc->docbuf); } - sc->docbuf->write(comment, commentlen); - highlightText(sc, this, sc->docbuf, 0); + sc->docbuf->write(m->comment, commentlen); + highlightText(sc, m, sc->docbuf, 0); } else { - dc->writeSections(sc, this, sc->docbuf); - emitMemberComments(sc); + dc->writeSections(sc, m, sc->docbuf); + emitMemberComments(m, sc); } //printf("BODY= '%.*s'\n", buf.offset, buf.data); - Macro::define(¯otable, (utf8_t *)"BODY", 4, (utf8_t *)buf.data, buf.offset); + Macro::define(&m->macrotable, (utf8_t *)"BODY", 4, (utf8_t *)buf.data, buf.offset); OutBuffer buf2; buf2.writestring("$(DDOC)\n"); size_t end = buf2.offset; - macrotable->expand(&buf2, 0, &end, NULL, 0); + m->macrotable->expand(&buf2, 0, &end, NULL, 0); #if 1 /* Remove all the escape sequences from buf2, @@ -344,15 +363,16 @@ void Module::gendocfile() } // Transfer image to file - assert(docfile); - docfile->setbuffer(buf.data, buf.offset); - docfile->ref = 1; - ensurePathToNameExists(Loc(), docfile->toChars()); - writeFile(loc, docfile); + assert(m->docfile); + m->docfile->setbuffer(buf.data, buf.offset); + m->docfile->ref = 1; + ensurePathToNameExists(Loc(), m->docfile->toChars()); + writeFile(m->loc, m->docfile); #else /* Remove all the escape sequences from buf2 */ - { size_t i = 0; + { + size_t i = 0; utf8_t *p = buf2.data; for (size_t j = 0; j < buf2.offset; j++) { @@ -368,10 +388,10 @@ void Module::gendocfile() } // Transfer image to file - docfile->setbuffer(buf2.data, buf2.offset); - docfile->ref = 1; - ensurePathToNameExists(Loc(), docfile->toChars()); - writeFile(loc, docfile); + m->docfile->setbuffer(buf2.data, buf2.offset); + m->docfile->ref = 1; + ensurePathToNameExists(Loc(), m->docfile->toChars()); + writeFile(m->loc, m->docfile); #endif } @@ -488,16 +508,29 @@ void escapeStrayParenthesis(OutBuffer *buf, size_t start, Dsymbol *s) } } -static bool emitAnchorName(OutBuffer *buf, Dsymbol *s) +// Basically, this is to skip over things like private{} blocks in a struct or +// class definition that don't add any components to the qualified name. +static Scope *skipNonQualScopes(Scope *sc) +{ + while (sc && !sc->scopesym) + sc = sc->enclosing; + return sc; +} + +static bool emitAnchorName(OutBuffer *buf, Dsymbol *s, Scope *sc) { if (!s || s->isPackage() || s->isModule()) return false; TemplateDeclaration *td; - bool dot; + bool dot = false; // Add parent names first - dot = emitAnchorName(buf, s->parent); + if (s->parent) + dot = emitAnchorName(buf, s->parent, sc); + else if (sc) + dot = emitAnchorName(buf, sc->scopesym, skipNonQualScopes(sc->enclosing)); + // Eponymous template members can share the parent anchor name if (s->parent && (td = s->parent->isTemplateDeclaration()) != NULL && td->onemember == s) @@ -518,10 +551,10 @@ static bool emitAnchorName(OutBuffer *buf, Dsymbol *s) return true; } -static void emitAnchor(OutBuffer *buf, Dsymbol *s) +static void emitAnchor(OutBuffer *buf, Dsymbol *s, Scope *sc) { buf->writestring("$(DDOC_ANCHOR "); - emitAnchorName(buf, s); + emitAnchorName(buf, s, skipNonQualScopes(sc)); buf->writeByte(')'); } @@ -556,7 +589,7 @@ void emitUnittestComment(Scope *sc, Dsymbol *s, size_t ofs) while (*c == ' ' || *c == '\t' || *c == '\n' || *c == '\r') ++c; OutBuffer codebuf; - codebuf.writestring("$(DDOC_EXAMPLES \n"); + codebuf.writestring("$(DDOC_EXAMPLES "); size_t o = codebuf.offset; codebuf.writestring((char *)c); @@ -571,7 +604,9 @@ void emitUnittestComment(Scope *sc, Dsymbol *s, size_t ofs) } codebuf.writestring(")"); - buf->insert(buf->offset - ofs, codebuf.data, codebuf.offset); + buf->insert(ofs, codebuf.data, codebuf.offset); + ofs += codebuf.offset; + sc->lastoffset2 = ofs; } } @@ -579,68 +614,92 @@ void emitUnittestComment(Scope *sc, Dsymbol *s, size_t ofs) * Emit doc comment to documentation file */ -void Dsymbol::emitDitto(Scope *sc) +void emitDitto(Dsymbol *s, Scope *sc) { //printf("Dsymbol::emitDitto() %s %s\n", kind(), toChars()); OutBuffer *buf = sc->docbuf; - size_t o; OutBuffer b; b.writestring("$(DDOC_DITTO "); - o = b.offset; - toDocBuffer(&b, sc); - //printf("b: '%.*s'\n", b.offset, b.data); - /* If 'this' is a function template, then highlightCode() was - * already run by FuncDeclaration::toDocbuffer(). - */ - TemplateDeclaration *td; - if (parent && - (td = parent->isTemplateDeclaration()) != NULL && - td->onemember == this) - { - } - else - highlightCode(sc, this, &b, o); + size_t o = b.offset; + toDocBuffer(s, &b, sc); + //printf("b: '%.*s'\n", b.offset, b.data); + /* If 'this' is a function template, then highlightCode() was + * already run by FuncDeclaration::toDocbuffer(). + */ + TemplateDeclaration *td; + if (s->parent && + (td = s->parent->isTemplateDeclaration()) != NULL && + td->onemember == s) + { + } + else + highlightCode(sc, s, &b, o); b.writeByte(')'); buf->spread(sc->lastoffset, b.offset); memcpy(buf->data + sc->lastoffset, b.data, b.offset); sc->lastoffset += b.offset; + sc->lastoffset2 += b.offset; + + Dsymbol *p = s; + if (!s->ddocUnittest && s->parent) + p = s->parent->isTemplateDeclaration(); + if (p) + emitUnittestComment(sc, p, sc->lastoffset2); +} - Dsymbol *s = this; - if (!s->ddocUnittest && parent) - s = parent->isTemplateDeclaration(); - if (s) - emitUnittestComment(sc, s, strlen(ddoc_decl_dd_e)); +/** Recursively expand template mixin member docs into the scope. */ +static void expandTemplateMixinComments(TemplateMixin *tm, Scope *sc) +{ + if (!tm->semanticRun) tm->semantic(sc); + TemplateDeclaration *td = (tm && tm->tempdecl) ? + tm->tempdecl->isTemplateDeclaration() : NULL; + if (td && td->members) + { + for (size_t i = 0; i < td->members->dim; i++) + { + Dsymbol *sm = (*td->members)[i]; + TemplateMixin *tmc = sm->isTemplateMixin(); + if (tmc && tmc->comment) + expandTemplateMixinComments(tmc, sc); + else + emitComment(sm, sc); + } + } } -void ScopeDsymbol::emitMemberComments(Scope *sc) +void emitMemberComments(ScopeDsymbol *sds, Scope *sc) { //printf("ScopeDsymbol::emitMemberComments() %s\n", toChars()); OutBuffer *buf = sc->docbuf; - if (members) - { const char *m = "$(DDOC_MEMBERS \n"; - - if (isModule()) - m = "$(DDOC_MODULE_MEMBERS \n"; - else if (isClassDeclaration()) - m = "$(DDOC_CLASS_MEMBERS \n"; - else if (isStructDeclaration()) - m = "$(DDOC_STRUCT_MEMBERS \n"; - else if (isEnumDeclaration()) - m = "$(DDOC_ENUM_MEMBERS \n"; - else if (isTemplateDeclaration()) - m = "$(DDOC_TEMPLATE_MEMBERS \n"; + if (sds->members) + { + const char *m = "$(DDOC_MEMBERS "; + if (sds->isModule()) + m = "$(DDOC_MODULE_MEMBERS "; + else if (sds->isClassDeclaration()) + m = "$(DDOC_CLASS_MEMBERS "; + else if (sds->isStructDeclaration()) + m = "$(DDOC_STRUCT_MEMBERS "; + else if (sds->isEnumDeclaration()) + m = "$(DDOC_ENUM_MEMBERS "; + else if (sds->isTemplateDeclaration()) + m = "$(DDOC_TEMPLATE_MEMBERS "; size_t offset1 = buf->offset; // save starting offset buf->writestring(m); size_t offset2 = buf->offset; // to see if we write anything - sc = sc->push(this); - for (size_t i = 0; i < members->dim; i++) + sc = sc->push(sds); + for (size_t i = 0; i < sds->members->dim; i++) { - Dsymbol *s = (*members)[i]; + Dsymbol *s = (*sds->members)[i]; //printf("\ts = '%s'\n", s->toChars()); - s->emitComment(sc); + + // only expand if parent is a non-template (semantic won't work) + if (s->comment && s->isTemplateMixin() && s->parent && !s->parent->isTemplateDeclaration()) + expandTemplateMixinComments((TemplateMixin *)s, sc); + emitComment(s, sc); } sc->pop(); if (buf->offset == offset2) @@ -656,543 +715,617 @@ void ScopeDsymbol::emitMemberComments(Scope *sc) void emitProtection(OutBuffer *buf, PROT prot) { - const char *p = (prot == PROTpublic) ? NULL : Pprotectionnames[prot]; - + const char *p = (prot == PROTpublic) ? NULL : protectionToChars(prot); if (p) buf->printf("%s ", p); } -void Dsymbol::emitComment(Scope *sc) { } -void InvariantDeclaration::emitComment(Scope *sc) { } -void UnitTestDeclaration::emitComment(Scope *sc) { } -void PostBlitDeclaration::emitComment(Scope *sc) { } -void DtorDeclaration::emitComment(Scope *sc) { } -void StaticCtorDeclaration::emitComment(Scope *sc) { } -void StaticDtorDeclaration::emitComment(Scope *sc) { } -void ClassInfoDeclaration::emitComment(Scope *sc) { } -void TypeInfoDeclaration::emitComment(Scope *sc) { } - - -void Declaration::emitComment(Scope *sc) -{ - //printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", this, toChars(), comment); - //printf("type = %p\n", type); - - if (protection == PROTprivate || sc->protection == PROTprivate || - !ident || (!type && !isCtorDeclaration() && !isAliasDeclaration())) - return; - if (!comment) - return; - - OutBuffer *buf = sc->docbuf; - DocComment *dc = DocComment::parse(sc, this, comment); - size_t o; - - if (!dc) - { - emitDitto(sc); - return; - } - dc->pmacrotable = &sc->module->macrotable; - - buf->writestring(ddoc_decl_s); - o = buf->offset; - toDocBuffer(buf, sc); - highlightCode(sc, this, buf, o); - sc->lastoffset = buf->offset; - buf->writestring(ddoc_decl_e); - - buf->writestring(ddoc_decl_dd_s); - dc->writeSections(sc, this, buf); - buf->writestring(ddoc_decl_dd_e); -} - -void AggregateDeclaration::emitComment(Scope *sc) +void emitComment(Dsymbol *s, Scope *sc) { - //printf("AggregateDeclaration::emitComment() '%s'\n", toChars()); - if (prot() == PROTprivate || sc->protection == PROTprivate) - return; - if (!comment) - return; - - OutBuffer *buf = sc->docbuf; - DocComment *dc = DocComment::parse(sc, this, comment); - - if (!dc) + class EmitComment : public Visitor { - emitDitto(sc); - return; - } - dc->pmacrotable = &sc->module->macrotable; - - buf->writestring(ddoc_decl_s); - size_t o = buf->offset; - toDocBuffer(buf, sc); - highlightCode(sc, this, buf, o); - sc->lastoffset = buf->offset; - buf->writestring(ddoc_decl_e); - - buf->writestring(ddoc_decl_dd_s); - dc->writeSections(sc, this, buf); - emitMemberComments(sc); - buf->writestring(ddoc_decl_dd_e); -} - -void TemplateDeclaration::emitComment(Scope *sc) -{ - //printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", toChars(), kind()); - if (prot() == PROTprivate || sc->protection == PROTprivate) - return; + public: + Scope *sc; - const utf8_t *com = comment; - int hasmembers = 1; - - Dsymbol *ss = this; - - if (onemember) - { - ss = onemember->isAggregateDeclaration(); - if (!ss) + EmitComment(Scope *sc) + : sc(sc) { - ss = onemember->isFuncDeclaration(); - if (ss) - { hasmembers = 0; - if (com != ss->comment) - com = Lexer::combineComments(com, ss->comment); - } - else - ss = this; } - } - if (!com) - return; + void visit(Dsymbol *) {} + void visit(InvariantDeclaration *) {} + void visit(UnitTestDeclaration *) {} + void visit(PostBlitDeclaration *) {} + void visit(DtorDeclaration *) {} + void visit(StaticCtorDeclaration *) {} + void visit(StaticDtorDeclaration *) {} + void visit(ClassInfoDeclaration *) {} + void visit(TypeInfoDeclaration *) {} + + void visit(Declaration *d) + { + //printf("Declaration::emitComment(%p '%s'), comment = '%s'\n", d, d->toChars(), d->comment); + //printf("type = %p\n", d->type); - OutBuffer *buf = sc->docbuf; - DocComment *dc = DocComment::parse(sc, this, com); - size_t o; + if (d->protection == PROTprivate || sc->protection == PROTprivate || + !d->ident || (!d->type && !d->isCtorDeclaration() && !d->isAliasDeclaration())) + return; + if (!d->comment) + return; - if (!dc) - { - ss->emitDitto(sc); - return; - } - dc->pmacrotable = &sc->module->macrotable; - - buf->writestring(ddoc_decl_s); - o = buf->offset; - ss->toDocBuffer(buf, sc); - if (ss == this) - highlightCode(sc, this, buf, o); - sc->lastoffset = buf->offset; - buf->writestring(ddoc_decl_e); - - buf->writestring(ddoc_decl_dd_s); - dc->writeSections(sc, this, buf); - if (hasmembers) - ((ScopeDsymbol *)ss)->emitMemberComments(sc); - buf->writestring(ddoc_decl_dd_e); -} + OutBuffer *buf = sc->docbuf; + DocComment *dc = DocComment::parse(sc, d, d->comment); -void EnumDeclaration::emitComment(Scope *sc) -{ - if (prot() == PROTprivate || sc->protection == PROTprivate) - return; - //if (!comment) - { - if (isAnonymous() && members) - { - for (size_t i = 0; i < members->dim; i++) + if (!dc) { - Dsymbol *s = (*members)[i]; - s->emitComment(sc); + emitDitto(d, sc); + return; } - return; + dc->pmacrotable = &sc->module->macrotable; + + buf->writestring(ddoc_decl_s); + size_t o = buf->offset; + toDocBuffer(d, buf, sc); + highlightCode(sc, d, buf, o); + sc->lastoffset = buf->offset; + buf->writestring(ddoc_decl_e); + + buf->writestring(ddoc_decl_dd_s); + dc->writeSections(sc, d, buf); + buf->writestring(ddoc_decl_dd_e); } - } - if (!comment) - return; - if (isAnonymous()) - return; - OutBuffer *buf = sc->docbuf; - DocComment *dc = DocComment::parse(sc, this, comment); + void visit(AggregateDeclaration *ad) + { + //printf("AggregateDeclaration::emitComment() '%s'\n", ad->toChars()); + if (ad->prot() == PROTprivate || sc->protection == PROTprivate) + return; + if (!ad->comment) + return; - if (!dc) - { - emitDitto(sc); - return; - } - dc->pmacrotable = &sc->module->macrotable; - - buf->writestring(ddoc_decl_s); - size_t o = buf->offset; - toDocBuffer(buf, sc); - highlightCode(sc, this, buf, o); - sc->lastoffset = buf->offset; - buf->writestring(ddoc_decl_e); - - buf->writestring(ddoc_decl_dd_s); - dc->writeSections(sc, this, buf); - emitMemberComments(sc); - buf->writestring(ddoc_decl_dd_e); -} + OutBuffer *buf = sc->docbuf; + DocComment *dc = DocComment::parse(sc, ad, ad->comment); -void EnumMember::emitComment(Scope *sc) -{ - //printf("EnumMember::emitComment(%p '%s'), comment = '%s'\n", this, toChars(), comment); - if (prot() == PROTprivate || sc->protection == PROTprivate) - return; - if (!comment) - return; + if (!dc) + { + emitDitto(ad, sc); + return; + } + dc->pmacrotable = &sc->module->macrotable; - OutBuffer *buf = sc->docbuf; - DocComment *dc = DocComment::parse(sc, this, comment); + buf->writestring(ddoc_decl_s); + size_t o = buf->offset; + toDocBuffer(ad, buf, sc); + highlightCode(sc, ad, buf, o); + sc->lastoffset = buf->offset; + buf->writestring(ddoc_decl_e); + + buf->writestring(ddoc_decl_dd_s); + dc->writeSections(sc, ad, buf); + emitMemberComments(ad, sc); + buf->writestring(ddoc_decl_dd_e); + } - if (!dc) - { - emitDitto(sc); - return; - } - dc->pmacrotable = &sc->module->macrotable; - - buf->writestring(ddoc_decl_s); - size_t o = buf->offset; - toDocBuffer(buf, sc); - highlightCode(sc, this, buf, o); - sc->lastoffset = buf->offset; - buf->writestring(ddoc_decl_e); - - buf->writestring(ddoc_decl_dd_s); - dc->writeSections(sc, this, buf); - buf->writestring(ddoc_decl_dd_e); -} + void visit(TemplateDeclaration *td) + { + //printf("TemplateDeclaration::emitComment() '%s', kind = %s\n", td->toChars(), td->kind()); + if (td->prot() == PROTprivate || sc->protection == PROTprivate) + return; -/******************************* toDocBuffer **********************************/ + const utf8_t *com = td->comment; + bool hasmembers = true; -void Dsymbol::toDocBuffer(OutBuffer *buf, Scope *sc) -{ - //printf("Dsymbol::toDocbuffer() %s\n", toChars()); - HdrGenState hgs; + Dsymbol *ss = td; - hgs.ddoc = 1; - toCBuffer(buf, &hgs); -} + if (td->onemember) + { + ss = td->onemember->isAggregateDeclaration(); + if (!ss) + { + ss = td->onemember->isFuncDeclaration(); + if (ss) + { + hasmembers = false; + if (com != ss->comment) + com = Lexer::combineComments(com, ss->comment); + } + else + ss = td; + } + } -void prefix(OutBuffer *buf, Dsymbol *s) -{ - if (s->isDeprecated()) - buf->writestring("deprecated "); - Declaration *d = s->isDeclaration(); - if (d) - { - emitProtection(buf, d->protection); + if (!com) + return; - if (d->isStatic()) - buf->writestring("static "); - else if (d->isFinal()) - buf->writestring("final "); - else if (d->isAbstract()) - buf->writestring("abstract "); + OutBuffer *buf = sc->docbuf; + DocComment *dc = DocComment::parse(sc, td, com); + size_t o; - if (!d->isFuncDeclaration()) // toCBufferWithAttributes handles this - { - if (d->isConst()) - buf->writestring("const "); - if (d->isImmutable()) - buf->writestring("immutable "); - if (d->isSynchronized()) - buf->writestring("synchronized "); + if (!dc) + { + emitDitto(ss, sc); + return; + } + dc->pmacrotable = &sc->module->macrotable; + + buf->writestring(ddoc_decl_s); + o = buf->offset; + toDocBuffer(ss, buf, sc); + if (ss == td) + highlightCode(sc, td, buf, o); + sc->lastoffset = buf->offset; + buf->writestring(ddoc_decl_e); + + buf->writestring(ddoc_decl_dd_s); + dc->writeSections(sc, td, buf); + if (hasmembers) + emitMemberComments((ScopeDsymbol *)ss, sc); + buf->writestring(ddoc_decl_dd_e); } - } -} -void declarationToDocBuffer(Declaration *decl, OutBuffer *buf, TemplateDeclaration *td) -{ - //printf("declarationToDocBuffer() %s, originalType = %s, td = %s\n", decl->toChars(), decl->originalType ? decl->originalType->toChars() : "--", td ? td->toChars() : "--"); - if (decl->ident) - { - if (decl->isDeprecated()) - buf->writestring("$(DEPRECATED "); + void visit(EnumDeclaration *ed) + { + if (ed->prot() == PROTprivate || sc->protection == PROTprivate) + return; + if (ed->isAnonymous() && ed->members) + { + for (size_t i = 0; i < ed->members->dim; i++) + { + Dsymbol *s = (*ed->members)[i]; + emitComment(s, sc); + } + return; + } + if (!ed->comment) + return; + if (ed->isAnonymous()) + return; - prefix(buf, decl); + OutBuffer *buf = sc->docbuf; + DocComment *dc = DocComment::parse(sc, ed, ed->comment); - if (decl->type) - { HdrGenState hgs; - hgs.ddoc = 1; - Type *origType = decl->originalType ? decl->originalType : decl->type; - if (origType->ty == Tfunction) + if (!dc) { - TypeFunction *attrType = (TypeFunction*)(decl->ident == Id::ctor ? origType : decl->type); - ((TypeFunction*)origType)->toCBufferWithAttributes(buf, decl->ident, &hgs, attrType, td); + emitDitto(ed, sc); + return; } - else - origType->toCBuffer(buf, decl->ident, &hgs); + dc->pmacrotable = &sc->module->macrotable; + + buf->writestring(ddoc_decl_s); + size_t o = buf->offset; + toDocBuffer(ed, buf, sc); + highlightCode(sc, ed, buf, o); + sc->lastoffset = buf->offset; + buf->writestring(ddoc_decl_e); + + buf->writestring(ddoc_decl_dd_s); + dc->writeSections(sc, ed, buf); + emitMemberComments(ed, sc); + buf->writestring(ddoc_decl_dd_e); } - else - buf->writestring(decl->ident->toChars()); - // emit constraints if declaration is a templated declaration - if (td && td->constraint) + void visit(EnumMember *em) { - HdrGenState hgs; - hgs.ddoc = 1; - buf->writestring(" if ("); - td->constraint->toCBuffer(buf, &hgs); - buf->writeByte(')'); - } + //printf("EnumMember::emitComment(%p '%s'), comment = '%s'\n", em, em->toChars(), em->comment); + if (em->prot() == PROTprivate || sc->protection == PROTprivate) + return; + if (!em->comment) + return; - if (decl->isDeprecated()) - buf->writestring(")"); + OutBuffer *buf = sc->docbuf; + DocComment *dc = DocComment::parse(sc, em, em->comment); - buf->writestring(";\n"); - } -} + if (!dc) + { + emitDitto(em, sc); + return; + } + dc->pmacrotable = &sc->module->macrotable; -void Declaration::toDocBuffer(OutBuffer *buf, Scope *sc) -{ - declarationToDocBuffer(this, buf, NULL); -} + buf->writestring(ddoc_decl_s); + size_t o = buf->offset; + toDocBuffer(em, buf, sc); + highlightCode(sc, em, buf, o); + sc->lastoffset = buf->offset; + buf->writestring(ddoc_decl_e); + + buf->writestring(ddoc_decl_dd_s); + dc->writeSections(sc, em, buf); + buf->writestring(ddoc_decl_dd_e); + } -void AliasDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc) -{ - //printf("AliasDeclaration::toDocbuffer() %s\n", toChars()); - if (ident) - { - if (isDeprecated()) - buf->writestring("deprecated "); + void visit(AttribDeclaration *ad) + { + //printf("AttribDeclaration::emitComment(sc = %p)\n", sc); + + /* A general problem with this, illustrated by BUGZILLA 2516, + * is that attributes are not transmitted through to the underlying + * member declarations for template bodies, because semantic analysis + * is not done for template declaration bodies + * (only template instantiations). + * Hence, Ddoc omits attributes from template members. + */ - emitProtection(buf, protection); - buf->printf("alias %s = ", toChars()); + Dsymbols *d = ad->include(NULL, NULL); - if (Dsymbol *s = aliassym) // ident alias + if (d) + { + for (size_t i = 0; i < d->dim; i++) + { + Dsymbol *s = (*d)[i]; + //printf("AttribDeclaration::emitComment %s\n", s->toChars()); + emitComment(s, sc); + } + } + } + + void visit(ProtDeclaration *pd) { - prettyPrintDsymbol(buf, s, parent); + if (pd->decl) + { + sc = sc->push(); + sc->protection = pd->protection; + visit((AttribDeclaration *)pd); + sc = sc->pop(); + } } - else if (Type *type = getType()) // type alias + + void visit(ConditionalDeclaration *cd) { - if (type->ty == Tclass || type->ty == Tstruct || type->ty == Tenum) + //printf("ConditionalDeclaration::emitComment(sc = %p)\n", sc); + if (cd->condition->inc) { - if (Dsymbol *s = type->toDsymbol(NULL)) // elaborate type - prettyPrintDsymbol(buf, s, parent); - else - buf->writestring(type->toChars()); + visit((AttribDeclaration *)cd); } - else + else if (sc->docbuf) { - // simple type - buf->writestring(type->toChars()); + /* If generating doc comment, be careful because if we're inside + * a template, then include(NULL, NULL) will fail. + */ + Dsymbols *d = cd->decl ? cd->decl : cd->elsedecl; + for (size_t i = 0; i < d->dim; i++) + { + Dsymbol *s = (*d)[i]; + emitComment(s, sc); + } } } + }; - buf->writestring(";\n"); - } + EmitComment v(sc); + s->accept(&v); } -void parentToBuffer(OutBuffer *buf, Dsymbol *s) -{ - if (s && !s->isPackage() && !s->isModule()) - { - parentToBuffer(buf, s->parent); - buf->writestring(s->toChars()); - buf->writestring("."); - } -} +/******************************* toDocBuffer **********************************/ -bool inSameModule(Dsymbol *s, Dsymbol *p) +void toDocBuffer(Dsymbol *s, OutBuffer *buf, Scope *sc) { - for ( ; s ; s = s->parent) + class ToDocBuffer : public Visitor { - if (s->isModule()) - break; - } + public: + OutBuffer *buf; + Scope *sc; - for ( ; p ; p = p->parent) - { - if (p->isModule()) - break; - } + ToDocBuffer(OutBuffer *buf, Scope *sc) + : buf(buf), sc(sc) + { + } - return s == p; -} + void visit(Dsymbol *s) + { + //printf("Dsymbol::toDocbuffer() %s\n", s->toChars()); + HdrGenState hgs; + hgs.ddoc = 1; + s->toCBuffer(buf, &hgs); + } -void prettyPrintDsymbol(OutBuffer *buf, Dsymbol *s, Dsymbol *parent) -{ - if (s->parent && (s->parent == parent)) // in current scope -> naked name - { - buf->writestring(s->toChars()); - } - else - if (!inSameModule(s, parent)) // in another module -> full name - { - buf->writestring(s->toPrettyChars()); - } - else // nested in a type in this module -> full name w/o module name - { - // if alias is nested in a user-type use module-scope lookup - if (!parent->isModule() && !parent->isPackage()) - buf->writestring("."); + void prefix(Dsymbol *s) + { + if (s->isDeprecated()) + buf->writestring("deprecated "); + Declaration *d = s->isDeclaration(); + if (d) + { + emitProtection(buf, d->protection); - parentToBuffer(buf, s->parent); - buf->writestring(s->toChars()); - } -} + if (d->isStatic()) + buf->writestring("static "); + else if (d->isFinal()) + buf->writestring("final "); + else if (d->isAbstract()) + buf->writestring("abstract "); -void TypedefDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc) -{ - if (ident) - { - if (isDeprecated()) - buf->writestring("deprecated "); + if (!d->isFuncDeclaration()) // functionToBufferFull handles this + { + if (d->isConst()) + buf->writestring("const "); + if (d->isImmutable()) + buf->writestring("immutable "); + if (d->isSynchronized()) + buf->writestring("synchronized "); + } + } + } - emitProtection(buf, protection); - buf->writestring("typedef "); - buf->writestring(toChars()); - buf->writestring(";\n"); - } -} + void declarationToDocBuffer(Declaration *decl, TemplateDeclaration *td) + { + //printf("declarationToDocBuffer() %s, originalType = %s, td = %s\n", decl->toChars(), decl->originalType ? decl->originalType->toChars() : "--", td ? td->toChars() : "--"); + if (decl->ident) + { + if (decl->isDeprecated()) + buf->writestring("$(DEPRECATED "); + prefix(decl); -void FuncDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc) -{ - //printf("FuncDeclaration::toDocbuffer() %s\n", toChars()); - if (ident) - { - TemplateDeclaration *td; + if (decl->type) + { + HdrGenState hgs; + hgs.ddoc = 1; + Type *origType = decl->originalType ? decl->originalType : decl->type; + if (origType->ty == Tfunction) + { + functionToBufferFull((TypeFunction *)origType, buf, decl->ident, &hgs, td); + } + else + origType->toCBuffer(buf, decl->ident, &hgs); + } + else + buf->writestring(decl->ident->toChars()); - if (parent && - (td = parent->isTemplateDeclaration()) != NULL && - td->onemember == this) - { /* It's a function template - */ - size_t o = buf->offset; + // emit constraints if declaration is a templated declaration + if (td && td->constraint) + { + HdrGenState hgs; + hgs.ddoc = 1; + buf->writestring(" if ("); + td->constraint->toCBuffer(buf, &hgs); + buf->writeByte(')'); + } - declarationToDocBuffer(this, buf, td); + if (decl->isDeprecated()) + buf->writestring(")"); - highlightCode(sc, this, buf, o); + buf->writestring(";\n"); + } } - else + + void visit(Declaration *d) { - Declaration::toDocBuffer(buf, sc); + declarationToDocBuffer(d, NULL); } - } -} -void AggregateDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc) -{ - if (ident) - { -#if 0 - emitProtection(buf, protection); -#endif - buf->printf("%s %s", kind(), toChars()); - buf->writestring(";\n"); - } -} + void visit(AliasDeclaration *ad) + { + //printf("AliasDeclaration::toDocbuffer() %s\n", ad->toChars()); + if (ad->ident) + { + if (ad->isDeprecated()) + buf->writestring("deprecated "); -void StructDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc) -{ - //printf("StructDeclaration::toDocbuffer() %s\n", toChars()); - if (ident) - { -#if 0 - emitProtection(buf, protection); -#endif - TemplateDeclaration *td; + emitProtection(buf, ad->protection); + buf->printf("alias %s = ", ad->toChars()); - if (parent && - (td = parent->isTemplateDeclaration()) != NULL && - td->onemember == this) - { size_t o = buf->offset; - td->toDocBuffer(buf, sc); - highlightCode(sc, this, buf, o); + if (Dsymbol *s = ad->aliassym) // ident alias + { + prettyPrintDsymbol(s, ad->parent); + } + else if (Type *type = ad->getType()) // type alias + { + if (type->ty == Tclass || type->ty == Tstruct || type->ty == Tenum) + { + if (Dsymbol *s = type->toDsymbol(NULL)) // elaborate type + prettyPrintDsymbol(s, ad->parent); + else + buf->writestring(type->toChars()); + } + else + { + // simple type + buf->writestring(type->toChars()); + } + } + + buf->writestring(";\n"); + } } - else + + void parentToBuffer(Dsymbol *s) { - buf->printf("%s %s", kind(), toChars()); + if (s && !s->isPackage() && !s->isModule()) + { + parentToBuffer(s->parent); + buf->writestring(s->toChars()); + buf->writestring("."); + } } - buf->writestring(";\n"); - } -} -void ClassDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc) -{ - //printf("ClassDeclaration::toDocbuffer() %s\n", toChars()); - if (ident) - { -#if 0 - emitProtection(buf, protection); -#endif - TemplateDeclaration *td; + static bool inSameModule(Dsymbol *s, Dsymbol *p) + { + for ( ; s ; s = s->parent) + { + if (s->isModule()) + break; + } + + for ( ; p ; p = p->parent) + { + if (p->isModule()) + break; + } - if (parent && - (td = parent->isTemplateDeclaration()) != NULL && - td->onemember == this) - { size_t o = buf->offset; - td->toDocBuffer(buf, sc); - highlightCode(sc, this, buf, o); + return s == p; } - else + + void prettyPrintDsymbol(Dsymbol *s, Dsymbol *parent) { - if (!isInterfaceDeclaration() && isAbstract()) - buf->writestring("abstract "); - buf->printf("%s %s", kind(), toChars()); + if (s->parent && (s->parent == parent)) // in current scope -> naked name + { + buf->writestring(s->toChars()); + } + else if (!inSameModule(s, parent)) // in another module -> full name + { + buf->writestring(s->toPrettyChars()); + } + else // nested in a type in this module -> full name w/o module name + { + // if alias is nested in a user-type use module-scope lookup + if (!parent->isModule() && !parent->isPackage()) + buf->writestring("."); + + parentToBuffer(s->parent); + buf->writestring(s->toChars()); + } } - int any = 0; - for (size_t i = 0; i < baseclasses->dim; i++) - { BaseClass *bc = (*baseclasses)[i]; - if (bc->protection == PROTprivate) - continue; - if (bc->base && bc->base->ident == Id::Object) - continue; + void visit(FuncDeclaration *fd) + { + //printf("FuncDeclaration::toDocbuffer() %s\n", fd->toChars()); + if (fd->ident) + { + TemplateDeclaration *td; - if (any) - buf->writestring(", "); - else - { buf->writestring(": "); - any = 1; + if (fd->parent && + (td = fd->parent->isTemplateDeclaration()) != NULL && + td->onemember == fd) + { + /* It's a function template + */ + size_t o = buf->offset; + + declarationToDocBuffer(fd, td); + + highlightCode(sc, fd, buf, o); + } + else + { + visit((Declaration *)fd); + } } - emitProtection(buf, bc->protection); - if (bc->base) + } + + void visit(AggregateDeclaration *ad) + { + if (ad->ident) { - buf->writestring(bc->base->toPrettyChars()); + #if 0 + emitProtection(buf, ad->protection); + #endif + buf->printf("%s %s", ad->kind(), ad->toChars()); + buf->writestring(";\n"); } - else + } + + void visit(StructDeclaration *sd) + { + //printf("StructDeclaration::toDocbuffer() %s\n", sd->toChars()); + if (sd->ident) { - HdrGenState hgs; - bc->type->toCBuffer(buf, NULL, &hgs); + #if 0 + emitProtection(buf, sd->protection); + #endif + TemplateDeclaration *td; + + if (sd->parent && + (td = sd->parent->isTemplateDeclaration()) != NULL && + td->onemember == sd) + { + size_t o = buf->offset; + toDocBuffer(td, buf, sc); + highlightCode(sc, sd, buf, o); + } + else + { + buf->printf("%s %s", sd->kind(), sd->toChars()); + } + buf->writestring(";\n"); } } - buf->writestring(";\n"); - } -} + void visit(ClassDeclaration *cd) + { + //printf("ClassDeclaration::toDocbuffer() %s\n", cd->toChars()); + if (cd->ident) + { + #if 0 + emitProtection(buf, cd->protection); + #endif + TemplateDeclaration *td; + + if (cd->parent && + (td = cd->parent->isTemplateDeclaration()) != NULL && + td->onemember == cd) + { + size_t o = buf->offset; + toDocBuffer(td, buf, sc); + highlightCode(sc, cd, buf, o); + } + else + { + if (!cd->isInterfaceDeclaration() && cd->isAbstract()) + buf->writestring("abstract "); + buf->printf("%s %s", cd->kind(), cd->toChars()); + } + int any = 0; + for (size_t i = 0; i < cd->baseclasses->dim; i++) + { + BaseClass *bc = (*cd->baseclasses)[i]; -void EnumDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc) -{ - if (ident) - { - buf->printf("%s %s", kind(), toChars()); - if (memtype) + if (bc->protection == PROTprivate) + continue; + if (bc->base && bc->base->ident == Id::Object) + continue; + + if (any) + buf->writestring(", "); + else + { + buf->writestring(": "); + any = 1; + } + emitProtection(buf, bc->protection); + if (bc->base) + { + buf->printf("$(DDOC_PSUPER_SYMBOL %s)", bc->base->toPrettyChars()); + } + else + { + HdrGenState hgs; + bc->type->toCBuffer(buf, NULL, &hgs); + } + } + buf->writestring(";\n"); + } + } + + void visit(EnumDeclaration *ed) { - buf->writestring(": $(DDOC_ENUM_BASETYPE "); - HdrGenState *hgs = NULL; - memtype->toCBuffer(buf, NULL, hgs); - buf->writestring(")"); + if (ed->ident) + { + buf->printf("%s %s", ed->kind(), ed->toChars()); + if (ed->memtype) + { + buf->writestring(": $(DDOC_ENUM_BASETYPE "); + HdrGenState hgs; + ed->memtype->toCBuffer(buf, NULL, &hgs); + buf->writestring(")"); + } + buf->writestring(";\n"); + } } - buf->writestring(";\n"); - } -} -void EnumMember::toDocBuffer(OutBuffer *buf, Scope *sc) -{ - if (ident) - { - buf->writestring(toChars()); - } -} + void visit(EnumMember *em) + { + if (em->ident) + { + buf->writestring(em->toChars()); + } + } + }; + + ToDocBuffer v(buf, sc); + s->accept(&v); +} /********************************* DocComment *********************************/ @@ -1359,7 +1492,7 @@ void DocComment::writeSections(Scope *sc, Dsymbol *s, OutBuffer *buf) //printf("DocComment::writeSections()\n"); if (sections.dim || s->ddocUnittest) { - buf->writestring("$(DDOC_SECTIONS \n"); + buf->writestring("$(DDOC_SECTIONS "); for (size_t i = 0; i < sections.dim; i++) { Section *sec = sections[i]; @@ -1379,7 +1512,8 @@ void DocComment::writeSections(Scope *sc, Dsymbol *s, OutBuffer *buf) } } if (s->ddocUnittest) - emitUnittestComment(sc, s, 0); + emitUnittestComment(sc, s, buf->offset); + sc->lastoffset2 = buf->offset; buf->writestring(")\n"); } else @@ -1442,19 +1576,19 @@ void ParamSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) size_t len = bodylen; const utf8_t *pend = p + len; - const utf8_t *tempstart; - size_t templen; + const utf8_t *tempstart = NULL; + size_t templen = 0; - const utf8_t *namestart; + const utf8_t *namestart = NULL; size_t namelen = 0; // !=0 if line continuation - const utf8_t *textstart; - size_t textlen; + const utf8_t *textstart = NULL; + size_t textlen = 0; size_t o, paramcount = 0; - Parameter *arg; + Parameter *arg = NULL; - buf->writestring("$(DDOC_PARAMS \n"); + buf->writestring("$(DDOC_PARAMS "); while (p < pend) { // Skip to start of macro @@ -1472,7 +1606,7 @@ void ParamSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) goto Lcont; default: - if (isIdStart(p)) + if (isIdStart(p) || isCVariadicArg(p, pend - p)) break; if (namelen) goto Ltext; // continuation of prev macro @@ -1484,13 +1618,17 @@ void ParamSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) while (isIdTail(p)) p += utfStride(p); + if (isCVariadicArg(p, pend - p)) + p += 3; + templen = p - tempstart; while (*p == ' ' || *p == '\t') p++; if (*p != '=') - { if (namelen) + { + if (namelen) goto Ltext; // continuation of prev macro goto Lskipline; } @@ -1507,7 +1645,12 @@ void ParamSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) buf->writestring("$(DDOC_PARAM_ID "); o = buf->offset; arg = isFunctionParameter(s, namestart, namelen); - if (arg && arg->type && arg->ident) + bool isCVariadic = isCVariadicParameter(s, namestart, namelen); + if (isCVariadic) + { + buf->writestring("..."); + } + else if (arg && arg->type && arg->ident) { arg->type->toCBuffer(buf, arg->ident, &hgs); } @@ -1568,7 +1711,7 @@ void ParamSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) TypeFunction *tf = isTypeFunction(s); if (tf) { - size_t pcount = tf->parameters ? tf->parameters->dim : 0; + size_t pcount = (tf->parameters ? tf->parameters->dim : 0) + (int)(tf->varargs == 1); if (pcount != paramcount) { warning(s->loc, "Ddoc: parameter count mismatch"); @@ -1599,14 +1742,14 @@ void DocComment::parseMacros(Escape **pescapetable, Macro **pmacrotable, const u size_t len = mlen; const utf8_t *pend = p + len; - const utf8_t *tempstart; - size_t templen; + const utf8_t *tempstart = NULL; + size_t templen = 0; - const utf8_t *namestart; + const utf8_t *namestart = NULL; size_t namelen = 0; // !=0 if line continuation - const utf8_t *textstart; - size_t textlen; + const utf8_t *textstart = NULL; + size_t textlen = 0; while (p < pend) { @@ -2015,6 +2158,26 @@ TemplateParameter *isTemplateParameter(Dsymbol *s, const utf8_t *p, size_t len) return NULL; } +/** Return true if str is a reserved symbol name that starts with a double underscore. */ +bool isReservedName(utf8_t *str, size_t len) +{ + static const char *table[] = { + "__ctor", "__dtor", "__cpctor", "__postblit", "__invariant", "__unitTest", + "__require", "__ensure", "__dollar", "__ctfe", "__withSym", "__result", + "__returnLabel", "__vptr", "__monitor", "__gate", "__xopEquals", "__xopCmp", + "__LINE__", "__FILE__", "__MODULE__", "__FUNCTION__", "__PRETTY_FUNCTION__", + "__DATE__", "__TIME__", "__TIMESTAMP__", "__VENDOR__", "__VERSION__", + "__EOF__", "__LOCAL_SIZE", "___tls_get_addr", "__entrypoint", "__va_argsave_t", + "__va_argsave", NULL }; + + for (int i = 0; table[i]; i++) + { + if (cmp(table[i], str, len) == 0) + return true; + } + return false; +} + /************************************************** * Highlight text section. */ @@ -2030,7 +2193,7 @@ void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset) int leadingBlank = 1; int inCode = 0; //int inComment = 0; // in comment - size_t iCodeStart; // start of code section + size_t iCodeStart = 0; // start of code section size_t codeIndent = 0; size_t iLineStart = offset; @@ -2235,7 +2398,8 @@ void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset) i -= 2; // in next loop, c should be '\n' } else - { static const char pre[] = "$(D_CODE \n"; + { + static const char pre[] = "$(D_CODE "; inCode = 1; codeIndent = istart - iLineStart; // save indent count @@ -2261,7 +2425,9 @@ void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset) break; } - if (buf->data[i] == '_') // leading '_' means no highlight + // leading '_' means no highlight unless it's a reserved symbol name + if (buf->data[i] == '_' && + (i == buf->size-1 || !isReservedName((utf8_t *)(buf->data + i), j - i))) { buf->remove(i, 1); i = j - 1; @@ -2280,7 +2446,9 @@ void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset) } else { - if (f && isFunctionParameter(f, (utf8_t *)buf->data + i, j - i)) + utf8_t *start = (utf8_t *)buf->data + i; + size_t end = j - i; + if (f && (isFunctionParameter(f, start, end) || isCVariadicParameter(f, start, end))) { //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j); i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1; @@ -2309,7 +2477,7 @@ void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset, bool an { OutBuffer ancbuf; - emitAnchor(&ancbuf, s); + emitAnchor(&ancbuf, s, sc); buf->insert(offset, (char *)ancbuf.data, ancbuf.offset); offset += ancbuf.offset; } @@ -2341,7 +2509,9 @@ void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset, bool an } else if (f) { - if (isFunctionParameter(f, (utf8_t *)buf->data + i, j - i)) + utf8_t *start = (utf8_t *)buf->data + i; + size_t end = j - i; + if (isFunctionParameter(f, start, end) || isCVariadicParameter(f, start, end)) { //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j); i = buf->bracket(i, "$(DDOC_PARAM ", j, ")") - 1; @@ -2406,7 +2576,8 @@ void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset) } else if (f) { - if (isFunctionParameter(f, tok.ptr, lex.p - tok.ptr)) + size_t end = lex.p - tok.ptr; + if (isFunctionParameter(f, tok.ptr, end) || isCVariadicParameter(f, tok.ptr, end)) { //printf("highlighting arg '%s', i = %d, j = %d\n", arg->ident->toChars(), i, j); highlight = "$(D_PARAM "; @@ -2479,6 +2650,15 @@ const char *Escape::escapeChar(unsigned c) #endif } +/**************************************** + * Determine if p points to the start of a "..." parameter identifier. + */ + +bool isCVariadicArg(const utf8_t *p, size_t len) +{ + return len >= 3 && cmp("...", p, 3) == 0; +} + /**************************************** * Determine if p points to the start of an identifier. */ diff --git a/gcc/d/dfrontend/doc.h b/gcc/d/dfrontend/doc.h index 4f2dd6606..92f9a79e7 100644 --- a/gcc/d/dfrontend/doc.h +++ b/gcc/d/dfrontend/doc.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2006 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/doc.h + */ #ifndef DMD_DOC_H #define DMD_DOC_H @@ -16,8 +17,6 @@ #endif /* __DMC__ */ void escapeDdocString(OutBuffer *buf, size_t start); -void parentToBuffer(OutBuffer *buf, Dsymbol *s); -bool inSameModule(Dsymbol *s, Dsymbol *p); -void prettyPrintDsymbol(OutBuffer *buf, Dsymbol *s, Dsymbol *parent); +void gendocfile(Module *m); #endif diff --git a/gcc/d/dfrontend/dsymbol.c b/gcc/d/dfrontend/dsymbol.c index 45e7b0ece..9a1184ad9 100644 --- a/gcc/d/dfrontend/dsymbol.c +++ b/gcc/d/dfrontend/dsymbol.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/dsymbol.c + */ #include #include @@ -33,8 +34,6 @@ #include "attrib.h" #include "enum.h" -const char* Pprotectionnames[] = {NULL, "none", "private", "package", "protected", "public", "export"}; - /****************************** Dsymbol ******************************/ @@ -129,8 +128,8 @@ bool Dsymbol::oneMembers(Dsymbols *members, Dsymbol **ps, Identifier *ident) if (members) { for (size_t i = 0; i < members->dim; i++) - { Dsymbol *sx = (*members)[i]; - + { + Dsymbol *sx = (*members)[i]; bool x = sx->oneMember(ps, ident); //printf("\t[%d] kind %s = %d, s = %p\n", i, sx->kind(), x, *ps); if (!x) @@ -166,7 +165,8 @@ bool Dsymbol::oneMembers(Dsymbols *members, Dsymbol **ps, Identifier *ident) } } else // more than one symbol - { *ps = NULL; + { + *ps = NULL; //printf("\tfalse 2\n"); return false; } @@ -208,7 +208,12 @@ char *Dsymbol::toChars() return ident ? ident->toChars() : (char *)"__anonymous"; } -const char *Dsymbol::toPrettyChars() +char *Dsymbol::toPrettyCharsHelper() +{ + return toChars(); +} + +const char *Dsymbol::toPrettyChars(bool QualifyTypes) { Dsymbol *p; char *s; char *q; @@ -220,14 +225,14 @@ const char *Dsymbol::toPrettyChars() len = 0; for (p = this; p; p = p->parent) - len += strlen(p->toChars()) + 1; + len += strlen(QualifyTypes ? p->toPrettyCharsHelper() : p->toChars()) + 1; s = (char *)mem.malloc(len); q = s + len - 1; *q = 0; for (p = this; p; p = p->parent) { - char *t = p->toChars(); + char *t = QualifyTypes ? p->toPrettyCharsHelper() : p->toChars(); len = strlen(t); q -= len; memcpy(q, t, len); @@ -321,13 +326,23 @@ TemplateInstance *Dsymbol::isSpeculative() while (par) { TemplateInstance *ti = par->isTemplateInstance(); - if (ti && ti->speculative) + if (ti && ti->gagged) return ti; par = par->toParent(); } return NULL; } +Ungag Dsymbol::ungagSpeculative() +{ + unsigned oldgag = global.gag; + + if (global.gag && !isSpeculative() && !toParent2()->isFuncDeclaration()) + global.gag = 0; + + return Ungag(oldgag); +} + bool Dsymbol::isAnonymous() { return ident == NULL; @@ -382,15 +397,6 @@ void Dsymbol::semantic3(Scope *sc) // Most Dsymbols have no further semantic analysis needed } -/************************************* - * Look for function inlining possibilities. - */ - -void Dsymbol::inlineScan() -{ - // Most Dsymbols aren't functions -} - /********************************************* * Search for ident as member of s. * Input: @@ -424,6 +430,7 @@ void *symbol_search_fp(void *arg, const char *seed) assert(id); Dsymbol *s = (Dsymbol *)arg; + Module::clearCache(); return (void *)s->search(Loc(), id, IgnoreErrors | IgnoreAmbiguous); } @@ -448,6 +455,15 @@ Dsymbol *Dsymbol::searchX(Loc loc, Scope *sc, RootObject *id) Dsymbol *s = toAlias(); Dsymbol *sm; + if (Declaration *d = s->isDeclaration()) + { + if (d->inuse) + { + ::error(loc, "circular reference to '%s'", d->toPrettyChars()); + return NULL; + } + } + switch (id->dyncast()) { case DYNCAST_IDENTIFIER: @@ -455,7 +471,8 @@ Dsymbol *Dsymbol::searchX(Loc loc, Scope *sc, RootObject *id) break; case DYNCAST_DSYMBOL: - { // It's a template instance + { + // It's a template instance //printf("\ttemplate instance id\n"); Dsymbol *st = (Dsymbol *)id; TemplateInstance *ti = st->isTemplateInstance(); @@ -540,11 +557,6 @@ ClassDeclaration *Dsymbol::isClassMember() // are we a member of a class? return ad ? ad->isClassDeclaration() : NULL; } -void Dsymbol::defineRef(Dsymbol *s) -{ - assert(0); -} - bool Dsymbol::isExport() { return false; @@ -598,25 +610,23 @@ int Dsymbol::apply(Dsymbol_apply_ft_t fp, void *param) return (*fp)(this, param); } -int Dsymbol::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) +int Dsymbol::addMember(Scope *sc, ScopeDsymbol *sds, int memnum) { //printf("Dsymbol::addMember('%s')\n", toChars()); - //printf("Dsymbol::addMember(this = %p, '%s' scopesym = '%s')\n", this, toChars(), sd->toChars()); - //printf("Dsymbol::addMember(this = %p, '%s' sd = %p, sd->symtab = %p)\n", this, toChars(), sd, sd->symtab); - parent = sd; + //printf("Dsymbol::addMember(this = %p, '%s' scopesym = '%s')\n", this, toChars(), sds->toChars()); + //printf("Dsymbol::addMember(this = %p, '%s' sds = %p, sds->symtab = %p)\n", this, toChars(), sds, sds->symtab); + parent = sds; if (!isAnonymous()) // no name, so can't add it to symbol table { - if (!sd->symtabInsert(this)) // if name is already defined + if (!sds->symtabInsert(this)) // if name is already defined { - Dsymbol *s2; - - s2 = sd->symtab->lookup(ident); + Dsymbol *s2 = sds->symtab->lookup(ident); if (!s2->overloadInsert(this)) { - sd->multiplyDefined(Loc(), this, s2); + sds->multiplyDefined(Loc(), this, s2); } } - if (sd->isAggregateDeclaration() || sd->isEnumDeclaration()) + if (sds->isAggregateDeclaration() || sds->isEnumDeclaration()) { if (ident == Id::__sizeof || ident == Id::__xalignof || ident == Id::mangleof) error(".%s property cannot be redefined", ident->toChars()); @@ -713,9 +723,8 @@ void Dsymbol::checkDeprecated(Loc loc, Scope *sc) Module *Dsymbol::getModule() { //printf("Dsymbol::getModule()\n"); - TemplateDeclaration *td = getFuncTemplateDecl(this); - if (td) - return td->getModule(); + if (TemplateInstance *ti = isInstantiated()) + return ti->tempdecl->getModule(); Dsymbol *s = this; while (s) @@ -736,9 +745,8 @@ Module *Dsymbol::getModule() Module *Dsymbol::getAccessModule() { //printf("Dsymbol::getAccessModule()\n"); - TemplateDeclaration *td = getFuncTemplateDecl(this); - if (td) - return td->getAccessModule(); + if (TemplateInstance *ti = isInstantiated()) + return ti->tempdecl->getAccessModule(); Dsymbol *s = this; while (s) @@ -749,10 +757,12 @@ Module *Dsymbol::getAccessModule() return m; TemplateInstance *ti = s->isTemplateInstance(); if (ti && ti->enclosing) + { /* Because of local template instantiation, the parent isn't where the access * rights come from - it's the template declaration */ s = ti->tempdecl; + } else s = s->parent; } @@ -878,13 +888,13 @@ Dsymbol *ScopeDsymbol::syntaxCopy(Dsymbol *s) { //printf("ScopeDsymbol::syntaxCopy('%s')\n", toChars()); - ScopeDsymbol *sd; + ScopeDsymbol *sds; if (s) - sd = (ScopeDsymbol *)s; + sds = (ScopeDsymbol *)s; else - sd = new ScopeDsymbol(ident); - sd->members = arraySyntaxCopy(members); - return sd; + sds = new ScopeDsymbol(ident); + sds->members = arraySyntaxCopy(members); + return sds; } /***************************************** @@ -905,9 +915,8 @@ Dsymbol *ScopeDsymbol::search(Loc loc, Identifier *ident, int flags) //printf("\ts = '%s.%s'\n",toChars(),s1->toChars()); return s1; } - else if (!imports) - return NULL; - else + + if (imports) { Dsymbol *s = NULL; OverloadSet *a = NULL; @@ -926,7 +935,11 @@ Dsymbol *ScopeDsymbol::search(Loc loc, Identifier *ident, int flags) */ Dsymbol *s2 = ss->search(loc, ident, ss->isModule() ? IgnorePrivateMembers : IgnoreNone); if (!s) + { s = s2; + if (s && s->isOverloadSet()) + a = mergeOverloadSet(a, s); + } else if (s2 && s != s2) { if (s->toAlias() == s2->toAlias() || @@ -967,28 +980,10 @@ Dsymbol *ScopeDsymbol::search(Loc loc, Identifier *ident, int flags) /* If both s2 and s are overloadable (though we only * need to check s once) */ - if (s2->isOverloadable() && (a || s->isOverloadable())) + if ((s2->isOverloadSet() || s2->isOverloadable()) && + (a || s->isOverloadable())) { - if (!a) - { - a = new OverloadSet(s->ident); - a->parent = this; - } - /* Don't add to a[] if s2 is alias of previous sym - */ - for (size_t j = 0; j < a->a.dim; j++) - { - Dsymbol *s3 = a->a[j]; - if (s2->toAlias() == s3->toAlias()) - { - if (s3->isDeprecated() || - s2->prot() > s3->prot() && s2->prot() != PROTnone) - a->a[j] = s2; - goto Lcontinue; - } - } - a->push(s2); - Lcontinue: + a = mergeOverloadSet(a, s2); continue; } if (flags & IgnoreAmbiguous) // if return NULL on ambiguity @@ -1001,24 +996,76 @@ Dsymbol *ScopeDsymbol::search(Loc loc, Identifier *ident, int flags) } } - /* Build special symbol if we had multiple finds - */ - if (a) - { assert(s); - a->push(s); - s = a; - } - if (s) { + /* Build special symbol if we had multiple finds + */ + if (a) + { + if (!s->isOverloadSet()) + a = mergeOverloadSet(a, s); + s = a; + } + if (!(flags & IgnoreErrors) && s->prot() == PROTprivate && !s->parent->isTemplateMixin()) { if (!s->isImport()) error(loc, "%s %s is private", s->kind(), s->toPrettyChars()); } + return s; + } + } + + return s1; +} + +OverloadSet *ScopeDsymbol::mergeOverloadSet(OverloadSet *os, Dsymbol *s) +{ + if (!os) + { + os = new OverloadSet(s->ident); + os->parent = this; + } + if (OverloadSet *os2 = s->isOverloadSet()) + { + // Merge the cross-module overload set 'os2' into 'os' + if (os->a.dim == 0) + { + os->a.setDim(os2->a.dim); + memcpy(os->a.tdata(), os2->a.tdata(), sizeof(os->a[0]) * os2->a.dim); + } + else + { + for (size_t i = 0; i < os2->a.dim; i++) + { + os = mergeOverloadSet(os, os2->a[i]); + } } - return s; } + else + { + assert(s->isOverloadable()); + + /* Don't add to os[] if s is alias of previous sym + */ + for (size_t j = 0; j < os->a.dim; j++) + { + Dsymbol *s2 = os->a[j]; + if (s->toAlias() == s2->toAlias()) + { + if (s2->isDeprecated() || + s->prot() > s2->prot() && s->prot() != PROTnone) + { + os->a[j] = s; + } + goto Lcontinue; + } + } + os->push(s); + Lcontinue: + ; + } + return os; } void ScopeDsymbol::importScope(Dsymbol *s, PROT protection) @@ -1054,15 +1101,6 @@ bool ScopeDsymbol::isforwardRef() return (members == NULL); } -void ScopeDsymbol::defineRef(Dsymbol *s) -{ - ScopeDsymbol *ss; - - ss = s->isScopeDsymbol(); - members = ss->members; - ss->members = NULL; -} - void ScopeDsymbol::multiplyDefined(Loc loc, Dsymbol *s1, Dsymbol *s2) { #if 0 @@ -1086,28 +1124,6 @@ void ScopeDsymbol::multiplyDefined(Loc loc, Dsymbol *s1, Dsymbol *s2) } } -Dsymbol *ScopeDsymbol::nameCollision(Dsymbol *s) -{ - Dsymbol *sprev; - - // Look to see if we are defining a forward referenced symbol - - sprev = symtab->lookup(s->ident); - assert(sprev); - if (s->equals(sprev)) // if the same symbol - { - if (s->isforwardRef()) // if second declaration is a forward reference - return sprev; - if (sprev->isforwardRef()) - { - sprev->defineRef(s); // copy data from s into sprev - return sprev; - } - } - multiplyDefined(Loc(), s, sprev); - return sprev; -} - const char *ScopeDsymbol::kind() { return "ScopeDsymbol"; @@ -1286,10 +1302,7 @@ Dsymbol *WithScopeSymbol::search(Loc loc, Identifier *ident, int flags) else { Type *t = e->type->toBasetype(); - if (t->ty == Taarray) - s = ((TypeAArray *)t)->getImpl(); - else - s = t->toDsymbol(NULL); + s = t->toDsymbol(NULL); } if (s) { @@ -1336,13 +1349,14 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) { //printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident->toChars(), flags); if (ident == Id::dollar) - { VarDeclaration **pvar; + { + VarDeclaration **pvar; Expression *ce; L1: - if (td) - { /* $ gives the number of elements in the tuple + { + /* $ gives the number of elements in the tuple */ VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL); Expression *e = new IntegerExp(Loc(), td->objects->dim, Type::tsize_t); @@ -1353,7 +1367,8 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) } if (type) - { /* $ gives the number of type entries in the type tuple + { + /* $ gives the number of type entries in the type tuple */ VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL); Expression *e = new IntegerExp(Loc(), type->arguments->dim, Type::tsize_t); @@ -1364,34 +1379,36 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) } if (exp->op == TOKindex) - { /* array[index] where index is some function of $ + { + /* array[index] where index is some function of $ */ IndexExp *ie = (IndexExp *)exp; - pvar = &ie->lengthVar; ce = ie->e1; } else if (exp->op == TOKslice) - { /* array[lwr .. upr] where lwr or upr is some function of $ + { + /* array[lwr .. upr] where lwr or upr is some function of $ */ SliceExp *se = (SliceExp *)exp; - pvar = &se->lengthVar; ce = se->e1; } else if (exp->op == TOKarray) - { /* array[e0, e1, e2, e3] where e0, e1, e2 are some function of $ + { + /* array[e0, e1, e2, e3] where e0, e1, e2 are some function of $ * $ is a opDollar!(dim)() where dim is the dimension(0,1,2,...) */ ArrayExp *ae = (ArrayExp *)exp; - pvar = &ae->lengthVar; ce = ae->e1; } else + { /* Didn't find $, look in enclosing scope(s). */ return NULL; + } while (ce->op == TOKcomma) ce = ((CommaExp *)ce)->e2; @@ -1404,7 +1421,8 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) { Type *t = ((TypeExp *)ce)->type; if (t->ty == Ttuple) - { type = (TypeTuple *)t; + { + type = (TypeTuple *)t; goto L1; } } @@ -1413,12 +1431,14 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) * multiple times, it gets set only once. */ if (!*pvar) // if not already initialized - { /* Create variable v and set it to the value of $ + { + /* Create variable v and set it to the value of $ */ VarDeclaration *v; Type *t; if (ce->op == TOKtuple) - { /* It is for an expression tuple, so the + { + /* It is for an expression tuple, so the * length will be a const. */ Expression *e = new IntegerExp(Loc(), ((TupleExp *)ce)->exps->dim, Type::tsize_t); @@ -1427,18 +1447,10 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) } else if (ce->type && (t = ce->type->toBasetype()) != NULL && (t->ty == Tstruct || t->ty == Tclass)) - { // Look for opDollar + { + // Look for opDollar assert(exp->op == TOKarray || exp->op == TOKslice); - AggregateDeclaration *ad = NULL; - - if (t->ty == Tclass) - { - ad = ((TypeClass *)t)->sym; - } - else if (t->ty == Tstruct) - { - ad = ((TypeStruct *)t)->sym; - } + AggregateDeclaration *ad = isAggregate(t); assert(ad); Dsymbol *s = ad->search(loc, Id::opDollar); @@ -1450,7 +1462,7 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) // Check for multi-dimensional opDollar(dim) template. if (TemplateDeclaration *td = s->isTemplateDeclaration()) { - dinteger_t dim; + dinteger_t dim = 0; if (exp->op == TOKarray) { dim = ((ArrayExp *)exp)->currentDimension; @@ -1459,6 +1471,10 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) { dim = 0; // slices are currently always one-dimensional } + else + { + assert(0); + } Objects *tiargs = new Objects(); Expression *edim = new IntegerExp(Loc(), dim, Type::tsize_t); @@ -1467,7 +1483,8 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) e = new DotTemplateInstanceExp(loc, ce, td->ident, tiargs); } else - { /* opDollar exists, but it's not a template. + { + /* opDollar exists, but it's not a template. * This is acceptable ONLY for single-dimension indexing. * Note that it's impossible to have both template & function opDollar, * because both take no arguments. @@ -1488,10 +1505,11 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) if (t && t->ty == Tfunction) e = new CallExp(e->loc, e); v = new VarDeclaration(loc, NULL, Id::dollar, new ExpInitializer(Loc(), e)); - v->storage_class |= STCtemp; + v->storage_class |= STCtemp | STCctfe; } else - { /* For arrays, $ will either be a compile-time constant + { + /* For arrays, $ will either be a compile-time constant * (in which case its value in set during constant-folding), * or a variable (in which case an expression is created in * toir.c). @@ -1520,14 +1538,14 @@ DsymbolTable::DsymbolTable() Dsymbol *DsymbolTable::lookup(Identifier *ident) { //printf("DsymbolTable::lookup(%s)\n", (char*)ident->string); - return (Dsymbol *)_aaGetRvalue(tab, ident); + return (Dsymbol *)dmd_aaGetRvalue(tab, (void *)ident); } Dsymbol *DsymbolTable::insert(Dsymbol *s) { //printf("DsymbolTable::insert(this = %p, '%s')\n", this, s->ident->toChars()); Identifier *ident = s->ident; - Dsymbol **ps = (Dsymbol **)_aaGet(&tab, ident); + Dsymbol **ps = (Dsymbol **)dmd_aaGet(&tab, (void *)ident); if (*ps) return NULL; // already in table *ps = s; @@ -1537,7 +1555,7 @@ Dsymbol *DsymbolTable::insert(Dsymbol *s) Dsymbol *DsymbolTable::insert(Identifier *ident, Dsymbol *s) { //printf("DsymbolTable::insert()\n"); - Dsymbol **ps = (Dsymbol **)_aaGet(&tab, ident); + Dsymbol **ps = (Dsymbol **)dmd_aaGet(&tab, (void *)ident); if (*ps) return NULL; // already in table *ps = s; @@ -1547,7 +1565,7 @@ Dsymbol *DsymbolTable::insert(Identifier *ident, Dsymbol *s) Dsymbol *DsymbolTable::update(Dsymbol *s) { Identifier *ident = s->ident; - Dsymbol **ps = (Dsymbol **)_aaGet(&tab, ident); + Dsymbol **ps = (Dsymbol **)dmd_aaGet(&tab, (void *)ident); *ps = s; return s; } diff --git a/gcc/d/dfrontend/dsymbol.h b/gcc/d/dfrontend/dsymbol.h index 804d52e64..b9176efcf 100644 --- a/gcc/d/dfrontend/dsymbol.h +++ b/gcc/d/dfrontend/dsymbol.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/dsymbol.h + */ #ifndef DMD_DSYMBOL_H #define DMD_DSYMBOL_H @@ -29,7 +30,6 @@ class Declaration; class ThisDeclaration; class TypeInfoDeclaration; class TupleDeclaration; -class TypedefDeclaration; class AliasDeclaration; class AggregateDeclaration; class EnumDeclaration; @@ -39,6 +39,7 @@ class StructDeclaration; class UnionDeclaration; class FuncDeclaration; class FuncAliasDeclaration; +class OverDeclaration; class FuncLiteralDeclaration; class CtorDeclaration; class PostBlitDeclaration; @@ -64,6 +65,7 @@ class ScopeDsymbol; class TemplateDeclaration; class TemplateInstance; class TemplateMixin; +class Nspace; class EnumMember; class WithScopeSymbol; class ArrayScopeSymbol; @@ -79,8 +81,16 @@ typedef union tree_node TYPE; struct TYPE; #endif -// Back end -struct Classsym; +struct Ungag +{ + unsigned oldgag; + + Ungag(unsigned old) : oldgag(old) {} + ~Ungag() { global.gag = oldgag; } +}; + +const char *mangle(Dsymbol *s); +const char *mangleExact(FuncDeclaration *fd); enum PROT { @@ -93,8 +103,9 @@ enum PROT PROTexport, }; -// this is used for printing the protection in json, traits, docs, etc. -extern const char* Pprotectionnames[]; +// in hdrgen.c +void protectionToBuffer(OutBuffer *buf, PROT prot); +const char *protectionToChars(PROT prot); /* State of symbol in winding its way through the passes of the compiler */ @@ -144,6 +155,7 @@ class Dsymbol : public RootObject Dsymbol(Identifier *); static Dsymbol *create(Identifier *); char *toChars(); + virtual char *toPrettyCharsHelper(); // helper to print fully qualified (template) arguments Loc& getLoc(); char *locToChars(); bool equals(RootObject *o); @@ -162,32 +174,29 @@ class Dsymbol : public RootObject TemplateInstance *isSpeculative(); Ungag ungagSpeculative(); - int dyncast() { return DYNCAST_DSYMBOL; } // kludge for template.isSymbol() + // kludge for template.isSymbol() + int dyncast() { return DYNCAST_DSYMBOL; } static Dsymbols *arraySyntaxCopy(Dsymbols *a); virtual Identifier *getIdent(); - virtual const char *toPrettyChars(); + virtual const char *toPrettyChars(bool QualifyTypes = false); virtual const char *kind(); virtual Dsymbol *toAlias(); // resolve real symbol virtual int apply(Dsymbol_apply_ft_t fp, void *param); - virtual int addMember(Scope *sc, ScopeDsymbol *s, int memnum); + virtual int addMember(Scope *sc, ScopeDsymbol *sds, int memnum); virtual void setScope(Scope *sc); virtual void importAll(Scope *sc); virtual void semantic(Scope *sc); virtual void semantic2(Scope *sc); virtual void semantic3(Scope *sc); - virtual void inlineScan(); virtual Dsymbol *search(Loc loc, Identifier *ident, int flags = IgnoreNone); Dsymbol *search_correct(Identifier *id); Dsymbol *searchX(Loc loc, Scope *sc, RootObject *id); virtual bool overloadInsert(Dsymbol *s); - virtual void toHBuffer(OutBuffer *buf, HdrGenState *hgs); virtual void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - virtual void toDocBuffer(OutBuffer *buf, Scope *sc); virtual unsigned size(Loc loc); virtual bool isforwardRef(); - virtual void defineRef(Dsymbol *s); virtual AggregateDeclaration *isThis(); // is a 'this' required to access the member AggregateDeclaration *isAggregateMember(); // are we a member of an aggregate? AggregateDeclaration *isAggregateMember2(); // are we a member of an aggregate? @@ -200,7 +209,6 @@ class Dsymbol : public RootObject virtual LabelDsymbol *isLabel(); // is this a LabelDsymbol? virtual AggregateDeclaration *isMember(); // is this symbol a member of an AggregateDeclaration? virtual Type *getType(); // is this a type? - virtual const char *mangle(bool isv = false); virtual bool needThis(); // need a 'this' pointer? virtual PROT prot(); virtual Dsymbol *syntaxCopy(Dsymbol *s); // copy only syntax trees @@ -213,16 +221,12 @@ class Dsymbol : public RootObject virtual void checkCtorConstInit() { } virtual void addComment(const utf8_t *comment); - virtual void emitComment(Scope *sc); - void emitDitto(Scope *sc); bool inNonRoot(); // Backend - virtual Symbol *toSymbol(); // to backend symbol - virtual void toObjFile(int multiobj); // compile to .obj file - virtual int cvMember(unsigned char *p); // emit cv debug info for member + virtual void toObjFile(bool multiobj); // compile to .obj file Symbol *toImport(); // to backend import symbol static Symbol *toImport(Symbol *s); // to backend import symbol @@ -236,15 +240,16 @@ class Dsymbol : public RootObject virtual TemplateDeclaration *isTemplateDeclaration() { return NULL; } virtual TemplateInstance *isTemplateInstance() { return NULL; } virtual TemplateMixin *isTemplateMixin() { return NULL; } + virtual Nspace *isNspace() { return NULL; } virtual Declaration *isDeclaration() { return NULL; } virtual ThisDeclaration *isThisDeclaration() { return NULL; } virtual TypeInfoDeclaration *isTypeInfoDeclaration() { return NULL; } virtual TupleDeclaration *isTupleDeclaration() { return NULL; } - virtual TypedefDeclaration *isTypedefDeclaration() { return NULL; } virtual AliasDeclaration *isAliasDeclaration() { return NULL; } virtual AggregateDeclaration *isAggregateDeclaration() { return NULL; } virtual FuncDeclaration *isFuncDeclaration() { return NULL; } virtual FuncAliasDeclaration *isFuncAliasDeclaration() { return NULL; } + virtual OverDeclaration *isOverDeclaration() { return NULL; } virtual FuncLiteralDeclaration *isFuncLiteralDeclaration() { return NULL; } virtual CtorDeclaration *isCtorDeclaration() { return NULL; } virtual PostBlitDeclaration *isPostBlitDeclaration() { return NULL; } @@ -281,25 +286,24 @@ class ScopeDsymbol : public Dsymbol Dsymbols *members; // all Dsymbol's in this scope DsymbolTable *symtab; // members[] sorted into table +private: Dsymbols *imports; // imported Dsymbol's PROT *prots; // array of PROT, one for each import +public: ScopeDsymbol(); ScopeDsymbol(Identifier *id); Dsymbol *syntaxCopy(Dsymbol *s); Dsymbol *search(Loc loc, Identifier *ident, int flags = IgnoreNone); + OverloadSet *mergeOverloadSet(OverloadSet *os, Dsymbol *s); void importScope(Dsymbol *s, PROT protection); bool isforwardRef(); - void defineRef(Dsymbol *s); static void multiplyDefined(Loc loc, Dsymbol *s1, Dsymbol *s2); - Dsymbol *nameCollision(Dsymbol *s); const char *kind(); FuncDeclaration *findGetMembers(); virtual Dsymbol *symtabInsert(Dsymbol *s); bool hasStaticCtorOrDtor(); - void emitMemberComments(Scope *sc); - static size_t dim(Dsymbols *members); static Dsymbol *getNth(Dsymbols *members, size_t nth, size_t *pn = NULL); diff --git a/gcc/d/dfrontend/dump.c b/gcc/d/dfrontend/dump.c deleted file mode 100644 index e0d431863..000000000 --- a/gcc/d/dfrontend/dump.c +++ /dev/null @@ -1,152 +0,0 @@ - -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. - -#include -#include -#include - -#include "mars.h" -#include "mtype.h" -#include "declaration.h" -#include "expression.h" -#include "template.h" - -static void indent(int indent) -{ - int i; - - for (i = 0; i < indent; i++) - printf(" "); -} - -static char *type_print(Type *type) -{ - return type ? type->toChars() : (char *) "null"; -} - -void dumpExpressions(int i, Expressions *exps) -{ - if (exps) - { - for (size_t j = 0; j < exps->dim; j++) - { Expression *e = (*exps)[j]; - indent(i); - printf("(\n"); - e->dump(i + 2); - indent(i); - printf(")\n"); - } - } -} - -void Expression::dump(int i) -{ - indent(i); - printf("%p %s type=%s\n", this, Token::toChars(op), type_print(type)); -} - -void IntegerExp::dump(int i) -{ - indent(i); - printf("%p %lld type=%s\n", this, (ulonglong)value, type_print(type)); -} - -void IdentifierExp::dump(int i) -{ - indent(i); - printf("%p ident '%s' type=%s\n", this, ident->toChars(), type_print(type)); -} - -void DsymbolExp::dump(int i) -{ - indent(i); - printf("%p %s type=%s\n", this, s->toChars(), type_print(type)); -} - -void VarExp::dump(int i) -{ - indent(i); - printf("%p %s var=%s type=%s\n", this, Token::toChars(op), var->toChars(), type_print(type)); -} - -void UnaExp::dump(int i) -{ - indent(i); - printf("%p %s type=%s e1=%p\n", this, Token::toChars(op), type_print(type), e1); - if (e1) - e1->dump(i + 2); -} - -void CallExp::dump(int i) -{ - UnaExp::dump(i); - dumpExpressions(i, arguments); -} - -void SliceExp::dump(int i) -{ - indent(i); - printf("%p %s type=%s e1=%p\n", this, Token::toChars(op), type_print(type), e1); - if (e1) - e1->dump(i + 2); - if (lwr) - lwr->dump(i + 2); - if (upr) - upr->dump(i + 2); -} - -void DotIdExp::dump(int i) -{ - indent(i); - printf("%p %s type=%s ident=%s e1=%p\n", this, Token::toChars(op), type_print(type), ident->toChars(), e1); - if (e1) - e1->dump(i + 2); -} - -void DotVarExp::dump(int i) -{ - indent(i); - printf("%p %s type=%s var='%s' e1=%p\n", this, Token::toChars(op), type_print(type), var->toChars(), e1); - if (e1) - e1->dump(i + 2); -} - -void DotTemplateInstanceExp::dump(int i) -{ - indent(i); - printf("%p %s type=%s ti='%s' e1=%p\n", this, Token::toChars(op), type_print(type), ti->toChars(), e1); - if (e1) - e1->dump(i + 2); -} - -void DelegateExp::dump(int i) -{ - indent(i); - printf("%p %s func=%s type=%s e1=%p\n", this, Token::toChars(op), func->toChars(), type_print(type), e1); - if (e1) - e1->dump(i + 2); -} - -void BinExp::dump(int i) -{ - indent(i); - const char *sop = Token::toChars(op); - if (op == TOKblit) - sop = "blit"; - else if (op == TOKconstruct) - sop = "construct"; - printf("%p %s type=%s e1=%p e2=%p\n", this, sop, type_print(type), e1, e2); - if (e1) - e1->dump(i + 2); - if (e2) - e2->dump(i + 2); -} - - diff --git a/gcc/d/dfrontend/entity.c b/gcc/d/dfrontend/entity.c index f937db95d..b3a0eca12 100644 --- a/gcc/d/dfrontend/entity.c +++ b/gcc/d/dfrontend/entity.c @@ -1,12 +1,13 @@ -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. - +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/entity.c + */ #include #include diff --git a/gcc/d/dfrontend/enum.c b/gcc/d/dfrontend/enum.c index 6a7584211..1a24cc6b2 100644 --- a/gcc/d/dfrontend/enum.c +++ b/gcc/d/dfrontend/enum.c @@ -1,11 +1,13 @@ -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/enum.c + */ #include #include @@ -72,7 +74,7 @@ void EnumDeclaration::setScope(Scope *sc) ScopeDsymbol::setScope(sc); } -int EnumDeclaration::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) +int EnumDeclaration::addMember(Scope *sc, ScopeDsymbol *sds, int memnum) { #if 0 printf("EnumDeclaration::addMember() %s\n", toChars()); @@ -85,11 +87,11 @@ int EnumDeclaration::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) /* Anonymous enum members get added to enclosing scope. */ - ScopeDsymbol *scopesym = isAnonymous() ? sd : this; + ScopeDsymbol *scopesym = isAnonymous() ? sds : this; if (!isAnonymous()) { - ScopeDsymbol::addMember(sc, sd, memnum); + ScopeDsymbol::addMember(sc, sds, memnum); if (!symtab) symtab = new DsymbolTable(); @@ -171,7 +173,7 @@ void EnumDeclaration::semantic(Scope *sc) if (!sym->memtype || !sym->members || !sym->symtab || sym->scope) { // memtype is forward referenced, so try again later - scope = scx ? scx : new Scope(*sc); + scope = scx ? scx : sc->copy(); scope->setNoFree(); scope->module->addDeferredSemantic(this); Module::dprogress = dprogress_save; @@ -304,7 +306,7 @@ Expression *EnumDeclaration::getMaxMinValue(Loc loc, Identifier *id) goto Lerrors; } if (*pval) - return *pval; + goto Ldone; if (scope) semantic(scope); @@ -358,7 +360,16 @@ Expression *EnumDeclaration::getMaxMinValue(Loc loc, Identifier *id) *pval = e; } } - return *pval; +Ldone: + { + Expression *e = *pval; + if (e->op != TOKerror) + { + e = e->copy(); + e->loc = loc; + } + return e; + } Lerrors: *pval = new ErrorExp(); @@ -513,6 +524,7 @@ EnumMember::EnumMember(Loc loc, Identifier *id, Expression *value, Type *type) { this->ed = NULL; this->value = value; + this->origValue = value; this->type = type; this->loc = loc; this->vd = NULL; @@ -534,9 +546,13 @@ Dsymbol *EnumMember::syntaxCopy(Dsymbol *s) em->loc = loc; em->value = e; em->type = t; + em->origValue = origValue ? origValue->syntaxCopy() : NULL; } else + { em = new EnumMember(loc, ident, e, t); + em->origValue = origValue ? origValue->syntaxCopy() : NULL; + } return em; } @@ -599,6 +615,8 @@ void EnumMember::semantic(Scope *sc) e = e->semantic(sc); e = resolveProperties(sc, e); e = e->ctfeInterpret(); + if (e->op == TOKerror) + goto Lerrors; if (first && !ed->memtype && !ed->isAnonymous()) { ed->memtype = e->type; @@ -613,6 +631,10 @@ void EnumMember::semantic(Scope *sc) { e = e->implicitCastTo(sc, ed->memtype); e = e->ctfeInterpret(); + + // save origValue for better json output + origValue = e; + if (!ed->isAnonymous()) e = e->castTo(sc, ed->type); } @@ -621,6 +643,9 @@ void EnumMember::semantic(Scope *sc) e = e->implicitCastTo(sc, type); e = e->ctfeInterpret(); assert(ed->isAnonymous()); + + // save origValue for better json output + origValue = e; } value = e; } @@ -638,6 +663,10 @@ void EnumMember::semantic(Scope *sc) Expression *e = new IntegerExp(loc, 0, Type::tint32); e = e->implicitCastTo(sc, t); e = e->ctfeInterpret(); + + // save origValue for better json output + origValue = e; + if (!ed->isAnonymous()) e = e->castTo(sc, ed->type); value = e; @@ -667,7 +696,7 @@ void EnumMember::semantic(Scope *sc) Expression *eprev = emprev->value; Type *tprev = eprev->type->equals(ed->type) ? ed->memtype : eprev->type; - Expression *emax = tprev->getProperty(Loc(), Id::max, 0); + Expression *emax = tprev->getProperty(ed->loc, Id::max, 0); emax = emax->semantic(sc); emax = emax->ctfeInterpret(); @@ -689,6 +718,17 @@ void EnumMember::semantic(Scope *sc) e = e->castTo(sc, eprev->type); e = e->ctfeInterpret(); + // save origValue (without cast) for better json output + if (e->op != TOKerror) // avoid duplicate diagnostics + { + assert(emprev->origValue); + origValue = new AddExp(loc, emprev->origValue, new IntegerExp(loc, 1, Type::tint32)); + origValue = origValue->semantic(sc); + origValue = origValue->ctfeInterpret(); + } + + if (e->op == TOKerror) + goto Lerrors; if (e->type->isfloating()) { // Check that e != eprev (not always true for floats) @@ -704,6 +744,7 @@ void EnumMember::semantic(Scope *sc) value = e; } + assert(origValue); semanticRun = PASSsemanticdone; } diff --git a/gcc/d/dfrontend/enum.h b/gcc/d/dfrontend/enum.h index 16988a9fd..5a97e48fd 100644 --- a/gcc/d/dfrontend/enum.h +++ b/gcc/d/dfrontend/enum.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/enum.h + */ #ifndef DMD_ENUM_H #define DMD_ENUM_H @@ -51,7 +52,7 @@ class EnumDeclaration : public ScopeDsymbol EnumDeclaration(Loc loc, Identifier *id, Type *memtype); Dsymbol *syntaxCopy(Dsymbol *s); - int addMember(Scope *sc, ScopeDsymbol *sd, int memnum); + int addMember(Scope *sc, ScopeDsymbol *sds, int memnum); void setScope(Scope *sc); void semantic(Scope *sc); bool oneMember(Dsymbol **ps, Identifier *ident); @@ -65,14 +66,10 @@ class EnumDeclaration : public ScopeDsymbol Expression *getDefaultValue(Loc loc); Type *getMemtype(Loc loc); - void emitComment(Scope *sc); - void toDocBuffer(OutBuffer *buf, Scope *sc); - EnumDeclaration *isEnumDeclaration() { return this; } - void toObjFile(int multiobj); // compile to .obj file + void toObjFile(bool multiobj); // compile to .obj file void toDebug(); - int cvMember(unsigned char *p); Symbol *sinit; Symbol *toInitializer(); @@ -89,6 +86,9 @@ class EnumMember : public Dsymbol * 3. type id = value */ Expression *value; + Expression *origValue; // A cast() is injected to 'value' after semantic(), + // but 'origValue' will preserve the original value, + // or previous value + 1 if none was specified. Type *type; EnumDeclaration *ed; @@ -101,9 +101,6 @@ class EnumMember : public Dsymbol void semantic(Scope *sc); Expression *getVarExp(Loc loc, Scope *sc); - void emitComment(Scope *sc); - void toDocBuffer(OutBuffer *buf, Scope *sc); - EnumMember *isEnumMember() { return this; } void accept(Visitor *v) { v->visit(this); } }; diff --git a/gcc/d/dfrontend/expression.c b/gcc/d/dfrontend/expression.c index 9692681c5..4a02e4618 100644 --- a/gcc/d/dfrontend/expression.c +++ b/gcc/d/dfrontend/expression.c @@ -1,11 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. + +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/expression.c + */ #include #include @@ -37,11 +39,17 @@ #include "parse.h" #include "doc.h" #include "aav.h" +#include "nspace.h" bool isArrayOpValid(Expression *e); +bool isNonAssignmentArrayOp(Expression *e); Expression *createTypeInfoArray(Scope *sc, Expression *args[], size_t dim); Expression *expandVar(int result, VarDeclaration *v); -void functionToCBuffer2(TypeFunction *t, OutBuffer *buf, HdrGenState *hgs, int mod, const char *kind); +void functionToBufferWithIdent(TypeFunction *t, OutBuffer *buf, const char *ident); +TypeTuple *toArgTypes(Type *t); +void toBufferShort(Type *t, OutBuffer *buf, HdrGenState *hgs); +void accessCheck(AggregateDeclaration *ad, Loc loc, Scope *sc, Dsymbol *smember); +bool checkFrameAccess(Loc loc, Scope *sc, AggregateDeclaration *ad, size_t istart = 0); #define LOGSEMANTIC 0 @@ -226,7 +234,7 @@ bool isNeedThisScope(Scope *sc, Declaration *d) } if (FuncDeclaration *f = s->isFuncDeclaration()) { - if (f->isFuncLiteralDeclaration()) + if (f->isFuncLiteralDeclaration() && f->isNested()) continue; if (f->isMember2()) break; @@ -299,6 +307,8 @@ Expression *resolvePropertiesX(Scope *sc, Expression *e1, Expression *e2 = NULL) FuncDeclaration *f = resolveFuncCall(loc, sc, os->a[i], tiargs, tthis, &a, 1); if (f) { + if (f->errors) + return new ErrorExp(); fd = f; assert(fd->type->ty == Tfunction); TypeFunction *tf = (TypeFunction *)fd->type; @@ -318,6 +328,8 @@ Expression *resolvePropertiesX(Scope *sc, Expression *e1, Expression *e2 = NULL) FuncDeclaration *f = resolveFuncCall(loc, sc, os->a[i], tiargs, tthis, NULL, 1); if (f) { + if (f->errors) + return new ErrorExp(); fd = f; assert(fd->type->ty == Tfunction); TypeFunction *tf = (TypeFunction *)fd->type; @@ -374,11 +386,7 @@ Expression *resolvePropertiesX(Scope *sc, Expression *e1, Expression *e2 = NULL) { //assert(ti->needsTypeInference(sc)); if (!ti->semanticTiargs(sc)) - { - ti->inst = ti; - ti->inst->errors = true; goto Leprop; - } tiargs = ti->tiargs; tthis = NULL; if ((os = ti->tempdecl->isOverloadSet()) != NULL) @@ -422,6 +430,8 @@ Expression *resolvePropertiesX(Scope *sc, Expression *e1, Expression *e2 = NULL) FuncDeclaration *fd = resolveFuncCall(loc, sc, s, tiargs, tthis, &a, 1); if (fd && fd->type) { + if (fd->errors) + return new ErrorExp(); assert(fd->type->ty == Tfunction); TypeFunction *tf = (TypeFunction *)fd->type; if (!tf->isproperty && global.params.enforcePropertySyntax) @@ -434,6 +444,8 @@ Expression *resolvePropertiesX(Scope *sc, Expression *e1, Expression *e2 = NULL) FuncDeclaration *fd = resolveFuncCall(loc, sc, s, tiargs, tthis, NULL, 1); if (fd && fd->type) { + if (fd->errors) + return new ErrorExp(); assert(fd->type->ty == Tfunction); TypeFunction *tf = (TypeFunction *)fd->type; if (!e2 || tf->isref) @@ -460,6 +472,12 @@ Expression *resolvePropertiesX(Scope *sc, Expression *e1, Expression *e2 = NULL) if (e2) goto Leprop; } + if (e1->op == TOKvar) + { + VarExp *ve = (VarExp *)e1; + if (VarDeclaration *v = ve->var->isVarDeclaration()) + ve->checkPurity(sc, v); + } if (e2) return NULL; @@ -569,6 +587,7 @@ void checkPropertyCall(Expression *e, Expression *emsg) Expression *resolvePropertiesOnly(Scope *sc, Expression *e1) { + //printf("e1 = %s %s\n", Token::toChars(e1->op), e1->toChars()); OverloadSet *os; FuncDeclaration *fd; TemplateDeclaration *td; @@ -655,7 +674,8 @@ Expression *resolvePropertiesOnly(Scope *sc, Expression *e1) fd = dve->var->isFuncDeclaration(); goto Lfd; } - else if (e1->op == TOKvar && e1->type->ty == Tfunction) + else if (e1->op == TOKvar && e1->type->ty == Tfunction && + (sc->intypeof || !((VarExp *)e1)->var->needThis())) { fd = ((VarExp *)e1)->var->isFuncDeclaration(); Lfd: @@ -719,7 +739,7 @@ Expression *searchUFCS(Scope *sc, UnaExp *ue, Identifier *ident) DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)ue; TemplateInstance *ti = new TemplateInstance(loc, s->ident); ti->tiargs = dti->ti->tiargs; // for better diagnostic message - if (!ti->updateTemplateDeclaration(sc, s)) + if (!ti->updateTempDecl(sc, s)) return new ErrorExp(); return new ScopeExp(loc, ti); } @@ -801,19 +821,6 @@ Expression *resolveUFCS(Scope *sc, CallExp *ce) return new RemoveExp(loc, eleft, key); } - else if (ident == Id::apply || ident == Id::applyReverse) - { - return NULL; - } - else - { - TypeAArray *taa = (TypeAArray *)t; - assert(taa->ty == Taarray); - StructDeclaration *sd = taa->getImpl(); - Dsymbol *s = sd->search(Loc(), ident, IgnoreErrors); - if (s) - return NULL; - } } else { @@ -824,10 +831,9 @@ Expression *resolveUFCS(Scope *sc, CallExp *ce) { unsigned errors = global.startGagging(); e = ce->syntaxCopy()->semantic(sc); - if (global.endGagging(errors)) - {} /* fall down to UFCS */ - else + if (!global.endGagging(errors)) return e; + /* fall down to UFCS */ } else return NULL; @@ -951,9 +957,10 @@ bool arrayExpressionSemantic(Expressions *exps, Scope *sc) if (e) { e = e->semantic(sc); - (*exps)[i] = e; if (e->op == TOKerror) err = true; + else + (*exps)[i] = e; } } } @@ -965,13 +972,14 @@ bool arrayExpressionSemantic(Expressions *exps, Scope *sc) * Perform canThrow() on an array of Expressions. */ -int arrayExpressionCanThrow(Expressions *exps, bool mustNotThrow) +int arrayExpressionCanThrow(Expressions *exps, FuncDeclaration *func, bool mustNotThrow) { if (exps) { for (size_t i = 0; i < exps->dim; i++) - { Expression *e = (*exps)[i]; - if (e && e->canThrow(mustNotThrow)) + { + Expression *e = (*exps)[i]; + if (e && canThrow(e, func, mustNotThrow)) return 1; } } @@ -1100,7 +1108,7 @@ int expandAliasThisTuples(Expressions *exps, size_t starti) return -1; } -Expressions *arrayExpressionToCommonType(Scope *sc, Expressions *exps, Type **pt) +bool arrayExpressionToCommonType(Scope *sc, Expressions *exps, Type **pt) { /* The type is determined by applying ?: to each pair. */ @@ -1123,54 +1131,74 @@ Expressions *arrayExpressionToCommonType(Scope *sc, Expressions *exps, Type **pt if (!e->type) { e->error("%s has no value", e->toChars()); - e = new ErrorExp(); + t0 = Type::terror; + continue; + } + if (e->op == TOKtype) + { + e->rvalue(); + t0 = Type::terror; + continue; + } + if (isNonAssignmentArrayOp(e)) + { + e->error("array operation %s without assignment not implemented", e->toChars()); + t0 = Type::terror; + continue; } e = e->isLvalue() ? callCpCtor(sc, e) : valueNoDtor(e); - if (t0) + if (t0 && !t0->equals(e->type)) { - if (t0 != e->type) + /* This applies ?: to merge the types. It's backwards; + * ?: should call this function to merge types. + */ + condexp.type = NULL; + condexp.e1 = e0; + condexp.e2 = e; + condexp.loc = e->loc; + Expression *ex = condexp.semantic(sc); + if (ex->op == TOKerror) + e = ex; + else { - /* This applies ?: to merge the types. It's backwards; - * ?: should call this function to merge types. - */ - condexp.type = NULL; - condexp.e1 = e0; - condexp.e2 = e; - condexp.loc = e->loc; - condexp.semantic(sc); (*exps)[j0] = condexp.e1; e = condexp.e2; - j0 = i; - e0 = e; - t0 = e0->type; } } - else - { - j0 = i; - e0 = e; - t0 = e->type; - } - (*exps)[i] = e; + j0 = i; + e0 = e; + t0 = e->type; + if (e->op != TOKerror) + (*exps)[i] = e; } - if (t0) + if (!t0) + t0 = Type::tvoid; // [] is typed as void[] + else if (t0->ty != Terror) { for (size_t i = 0; i < exps->dim; i++) - { Expression *e = (*exps)[i]; + { + Expression *e = (*exps)[i]; e = e->implicitCastTo(sc, t0); + //assert(e->op != TOKerror); + if (e->op == TOKerror) + { + /* Bugzilla 13024: a workaround for the bug in typeMerge - + * it should paint e1 and e2 by deduced common type, + * but doesn't in this particular case. + */ + t0 = Type::terror; + break; + } (*exps)[i] = e; } } - else - t0 = Type::tvoid; // [] is typed as void[] if (pt) *pt = t0; - // Eventually, we want to make this copy-on-write - return exps; + return (t0 == Type::terror); } /**************************************** @@ -1183,17 +1211,11 @@ TemplateDeclaration *getFuncTemplateDecl(Dsymbol *s) if (f && f->parent) { TemplateInstance *ti = f->parent->isTemplateInstance(); - TemplateDeclaration *td; - if (ti && - !ti->isTemplateMixin() && - (ti->name == f->ident || - ti->toAlias()->ident == f->ident) - && - ti->tempdecl && - (td = ti->tempdecl->isTemplateDeclaration()) != NULL && - td->onemember) + if (ti && !ti->isTemplateMixin() && + ti->tempdecl && ((TemplateDeclaration *)ti->tempdecl)->onemember && + ti->tempdecl->ident == f->ident) { - return td; + return (TemplateDeclaration *)ti->tempdecl; } } return NULL; @@ -1221,6 +1243,12 @@ bool preFunctionParameters(Loc loc, Scope *sc, Expressions *exps) arg = new ErrorExp(); err = true; } + if (isNonAssignmentArrayOp(arg)) + { + arg->error("array operation %s without assignment not implemented", arg->toChars()); + arg = new ErrorExp(); + err = true; + } (*exps)[i] = arg; } } @@ -1308,6 +1336,7 @@ bool Expression::checkPostblit(Scope *sc, Type *t) { checkPurity(sc, sd->postblit); checkSafety(sc, sd->postblit); + checkNogc(sc, sd->postblit); } return true; } @@ -1373,7 +1402,8 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, size_t nparams = Parameter::dim(tf->parameters); if (nargs > nparams && tf->varargs == 0) - { error(loc, "expected %llu arguments, not %llu for non-variadic function type %s", (ulonglong)nparams, (ulonglong)nargs, tf->toChars()); + { + error(loc, "expected %llu arguments, not %llu for non-variadic function type %s", (ulonglong)nparams, (ulonglong)nargs, tf->toChars()); return Type::terror; } @@ -1434,7 +1464,7 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, return Type::terror; } arg = p->defaultArg; - arg = arg->inlineCopy(sc); + arg = inlineCopy(arg, sc); // __FILE__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__ arg = arg->resolveLoc(loc, sc); arguments->push(arg); @@ -1464,7 +1494,8 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, { case Tsarray: case Tarray: - { // Create a static array variable v of type arg->type + { + // Create a static array variable v of type arg->type Identifier *id = Lexer::uniqueId("__arrayArg"); Type *t = new TypeSArray(((TypeArray *)tb)->next, new IntegerExp(nargs - i)); t = t->semantic(loc, sc); @@ -1473,26 +1504,23 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, v->storage_class |= STCtemp | STCctfe; v->semantic(sc); v->parent = sc->parent; - //sc->insert(v); - Expression *c = new DeclarationExp(Loc(), v); + Expression *c = new DeclarationExp(loc, v); c->type = v->type; for (size_t u = i; u < nargs; u++) { Expression *a = (*arguments)[u]; TypeArray *ta = (TypeArray *)tb; - a = a->inferType(ta->next); (*arguments)[u] = a; - if (tret && !ta->next->equals(a->type)) + if (tret && a->implicitConvTo(tret)) { - if (tret->toBasetype()->ty == Tvoid || - a->implicitConvTo(tret)) - { - a = a->toDelegate(sc, tret); - } + a = a->implicitCastTo(sc, tret); + a = a->optimize(WANTvalue); + a = toDelegate(a, sc); } - + else + a = a->implicitCastTo(sc, ta->next); Expression *e = new VarExp(loc, v); e = new IndexExp(loc, e, new IntegerExp(u + 1 - nparams)); ConstructExp *ae = new ConstructExp(loc, e, a); @@ -1507,7 +1535,8 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, break; } case Tclass: - { /* Set arg to be: + { + /* Set arg to be: * new Tclass(arg0, arg1, ..., argn) */ Expressions *args = new Expressions(); @@ -1519,7 +1548,8 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, } default: if (!arg) - { error(loc, "not enough arguments"); + { + error(loc, "not enough arguments"); return Type::terror; } break; @@ -1596,29 +1626,20 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, { Expression *arg = (*arguments)[i]; assert(arg); - if (i < nparams) { Parameter *p = Parameter::getNth(tf->parameters, i); if (!(p->storageClass & STClazy && p->type->ty == Tvoid)) { + Type *tprm = p->type; if (p->type->hasWild()) - { - arg = arg->implicitCastTo(sc, p->type->substWildTo(wildmatch)); - arg = arg->optimize(WANTvalue, (p->storageClass & STCref) != 0); - } - else if (!p->type->equals(arg->type)) + tprm = p->type->substWildTo(wildmatch); + if (!tprm->equals(arg->type)) { //printf("arg->type = %s, p->type = %s\n", arg->type->toChars(), p->type->toChars()); - if (arg->op == TOKtype) - { arg->error("cannot pass type %s as function argument", arg->toChars()); - arg = new ErrorExp(); - goto L3; - } - else - arg = arg->implicitCastTo(sc, p->type); - arg = arg->optimize(WANTvalue, (p->storageClass & STCref) != 0); + arg = arg->implicitCastTo(sc, tprm); + arg = arg->optimize(WANTvalue, (p->storageClass & (STCref | STCout)) != 0); } } if (p->storageClass & STCref) @@ -1637,7 +1658,7 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, else if (p->storageClass & STClazy) { // Convert lazy argument to a delegate - arg = arg->toDelegate(sc, p->type); + arg = toDelegate(arg, sc); } else { @@ -1655,20 +1676,21 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, if (a->op == TOKcast) a = ((CastExp *)a)->e1; - /* Function literals can only appear once, so if this - * appearance was scoped, there cannot be any others. - */ if (a->op == TOKfunction) - { FuncExp *fe = (FuncExp *)a; + { + /* Function literals can only appear once, so if this + * appearance was scoped, there cannot be any others. + */ + FuncExp *fe = (FuncExp *)a; fe->fd->tookAddressOf = 0; } - - /* For passing a delegate to a scoped parameter, - * this doesn't count as taking the address of it. - * We only worry about 'escaping' references to the function. - */ else if (a->op == TOKdelegate) - { DelegateExp *de = (DelegateExp *)a; + { + /* For passing a delegate to a scoped parameter, + * this doesn't count as taking the address of it. + * We only worry about 'escaping' references to the function. + */ + DelegateExp *de = (DelegateExp *)a; if (de->e1->op == TOKvar) { VarExp *ve = (VarExp *)de->e1; FuncDeclaration *f = ve->var->isFuncDeclaration(); @@ -1688,7 +1710,7 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, if (tf->linkage != LINKd) { // Promote bytes, words, etc., to ints - arg = arg->integralPromotions(sc); + arg = integralPromotions(arg, sc); // Promote floats to doubles switch (arg->type->ty) @@ -1803,81 +1825,6 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, return tret; } -/************************************************** - * Write expression out to buf, but wrap it - * in ( ) if its precedence is less than pr. - */ - -void expToCBuffer(OutBuffer *buf, HdrGenState *hgs, Expression *e, PREC pr) -{ -#ifdef DEBUG - if (precedence[e->op] == PREC_zero) - printf("precedence not defined for token '%s'\n",Token::tochars[e->op]); -#endif - assert(precedence[e->op] != PREC_zero); - assert(pr != PREC_zero); - - //if (precedence[e->op] == 0) e->dump(0); - if (precedence[e->op] < pr || - /* Despite precedence, we don't allow aop] == pr)) - { - buf->writeByte('('); - e->toCBuffer(buf, hgs); - buf->writeByte(')'); - } - else - e->toCBuffer(buf, hgs); -} - -void sizeToCBuffer(OutBuffer *buf, HdrGenState *hgs, Expression *e) -{ - if (e->type == Type::tsize_t) - { - Expression *ex = (e->op == TOKcast ? ((CastExp *)e)->e1 : e); - ex = ex->optimize(WANTvalue); - - dinteger_t uval = ex->op == TOKint64 ? ex->toInteger() : (dinteger_t)-1; - if ((sinteger_t)uval >= 0) - { - dinteger_t sizemax; - if (Target::ptrsize == 4) - sizemax = 0xFFFFFFFFUL; - else if (Target::ptrsize == 8) - sizemax = 0xFFFFFFFFFFFFFFFFULL; - else - assert(0); - if (uval <= sizemax && uval <= 0x7FFFFFFFFFFFFFFFULL) - { - buf->printf("%llu", uval); - return; - } - } - } - expToCBuffer(buf, hgs, e, PREC_assign); -} - -/************************************************** - * Write out argument list to buf. - */ - -void argsToCBuffer(OutBuffer *buf, Expressions *expressions, HdrGenState *hgs) -{ - if (expressions) - { - for (size_t i = 0; i < expressions->dim; i++) - { Expression *e = (*expressions)[i]; - - if (i) - buf->writestring(", "); - if (e) - expToCBuffer(buf, hgs, e, PREC_assign); - } - } -} - /************************************************** * Write out argument types to buf. */ @@ -1893,7 +1840,7 @@ void argExpTypesToCBuffer(OutBuffer *buf, Expressions *arguments, HdrGenState *h if (i) buf->writestring(", "); argbuf.reset(); - e->type->toCBuffer2(&argbuf, hgs, 0); + toBufferShort(e->type, &argbuf, hgs); buf->write(&argbuf); } } @@ -1929,7 +1876,7 @@ void Expression::init() Expression *Expression::syntaxCopy() { //printf("Expression::syntaxCopy()\n"); - //dump(0); + //print(); return copy(); } @@ -1945,7 +1892,7 @@ Expression *Expression::copy() #ifdef DEBUG fprintf(stderr, "No expression copy for: %s\n", toChars()); printf("op = %d\n", op); - dump(0); + print(); #endif assert(0); } @@ -2002,10 +1949,7 @@ char *Expression::toChars() OutBuffer buf; toCBuffer(&buf, &hgs); - buf.writeByte(0); - char *p = (char *)buf.data; - buf.data = NULL; - return p; + return buf.extractString(); } void Expression::error(const char *format, ...) @@ -2041,22 +1985,25 @@ void Expression::deprecation(const char *format, ...) } } -int Expression::rvalue(bool allowVoid) +bool Expression::rvalue() { - if (!allowVoid && type && type->toBasetype()->ty == Tvoid) + if (type && type->toBasetype()->ty == Tvoid) { error("expression %s is void and has no value", toChars()); #if 0 - dump(0); + print(); halt(); #endif if (!global.gag) type = Type::terror; - return 0; + return false; } - return 1; + return true; } +/********************************** + * Combine e1 and e2 by CommaExp if both are not NULL. + */ Expression *Expression::combine(Expression *e1, Expression *e2) { if (e1) @@ -2072,6 +2019,35 @@ Expression *Expression::combine(Expression *e1, Expression *e2) return e1; } +/********************************** + * If 'e' is a tree of commas, returns the leftmost expression + * by stripping off it from the tree. The remained part of the tree + * is returned via *pe0. + * Otherwise 'e' is directly returned and *pe0 is set to NULL. + */ +Expression *Expression::extractLast(Expression *e, Expression **pe0) +{ + if (e->op == TOKcomma) + { + CommaExp *ce = (CommaExp *)e; + *pe0 = ce; + + Expression **pe = &e; + while (((CommaExp *)(*pe))->e2->op == TOKcomma) + { + ce = (CommaExp *)(*pe); + pe = &ce->e2; + } + + *pe = ce->e2; + if (pe == &e) + *pe0 = ce->e1; + } + else + *pe0 = NULL; + return e; +} + dinteger_t Expression::toInteger() { //printf("Expression %s\n", Token::toChars(op)); @@ -2103,16 +2079,11 @@ complex_t Expression::toComplex() return (complex_t)0.0; } -StringExp *Expression::toString() +StringExp *Expression::toStringExp() { return NULL; } -void Expression::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring(Token::toChars(op)); -} - void Expression::toMangleBuffer(OutBuffer *buf) { error("expression %s is not a valid template value argument", toChars()); @@ -2138,7 +2109,12 @@ Expression *Expression::toLvalue(Scope *sc, Expression *e) e = this; else if (!loc.filename) loc = e->loc; - error("%s is not an lvalue", e->toChars()); + + if (e->op == TOKtype) + error("%s '%s' is a type, not an lvalue", e->type->kind(), e->type->toChars()); + else + error("%s is not an lvalue", e->toChars()); + return new ErrorExp(); } @@ -2165,39 +2141,55 @@ Expression *Expression::modifiableLvalue(Scope *sc, Expression *e) if (checkModifiable(sc) == 1) { assert(type); - if (type->isMutable()) + if (!type->isMutable()) { - if (!type->isAssignable()) - { error("cannot modify struct %s %s with immutable members", toChars(), type->toChars()); - goto Lerror; - } + error("cannot modify %s expression %s", MODtoChars(type->mod), toChars()); + return new ErrorExp(); } - else + else if (!type->isAssignable()) { - Declaration *var = NULL; - if (op == TOKvar) - var = ((VarExp *)this)->var; - else if (op == TOKdotvar) - var = ((DotVarExp *)this)->var; - if (var && var->storage_class & STCctorinit) - { - const char *p = var->isStatic() ? "static " : ""; - error("can only initialize %sconst member %s inside %sconstructor", - p, var->toChars(), p); - } - else - { - error("cannot modify %s expression %s", MODtoChars(type->mod), toChars()); - } - goto Lerror; + error("cannot modify struct %s %s with immutable members", toChars(), type->toChars()); + return new ErrorExp(); } } return toLvalue(sc, e); - -Lerror: - return new ErrorExp(); } +/******************************* + * Check whether the expression allows RMW operations, error with rmw operator diagnostic if not. + * exp is the RHS expression, or NULL if ++/-- is used (for diagnostics) + */ +Expression *Expression::checkReadModifyWrite(TOK rmwOp, Expression *ex) +{ + //printf("Expression::checkReadModifyWrite() %s %s", toChars(), ex ? ex->toChars() : ""); + if (!type || !type->isShared()) + return NULL; + + // atomicOp uses opAssign (+=/-=) rather than opOp (++/--) for the CT string literal. + switch (rmwOp) + { + case TOKplusplus: + case TOKpreplusplus: + rmwOp = TOKaddass; + break; + + case TOKminusminus: + case TOKpreminusminus: + rmwOp = TOKminass; + break; + + default: + break; + } + + deprecation("Read-modify-write operations are not allowed for shared variables. " + "Use core.atomic.atomicOp!\"%s\"(%s, %s) instead.", + Token::tochars[rmwOp], toChars(), ex ? ex->toChars() : "1"); + return NULL; + + // note: enable when deprecation becomes an error. + // return new ErrorExp(); +} /************************************ * Detect cases where pointers to the stack can 'escape' the @@ -2261,93 +2253,60 @@ void Expression::checkDeprecated(Scope *sc, Dsymbol *s) */ void Expression::checkPurity(Scope *sc, FuncDeclaration *f) { -#if 1 - if (sc->func && !sc->intypeof && !(sc->flags & SCOPEdebug)) - { - /* Given: - * void f() - * { pure void g() - * { - * void h() - * { - * void i() { } - * } - * } - * } - * g() can call h() but not f() - * i() can call h() and g() but not f() - */ + if (!sc->func) + return; + if (sc->func == f) + return; + if (sc->intypeof == 1) + return; + if (sc->flags & (SCOPEctfe | SCOPEdebug)) + return; + + /* Given: + * void f() + * { pure void g() + * { + * void h() + * { + * void i() { } + * } + * } + * } + * g() can call h() but not f() + * i() can call h() and g() but not f() + */ - // Find the closest pure parent of the calling function - FuncDeclaration *outerfunc = sc->func; - while ( outerfunc->toParent2() && - !outerfunc->isPureBypassingInference() && - outerfunc->toParent2()->isFuncDeclaration()) - { - outerfunc = outerfunc->toParent2()->isFuncDeclaration(); - if (outerfunc->type->ty == Terror) - return; - } - - // Find the closest pure parent of the called function - if (getFuncTemplateDecl(f) && !f->isNested() && - f->parent->isTemplateInstance()->enclosing == NULL) - { // The closest pure parent of instantiated non-nested template function is - // always itself. - if (!f->isPure() && outerfunc->setImpure() && !(sc->flags & SCOPEctfe)) - error("pure function '%s' cannot call impure function '%s'", - outerfunc->toPrettyChars(), f->toPrettyChars()); + // Find the closest pure parent of the calling function + FuncDeclaration *outerfunc = sc->func; + while ( outerfunc->toParent2() && + !outerfunc->isPureBypassingInference() && + outerfunc->toParent2()->isFuncDeclaration()) + { + outerfunc = outerfunc->toParent2()->isFuncDeclaration(); + if (outerfunc->type->ty == Terror) return; - } - FuncDeclaration *calledparent = f; - while ( calledparent->toParent2() && - !calledparent->isPureBypassingInference() && - calledparent->toParent2()->isFuncDeclaration()) - { - calledparent = calledparent->toParent2()->isFuncDeclaration(); - if (calledparent->type->ty == Terror) - return; - } + } - /* Both escape!allocator and escapeImpl!allocator are impure at [a], - * but they are nested template function that instantiated in test(). - * Then calling them from [a] doesn't break purity. - * It's similar to normal impure nested function inside pure function. - * - * auto escapeImpl(alias fun)() { - * return fun(); - * } - * auto escape(alias fun)() { - * return escape!fun(); - * } - * pure string test() { - * char[] allocator() { return new char[1]; } // impure - * return escape!allocator(); // [a] - * } - */ - if (getFuncTemplateDecl(outerfunc) && - outerfunc->toParent2() == calledparent && - f != calledparent) - { + FuncDeclaration *calledparent = f; + while ( calledparent->toParent2() && + !calledparent->isPureBypassingInference() && + calledparent->toParent2()->isFuncDeclaration()) + { + calledparent = calledparent->toParent2()->isFuncDeclaration(); + if (calledparent->type->ty == Terror) return; - } + } - // If the caller has a pure parent, then either the called func must be pure, - // OR, they must have the same pure parent. - if (/*outerfunc->isPure() &&*/ // comment out because we deduce purity now - !f->isPure() && calledparent != outerfunc && - !(sc->flags & SCOPEctfe)) + // If the caller has a pure parent, then either the called func must be pure, + // OR, they must have the same pure parent. + if (!f->isPure() && calledparent != outerfunc) + { + if (sc->flags & SCOPEcompile ? outerfunc->isPureBypassingInferenceX() : outerfunc->setImpure()) { - if (outerfunc->setImpure()) - error("pure function '%s' cannot call impure function '%s'", - outerfunc->toPrettyChars(), f->toPrettyChars()); + error("pure function '%s' cannot call impure function '%s'", + outerfunc->toPrettyChars(), f->toPrettyChars()); } } -#else - if (sc->func && sc->func->isPure() && !sc->intypeof && !f->isPure()) - error("pure function '%s' cannot call impure function '%s'", - sc->func->toPrettyChars(), f->toPrettyChars()); -#endif } /******************************************* @@ -2360,107 +2319,122 @@ void Expression::checkPurity(Scope *sc, VarDeclaration *v) /* Look for purity and safety violations when accessing variable v * from current function. */ - if (sc->func && - !sc->intypeof && // allow violations inside typeof(expression) - !(sc->flags & SCOPEdebug) && // allow violations inside debug conditionals - v->ident != Id::ctfe && // magic variable never violates pure and safe - !v->isImmutable() && // always safe and pure to access immutables... - !(v->isConst() && !v->isRef() && (v->isDataseg() || v->isParameter()) && - v->type->implicitConvTo(v->type->immutableOf())) && - // or const global/parameter values which have no mutable indirections - !(v->storage_class & STCmanifest) // ...or manifest constants - ) - { - if (v->isDataseg()) + if (!sc->func) + return; + if (sc->intypeof == 1) + return; // allow violations inside typeof(expression) + if (sc->flags & (SCOPEctfe | SCOPEdebug)) + return; // allow violations inside compile-time evaluated expressions and debug conditionals + if (v->ident == Id::ctfe) + return; // magic variable never violates pure and safe + if (v->isImmutable()) + return; // always safe and pure to access immutables... + if (v->isConst() && !v->isRef() && (v->isDataseg() || v->isParameter()) && + v->type->implicitConvTo(v->type->immutableOf())) + return; // or const global/parameter values which have no mutable indirections + if (v->storage_class & STCmanifest) + return; // ...or manifest constants + + if (v->isDataseg()) + { + // Bugzilla 7533: Accessing implicit generated __gate is pure. + if (v->ident == Id::gate) + return; + + /* Accessing global mutable state. + * Therefore, this function and all its immediately enclosing + * functions must be pure. + */ + for (Dsymbol *s = sc->func; s; s = s->toParent2()) { - /* Accessing global mutable state. - * Therefore, this function and all its immediately enclosing - * functions must be pure. - */ - bool msg = false; - for (Dsymbol *s = sc->func; s; s = s->toParent2()) + FuncDeclaration *ff = s->isFuncDeclaration(); + if (!ff) + break; + if (sc->flags & SCOPEcompile ? ff->isPureBypassingInferenceX() : ff->setImpure()) { - FuncDeclaration *ff = s->isFuncDeclaration(); - if (!ff) - break; - // Accessing implicit generated __gate is pure. - if (ff->setImpure() && !msg && strcmp(v->ident->toChars(), "__gate")) - { error("pure function '%s' cannot access mutable static data '%s'", - sc->func->toPrettyChars(), v->toChars()); - msg = true; // only need the innermost message - } + error("pure function '%s' cannot access mutable static data '%s'", + sc->func->toPrettyChars(), v->toChars()); + break; } } - else + } + else + { + /* Bugzilla 10981: Special case for the contracts of pure virtual function. + * Rewrite: + * tret foo(int i) pure + * in { assert(i); } out { assert(i); } body { ... } + * + * as: + * tret foo(int i) pure { + * void __require() pure { assert(i); } // allow accessing to i + * void __ensure() pure { assert(i); } // allow accessing to i + * __require(); + * ... + * __ensure(); + * } + */ + if ((sc->func->ident == Id::require || sc->func->ident == Id::ensure) && + v->isParameter() && sc->func->parent == v->parent) { - /* Bugzilla 10981: Special case for the contracts of pure virtual function. - * Rewrite: - * tret foo(int i) pure - * in { assert(i); } out { assert(i); } body { ... } - * - * as: - * tret foo(int i) pure { - * void __require() pure { assert(i); } // allow accessing to i - * void __ensure() pure { assert(i); } // allow accessing to i - * __require(); - * ... - * __ensure(); - * } - */ - if ((sc->func->ident == Id::require || sc->func->ident == Id::ensure) && - v->isParameter() && sc->func->parent == v->parent) - { - return; - } + return; + } - /* Given: - * void f() - * { int fx; - * pure void g() - * { int gx; - * void h() - * { int hx; - * void i() { } - * } - * } - * } - * i() can modify hx and gx but not fx - */ + /* Given: + * void f() + * { int fx; + * pure void g() + * { int gx; + * void h() + * { int hx; + * void i() { } + * } + * } + * } + * i() can modify hx and gx but not fx + */ - Dsymbol *vparent = v->toParent2(); - for (Dsymbol *s = sc->func; s; s = s->toParent2()) + Dsymbol *vparent = v->toParent2(); + for (Dsymbol *s = sc->func; s; s = s->toParent2()) + { + if (s == vparent) + break; + FuncDeclaration *ff = s->isFuncDeclaration(); + if (!ff) + break; + if (sc->flags & SCOPEcompile ? ff->isPureBypassingInferenceX() : ff->setImpure()) { - if (s == vparent) - break; - FuncDeclaration *ff = s->isFuncDeclaration(); - if (!ff) - break; - if (ff->setImpure()) - { error("pure nested function '%s' cannot access mutable data '%s'", - ff->toChars(), v->toChars()); - break; - } + error("pure nested function '%s' cannot access mutable data '%s'", + ff->toChars(), v->toChars()); + break; } } + } - /* Do not allow safe functions to access __gshared data - */ - if (v->storage_class & STCgshared) - { - if (sc->func->setUnsafe()) - error("safe function '%s' cannot access __gshared data '%s'", - sc->func->toChars(), v->toChars()); - } + /* Do not allow safe functions to access __gshared data + */ + if (v->storage_class & STCgshared) + { + if (sc->func->setUnsafe()) + error("safe function '%s' cannot access __gshared data '%s'", + sc->func->toChars(), v->toChars()); } } void Expression::checkSafety(Scope *sc, FuncDeclaration *f) { - if (sc->func && !sc->intypeof && - !(sc->flags & SCOPEctfe) && - !f->isSafe() && !f->isTrusted()) + if (!sc->func) + return; + if (sc->func == f) + return; + if (sc->intypeof == 1) + return; + if (sc->flags & SCOPEctfe) + return; + + if (!f->isSafe() && !f->isTrusted()) { - if (sc->func->setUnsafe()) + if (sc->flags & SCOPEcompile ? sc->func->isSafeBypassingInference() : sc->func->setUnsafe()) { if (loc.linnum == 0) // e.g. implicitly generated dtor loc = sc->func->loc; @@ -2471,17 +2445,41 @@ void Expression::checkSafety(Scope *sc, FuncDeclaration *f) } } -/***************************** - * Check that expression can be tested for true or false. - */ - +void Expression::checkNogc(Scope *sc, FuncDeclaration *f) +{ + if (!sc->func) + return; + if (sc->func == f) + return; + if (sc->intypeof == 1) + return; + if (sc->flags & SCOPEctfe) + return; + + if (!f->isNogc()) + { + if (sc->flags & SCOPEcompile ? sc->func->isNogcBypassingInference() : sc->func->setGC()) + { + if (loc.linnum == 0) // e.g. implicitly generated dtor + loc = sc->func->loc; + + error("@nogc function '%s' cannot call non-@nogc function '%s'", + sc->func->toPrettyChars(), f->toPrettyChars()); + } + } +} + +/***************************** + * Check that expression can be tested for true or false. + */ + Expression *Expression::checkToBoolean(Scope *sc) { // Default is 'yes' - do nothing #ifdef DEBUG if (!type) - dump(0); + print(); assert(type); #endif @@ -2539,11 +2537,13 @@ Expression *Expression::checkToPointer() * Take address of expression. */ -Expression *Expression::addressOf(Scope *sc) +Expression *Expression::addressOf() { //printf("Expression::addressOf()\n"); - Expression *e = toLvalue(sc, NULL); - e = new AddrExp(loc, e); +#ifdef DEBUG + assert(op == TOKerror || isLvalue()); +#endif + Expression *e = new AddrExp(loc, this); e->type = type->pointerTo(); return e; } @@ -2618,7 +2618,8 @@ IntegerExp::IntegerExp(Loc loc, dinteger_t value, Type *type) : Expression(loc, TOKint64, sizeof(IntegerExp)) { //printf("IntegerExp(value = %lld, type = '%s')\n", value, type ? type->toChars() : ""); - if (type && !type->isscalar()) + assert(type); + if (!type->isscalar()) { //printf("%s, loc = %d\n", toChars(), loc.linnum); if (type->ty != Terror) @@ -2626,14 +2627,14 @@ IntegerExp::IntegerExp(Loc loc, dinteger_t value, Type *type) type = Type::terror; } this->type = type; - this->value = value; + setInteger(value); } IntegerExp::IntegerExp(dinteger_t value) : Expression(Loc(), TOKint64, sizeof(IntegerExp)) { this->type = Type::tint32; - this->value = value; + this->value = (d_int32) value; } bool IntegerExp::equals(RootObject *o) @@ -2657,72 +2658,53 @@ char *IntegerExp::toChars() return Expression::toChars(); } -dinteger_t IntegerExp::toInteger() -{ Type *t; - - t = type; - while (t) - { - switch (t->ty) - { - case Tbool: value = (value != 0); break; - case Tint8: value = (d_int8) value; break; - case Tchar: - case Tuns8: value = (d_uns8) value; break; - case Tint16: value = (d_int16) value; break; - case Twchar: - case Tuns16: value = (d_uns16) value; break; - case Tint32: value = (d_int32) value; break; - case Tdchar: - case Tuns32: value = (d_uns32) value; break; - case Tint64: value = (d_int64) value; break; - case Tuns64: value = (d_uns64) value; break; - case Tpointer: - if (Target::ptrsize == 4) - value = (d_uns32) value; - else if (Target::ptrsize == 8) - value = (d_uns64) value; - else - assert(0); - break; - - case Tenum: - { - TypeEnum *te = (TypeEnum *)t; - t = te->sym->memtype; - continue; - } - - case Ttypedef: - { - TypeTypedef *tt = (TypeTypedef *)t; - t = tt->sym->basetype; - continue; - } +void IntegerExp::setInteger(dinteger_t value) +{ + this->value = value; + normalize(); +} - default: - /* This can happen if errors, such as - * the type is painted on like in fromConstInitializer(). - */ - if (!global.errors) - { - printf("e = %p, ty = %d\n", this, type->ty); - type->print(); - assert(0); - } - break; - } - break; +void IntegerExp::normalize() +{ + /* 'Normalize' the value of the integer to be in range of the type + */ + switch (type->toBasetype()->ty) + { + case Tbool: value = (value != 0); break; + case Tint8: value = (d_int8) value; break; + case Tchar: + case Tuns8: value = (d_uns8) value; break; + case Tint16: value = (d_int16) value; break; + case Twchar: + case Tuns16: value = (d_uns16) value; break; + case Tint32: value = (d_int32) value; break; + case Tdchar: + case Tuns32: value = (d_uns32) value; break; + case Tint64: value = (d_int64) value; break; + case Tuns64: value = (d_uns64) value; break; + case Tpointer: + if (Target::ptrsize == 4) + value = (d_uns32) value; + else if (Target::ptrsize == 8) + value = (d_uns64) value; + else + assert(0); + break; + default: + break; } +} + +dinteger_t IntegerExp::toInteger() +{ + normalize(); // necessary until we fix all the paints of 'type' return value; } real_t IntegerExp::toReal() { - Type *t; - - toInteger(); - t = type->toBasetype(); + normalize(); // necessary until we fix all the paints of 'type' + Type *t = type->toBasetype(); if (t->ty == Tuns64) return ldouble((d_uns64)value); else @@ -2747,22 +2729,10 @@ int IntegerExp::isBool(int result) Expression *IntegerExp::semantic(Scope *sc) { - if (!type) - { - // Determine what the type of this number is - dinteger_t number = value; - - if (number & 0x8000000000000000LL) - type = Type::tuns64; - else if (number & 0xFFFFFFFF80000000LL) - type = Type::tint64; - else - type = Type::tint32; - } - else - { if (!type->deco) - type = type->semantic(loc, sc); - } + assert(type && type->deco); + if (type->ty == Terror) + return new ErrorExp(); + normalize(); return this; } @@ -2776,138 +2746,12 @@ Expression *IntegerExp::toLvalue(Scope *sc, Expression *e) return new ErrorExp(); } -void IntegerExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - dinteger_t v = toInteger(); - - if (type) - { Type *t = type; - - L1: - switch (t->ty) - { - case Tenum: - { TypeEnum *te = (TypeEnum *)t; - buf->printf("cast(%s)", te->sym->toChars()); - t = te->sym->memtype; - goto L1; - } - - case Ttypedef: - { TypeTypedef *tt = (TypeTypedef *)t; - buf->printf("cast(%s)", tt->sym->toChars()); - t = tt->sym->basetype; - goto L1; - } - - case Twchar: // BUG: need to cast(wchar) - case Tdchar: // BUG: need to cast(dchar) - if ((uinteger_t)v > 0xFF) - { - buf->printf("'\\U%08x'", v); - break; - } - case Tchar: - { - size_t o = buf->offset; - if (v == '\'') - buf->writestring("'\\''"); - else if (isprint((int)v) && v != '\\') - buf->printf("'%c'", (int)v); - else - buf->printf("'\\x%02x'", (int)v); - if (hgs->ddoc) - escapeDdocString(buf, o); - break; - } - - case Tint8: - buf->writestring("cast(byte)"); - goto L2; - - case Tint16: - buf->writestring("cast(short)"); - goto L2; - - case Tint32: - L2: - buf->printf("%d", (int)v); - break; - - case Tuns8: - buf->writestring("cast(ubyte)"); - goto L3; - - case Tuns16: - buf->writestring("cast(ushort)"); - goto L3; - - case Tuns32: - L3: - buf->printf("%uu", (unsigned)v); - break; - - case Tint64: - buf->printf("%lldL", v); - break; - - case Tuns64: - L4: - buf->printf("%lluLU", v); - break; - - case Tbool: - buf->writestring((char *)(v ? "true" : "false")); - break; - - case Tpointer: - buf->writestring("cast("); - buf->writestring(t->toChars()); - buf->writeByte(')'); - if (Target::ptrsize == 4) - goto L3; - else if (Target::ptrsize == 8) - goto L4; - else - assert(0); - - default: - /* This can happen if errors, such as - * the type is painted on like in fromConstInitializer(). - */ - if (!global.errors) - { -#ifdef DEBUG - t->print(); -#endif - assert(0); - } - break; - } - } - else if (v & 0x8000000000000000LL) - buf->printf("0x%llx", v); - else - buf->printf("%lld", v); -} - void IntegerExp::toMangleBuffer(OutBuffer *buf) { if ((sinteger_t)value < 0) buf->printf("N%lld", -value); else - { - /* This is an awful hack to maintain backwards compatibility. - * There really always should be an 'i' before a number, but - * there wasn't in earlier implementations, so to maintain - * backwards compatibility it is only done if necessary to disambiguate. - * See bugzilla 3029 - */ - if (buf->offset > 0 && isdigit(buf->data[buf->offset - 1])) - buf->writeByte('i'); - - buf->printf("%lld", value); - } + buf->printf("i%lld", value); } /******************************** ErrorExp **************************/ @@ -2927,11 +2771,6 @@ Expression *ErrorExp::toLvalue(Scope *sc, Expression *e) return this; } -void ErrorExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("__error"); -} - /******************************** RealExp **************************/ RealExp::RealExp(Loc loc, real_t value, Type *type) @@ -3028,56 +2867,6 @@ int RealExp::isBool(int result) : (value == 0); } -void floatToBuffer(OutBuffer *buf, Type *type, real_t value) -{ - /* In order to get an exact representation, try converting it - * to decimal then back again. If it matches, use it. - * If it doesn't, fall back to hex, which is - * always exact. - * Longest string is for -real.max: - * "-1.18973e+4932\0".length == 17 - * "-0xf.fffffffffffffffp+16380\0".length == 28 - */ - const size_t BUFFER_LEN = 32; - char buffer[BUFFER_LEN]; - ld_sprint(buffer, 'g', value); - assert(strlen(buffer) < BUFFER_LEN); - - real_t r = Port::strtold(buffer, NULL); - if (r != value) // if exact duplication - ld_sprint(buffer, 'a', value); - buf->writestring(buffer); - - if (type) - { - Type *t = type->toBasetype(); - switch (t->ty) - { - case Tfloat32: - case Timaginary32: - case Tcomplex32: - buf->writeByte('F'); - break; - - case Tfloat80: - case Timaginary80: - case Tcomplex80: - buf->writeByte('L'); - break; - - default: - break; - } - if (t->isimaginary()) - buf->writeByte('i'); - } -} - -void RealExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - floatToBuffer(buf, type, value); -} - void realToMangleBuffer(OutBuffer *buf, real_t value) { /* Rely on %A to get portable mangling. @@ -3101,7 +2890,7 @@ void realToMangleBuffer(OutBuffer *buf, real_t value) char buffer[BUFFER_LEN]; size_t n = ld_sprint(buffer, 'A', value); assert(n < BUFFER_LEN); - for (int i = 0; i < n; i++) + for (size_t i = 0; i < n; i++) { char c = buffer[i]; switch (c) @@ -3217,18 +3006,6 @@ int ComplexExp::isBool(int result) return !value; } -void ComplexExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - /* Print as: - * (re+imi) - */ - buf->writeByte('('); - floatToBuffer(buf, type, creall(value)); - buf->writeByte('+'); - floatToBuffer(buf, type, cimagl(value)); - buf->writestring("i)"); -} - void ComplexExp::toMangleBuffer(OutBuffer *buf) { buf->writeByte('c'); @@ -3254,19 +3031,21 @@ IdentifierExp *IdentifierExp::create(Loc loc, Identifier *ident) Expression *IdentifierExp::semantic(Scope *sc) { - Dsymbol *s; - Dsymbol *scopesym; - #if LOGSEMANTIC printf("IdentifierExp::semantic('%s')\n", ident->toChars()); #endif - s = sc->search(loc, ident, &scopesym); - if (s) - { Expression *e; + if (type) // This is used as the dummy expression + return this; + Dsymbol *scopesym; + Dsymbol *s = sc->search(loc, ident, &scopesym); + if (s) + { if (s->errors) return new ErrorExp(); + Expression *e; + /* See if the symbol was a member of an enclosing 'with' */ WithScopeSymbol *withsym = scopesym->isWithScopeSymbol(); @@ -3277,14 +3056,15 @@ Expression *IdentifierExp::semantic(Scope *sc) // First find the scope of the with Scope *scwith = sc; while (scwith->scopesym != scopesym) - { scwith = scwith->enclosing; + { + scwith = scwith->enclosing; assert(scwith); } // Look at enclosing scopes for symbols with the same name, // in the same function for (Scope *scx = scwith; scx && scx->func == scwith->func; scx = scx->enclosing) - { Dsymbol *s2; - + { + Dsymbol *s2; if (scx->scopesym && scx->scopesym->symtab && (s2 = scx->scopesym->symtab->lookup(s->ident)) != NULL && s != s2) @@ -3302,7 +3082,8 @@ Expression *IdentifierExp::semantic(Scope *sc) e = new DotIdExp(loc, e, ident); } else - { Type *t = withsym->withstate->wthis->type; + { + Type *t = withsym->withstate->wthis->type; if (t->ty == Tpointer) t = ((TypePointer *)t)->next; e = typeDotIdExp(loc, t, ident); @@ -3331,6 +3112,7 @@ Expression *IdentifierExp::semantic(Scope *sc) } return e->semantic(sc); } + if (hasThis(sc)) { AggregateDeclaration *ad = sc->getStructClassScope(); @@ -3345,14 +3127,17 @@ Expression *IdentifierExp::semantic(Scope *sc) return e; } } + if (ident == Id::ctfe) - { // Create the magic __ctfe bool variable - VarDeclaration *vd = new VarDeclaration(loc, Type::tbool, Id::ctfe, NULL); - vd->storage_class |= STCtemp; - Expression *e = new VarExp(loc, vd); - e = e->semantic(sc); - return e; + { + // Create the magic __ctfe bool variable + VarDeclaration *vd = new VarDeclaration(loc, Type::tbool, Id::ctfe, NULL); + vd->storage_class |= STCtemp; + Expression *e = new VarExp(loc, vd); + e = e->semantic(sc); + return e; } + const char *n = importHint(ident->toChars()); if (n) error("'%s' is not defined, perhaps you need to import %s; ?", ident->toChars(), n); @@ -3372,15 +3157,6 @@ char *IdentifierExp::toChars() return ident->toChars(); } -void IdentifierExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - if (hgs->hdrgen) - buf->writestring(ident->toHChars2()); - else - buf->writestring(ident->toChars()); -} - - int IdentifierExp::isLvalue() { return 1; @@ -3482,6 +3258,14 @@ Expression *DsymbolExp::semantic(Scope *sc) } e = e->copy(); e->loc = loc; // for better error message + + // Detect recursive initializers. + // BUG: The check for speculative gagging is not correct + if (v->inuse && !global.gag) + { + e->error("circular initialization of %s", v->toChars()); + return new ErrorExp(); + } e = e->semantic(sc); return e; } @@ -3505,7 +3289,8 @@ Expression *DsymbolExp::semantic(Scope *sc) if (!f->type->deco) { - error("forward reference to %s", toChars()); + const char *trailMsg = f->inferRetType ? "inferred return type of function call " : ""; + error("forward reference to %s'%s'", trailMsg, toChars()); return new ErrorExp(); } FuncDeclaration *fd = s->isFuncDeclaration(); @@ -3539,6 +3324,12 @@ Expression *DsymbolExp::semantic(Scope *sc) return ie->semantic(sc); } + if (Nspace *ns = s->isNspace()) + { + ScopeExp *ie = new ScopeExp(loc, ns); + return ie->semantic(sc); + } + if (Type *t = s->getType()) { TypeExp *te = new TypeExp(loc, t); @@ -3554,13 +3345,12 @@ Expression *DsymbolExp::semantic(Scope *sc) if (TemplateInstance *ti = s->isTemplateInstance()) { - if (!ti->semanticRun) - ti->semantic(sc); + ti->semantic(sc); + if (!ti->inst || ti->errors) + return new ErrorExp(); s = ti->toAlias(); if (!s->isTemplateInstance()) goto Lagain; - if (ti->errors) - return new ErrorExp(); e = new ScopeExp(loc, ti); e = e->semantic(sc); return e; @@ -3590,12 +3380,6 @@ char *DsymbolExp::toChars() return s->toChars(); } -void DsymbolExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring(s->toChars()); -} - - int DsymbolExp::isLvalue() { return 1; @@ -3621,9 +3405,7 @@ Expression *ThisExp::semantic(Scope *sc) printf("ThisExp::semantic()\n"); #endif if (type) - { //assert(global.errors || var); return this; - } FuncDeclaration *fd = hasThis(sc); // fd is the uplevel function with the 'this' variable @@ -3676,12 +3458,6 @@ int ThisExp::isBool(int result) return result ? true : false; } -void ThisExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("this"); -} - - int ThisExp::isLvalue() { return 1; @@ -3696,7 +3472,7 @@ Expression *ThisExp::modifiableLvalue(Scope *sc, Expression *e) { if (type->toBasetype()->ty == Tclass) { - error("Cannot modify '%s'", toChars()); + error("cannot modify '%s' reference", toChars()); return toLvalue(sc, e); } return Expression::modifiableLvalue(sc, e); @@ -3712,9 +3488,6 @@ SuperExp::SuperExp(Loc loc) Expression *SuperExp::semantic(Scope *sc) { - ClassDeclaration *cd; - Dsymbol *s; - #if LOGSEMANTIC printf("SuperExp::semantic('%s')\n", toChars()); #endif @@ -3722,6 +3495,8 @@ Expression *SuperExp::semantic(Scope *sc) return this; FuncDeclaration *fd = hasThis(sc); + ClassDeclaration *cd; + Dsymbol *s; /* Special case for typeof(this) and typeof(super) since both * should work even if they are not inside a non-static member function @@ -3741,7 +3516,8 @@ Expression *SuperExp::semantic(Scope *sc) { cd = cd->baseClass; if (!cd) - { error("class %s has no 'super'", s->toChars()); + { + error("class %s has no 'super'", s->toChars()); goto Lerr; } type = cd->type; @@ -3782,18 +3558,11 @@ Expression *SuperExp::semantic(Scope *sc) sc->callSuper |= CSXsuper; return this; - Lerr: error("'super' is only allowed in non-static class member functions"); return new ErrorExp(); } -void SuperExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("super"); -} - - /******************************** NullExp **************************/ NullExp::NullExp(Loc loc, Type *type) @@ -3808,8 +3577,11 @@ bool NullExp::equals(RootObject *o) if (o && o->dyncast() == DYNCAST_EXPRESSION) { Expression *e = (Expression *)o; - if (e->op == TOKnull) + if (e->op == TOKnull && + type->equals(e->type)) + { return true; + } } return false; } @@ -3820,8 +3592,10 @@ Expression *NullExp::semantic(Scope *sc) printf("NullExp::semantic('%s')\n", toChars()); #endif // NULL is the same as (void *)0 - if (!type) - type = Type::tnull; + if (type) + return this; + + type = Type::tnull; return this; } @@ -3830,7 +3604,7 @@ int NullExp::isBool(int result) return result ? false : true; } -StringExp *NullExp::toString() +StringExp *NullExp::toStringExp() { if (implicitConvTo(Type::tstring)) { @@ -3841,11 +3615,6 @@ StringExp *NullExp::toString() return NULL; } -void NullExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("null"); -} - void NullExp::toMangleBuffer(OutBuffer *buf) { buf->writeByte('n'); @@ -3918,72 +3687,78 @@ Expression *StringExp::semantic(Scope *sc) #if LOGSEMANTIC printf("StringExp::semantic() %s\n", toChars()); #endif - if (!type) - { OutBuffer buffer; - size_t newlen = 0; - const char *p; - size_t u; - unsigned c; + if (type) + return this; - switch (postfix) - { - case 'd': - for (u = 0; u < len;) + OutBuffer buffer; + size_t newlen = 0; + const char *p; + size_t u; + unsigned c; + + switch (postfix) + { + case 'd': + for (u = 0; u < len;) + { + p = utf_decodeChar((utf8_t *)string, len, &u, &c); + if (p) { - p = utf_decodeChar((utf8_t *)string, len, &u, &c); - if (p) - { error("%s", p); - return new ErrorExp(); - } - else - { buffer.write4(c); - newlen++; - } + error("%s", p); + return new ErrorExp(); } - buffer.write4(0); - string = buffer.extractData(); - len = newlen; - sz = 4; - //type = new TypeSArray(Type::tdchar, new IntegerExp(loc, len, Type::tindex)); - type = new TypeDArray(Type::tdchar->immutableOf()); - committed = 1; - break; - - case 'w': - for (u = 0; u < len;) + else { - p = utf_decodeChar((utf8_t *)string, len, &u, &c); - if (p) - { error("%s", p); - return new ErrorExp(); - } - else - { buffer.writeUTF16(c); - newlen++; - if (c >= 0x10000) - newlen++; - } + buffer.write4(c); + newlen++; } - buffer.writeUTF16(0); - string = buffer.extractData(); - len = newlen; - sz = 2; - //type = new TypeSArray(Type::twchar, new IntegerExp(loc, len, Type::tindex)); - type = new TypeDArray(Type::twchar->immutableOf()); - committed = 1; - break; + } + buffer.write4(0); + string = buffer.extractData(); + len = newlen; + sz = 4; + //type = new TypeSArray(Type::tdchar, new IntegerExp(loc, len, Type::tindex)); + type = new TypeDArray(Type::tdchar->immutableOf()); + committed = 1; + break; - case 'c': - committed = 1; - default: - //type = new TypeSArray(Type::tchar, new IntegerExp(loc, len, Type::tindex)); - type = new TypeDArray(Type::tchar->immutableOf()); - break; - } - type = type->semantic(loc, sc); - //type = type->immutableOf(); - //printf("type = %s\n", type->toChars()); + case 'w': + for (u = 0; u < len;) + { + p = utf_decodeChar((utf8_t *)string, len, &u, &c); + if (p) + { + error("%s", p); + return new ErrorExp(); + } + else + { + buffer.writeUTF16(c); + newlen++; + if (c >= 0x10000) + newlen++; + } + } + buffer.writeUTF16(0); + string = buffer.extractData(); + len = newlen; + sz = 2; + //type = new TypeSArray(Type::twchar, new IntegerExp(loc, len, Type::tindex)); + type = new TypeDArray(Type::twchar->immutableOf()); + committed = 1; + break; + + case 'c': + committed = 1; + default: + //type = new TypeSArray(Type::tchar, new IntegerExp(loc, len, Type::tindex)); + type = new TypeDArray(Type::tchar->immutableOf()); + break; } + type = type->semantic(loc, sc); + //type = type->immutableOf(); + //printf("type = %s\n", type->toChars()); + return this; } @@ -4035,7 +3810,7 @@ size_t StringExp::length() return result; } -StringExp *StringExp::toString() +StringExp *StringExp::toStringExp() { return this; } @@ -4138,7 +3913,7 @@ Expression *StringExp::toLvalue(Scope *sc, Expression *e) Expression *StringExp::modifiableLvalue(Scope *sc, Expression *e) { - error("Cannot modify '%s'", toChars()); + error("cannot modify string literal %s", toChars()); return new ErrorExp(); } @@ -4166,41 +3941,6 @@ unsigned StringExp::charAt(uinteger_t i) return value; } -void StringExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writeByte('"'); - size_t o = buf->offset; - for (size_t i = 0; i < len; i++) - { unsigned c = charAt(i); - - switch (c) - { - case '"': - case '\\': - if (!hgs->console) - buf->writeByte('\\'); - default: - if (c <= 0xFF) - { if (c <= 0x7F && (isprint(c) || hgs->console)) - buf->writeByte(c); - else - buf->printf("\\x%02x", c); - } - else if (c <= 0xFFFF) - buf->printf("\\x%02x\\x%02x", c & 0xFF, c >> 8); - else - buf->printf("\\x%02x\\x%02x\\x%02x\\x%02x", - c & 0xFF, (c >> 8) & 0xFF, (c >> 16) & 0xFF, c >> 24); - break; - } - } - if (hgs->ddoc) - escapeDdocString(buf, o); - buf->writeByte('"'); - if (postfix) - buf->writeByte(postfix); -} - void StringExp::toMangleBuffer(OutBuffer *buf) { char m; OutBuffer tmp; @@ -4290,6 +4030,11 @@ bool ArrayLiteralExp::equals(RootObject *o) ArrayLiteralExp *ae = (ArrayLiteralExp *)o; if (elements->dim != ae->elements->dim) return false; + if (elements->dim == 0 && + !type->equals(ae->type)) + { + return false; + } for (size_t i = 0; i < elements->dim; i++) { Expression *e1 = (*elements)[i]; @@ -4324,7 +4069,8 @@ Expression *ArrayLiteralExp::semantic(Scope *sc) expandTuples(elements); Type *t0; - elements = arrayExpressionToCommonType(sc, elements, &t0); + if (arrayExpressionToCommonType(sc, elements, &t0)) + return new ErrorExp(); type = t0->arrayOf(); //type = new TypeSArray(t0, new IntegerExp(elements->dim)); @@ -4349,7 +4095,7 @@ int ArrayLiteralExp::isBool(int result) return result ? (dim != 0) : (dim == 0); } -StringExp *ArrayLiteralExp::toString() +StringExp *ArrayLiteralExp::toStringExp() { TY telem = type->nextOf()->toBasetype()->ty; @@ -4363,18 +4109,18 @@ StringExp *ArrayLiteralExp::toString() OutBuffer buf; if (elements) { - for (int i = 0; i < elements->dim; ++i) + for (size_t i = 0; i < elements->dim; ++i) { Expression *ch = (*elements)[i]; if (ch->op != TOKint64) return NULL; - if (sz == 1) buf.writebyte((unsigned)ch->toInteger()); + if (sz == 1) buf.writeByte((unsigned)ch->toInteger()); else if (sz == 2) buf.writeword((unsigned)ch->toInteger()); else buf.write4((unsigned)ch->toInteger()); } } char prefix; - if (sz == 1) { prefix = 'c'; buf.writebyte(0); } + if (sz == 1) { prefix = 'c'; buf.writeByte(0); } else if (sz == 2) { prefix = 'w'; buf.writeword(0); } else { prefix = 'd'; buf.write4(0); } @@ -4387,13 +4133,6 @@ StringExp *ArrayLiteralExp::toString() return NULL; } -void ArrayLiteralExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writeByte('['); - argsToCBuffer(buf, elements, hgs); - buf->writeByte(']'); -} - void ArrayLiteralExp::toMangleBuffer(OutBuffer *buf) { size_t dim = elements ? elements->dim : 0; @@ -4457,7 +4196,6 @@ Expression *AssocArrayLiteralExp::semantic(Scope *sc) #if LOGSEMANTIC printf("AssocArrayLiteralExp::semantic('%s')\n", toChars()); #endif - if (type) return this; @@ -4476,8 +4214,10 @@ Expression *AssocArrayLiteralExp::semantic(Scope *sc) Type *tkey = NULL; Type *tvalue = NULL; - keys = arrayExpressionToCommonType(sc, keys, &tkey); - values = arrayExpressionToCommonType(sc, values, &tvalue); + err_keys = arrayExpressionToCommonType(sc, keys, &tkey); + err_vals = arrayExpressionToCommonType(sc, values, &tvalue); + if (err_keys || err_vals) + return new ErrorExp(); if (tkey == Type::terror || tvalue == Type::terror) return new ErrorExp; @@ -4497,22 +4237,6 @@ int AssocArrayLiteralExp::isBool(int result) return result ? (dim != 0) : (dim == 0); } -void AssocArrayLiteralExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writeByte('['); - for (size_t i = 0; i < keys->dim; i++) - { Expression *key = (*keys)[i]; - Expression *value = (*values)[i]; - - if (i) - buf->writestring(", "); - expToCBuffer(buf, hgs, key, PREC_assign); - buf->writeByte(':'); - expToCBuffer(buf, hgs, value, PREC_assign); - } - buf->writeByte(']'); -} - void AssocArrayLiteralExp::toMangleBuffer(OutBuffer *buf) { size_t dim = keys->dim; @@ -4562,7 +4286,7 @@ bool StructLiteralExp::equals(RootObject *o) ((Expression *)o)->op == TOKstructliteral) { StructLiteralExp *se = (StructLiteralExp *)o; - if (sd != se->sd) + if (!type->equals(se->type)) return false; if (elements->dim != se->elements->dim) return false; @@ -4597,61 +4321,18 @@ Expression *StructLiteralExp::semantic(Scope *sc) sd->size(loc); if (sd->sizeok != SIZEOKdone) return new ErrorExp(); - size_t nfields = sd->fields.dim - sd->isNested(); if (arrayExpressionSemantic(elements, sc)) // run semantic() on each element return new ErrorExp(); expandTuples(elements); - size_t offset = 0; - for (size_t i = 0; i < elements->dim; i++) - { - Expression *e = (*elements)[i]; - if (!e) - continue; - - e = resolveProperties(sc, e); - if (i >= nfields) - { - if (i == sd->fields.dim - 1 && sd->isNested() && e->op == TOKnull) - { // CTFE sometimes creates null as hidden pointer; we'll allow this. - continue; - } -#if 0 - for (size_t i = 0; i < sd->fields.dim; i++) - printf("[%d] = %s\n", i, sd->fields[i]->toChars()); -#endif - error("more initializers than fields (%d) of %s", nfields, sd->toChars()); - return new ErrorExp(); - } - VarDeclaration *v = sd->fields[i]; - if (v->offset < offset) - { - error("overlapping initialization for %s", v->toChars()); - return new ErrorExp(); - } - offset = (unsigned)(v->offset + v->type->size()); - Type *telem = v->type; - if (stype) - telem = telem->addMod(stype->mod); - Type *origType = telem; - while (!e->implicitConvTo(telem) && telem->toBasetype()->ty == Tsarray) - { - /* Static array initialization, as in: - * T[3][5] = e; - */ - telem = telem->toBasetype()->nextOf(); - } - - if (!e->implicitConvTo(telem)) - telem = origType; // restore type for better diagnostic - - e = e->implicitCastTo(sc, telem); - if (e->op == TOKerror) - return e; + /* Fit elements[] to the corresponding type of field[]. + */ + if (!sd->fit(loc, sc, elements, stype)) + return new ErrorExp(); - (*elements)[i] = e->isLvalue() ? callCpCtor(sc, e) : valueNoDtor(e); - } + if (!checkFrameAccess(loc, sc, sd, elements->dim)) + return new ErrorExp(); /* Fill out remainder of elements[] with default initializers for fields[] */ @@ -4773,28 +4454,6 @@ int StructLiteralExp::getFieldIndex(Type *type, unsigned offset) return -1; } -void StructLiteralExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring(sd->toChars()); - buf->writeByte('('); - - // CTFE can generate struct literals that contain an AddrExp pointing - // to themselves, need to avoid infinite recursion: - // struct S { this(int){ this.s = &this; } S* s; } - // const foo = new S(0); - if (stageflags & stageToCBuffer) - buf->writestring(""); - else - { - int old = stageflags; - stageflags |= stageToCBuffer; - argsToCBuffer(buf, elements, hgs); - stageflags = old; - } - - buf->writeByte(')'); -} - void StructLiteralExp::toMangleBuffer(OutBuffer *buf) { size_t dim = elements ? elements->dim : 0; @@ -4847,7 +4506,7 @@ Expression *TypeExp::semantic(Scope *sc) Type *t; Dsymbol *s; - type->resolve(loc, sc, &e, &t, &s); + type->resolve(loc, sc, &e, &t, &s, true); if (e) { //printf("e = %s %s\n", Token::toChars(e->op), e->toChars()); @@ -4871,15 +4530,10 @@ Expression *TypeExp::semantic(Scope *sc) return e; } -int TypeExp::rvalue(bool allowVoid) +bool TypeExp::rvalue() { error("type %s has no value", toChars()); - return 0; -} - -void TypeExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - type->toCBuffer(buf, NULL, hgs); + return false; } /************************************************************/ @@ -4912,13 +4566,18 @@ Expression *ScopeExp::semantic(Scope *sc) TemplateInstance *ti = sds->isTemplateInstance(); if (ti) { - if (!ti->findTemplateDeclaration(sc) || + WithScopeSymbol *withsym; + if (!ti->findTempDecl(sc, &withsym) || !ti->semanticTiargs(sc)) { - ti->inst = ti; - ti->inst->errors = true; return new ErrorExp(); } + if (withsym && withsym->withstate->wthis) + { + Expression *e = new VarExp(loc, withsym->withstate->wthis); + e = new DotTemplateInstanceExp(loc, e, ti); + return e->semantic(sc); + } if (ti->needsTypeInference(sc)) { if (TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration()) @@ -4945,24 +4604,22 @@ Expression *ScopeExp::semantic(Scope *sc) } return this; } - unsigned olderrs = global.errors; - if (!ti->semanticRun) - ti->semantic(sc); - if (ti->inst) + ti->semantic(sc); + if (!ti->inst || ti->errors) + return new ErrorExp(); + { - if (ti->inst->errors) - return new ErrorExp(); - Dsymbol *s = ti->inst->toAlias(); + Dsymbol *s = ti->toAlias(); ScopeDsymbol *sds2 = s->isScopeDsymbol(); if (!sds2) { Expression *e; //printf("s = %s, '%s'\n", s->kind(), s->toChars()); - if (ti->withsym && ti->withsym->withstate->wthis) + if (withsym && withsym->withstate->wthis) { // Same as wthis.s - e = new VarExp(loc, ti->withsym->withstate->wthis); + e = new VarExp(loc, withsym->withstate->wthis); e = new DotVarExp(loc, e, s->isDeclaration()); } else @@ -4978,8 +4635,6 @@ Expression *ScopeExp::semantic(Scope *sc) } //printf("sds = %s, '%s'\n", sds->kind(), sds->toChars()); } - if (olderrs != global.errors) - return new ErrorExp(); } else { @@ -4996,28 +4651,6 @@ Expression *ScopeExp::semantic(Scope *sc) return this; } -void ScopeExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - if (sds->isTemplateInstance()) - { - sds->toCBuffer(buf, hgs); - } - else if (hgs != NULL && hgs->ddoc) - { // fixes bug 6491 - Module *module = sds->isModule(); - if (module) - buf->writestring(module->md->toChars()); - else - buf->writestring(sds->toChars()); - } - else - { - buf->writestring(sds->kind()); - buf->writestring(" "); - buf->writestring(sds->toChars()); - } -} - /********************** TemplateExp **************************************/ // Mainly just a placeholder @@ -5030,15 +4663,10 @@ TemplateExp::TemplateExp(Loc loc, TemplateDeclaration *td, FuncDeclaration *fd) this->fd = fd; } -void TemplateExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring(td->toChars()); -} - -int TemplateExp::rvalue(bool allowVoid) +bool TemplateExp::rvalue() { error("template %s has no value", toChars()); - return 0; + return false; } int TemplateExp::isLvalue() @@ -5085,10 +4713,6 @@ Expression *NewExp::syntaxCopy() Expression *NewExp::semantic(Scope *sc) { - Type *tb; - ClassDeclaration *cdthis = NULL; - size_t nargs; - #if LOGSEMANTIC printf("NewExp::semantic() %s\n", toChars()); if (thisexp) @@ -5098,6 +4722,10 @@ Expression *NewExp::semantic(Scope *sc) if (type) // if semantic() already run return this; + Type *tb; + ClassDeclaration *cdthis = NULL; + size_t nargs; + Lagain: if (thisexp) { @@ -5148,7 +4776,8 @@ Expression *NewExp::semantic(Scope *sc) nargs = arguments ? arguments->dim : 0; if (thisexp && tb->ty != Tclass) - { error("e.new is only for allocating nested classes, not %s", tb->toChars()); + { + error("e.new is only for allocating nested classes, not %s", tb->toChars()); goto Lerr; } @@ -5159,13 +4788,16 @@ Expression *NewExp::semantic(Scope *sc) if (cd->scope) cd->semantic(NULL); if (cd->isInterfaceDeclaration()) - { error("cannot create instance of interface %s", cd->toChars()); + { + error("cannot create instance of interface %s", cd->toChars()); goto Lerr; } else if (cd->isAbstract()) - { error("cannot create instance of abstract class %s", cd->toChars()); + { + error("cannot create instance of abstract class %s", cd->toChars()); for (size_t i = 0; i < cd->vtbl.dim; i++) - { FuncDeclaration *fd = cd->vtbl[i]->isFuncDeclaration(); + { + FuncDeclaration *fd = cd->vtbl[i]->isFuncDeclaration(); if (fd && fd->isAbstract()) errorSupplemental(loc, "function '%s' is not implemented", fd->toFullSignature()); } @@ -5173,12 +4805,14 @@ Expression *NewExp::semantic(Scope *sc) } if (cd->noDefaultCtor && !nargs && !cd->defaultCtor) - { error("default construction is disabled for type %s", cd->type->toChars()); + { + error("default construction is disabled for type %s", cd->type->toChars()); goto Lerr; } checkDeprecated(sc, cd); if (cd->isNested()) - { /* We need a 'this' pointer for the nested class. + { + /* We need a 'this' pointer for the nested class. * Ensure we have the right one. */ Dsymbol *s = cd->toParent2(); @@ -5193,7 +4827,8 @@ Expression *NewExp::semantic(Scope *sc) // Supply an implicit 'this' and try again thisexp = new ThisExp(loc); for (Dsymbol *sp = sc->parent; 1; sp = sp->parent) - { if (!sp) + { + if (!sp) { error("outer class %s 'this' needed to 'new' nested class %s", cdn->toChars(), cd->toChars()); goto Lerr; @@ -5213,13 +4848,15 @@ Expression *NewExp::semantic(Scope *sc) { //printf("cdthis = %s\n", cdthis->toChars()); if (cdthis != cdn && !cdn->isBaseOf(cdthis, NULL)) - { error("'this' for nested class must be of type %s, not %s", cdn->toChars(), thisexp->type->toChars()); + { + error("'this' for nested class must be of type %s, not %s", cdn->toChars(), thisexp->type->toChars()); goto Lerr; } } } else if (thisexp) - { error("e.new is only for allocating nested classes"); + { + error("e.new is only for allocating nested classes"); goto Lerr; } else if (fdn) @@ -5235,28 +4872,34 @@ Expression *NewExp::semantic(Scope *sc) error("outer function context of %s is needed to 'new' nested class %s", fdn->toPrettyChars(), cd->toPrettyChars()); goto Lerr; } + else if (FuncLiteralDeclaration *fld = sp->isFuncLiteralDeclaration()) + { + fld->tok = TOKdelegate; + } } } else assert(0); } else if (thisexp) - { error("e.new is only for allocating nested classes"); + { + error("e.new is only for allocating nested classes"); goto Lerr; } - FuncDeclaration *f = NULL; if (cd->ctor) - f = resolveFuncCall(loc, sc, cd->ctor, NULL, tb, arguments, 0); - if (f) { + FuncDeclaration *f = resolveFuncCall(loc, sc, cd->ctor, NULL, tb, arguments, 0); + if (!f || f->errors) + goto Lerr; checkDeprecated(sc, f); checkPurity(sc, f); checkSafety(sc, f); + checkNogc(sc, f); member = f->isCtorDeclaration(); assert(member); - cd->accessCheck(loc, sc, member); + accessCheck(cd, loc, sc, member); TypeFunction *tf = (TypeFunction *)f->type; @@ -5270,7 +4913,8 @@ Expression *NewExp::semantic(Scope *sc) else { if (nargs) - { error("no constructor for %s", cd->toChars()); + { + error("no constructor for %s", cd->toChars()); goto Lerr; } } @@ -5283,8 +4927,8 @@ Expression *NewExp::semantic(Scope *sc) newargs = new Expressions(); newargs->shift(e); - f = resolveFuncCall(loc, sc, cd->aggNew, NULL, tb, newargs); - if (!f) + FuncDeclaration *f = resolveFuncCall(loc, sc, cd->aggNew, NULL, tb, newargs); + if (!f || f->errors) goto Lerr; allocator = f->isNewDeclaration(); assert(allocator); @@ -5298,7 +4942,8 @@ Expression *NewExp::semantic(Scope *sc) else { if (newargs && newargs->dim) - { error("no allocator for %s", cd->toChars()); + { + error("no allocator for %s", cd->toChars()); goto Lerr; } } @@ -5307,10 +4952,12 @@ Expression *NewExp::semantic(Scope *sc) { TypeStruct *ts = (TypeStruct *)tb; StructDeclaration *sd = ts->sym; - if (sd->scope) - sd->semantic(NULL); + sd->size(loc); + if (sd->sizeok != SIZEOKdone) + return new ErrorExp(); if (sd->noDefaultCtor && !nargs) - { error("default construction is disabled for type %s", sd->type->toChars()); + { + error("default construction is disabled for type %s", sd->type->toChars()); goto Lerr; } @@ -5323,7 +4970,7 @@ Expression *NewExp::semantic(Scope *sc) newargs->shift(e); FuncDeclaration *f = resolveFuncCall(loc, sc, sd->aggNew, NULL, tb, newargs); - if (!f) + if (!f || f->errors) goto Lerr; allocator = f->isNewDeclaration(); assert(allocator); @@ -5337,23 +4984,25 @@ Expression *NewExp::semantic(Scope *sc) else { if (newargs && newargs->dim) - { error("no allocator for %s", sd->toChars()); + { + error("no allocator for %s", sd->toChars()); goto Lerr; } } - FuncDeclaration *f = NULL; if (sd->ctor && nargs) - f = resolveFuncCall(loc, sc, sd->ctor, NULL, tb, arguments, 0); - if (f) { + FuncDeclaration *f = resolveFuncCall(loc, sc, sd->ctor, NULL, tb, arguments, 0); + if (!f || f->errors) + goto Lerr; checkDeprecated(sc, f); checkPurity(sc, f); checkSafety(sc, f); + checkNogc(sc, f); member = f->isCtorDeclaration(); assert(member); - sd->accessCheck(loc, sc, member); + accessCheck(sd, loc, sc, member); TypeFunction *tf = (TypeFunction *)f->type; @@ -5364,31 +5013,13 @@ Expression *NewExp::semantic(Scope *sc) if (olderrors != global.errors) return new ErrorExp(); } - else if (nargs) + else { - Type *tptr = type->pointerTo(); - - /* Rewrite: - * new S(arguments) - * as: - * (((S* __newsl = new S()), (*__newsl = S(arguments))), __newsl) - */ - Identifier *id = Lexer::uniqueId("__newsl"); - ExpInitializer *ei = new ExpInitializer(loc, this); - VarDeclaration *v = new VarDeclaration(loc, tptr, id, ei); - v->storage_class |= STCtemp | STCctfe; - Expression *e = new DeclarationExp(loc, v); - Expression *ve = new VarExp(loc, v); - Expression *se = new StructLiteralExp(loc, sd, arguments, type); - Expression *ae = new ConstructExp(loc, new PtrExp(loc, ve), se); - e = new CommaExp(loc, e, ae); - e = new CommaExp(loc, e, ve); - - // rewrite this - this->arguments = NULL; - this->type = tptr; + if (!sd->fit(loc, sc, arguments, tb)) + return new ErrorExp(); - return e->semantic(sc); + if (!sd->fill(loc, arguments, false)) + return new ErrorExp(); } type = type->pointerTo(); @@ -5399,13 +5030,15 @@ Expression *NewExp::semantic(Scope *sc) Dsymbol *s = tn->toDsymbol(sc); AggregateDeclaration *ad = s ? s->isAggregateDeclaration() : NULL; if (ad && ad->noDefaultCtor) - { error("default construction is disabled for type %s", tb->nextOf()->toChars()); + { + error("default construction is disabled for type %s", tb->nextOf()->toChars()); goto Lerr; } for (size_t i = 0; i < nargs; i++) { if (tb->ty != Tarray) - { error("too many arguments for array"); + { + error("too many arguments for array"); goto Lerr; } @@ -5414,7 +5047,8 @@ Expression *NewExp::semantic(Scope *sc) arg = arg->implicitCastTo(sc, Type::tsize_t); arg = arg->optimize(WANTvalue); if (arg->op == TOKint64 && (sinteger_t)arg->toInteger() < 0) - { error("negative array index %s", arg->toChars()); + { + error("negative array index %s", arg->toChars()); goto Lerr; } (*arguments)[i] = arg; @@ -5423,8 +5057,18 @@ Expression *NewExp::semantic(Scope *sc) } else if (tb->isscalar()) { - if (nargs) - { error("no constructor for %s", type->toChars()); + if (!nargs) + { + } + else if (nargs == 1) + { + Expression *e = (*arguments)[0]; + e = e->implicitCastTo(sc, tb); + (*arguments)[0] = e; + } + else + { + error("more than one argument for construction of %s", type->toChars()); goto Lerr; } @@ -5446,29 +5090,6 @@ Expression *NewExp::semantic(Scope *sc) return new ErrorExp(); } - -void NewExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - if (thisexp) - { expToCBuffer(buf, hgs, thisexp, PREC_primary); - buf->writeByte('.'); - } - buf->writestring("new "); - if (newargs && newargs->dim) - { - buf->writeByte('('); - argsToCBuffer(buf, newargs, hgs); - buf->writeByte(')'); - } - newtype->toCBuffer(buf, NULL, hgs); - if (arguments && arguments->dim) - { - buf->writeByte('('); - argsToCBuffer(buf, arguments, hgs); - buf->writeByte(')'); - } -} - /********************** NewAnonClassExp **************************************/ NewAnonClassExp::NewAnonClassExp(Loc loc, Expression *thisexp, @@ -5500,11 +5121,10 @@ Expression *NewAnonClassExp::semantic(Scope *sc) #endif Expression *d = new DeclarationExp(loc, cd); - sc = sc->startCTFE(); // just create new scope + sc = sc->push(); // just create new scope sc->flags &= ~SCOPEctfe; // temporary stop CTFE d = d->semantic(sc); - sc->flags |= SCOPEctfe; - sc = sc->endCTFE(); + sc = sc->pop(); Expression *n = new NewExp(loc, thisexp, newargs, cd->type, arguments); @@ -5512,34 +5132,6 @@ Expression *NewAnonClassExp::semantic(Scope *sc) return c->semantic(sc); } - -void NewAnonClassExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - if (thisexp) - { expToCBuffer(buf, hgs, thisexp, PREC_primary); - buf->writeByte('.'); - } - buf->writestring("new"); - if (newargs && newargs->dim) - { - buf->writeByte('('); - argsToCBuffer(buf, newargs, hgs); - buf->writeByte(')'); - } - buf->writestring(" class "); - if (arguments && arguments->dim) - { - buf->writeByte('('); - argsToCBuffer(buf, arguments, hgs); - buf->writeByte(')'); - } - //buf->writestring(" { }"); - if (cd) - { - cd->toCBuffer(buf, hgs); - } -} - /********************** SymbolExp **************************************/ SymbolExp::SymbolExp(Loc loc, TOK op, int size, Declaration *var, bool hasOverloads) @@ -5600,16 +5192,6 @@ void SymOffExp::checkEscape() } } -void SymOffExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - if (offset) - buf->printf("(& %s+%u)", var->toChars(), offset); - else if (var->isTypeInfoDeclaration()) - buf->printf("%s", var->toChars()); - else - buf->printf("& %s", var->toChars()); -} - /******************************** VarExp **************************/ VarExp::VarExp(Loc loc, Declaration *var, bool hasOverloads) @@ -5646,10 +5228,10 @@ Expression *VarExp::semantic(Scope *sc) #if LOGSEMANTIC printf("VarExp::semantic(%s)\n", toChars()); #endif - if (FuncDeclaration *f = var->isFuncDeclaration()) + if (FuncDeclaration *fd = var->isFuncDeclaration()) { //printf("L%d fd = %s\n", __LINE__, f->toChars()); - if (!f->functionSemantic()) + if (!fd->functionSemantic()) return new ErrorExp(); } @@ -5665,16 +5247,19 @@ Expression *VarExp::semantic(Scope *sc) */ //accessCheck(loc, sc, NULL, var); - VarDeclaration *v = var->isVarDeclaration(); - if (v) + if (VarDeclaration *vd = var->isVarDeclaration()) { hasOverloads = 0; - v->checkNestedReference(sc, loc); - checkPurity(sc, v); + vd->checkNestedReference(sc, loc); + } + else if (FuncDeclaration *fd = var->isFuncDeclaration()) + { + fd->checkNestedReference(sc, loc); + } + else if (OverDeclaration *od = var->isOverDeclaration()) + { + type = Type::tvoid; // ambiguous type? } - FuncDeclaration *f = var->isFuncDeclaration(); - if (f) - f->checkNestedReference(sc, loc); return this; } @@ -5684,11 +5269,6 @@ char *VarExp::toChars() return var->toChars(); } -void VarExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring(var->toChars()); -} - void VarExp::checkEscape() { VarDeclaration *v = var->isVarDeclaration(); @@ -5754,7 +5334,7 @@ Expression *VarExp::modifiableLvalue(Scope *sc, Expression *e) //printf("VarExp::modifiableLvalue('%s')\n", var->toChars()); if (var->storage_class & STCmanifest) { - error("Cannot modify '%s'", toChars()); + error("cannot modify manifest constant '%s'", toChars()); return new ErrorExp(); } // See if this expression is a modifiable lvalue (i.e. not const) @@ -5782,12 +5362,6 @@ Expression *OverExp::toLvalue(Scope *sc, Expression *e) return this; } -void OverExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring(vars->ident->toChars()); -} - - /******************************** TupleExp **************************/ TupleExp::TupleExp(Loc loc, Expression *e0, Expressions *exps) @@ -5893,7 +5467,8 @@ Expression *TupleExp::semantic(Scope *sc) } else if (e->op == TOKerror) err = true; - (*exps)[i] = e; + else + (*exps)[i] = e; } if (err) return new ErrorExp(); @@ -5905,25 +5480,6 @@ Expression *TupleExp::semantic(Scope *sc) return this; } -void TupleExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - if (e0) - { - buf->writeByte('('); - e0->toCBuffer(buf, hgs); - buf->writestring(", tuple("); - argsToCBuffer(buf, exps, hgs); - buf->writestring("))"); - } - else - { - buf->writestring("tuple("); - argsToCBuffer(buf, exps, hgs); - buf->writeByte(')'); - } -} - - void TupleExp::checkEscape() { for (size_t i = 0; i < exps->dim; i++) @@ -5943,6 +5499,16 @@ FuncExp::FuncExp(Loc loc, FuncLiteralDeclaration *fd, TemplateDeclaration *td) assert(fd->fbody); } +bool FuncExp::rvalue() +{ + if (td) + { + error("template lambda has no value"); + return false; + } + return true; +} + void FuncExp::genIdent(Scope *sc) { if (fd->ident == Id::empty) @@ -5956,24 +5522,32 @@ void FuncExp::genIdent(Scope *sc) DsymbolTable *symtab; if (FuncDeclaration *func = sc->parent->isFuncDeclaration()) { - symtab = func->localsymtab; - if (symtab) + if (func->localsymtab == NULL) { // Inside template constraint, symtab is not set yet. - goto L1; + // Initialize it lazily. + func->localsymtab = new DsymbolTable(); } + symtab = func->localsymtab; } else { - symtab = sc->parent->isScopeDsymbol()->symtab; - L1: - assert(symtab); - int num = (int)_aaLen(symtab->tab) + 1; - Identifier *id = Lexer::uniqueId(s, num); - fd->ident = id; - if (td) td->ident = id; - symtab->insert(td ? (Dsymbol *)td : (Dsymbol *)fd); + ScopeDsymbol *sds = sc->parent->isScopeDsymbol(); + if (!sds->symtab) + { + // Inside template constraint, symtab may not be set yet. + // Initialize it lazily. + assert(sds->isTemplateInstance()); + sds->symtab = new DsymbolTable(); + } + symtab = sds->symtab; } + assert(symtab); + int num = (int)dmd_aaLen(symtab->tab) + 1; + Identifier *id = Lexer::uniqueId(s, num); + fd->ident = id; + if (td) td->ident = id; + symtab->insert(td ? (Dsymbol *)td : (Dsymbol *)fd); } } @@ -6004,8 +5578,9 @@ Expression *FuncExp::semantic(Scope *sc) #endif Expression *e = this; - sc = sc->startCTFE(); // just create new scope + sc = sc->push(); // just create new scope sc->flags &= ~SCOPEctfe; // temporary stop CTFE + sc->protection = PROTpublic; // Bugzilla 12506 if (!type || type == Type::tvoid) { @@ -6021,12 +5596,14 @@ Expression *FuncExp::semantic(Scope *sc) // Set target of return type inference if (fd->treq && !fd->type->nextOf()) - { TypeFunction *tfv = NULL; + { + TypeFunction *tfv = NULL; if (fd->treq->ty == Tdelegate || (fd->treq->ty == Tpointer && fd->treq->nextOf()->ty == Tfunction)) tfv = (TypeFunction *)fd->treq->nextOf(); if (tfv) - { TypeFunction *tfl = (TypeFunction *)fd->type; + { + TypeFunction *tfl = (TypeFunction *)fd->type; tfl->next = tfv->nextOf(); } } @@ -6038,34 +5615,31 @@ Expression *FuncExp::semantic(Scope *sc) td->semantic(sc); type = Type::tvoid; // temporary type - if (fd->treq) // defer type determination - e = inferType(fd->treq); + if (fd->treq) // defer type determination + { + FuncExp *fe; + if (matchType(fd->treq, sc, &fe) > MATCHnomatch) + e = fe; + else + e = new ErrorExp(); + } goto Ldone; } unsigned olderrors = global.errors; fd->semantic(sc); - //fd->parent = sc->parent; - if (olderrors != global.errors) - { - } - else + if (olderrors == global.errors) { fd->semantic2(sc); - if ( (olderrors == global.errors) || - // need to infer return type - (fd->type && fd->type->ty == Tfunction && !fd->type->nextOf())) - { + if (olderrors == global.errors) fd->semantic3(sc); - } } - - // need to infer return type if (olderrors != global.errors) { if (fd->type && fd->type->ty == Tfunction && !fd->type->nextOf()) ((TypeFunction *)fd->type)->next = Type::terror; - return new ErrorExp(); + e = new ErrorExp(); + goto Ldone; } // Type is a "delegate to" or "pointer to" the function literal @@ -6092,7 +5666,8 @@ Expression *FuncExp::semantic(Scope *sc) * So, should keep fd->tok == TOKreserve if fd->treq == NULL. */ if (fd->treq && fd->treq->ty == Tpointer) - { // change to non-nested + { + // change to non-nested fd->tok = TOKfunction; fd->vthis = NULL; } @@ -6100,13 +5675,176 @@ Expression *FuncExp::semantic(Scope *sc) fd->tookAddressOf++; } Ldone: - sc->flags |= SCOPEctfe; - sc = sc->endCTFE(); + sc = sc->pop(); return e; } -// used from CallExp::semantic() -Expression *FuncExp::semantic(Scope *sc, Expressions *arguments) +MATCH FuncExp::matchType(Type *to, Scope *sc, FuncExp **presult, int flag) +{ + //printf("FuncExp::matchType('%s'), to=%s\n", type ? type->toChars() : "null", to->toChars()); + if (presult) + *presult = NULL; + + TypeFunction *tof = NULL; + if (to->ty == Tdelegate) + { + if (tok == TOKfunction) + { + if (!flag) + error("cannot match function literal to delegate type '%s'", to->toChars()); + return MATCHnomatch; + } + tof = (TypeFunction *)to->nextOf(); + } + else if (to->ty == Tpointer && to->nextOf()->ty == Tfunction) + { + if (tok == TOKdelegate) + { + if (!flag) + error("cannot match delegate literal to function pointer type '%s'", to->toChars()); + return MATCHnomatch; + } + tof = (TypeFunction *)to->nextOf(); + } + + if (td) + { + if (!tof) + { + L1: + if (!flag) + error("cannot infer parameter types from %s", to->toChars()); + return MATCHnomatch; + } + + // Parameter types inference from 'tof' + assert(td->scope); + TypeFunction *tf = (TypeFunction *)fd->type; + //printf("\ttof = %s\n", tof->toChars()); + //printf("\ttf = %s\n", tf->toChars()); + size_t dim = Parameter::dim(tf->parameters); + + if (Parameter::dim(tof->parameters) != dim || + tof->varargs != tf->varargs) + goto L1; + + Objects *tiargs = new Objects(); + tiargs->reserve(td->parameters->dim); + + for (size_t i = 0; i < td->parameters->dim; i++) + { + TemplateParameter *tp = (*td->parameters)[i]; + size_t u = 0; + for (; u < dim; u++) + { + Parameter *p = Parameter::getNth(tf->parameters, u); + if (p->type->ty == Tident && + ((TypeIdentifier *)p->type)->ident == tp->ident) + { + break; + } + } + assert(u < dim); + Parameter *pto = Parameter::getNth(tof->parameters, u); + Type *t = pto->type; + if (t->ty == Terror) + goto L1; + tiargs->push(t); + } + + // Set target of return type inference + if (!tf->next && tof->next) + fd->treq = to; + + TemplateInstance *ti = new TemplateInstance(loc, td, tiargs); + Expression *ex = (new ScopeExp(loc, ti))->semantic(td->scope); + + // Reset inference target for the later re-semantic + fd->treq = NULL; + + if (ex->op == TOKerror) + return MATCHnomatch; + if (ex->op != TOKfunction) + goto L1; + return ((FuncExp *)ex)->matchType(to, sc, presult, flag); + } + + if (!tof || !tof->next) + return MATCHnomatch; + + assert(type && type != Type::tvoid); + TypeFunction *tfx = (TypeFunction *)fd->type; + bool convertMatch = (type->ty != to->ty); + + if (fd->inferRetType && tfx->next->implicitConvTo(tof->next) == MATCHconvert) + { + /* If return type is inferred and covariant return, + * tweak return statements to required return type. + * + * interface I {} + * class C : Object, I{} + * + * I delegate() dg = delegate() { return new class C(); } + */ + convertMatch = true; + + TypeFunction *tfy = new TypeFunction(tfx->parameters, tof->next, tfx->varargs, tfx->linkage, STCundefined); + tfy->mod = tfx->mod; + tfy->isnothrow = tfx->isnothrow; + tfy->isnogc = tfx->isnogc; + tfy->purity = tfx->purity; + tfy->isproperty = tfx->isproperty; + tfy->isref = tfx->isref; + tfy->iswild = tfx->iswild; + tfy->deco = tfy->merge()->deco; + + tfx = tfy; + } + + Type *tx; + if (tok == TOKdelegate || + tok == TOKreserved && (type->ty == Tdelegate || + type->ty == Tpointer && to->ty == Tdelegate)) + { + // Allow conversion from implicit function pointer to delegate + tx = new TypeDelegate(tfx); + tx->deco = tx->merge()->deco; + } + else + { + assert(tok == TOKfunction || + tok == TOKreserved && type->ty == Tpointer); + tx = tfx->pointerTo(); + } + //printf("\ttx = %s, to = %s\n", tx->toChars(), to->toChars()); + + MATCH m = tx->implicitConvTo(to); + if (m > MATCHnomatch) + { + // MATCHexact: exact type match + // MATCHconst: covairiant type match (eg. attributes difference) + // MATCHconvert: context conversion + m = convertMatch ? MATCHconvert : tx->equals(to) ? MATCHexact : MATCHconst; + + if (presult) + { + (*presult) = (FuncExp *)copy(); + (*presult)->type = to; + + // Bugzilla 12508: Tweak function body for covariant returns. + (*presult)->fd->modifyReturns(sc, tof->next); + } + } + else if (!flag) + { + error("cannot implicitly convert expression (%s) of type %s to %s", + toChars(), tx->toChars(), to->toChars()); + } + return m; +} + +// used from CallExp::semantic() +Expression *FuncExp::semantic(Scope *sc, Expressions *arguments) { if ((!type || type == Type::tvoid) && td && arguments && arguments->dim) { @@ -6164,13 +5902,6 @@ char *FuncExp::toChars() return fd->toChars(); } -void FuncExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - fd->toCBuffer(buf, hgs); - //buf->writestring(fd->toChars()); -} - - /******************************** DeclarationExp **************************/ DeclarationExp::DeclarationExp(Loc loc, Dsymbol *declaration) @@ -6237,11 +5968,11 @@ Expression *DeclarationExp::semantic(Scope *sc) } else if (sc->func) { + // Bugzilla 11720 - include Dataseg variables if ((s->isFuncDeclaration() || - s->isTypedefDeclaration() || s->isAggregateDeclaration() || s->isEnumDeclaration() || - v && v->isDataseg()) && // Bugzilla 11720 + v && v->isDataseg()) && !sc->func->localsymtab->insert(s)) { error("declaration %s is already defined in another scope in %s", @@ -6268,9 +5999,9 @@ Expression *DeclarationExp::semantic(Scope *sc) if (!s->isVarDeclaration()) { Scope *sc2 = sc; - if (sc2->stc & (STCpure | STCnothrow)) + if (sc2->stc & (STCpure | STCnothrow | STCnogc)) sc2 = sc->push(); - sc2->stc &= ~(STCpure | STCnothrow); + sc2->stc &= ~(STCpure | STCnothrow | STCnogc); declaration->semantic(sc2); if (sc2 != sc) sc2->pop(); @@ -6289,13 +6020,6 @@ Expression *DeclarationExp::semantic(Scope *sc) return this; } - -void DeclarationExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - declaration->toCBuffer(buf, hgs); -} - - /************************ TypeidExp ************************************/ /* @@ -6316,8 +6040,7 @@ Expression *TypeidExp::syntaxCopy() Expression *TypeidExp::semantic(Scope *sc) -{ Expression *e; - +{ #if LOGSEMANTIC printf("TypeidExp::semantic() %s\n", toChars()); #endif @@ -6351,6 +6074,7 @@ Expression *TypeidExp::semantic(Scope *sc) return new ErrorExp(); } + Expression *e; if (ea && ta->toBasetype()->ty == Tclass) { /* Get the dynamic type, which is .classinfo @@ -6378,13 +6102,6 @@ Expression *TypeidExp::semantic(Scope *sc) return e; } -void TypeidExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("typeid("); - ObjectToCBuffer(buf, hgs, obj); - buf->writeByte(')'); -} - /************************ TraitsExp ************************************/ /* * __traits(identifier, args...) @@ -6403,21 +6120,9 @@ Expression *TraitsExp::syntaxCopy() return new TraitsExp(loc, ident, TemplateInstance::arraySyntaxCopy(args)); } - -void TraitsExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +Expression *TraitsExp::semantic(Scope *sc) { - buf->writestring("__traits("); - buf->writestring(ident->toChars()); - if (args) - { - for (size_t i = 0; i < args->dim; i++) - { - buf->writestring(", ");; - RootObject *oarg = (*args)[i]; - ObjectToCBuffer(buf, hgs, oarg); - } - } - buf->writeByte(')'); + return semanticTraits(this, sc); } /************************************************************/ @@ -6436,12 +6141,6 @@ Expression *HaltExp::semantic(Scope *sc) return this; } - -void HaltExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("halt"); -} - /************************************************************/ IsExp::IsExp(Loc loc, Type *targ, Identifier *id, TOK tok, @@ -6480,8 +6179,7 @@ Expression *IsExp::syntaxCopy() } Expression *IsExp::semantic(Scope *sc) -{ Type *tded; - +{ /* is(targ id tok tspec) * is(targ id : tok2) * is(targ id == tok2) @@ -6489,10 +6187,12 @@ Expression *IsExp::semantic(Scope *sc) //printf("IsExp::semantic(%s)\n", toChars()); if (id && !(sc->flags & (SCOPEstaticif | SCOPEstaticassert))) - { error("can only declare type aliases within static if conditionals or static asserts"); + { + error("can only declare type aliases within static if conditionals or static asserts"); return new ErrorExp(); } + Type *tded = NULL; Type *t = targ->trySemantic(loc, sc); if (!t) goto Lno; // errors, so condition is false @@ -6502,10 +6202,7 @@ Expression *IsExp::semantic(Scope *sc) switch (tok2) { case TOKtypedef: - if (targ->ty != Ttypedef) - goto Lno; - tded = ((TypeTypedef *)targ)->sym->basetype; - break; + goto Lno; case TOKstruct: if (targ->ty != Tstruct) @@ -6652,7 +6349,7 @@ Expression *IsExp::semantic(Scope *sc) * The results of this are highly platform dependent, and intended * primarly for use in implementing va_arg(). */ - tded = targ->toArgTypes(); + tded = toArgTypes(targ); if (!tded) goto Lno; // not valid for a parameter break; @@ -6703,7 +6400,7 @@ Expression *IsExp::semantic(Scope *sc) dedtypes.setDim(parameters->dim); dedtypes.zero(); - MATCH m = targ->deduceType(sc, tspec, parameters, &dedtypes); + MATCH m = deduceType(targ, sc, tspec, parameters, &dedtypes); //printf("targ: %s\n", targ->toChars()); //printf("tspec: %s\n", tspec->toChars()); if (m <= MATCHnomatch || @@ -6730,8 +6427,8 @@ Expression *IsExp::semantic(Scope *sc) if (m <= MATCHnomatch) goto Lno; s->semantic(sc); - if (sc->sd) - s->addMember(sc, sc->sd, 1); + if (sc->sds) + s->addMember(sc, sc->sds, 1); else if (!sc->insert(s)) error("declaration %s is already defined", s->toChars()); } @@ -6762,8 +6459,8 @@ Expression *IsExp::semantic(Scope *sc) */ if (!tup && !sc->insert(s)) error("declaration %s is already defined", s->toChars()); - if (sc->sd) - s->addMember(sc, sc->sd, 1); + if (sc->sds) + s->addMember(sc, sc->sds, 1); } //printf("Lyes\n"); return new IntegerExp(loc, 1, Type::tbool); @@ -6773,35 +6470,6 @@ Expression *IsExp::semantic(Scope *sc) return new IntegerExp(loc, 0, Type::tbool); } -void IsExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("is("); - targ->toCBuffer(buf, id, hgs); - if (tok2 != TOKreserved) - { - buf->printf(" %s %s", Token::toChars(tok), Token::toChars(tok2)); - } - else if (tspec) - { - if (tok == TOKcolon) - buf->writestring(" : "); - else - buf->writestring(" == "); - tspec->toCBuffer(buf, NULL, hgs); - } - if (parameters) - { - for (size_t i = 0; i < parameters->dim; i++) - { - buf->writestring(", "); - TemplateParameter *tp = (*parameters)[i]; - tp->toCBuffer(buf, hgs); - } - } - buf->writeByte(')'); -} - - /************************************************************/ UnaExp::UnaExp(Loc loc, TOK op, int size, Expression *e1) @@ -6819,15 +6487,20 @@ Expression *UnaExp::syntaxCopy() return e; } -Expression *UnaExp::semantic(Scope *sc) +/************************** + * Helper function for easy error propagation. + * If error occurs, returns ErrorExp. Otherwise returns NULL. + */ +Expression *UnaExp::unaSemantic(Scope *sc) { #if LOGSEMANTIC printf("UnaExp::semantic('%s')\n", toChars()); #endif - e1 = e1->semantic(sc); -// if (!e1->type) -// error("%s has no value", e1->toChars()); - return this; + Expression *e1x = e1->semantic(sc); + if (e1x->op == TOKerror) + return e1x; + e1 = e1x; + return NULL; } Expression *UnaExp::resolveLoc(Loc loc, Scope *sc) @@ -6836,12 +6509,6 @@ Expression *UnaExp::resolveLoc(Loc loc, Scope *sc) return this; } -void UnaExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring(Token::toChars(op)); - expToCBuffer(buf, hgs, e1, precedence[op]); -} - /************************************************************/ BinExp::BinExp(Loc loc, TOK op, int size, Expression *e1, Expression *e2) @@ -6863,27 +6530,41 @@ Expression *BinExp::syntaxCopy() return e; } -Expression *BinExp::semantic(Scope *sc) +/************************** + * Helper function for easy error propagation. + * If error occurs, returns ErrorExp. Otherwise returns NULL. + */ +Expression *BinExp::binSemantic(Scope *sc) { #if LOGSEMANTIC printf("BinExp::semantic('%s')\n", toChars()); #endif - e1 = e1->semantic(sc); - e2 = e2->semantic(sc); - if (e1->op == TOKerror || e2->op == TOKerror) - return new ErrorExp(); - return this; + Expression *e1x = e1->semantic(sc); + Expression *e2x = e2->semantic(sc); + if (e1x->op == TOKerror) + return e1x; + if (e2x->op == TOKerror) + return e2x; + e1 = e1x; + e2 = e2x; + return NULL; } -Expression *BinExp::semanticp(Scope *sc) +Expression *BinExp::binSemanticProp(Scope *sc) { - BinExp::semantic(sc); - e1 = resolveProperties(sc, e1); - e2 = resolveProperties(sc, e2); - return this; + if (Expression *ex = binSemantic(sc)) + return ex; + Expression *e1x = resolveProperties(sc, e1); + Expression *e2x = resolveProperties(sc, e2); + if (e1x->op == TOKerror) + return e1x; + if (e2x->op == TOKerror) + return e2x; + e1 = e1x; + e2 = e2x; + return NULL; } - Expression *BinExp::checkComplexOpAssign(Scope *sc) { // generate an error if this is a nonsensical *=,/=, or %=, eg real *= imaginary @@ -6892,23 +6573,26 @@ Expression *BinExp::checkComplexOpAssign(Scope *sc) // Any multiplication by an imaginary or complex number yields a complex result. // r *= c, i*=c, r*=i, i*=i are all forbidden operations. const char *opstr = Token::toChars(op); - if ( e1->type->isreal() && e2->type->iscomplex()) + if (e1->type->isreal() && e2->type->iscomplex()) { error("%s %s %s is undefined. Did you mean %s %s %s.re ?", e1->type->toChars(), opstr, e2->type->toChars(), e1->type->toChars(), opstr, e2->type->toChars()); + return new ErrorExp(); } else if (e1->type->isimaginary() && e2->type->iscomplex()) { error("%s %s %s is undefined. Did you mean %s %s %s.im ?", e1->type->toChars(), opstr, e2->type->toChars(), e1->type->toChars(), opstr, e2->type->toChars()); + return new ErrorExp(); } else if ((e1->type->isreal() || e1->type->isimaginary()) && e2->type->isimaginary()) { error("%s %s %s is an undefined operation", e1->type->toChars(), opstr, e2->type->toChars()); + return new ErrorExp(); } } @@ -6917,12 +6601,12 @@ Expression *BinExp::checkComplexOpAssign(Scope *sc) { // Addition or subtraction of a real and an imaginary is a complex result. // Thus, r+=i, r+=c, i+=r, i+=c are all forbidden operations. - if ( (e1->type->isreal() && (e2->type->isimaginary() || e2->type->iscomplex())) || - (e1->type->isimaginary() && (e2->type->isreal() || e2->type->iscomplex())) - ) + if ((e1->type->isreal() && (e2->type->isimaginary() || e2->type->iscomplex())) || + (e1->type->isimaginary() && (e2->type->isreal() || e2->type->iscomplex()))) { error("%s %s %s is undefined (result is complex)", e1->type->toChars(), Token::toChars(op), e2->type->toChars()); + return new ErrorExp(); } if (type->isreal() || type->isimaginary()) { @@ -6960,13 +6644,15 @@ Expression *BinExp::checkComplexOpAssign(Scope *sc) } } } - } else if (op == TOKdivass) + } + else if (op == TOKdivass) { if (e2->type->isimaginary()) { Type *t1 = e1->type; if (t1->isreal()) - { // x/iv = i(-x/v) + { + // x/iv = i(-x/v) // Therefore, the result is 0 e2 = new CommaExp(loc, e2, new RealExp(loc, ldouble(0.0), t1)); e2->type = t1; @@ -6975,8 +6661,8 @@ Expression *BinExp::checkComplexOpAssign(Scope *sc) return e; } else if (t1->isimaginary()) - { Type *t2; - + { + Type *t2; switch (t1->ty) { case Timaginary32: t2 = Type::tfloat32; break; @@ -6991,7 +6677,8 @@ Expression *BinExp::checkComplexOpAssign(Scope *sc) return e; } } - } else if (op == TOKmodass) + } + else if (op == TOKmodass) { if (e2->type->iscomplex()) { @@ -7002,15 +6689,6 @@ Expression *BinExp::checkComplexOpAssign(Scope *sc) return this; } -void BinExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - expToCBuffer(buf, hgs, e1, precedence[op]); - buf->writeByte(' '); - buf->writestring(Token::toChars(op)); - buf->writeByte(' '); - expToCBuffer(buf, hgs, e2, (PREC)(precedence[op] + 1)); -} - int BinExp::isunsigned() { return e1->type->isunsigned() || e2->type->isunsigned(); @@ -7032,8 +6710,8 @@ Expression *BinExp::incompatibleTypes() else { error("incompatible types for ((%s) %s (%s)): '%s' and '%s'", - e1->toChars(), Token::toChars(thisOp), e2->toChars(), - e1->type->toChars(), e2->type->toChars()); + e1->toChars(), Token::toChars(thisOp), e2->toChars(), + e1->type->toChars(), e2->type->toChars()); } return new ErrorExp(); } @@ -7044,29 +6722,31 @@ Expression *BinExp::incompatibleTypes() Expression *BinAssignExp::semantic(Scope *sc) { - Expression *e; - if (type) return this; - e = op_overload(sc); + Expression *e = op_overload(sc); + if (e) + return e; + + e = e1->checkReadModifyWrite(op, e2); if (e) return e; if (e1->op == TOKarraylength) { + // arr.length op= e2; e = ArrayLengthExp::rewriteOpAssign(this); e = e->semantic(sc); return e; } - else if (e1->op == TOKslice || e1->type->ty == Tarray || e1->type->ty == Tsarray) + if (e1->op == TOKslice || e1->type->ty == Tarray || e1->type->ty == Tsarray) { // T[] op= ... - e = typeCombine(sc); - if (e->op == TOKerror) - return e; + if (Expression *ex = typeCombine(this, sc)) + return ex; type = e1->type; - return arrayOp(sc); + return arrayOp(this, sc); } e1 = e1->semantic(sc); @@ -7088,9 +6768,11 @@ Expression *BinAssignExp::semantic(Scope *sc) if ((op == TOKaddass || op == TOKminass) && e1->type->toBasetype()->ty == Tpointer && e2->type->toBasetype()->isintegral()) - return scaleFactor(sc); + return scaleFactor(this, sc); + + if (Expression *ex = typeCombine(this, sc)) + return ex; - typeCombine(sc); if (arith) { e1 = e1->checkArithmetic(); @@ -7126,8 +6808,12 @@ Expression *BinAssignExp::semantic(Scope *sc) if (e1->op == TOKerror || e2->op == TOKerror) return new ErrorExp(); - checkComplexOpAssign(sc); - return reorderSettingAAElem(sc); + e = checkComplexOpAssign(sc); + if (e->op == TOKerror) + return e; + + assert(e->op == TOKassign || e == this); + return ((BinExp *)e)->reorderSettingAAElem(sc); } int BinAssignExp::isLvalue() @@ -7207,7 +6893,7 @@ Expression *CompileExp::semantic(Scope *sc) return new ErrorExp(); } e1 = e1->ctfeInterpret(); - StringExp *se = e1->toString(); + StringExp *se = e1->toStringExp(); if (!se) { error("argument to mixin must be a string, not (%s)", e1->toChars()); return new ErrorExp(); @@ -7227,13 +6913,6 @@ Expression *CompileExp::semantic(Scope *sc) return e->semantic(sc); } -void CompileExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("mixin("); - expToCBuffer(buf, hgs, e1, PREC_assign); - buf->writeByte(')'); -} - /************************************************************/ FileExp::FileExp(Loc loc, Expression *e) @@ -7317,13 +6996,6 @@ Expression *FileExp::semantic(Scope *sc) return new ErrorExp(); } -void FileExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("import("); - expToCBuffer(buf, hgs, e1, PREC_assign); - buf->writeByte(')'); -} - /************************************************************/ AssertExp::AssertExp(Loc loc, Expression *e, Expression *msg) @@ -7344,7 +7016,8 @@ Expression *AssertExp::semantic(Scope *sc) #if LOGSEMANTIC printf("AssertExp::semantic('%s')\n", toChars()); #endif - UnaExp::semantic(sc); + if (Expression *ex = unaSemantic(sc)) + return ex; e1 = resolveProperties(sc, e1); // BUG: see if we can do compile time elimination of the Assert e1 = e1->optimize(WANTvalue); @@ -7377,19 +7050,6 @@ Expression *AssertExp::semantic(Scope *sc) return this; } - -void AssertExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("assert("); - expToCBuffer(buf, hgs, e1, PREC_assign); - if (msg) - { - buf->writestring(", "); - expToCBuffer(buf, hgs, msg, PREC_assign); - } - buf->writeByte(')'); -} - /************************************************************/ DotIdExp::DotIdExp(Loc loc, Expression *e, Identifier *ident) @@ -7435,12 +7095,12 @@ Expression *DotIdExp::semantic(Scope *sc) Expression *DotIdExp::semanticX(Scope *sc) { //printf("DotIdExp::semanticX(this = %p, '%s')\n", this, toChars()); - UnaExp::semantic(sc); - if (e1->op == TOKerror) - return e1; + if (Expression *ex = unaSemantic(sc)) + return ex; if (ident == Id::mangleof) - { // symbol.mangleof + { + // symbol.mangleof Dsymbol *ds; switch (e1->op) { @@ -7455,9 +7115,24 @@ Expression *DotIdExp::semanticX(Scope *sc) goto L1; case TOKoverloadset: ds = ((OverExp *)e1)->vars; + goto L1; + case TOKtemplate: + { + TemplateExp *te = (TemplateExp *)e1; + ds = te->fd ? (Dsymbol *)te->fd : te->td; + } L1: { - const char* s = ds->mangle(); + assert(ds); + if (FuncDeclaration *f = ds->isFuncDeclaration()) + { + if (!f->type->deco) + { + error("forward reference to %s", f->toChars()); + return new ErrorExp(); + } + } + const char* s = mangle(ds); Expression *e = new StringExp(loc, (void*)s, strlen(s), 'c'); e = e->semantic(sc); return e; @@ -7467,6 +7142,12 @@ Expression *DotIdExp::semanticX(Scope *sc) } } + if (e1->op == TOKvar && e1->type->toBasetype()->ty == Tsarray && ident == Id::length) + { + // bypass checkPurity + return e1->type->dotExp(sc, e1, ident, 0); + } + if (e1->op == TOKdotexp) { } @@ -7487,7 +7168,7 @@ Expression *DotIdExp::semanticX(Scope *sc) (*exps)[i] = e; } // Don't evaluate te->e0 in runtime - Expression *e = new TupleExp(loc, /*te->e0*/NULL, exps); + Expression *e = new TupleExp(loc, NULL, exps); e = e->semantic(sc); return e; } @@ -7643,6 +7324,16 @@ Expression *DotIdExp::semanticY(Scope *sc, int flag) } return e; } + if (OverDeclaration *od = s->isOverDeclaration()) + { + e = new VarExp(loc, od, 1); + if (eleft) + { + e = new CommaExp(loc, eleft, e); + e->type = Type::tvoid; // ambiguous type? + } + return e; + } OverloadSet *o = s->isOverloadSet(); if (o) { //printf("'%s' is an overload set\n", o->toChars()); @@ -7717,12 +7408,21 @@ Expression *DotIdExp::semanticY(Scope *sc, int flag) ident != Id::init && ident != Id::__sizeof && ident != Id::__xalignof && ident != Id::offsetof && ident != Id::mangleof && ident != Id::stringof) - { /* Rewrite: + { + Type *t1bn = t1b->nextOf(); + if (flag) + { + AggregateDeclaration *ad = isAggregate(t1bn); + if (ad && !ad->members) // Bugzilla 11312 + return NULL; + } + + /* Rewrite: * p.ident * as: * (*p).ident */ - if (flag && t1b->nextOf()->ty == Tvoid) + if (flag && t1bn->ty == Tvoid) return NULL; e = new PtrExp(loc, e1); e = e->semantic(sc); @@ -7739,14 +7439,6 @@ Expression *DotIdExp::semanticY(Scope *sc, int flag) } } -void DotIdExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - //printf("DotIdExp::toCBuffer()\n"); - expToCBuffer(buf, hgs, e1, PREC_primary); - buf->writeByte('.'); - buf->writestring(ident->toChars()); -} - /********************** DotTemplateExp ***********************************/ // Mainly just a placeholder @@ -7758,14 +7450,13 @@ DotTemplateExp::DotTemplateExp(Loc loc, Expression *e, TemplateDeclaration *td) this->td = td; } -void DotTemplateExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +Expression *DotTemplateExp::semantic(Scope *sc) { - expToCBuffer(buf, hgs, e1, PREC_primary); - buf->writeByte('.'); - buf->writestring(td->toChars()); + if (Expression *ex = unaSemantic(sc)) + return ex; + return this; } - /************************************************************/ DotVarExp::DotVarExp(Loc loc, Expression *e, Declaration *v, bool hasOverloads) @@ -7781,133 +7472,135 @@ Expression *DotVarExp::semantic(Scope *sc) #if LOGSEMANTIC printf("DotVarExp::semantic('%s')\n", toChars()); #endif - if (!type) - { - var = var->toAlias()->isDeclaration(); + if (type) + return this; - TupleDeclaration *tup = var->isTupleDeclaration(); - if (tup) - { /* Replace: - * e1.tuple(a, b, c) - * with: - * tuple(e1.a, e1.b, e1.c) - */ - e1 = e1->semantic(sc); - Expressions *exps = new Expressions; - Expression *e0 = NULL; - Expression *ev = e1; - if (sc->func && e1->hasSideEffect()) - { - Identifier *id = Lexer::uniqueId("__tup"); - ExpInitializer *ei = new ExpInitializer(e1->loc, e1); - VarDeclaration *v = new VarDeclaration(e1->loc, NULL, id, ei); - v->storage_class |= STCtemp | STCctfe; - if (e1->isLvalue()) - v->storage_class |= STCref | STCforeach; - e0 = new DeclarationExp(e1->loc, v); - ev = new VarExp(e1->loc, v); - e0 = e0->semantic(sc); - ev = ev->semantic(sc); - } - - exps->reserve(tup->objects->dim); - for (size_t i = 0; i < tup->objects->dim; i++) - { RootObject *o = (*tup->objects)[i]; - Expression *e; - if (o->dyncast() == DYNCAST_EXPRESSION) - { - e = (Expression *)o; - if (e->op == TOKdsymbol) - { - Dsymbol *s = ((DsymbolExp *)e)->s; - e = new DotVarExp(loc, ev, s->isDeclaration()); - } - } - else if (o->dyncast() == DYNCAST_DSYMBOL) - { - e = new DsymbolExp(loc, (Dsymbol *)o); - } - else if (o->dyncast() == DYNCAST_TYPE) - { - e = new TypeExp(loc, (Type *)o); - } - else - { - error("%s is not an expression", o->toChars()); - goto Lerr; - } - exps->push(e); - } - Expression *e = new TupleExp(loc, e0, exps); - e = e->semantic(sc); - return e; - } + var = var->toAlias()->isDeclaration(); + TupleDeclaration *tup = var->isTupleDeclaration(); + if (tup) + { + /* Replace: + * e1.tuple(a, b, c) + * with: + * tuple(e1.a, e1.b, e1.c) + */ e1 = e1->semantic(sc); - e1 = e1->addDtorHook(sc); - - Type *t1 = e1->type; - FuncDeclaration *f = var->isFuncDeclaration(); - if (f) // for functions, do checks after overload resolution + Expressions *exps = new Expressions; + Expression *e0 = NULL; + Expression *ev = e1; + if (sc->func && !isTrivialExp(e1)) { - //printf("L%d fd = %s\n", __LINE__, f->toChars()); - if (!f->functionSemantic()) - return new ErrorExp(); - - type = f->type; - assert(type); + Identifier *id = Lexer::uniqueId("__tup"); + ExpInitializer *ei = new ExpInitializer(e1->loc, e1); + VarDeclaration *v = new VarDeclaration(e1->loc, NULL, id, ei); + v->storage_class |= STCtemp | STCctfe + | (e1->isLvalue() ? STCref | STCforeach : STCrvalue); + e0 = new DeclarationExp(e1->loc, v); + ev = new VarExp(e1->loc, v); + e0 = e0->semantic(sc); + ev = ev->semantic(sc); } - else + + exps->reserve(tup->objects->dim); + for (size_t i = 0; i < tup->objects->dim; i++) { - type = var->type; - if (!type && global.errors) - { // var is goofed up, just return 0 - goto Lerr; + RootObject *o = (*tup->objects)[i]; + Expression *e; + if (o->dyncast() == DYNCAST_EXPRESSION) + { + e = (Expression *)o; + if (e->op == TOKdsymbol) + { + Dsymbol *s = ((DsymbolExp *)e)->s; + e = new DotVarExp(loc, ev, s->isDeclaration()); + } } - assert(type); - - if (t1->ty == Tpointer) - t1 = t1->nextOf(); - - type = type->addMod(t1->mod); - - Dsymbol *vparent = var->toParent(); - AggregateDeclaration *ad = vparent ? vparent->isAggregateDeclaration() : NULL; - - if (Expression *e1x = getRightThis(loc, sc, ad, e1, var, 1)) - e1 = e1x; - else + else if (o->dyncast() == DYNCAST_DSYMBOL) { - /* Later checkRightThis will report correct error for invalid field variable access. - */ - Expression *e = new VarExp(loc, var); - e = e->semantic(sc); - return e; + e = new DsymbolExp(loc, (Dsymbol *)o); } - accessCheck(loc, sc, e1, var); - - VarDeclaration *v = var->isVarDeclaration(); - Expression *e = expandVar(WANTvalue, v); - if (e) - return e; - - if (v && v->isDataseg()) // fix bugzilla 8238 + else if (o->dyncast() == DYNCAST_TYPE) { - // (e1, v) - accessCheck(loc, sc, e1, v); - VarExp *ve = new VarExp(loc, v); - e = new CommaExp(loc, e1, ve); - e = e->semantic(sc); - return e; + e = new TypeExp(loc, (Type *)o); + } + else + { + error("%s is not an expression", o->toChars()); + return new ErrorExp(); } + exps->push(e); + } + Expression *e = new TupleExp(loc, e0, exps); + e = e->semantic(sc); + return e; + } + + e1 = e1->semantic(sc); + e1 = e1->addDtorHook(sc); + + Type *t1 = e1->type; + if (FuncDeclaration *fd = var->isFuncDeclaration()) + { + // for functions, do checks after overload resolution + if (!fd->functionSemantic()) + return new ErrorExp(); + + type = fd->type; + assert(type); + } + else if (OverDeclaration *od = var->isOverDeclaration()) + { + type = Type::tvoid; // ambiguous type? + } + else + { + type = var->type; + if (!type && global.errors) + { + // var is goofed up, just return 0 + return new ErrorExp(); + } + assert(type); + + if (t1->ty == Tpointer) + t1 = t1->nextOf(); + + type = type->addMod(t1->mod); + + Dsymbol *vparent = var->toParent(); + AggregateDeclaration *ad = vparent ? vparent->isAggregateDeclaration() : NULL; + + if (Expression *e1x = getRightThis(loc, sc, ad, e1, var, 1)) + e1 = e1x; + else + { + /* Later checkRightThis will report correct error for invalid field variable access. + */ + Expression *e = new VarExp(loc, var); + e = e->semantic(sc); + return e; + } + accessCheck(loc, sc, e1, var); + + VarDeclaration *v = var->isVarDeclaration(); + Expression *e = expandVar(WANTvalue, v); + if (e) + return e; + + if (v && v->isDataseg()) // fix bugzilla 8238 + { + // (e1, v) + accessCheck(loc, sc, e1, v); + VarExp *ve = new VarExp(loc, v); + e = new CommaExp(loc, e1, ve); + e = e->semantic(sc); + return e; } } //printf("-DotVarExp::semantic('%s')\n", toChars()); return this; - -Lerr: - return new ErrorExp(); } int DotVarExp::isLvalue() @@ -7966,23 +7659,47 @@ int modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1) if (var->type->isMutable() && e1->type->isMutable()) result = false; else - ::error(loc, "multiple field %s initialization", var->toChars()); + { + const char *modStr = !var->type->isMutable() ? MODtoChars(var->type->mod) : MODtoChars(e1->type->mod); + ::error(loc, "%s field '%s' initialized multiple times", modStr, var->toChars()); + } } else if (sc->noctor || fi & CSXlabel) { if (!mustInit && var->type->isMutable() && e1->type->isMutable()) result = false; else - ::error(loc, "field %s initializing not allowed in loops or after labels", var->toChars()); + { + const char *modStr = !var->type->isMutable() ? MODtoChars(var->type->mod) : MODtoChars(e1->type->mod); + ::error(loc, "%s field '%s' initialization is not allowed in loops or after labels", modStr, var->toChars()); + } } sc->fieldinit[i] |= CSXthis_ctor; } + else if (fd != sc->func) + { + if (var->type->isMutable()) + result = false; + else if (sc->func->fes) + { + const char *p = var->isField() ? "field" : var->kind(); + ::error(loc, "%s %s '%s' initialization is not allowed in foreach loop", + MODtoChars(var->type->mod), p, var->toChars()); + } + else + { + const char *p = var->isField() ? "field" : var->kind(); + ::error(loc, "%s %s '%s' initialization is not allowed in nested function '%s'", + MODtoChars(var->type->mod), p, var->toChars(), sc->func->toChars()); + } + } return result; } else { if (s) - { s = s->toParent2(); + { + s = s->toParent2(); continue; } } @@ -8012,13 +7729,6 @@ Expression *DotVarExp::modifiableLvalue(Scope *sc, Expression *e) return Expression::modifiableLvalue(sc, e); } -void DotVarExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - expToCBuffer(buf, hgs, e1, PREC_primary); - buf->writeByte('.'); - buf->writestring(var->toChars()); -} - /************************************************************/ /* Things like: @@ -8033,6 +7743,12 @@ DotTemplateInstanceExp::DotTemplateInstanceExp(Loc loc, Expression *e, Identifie this->ti->tiargs = tiargs; } +DotTemplateInstanceExp::DotTemplateInstanceExp(Loc loc, Expression *e, TemplateInstance *ti) + : UnaExp(loc, TOKdotti, sizeof(DotTemplateInstanceExp), e) +{ + this->ti = ti; +} + Expression *DotTemplateInstanceExp::syntaxCopy() { DotTemplateInstanceExp *de = new DotTemplateInstanceExp(loc, @@ -8065,7 +7781,7 @@ bool DotTemplateInstanceExp::findTempDecl(Scope *sc) case TOKvar: s = ((VarExp *)e)->var; break; default: return false; } - return ti->updateTemplateDeclaration(sc, s); + return ti->updateTempDecl(sc, s); } Expression *DotTemplateInstanceExp::semantic(Scope *sc) @@ -8125,77 +7841,100 @@ Expression *DotTemplateInstanceExp::semanticY(Scope *sc, int flag) if (e->op == TOKdotvar) { DotVarExp *dve = (DotVarExp *)e; - FuncDeclaration *f = dve->var->isFuncDeclaration(); - if (f) + if (FuncDeclaration *fd = dve->var->isFuncDeclaration()) { - TemplateDeclaration *td = f->findTemplateDeclRoot(); + TemplateDeclaration *td = fd->findTemplateDeclRoot(); if (td) { e = new DotTemplateExp(dve->loc, dve->e1, td); e = e->semantic(sc); } } + else if (OverDeclaration *od = dve->var->isOverDeclaration()) + { + e1 = dve->e1; // pull semantic() result + if (!findTempDecl(sc)) + goto Lerr; + if (ti->needsTypeInference(sc)) + return this; + ti->semantic(sc); + if (!ti->inst || ti->errors) // if template failed to expand + return new ErrorExp(); + Dsymbol *s = ti->toAlias(); + Declaration *v = s->isDeclaration(); + if (v) + { + if (v->type && !v->type->deco) + v->type = v->type->semantic(v->loc, sc); + e = new DotVarExp(loc, e1, v); + e = e->semantic(sc); + return e; + } + e = new ScopeExp(loc, ti); + e = new DotExp(loc, e1, e); + e = e->semantic(sc); + return e; + } } else if (e->op == TOKvar) { VarExp *ve = (VarExp *)e; - FuncDeclaration *f = ve->var->isFuncDeclaration(); - if (f) + if (FuncDeclaration *fd = ve->var->isFuncDeclaration()) { - TemplateDeclaration *td = f->findTemplateDeclRoot(); + TemplateDeclaration *td = fd->findTemplateDeclRoot(); if (td) { e = new ScopeExp(ve->loc, td); e = e->semantic(sc); } } + else if (OverDeclaration *od = ve->var->isOverDeclaration()) + { + ti->tempdecl = od; + e = new ScopeExp(loc, ti); + e = e->semantic(sc); + return e; + } } if (e->op == TOKdottd) { - if (ti->errors) - return new ErrorExp(); DotTemplateExp *dte = (DotTemplateExp *)e; - Expression *eleft = dte->e1; + e1 = dte->e1; // pull semantic() result + ti->tempdecl = dte->td; if (!ti->semanticTiargs(sc)) - { - ti->inst = ti; - ti->inst->errors = true; return new ErrorExp(); - } if (ti->needsTypeInference(sc)) - { - e1 = eleft; // save result of semantic() return this; - } - else - ti->semantic(sc); - if (!ti->inst) // if template failed to expand + ti->semantic(sc); + if (!ti->inst || ti->errors) // if template failed to expand return new ErrorExp(); - Dsymbol *s = ti->inst->toAlias(); + Dsymbol *s = ti->toAlias(); Declaration *v = s->isDeclaration(); if (v && (v->isFuncDeclaration() || v->isVarDeclaration())) { - e = new DotVarExp(loc, eleft, v); + e = new DotVarExp(loc, e1, v); e = e->semantic(sc); return e; } - if (eleft->op == TOKtype) + if (e1->op == TOKtype) { e = new DsymbolExp(loc, s); e = e->semantic(sc); return e; } e = new ScopeExp(loc, ti); - e = new DotExp(loc, eleft, e); + e = new DotExp(loc, e1, e); e = e->semantic(sc); return e; } else if (e->op == TOKimport) - { ScopeExp *se = (ScopeExp *)e; + { + ScopeExp *se = (ScopeExp *)e; TemplateDeclaration *td = se->sds->isTemplateDeclaration(); if (!td) - { error("%s is not a template", e->toChars()); + { + error("%s is not a template", e->toChars()); return new ErrorExp(); } ti->tempdecl = td; @@ -8204,52 +7943,49 @@ Expression *DotTemplateInstanceExp::semanticY(Scope *sc, int flag) return e; } else if (e->op == TOKdotexp) - { DotExp *de = (DotExp *)e; - Expression *eleft = de->e1; + { + DotExp *de = (DotExp *)e; + e1 = de->e1; // pull semantic() result if (de->e2->op == TOKoverloadset) { if (!findTempDecl(sc) || !ti->semanticTiargs(sc)) { - ti->inst = ti; - ti->inst->errors = true; return new ErrorExp(); } if (ti->needsTypeInference(sc)) - { - e1 = eleft; return this; - } - else - ti->semantic(sc); - if (!ti->inst) // if template failed to expand + ti->semantic(sc); + if (!ti->inst || ti->errors) // if template failed to expand return new ErrorExp(); - Dsymbol *s = ti->inst->toAlias(); + Dsymbol *s = ti->toAlias(); Declaration *v = s->isDeclaration(); if (v) { if (v->type && !v->type->deco) v->type = v->type->semantic(v->loc, sc); - e = new DotVarExp(loc, eleft, v); + e = new DotVarExp(loc, e1, v); e = e->semantic(sc); return e; } e = new ScopeExp(loc, ti); - e = new DotExp(loc, eleft, e); + e = new DotExp(loc, e1, e); e = e->semantic(sc); return e; } if (de->e2->op == TOKimport) - { // This should *really* be moved to ScopeExp::semantic() + { + // This should *really* be moved to ScopeExp::semantic() ScopeExp *se = (ScopeExp *)de->e2; de->e2 = new DsymbolExp(loc, se->sds); de->e2 = de->e2->semantic(sc); } if (de->e2->op == TOKtemplate) - { TemplateExp *te = (TemplateExp *) de->e2; + { + TemplateExp *te = (TemplateExp *) de->e2; e = new DotTemplateExp(loc,de->e1,te->td); } else @@ -8273,13 +8009,6 @@ Expression *DotTemplateInstanceExp::semanticY(Scope *sc, int flag) return new ErrorExp(); } -void DotTemplateInstanceExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - expToCBuffer(buf, hgs, e1, PREC_primary); - buf->writeByte('.'); - ti->toCBuffer(buf, hgs); -} - /************************************************************/ DelegateExp::DelegateExp(Loc loc, Expression *e, FuncDeclaration *f, bool hasOverloads) @@ -8294,34 +8023,24 @@ Expression *DelegateExp::semantic(Scope *sc) #if LOGSEMANTIC printf("DelegateExp::semantic('%s')\n", toChars()); #endif - if (!type) + if (type) + return this; + + e1 = e1->semantic(sc); + type = new TypeDelegate(func->type); + type = type->semantic(loc, sc); + AggregateDeclaration *ad = func->toParent()->isAggregateDeclaration(); + if (func->needThis()) + e1 = getRightThis(loc, sc, ad, e1, func); + if (ad && ad->isClassDeclaration() && ad->type != e1->type) { + // A downcast is required for interfaces, see Bugzilla 3706 + e1 = new CastExp(loc, e1, ad->type); e1 = e1->semantic(sc); - type = new TypeDelegate(func->type); - type = type->semantic(loc, sc); - AggregateDeclaration *ad = func->toParent()->isAggregateDeclaration(); - if (func->needThis()) - e1 = getRightThis(loc, sc, ad, e1, func); - if (ad && ad->isClassDeclaration() && ad->type != e1->type) - { // A downcast is required for interfaces, see Bugzilla 3706 - e1 = new CastExp(loc, e1, ad->type); - e1 = e1->semantic(sc); - } } return this; } -void DelegateExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writeByte('&'); - if (!func->isNested()) - { - expToCBuffer(buf, hgs, e1, PREC_primary); - buf->writeByte('.'); - } - buf->writestring(func->toChars()); -} - /************************************************************/ DotTypeExp::DotTypeExp(Loc loc, Expression *e, Dsymbol *s) @@ -8336,17 +8055,11 @@ Expression *DotTypeExp::semantic(Scope *sc) #if LOGSEMANTIC printf("DotTypeExp::semantic('%s')\n", toChars()); #endif - UnaExp::semantic(sc); + if (Expression *ex = unaSemantic(sc)) + return ex; return this; } -void DotTypeExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - expToCBuffer(buf, hgs, e1, PREC_primary); - buf->writeByte('.'); - buf->writestring(sym->toChars()); -} - /************************************************************/ CallExp::CallExp(Loc loc, Expression *e, Expressions *exps) @@ -8360,6 +8073,7 @@ CallExp::CallExp(Loc loc, Expression *e) : UnaExp(loc, TOKcall, sizeof(CallExp), e) { this->arguments = NULL; + this->f = NULL; } CallExp::CallExp(Loc loc, Expression *e, Expression *earg1) @@ -8367,10 +8081,12 @@ CallExp::CallExp(Loc loc, Expression *e, Expression *earg1) { Expressions *arguments = new Expressions(); if (earg1) - { arguments->setDim(1); + { + arguments->setDim(1); (*arguments)[0] = earg1; } this->arguments = arguments; + this->f = NULL; } CallExp::CallExp(Loc loc, Expression *e, Expression *earg1, Expression *earg2) @@ -8382,6 +8098,7 @@ CallExp::CallExp(Loc loc, Expression *e, Expression *earg1, Expression *earg2) (*arguments)[1] = earg2; this->arguments = arguments; + this->f = NULL; } CallExp *CallExp::create(Loc loc, Expression *e, Expressions *exps) @@ -8406,12 +8123,6 @@ Expression *CallExp::syntaxCopy() Expression *CallExp::semantic(Scope *sc) { - Type *t1; - Objects *tiargs = NULL; // initial list of template arguments - Expression *ethis = NULL; - Type *tthis = NULL; - Expression *e1org = e1; - #if LOGSEMANTIC printf("CallExp::semantic() %s\n", toChars()); #endif @@ -8426,6 +8137,12 @@ Expression *CallExp::semantic(Scope *sc) } #endif + Type *t1; + Objects *tiargs = NULL; // initial list of template arguments + Expression *ethis = NULL; + Type *tthis = NULL; + Expression *e1org = e1; + if (e1->op == TOKcomma) { /* Rewrite (a,b)(args) as (a,(b(args))) @@ -8447,8 +8164,11 @@ Expression *CallExp::semantic(Scope *sc) if (e1->op == TOKfunction) { - arrayExpressionSemantic(arguments, sc); - preFunctionParameters(loc, sc, arguments); + if (arrayExpressionSemantic(arguments, sc) || + preFunctionParameters(loc, sc, arguments)) + { + return new ErrorExp(); + } // Run e1 semantic even if arguments have any errors FuncExp *fe = (FuncExp *)e1; @@ -8457,30 +8177,33 @@ Expression *CallExp::semantic(Scope *sc) return e1; } - { - Expression *e = resolveUFCS(sc, this); - if (e) - return e; - } + if (Expression *ex = resolveUFCS(sc, this)) + return ex; /* This recognizes: * foo!(tiargs)(funcargs) */ if (e1->op == TOKimport && !e1->type) - { ScopeExp *se = (ScopeExp *)e1; + { + ScopeExp *se = (ScopeExp *)e1; TemplateInstance *ti = se->sds->isTemplateInstance(); - if (ti && !ti->semanticRun) + if (ti) { /* Attempt to instantiate ti. If that works, go with it. * If not, go with partial explicit specialization. */ - if (!ti->findTemplateDeclaration(sc) || + WithScopeSymbol *withsym; + if (!ti->findTempDecl(sc, &withsym) || !ti->semanticTiargs(sc)) { - ti->inst = ti; - ti->inst->errors = true; return new ErrorExp(); } + if (withsym && withsym->withstate->wthis) + { + e1 = new VarExp(e1->loc, withsym->withstate->wthis); + e1 = new DotTemplateInstanceExp(e1->loc, e1, ti); + goto Ldotti; + } if (ti->needsTypeInference(sc, 1)) { /* Go with partial explicit specialization @@ -8489,14 +8212,17 @@ Expression *CallExp::semantic(Scope *sc) assert(ti->tempdecl); if (TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration()) e1 = new TemplateExp(loc, td); + else if (OverDeclaration *od = ti->tempdecl->isOverDeclaration()) + e1 = new VarExp(loc, od); else e1 = new OverExp(loc, ti->tempdecl->isOverloadSet()); } else { - ti->semantic(sc); - if (ti->errors) - e1 = new ErrorExp(); + Expression *e1x = e1->semantic(sc); + if (e1x->op == TOKerror) + return e1x; + e1 = e1x; } } } @@ -8506,9 +8232,9 @@ Expression *CallExp::semantic(Scope *sc) */ Ldotti: if (e1->op == TOKdotti && !e1->type) - { DotTemplateInstanceExp *se = (DotTemplateInstanceExp *)e1; + { + DotTemplateInstanceExp *se = (DotTemplateInstanceExp *)e1; TemplateInstance *ti = se->ti; - if (!ti->semanticRun) { /* Attempt to instantiate ti. If that works, go with it. * If not, go with partial explicit specialization. @@ -8516,8 +8242,6 @@ Expression *CallExp::semantic(Scope *sc) if (!se->findTempDecl(sc) || !ti->semanticTiargs(sc)) { - ti->inst = ti; - ti->inst->errors = true; return new ErrorExp(); } if (ti->needsTypeInference(sc, 1)) @@ -8528,12 +8252,19 @@ Expression *CallExp::semantic(Scope *sc) assert(ti->tempdecl); if (TemplateDeclaration *td = ti->tempdecl->isTemplateDeclaration()) e1 = new DotTemplateExp(loc, se->e1, td); + else if (OverDeclaration *od = ti->tempdecl->isOverDeclaration()) + { + e1 = new DotVarExp(loc, se->e1, od); + } else e1 = new DotExp(loc, se->e1, new OverExp(loc, ti->tempdecl->isOverloadSet())); } else { - e1 = e1->semantic(sc); + Expression *e1x = e1->semantic(sc); + if (e1x->op == TOKerror) + return e1x; + e1 = e1x; } } } @@ -8548,7 +8279,8 @@ Expression *CallExp::semantic(Scope *sc) else { if (e1->op == TOKdot) - { DotIdExp *die = (DotIdExp *)e1; + { + DotIdExp *die = (DotIdExp *)e1; e1 = die->semantic(sc); /* Look for e1 having been rewritten to expr.opDispatch!(string) * We handle such earlier, so go back. @@ -8568,17 +8300,17 @@ Expression *CallExp::semantic(Scope *sc) --nest; return new ErrorExp(); } - UnaExp::semantic(sc); + Expression *ex = unaSemantic(sc); --nest; - if (e1->op == TOKerror) - return e1; + if (ex) + return ex; } /* Look for e1 being a lazy parameter */ if (e1->op == TOKvar) - { VarExp *ve = (VarExp *)e1; - + { + VarExp *ve = (VarExp *)e1; if (ve->var->storage_class & STClazy) { // lazy paramaters can be called without violating purity and safety @@ -8589,10 +8321,13 @@ Expression *CallExp::semantic(Scope *sc) TypeDelegate *t = new TypeDelegate(tf); ve->type = t->semantic(loc, sc); } + if (VarDeclaration *v = ve->var->isVarDeclaration()) + ve->checkPurity(sc, v); } if (e1->op == TOKimport) - { // Perhaps this should be moved to ScopeExp::semantic() + { + // Perhaps this should be moved to ScopeExp::semantic() ScopeExp *se = (ScopeExp *)e1; e1 = new DsymbolExp(loc, se->sds); e1 = e1->semantic(sc); @@ -8615,17 +8350,24 @@ Expression *CallExp::semantic(Scope *sc) } if (de->e2->op == TOKimport) - { // This should *really* be moved to ScopeExp::semantic() + { + // This should *really* be moved to ScopeExp::semantic() ScopeExp *se = (ScopeExp *)de->e2; de->e2 = new DsymbolExp(loc, se->sds); de->e2 = de->e2->semantic(sc); } if (de->e2->op == TOKtemplate) - { TemplateExp *te = (TemplateExp *) de->e2; + { + TemplateExp *te = (TemplateExp *) de->e2; e1 = new DotTemplateExp(loc,de->e1,te->td); } } + else if (e1->op == TOKstar && e1->type->ty == Tfunction) + { + // Rewrite (*fp)(arguments) to fp(arguments) + e1 = ((PtrExp *)e1)->e1; + } } t1 = NULL; @@ -8745,13 +8487,32 @@ Expression *CallExp::semantic(Scope *sc) e = e->semantic(sc); return e; } + else if (e1->op == TOKtype && t1->isscalar()) + { + Expression *e; + if (!arguments || arguments->dim == 0) + { + e = t1->defaultInitLiteral(loc); + } + else if (arguments->dim == 1) + { + e = (*arguments)[0]; + e = e->implicitCastTo(sc, t1); + e = new CastExp(loc, e, t1); + } + else + { + error("more than one argument for construction of %s", t1->toChars()); + e = new ErrorExp(); + } + e = e->semantic(sc); + return e; + } } if (e1->op == TOKdotvar && t1->ty == Tfunction || e1->op == TOKdottd) { - DotVarExp *dve; - DotTemplateExp *dte; UnaExp *ue = (UnaExp *)(e1); Expression *ue1 = ue->e1; @@ -8765,21 +8526,26 @@ Expression *CallExp::semantic(Scope *sc) ue1 = NULL; } + DotVarExp *dve; + DotTemplateExp *dte; Dsymbol *s; if (e1->op == TOKdotvar) { dve = (DotVarExp *)(e1); + dte = NULL; s = dve->var; tiargs = NULL; } else - { dte = (DotTemplateExp *)(e1); + { + dve = NULL; + dte = (DotTemplateExp *)(e1); s = dte->td; } // Do overload resolution f = resolveFuncCall(loc, sc, s, tiargs, ue1 ? ue1->type : NULL, arguments); - if (!f || f->type->ty == Terror) + if (!f || f->errors || f->type->ty == Terror) return new ErrorExp(); if (f->needThis()) @@ -8807,6 +8573,7 @@ Expression *CallExp::semantic(Scope *sc) checkDeprecated(sc, f); checkPurity(sc, f); checkSafety(sc, f); + checkNogc(sc, f); accessCheck(loc, sc, ue->e1, f); if (!f->needThis()) { @@ -8886,12 +8653,13 @@ Expression *CallExp::semantic(Scope *sc) tthis = cd->type->addMod(sc->func->type->mod); f = resolveFuncCall(loc, sc, cd->baseClass->ctor, NULL, tthis, arguments, 0); - if (!f) + if (!f || f->errors) return new ErrorExp(); accessCheck(loc, sc, NULL, f); checkDeprecated(sc, f); checkPurity(sc, f); checkSafety(sc, f); + checkNogc(sc, f); e1 = new DotVarExp(e1->loc, e1, f); e1 = e1->semantic(sc); t1 = e1->type; @@ -8925,11 +8693,12 @@ Expression *CallExp::semantic(Scope *sc) tthis = cd->type->addMod(sc->func->type->mod); f = resolveFuncCall(loc, sc, cd->ctor, NULL, tthis, arguments, 0); - if (!f) + if (!f || f->errors) return new ErrorExp(); checkDeprecated(sc, f); checkPurity(sc, f); checkSafety(sc, f); + checkNogc(sc, f); e1 = new DotVarExp(e1->loc, e1, f); e1 = e1->semantic(sc); t1 = e1->type; @@ -8937,7 +8706,8 @@ Expression *CallExp::semantic(Scope *sc) // BUG: this should really be done by checking the static // call graph if (f == sc->func) - { error("cyclic constructor call"); + { + error("cyclic constructor call"); return new ErrorExp(); } } @@ -8948,16 +8718,22 @@ Expression *CallExp::semantic(Scope *sc) FuncDeclaration *f = NULL; Dsymbol *s = NULL; for (size_t i = 0; i < eo->vars->a.dim; i++) - { s = eo->vars->a[i]; + { + s = eo->vars->a[i]; if (tiargs && s->isFuncDeclaration()) continue; FuncDeclaration *f2 = resolveFuncCall(loc, sc, s, tiargs, tthis, arguments, 1); if (f2) - { if (f) + { + if (f2->errors) + return new ErrorExp(); + if (f) + { /* Error if match in more than one overload set, * even if one is a 'better' match than the other. */ ScopeDsymbol::multiplyDefined(loc, f, f2); + } else f = f2; } @@ -8985,6 +8761,7 @@ Expression *CallExp::semantic(Scope *sc) { TypeFunction *tf; const char *p; + Dsymbol *s; f = NULL; if (e1->op == TOKfunction) { @@ -9006,20 +8783,44 @@ Expression *CallExp::semantic(Scope *sc) tf = (TypeFunction *)(((TypePointer *)t1)->next); p = "function pointer"; } - else if (e1->op == TOKtemplate) + else if (e1->op == TOKdotvar && + ((DotVarExp *)e1)->var->isOverDeclaration()) { - TemplateExp *te = (TemplateExp *)e1; - f = resolveFuncCall(loc, sc, te->td, tiargs, NULL, arguments); + DotVarExp *dve = (DotVarExp *)e1; + f = resolveFuncCall(loc, sc, dve->var, tiargs, dve->e1->type, arguments, 2); if (!f) return new ErrorExp(); if (f->needThis()) + { + dve->var = f; + dve->type = f->type; + dve->hasOverloads = 0; + goto Lagain; + } + e1 = new VarExp(dve->loc, f, 0); + Expression *e = new CommaExp(loc, dve->e1, this); + return e->semantic(sc); + } + else if (e1->op == TOKvar && + ((VarExp *)e1)->var->isOverDeclaration()) + { + s = ((VarExp *)e1)->var; + goto L2; + } + else if (e1->op == TOKtemplate) + { + s = ((TemplateExp *)e1)->td; + L2: + f = resolveFuncCall(loc, sc, s, tiargs, NULL, arguments, 2); + if (!f || f->errors) + return new ErrorExp(); + if (f->needThis()) { if (hasThis(sc)) { // Supply an implicit 'this', as in - // this.ident - - e1 = new DotTemplateExp(loc, (new ThisExp(loc))->semantic(sc), te->td); + // this.ident + e1 = new DotVarExp(loc, (new ThisExp(loc))->semantic(sc), f); goto Lagain; } else if (isNeedThisScope(sc, f)) @@ -9028,8 +8829,7 @@ Expression *CallExp::semantic(Scope *sc) return new ErrorExp(); } } - - e1 = new VarExp(loc, f); + e1 = new VarExp(e1->loc, f, 0); goto Lagain; } else @@ -9058,7 +8858,7 @@ Expression *CallExp::semantic(Scope *sc) //printf("tf = %s, args = %s\n", tf->deco, (*arguments)[0]->type->deco); ::error(loc, "%s %s %s is not callable using argument types %s", p, e1->toChars(), Parameter::argsTypesToChars(tf->parameters, tf->varargs), - buf.toChars()); + buf.peekString()); return new ErrorExp(); } @@ -9068,20 +8868,29 @@ Expression *CallExp::semantic(Scope *sc) { checkPurity(sc, f); checkSafety(sc, f); + checkNogc(sc, f); f->checkNestedReference(sc, loc); } - else if (sc->func && !(sc->flags & SCOPEctfe)) + else if (sc->func && sc->intypeof != 1 && !(sc->flags & SCOPEctfe)) { + bool err = false; if (!tf->purity && !(sc->flags & SCOPEdebug) && sc->func->setImpure()) { error("pure function '%s' cannot call impure %s '%s'", sc->func->toPrettyChars(), p, e1->toChars()); - return new ErrorExp(); + err = true; + } + if (!tf->isnogc && sc->func->setGC()) + { + error("@nogc function '%s' cannot call non-@nogc %s '%s'", sc->func->toPrettyChars(), p, e1->toChars()); + err = true; } if (tf->trust <= TRUSTsystem && sc->func->setUnsafe()) { error("safe function '%s' cannot call system %s '%s'", sc->func->toPrettyChars(), p, e1->toChars()); - return new ErrorExp(); + err = true; } + if (err) + return new ErrorExp(); } if (t1->ty == Tpointer) @@ -9123,12 +8932,12 @@ Expression *CallExp::semantic(Scope *sc) //printf("tf = %s, args = %s\n", tf->deco, (*arguments)[0]->type->deco); ::error(loc, "%s %s is not callable using argument types %s", e1->toChars(), Parameter::argsTypesToChars(tf->parameters, tf->varargs), - buf.toChars()); + buf.peekString()); - return new ErrorExp(); + f = NULL; } } - if (!f) + if (!f || f->errors) return new ErrorExp(); if (f->needThis()) @@ -9151,6 +8960,7 @@ Expression *CallExp::semantic(Scope *sc) checkDeprecated(sc, f); checkPurity(sc, f); checkSafety(sc, f); + checkNogc(sc, f); f->checkNestedReference(sc, loc); accessCheck(loc, sc, NULL, f); @@ -9168,7 +8978,7 @@ Expression *CallExp::semantic(Scope *sc) if (!arguments) arguments = new Expressions(); - int olderrors = global.errors; + unsigned int olderrors = global.errors; type = functionParameters(loc, sc, (TypeFunction *)(t1), tthis, arguments, f); if (olderrors != global.errors) return new ErrorExp(); @@ -9262,23 +9072,6 @@ Expression *CallExp::addDtorHook(Scope *sc) return this; } -void CallExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - if (e1->op == TOKtype) - /* Avoid parens around type to prevent forbidden cast syntax: - * (sometype)(arg1) - * This is ok since types in constructor calls - * can never depend on parens anyway - */ - e1->toCBuffer(buf, hgs); - else - expToCBuffer(buf, hgs, e1, precedence[op]); - buf->writeByte('('); - argsToCBuffer(buf, arguments, hgs); - buf->writeByte(')'); -} - - /************************************************************/ AddrExp::AddrExp(Loc loc, Expression *e) @@ -9291,180 +9084,182 @@ Expression *AddrExp::semantic(Scope *sc) #if LOGSEMANTIC printf("AddrExp::semantic('%s')\n", toChars()); #endif - if (!type) + if (type) + return this; + + if (Expression *ex = unaSemantic(sc)) + return ex; + int wasCond = e1->op == TOKquestion; + if (e1->op == TOKdotti) { - UnaExp::semantic(sc); - if (e1->type == Type::terror) - return new ErrorExp(); - int wasCond = e1->op == TOKquestion; - if (e1->op == TOKdotti) - { - DotTemplateInstanceExp* dti = (DotTemplateInstanceExp *)e1; - TemplateInstance *ti = dti->ti; - if (!ti->semanticRun) - { - //assert(ti->needsTypeInference(sc)); - ti->semantic(sc); - if (!ti->inst || ti->errors) // if template failed to expand - return new ErrorExp; - Dsymbol *s = ti->inst->toAlias(); - FuncDeclaration *f = s->isFuncDeclaration(); - assert(f); - e1 = new DotVarExp(e1->loc, dti->e1, f); - e1 = e1->semantic(sc); - } - } - else if (e1->op == TOKimport) + DotTemplateInstanceExp* dti = (DotTemplateInstanceExp *)e1; + TemplateInstance *ti = dti->ti; { - TemplateInstance *ti = ((ScopeExp *)e1)->sds->isTemplateInstance(); - if (ti && !ti->semanticRun) + //assert(ti->needsTypeInference(sc)); + ti->semantic(sc); + if (!ti->inst || ti->errors) // if template failed to expand + return new ErrorExp; + Dsymbol *s = ti->toAlias(); + FuncDeclaration *f = s->isFuncDeclaration(); + if (f) { - //assert(ti->needsTypeInference(sc)); - ti->semantic(sc); - if (!ti->inst || ti->errors) // if template failed to expand - return new ErrorExp; - Dsymbol *s = ti->inst->toAlias(); - FuncDeclaration *f = s->isFuncDeclaration(); - assert(f); - e1 = new VarExp(e1->loc, f); + e1 = new DotVarExp(e1->loc, dti->e1, f); e1 = e1->semantic(sc); } } - e1 = e1->toLvalue(sc, NULL); - if (e1->op == TOKerror) - return e1; - if (!e1->type) - { - error("cannot take address of %s", e1->toChars()); - return new ErrorExp(); - } - if (!e1->type->deco) + } + else if (e1->op == TOKimport) + { + TemplateInstance *ti = ((ScopeExp *)e1)->sds->isTemplateInstance(); + if (ti) { - /* No deco means semantic() was not run on the type. - * We have to run semantic() on the symbol to get the right type: - * auto x = &bar; - * pure: int bar() { return 1;} - * otherwise the 'pure' is missing from the type assigned to x. - */ - - if (e1->op == TOKvar) + //assert(ti->needsTypeInference(sc)); + ti->semantic(sc); + if (!ti->inst || ti->errors) // if template failed to expand + return new ErrorExp; + Dsymbol *s = ti->toAlias(); + FuncDeclaration *f = s->isFuncDeclaration(); + if (f) { - VarExp *ve = (VarExp *)e1; - Declaration *d = ve->var; - error("forward reference to %s %s", d->kind(), d->toChars()); + e1 = new VarExp(e1->loc, f); + e1 = e1->semantic(sc); } - else - error("forward reference to %s", e1->toChars()); - return new ErrorExp(); } + } + e1 = e1->toLvalue(sc, NULL); + if (e1->op == TOKerror) + return e1; + if (!e1->type) + { + error("cannot take address of %s", e1->toChars()); + return new ErrorExp(); + } + if (!e1->type->deco) + { + /* No deco means semantic() was not run on the type. + * We have to run semantic() on the symbol to get the right type: + * auto x = &bar; + * pure: int bar() { return 1;} + * otherwise the 'pure' is missing from the type assigned to x. + */ + + if (e1->op == TOKvar) + { + VarExp *ve = (VarExp *)e1; + Declaration *d = ve->var; + error("forward reference to %s %s", d->kind(), d->toChars()); + } + else + error("forward reference to %s", e1->toChars()); + return new ErrorExp(); + } - type = e1->type->pointerTo(); + type = e1->type->pointerTo(); - // See if this should really be a delegate - if (e1->op == TOKdotvar) + // See if this should really be a delegate + if (e1->op == TOKdotvar) + { + DotVarExp *dve = (DotVarExp *)e1; + FuncDeclaration *f = dve->var->isFuncDeclaration(); + if (f) { - DotVarExp *dve = (DotVarExp *)e1; - FuncDeclaration *f = dve->var->isFuncDeclaration(); - if (f) - { - f = f->toAliasFunc(); // FIXME, should see overlods - Bugzilla 1983 - if (!dve->hasOverloads) - f->tookAddressOf++; + f = f->toAliasFunc(); // FIXME, should see overlods - Bugzilla 1983 + if (!dve->hasOverloads) + f->tookAddressOf++; - Expression *e; - if ( f->needThis()) - e = new DelegateExp(loc, dve->e1, f, dve->hasOverloads); - else // It is a function pointer. Convert &v.f() --> (v, &V.f()) - e = new CommaExp(loc, dve->e1, new AddrExp(loc, new VarExp(loc, f))); - e = e->semantic(sc); - return e; - } + Expression *e; + if ( f->needThis()) + e = new DelegateExp(loc, dve->e1, f, dve->hasOverloads); + else // It is a function pointer. Convert &v.f() --> (v, &V.f()) + e = new CommaExp(loc, dve->e1, new AddrExp(loc, new VarExp(loc, f))); + e = e->semantic(sc); + return e; } - else if (e1->op == TOKvar) - { - VarExp *ve = (VarExp *)e1; + } + else if (e1->op == TOKvar) + { + VarExp *ve = (VarExp *)e1; - VarDeclaration *v = ve->var->isVarDeclaration(); - if (v) + VarDeclaration *v = ve->var->isVarDeclaration(); + if (v) + { + if (!v->canTakeAddressOf()) { - if (!v->canTakeAddressOf()) - { error("cannot take address of %s", e1->toChars()); - return new ErrorExp(); - } + error("cannot take address of %s", e1->toChars()); + return new ErrorExp(); + } - if (sc->func && !sc->intypeof && !v->isDataseg()) + if (sc->func && !sc->intypeof && !v->isDataseg()) + { + if (sc->func->setUnsafe()) { - if (sc->func->setUnsafe()) - { - const char *p = v->isParameter() ? "parameter" : "local"; - error("cannot take address of %s %s in @safe function %s", - p, - v->toChars(), - sc->func->toChars()); - } + const char *p = v->isParameter() ? "parameter" : "local"; + error("cannot take address of %s %s in @safe function %s", + p, + v->toChars(), + sc->func->toChars()); } } + } - FuncDeclaration *f = ve->var->isFuncDeclaration(); - if (f) + FuncDeclaration *f = ve->var->isFuncDeclaration(); + if (f) + { + /* Because nested functions cannot be overloaded, + * mark here that we took its address because castTo() + * may not be called with an exact match. + */ + if (!ve->hasOverloads || f->isNested()) + f->tookAddressOf++; + if (f->isNested()) { - if (!ve->hasOverloads || - /* Because nested functions cannot be overloaded, - * mark here that we took its address because castTo() - * may not be called with an exact match. - */ - f->isNested()) - f->tookAddressOf++; - if (f->isNested()) + if (f->isFuncLiteralDeclaration()) { - if (f->isFuncLiteralDeclaration()) + if (!f->FuncDeclaration::isNested()) { - if (!f->FuncDeclaration::isNested()) - { /* Supply a 'null' for a this pointer if no this is available - */ - Expression *e = new DelegateExp(loc, new NullExp(loc, Type::tnull), f, ve->hasOverloads); - e = e->semantic(sc); - return e; - } + /* Supply a 'null' for a this pointer if no this is available + */ + Expression *e = new DelegateExp(loc, new NullExp(loc, Type::tnull), f, ve->hasOverloads); + e = e->semantic(sc); + return e; } - Expression *e = new DelegateExp(loc, e1, f, ve->hasOverloads); - e = e->semantic(sc); - return e; - } - if (f->needThis() && hasThis(sc)) - { - /* Should probably supply 'this' after overload resolution, - * not before. - */ - Expression *ethis = new ThisExp(loc); - Expression *e = new DelegateExp(loc, ethis, f, ve->hasOverloads); - e = e->semantic(sc); - return e; } + Expression *e = new DelegateExp(loc, e1, f, ve->hasOverloads); + e = e->semantic(sc); + return e; + } + if (f->needThis() && hasThis(sc)) + { + /* Should probably supply 'this' after overload resolution, + * not before. + */ + Expression *ethis = new ThisExp(loc); + Expression *e = new DelegateExp(loc, ethis, f, ve->hasOverloads); + e = e->semantic(sc); + return e; } } - else if (wasCond) - { - /* a ? b : c was transformed to *(a ? &b : &c), but we still - * need to do safety checks - */ - assert(e1->op == TOKstar); - PtrExp *pe = (PtrExp *)e1; - assert(pe->e1->op == TOKquestion); - CondExp *ce = (CondExp *)pe->e1; - assert(ce->e1->op == TOKaddress); - assert(ce->e2->op == TOKaddress); - - // Re-run semantic on the address expressions only - ce->e1->type = NULL; - ce->e1 = ce->e1->semantic(sc); - ce->e2->type = NULL; - ce->e2 = ce->e2->semantic(sc); - } + } + else if (wasCond) + { + /* a ? b : c was transformed to *(a ? &b : &c), but we still + * need to do safety checks + */ + assert(e1->op == TOKstar); + PtrExp *pe = (PtrExp *)e1; + assert(pe->e1->op == TOKquestion); + CondExp *ce = (CondExp *)pe->e1; + assert(ce->e1->op == TOKaddress); + assert(ce->e2->op == TOKaddress); - return optimize(WANTvalue); + // Re-run semantic on the address expressions only + ce->e1->type = NULL; + ce->e1 = ce->e1->semantic(sc); + ce->e2->type = NULL; + ce->e2 = ce->e2->semantic(sc); } - return this; + + return optimize(WANTvalue); } void AddrExp::checkEscape() @@ -9492,33 +9287,35 @@ Expression *PtrExp::semantic(Scope *sc) #if LOGSEMANTIC printf("PtrExp::semantic('%s')\n", toChars()); #endif - if (!type) + if (type) + return this; + + Expression *e = op_overload(sc); + if (e) + return e; + + Type *tb = e1->type->toBasetype(); + switch (tb->ty) { - Expression *e = op_overload(sc); - if (e) - return e; - Type *tb = e1->type->toBasetype(); - switch (tb->ty) - { - case Tpointer: - type = ((TypePointer *)tb)->next; - break; + case Tpointer: + type = ((TypePointer *)tb)->next; + break; - case Tsarray: - case Tarray: - deprecation("using * on an array is deprecated; use *(%s).ptr instead", e1->toChars()); - type = ((TypeArray *)tb)->next; - e1 = e1->castTo(sc, type->pointerTo()); - break; + case Tsarray: + case Tarray: + deprecation("using * on an array is deprecated; use *(%s).ptr instead", e1->toChars()); + type = ((TypeArray *)tb)->next; + e1 = e1->castTo(sc, type->pointerTo()); + break; - default: - error("can only * a pointer, not a '%s'", e1->type->toChars()); - case Terror: - return new ErrorExp(); - } - if (!rvalue()) + default: + error("can only * a pointer, not a '%s'", e1->type->toChars()); + case Terror: return new ErrorExp(); } + if (!rvalue()) + return new ErrorExp(); + return this; } @@ -9557,12 +9354,6 @@ Expression *PtrExp::modifiableLvalue(Scope *sc, Expression *e) return Expression::modifiableLvalue(sc, e); } -void PtrExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writeByte('*'); - expToCBuffer(buf, hgs, e1, precedence[op]); -} - /************************************************************/ NegExp::NegExp(Loc loc, Expression *e) @@ -9575,29 +9366,30 @@ Expression *NegExp::semantic(Scope *sc) #if LOGSEMANTIC printf("NegExp::semantic('%s')\n", toChars()); #endif - if (!type) - { - Expression *e = op_overload(sc); - if (e) - return e; + if (type) + return this; - type = e1->type; - Type *tb = type->toBasetype(); - if (tb->ty == Tarray || tb->ty == Tsarray) + Expression *e = op_overload(sc); + if (e) + return e; + + type = e1->type; + Type *tb = type->toBasetype(); + if (tb->ty == Tarray || tb->ty == Tsarray) + { + if (!isArrayOpValid(e1)) { - if (!isArrayOpValid(e1)) - { - error("invalid array operation %s (did you forget a [] ?)", toChars()); - return new ErrorExp(); - } - return this; + error("invalid array operation %s (did you forget a [] ?)", toChars()); + return new ErrorExp(); } - - e1->checkNoBool(); - e1 = e1->checkArithmetic(); - if (e1->op == TOKerror) - return e1; + return this; } + + e1->checkNoBool(); + e1 = e1->checkArithmetic(); + if (e1->op == TOKerror) + return e1; + return this; } @@ -9609,15 +9401,16 @@ UAddExp::UAddExp(Loc loc, Expression *e) } Expression *UAddExp::semantic(Scope *sc) -{ Expression *e; - +{ #if LOGSEMANTIC printf("UAddExp::semantic('%s')\n", toChars()); #endif assert(!type); - e = op_overload(sc); + + Expression *e = op_overload(sc); if (e) return e; + e1->checkNoBool(); e1->checkArithmetic(); return e1; @@ -9632,29 +9425,30 @@ ComExp::ComExp(Loc loc, Expression *e) Expression *ComExp::semantic(Scope *sc) { - if (!type) - { - Expression *e = op_overload(sc); - if (e) - return e; + if (type) + return this; - type = e1->type; - Type *tb = type->toBasetype(); - if (tb->ty == Tarray || tb->ty == Tsarray) + Expression *e = op_overload(sc); + if (e) + return e; + + type = e1->type; + Type *tb = type->toBasetype(); + if (tb->ty == Tarray || tb->ty == Tsarray) + { + if (!isArrayOpValid(e1)) { - if (!isArrayOpValid(e1)) - { - error("invalid array operation %s (did you forget a [] ?)", toChars()); - return new ErrorExp(); - } - return this; + error("invalid array operation %s (did you forget a [] ?)", toChars()); + return new ErrorExp(); } - - e1->checkNoBool(); - e1 = e1->checkIntegral(); - if (e1->op == TOKerror) - return e1; + return this; } + + e1->checkNoBool(); + e1 = e1->checkIntegral(); + if (e1->op == TOKerror) + return e1; + return this; } @@ -9667,15 +9461,18 @@ NotExp::NotExp(Loc loc, Expression *e) Expression *NotExp::semantic(Scope *sc) { - if (!type) - { // Note there is no operator overload - UnaExp::semantic(sc); - e1 = resolveProperties(sc, e1); - e1 = e1->checkToBoolean(sc); - if (e1->type == Type::terror) - return e1; - type = Type::tboolean; - } + if (type) + return this; + + // Note there is no operator overload + if (Expression *ex = unaSemantic(sc)) + return ex; + e1 = resolveProperties(sc, e1); + e1 = e1->checkToBoolean(sc); + if (e1->type == Type::terror) + return e1; + + type = Type::tboolean; return this; } @@ -9689,15 +9486,18 @@ BoolExp::BoolExp(Loc loc, Expression *e, Type *t) Expression *BoolExp::semantic(Scope *sc) { - if (!type) - { // Note there is no operator overload - UnaExp::semantic(sc); - e1 = resolveProperties(sc, e1); - e1 = e1->checkToBoolean(sc); - if (e1->type == Type::terror) - return e1; - type = Type::tboolean; - } + if (type) + return this; + + // Note there is no operator overload + if (Expression *ex = unaSemantic(sc)) + return ex; + e1 = resolveProperties(sc, e1); + e1 = e1->checkToBoolean(sc); + if (e1->type == Type::terror) + return e1; + + type = Type::tboolean; return this; } @@ -9710,7 +9510,8 @@ DeleteExp::DeleteExp(Loc loc, Expression *e) Expression *DeleteExp::semantic(Scope *sc) { - UnaExp::semantic(sc); + if (Expression *ex = unaSemantic(sc)) + return ex; e1 = resolveProperties(sc, e1); e1 = e1->modifiableLvalue(sc, NULL); if (e1->op == TOKerror) @@ -9727,6 +9528,7 @@ Expression *DeleteExp::semantic(Scope *sc) { /* Because COM classes are deleted by IUnknown.Release() */ error("cannot delete instance of COM interface %s", cd->toChars()); + goto Lerr; } break; } @@ -9804,7 +9606,7 @@ Expression *DeleteExp::semantic(Scope *sc) break; } error("cannot delete type %s", e1->type->toChars()); - return new ErrorExp(); + goto Lerr; } if (e1->op == TOKindex) @@ -9812,10 +9614,16 @@ Expression *DeleteExp::semantic(Scope *sc) IndexExp *ae = (IndexExp *)(e1); Type *tb1 = ae->e1->type->toBasetype(); if (tb1->ty == Taarray) - error("delete aa[key] deprecated, use aa.remove(key)"); + { + error("use 'aa.remove(key)' instead of 'delete aa[key]'"); + goto Lerr; + } } return this; + +Lerr: + return new ErrorExp(); } @@ -9825,12 +9633,6 @@ Expression *DeleteExp::checkToBoolean(Scope *sc) return new ErrorExp(); } -void DeleteExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("delete "); - expToCBuffer(buf, hgs, e1, precedence[op]); -} - /************************************************************/ CastExp::CastExp(Loc loc, Expression *e, Type *t) @@ -9861,13 +9663,16 @@ Expression *CastExp::semantic(Scope *sc) #if LOGSEMANTIC printf("CastExp::semantic('%s')\n", toChars()); #endif - -//static int x; assert(++x < 10); - + //static int x; assert(++x < 10); if (type) return this; - UnaExp::semantic(sc); - e1 = resolveProperties(sc, e1); + + if (Expression *ex = unaSemantic(sc)) + return ex; + Expression *e1x = resolveProperties(sc, e1); + if (e1x->op == TOKerror) + return e1x; + e1 = e1x; if (e1->type) // if not a tuple { @@ -9886,8 +9691,6 @@ Expression *CastExp::semantic(Scope *sc) error("cannot cast %s to tuple type %s", e1->toChars(), to->toChars()); return new ErrorExp(); } - if (e1->type->ty == Terror) - return new ErrorExp(); // cast(void) is used to mark e1 as unused, so it is safe if (to->ty == Tvoid) @@ -9897,9 +9700,7 @@ Expression *CastExp::semantic(Scope *sc) { Expression *e = op_overload(sc); if (e) - { return e->implicitCastTo(sc, to); - } } if (e1->op == TOKtemplate) @@ -10040,16 +9841,7 @@ Expression *CastExp::semantic(Scope *sc) } Lsafe: - /* Instantiate AA implementations during semantic analysis. - */ { - Type *tfrom = e1->type->toBasetype(); - Type *t = to->toBasetype(); - if (tfrom->ty == Taarray) - ((TypeAArray *)tfrom)->getImpl(); - if (t->ty == Taarray) - ((TypeAArray *)t)->getImpl(); - if (to->ty == Tvoid) { type = to; @@ -10081,20 +9873,6 @@ void CastExp::checkEscape() } } -void CastExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("cast("); - if (to) - to->toCBuffer(buf, NULL, hgs); - else - { - MODtoBuffer(buf, mod); - } - buf->writeByte(')'); - expToCBuffer(buf, hgs, e1, precedence[op]); -} - - /************************************************************/ VectorExp::VectorExp(Loc loc, Expression *e, Type *t) @@ -10115,27 +9893,21 @@ Expression *VectorExp::semantic(Scope *sc) #if LOGSEMANTIC printf("VectorExp::semantic('%s')\n", toChars()); #endif - if (type) return this; + e1 = e1->semantic(sc); type = to->semantic(loc, sc); if (e1->op == TOKerror || type->ty == Terror) return e1; + Type *tb = type->toBasetype(); assert(tb->ty == Tvector); TypeVector *tv = (TypeVector *)tb; Type *te = tv->elementType(); dim = (int)(tv->size(loc) / te->size(loc)); - return this; -} -void VectorExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("cast("); - to->toCBuffer(buf, NULL, hgs); - buf->writeByte(')'); - expToCBuffer(buf, hgs, e1, precedence[op]); + return this; } /************************************************************/ @@ -10165,18 +9937,18 @@ Expression *SliceExp::syntaxCopy() Expression *SliceExp::semantic(Scope *sc) { - Expression *e; - //FuncDeclaration *fd; - ScopeDsymbol *sym; - #if LOGSEMANTIC printf("SliceExp::semantic('%s')\n", toChars()); #endif if (type) return this; + Expression *e; + ScopeDsymbol *sym; + Lagain: - UnaExp::semantic(sc); + if (Expression *ex = unaSemantic(sc)) + return ex; e1 = resolveProperties(sc, e1); if (e1->op == TOKtype && e1->type->ty != Ttuple) { @@ -10233,7 +10005,10 @@ Expression *SliceExp::semantic(Scope *sc) if (search_function(ad, Id::slice)) { // Rewrite as e1.slice(lwr, upr) - Expression *e0 = resolveOpDollar(sc, this); + Expression *e0 = NULL; + Expression *ex = resolveOpDollar(sc, this, &e0); + if (ex->op == TOKerror) + return ex; Expressions *a = new Expressions(); assert(!lwr || upr); if (lwr) @@ -10243,9 +10018,8 @@ Expression *SliceExp::semantic(Scope *sc) } e = new DotIdExp(loc, e1, Id::slice); e = new CallExp(loc, e, a); - e = combine(e0, e); e = e->semantic(sc); - return e; + return Expression::combine(e0, e); } if (ad->aliasthis && e1->type != att1) { @@ -10322,16 +10096,19 @@ Expression *SliceExp::semantic(Scope *sc) uinteger_t i1 = lwr->toUInteger(); uinteger_t i2 = upr->toUInteger(); - size_t length; TupleExp *te; TypeTuple *tup; - + size_t length; if (e1->op == TOKtuple) // slicing an expression tuple - { te = (TupleExp *)e1; + { + te = (TupleExp *)e1; + tup = NULL; length = te->exps->dim; } else if (e1->op == TOKtype) // slicing a type tuple - { tup = (TypeTuple *)t; + { + te = NULL; + tup = (TypeTuple *)t; length = Parameter::dim(tup->arguments); } else @@ -10425,25 +10202,6 @@ int SliceExp::isBool(int result) return e1->isBool(result); } -void SliceExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - expToCBuffer(buf, hgs, e1, precedence[op]); - buf->writeByte('['); - if (upr || lwr) - { - if (lwr) - sizeToCBuffer(buf, hgs, lwr); - else - buf->writeByte('0'); - buf->writestring(".."); - if (upr) - sizeToCBuffer(buf, hgs, upr); - else - buf->writestring("$"); - } - buf->writeByte(']'); -} - /********************** ArrayLength **************************************/ ArrayLengthExp::ArrayLengthExp(Loc loc, Expression *e1) @@ -10456,13 +10214,14 @@ Expression *ArrayLengthExp::semantic(Scope *sc) #if LOGSEMANTIC printf("ArrayLengthExp::semantic('%s')\n", toChars()); #endif - if (!type) - { - UnaExp::semantic(sc); - e1 = resolveProperties(sc, e1); + if (type) + return this; - type = Type::tsize_t; - } + if (Expression *ex = unaSemantic(sc)) + return ex; + e1 = resolveProperties(sc, e1); + + type = Type::tsize_t; return this; } @@ -10525,10 +10284,118 @@ Expression *ArrayLengthExp::rewriteOpAssign(BinExp *exp) return e; } -void ArrayLengthExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +/*********************** IntervalExp ********************************/ + +// Mainly just a placeholder + +IntervalExp::IntervalExp(Loc loc, Expression *lwr, Expression *upr) + : Expression(loc, TOKinterval, sizeof(IntervalExp)) +{ + this->lwr = lwr; + this->upr = upr; +} + +Expression *IntervalExp::syntaxCopy() +{ + return new IntervalExp(loc, lwr->syntaxCopy(), upr->syntaxCopy()); +} + +Expression *IntervalExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("IntervalExp::semantic('%s')\n", toChars()); +#endif + if (type) + return this; + + Expression *le = lwr; + le = le->semantic(sc); + le = resolveProperties(sc, le); + + Expression *ue = upr; + ue = ue->semantic(sc); + ue = resolveProperties(sc, ue); + + if (le->op == TOKerror) + return le; + if (ue->op == TOKerror) + return ue; + + lwr = le; + upr = ue; + + type = Type::tvoid; + return this; +} + +/********************** DelegatePtrExp **************************************/ + +DelegatePtrExp::DelegatePtrExp(Loc loc, Expression *e1) + : UnaExp(loc, TOKdelegateptr, sizeof(DelegatePtrExp), e1) +{ +} + +Expression *DelegatePtrExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("DelegatePtrExp::semantic('%s')\n", toChars()); +#endif + if (!type) + { + unaSemantic(sc); + e1 = resolveProperties(sc, e1); + + if (e1->op == TOKerror) + return e1; + type = Type::tvoidptr; + } + return this; +} + +int DelegatePtrExp::isLvalue() +{ + return e1->isLvalue(); +} + +Expression *DelegatePtrExp::toLvalue(Scope *sc, Expression *e) +{ + e1 = e1->toLvalue(sc, e); + return this; +} + +/********************** DelegateFuncptrExp **************************************/ + +DelegateFuncptrExp::DelegateFuncptrExp(Loc loc, Expression *e1) + : UnaExp(loc, TOKdelegatefuncptr, sizeof(DelegateFuncptrExp), e1) +{ +} + +Expression *DelegateFuncptrExp::semantic(Scope *sc) +{ +#if LOGSEMANTIC + printf("DelegateFuncptrExp::semantic('%s')\n", toChars()); +#endif + if (!type) + { + unaSemantic(sc); + e1 = resolveProperties(sc, e1); + + if (e1->op == TOKerror) + return e1; + type = e1->type->nextOf()->pointerTo(); + } + return this; +} + +int DelegateFuncptrExp::isLvalue() { - expToCBuffer(buf, hgs, e1, PREC_primary); - buf->writestring(".length"); + return e1->isLvalue(); +} + +Expression *DelegateFuncptrExp::toLvalue(Scope *sc, Expression *e) +{ + e1 = e1->toLvalue(sc, e); + return this; } /*********************** ArrayExp *************************************/ @@ -10551,36 +10418,48 @@ Expression *ArrayExp::syntaxCopy() } Expression *ArrayExp::semantic(Scope *sc) -{ Expression *e; - Type *t1; - +{ #if LOGSEMANTIC printf("ArrayExp::semantic('%s')\n", toChars()); #endif - UnaExp::semantic(sc); - e1 = resolveProperties(sc, e1); - if (e1->op == TOKerror) - return e1; + if (Expression *ex = unaSemantic(sc)) + return ex; + Expression *e1x = resolveProperties(sc, e1); + if (e1x->op == TOKerror) + return e1x; + e1 = e1x; - t1 = e1->type->toBasetype(); + Type *t1 = e1->type->toBasetype(); if (t1->ty != Tclass && t1->ty != Tstruct) - { // Convert to IndexExp - if (arguments->dim != 1) - { error("only one index allowed to index %s", t1->toChars()); - goto Lerr; + { + // Convert to IndexExp + Expression *e; + if (arguments->dim == 0) + { + e = new SliceExp(loc, e1, NULL, NULL); + } + else if (arguments->dim == 1 && (*arguments)[0]->op == TOKinterval) + { + IntervalExp *ie = (IntervalExp *)(*arguments)[0]; + e = new SliceExp(loc, e1, ie->lwr, ie->upr); + } + else if (arguments->dim == 1) + { + e = new IndexExp(loc, e1, (*arguments)[0]); + } + else + { + error("only one index allowed to index %s", t1->toChars()); + return new ErrorExp(); } - e = new IndexExp(loc, e1, (*arguments)[0]); return e->semantic(sc); } - e = op_overload(sc); - if (!e) - { error("no [] operator overload for type %s", e1->type->toChars()); - goto Lerr; - } - return e; + Expression *e = op_overload(sc); + if (e) + return e; -Lerr: + error("no [] operator overload for type %s", e1->type->toChars()); return new ErrorExp(); } @@ -10599,15 +10478,6 @@ Expression *ArrayExp::toLvalue(Scope *sc, Expression *e) return this; } - -void ArrayExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - expToCBuffer(buf, hgs, e1, PREC_primary); - buf->writeByte('['); - argsToCBuffer(buf, arguments, hgs); - buf->writeByte(']'); -} - /************************* DotExp ***********************************/ DotExp::DotExp(Loc loc, Expression *e1, Expression *e2) @@ -10640,13 +10510,6 @@ Expression *DotExp::semantic(Scope *sc) return this; } -void DotExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - expToCBuffer(buf, hgs, e1, PREC_primary); - buf->writeByte('.'); - expToCBuffer(buf, hgs, e2, PREC_primary); -} - /************************* CommaExp ***********************************/ CommaExp::CommaExp(Loc loc, Expression *e1, Expression *e2) @@ -10656,11 +10519,14 @@ CommaExp::CommaExp(Loc loc, Expression *e1, Expression *e2) Expression *CommaExp::semantic(Scope *sc) { - if (!type) - { BinExp::semanticp(sc); - e1 = e1->addDtorHook(sc); - type = e2->type; - } + if (type) + return this; + + if (Expression *ex = binSemanticProp(sc)) + return ex; + e1 = e1->addDtorHook(sc); + + type = e2->type; return this; } @@ -10729,15 +10595,13 @@ Expression *IndexExp::syntaxCopy() } Expression *IndexExp::semantic(Scope *sc) -{ Expression *e; - Type *t1; - ScopeDsymbol *sym; - +{ #if LOGSEMANTIC printf("IndexExp::semantic('%s')\n", toChars()); #endif if (type) return this; + if (!e1->type) e1 = e1->semantic(sc); assert(e1->type); // semantic() should already be run on it @@ -10750,20 +10614,22 @@ Expression *IndexExp::semantic(Scope *sc) nt = new TypeAArray(e1->type, e2->type); else nt = new TypeSArray(e1->type, e2); - e = new TypeExp(loc, nt); + Expression *e = new TypeExp(loc, nt); return e->semantic(sc); } if (e1->op == TOKerror) - goto Lerr; - e = this; + return e1; + + Expression *e = this; // Note that unlike C we do not implement the int[ptr] - t1 = e1->type->toBasetype(); + Type *t1 = e1->type->toBasetype(); if (t1->ty == Tsarray || t1->ty == Tarray || t1->ty == Ttuple) - { // Create scope for 'length' variable - sym = new ArrayScopeSymbol(sc, this); + { + // Create scope for 'length' variable + ScopeDsymbol *sym = new ArrayScopeSymbol(sc, this); sym->loc = loc; sym->parent = sc->scopesym; sc = sc->push(sym); @@ -10774,7 +10640,7 @@ Expression *IndexExp::semantic(Scope *sc) e2 = resolveProperties(sc, e2); if (t1->ty == Ttuple) sc = sc->endCTFE(); if (e2->type == Type::terror) - goto Lerr; + return new ErrorExp(); if (e2->type->ty == Ttuple && ((TupleExp *)e2)->exps->dim == 1) // bug 4444 fix e2 = (*((TupleExp *)e2)->exps)[0]; @@ -10786,7 +10652,7 @@ Expression *IndexExp::semantic(Scope *sc) case Tpointer: e2 = e2->implicitCastTo(sc, Type::tsize_t); if (e2->type == Type::terror) - goto Lerr; + return new ErrorExp(); e2 = e2->optimize(WANTvalue); if (e2->op == TOKint64 && e2->toInteger() == 0) ; @@ -10802,7 +10668,7 @@ Expression *IndexExp::semantic(Scope *sc) case Tarray: e2 = e2->implicitCastTo(sc, Type::tsize_t); if (e2->type == Type::terror) - goto Lerr; + return new ErrorExp(); e->type = ((TypeNext *)t1)->next; break; @@ -10810,20 +10676,22 @@ Expression *IndexExp::semantic(Scope *sc) { e2 = e2->implicitCastTo(sc, Type::tsize_t); if (e2->type == Type::terror) - goto Lerr; - TypeSArray *tsa = (TypeSArray *)t1; + return new ErrorExp(); e->type = t1->nextOf(); break; } case Taarray: - { TypeAArray *taa = (TypeAArray *)t1; + { + TypeAArray *taa = (TypeAArray *)t1; /* We can skip the implicit conversion if they differ only by * constness (Bugzilla 2684, see also bug 2954b) */ if (!arrayTypeCompatibleWithoutCasting(e2->loc, e2->type, taa->index)) { e2 = e2->implicitCastTo(sc, taa->index); // type checking + if (e2->type == Type::terror) + return new ErrorExp(); } type = taa->next; break; @@ -10833,19 +10701,22 @@ Expression *IndexExp::semantic(Scope *sc) { e2 = e2->implicitCastTo(sc, Type::tsize_t); if (e2->type == Type::terror) - goto Lerr; + return new ErrorExp(); e2 = e2->ctfeInterpret(); uinteger_t index = e2->toUInteger(); - size_t length; + TupleExp *te; TypeTuple *tup; - + size_t length; if (e1->op == TOKtuple) - { te = (TupleExp *)e1; + { + te = (TupleExp *)e1; + tup = NULL; length = te->exps->dim; } else if (e1->op == TOKtype) { + te = NULL; tup = (TypeTuple *)t1; length = Parameter::dim(tup->arguments); } @@ -10867,18 +10738,19 @@ Expression *IndexExp::semantic(Scope *sc) { error("array index [%llu] is outside array bounds [0 .. %llu]", index, (ulonglong)length); - e = e1; + return new ErrorExp(); } break; } default: if (e1->op == TOKerror) - goto Lerr; + return e1; error("%s must be an array or pointer type, not %s", e1->toChars(), e1->type->toChars()); case Terror: - goto Lerr; + Lerror: + return new ErrorExp(); } if (t1->ty == Tsarray || t1->ty == Tarray) @@ -10891,14 +10763,11 @@ Expression *IndexExp::semantic(Scope *sc) e2 = e2->optimize(WANTvalue); dinteger_t length = el->toInteger(); if (length) - skipboundscheck = IntRange(SignExtendedNumber(0), SignExtendedNumber(length)).contains(e2->getIntRange()); + skipboundscheck = IntRange(SignExtendedNumber(0), SignExtendedNumber(length-1)).contains(getIntRange(e2)); } } return e; - -Lerr: - return new ErrorExp(); } int IndexExp::isLvalue() @@ -10928,7 +10797,7 @@ Expression *IndexExp::modifiableLvalue(Scope *sc, Expression *e) modifiable = 1; Type *t1 = e1->type->toBasetype(); if (t1->ty == Taarray) - { TypeAArray *taa = (TypeAArray *)t1; + { Type *t2b = e2->type->toBasetype(); if (t2b->ty == Tarray && t2b->nextOf()->isMutable()) error("associative arrays can only be assigned values with immutable keys, not %s", e2->type->toChars()); @@ -10939,15 +10808,6 @@ Expression *IndexExp::modifiableLvalue(Scope *sc, Expression *e) return Expression::modifiableLvalue(sc, e); } -void IndexExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - expToCBuffer(buf, hgs, e1, PREC_primary); - buf->writeByte('['); - sizeToCBuffer(buf, hgs, e2); - buf->writeByte(']'); -} - - /************************* PostExp ***********************************/ PostExp::PostExp(TOK op, Loc loc, Expression *e) @@ -10957,90 +10817,92 @@ PostExp::PostExp(TOK op, Loc loc, Expression *e) } Expression *PostExp::semantic(Scope *sc) -{ Expression *e = this; - +{ #if LOGSEMANTIC printf("PostExp::semantic('%s')\n", toChars()); #endif - if (!type) - { - BinExp::semantic(sc); - e1 = resolveProperties(sc, e1); + if (type) + return this; - e = op_overload(sc); - if (e) - return e; + if (Expression *ex = binSemantic(sc)) + return ex; + Expression *e1x = resolveProperties(sc, e1); + if (e1x->op == TOKerror) + return e1x; + e1 = e1x; - if (e1->op == TOKslice) - { - const char *s = op == TOKplusplus ? "increment" : "decrement"; - error("cannot post-%s array slice '%s', use pre-%s instead", s, e1->toChars(), s); - return new ErrorExp(); - } + Expression *e = op_overload(sc); + if (e) + return e; - e1 = e1->optimize(WANTvalue); - if (e1->op != TOKarraylength) - e1 = e1->modifiableLvalue(sc, e1); + e = e1->checkReadModifyWrite(op); + if (e) + return e; - Type *t1 = e1->type->toBasetype(); - if (t1->ty == Tclass || t1->ty == Tstruct || e1->op == TOKarraylength) - { /* Check for operator overloading, - * but rewrite in terms of ++e instead of e++ - */ + if (e1->op == TOKslice) + { + const char *s = op == TOKplusplus ? "increment" : "decrement"; + error("cannot post-%s array slice '%s', use pre-%s instead", s, e1->toChars(), s); + return new ErrorExp(); + } - /* If e1 is not trivial, take a reference to it - */ - Expression *de = NULL; - if (e1->op != TOKvar && e1->op != TOKarraylength) - { - // ref v = e1; - Identifier *id = Lexer::uniqueId("__postref"); - ExpInitializer *ei = new ExpInitializer(loc, e1); - VarDeclaration *v = new VarDeclaration(loc, e1->type, id, ei); - v->storage_class |= STCtemp | STCref | STCforeach; - de = new DeclarationExp(loc, v); - e1 = new VarExp(e1->loc, v); - } + e1 = e1->optimize(WANTvalue); + if (e1->op != TOKarraylength) + e1 = e1->modifiableLvalue(sc, e1); - /* Rewrite as: - * auto tmp = e1; ++e1; tmp - */ - Identifier *id = Lexer::uniqueId("__pitmp"); + Type *t1 = e1->type->toBasetype(); + if (t1->ty == Tclass || t1->ty == Tstruct || e1->op == TOKarraylength) + { + /* Check for operator overloading, + * but rewrite in terms of ++e instead of e++ + */ + + /* If e1 is not trivial, take a reference to it + */ + Expression *de = NULL; + if (e1->op != TOKvar && e1->op != TOKarraylength) + { + // ref v = e1; + Identifier *id = Lexer::uniqueId("__postref"); ExpInitializer *ei = new ExpInitializer(loc, e1); - VarDeclaration *tmp = new VarDeclaration(loc, e1->type, id, ei); - tmp->storage_class |= STCtemp; - Expression *ea = new DeclarationExp(loc, tmp); + VarDeclaration *v = new VarDeclaration(loc, e1->type, id, ei); + v->storage_class |= STCtemp | STCref | STCforeach; + de = new DeclarationExp(loc, v); + e1 = new VarExp(e1->loc, v); + } - Expression *eb = e1->syntaxCopy(); - eb = new PreExp(op == TOKplusplus ? TOKpreplusplus : TOKpreminusminus, loc, eb); + /* Rewrite as: + * auto tmp = e1; ++e1; tmp + */ + Identifier *id = Lexer::uniqueId("__pitmp"); + ExpInitializer *ei = new ExpInitializer(loc, e1); + VarDeclaration *tmp = new VarDeclaration(loc, e1->type, id, ei); + tmp->storage_class |= STCtemp; + Expression *ea = new DeclarationExp(loc, tmp); - Expression *ec = new VarExp(loc, tmp); + Expression *eb = e1->syntaxCopy(); + eb = new PreExp(op == TOKplusplus ? TOKpreplusplus : TOKpreminusminus, loc, eb); - // Combine de,ea,eb,ec - if (de) - ea = new CommaExp(loc, de, ea); - e = new CommaExp(loc, ea, eb); - e = new CommaExp(loc, e, ec); - e = e->semantic(sc); - return e; - } + Expression *ec = new VarExp(loc, tmp); - e = this; - e1->checkScalar(); - e1->checkNoBool(); - if (e1->type->ty == Tpointer) - e = scaleFactor(sc); - else - e2 = e2->castTo(sc, e1->type); - e->type = e1->type; + // Combine de,ea,eb,ec + if (de) + ea = new CommaExp(loc, de, ea); + e = new CommaExp(loc, ea, eb); + e = new CommaExp(loc, e, ec); + e = e->semantic(sc); + return e; } - return e; -} -void PostExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - expToCBuffer(buf, hgs, e1, precedence[op]); - buf->writestring(Token::toChars(op)); + e = this; + e1->checkScalar(); + e1->checkNoBool(); + if (e1->type->ty == Tpointer) + e = scaleFactor(this, sc); + else + e2 = e2->castTo(sc, e1->type); + e->type = e1->type; + return e; } /************************* PreExp ***********************************/ @@ -11052,9 +10914,9 @@ PreExp::PreExp(TOK op, Loc loc, Expression *e) Expression *PreExp::semantic(Scope *sc) { - Expression *e; + Expression *e = op_overload(sc); + // printf("PreExp::semantic('%s')\n", toChars()); - e = op_overload(sc); if (e) return e; @@ -11066,12 +10928,6 @@ Expression *PreExp::semantic(Scope *sc) return e->semantic(sc); } -void PreExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring(Token::toChars(op)); - expToCBuffer(buf, hgs, e1, precedence[op]); -} - /************************************************************/ /* op can be TOKassign, TOKconstruct, or TOKblit */ @@ -11084,24 +10940,23 @@ AssignExp::AssignExp(Loc loc, Expression *e1, Expression *e2) Expression *AssignExp::semantic(Scope *sc) { - Expression *e1old = e1; - #if LOGSEMANTIC printf("AssignExp::semantic('%s')\n", toChars()); #endif //printf("e1->op = %d, '%s'\n", e1->op, Token::toChars(e1->op)); //printf("e2->op = %d, '%s'\n", e2->op, Token::toChars(e2->op)); - if (type) return this; + Expression *e1old = e1; + if (e2->op == TOKcomma) { /* Rewrite to get rid of the comma from rvalue */ - AssignExp *ea = new AssignExp(loc, e1, ((CommaExp *)e2)->e2); - ea->op = op; - Expression *e = new CommaExp(loc, ((CommaExp *)e2)->e1, ea); + Expression *e0; + e2 = Expression::extractLast(e2, &e0); + Expression *e = Expression::combine(e0, this); return e->semantic(sc); } @@ -11114,42 +10969,75 @@ Expression *AssignExp::semantic(Scope *sc) ArrayExp *ae = (ArrayExp *)e1; ae->e1 = ae->e1->semantic(sc); ae->e1 = resolveProperties(sc, ae->e1); - Expression *ae1old = ae->e1; Type *t1 = ae->e1->type->toBasetype(); AggregateDeclaration *ad = isAggregate(t1); if (ad) { - L1: + Expression *e0 = NULL; + // Rewrite (a[i] = value) to (a.opIndexAssign(value, i)) if (search_function(ad, Id::indexass)) { // Deal with $ - Expression *e0 = resolveOpDollar(sc, ae); + Expression *ex = resolveOpDollar(sc, ae, &e0); + if (!ex) + goto Lfallback; + if (ex->op == TOKerror) + return ex; + + Expression *e2x = e2->semantic(sc); + if (e2x->op == TOKerror) + return e2x; + e2 = e2x; + Expressions *a = (Expressions *)ae->arguments->copy(); a->insert(0, e2); Expression *e = new DotIdExp(loc, ae->e1, Id::indexass); e = new CallExp(loc, e, a); - e = combine(e0, e); - e = e->semantic(sc); - return e; + if (ae->arguments->dim == 0) + e = e->trySemantic(sc); + else + e = e->semantic(sc); + if (!e) + goto Lfallback; + return Expression::combine(e0, e); } - } - // No opIndexAssign found yet, but there might be an alias this to try. - if (ad && ad->aliasthis && t1 != att1) - { - if (!att1 && t1->checkAliasThisRec()) - att1 = t1; - ae->e1 = resolveAliasThis(sc, ae->e1); - t1 = ae->e1->type->toBasetype(); - ad = isAggregate(t1); - if (ad) - goto L1; - } + // No opIndexAssign found yet, but there might be an alias this to try. + if (ad->aliasthis && t1 != ae->att1) + { + ArrayExp *aex = (ArrayExp *)ae->copy(); + if (!aex->att1 && t1->checkAliasThisRec()) + aex->att1 = t1; + aex->e1 = new DotIdExp(loc, ae->e1, ad->aliasthis->ident); + this->e1 = aex; + Expression *ex = this->trySemantic(sc); + if (ex) + return ex; + this->e1 = ae; // restore + } - ae->e1 = ae1old; // restore + Lfallback: + if (ae->arguments->dim == 0) + { + // a[] = e2 + SliceExp *se = new SliceExp(ae->loc, ae->e1, NULL, NULL); + se->att1 = ae->att1; + this->e1 = se; + return Expression::combine(e0, this->semantic(sc)); + } + if (ae->arguments->dim == 1 && (*ae->arguments)[0]->op == TOKinterval) + { + // a[lwr..upr] = e2 + IntervalExp *ie = (IntervalExp *)(*ae->arguments)[0]; + SliceExp *se = new SliceExp(ae->loc, ae->e1, ie->lwr, ie->upr); + se->att1 = ae->att1; + this->e1 = se; + return Expression::combine(e0, this->semantic(sc)); + } + } } /* Look for operator overloading of a[i..j]=value. * Do it before semantic() otherwise the a[i..j] will have been @@ -11160,17 +11048,18 @@ Expression *AssignExp::semantic(Scope *sc) SliceExp *ae = (SliceExp *)e1; ae->e1 = ae->e1->semantic(sc); ae->e1 = resolveProperties(sc, ae->e1); - Expression *ae1old = ae->e1; Type *t1 = ae->e1->type->toBasetype(); AggregateDeclaration *ad = isAggregate(t1); if (ad) { - L2: // Rewrite (a[i..j] = value) to (a.opSliceAssign(value, i, j)) if (search_function(ad, Id::sliceass)) { - Expression *e0 = resolveOpDollar(sc, ae); + Expression *e0 = NULL; + Expression *ex = resolveOpDollar(sc, ae, &e0); + if (ex->op == TOKerror) + return ex; Expressions *a = new Expressions(); a->push(e2); assert(!ae->lwr || ae->upr); @@ -11181,138 +11070,158 @@ Expression *AssignExp::semantic(Scope *sc) } Expression *e = new DotIdExp(loc, ae->e1, Id::sliceass); e = new CallExp(loc, e, a); - e = combine(e0, e); e = e->semantic(sc); - return e; + return Expression::combine(e0, e); } - } - // No opSliceAssign found yet, but there might be an alias this to try. - if (ad && ad->aliasthis && t1 != att1) - { - if (!att1 && t1->checkAliasThisRec()) - att1 = t1; - ae->e1 = resolveAliasThis(sc, ae->e1); - t1 = ae->e1->type->toBasetype(); - ad = isAggregate(t1); - if (ad) - goto L2; + // No opSliceAssign found yet, but there might be an alias this to try. + if (ad->aliasthis && t1 != ae->att1) + { + SliceExp *aex = (SliceExp *)ae->copy(); + if (!aex->att1 && t1->checkAliasThisRec()) + aex->att1 = t1; + aex->e1 = new DotIdExp(loc, ae->e1, ad->aliasthis->ident); + this->e1 = aex; + Expression *ex = this->trySemantic(sc); + if (ex) + return ex; + this->e1 = ae; // restore + } } - - ae->e1 = ae1old; // restore } - /* With UFCS, e.f = value - * Could mean: - * .f(e, value) - * or: - * .f(e) = value + /* Run this->e1 semantic. */ - if (e1->op == TOKdotti) - { - DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)e1; - Expression *e = dti->semanticY(sc, 1); - if (!e) - return resolveUFCSProperties(sc, e1, e2); - e1 = e; - } - else if (e1->op == TOKdot) { - DotIdExp *die = (DotIdExp *)e1; - Expression *e = die->semanticY(sc, 1); - if (e && isDotOpDispatch(e)) + Expression *e1x = e1; + + /* With UFCS, e.f = value + * Could mean: + * .f(e, value) + * or: + * .f(e) = value + */ + if (e1x->op == TOKdotti) { - unsigned errors = global.startGagging(); - e = resolvePropertiesX(sc, e, e2); - if (global.endGagging(errors)) - e = NULL; /* fall down to UFCS */ - else - return e; + DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)e1x; + Expression *e = dti->semanticY(sc, 1); + if (!e) + return resolveUFCSProperties(sc, e1x, e2); + e1x = e; } - if (!e) - return resolveUFCSProperties(sc, e1, e2); - e1 = e; - } - else - e1 = e1->semantic(sc); - if (e1->op == TOKerror) - return new ErrorExp(); + else if (e1x->op == TOKdot) + { + DotIdExp *die = (DotIdExp *)e1x; + Expression *e = die->semanticY(sc, 1); + if (e && isDotOpDispatch(e)) + { + unsigned errors = global.startGagging(); + e = resolvePropertiesX(sc, e, e2); + if (global.endGagging(errors)) + e = NULL; /* fall down to UFCS */ + else + return e; + } + if (!e) + return resolveUFCSProperties(sc, e1x, e2); + e1x = e; + } + else + e1x = e1x->semantic(sc); - /* We have f = value. - * Could mean: - * f(value) - * or: - * f() = value - */ - if (Expression *e = resolvePropertiesX(sc, e1, e2)) - return e; + /* We have f = value. + * Could mean: + * f(value) + * or: + * f() = value + */ + if (Expression *e = resolvePropertiesX(sc, e1x, e2)) + return e; - e1 = checkRightThis(sc, e1); + e1x = checkRightThis(sc, e1x); - assert(e1->type); + if (e1x->op == TOKerror) + return e1x; + e1 = e1x; + assert(e1->type); + } Type *t1 = e1->type->toBasetype(); - e2 = e2->inferType(t1); + /* Run this->e2 semantic. + * Different from other binary expressions, the analysis of e2 + * depends on the result of e1 in assignments. + */ + { + Expression *e2x = inferType(e2, t1); - e2 = e2->semantic(sc); - if (e2->op == TOKerror) - return new ErrorExp(); - e2 = resolveProperties(sc, e2); - if (!e2->rvalue()) - return new ErrorExp(); + e2x = e2x->semantic(sc); + e2x = resolveProperties(sc, e2x); + + if (e2x->op == TOKerror) + return e2x; + if (!e2x->rvalue()) + return new ErrorExp(); + e2 = e2x; + } /* Rewrite tuple assignment as a tuple of assignments. */ -Ltupleassign: - if (e1->op == TOKtuple && e2->op == TOKtuple) { - TupleExp *tup1 = (TupleExp *)e1; - TupleExp *tup2 = (TupleExp *)e2; - size_t dim = tup1->exps->dim; - Expression *e = NULL; - if (dim != tup2->exps->dim) - { - error("mismatched tuple lengths, %d and %d", (int)dim, (int)tup2->exps->dim); - return new ErrorExp(); - } - if (dim == 0) - { - e = new IntegerExp(loc, 0, Type::tint32); - e = new CastExp(loc, e, Type::tvoid); // avoid "has no effect" error - e = combine(combine(tup1->e0, tup2->e0), e); - } - else + Expression *e2x = e2; + + Ltupleassign: + if (e1->op == TOKtuple && e2x->op == TOKtuple) { - Expressions *exps = new Expressions; - exps->setDim(dim); - for (size_t i = 0; i < dim; i++) + TupleExp *tup1 = (TupleExp *)e1; + TupleExp *tup2 = (TupleExp *)e2x; + size_t dim = tup1->exps->dim; + Expression *e = NULL; + if (dim != tup2->exps->dim) { - Expression *ex1 = (*tup1->exps)[i]; - Expression *ex2 = (*tup2->exps)[i]; - (*exps)[i] = new AssignExp(loc, ex1, ex2); + error("mismatched tuple lengths, %d and %d", (int)dim, (int)tup2->exps->dim); + return new ErrorExp(); + } + if (dim == 0) + { + e = new IntegerExp(loc, 0, Type::tint32); + e = new CastExp(loc, e, Type::tvoid); // avoid "has no effect" error + e = combine(combine(tup1->e0, tup2->e0), e); } - e = new TupleExp(loc, combine(tup1->e0, tup2->e0), exps); + else + { + Expressions *exps = new Expressions; + exps->setDim(dim); + for (size_t i = 0; i < dim; i++) + { + Expression *ex1 = (*tup1->exps)[i]; + Expression *ex2 = (*tup2->exps)[i]; + (*exps)[i] = new AssignExp(loc, ex1, ex2); + } + e = new TupleExp(loc, combine(tup1->e0, tup2->e0), exps); + } + return e->semantic(sc); } - assert(e); - return e->semantic(sc); - } - if (e1->op == TOKtuple) - { - if (TupleDeclaration *td = isAliasThisTuple(e2)) + /* Look for form: e1 = e2->aliasthis. + */ + if (e1->op == TOKtuple) { + TupleDeclaration *td = isAliasThisTuple(e2x); + if (!td) + goto Lnomatch; + assert(e1->type->ty == Ttuple); TypeTuple *tt = (TypeTuple *)e1->type; Identifier *id = Lexer::uniqueId("__tup"); - ExpInitializer *ei = new ExpInitializer(e2->loc, e2); - VarDeclaration *v = new VarDeclaration(e2->loc, NULL, id, ei); + ExpInitializer *ei = new ExpInitializer(e2x->loc, e2x); + VarDeclaration *v = new VarDeclaration(e2x->loc, NULL, id, ei); v->storage_class |= STCtemp | STCctfe; - if (e2->isLvalue()) + if (e2x->isLvalue()) v->storage_class = STCref | STCforeach; - Expression *e0 = new DeclarationExp(e2->loc, v); - Expression *ev = new VarExp(e2->loc, v); - ev->type = e2->type; + Expression *e0 = new DeclarationExp(e2x->loc, v); + Expression *ev = new VarExp(e2x->loc, v); + ev->type = e2x->type; Expressions *iexps = new Expressions(); iexps->push(ev); @@ -11327,24 +11236,32 @@ Expression *AssignExp::semantic(Scope *sc) //printf("e = (%s %s, %s), ", Token::tochars[e->op], e->toChars(), e->type->toChars()); //printf("arg = (%s, %s)\n", arg->toChars(), arg->type->toChars()); - if (!e->type->implicitConvTo(arg->type)) + if (!arg || !e->type->implicitConvTo(arg->type)) { // expand initializer to tuple if (expandAliasThisTuples(iexps, u) != -1) + { + if (iexps->dim <= u) + break; goto Lexpand; - + } goto Lnomatch; } } - e2 = new TupleExp(e2->loc, e0, iexps); - e2 = e2->semantic(sc); + e2x = new TupleExp(e2x->loc, e0, iexps); + e2x = e2x->semantic(sc); + if (e2x->op == TOKerror) + return e2x; + // Do not need to overwrite this->e2 goto Ltupleassign; - - Lnomatch: - ; } + Lnomatch: + ; } + /* Inside constructor, if this is the first assignment of object field, + * rewrite this to initializing the field. + */ if (op == TOKassign && e1->checkModifiable(sc) == 2) { //printf("[%s] change to init - %s\n", loc.toChars(), toChars()); @@ -11362,19 +11279,22 @@ Expression *AssignExp::semantic(Scope *sc) } else if (t1->ty == Tstruct) { + Expression *e1x = e1; + Expression *e2x = e2; StructDeclaration *sd = ((TypeStruct *)t1)->sym; + if (op == TOKconstruct) { - Type *t2 = e2->type->toBasetype(); + Type *t2 = e2x->type->toBasetype(); if (t2->ty == Tstruct && sd == ((TypeStruct *)t2)->sym) { CallExp *ce; DotVarExp *dve; - if (sd->ctor && // there are constructors - e2->op == TOKcall && - (ce = (CallExp *)e2, ce->e1->op == TOKdotvar) && + if (sd->ctor && + e2x->op == TOKcall && + (ce = (CallExp *)e2x, ce->e1->op == TOKdotvar) && (dve = (DotVarExp *)ce->e1, dve->var->isCtorDeclaration()) && - e2->type->implicitConvTo(t1)) + e2x->type->implicitConvTo(t1)) { /* Look for form of constructor call which is: * __ctmp.ctor(arguments...) @@ -11392,14 +11312,14 @@ Expression *AssignExp::semantic(Scope *sc) else ae->e2 = t1->defaultInit(loc); // Keep ae->op == TOKconstruct - ae->type = e1->type; + ae->type = e1x->type; /* Replace __ctmp being constructed with e1. * We need to copy constructor call expression, * because it may be used in other place. */ DotVarExp *dvx = (DotVarExp *)dve->copy(); - dvx->e1 = this->e1; + dvx->e1 = e1x; CallExp *cx = (CallExp *)ce->copy(); cx->e1 = dvx; @@ -11411,28 +11331,33 @@ Expression *AssignExp::semantic(Scope *sc) { /* We have a copy constructor for this */ - if (e2->op == TOKquestion) + if (e2x->op == TOKquestion) { /* Rewrite as: * a ? e1 = b : e1 = c; */ - CondExp *econd = (CondExp *)e2; - Expression *ea1 = new ConstructExp(econd->e1->loc, e1, econd->e1); - Expression *ea2 = new ConstructExp(econd->e1->loc, e1, econd->e2); + CondExp *econd = (CondExp *)e2x; + Expression *ea1 = new ConstructExp(econd->e1->loc, e1x, econd->e1); + Expression *ea2 = new ConstructExp(econd->e1->loc, e1x, econd->e2); Expression *e = new CondExp(loc, econd->econd, ea1, ea2); return e->semantic(sc); } - if (e2->isLvalue()) + if (e2x->isLvalue()) { /* Rewrite as: * e1.cpctor(e2); */ - if (!e2->type->implicitConvTo(e1->type)) - error("conversion error from %s to %s", e2->type->toChars(), e1->type->toChars()); + if (!e2x->type->implicitConvTo(e1x->type)) + { + error("conversion error from %s to %s", e2x->type->toChars(), e1x->type->toChars()); + return new ErrorExp(); + } - Expression *e = new DotVarExp(loc, e1, sd->cpctor, 0); - e = new CallExp(loc, e, e2); + e1x = e1x->copy(); + e1x->type = e1x->type->mutableOf(); + Expression *e = new DotVarExp(loc, e1x, sd->cpctor, 0); + e = new CallExp(loc, e, e2x); return e->semantic(sc); } else @@ -11440,11 +11365,11 @@ Expression *AssignExp::semantic(Scope *sc) /* The struct value returned from the function is transferred * so should not call the destructor on it. */ - e2 = valueNoDtor(e2); + e2x = valueNoDtor(e2x); } } } - else if (!e2->implicitConvTo(t1)) + else if (!e2x->implicitConvTo(t1)) { if (sd->ctor) { @@ -11452,41 +11377,40 @@ Expression *AssignExp::semantic(Scope *sc) * Rewrite as: * e1 = init, e1.ctor(e2) */ - Expression *ex; - ex = new AssignExp(loc, e1, e1->type->defaultInit(loc)); - ex->op = TOKblit; - ex->type = e1->type; + Expression *einit; + einit = new BlitExp(loc, e1x, e1x->type->defaultInit(loc)); + einit->type = e1x->type; Expression *e; - e = new DotIdExp(loc, e1, Id::ctor); - e = new CallExp(loc, e, e2); - e = new CommaExp(loc, ex, e); + e = new DotIdExp(loc, e1x, Id::ctor); + e = new CallExp(loc, e, e2x); + e = new CommaExp(loc, einit, e); e = e->semantic(sc); return e; } - else if (search_function(sd, Id::call)) + if (search_function(sd, Id::call)) { /* Look for static opCall * (See bugzilla 2702 for more discussion) * Rewrite as: * e1 = typeof(e1).opCall(arguments) */ - Expression *e = typeDotIdExp(e2->loc, e1->type, Id::call); - e2 = new CallExp(loc, e, e2); - - e2 = e2->semantic(sc); - if (e2->op == TOKerror) - return new ErrorExp(); - e2 = resolveProperties(sc, e2); - if (!e2->rvalue()) + e2x = typeDotIdExp(e2x->loc, e1x->type, Id::call); + e2x = new CallExp(loc, e2x, this->e2); + + e2x = e2x->semantic(sc); + e2x = resolveProperties(sc, e2x); + if (e2x->op == TOKerror) + return e2x; + if (!e2x->rvalue()) return new ErrorExp(); } } } else if (op == TOKassign) { - if (e1->op == TOKindex && - ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) + if (e1x->op == TOKindex && + ((IndexExp *)e1x)->e1->type->toBasetype()->ty == Taarray) { /* * Rewrite: @@ -11499,42 +11423,39 @@ Expression *AssignExp::semantic(Scope *sc) * ? __aatmp[__aakey].opAssign(__aaval) * : ConstructExp(__aatmp[__aakey], __aaval)); */ - IndexExp *ie = (IndexExp *)e1; - Type *t2 = e2->type->toBasetype(); + IndexExp *ie = (IndexExp *)e1x; + Type *t2 = e2x->type->toBasetype(); Expression *e0 = NULL; Expression *ea = ie->e1; Expression *ek = ie->e2; - Expression *ev = e2; - if (ea->hasSideEffect()) + Expression *ev = e2x; + if (!isTrivialExp(ea)) { VarDeclaration *v = new VarDeclaration(loc, ie->e1->type, Lexer::uniqueId("__aatmp"), new ExpInitializer(loc, ie->e1)); - v->storage_class |= STCtemp | STCctfe; - if (ea->isLvalue()) - v->storage_class |= STCforeach | STCref; + v->storage_class |= STCtemp | STCctfe + | (ea->isLvalue() ? STCforeach | STCref : STCrvalue); v->semantic(sc); e0 = combine(e0, new DeclarationExp(loc, v)); ea = new VarExp(loc, v); } - if (ek->hasSideEffect()) + if (!isTrivialExp(ek)) { VarDeclaration *v = new VarDeclaration(loc, ie->e2->type, Lexer::uniqueId("__aakey"), new ExpInitializer(loc, ie->e2)); - v->storage_class |= STCtemp | STCctfe; - if (ek->isLvalue()) - v->storage_class |= STCforeach | STCref; + v->storage_class |= STCtemp | STCctfe + | (ek->isLvalue() ? STCforeach | STCref : STCrvalue); v->semantic(sc); e0 = combine(e0, new DeclarationExp(loc, v)); ek = new VarExp(loc, v); } - if (ev->hasSideEffect()) + if (!isTrivialExp(ev)) { - VarDeclaration *v = new VarDeclaration(loc, e2->type, - Lexer::uniqueId("__aaval"), new ExpInitializer(loc, e2)); - v->storage_class |= STCtemp | STCctfe; - if (ev->isLvalue()) - v->storage_class |= STCforeach | STCref; + VarDeclaration *v = new VarDeclaration(loc, e2x->type, + Lexer::uniqueId("__aaval"), new ExpInitializer(loc, e2x)); + v->storage_class |= STCtemp | STCctfe + | (ev->isLvalue() ? STCforeach | STCref : STCrvalue); v->semantic(sc); e0 = combine(e0, new DeclarationExp(loc, v)); ev = new VarExp(loc, v); @@ -11547,7 +11468,6 @@ Expression *AssignExp::semantic(Scope *sc) ae->e1 = ae->e1->semantic(sc); ae->e1 = ae->e1->optimize(WANTvalue); ae->e2 = ev; - //Expression *e = new CallExp(loc, new DotIdExp(loc, ex, Id::assign), ev); Expression *e = ae->op_overload(sc); if (e) { @@ -11592,6 +11512,9 @@ Expression *AssignExp::semantic(Scope *sc) } else assert(op == TOKblit); + + e1 = e1x; + e2 = e2x; } else if (t1->ty == Tclass) { @@ -11605,122 +11528,52 @@ Expression *AssignExp::semantic(Scope *sc) } else if (t1->ty == Tsarray) { - Type *t2 = e2->type->toBasetype(); + // SliceExp cannot have static array type without context inference. + assert(e1->op != TOKslice); - if (e1->op == TOKindex && - ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) - { - // Assignment to an AA of fixed-length arrays. - // Convert T[n][U] = T[] into T[n][U] = T[n] - e2 = e2->implicitCastTo(sc, e1->type); - if (e2->op == TOKerror) - return e2; - } - else if (op == TOKconstruct) + Expression *e1x = e1; + Expression *e2x = e2; + Type *t2 = e2x->type->toBasetype(); + + if (e2x->implicitConvTo(e1x->type)) { - Expression *e2x = e2; - if (e2x->op == TOKslice) - { - SliceExp *se = (SliceExp *)e2; - if (se->lwr == NULL && se->e1->implicitConvTo(e1->type)) - { - e2x = se->e1; - } - } - if (e2x->op == TOKcall && !e2x->isLvalue() && - e2x->implicitConvTo(e1->type)) + if (op != TOKblit && + (e2x->op == TOKslice && ((UnaExp *)e2x)->e1->isLvalue() || + e2x->op == TOKcast && ((UnaExp *)e2x)->e1->isLvalue() || + e2x->op != TOKslice && e2x->isLvalue())) { - // Keep the expression form for NRVO - e2 = e2x->implicitCastTo(sc, e1->type); - if (e2->op == TOKerror) - return e2; + e1x->checkPostblit(sc, t1); } - else + } + else + { + if (e2x->implicitConvTo(t1->nextOf()->arrayOf()) > MATCHnomatch) { - /* Rewrite: - * sa = e; as: sa[] = e; - * sa = arr; as: sa[] = arr[]; - * sa = [...]; as: sa[] = [...]; - */ - // Convert e2 to e2[], if t2 is impllicitly convertible to t1. - if (e2->op != TOKarrayliteral && t2->ty == Tsarray && t2->implicitConvTo(t1)) + uinteger_t dim1 = ((TypeSArray *)t1)->dim->toInteger(); + uinteger_t dim2 = dim1; + if (e2x->op == TOKarrayliteral) { - e2 = new SliceExp(e2->loc, e2, NULL, NULL); - e2 = e2->semantic(sc); + ArrayLiteralExp *ale = (ArrayLiteralExp *)e2x; + dim2 = ale->elements ? ale->elements->dim : 0; } - else if (!e2->implicitConvTo(e1->type)) + else if (e2x->op == TOKslice) { - // If multidimensional static array, treat as one large array - dinteger_t dim = ((TypeSArray *)t1)->dim->toInteger(); - Type *t = t1; - while (1) - { - t = t->nextOf()->toBasetype(); - if (t->ty != Tsarray) - break; - dim *= ((TypeSArray *)t)->dim->toInteger(); - e1->type = t->nextOf()->sarrayOf(dim); - } + Type *tx = toStaticArrayType((SliceExp *)e2x); + if (tx) + dim2 = ((TypeSArray *)tx)->dim->toInteger(); } - - // Convert e1 to e1[] - e1 = new SliceExp(e1->loc, e1, NULL, NULL); - e1 = e1->semantic(sc); - t1 = e1->type->toBasetype(); - } - } - else if (op == TOKassign) - { - /* Rewrite: - * sa = e; as: sa[] = e; - * sa = arr; as: sa[] = arr[]; - * sa = [...]; as: sa[] = [...]; - */ - - // Convert e2 to e2[], unless e2-> e1[0] - if (e2->op != TOKarrayliteral && t2->ty == Tsarray && !t2->implicitConvTo(t1->nextOf())) - { - e2 = new SliceExp(e2->loc, e2, NULL, NULL); - e2 = e2->semantic(sc); - } - else if (0 && global.params.warnings && !global.gag && op == TOKassign && - e2->op != TOKarrayliteral && e2->op != TOKstring && - !e2->implicitConvTo(t1)) - { // Disallow sa = da (Converted to sa[] = da[]) - // Disallow sa = e (Converted to sa[] = e) - const char* e1str = e1->toChars(); - const char* e2str = e2->toChars(); - if (e2->op == TOKslice || e2->implicitConvTo(t1->nextOf())) - warning("explicit element-wise assignment (%s)[] = %s is better than %s = %s", - e1str, e2str, e1str, e2str); - else - warning("explicit element-wise assignment (%s)[] = (%s)[] is better than %s = %s", - e1str, e2str, e1str, e2str); - - // Convert e2 to e2[] to avoid duplicated error message. - if (t2->ty == Tarray) + if (dim1 != dim2) { - Expression *e = new SliceExp(e2->loc, e2, NULL, NULL); - e2 = e->semantic(sc); + error("mismatched array lengths, %d and %d", (int)dim1, (int)dim2); + return new ErrorExp(); } } - // Convert e1 to e1[] - e1 = new SliceExp(e1->loc, e1, NULL, NULL); - e1 = e1->semantic(sc); - t1 = e1->type->toBasetype(); - } - else - { - assert(op == TOKblit); - - if (!e2->implicitConvTo(e1->type)) + // May be block or element-wise assignment, so + // convert e1 to e1[] + if (op != TOKassign) { - /* Internal handling for the default initialization - * of multi-dimentional static array: - * T[2][3] sa; // = T.init; if T is zero-init - */ - // Treat e1 as one large array + // If multidimensional static array, treat as one large array dinteger_t dim = ((TypeSArray *)t1)->dim->toInteger(); Type *t = t1; while (1) @@ -11729,13 +11582,20 @@ Expression *AssignExp::semantic(Scope *sc) if (t->ty != Tsarray) break; dim *= ((TypeSArray *)t)->dim->toInteger(); - e1->type = t->nextOf()->sarrayOf(dim); + e1x->type = t->nextOf()->sarrayOf(dim); } } - e1 = new SliceExp(loc, e1, NULL, NULL); - e1 = e1->semantic(sc); - t1 = e1->type->toBasetype(); + e1x = new SliceExp(e1x->loc, e1x, NULL, NULL); + e1x = e1x->semantic(sc); } + if (e1x->op == TOKerror) + return e1x; + if (e2x->op == TOKerror) + return e2x; + + e1 = e1x; + e2 = e2x; + t1 = e1x->type->toBasetype(); } /* Check the mutability of e1. @@ -11745,9 +11605,11 @@ Expression *AssignExp::semantic(Scope *sc) // e1 is not an lvalue, but we let code generator handle it ArrayLengthExp *ale = (ArrayLengthExp *)e1; - ale->e1 = ale->e1->modifiableLvalue(sc, e1); - if (ale->e1->op == TOKerror) - return ale->e1; + Expression *ale1x = ale->e1; + ale1x = ale1x->modifiableLvalue(sc, e1); + if (ale1x->op == TOKerror) + return ale1x; + ale->e1 = ale1x; Type *tn = ale->e1->type->toBasetype()->nextOf(); checkDefCtor(ale->loc, tn); @@ -11761,21 +11623,40 @@ Expression *AssignExp::semantic(Scope *sc) error("slice %s is not mutable", e1->toChars()); return new ErrorExp(); } + + // For conditional operator, both branches need conversion. + SliceExp *se = (SliceExp *)e1; + while (se->e1->op == TOKslice) + se = (SliceExp *)se->e1; + if (se->e1->op == TOKquestion && + se->e1->type->toBasetype()->ty == Tsarray) + { + se->e1 = se->e1->modifiableLvalue(sc, e1); + if (se->e1->op == TOKerror) + return se->e1; + } } else { + Expression *e1x = e1; + // Try to do a decent error message with the expression // before it got constant folded - if (e1->op != TOKvar) - e1 = e1->optimize(WANTvalue); + if (e1x->op != TOKvar) + e1x = e1x->optimize(WANTvalue); if (op == TOKassign) - e1 = e1->modifiableLvalue(sc, e1old); + e1x = e1x->modifiableLvalue(sc, e1old); + + if (e1x->op == TOKerror) + return e1x; + e1 = e1x; } - if (e1->op == TOKerror) - return e1; - Type *t2 = e2->type->toBasetype(); + /* Tweak e2 based on the type of e1. + */ + Expression *e2x = e2; + Type *t2 = e2x->type->toBasetype(); // If it is a array, get the element type. Note that it may be // multi-dimensional. @@ -11783,87 +11664,68 @@ Expression *AssignExp::semantic(Scope *sc) while (telem->ty == Tarray) telem = telem->nextOf(); - // Check for block assignment. If it is of type void[], void[][], etc, - // '= null' is the only allowable block assignment (Bug 7493) if (e1->op == TOKslice && - t1->nextOf() && (telem->ty != Tvoid || e2->op == TOKnull) && - e2->implicitConvTo(t1->nextOf()) + t1->nextOf() && (telem->ty != Tvoid || e2x->op == TOKnull) && + e2x->implicitConvTo(t1->nextOf()) ) { + // Check for block assignment. If it is of type void[], void[][], etc, + // '= null' is the only allowable block assignment (Bug 7493) // memset ismemset = 1; // make it easy for back end to tell what this is - e2 = e2->implicitCastTo(sc, t1->nextOf()); - if (op != TOKblit && e2->isLvalue()) - e2->checkPostblit(sc, t1->nextOf()); - } - else if (t1->ty == Tsarray) - { - /* Should have already converted e1 => e1[] - * unless it is an AA - */ - if (e1->op == TOKindex && t2->ty == Tsarray && - ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) - { - } - else - assert(op != TOKassign); - //error("cannot assign to static array %s", e1->toChars()); + e2x = e2x->implicitCastTo(sc, t1->nextOf()); + if (op != TOKblit && e2x->isLvalue()) + e1->checkPostblit(sc, t1->nextOf()); } - // Check element-wise assignment. else if (e1->op == TOKslice && (t2->ty == Tarray || t2->ty == Tsarray) && t2->nextOf()->implicitConvTo(t1->nextOf())) { + // Check element-wise assignment. + /* If assigned elements number is known at compile time, * check the mismatch. */ SliceExp *se1 = (SliceExp *)e1; - Type *tx1 = se1->e1->type->toBasetype(); - if (se1->lwr == NULL && tx1->ty == Tsarray) - { - Type *tx2 = t2; - if (e2->op == TOKslice && ((SliceExp *)e2)->lwr == NULL) - tx2 = ((SliceExp *)e2)->e1->type->toBasetype(); - uinteger_t dim1, dim2; - if (e2->op == TOKarrayliteral) - { - dim2 = ((ArrayLiteralExp *)e2)->elements->dim; - goto Lsa; - } - if (tx2->ty == Tsarray) - { - // sa1[] = sa2[]; - // sa1[] = sa2; - // sa1[] = [ ... ]; - dim2 = ((TypeSArray *)tx2)->dim->toInteger(); - Lsa: - dim1 = ((TypeSArray *)tx1)->dim->toInteger(); - if (dim1 != dim2) - { - error("mismatched array lengths, %d and %d", (int)dim1, (int)dim2); - return new ErrorExp(); - } + TypeSArray *tsa1 = (TypeSArray *)toStaticArrayType(se1); + TypeSArray *tsa2 = NULL; + if (e2x->op == TOKarrayliteral) + tsa2 = (TypeSArray *)t2->nextOf()->sarrayOf(((ArrayLiteralExp *)e2x)->elements->dim); + else if (e2x->op == TOKslice) + tsa2 = (TypeSArray *)toStaticArrayType((SliceExp *)e2x); + else if (t2->ty == Tsarray) + tsa2 = (TypeSArray *)t2; + if (tsa1 && tsa2) + { + uinteger_t dim1 = tsa1->dim->toInteger(); + uinteger_t dim2 = tsa2->dim->toInteger(); + if (dim1 != dim2) + { + error("mismatched array lengths, %d and %d", (int)dim1, (int)dim2); + return new ErrorExp(); } } + if (op != TOKblit && - (e2->op == TOKslice && ((UnaExp *)e2)->e1->isLvalue() || - e2->op == TOKcast && ((UnaExp *)e2)->e1->isLvalue() || - e2->op != TOKslice && e2->isLvalue())) + (e2x->op == TOKslice && ((UnaExp *)e2x)->e1->isLvalue() || + e2x->op == TOKcast && ((UnaExp *)e2x)->e1->isLvalue() || + e2x->op != TOKslice && e2x->isLvalue())) { - e2->checkPostblit(sc, t2->nextOf()); + e1->checkPostblit(sc, t1->nextOf()); } + if (0 && global.params.warnings && !global.gag && op == TOKassign && - e2->op != TOKslice && e2->op != TOKassign && - e2->op != TOKarrayliteral && e2->op != TOKstring && - !(e2->op == TOKadd || e2->op == TOKmin || - e2->op == TOKmul || e2->op == TOKdiv || - e2->op == TOKmod || e2->op == TOKxor || - e2->op == TOKand || e2->op == TOKor || - e2->op == TOKpow || - e2->op == TOKtilde || e2->op == TOKneg)) + e2x->op != TOKslice && e2x->op != TOKassign && + e2x->op != TOKarrayliteral && e2x->op != TOKstring && + !(e2x->op == TOKadd || e2x->op == TOKmin || + e2x->op == TOKmul || e2x->op == TOKdiv || + e2x->op == TOKmod || e2x->op == TOKxor || + e2x->op == TOKand || e2x->op == TOKor || + e2x->op == TOKpow || + e2x->op == TOKtilde || e2x->op == TOKneg)) { const char* e1str = e1->toChars(); - const char* e2str = e2->toChars(); + const char* e2str = e2x->toChars(); warning("explicit element-wise assignment %s = (%s)[] is better than %s = %s", e1str, e2str, e1str, e2str); } @@ -11883,29 +11745,40 @@ Expression *AssignExp::semantic(Scope *sc) * C[2] ca; D[] da; * ca[] = da; */ - e2 = e2->castTo(sc, e1->type->constOf()); + if (isArrayOpValid(e2x)) + { + // Don't add CastExp to keep AST for array operations + e2x = e2x->copy(); + e2x->type = e1->type->constOf(); + } + else + e2x = e2x->castTo(sc, e1->type->constOf()); } else - e2 = e2->implicitCastTo(sc, e1->type); + e2x = e2x->implicitCastTo(sc, e1->type); } else { if (0 && global.params.warnings && !global.gag && op == TOKassign && t1->ty == Tarray && t2->ty == Tsarray && - e2->op != TOKslice && //e2->op != TOKarrayliteral && + e2x->op != TOKslice && t2->implicitConvTo(t1)) { // Disallow ar[] = sa (Converted to ar[] = sa[]) // Disallow da = sa (Converted to da = sa[]) const char* e1str = e1->toChars(); - const char* e2str = e2->toChars(); + const char* e2str = e2x->toChars(); const char* atypestr = e1->op == TOKslice ? "element-wise" : "slice"; warning("explicit %s assignment %s = (%s)[] is better than %s = %s", atypestr, e1str, e2str, e1str, e2str); } - e2 = e2->implicitCastTo(sc, e1->type); + if (op == TOKblit) + e2x = e2x->castTo(sc, e1->type); + else + e2x = e2x->implicitCastTo(sc, e1->type); } - if (e2->op == TOKerror) - return new ErrorExp(); + if (e2x->op == TOKerror) + return e2x; + e2 = e2x; /* Look for array operations */ @@ -11919,7 +11792,7 @@ Expression *AssignExp::semantic(Scope *sc) e2->op == TOKtilde || e2->op == TOKneg)) { type = e1->type; - return arrayOp(sc); + return arrayOp(this, sc); } if (e1->op == TOKvar && @@ -11938,6 +11811,33 @@ Expression *AssignExp::semantic(Scope *sc) return op == TOKassign ? reorderSettingAAElem(sc) : this; } +int AssignExp::isLvalue() +{ + // Array-op 'x[] = y[]' should make an rvalue. + // Setting array length 'x.length = v' should make an rvalue. + if (e1->op == TOKslice || + e1->op == TOKarraylength) + { + return 0; + } + return 1; +} + +Expression *AssignExp::toLvalue(Scope *sc, Expression *ex) +{ + if (e1->op == TOKslice || + e1->op == TOKarraylength) + { + return Expression::toLvalue(sc, ex); + } + + /* In front-end level, AssignExp should make an lvalue of e1. + * Taking the address of e1 will be handled in low level layer, + * so this function does nothing. + */ + return this; +} + Expression *AssignExp::checkToBoolean(Scope *sc) { // Things like: @@ -11958,6 +11858,14 @@ ConstructExp::ConstructExp(Loc loc, Expression *e1, Expression *e2) /************************************************************/ +BlitExp::BlitExp(Loc loc, Expression *e1, Expression *e2) + : AssignExp(loc, e1, e2) +{ + op = TOKblit; +} + +/************************************************************/ + AddAssignExp::AddAssignExp(Loc loc, Expression *e1, Expression *e2) : BinAssignExp(loc, TOKaddass, sizeof(AddAssignExp), e1, e2) { @@ -11979,16 +11887,20 @@ CatAssignExp::CatAssignExp(Loc loc, Expression *e1, Expression *e2) Expression *CatAssignExp::semantic(Scope *sc) { + if (type) + return this; + //printf("CatAssignExp::semantic() %s\n", toChars()); Expression *e = op_overload(sc); if (e) return e; if (e1->op == TOKslice) - { SliceExp *se = (SliceExp *)e1; - + { + SliceExp *se = (SliceExp *)e1; if (se->e1->type->toBasetype()->ty == Tsarray) - { error("cannot append to static array %s", se->e1->type->toChars()); + { + error("cannot append to static array %s", se->e1->type->toChars()); return new ErrorExp(); } } @@ -11996,14 +11908,17 @@ Expression *CatAssignExp::semantic(Scope *sc) e1 = e1->modifiableLvalue(sc, e1); if (e1->op == TOKerror) return e1; + if (e2->op == TOKerror) + return e2; - Type *tb1 = e1->type->toBasetype(); - Type *tb1next = tb1->nextOf(); - - e2 = e2->inferType(tb1next); - if (!e2->rvalue()) + if (isNonAssignmentArrayOp(e2)) + { + error("array operation %s without assignment not implemented", e2->toChars()); return new ErrorExp(); + } + Type *tb1 = e1->type->toBasetype(); + Type *tb1next = tb1->nextOf(); Type *tb2 = e2->type->toBasetype(); if ((tb1->ty == Tarray) && @@ -12014,19 +11929,19 @@ Expression *CatAssignExp::semantic(Scope *sc) tb1next->ty == Tchar || tb1next->ty == Twchar || tb1next->ty == Tdchar)) ) ) - { // Append array + { + // Append array e1->checkPostblit(sc, tb1next); e2 = e2->castTo(sc, e1->type); - type = e1->type; } else if ((tb1->ty == Tarray) && e2->implicitConvTo(tb1next) ) - { // Append element + { + // Append element e2->checkPostblit(sc, tb2); e2 = e2->castTo(sc, tb1next); e2 = e2->isLvalue() ? callCpCtor(sc, e2) : valueNoDtor(e2); - type = e1->type; } else if (tb1->ty == Tarray && (tb1next->ty == Tchar || tb1next->ty == Twchar) && @@ -12035,7 +11950,6 @@ Expression *CatAssignExp::semantic(Scope *sc) ) { // Append dchar to char[] or wchar[] e2 = e2->castTo(sc, Type::tdchar); - type = e1->type; /* Do not allow appending wchar to char[] because if wchar happens * to be a surrogate pair, nothing good can result. @@ -12043,10 +11957,13 @@ Expression *CatAssignExp::semantic(Scope *sc) } else { - if (tb1 != Type::terror && tb2 != Type::terror) - error("cannot append type %s to type %s", tb2->toChars(), tb1->toChars()); + error("cannot append type %s to type %s", tb2->toChars(), tb1->toChars()); return new ErrorExp(); } + if (!e2->rvalue()) + return new ErrorExp(); + + type = e1->type; return reorderSettingAAElem(sc); } @@ -12122,21 +12039,23 @@ PowAssignExp::PowAssignExp(Loc loc, Expression *e1, Expression *e2) Expression *PowAssignExp::semantic(Scope *sc) { - Expression *e; - if (type) return this; - e = op_overload(sc); + Expression *e = op_overload(sc); + if (e) + return e; + + e = e1->checkReadModifyWrite(op, e2); if (e) return e; assert(e1->type && e2->type); if (e1->op == TOKslice || e1->type->ty == Tarray || e1->type->ty == Tsarray) - { // T[] ^^= ... - e = typeCombine(sc); - if (e->op == TOKerror) - return e; + { + // T[] ^^= ... + if (Expression *ex = typeCombine(this, sc)) + return ex; // Check element types are arithmetic Type *tb1 = e1->type->nextOf()->toBasetype(); @@ -12148,27 +12067,31 @@ Expression *PowAssignExp::semantic(Scope *sc) (tb2->isintegral() || tb2->isfloating())) { type = e1->type; - return arrayOp(sc); + return arrayOp(this, sc); } } else { e1 = e1->modifiableLvalue(sc, e1); - - e = reorderSettingAAElem(sc); - if (e != this) return e; } - if ( (e1->type->isintegral() || e1->type->isfloating()) && - (e2->type->isintegral() || e2->type->isfloating())) + if ((e1->type->isintegral() || e1->type->isfloating()) && + (e2->type->isintegral() || e2->type->isfloating())) { + Expression *e0 = NULL; + e = reorderSettingAAElem(sc); + e = extractLast(e, &e0); + assert(e == this); + if (e1->op == TOKvar) - { // Rewrite: e1 = e1 ^^ e2 + { + // Rewrite: e1 = e1 ^^ e2 e = new PowExp(loc, e1->syntaxCopy(), e2); e = new AssignExp(loc, e1, e); } else - { // Rewrite: ref tmp = e1; tmp = tmp ^^ e2 + { + // Rewrite: ref tmp = e1; tmp = tmp ^^ e2 Identifier *id = Lexer::uniqueId("__powtmp"); VarDeclaration *v = new VarDeclaration(e1->loc, e1->type, id, new ExpInitializer(loc, e1)); v->storage_class |= STCtemp | STCref | STCforeach; @@ -12178,6 +12101,7 @@ Expression *PowAssignExp::semantic(Scope *sc) e = new AssignExp(loc, new VarExp(e1->loc, v), e); e = new CommaExp(loc, de, e); } + e = Expression::combine(e0, e); e = e->semantic(sc); if (e->type->toBasetype()->ty == Tvector) return incompatibleTypes(); @@ -12186,7 +12110,6 @@ Expression *PowAssignExp::semantic(Scope *sc) return incompatibleTypes(); } - /************************* AddExp *****************************/ AddExp::AddExp(Loc loc, Expression *e1, Expression *e2) @@ -12199,84 +12122,84 @@ Expression *AddExp::semantic(Scope *sc) #if LOGSEMANTIC printf("AddExp::semantic('%s')\n", toChars()); #endif - if (!type) - { - BinExp::semanticp(sc); - Expression *e = op_overload(sc); - if (e) - return e; + if (type) + return this; - Type *tb1 = e1->type->toBasetype(); - Type *tb2 = e2->type->toBasetype(); + if (Expression *ex = binSemanticProp(sc)) + return ex; + Expression *e = op_overload(sc); + if (e) + return e; - if (tb1->ty == Tdelegate || - tb1->ty == Tpointer && tb1->nextOf()->ty == Tfunction) - { - e = e1->checkArithmetic(); - } - if (tb2->ty == Tdelegate || - tb2->ty == Tpointer && tb2->nextOf()->ty == Tfunction) - { - e = e2->checkArithmetic(); - } - if (e) - return e; + Type *tb1 = e1->type->toBasetype(); + Type *tb2 = e2->type->toBasetype(); - if (tb1->ty == Tpointer && e2->type->isintegral() || - tb2->ty == Tpointer && e1->type->isintegral()) - { - e = scaleFactor(sc); - } - else if (tb1->ty == Tpointer && tb2->ty == Tpointer) + if (tb1->ty == Tdelegate || + tb1->ty == Tpointer && tb1->nextOf()->ty == Tfunction) + { + e = e1->checkArithmetic(); + } + if (tb2->ty == Tdelegate || + tb2->ty == Tpointer && tb2->nextOf()->ty == Tfunction) + { + e = e2->checkArithmetic(); + } + if (e) + return e; + + if (tb1->ty == Tpointer && e2->type->isintegral() || + tb2->ty == Tpointer && e1->type->isintegral()) + { + return scaleFactor(this, sc); + } + + if (tb1->ty == Tpointer && tb2->ty == Tpointer) + { + return incompatibleTypes(); + } + + if (Expression *ex = typeCombine(this, sc)) + return ex; + + Type *tb = type->toBasetype(); + if (tb->ty == Tarray || tb->ty == Tsarray) + { + if (!isArrayOpValid(this)) { - return incompatibleTypes(); + error("invalid array operation %s (did you forget a [] ?)", toChars()); + return new ErrorExp(); } - else - { - typeCombine(sc); - Type *tb = type->toBasetype(); - if (tb->ty == Tarray || tb->ty == Tsarray) - { - if (!isArrayOpValid(this)) - { - error("invalid array operation %s (did you forget a [] ?)", toChars()); - return new ErrorExp(); - } - return this; - } + return this; + } - tb1 = e1->type->toBasetype(); - if (tb1->ty == Tvector && !tb1->isscalar()) - { - return incompatibleTypes(); - } - if ((tb1->isreal() && e2->type->isimaginary()) || - (tb1->isimaginary() && e2->type->isreal())) - { - switch (type->toBasetype()->ty) - { - case Tfloat32: - case Timaginary32: - type = Type::tcomplex32; - break; + tb1 = e1->type->toBasetype(); + if (tb1->ty == Tvector && !tb1->isscalar()) + { + return incompatibleTypes(); + } + if ((tb1->isreal() && e2->type->isimaginary()) || + (tb1->isimaginary() && e2->type->isreal())) + { + switch (type->toBasetype()->ty) + { + case Tfloat32: + case Timaginary32: + type = Type::tcomplex32; + break; - case Tfloat64: - case Timaginary64: - type = Type::tcomplex64; - break; + case Tfloat64: + case Timaginary64: + type = Type::tcomplex64; + break; - case Tfloat80: - case Timaginary80: - type = Type::tcomplex80; - break; + case Tfloat80: + case Timaginary80: + type = Type::tcomplex80; + break; - default: - assert(0); - } - } - e = this; + default: + assert(0); } - return e; } return this; } @@ -12296,7 +12219,8 @@ Expression *MinExp::semantic(Scope *sc) if (type) return this; - BinExp::semanticp(sc); + if (Expression *ex = binSemanticProp(sc)) + return ex; Expression *e = op_overload(sc); if (e) return e; @@ -12317,7 +12241,6 @@ Expression *MinExp::semantic(Scope *sc) if (e) return e; - e = this; if (t1->ty == Tpointer) { if (t2->ty == Tpointer) @@ -12326,7 +12249,10 @@ Expression *MinExp::semantic(Scope *sc) // Replace (ptr - ptr) with (ptr - ptr) / stride d_int64 stride; - typeCombine(sc); // make sure pointer types are compatible + // make sure pointer types are compatible + if (Expression *ex = typeCombine(this, sc)) + return ex; + type = Type::tptrdiff_t; stride = t2->nextOf()->size(); if (stride == 0) @@ -12338,67 +12264,68 @@ Expression *MinExp::semantic(Scope *sc) e = new DivExp(loc, this, new IntegerExp(Loc(), stride, Type::tptrdiff_t)); e->type = Type::tptrdiff_t; } - return e; } else if (t2->isintegral()) - e = scaleFactor(sc); + e = scaleFactor(this, sc); else - { error("can't subtract %s from pointer", t2->toChars()); - return new ErrorExp(); + { + error("can't subtract %s from pointer", t2->toChars()); + e = new ErrorExp(); } + return e; } - else if (t2->ty == Tpointer) + if (t2->ty == Tpointer) { type = e2->type; error("can't subtract pointer from %s", e1->type->toChars()); return new ErrorExp(); } - else + + if (Expression *ex = typeCombine(this, sc)) + return ex; + + Type *tb = type->toBasetype(); + if (tb->ty == Tarray || tb->ty == Tsarray) { - typeCombine(sc); - Type *tb = type->toBasetype(); - if (tb->ty == Tarray || tb->ty == Tsarray) + if (!isArrayOpValid(this)) { - if (!isArrayOpValid(this)) - { - error("invalid array operation %s (did you forget a [] ?)", toChars()); - return new ErrorExp(); - } - return this; + error("invalid array operation %s (did you forget a [] ?)", toChars()); + return new ErrorExp(); } + return this; + } - t1 = e1->type->toBasetype(); - t2 = e2->type->toBasetype(); - if (t1->ty == Tvector && !t1->isscalar()) - { - return incompatibleTypes(); - } - if ((t1->isreal() && t2->isimaginary()) || - (t1->isimaginary() && t2->isreal())) + t1 = e1->type->toBasetype(); + t2 = e2->type->toBasetype(); + if (t1->ty == Tvector && !t1->isscalar()) + { + return incompatibleTypes(); + } + if ((t1->isreal() && t2->isimaginary()) || + (t1->isimaginary() && t2->isreal())) + { + switch (type->ty) { - switch (type->ty) - { - case Tfloat32: - case Timaginary32: - type = Type::tcomplex32; - break; + case Tfloat32: + case Timaginary32: + type = Type::tcomplex32; + break; - case Tfloat64: - case Timaginary64: - type = Type::tcomplex64; - break; + case Tfloat64: + case Timaginary64: + type = Type::tcomplex64; + break; - case Tfloat80: - case Timaginary80: - type = Type::tcomplex80; - break; + case Tfloat80: + case Timaginary80: + type = Type::tcomplex80; + break; - default: - assert(0); - } + default: + assert(0); } } - return e; + return this; } /************************* CatExp *****************************/ @@ -12409,133 +12336,136 @@ CatExp::CatExp(Loc loc, Expression *e1, Expression *e2) } Expression *CatExp::semantic(Scope *sc) -{ Expression *e; - +{ //printf("CatExp::semantic() %s\n", toChars()); - if (!type) - { - BinExp::semanticp(sc); - e = op_overload(sc); - if (e) - return e; + if (type) + return this; - Type *tb1 = e1->type->toBasetype(); - Type *tb2 = e2->type->toBasetype(); + if (Expression *ex = binSemanticProp(sc)) + return ex; + Expression *e = op_overload(sc); + if (e) + return e; + Type *tb1 = e1->type->toBasetype(); + Type *tb2 = e2->type->toBasetype(); - /* BUG: Should handle things like: - * char c; - * c ~ ' ' - * ' ' ~ c; - */ + /* BUG: Should handle things like: + * char c; + * c ~ ' ' + * ' ' ~ c; + */ #if 0 - e1->type->print(); - e2->type->print(); + e1->type->print(); + e2->type->print(); #endif - Type *tb1next = tb1->nextOf(); - Type *tb2next = tb2->nextOf(); + Type *tb1next = tb1->nextOf(); + Type *tb2next = tb2->nextOf(); - if (tb1next && tb2next && - (tb1next->implicitConvTo(tb2next) >= MATCHconst || - tb2next->implicitConvTo(tb1next) >= MATCHconst) - ) - { - /* Here to avoid the case of: - * void*[] a = [cast(void*)1]; - * void*[] b = [cast(void*)2]; - * a ~ b; - * becoming: - * a ~ [cast(void*)b]; - */ - } - else if ((tb1->ty == Tsarray || tb1->ty == Tarray) && - e2->implicitConvTo(tb1next) >= MATCHconvert && - tb2->ty != Tvoid) + if (tb1next && tb2next && + (tb1next->implicitConvTo(tb2next) >= MATCHconst || + tb2next->implicitConvTo(tb1next) >= MATCHconst) + ) + { + /* Here to avoid the case of: + * void*[] a = [cast(void*)1]; + * void*[] b = [cast(void*)2]; + * a ~ b; + * becoming: + * a ~ [cast(void*)b]; + */ + } + else if ((tb1->ty == Tsarray || tb1->ty == Tarray) && + e2->implicitConvTo(tb1next) >= MATCHconvert && + tb2->ty != Tvoid) + { + e2->checkPostblit(sc, tb2); + e2 = e2->implicitCastTo(sc, tb1next); + type = tb1next->arrayOf(); + if (tb2->ty == Tarray || tb2->ty == Tsarray) { - e2->checkPostblit(sc, tb2); - e2 = e2->implicitCastTo(sc, tb1next); - type = tb1next->arrayOf(); - if (tb2->ty == Tarray || tb2->ty == Tsarray) - { // Make e2 into [e2] - e2 = new ArrayLiteralExp(e2->loc, e2); - e2->type = type; - } - return this; + // Make e2 into [e2] + e2 = new ArrayLiteralExp(e2->loc, e2); + e2->type = type; } - else if ((tb2->ty == Tsarray || tb2->ty == Tarray) && - e1->implicitConvTo(tb2next) >= MATCHconvert && - tb1->ty != Tvoid) + return this; + } + else if ((tb2->ty == Tsarray || tb2->ty == Tarray) && + e1->implicitConvTo(tb2next) >= MATCHconvert && + tb1->ty != Tvoid) + { + e1->checkPostblit(sc, tb1); + e1 = e1->implicitCastTo(sc, tb2next); + type = tb2next->arrayOf(); + if (tb1->ty == Tarray || tb1->ty == Tsarray) { - e1->checkPostblit(sc, tb1); - e1 = e1->implicitCastTo(sc, tb2next); - type = tb2next->arrayOf(); - if (tb1->ty == Tarray || tb1->ty == Tsarray) - { // Make e1 into [e1] - e1 = new ArrayLiteralExp(e1->loc, e1); - e1->type = type; - } - return this; + // Make e1 into [e1] + e1 = new ArrayLiteralExp(e1->loc, e1); + e1->type = type; } + return this; + } - if ((tb1->ty == Tsarray || tb1->ty == Tarray) && - (tb2->ty == Tsarray || tb2->ty == Tarray) && - (tb1next->mod || tb2next->mod) && - (tb1next->mod != tb2next->mod) - ) - { - Type *t1 = tb1next->mutableOf()->constOf()->arrayOf(); - Type *t2 = tb2next->mutableOf()->constOf()->arrayOf(); - if (e1->op == TOKstring && !((StringExp *)e1)->committed) - e1->type = t1; - else - e1 = e1->castTo(sc, t1); - if (e2->op == TOKstring && !((StringExp *)e2)->committed) - e2->type = t2; - else - e2 = e2->castTo(sc, t2); - } + if ((tb1->ty == Tsarray || tb1->ty == Tarray) && + (tb2->ty == Tsarray || tb2->ty == Tarray) && + (tb1next->mod || tb2next->mod) && + (tb1next->mod != tb2next->mod) + ) + { + Type *t1 = tb1next->mutableOf()->constOf()->arrayOf(); + Type *t2 = tb2next->mutableOf()->constOf()->arrayOf(); + if (e1->op == TOKstring && !((StringExp *)e1)->committed) + e1->type = t1; + else + e1 = e1->castTo(sc, t1); + if (e2->op == TOKstring && !((StringExp *)e2)->committed) + e2->type = t2; + else + e2 = e2->castTo(sc, t2); + } - typeCombine(sc); - type = type->toHeadMutable(); + if (Expression *ex = typeCombine(this, sc)) + return ex; + type = type->toHeadMutable(); - Type *tb = type->toBasetype(); - if (tb->ty == Tsarray) - type = tb->nextOf()->arrayOf(); - if (type->ty == Tarray && tb1next && tb2next && - tb1next->mod != tb2next->mod) - { - type = type->nextOf()->toHeadMutable()->arrayOf(); - } - if (Type *tbn = tb->nextOf()) - { - checkPostblit(sc, tbn); - } + Type *tb = type->toBasetype(); + if (tb->ty == Tsarray) + type = tb->nextOf()->arrayOf(); + if (type->ty == Tarray && tb1next && tb2next && + tb1next->mod != tb2next->mod) + { + type = type->nextOf()->toHeadMutable()->arrayOf(); + } + if (Type *tbn = tb->nextOf()) + { + checkPostblit(sc, tbn); + } #if 0 - e1->type->print(); - e2->type->print(); - type->print(); - print(); + e1->type->print(); + e2->type->print(); + type->print(); + print(); #endif - Type *t1 = e1->type->toBasetype(); - Type *t2 = e2->type->toBasetype(); - if (e1->op == TOKstring && e2->op == TOKstring) - e = optimize(WANTvalue); - else if ((t1->ty == Tarray || t1->ty == Tsarray) && - (t2->ty == Tarray || t2->ty == Tsarray)) - { - e = this; - } - else - { - //printf("(%s) ~ (%s)\n", e1->toChars(), e2->toChars()); - incompatibleTypes(); - return new ErrorExp(); - } - e->type = e->type->semantic(loc, sc); - return e; + Type *t1 = e1->type->toBasetype(); + Type *t2 = e2->type->toBasetype(); + if (e1->op == TOKstring && e2->op == TOKstring) + { + e = optimize(WANTvalue); } - return this; + else if ((t1->ty == Tarray || t1->ty == Tsarray) && + (t2->ty == Tarray || t2->ty == Tsarray)) + { + e = this; + } + else + { + //printf("(%s) ~ (%s)\n", e1->toChars(), e2->toChars()); + return incompatibleTypes(); + } + + e->type = e->type->semantic(loc, sc); + return e; } /************************************************************/ @@ -12553,12 +12483,15 @@ Expression *MulExp::semantic(Scope *sc) if (type) return this; - BinExp::semanticp(sc); + if (Expression *ex = binSemanticProp(sc)) + return ex; Expression *e = op_overload(sc); if (e) return e; - typeCombine(sc); + if (Expression *ex = typeCombine(this, sc)) + return ex; + Type *tb = type->toBasetype(); if (tb->ty == Tarray || tb->ty == Tsarray) { @@ -12638,12 +12571,15 @@ Expression *DivExp::semantic(Scope *sc) if (type) return this; - BinExp::semanticp(sc); + if (Expression *ex = binSemanticProp(sc)) + return ex; Expression *e = op_overload(sc); if (e) return e; - typeCombine(sc); + if (Expression *ex = typeCombine(this, sc)) + return ex; + Type *tb = type->toBasetype(); if (tb->ty == Tarray || tb->ty == Tsarray) { @@ -12722,12 +12658,15 @@ Expression *ModExp::semantic(Scope *sc) if (type) return this; - BinExp::semanticp(sc); + if (Expression *ex = binSemanticProp(sc)) + return ex; Expression *e = op_overload(sc); if (e) return e; - typeCombine(sc); + if (Expression *ex = typeCombine(this, sc)) + return ex; + Type *tb = type->toBasetype(); if (tb->ty == Tarray || tb->ty == Tsarray) { @@ -12794,12 +12733,15 @@ Expression *PowExp::semantic(Scope *sc) return this; //printf("PowExp::semantic() %s\n", toChars()); - BinExp::semanticp(sc); + if (Expression *ex = binSemanticProp(sc)) + return ex; Expression *e = op_overload(sc); if (e) return e; - typeCombine(sc); + if (Expression *ex = typeCombine(this, sc)) + return ex; + Type *tb = type->toBasetype(); if (tb->ty == Tarray || tb->ty == Tsarray) { @@ -12821,8 +12763,6 @@ Expression *PowExp::semantic(Scope *sc) // For built-in numeric types, there are several cases. // TODO: backend support, especially for e1 ^^ 2. - bool wantSqrt = false; - // First, attempt to fold the expression. e = optimize(WANTvalue); if (e->op != TOKpow) @@ -12866,9 +12806,9 @@ Expression *PowExp::semantic(Scope *sc) // Leave handling of PowExp to the backend, or throw // an error gracefully if no backend support exists. - typeCombine(sc); - e = this; - return e; + if (Expression *ex = typeCombine(this, sc)) + return ex; + return this; } e = new ScopeExp(loc, mmath); @@ -12894,23 +12834,28 @@ ShlExp::ShlExp(Loc loc, Expression *e1, Expression *e2) } Expression *ShlExp::semantic(Scope *sc) -{ Expression *e; - +{ //printf("ShlExp::semantic(), type = %p\n", type); - if (!type) - { BinExp::semanticp(sc); - e = op_overload(sc); - if (e) - return e; - e1 = e1->checkIntegral(); - e2 = e2->checkIntegral(); - if (e1->type->toBasetype()->ty == Tvector || - e2->type->toBasetype()->ty == Tvector) - return incompatibleTypes(); - e1 = e1->integralPromotions(sc); - e2 = e2->castTo(sc, Type::tshiftcnt); - type = e1->type; + if (type) + return this; + + if (Expression *ex = binSemanticProp(sc)) + return ex; + Expression *e = op_overload(sc); + if (e) + return e; + + e1 = e1->checkIntegral(); + e2 = e2->checkIntegral(); + if (e1->type->toBasetype()->ty == Tvector || + e2->type->toBasetype()->ty == Tvector) + { + return incompatibleTypes(); } + e1 = integralPromotions(e1, sc); + e2 = e2->castTo(sc, Type::tshiftcnt); + + type = e1->type; return this; } @@ -12922,22 +12867,27 @@ ShrExp::ShrExp(Loc loc, Expression *e1, Expression *e2) } Expression *ShrExp::semantic(Scope *sc) -{ Expression *e; +{ + if (type) + return this; - if (!type) - { BinExp::semanticp(sc); - e = op_overload(sc); - if (e) - return e; - e1 = e1->checkIntegral(); - e2 = e2->checkIntegral(); - if (e1->type->toBasetype()->ty == Tvector || - e2->type->toBasetype()->ty == Tvector) - return incompatibleTypes(); - e1 = e1->integralPromotions(sc); - e2 = e2->castTo(sc, Type::tshiftcnt); - type = e1->type; + if (Expression *ex = binSemanticProp(sc)) + return ex; + Expression *e = op_overload(sc); + if (e) + return e; + + e1 = e1->checkIntegral(); + e2 = e2->checkIntegral(); + if (e1->type->toBasetype()->ty == Tvector || + e2->type->toBasetype()->ty == Tvector) + { + return incompatibleTypes(); } + e1 = integralPromotions(e1, sc); + e2 = e2->castTo(sc, Type::tshiftcnt); + + type = e1->type; return this; } @@ -12949,22 +12899,28 @@ UshrExp::UshrExp(Loc loc, Expression *e1, Expression *e2) } Expression *UshrExp::semantic(Scope *sc) -{ Expression *e; +{ + if (type) + return this; - if (!type) - { BinExp::semanticp(sc); - e = op_overload(sc); - if (e) - return e; - e1 = e1->checkIntegral(); - e2 = e2->checkIntegral(); - if (e1->type->toBasetype()->ty == Tvector || - e2->type->toBasetype()->ty == Tvector) - return incompatibleTypes(); - e1 = e1->integralPromotions(sc); - e2 = e2->castTo(sc, Type::tshiftcnt); - type = e1->type; + if (Expression *ex = binSemanticProp(sc)) + return ex; + Expression *e = op_overload(sc); + if (e) + return e; + + e1 = e1->checkIntegral(); + e2 = e2->checkIntegral(); + if (e1->type->toBasetype()->ty == Tvector || + e2->type->toBasetype()->ty == Tvector) + { + return incompatibleTypes(); } + + e1 = integralPromotions(e1, sc); + e2 = e2->castTo(sc, Type::tshiftcnt); + + type = e1->type; return this; } @@ -12977,39 +12933,43 @@ AndExp::AndExp(Loc loc, Expression *e1, Expression *e2) Expression *AndExp::semantic(Scope *sc) { - if (!type) + if (type) + return this; + + if (Expression *ex = binSemanticProp(sc)) + return ex; + Expression *e = op_overload(sc); + if (e) + return e; + + if (e1->type->toBasetype()->ty == Tbool && + e2->type->toBasetype()->ty == Tbool) { - BinExp::semanticp(sc); - Expression *e = op_overload(sc); - if (e) - return e; + type = e1->type; + return this; + } - if (e1->type->toBasetype()->ty == Tbool && - e2->type->toBasetype()->ty == Tbool) - { - type = e1->type; - return this; - } + if (Expression *ex = typeCombine(this, sc)) + return ex; - typeCombine(sc); - Type *tb = type->toBasetype(); - if (tb->ty == Tarray || tb->ty == Tsarray) + Type *tb = type->toBasetype(); + if (tb->ty == Tarray || tb->ty == Tsarray) + { + if (!isArrayOpValid(this)) { - if (!isArrayOpValid(this)) - { - error("invalid array operation %s (did you forget a [] ?)", toChars()); - return new ErrorExp(); - } - return this; + error("invalid array operation %s (did you forget a [] ?)", toChars()); + return new ErrorExp(); } - - e1 = e1->checkIntegral(); - e2 = e2->checkIntegral(); - if (e1->op == TOKerror) - return e1; - if (e2->op == TOKerror) - return e2; + return this; } + + e1 = e1->checkIntegral(); + e2 = e2->checkIntegral(); + if (e1->op == TOKerror) + return e1; + if (e2->op == TOKerror) + return e2; + return this; } @@ -13022,39 +12982,43 @@ OrExp::OrExp(Loc loc, Expression *e1, Expression *e2) Expression *OrExp::semantic(Scope *sc) { - if (!type) + if (type) + return this; + + if (Expression *ex = binSemanticProp(sc)) + return ex; + Expression *e = op_overload(sc); + if (e) + return e; + + if (e1->type->toBasetype()->ty == Tbool && + e2->type->toBasetype()->ty == Tbool) { - BinExp::semanticp(sc); - Expression *e = op_overload(sc); - if (e) - return e; + type = e1->type; + return this; + } - if (e1->type->toBasetype()->ty == Tbool && - e2->type->toBasetype()->ty == Tbool) - { - type = e1->type; - return this; - } + if (Expression *ex = typeCombine(this, sc)) + return ex; - typeCombine(sc); - Type *tb = type->toBasetype(); - if (tb->ty == Tarray || tb->ty == Tsarray) + Type *tb = type->toBasetype(); + if (tb->ty == Tarray || tb->ty == Tsarray) + { + if (!isArrayOpValid(this)) { - if (!isArrayOpValid(this)) - { - error("invalid array operation %s (did you forget a [] ?)", toChars()); - return new ErrorExp(); - } - return this; + error("invalid array operation %s (did you forget a [] ?)", toChars()); + return new ErrorExp(); } - - e1 = e1->checkIntegral(); - e2 = e2->checkIntegral(); - if (e1->op == TOKerror) - return e1; - if (e2->op == TOKerror) - return e2; + return this; } + + e1 = e1->checkIntegral(); + e2 = e2->checkIntegral(); + if (e1->op == TOKerror) + return e1; + if (e2->op == TOKerror) + return e2; + return this; } @@ -13067,39 +13031,43 @@ XorExp::XorExp(Loc loc, Expression *e1, Expression *e2) Expression *XorExp::semantic(Scope *sc) { - if (!type) + if (type) + return this; + + if (Expression *ex = binSemanticProp(sc)) + return ex; + Expression *e = op_overload(sc); + if (e) + return e; + + if (e1->type->toBasetype()->ty == Tbool && + e2->type->toBasetype()->ty == Tbool) { - BinExp::semanticp(sc); - Expression *e = op_overload(sc); - if (e) - return e; + type = e1->type; + return this; + } - if (e1->type->toBasetype()->ty == Tbool && - e2->type->toBasetype()->ty == Tbool) - { - type = e1->type; - return this; - } + if (Expression *ex = typeCombine(this, sc)) + return ex; - typeCombine(sc); - Type *tb = type->toBasetype(); - if (tb->ty == Tarray || tb->ty == Tsarray) + Type *tb = type->toBasetype(); + if (tb->ty == Tarray || tb->ty == Tsarray) + { + if (!isArrayOpValid(this)) { - if (!isArrayOpValid(this)) - { - error("invalid array operation %s (did you forget a [] ?)", toChars()); - return new ErrorExp(); - } - return this; + error("invalid array operation %s (did you forget a [] ?)", toChars()); + return new ErrorExp(); } - - e1 = e1->checkIntegral(); - e2 = e2->checkIntegral(); - if (e1->op == TOKerror) - return e1; - if (e2->op == TOKerror) - return e2; + return this; } + + e1 = e1->checkIntegral(); + e2 = e2->checkIntegral(); + if (e1->op == TOKerror) + return e1; + if (e2->op == TOKerror) + return e2; + return this; } @@ -13113,14 +13081,12 @@ OrOrExp::OrOrExp(Loc loc, Expression *e1, Expression *e2) Expression *OrOrExp::semantic(Scope *sc) { - unsigned cs1; - // same as for AndAnd e1 = e1->semantic(sc); e1 = resolveProperties(sc, e1); e1 = e1->checkToPointer(); e1 = e1->checkToBoolean(sc); - cs1 = sc->callSuper; + unsigned cs1 = sc->callSuper; if (sc->flags & SCOPEstaticif) { @@ -13146,13 +13112,15 @@ Expression *OrOrExp::semantic(Scope *sc) type = Type::tboolean; } if (e2->op == TOKtype || e2->op == TOKimport) - { error("%s is not an expression", e2->toChars()); + { + error("%s is not an expression", e2->toChars()); return new ErrorExp(); } if (e1->op == TOKerror) return e1; if (e2->op == TOKerror) return e2; + return this; } @@ -13171,14 +13139,12 @@ AndAndExp::AndAndExp(Loc loc, Expression *e1, Expression *e2) Expression *AndAndExp::semantic(Scope *sc) { - unsigned cs1; - // same as for OrOr e1 = e1->semantic(sc); e1 = resolveProperties(sc, e1); e1 = e1->checkToPointer(); e1 = e1->checkToBoolean(sc); - cs1 = sc->callSuper; + unsigned cs1 = sc->callSuper; if (sc->flags & SCOPEstaticif) { @@ -13204,13 +13170,15 @@ Expression *AndAndExp::semantic(Scope *sc) type = Type::tboolean; } if (e2->op == TOKtype || e2->op == TOKimport) - { error("%s is not an expression", e2->toChars()); + { + error("%s is not an expression", e2->toChars()); return new ErrorExp(); } if (e1->op == TOKerror) return e1; if (e2->op == TOKerror) return e2; + return this; } @@ -13228,13 +13196,13 @@ InExp::InExp(Loc loc, Expression *e1, Expression *e2) } Expression *InExp::semantic(Scope *sc) -{ Expression *e; - +{ if (type) return this; - BinExp::semanticp(sc); - e = op_overload(sc); + if (Expression *ex = binSemanticProp(sc)) + return ex; + Expression *e = op_overload(sc); if (e) return e; @@ -13277,12 +13245,11 @@ RemoveExp::RemoveExp(Loc loc, Expression *e1, Expression *e2) type = Type::tboolean; } -void RemoveExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +Expression *RemoveExp::semantic(Scope *sc) { - expToCBuffer(buf, hgs, e1, PREC_primary); - buf->writestring(".remove("); - expToCBuffer(buf, hgs, e2, PREC_assign); - buf->writestring(")"); + if (Expression *ex = binSemantic(sc)) + return ex; + return this; } /************************************************************/ @@ -13293,16 +13260,15 @@ CmpExp::CmpExp(TOK op, Loc loc, Expression *e1, Expression *e2) } Expression *CmpExp::semantic(Scope *sc) -{ Expression *e; - +{ #if LOGSEMANTIC printf("CmpExp::semantic('%s')\n", toChars()); #endif if (type) return this; - BinExp::semanticp(sc); - + if (Expression *ex = binSemanticProp(sc)) + return ex; Type *t1 = e1->type->toBasetype(); Type *t2 = e2->type->toBasetype(); if (t1->ty == Tclass && e2->op == TOKnull || @@ -13312,7 +13278,7 @@ Expression *CmpExp::semantic(Scope *sc) return new ErrorExp(); } - e = op_overload(sc); + Expression *e = op_overload(sc); if (e) { if (!e->type->isscalar() && e->type->equals(e1->type)) @@ -13333,13 +13299,11 @@ Expression *CmpExp::semantic(Scope *sc) if (e1->op == TOKslice && t1->ty == Tarray && e2->implicitConvTo(t1->nextOf()) || e2->op == TOKslice && t2->ty == Tarray && e1->implicitConvTo(t2->nextOf())) { - incompatibleTypes(); - return new ErrorExp(); + return incompatibleTypes(); } - e = typeCombine(sc); - if (e->op == TOKerror) - return e; + if (Expression *ex = typeCombine(this, sc)) + return ex; type = Type::tboolean; @@ -13358,7 +13322,6 @@ Expression *CmpExp::semantic(Scope *sc) error("array comparison type mismatch, %s vs %s", t1next->toChars(), t2next->toChars()); return new ErrorExp(); } - e = this; } else if (t1->ty == Tstruct || t2->ty == Tstruct || (t1->ty == Tclass && t2->ty == Tclass)) @@ -13387,7 +13350,6 @@ Expression *CmpExp::semantic(Scope *sc) { if (!e1->rvalue() || !e2->rvalue()) return new ErrorExp(); - e = this; } TOK altop; @@ -13435,7 +13397,7 @@ Expression *CmpExp::semantic(Scope *sc) } //printf("CmpExp: %s, type = %s\n", e->toChars(), e->type->toChars()); - return e; + return this; } /************************************************************/ @@ -13446,7 +13408,7 @@ EqualExp::EqualExp(TOK op, Loc loc, Expression *e1, Expression *e2) assert(op == TOKequal || op == TOKnotequal); } -int needDirectEq(Scope *sc, Type *t1, Type *t2) +bool needDirectEq(Scope *sc, Type *t1, Type *t2) { assert(t1->ty == Tarray || t1->ty == Tsarray); assert(t2->ty == Tarray || t2->ty == Tsarray); @@ -13475,14 +13437,13 @@ int needDirectEq(Scope *sc, Type *t1, Type *t2) } Expression *EqualExp::semantic(Scope *sc) -{ Expression *e; - +{ //printf("EqualExp::semantic('%s')\n", toChars()); if (type) return this; - BinExp::semanticp(sc); - + if (Expression *ex = binSemanticProp(sc)) + return ex; if (e1->op == TOKtype || e2->op == TOKtype) return incompatibleTypes(); @@ -13499,11 +13460,10 @@ Expression *EqualExp::semantic(Scope *sc) VarExp *ve1 = (VarExp *)ae1->e1; VarExp *ve2 = (VarExp *)ae2->e1; - if (ve1->var == ve2->var /*|| ve1->var->toSymbol() == ve2->var->toSymbol()*/) + if (ve1->var == ve2->var) { // They are the same, result is 'true' for ==, 'false' for != - e = new IntegerExp(loc, (op == TOKequal), Type::tboolean); - return e; + return new IntegerExp(loc, (op == TOKequal), Type::tboolean); } } } @@ -13528,33 +13488,28 @@ Expression *EqualExp::semantic(Scope *sc) * _ArrayEq(e1, e2) */ Expression *eq = new IdentifierExp(loc, Id::_ArrayEq); - Expressions *args = new Expressions(); - args->push(e1); - args->push(e2); - e = new CallExp(loc, eq, args); + Expression *e = new CallExp(loc, eq, e1, e2); if (op == TOKnotequal) e = new NotExp(loc, e); e = e->trySemantic(sc); // for better error message if (!e) - { error("cannot compare %s and %s", t1->toChars(), t2->toChars()); + { + error("cannot compare %s and %s", t1->toChars(), t2->toChars()); return new ErrorExp(); } return e; } } - //if (e2->op != TOKnull) + Expression *e = op_overload(sc); + if (e) { - e = op_overload(sc); - if (e) + if (e->op == TOKcall && op == TOKnotequal) { - if (e->op == TOKcall && op == TOKnotequal) - { - e = new NotExp(e->loc, e); - e = e->semantic(sc); - } - return e; + e = new NotExp(e->loc, e); + e = e->semantic(sc); } + return e; } /* Disallow comparing T[]==T and T==T[] @@ -13562,8 +13517,7 @@ Expression *EqualExp::semantic(Scope *sc) if (e1->op == TOKslice && t1->ty == Tarray && e2->implicitConvTo(t1->nextOf()) || e2->op == TOKslice && t2->ty == Tarray && e1->implicitConvTo(t2->nextOf())) { - incompatibleTypes(); - return new ErrorExp(); + return incompatibleTypes(); } if (t1->ty == Tstruct && t2->ty == Tstruct) @@ -13571,7 +13525,7 @@ Expression *EqualExp::semantic(Scope *sc) StructDeclaration *sd = ((TypeStruct *)t1)->sym; if (sd == ((TypeStruct *)t2)->sym) { - if (sd->needOpEquals()) + if (needOpEquals(sd)) { this->e1 = new DotIdExp(loc, e1, Id::tupleof); this->e2 = new DotIdExp(loc, e2, Id::tupleof); @@ -13623,9 +13577,8 @@ Expression *EqualExp::semantic(Scope *sc) return e->semantic(sc); } - e = typeCombine(sc); - if (e->op == TOKerror) - return e; + if (Expression *ex = typeCombine(this, sc)) + return ex; type = Type::tboolean; @@ -13645,7 +13598,7 @@ Expression *EqualExp::semantic(Scope *sc) if (e1->type->toBasetype()->ty == Tvector) return incompatibleTypes(); - return e; + return this; } /************************************************************/ @@ -13660,12 +13613,12 @@ Expression *IdentityExp::semantic(Scope *sc) if (type) return this; - BinExp::semanticp(sc); + if (Expression *ex = binSemanticProp(sc)) + return ex; type = Type::tboolean; - Expression *e = typeCombine(sc); - if (e->op == TOKerror) - return e; + if (Expression *ex = typeCombine(this, sc)) + return ex; if (e1->type != e2->type && e1->type->isfloating() && e2->type->isfloating()) { @@ -13702,33 +13655,37 @@ Expression *CondExp::semantic(Scope *sc) if (type) return this; - econd = econd->semantic(sc); - econd = resolveProperties(sc, econd); - econd = econd->checkToPointer(); - econd = econd->checkToBoolean(sc); + Expression *ec = econd->semantic(sc); + ec = resolveProperties(sc, ec); + ec = ec->checkToPointer(); + ec = ec->checkToBoolean(sc); unsigned cs0 = sc->callSuper; unsigned *fi0 = sc->saveFieldInit(); - e1 = e1->semantic(sc); - e1 = resolveProperties(sc, e1); + Expression *e1x = e1->semantic(sc); + e1x = resolveProperties(sc, e1x); unsigned cs1 = sc->callSuper; unsigned *fi1 = sc->fieldinit; sc->callSuper = cs0; sc->fieldinit = fi0; - e2 = e2->semantic(sc); - e2 = resolveProperties(sc, e2); + Expression *e2x = e2->semantic(sc); + e2x = resolveProperties(sc, e2x); sc->mergeCallSuper(loc, cs1); sc->mergeFieldInit(loc, fi1); - if (econd->type == Type::terror) - return econd; - if (e1->type == Type::terror) - return e1; - if (e2->type == Type::terror) - return e2; + if (ec->op == TOKerror) return ec; + if (ec->type == Type::terror) return new ErrorExp(); + econd = ec; + if (e1x->op == TOKerror) return e1x; + if (e1x->type == Type::terror) return new ErrorExp(); + e1 = e1x; + + if (e2x->op == TOKerror) return e2x; + if (e2x->type == Type::terror) return new ErrorExp(); + e2 = e2x; // If either operand is void, the result is void Type *t1 = e1->type; @@ -13739,7 +13696,8 @@ Expression *CondExp::semantic(Scope *sc) type = t1; else { - typeCombine(sc); + if (Expression *ex = typeCombine(this, sc)) + return ex; switch (e1->type->toBasetype()->ty) { case Tcomplex32: @@ -13781,9 +13739,8 @@ Expression *CondExp::toLvalue(Scope *sc, Expression *ex) { // convert (econd ? e1 : e2) to *(econd ? &e1 : &e2) PtrExp *e = new PtrExp(loc, this, type); - e1 = e1->addressOf(sc); - e2 = e2->addressOf(sc); - //typeCombine(sc); + e1 = e1->toLvalue(sc, NULL)->addressOf(); + e2 = e2->toLvalue(sc, NULL)->addressOf(); type = e2->type; return e; } @@ -13821,17 +13778,6 @@ Expression *CondExp::checkToBoolean(Scope *sc) return this; } - -void CondExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - expToCBuffer(buf, hgs, econd, PREC_oror); - buf->writestring(" ? "); - expToCBuffer(buf, hgs, e1, PREC_expr); - buf->writestring(" : "); - expToCBuffer(buf, hgs, e2, PREC_cond); -} - - /****************************************************************/ DefaultInitExp::DefaultInitExp(Loc loc, TOK subop, int size) @@ -13840,11 +13786,6 @@ DefaultInitExp::DefaultInitExp(Loc loc, TOK subop, int size) this->subop = subop; } -void DefaultInitExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring(Token::toChars(subop)); -} - /****************************************************************/ FileInitExp::FileInitExp(Loc loc) @@ -13927,7 +13868,8 @@ Expression *FuncInitExp::semantic(Scope *sc) { //printf("FuncInitExp::semantic()\n"); type = Type::tstring; - if (sc->func) return this->resolveLoc(Loc(), sc); + if (sc->func) + return this->resolveLoc(Loc(), sc); return this; } @@ -13957,7 +13899,8 @@ Expression *PrettyFuncInitExp::semantic(Scope *sc) { //printf("PrettyFuncInitExp::semantic()\n"); type = Type::tstring; - if (sc->func) return this->resolveLoc(Loc(), sc); + if (sc->func) + return this->resolveLoc(Loc(), sc); return this; } @@ -13973,11 +13916,9 @@ Expression *PrettyFuncInitExp::resolveLoc(Loc loc, Scope *sc) if (fd) { const char *funcStr = fd->Dsymbol::toPrettyChars(); - HdrGenState hgs; OutBuffer buf; - functionToCBuffer2((TypeFunction *)fd->type, &buf, &hgs, 0, funcStr); - buf.writebyte(0); - s = (const char *)buf.extractData(); + functionToBufferWithIdent((TypeFunction *)fd->type, &buf, funcStr); + s = buf.extractString(); } else { @@ -13990,29 +13931,46 @@ Expression *PrettyFuncInitExp::resolveLoc(Loc loc, Scope *sc) return e; } +/****************************************************************/ +#ifdef IN_GCC + +WrappedExp::WrappedExp(Loc loc, elem *e1, Type *type) + : Expression(loc, TOKcomma, sizeof(WrappedExp)) +{ + this->e1 = e1; + this->type = type; +} + +#endif + Expression *extractOpDollarSideEffect(Scope *sc, UnaExp *ue) { - Expression *e0 = NULL; - if (ue->e1->hasSideEffect()) + Expression *e0; + Expression *e1 = Expression::extractLast(ue->e1, &e0); + // Bugzilla 12585: Extract the side effect part if ue->e1 is comma. + + if (!isTrivialExp(e1)) { - /* Even if opDollar is needed, 'ue->e1' should be evaluate only once. So + /* Even if opDollar is needed, 'e1' should be evaluate only once. So * Rewrite: - * ue->e1.opIndex( ... use of $ ... ) - * ue->e1.opSlice( ... use of $ ... ) + * e1.opIndex( ... use of $ ... ) + * e1.opSlice( ... use of $ ... ) * as: - * (ref __dop = ue->e1, __dop).opIndex( ... __dop.opDollar ...) - * (ref __dop = ue->e1, __dop).opSlice( ... __dop.opDollar ...) + * (ref __dop = e1, __dop).opIndex( ... __dop.opDollar ...) + * (ref __dop = e1, __dop).opSlice( ... __dop.opDollar ...) */ Identifier *id = Lexer::uniqueId("__dop"); - ExpInitializer *ei = new ExpInitializer(ue->loc, ue->e1); - VarDeclaration *v = new VarDeclaration(ue->loc, ue->e1->type, id, ei); + ExpInitializer *ei = new ExpInitializer(ue->loc, e1); + VarDeclaration *v = new VarDeclaration(ue->loc, e1->type, id, ei); v->storage_class |= STCtemp | STCctfe - | (ue->e1->isLvalue() ? (STCforeach | STCref) : 0); - e0 = new DeclarationExp(ue->loc, v); - e0 = e0->semantic(sc); - ue->e1 = new VarExp(ue->loc, v); - ue->e1 = ue->e1->semantic(sc); + | (e1->isLvalue() ? STCforeach | STCref : STCrvalue); + Expression *de = new DeclarationExp(ue->loc, v); + de = de->semantic(sc); + e0 = Expression::combine(e0, de); + e1 = new VarExp(ue->loc, v); + e1 = e1->semantic(sc); } + ue->e1 = e1; return e0; } @@ -14021,14 +13979,32 @@ Expression *extractOpDollarSideEffect(Scope *sc, UnaExp *ue) * if '$' was used. */ -Expression *resolveOpDollar(Scope *sc, ArrayExp *ae) +Expression *resolveOpDollar(Scope *sc, ArrayExp *ae, Expression **pe0) { assert(!ae->lengthVar); - Expression *e0 = extractOpDollarSideEffect(sc, ae); + *pe0 = NULL; + + AggregateDeclaration *ad = isAggregate(ae->e1->type); + Dsymbol *slice = search_function(ad, Id::slice); + //printf("slice = %s %s\n", slice->kind(), slice->toChars()); for (size_t i = 0; i < ae->arguments->dim; i++) { + if (i == 0) + *pe0 = extractOpDollarSideEffect(sc, ae); + + Expression *e = (*ae->arguments)[i]; + if (e->op == TOKinterval && !(slice && slice->isTemplateDeclaration())) + { + Lfallback: + if (ae->arguments->dim == 1) + return NULL; + ae->error("multi-dimensional slicing requires template opSlice"); + return new ErrorExp(); + } + //printf("[%d] e = %s\n", i, e->toChars()); + // Create scope for '$' variable for this dimension ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, ae); sym->loc = ae->loc; @@ -14037,23 +14013,56 @@ Expression *resolveOpDollar(Scope *sc, ArrayExp *ae) ae->lengthVar = NULL; // Create it only if required ae->currentDimension = i; // Dimension for $, if required - Expression *e = (*ae->arguments)[i]; e = e->semantic(sc); e = resolveProperties(sc, e); - if (!e->type) - ae->error("%s has no value", e->toChars()); + if (ae->lengthVar && sc->func) { // If $ was used, declare it now Expression *de = new DeclarationExp(ae->loc, ae->lengthVar); - e = new CommaExp(Loc(), de, e); + de = de->semantic(sc); + *pe0 = Expression::combine(*pe0, de); + } + sc = sc->pop(); + + if (e->op == TOKinterval) + { + IntervalExp *ie = (IntervalExp *)e; + + Objects *tiargs = new Objects(); + Expression *edim = new IntegerExp(ae->loc, i, Type::tsize_t); + edim = edim->semantic(sc); + tiargs->push(edim); + + Expressions *fargs = new Expressions(); + fargs->push(ie->lwr); + fargs->push(ie->upr); + + unsigned xerrors = global.startGagging(); + sc = sc->push(); + FuncDeclaration *fslice = resolveFuncCall(ae->loc, sc, slice, tiargs, ae->e1->type, fargs, 1); + sc = sc->pop(); + global.endGagging(xerrors); + if (!fslice) + goto Lfallback; + + e = new DotTemplateInstanceExp(ae->loc, ae->e1, slice->ident, tiargs); + e = new CallExp(ae->loc, e, fargs); e = e->semantic(sc); } + + if (!e->type) + { + ae->error("%s has no value", e->toChars()); + e = new ErrorExp(); + } + if (e->op == TOKerror) + return e; + (*ae->arguments)[i] = e; - sc = sc->pop(); } - return e0; + return ae; } /************************************** @@ -14061,14 +14070,15 @@ Expression *resolveOpDollar(Scope *sc, ArrayExp *ae) * if '$' was used. */ -Expression *resolveOpDollar(Scope *sc, SliceExp *se) +Expression *resolveOpDollar(Scope *sc, SliceExp *se, Expression **pe0) { assert(!se->lengthVar); assert(!se->lwr || se->upr); - if (!se->lwr) return NULL; + if (!se->lwr) + return se; - Expression *e0 = extractOpDollarSideEffect(sc, se); + *pe0 = extractOpDollarSideEffect(sc, se); // create scope for '$' ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, se); @@ -14082,7 +14092,10 @@ Expression *resolveOpDollar(Scope *sc, SliceExp *se) e = e->semantic(sc); e = resolveProperties(sc, e); if (!e->type) + { se->error("%s has no value", e->toChars()); + return new ErrorExp(); + } (i == 0 ? se->lwr : se->upr) = e; } @@ -14090,74 +14103,112 @@ Expression *resolveOpDollar(Scope *sc, SliceExp *se) { // If $ was used, declare it now Expression *de = new DeclarationExp(se->loc, se->lengthVar); - se->lwr = new CommaExp(Loc(), de, se->lwr); - se->lwr = se->lwr->semantic(sc); + de = de->semantic(sc); + *pe0 = Expression::combine(*pe0, de); } sc = sc->pop(); - return e0; + return se; } Expression *BinExp::reorderSettingAAElem(Scope *sc) { - if (this->e1->op != TOKindex) - return this; - IndexExp *ie = (IndexExp *)e1; - Type *t1 = ie->e1->type->toBasetype(); - if (t1->ty != Taarray) - return this; + BinExp *be = this; - /* Check recursive conversion */ - VarDeclaration *var; - bool isrefvar = (e2->op == TOKvar && - (var = ((VarExp *)e2)->var->isVarDeclaration()) != NULL); - if (isrefvar) - return this; + if (be->e1->op != TOKindex) + return be; + IndexExp *ie = (IndexExp *)be->e1; + if (ie->e1->type->toBasetype()->ty != Taarray) + return be; /* Fix evaluation order of setting AA element. (Bugzilla 3825) * Rewrite: - * aa[key] op= val; + * aa[k1][k2][k3] op= val; * as: - * ref __aatmp = aa; - * ref __aakey = key; - * ref __aaval = val; - * __aatmp[__aakey] op= __aaval; // assignment + * auto ref __aatmp = aa; + * auto ref __aakey3 = k1, __aakey2 = k2, __aakey1 = k3; + * auto ref __aaval = val; + * __aatmp[__aakey3][__aakey2][__aakey1] op= __aaval; // assignment */ - Expression *ec = NULL; - if (ie->e1->hasSideEffect()) + + Expression *de = NULL; + while (1) + { + if (!isTrivialExp(ie->e2)) + { + Identifier *id = Lexer::uniqueId("__aakey"); + VarDeclaration *vd = new VarDeclaration(ie->e2->loc, ie->e2->type, id, new ExpInitializer(ie->e2->loc, ie->e2)); + vd->storage_class |= STCtemp + | (ie->e2->isLvalue() ? STCref | STCforeach : STCrvalue); + de = Expression::combine(new DeclarationExp(ie->e2->loc, vd), de); + + ie->e2 = new VarExp(ie->e2->loc, vd); + ie->e2->type = vd->type; + } + + Expression *ie1 = ie->e1; + if (ie1->op != TOKindex || + ((IndexExp *)ie1)->e1->type->toBasetype()->ty != Taarray) + { + break; + } + ie = (IndexExp *)ie1; + } + assert(ie->e1->type->toBasetype()->ty == Taarray); + + if (!isTrivialExp(ie->e1)) { Identifier *id = Lexer::uniqueId("__aatmp"); VarDeclaration *vd = new VarDeclaration(ie->e1->loc, ie->e1->type, id, new ExpInitializer(ie->e1->loc, ie->e1)); - vd->storage_class |= STCtemp; - Expression *de = new DeclarationExp(ie->e1->loc, vd); - if (ie->e1->isLvalue()) - vd->storage_class |= STCref | STCforeach; - ec = de; + vd->storage_class |= STCtemp + | (ie->e1->isLvalue() ? STCref | STCforeach : STCrvalue); + de = Expression::combine(new DeclarationExp(ie->e1->loc, vd), de); + ie->e1 = new VarExp(ie->e1->loc, vd); + ie->e1->type = vd->type; } - if (ie->e2->hasSideEffect()) - { - Identifier *id = Lexer::uniqueId("__aakey"); - VarDeclaration *vd = new VarDeclaration(ie->e2->loc, ie->e2->type, id, new ExpInitializer(ie->e2->loc, ie->e2)); - vd->storage_class |= STCtemp; - if (ie->e2->isLvalue()) - vd->storage_class |= STCref | STCforeach; - Expression *de = new DeclarationExp(ie->e2->loc, vd); - ec = ec ? new CommaExp(loc, ec, de) : de; - ie->e2 = new VarExp(ie->e2->loc, vd); - } { Identifier *id = Lexer::uniqueId("__aaval"); - VarDeclaration *vd = new VarDeclaration(loc, this->e2->type, id, new ExpInitializer(this->e2->loc, this->e2)); - vd->storage_class |= STCtemp | STCrvalue; - if (this->e2->isLvalue()) - vd->storage_class |= STCref | STCforeach; - Expression *de = new DeclarationExp(this->e2->loc, vd); + VarDeclaration *vd = new VarDeclaration(be->loc, be->e2->type, id, new ExpInitializer(be->e2->loc, be->e2)); + vd->storage_class |= STCtemp + | (be->e2->isLvalue() ? STCref | STCforeach : STCrvalue); + de = Expression::combine(de, new DeclarationExp(be->e2->loc, vd)); + + be->e2 = new VarExp(be->e2->loc, vd); + be->e2->type = vd->type; + } + + de = de->semantic(sc); + //printf("-de = %s, be = %s\n", de->toChars(), be->toChars()); + return Expression::combine(de, be); +} + +/*************************************** + * Create a static array of TypeInfo references + * corresponding to an array of Expression's. + * Used to supply hidden _arguments[] value for variadic D functions. + */ - ec = ec ? new CommaExp(loc, ec, de) : de; - this->e2 = new VarExp(this->e2->loc, vd); +Expression *createTypeInfoArray(Scope *sc, Expression *exps[], size_t dim) +{ + /* + * Pass a reference to the TypeInfo_Tuple corresponding to the types of the + * arguments. Source compatibility is maintained by computing _arguments[] + * at the start of the called function by offseting into the TypeInfo_Tuple + * reference. + */ + Parameters *args = new Parameters; + args->setDim(dim); + for (size_t i = 0; i < dim; i++) + { + Parameter *arg = new Parameter(STCin, exps[i]->type, NULL, NULL); + (*args)[i] = arg; } - ec = new CommaExp(loc, ec, this); - return ec->semantic(sc); + TypeTuple *tup = new TypeTuple(args); + Expression *e = tup->getTypeInfo(sc); + e = e->optimize(WANTvalue); + assert(e->op == TOKsymoff); // should be SymOffExp + + return e; } diff --git a/gcc/d/dfrontend/expression.h b/gcc/d/dfrontend/expression.h index 38b29d3ee..6ff307474 100644 --- a/gcc/d/dfrontend/expression.h +++ b/gcc/d/dfrontend/expression.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/expression.h + */ #ifndef DMD_EXPRESSION_H #define DMD_EXPRESSION_H @@ -16,6 +17,7 @@ #include "lexer.h" #include "arraytypes.h" #include "intrange.h" +#include "visitor.h" class Type; class TypeVector; @@ -31,9 +33,6 @@ class Dsymbol; class Import; class Module; class ScopeDsymbol; -struct InlineCostState; -struct InlineDoState; -struct InlineScanState; class Expression; class Declaration; class AggregateDeclaration; @@ -69,8 +68,6 @@ struct elem; void initPrecedence(); -typedef int (*apply_fp_t)(Expression *, void *); - Expression *resolveProperties(Scope *sc, Expression *e); Expression *resolvePropertiesOnly(Scope *sc, Expression *e1); void accessCheck(Loc loc, Scope *sc, Expression *e, Declaration *d); @@ -84,16 +81,45 @@ int expandAliasThisTuples(Expressions *exps, size_t starti = 0); FuncDeclaration *hasThis(Scope *sc); Expression *fromConstInitializer(int result, Expression *e); bool arrayExpressionSemantic(Expressions *exps, Scope *sc); -int arrayExpressionCanThrow(Expressions *exps, bool mustNotThrow); +int arrayExpressionCanThrow(Expressions *exps, FuncDeclaration *func, bool mustNotThrow); TemplateDeclaration *getFuncTemplateDecl(Dsymbol *s); Expression *valueNoDtor(Expression *e); int modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1); Expression *resolveAliasThis(Scope *sc, Expression *e); Expression *callCpCtor(Scope *sc, Expression *e); -Expression *resolveOpDollar(Scope *sc, ArrayExp *ae); -Expression *resolveOpDollar(Scope *sc, SliceExp *se); - +Expression *resolveOpDollar(Scope *sc, ArrayExp *ae, Expression **pe0); +Expression *resolveOpDollar(Scope *sc, SliceExp *se, Expression **pe0); +Expression *integralPromotions(Expression *e, Scope *sc); +void discardValue(Expression *e); +bool isTrivialExp(Expression *e); + +int isConst(Expression *e); +Expression *toDelegate(Expression *e, Scope *sc); AggregateDeclaration *isAggregate(Type *t); +IntRange getIntRange(Expression *e); +bool isArrayOperand(Expression *e); +Expression *arrayOp(BinExp *e, Scope *sc); +Expression *arrayOp(BinAssignExp *e, Scope *sc); +bool hasSideEffect(Expression *e); +bool canThrow(Expression *e, FuncDeclaration *func, bool mustNotThrow); +Expression *Expression_optimize(Expression *e, int result, bool keepLvalue); +dt_t **Expression_toDt(Expression *e, dt_t **pdt); +elem *toElem(Expression *e, IRState *irs); +MATCH implicitConvTo(Expression *e, Type *t); +Expression *implicitCastTo(Expression *e, Scope *sc, Type *t); +Expression *castTo(Expression *e, Scope *sc, Type *t); +void toCBuffer(Expression *e, OutBuffer *buf, HdrGenState *hgs); +Expression *ctfeInterpret(Expression *); +Expression *inlineCopy(Expression *e, Scope *sc); +Expression *op_overload(Expression *e, Scope *sc); +Type *toStaticArrayType(SliceExp *e); +Expression *scaleFactor(BinExp *be, Scope *sc); +Expression *typeCombine(BinExp *be, Scope *sc); +Expression *inferType(Expression *e, Type *t, int flag = 0); +Expression *semanticTraits(TraitsExp *e, Scope *sc); +Type *getIndirection(Type *t); + +Expression *checkGC(Scope *sc, Expression *e); /* Run CTFE on the expression, but allow the expression to be a TypeExp * or a tuple containing a TypeExp. (This is required by pragma(msg)). @@ -110,6 +136,8 @@ enum CtfeGoal ctfeNeedNothing // The return value is not required }; +Expression *interpret(Expression *e, InterState *istate, CtfeGoal goal); + #define WANTflags 1 #define WANTvalue 2 // Same as WANTvalue, but also expand variables as far as possible @@ -128,21 +156,24 @@ class Expression : public RootObject static void init(); Expression *copy(); virtual Expression *syntaxCopy(); - virtual int apply(apply_fp_t fp, void *param); virtual Expression *semantic(Scope *sc); Expression *trySemantic(Scope *sc); - int dyncast() { return DYNCAST_EXPRESSION; } // kludge for template.isExpression() + // kludge for template.isExpression() + int dyncast() { return DYNCAST_EXPRESSION; } void print(); char *toChars(); - virtual void dump(int indent); void error(const char *format, ...); void warning(const char *format, ...); void deprecation(const char *format, ...); - virtual int rvalue(bool allowVoid = false); + virtual bool rvalue(); + // creates a single expression which is effectively (e1, e2) + // this new expression does not necessarily need to have valid D source code representation, + // for example, it may include declaration expressions static Expression *combine(Expression *e1, Expression *e2); + static Expression *extractLast(Expression *e, Expression **pe0); static Expressions *arraySyntaxCopy(Expressions *exps); virtual dinteger_t toInteger(); @@ -150,17 +181,27 @@ class Expression : public RootObject virtual real_t toReal(); virtual real_t toImaginary(); virtual complex_t toComplex(); - virtual StringExp *toString(); - virtual void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + virtual StringExp *toStringExp(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs) + { + ::toCBuffer(this, buf, hgs); + } virtual void toMangleBuffer(OutBuffer *buf); virtual int isLvalue(); virtual Expression *toLvalue(Scope *sc, Expression *e); virtual Expression *modifiableLvalue(Scope *sc, Expression *e); - virtual Expression *implicitCastTo(Scope *sc, Type *t); - virtual MATCH implicitConvTo(Type *t); - virtual IntRange getIntRange(); - virtual Expression *castTo(Scope *sc, Type *t); - virtual Expression *inferType(Type *t, int flag = 0, Scope *sc = NULL, TemplateParameters *tparams = NULL); + Expression *implicitCastTo(Scope *sc, Type *t) + { + return ::implicitCastTo(this, sc, t); + } + MATCH implicitConvTo(Type *t) + { + return ::implicitConvTo(this, t); + } + Expression *castTo(Scope *sc, Type *t) + { + return ::castTo(this, sc, t); + } virtual void checkEscape(); virtual void checkEscapeRef(); virtual Expression *resolveLoc(Loc loc, Scope *sc); @@ -168,83 +209,76 @@ class Expression : public RootObject void checkNoBool(); Expression *checkIntegral(); Expression *checkArithmetic(); + Expression *checkReadModifyWrite(TOK rmwOp, Expression *exp = NULL); void checkDeprecated(Scope *sc, Dsymbol *s); void checkPurity(Scope *sc, FuncDeclaration *f); void checkPurity(Scope *sc, VarDeclaration *v); void checkSafety(Scope *sc, FuncDeclaration *f); + void checkNogc(Scope *sc, FuncDeclaration *f); bool checkPostblit(Scope *sc, Type *t); virtual int checkModifiable(Scope *sc, int flag = 0); virtual Expression *checkToBoolean(Scope *sc); virtual Expression *addDtorHook(Scope *sc); Expression *checkToPointer(); - Expression *addressOf(Scope *sc); + Expression *addressOf(); Expression *deref(); - Expression *integralPromotions(Scope *sc); - - Expression *toDelegate(Scope *sc, Type *t); - virtual Expression *optimize(int result, bool keepLvalue = false); + Expression *optimize(int result, bool keepLvalue = false) + { + return Expression_optimize(this, result, keepLvalue); + } // Entry point for CTFE. // A compile-time result is required. Give an error if not possible - Expression *ctfeInterpret(); - - // Implementation of CTFE for this expression - virtual Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + Expression *ctfeInterpret() + { + return ::ctfeInterpret(this); + } + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue) + { + return ::interpret(this, istate, goal); + } - virtual int isConst(); + int isConst() { return ::isConst(this); } virtual int isBool(int result); - bool hasSideEffect(); - void discardValue(); - void useValue(); - bool canThrow(bool mustNotThrow); - - virtual int inlineCost3(InlineCostState *ics); - virtual Expression *doInline(InlineDoState *ids); - virtual Expression *inlineScan(InlineScanState *iss); - Expression *inlineCopy(Scope *sc); - - // For operator overloading - virtual int isCommutative(); - virtual Identifier *opId(); - virtual Identifier *opId_r(); - - // For array ops - virtual void buildArrayIdent(OutBuffer *buf, Expressions *arguments); - virtual Expression *buildArrayLoop(Parameters *fparams); - int isArrayOperand(); + Expression *op_overload(Scope *sc) + { + return ::op_overload(this, sc); + } // Back end virtual elem *toElem(IRState *irs); elem *toElemDtor(IRState *irs); virtual dt_t **toDt(dt_t **pdt); + virtual void accept(Visitor *v) { v->visit(this); } }; class IntegerExp : public Expression { -public: +private: dinteger_t value; +public: IntegerExp(Loc loc, dinteger_t value, Type *type); IntegerExp(dinteger_t value); bool equals(RootObject *o); Expression *semantic(Scope *sc); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); char *toChars(); - void dump(int indent); - IntRange getIntRange(); dinteger_t toInteger(); real_t toReal(); real_t toImaginary(); complex_t toComplex(); - int isConst(); int isBool(int result); - MATCH implicitConvTo(Type *t); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void toMangleBuffer(OutBuffer *buf); Expression *toLvalue(Scope *sc, Expression *e); elem *toElem(IRState *irs); dt_t **toDt(dt_t **pdt); + void accept(Visitor *v) { v->visit(this); } + dinteger_t getInteger() { return value; } + void setInteger(dinteger_t value); + +private: + void normalize(); }; class ErrorExp : public Expression @@ -252,11 +286,8 @@ class ErrorExp : public Expression public: ErrorExp(); - Expression *implicitCastTo(Scope *sc, Type *t); - MATCH implicitConvTo(Type *t); - Expression *castTo(Scope *sc, Type *t); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); Expression *toLvalue(Scope *sc, Expression *e); + void accept(Visitor *v) { v->visit(this); } }; class RealExp : public Expression @@ -267,20 +298,17 @@ class RealExp : public Expression RealExp(Loc loc, real_t value, Type *type); bool equals(RootObject *o); Expression *semantic(Scope *sc); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); char *toChars(); dinteger_t toInteger(); uinteger_t toUInteger(); real_t toReal(); real_t toImaginary(); complex_t toComplex(); - Expression *castTo(Scope *sc, Type *t); - int isConst(); int isBool(int result); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void toMangleBuffer(OutBuffer *buf); elem *toElem(IRState *irs); dt_t **toDt(dt_t **pdt); + void accept(Visitor *v) { v->visit(this); } }; class ComplexExp : public Expression @@ -291,20 +319,17 @@ class ComplexExp : public Expression ComplexExp(Loc loc, complex_t value, Type *type); bool equals(RootObject *o); Expression *semantic(Scope *sc); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); char *toChars(); dinteger_t toInteger(); uinteger_t toUInteger(); real_t toReal(); real_t toImaginary(); complex_t toComplex(); - Expression *castTo(Scope *sc, Type *t); - int isConst(); int isBool(int result); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void toMangleBuffer(OutBuffer *buf); elem *toElem(IRState *irs); dt_t **toDt(dt_t **pdt); + void accept(Visitor *v) { v->visit(this); } }; class IdentifierExp : public Expression @@ -317,16 +342,16 @@ class IdentifierExp : public Expression static IdentifierExp *create(Loc loc, Identifier *ident); Expression *semantic(Scope *sc); char *toChars(); - void dump(int indent); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); + void accept(Visitor *v) { v->visit(this); } }; class DollarExp : public IdentifierExp { public: DollarExp(Loc loc); + void accept(Visitor *v) { v->visit(this); } }; class DsymbolExp : public Expression @@ -338,10 +363,9 @@ class DsymbolExp : public Expression DsymbolExp(Loc loc, Dsymbol *s, bool hasOverloads = false); Expression *semantic(Scope *sc); char *toChars(); - void dump(int indent); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); + void accept(Visitor *v) { v->visit(this); } }; class ThisExp : public Expression @@ -351,18 +375,13 @@ class ThisExp : public Expression ThisExp(Loc loc); Expression *semantic(Scope *sc); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); int isBool(int result); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); Expression *modifiableLvalue(Scope *sc, Expression *e); - int inlineCost3(InlineCostState *ics); - Expression *doInline(InlineDoState *ids); - //Expression *inlineScan(InlineScanState *iss); - elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class SuperExp : public ThisExp @@ -370,10 +389,8 @@ class SuperExp : public ThisExp public: SuperExp(Loc loc); Expression *semantic(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - Expression *doInline(InlineDoState *ids); - //Expression *inlineScan(InlineScanState *iss); + void accept(Visitor *v) { v->visit(this); } }; class NullExp : public Expression @@ -385,15 +402,11 @@ class NullExp : public Expression bool equals(RootObject *o); Expression *semantic(Scope *sc); int isBool(int result); - int isConst(); - StringExp *toString(); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + StringExp *toStringExp(); void toMangleBuffer(OutBuffer *buf); - MATCH implicitConvTo(Type *t); - Expression *castTo(Scope *sc, Type *t); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); elem *toElem(IRState *irs); dt_t **toDt(dt_t **pdt); + void accept(Visitor *v) { v->visit(this); } }; class StringExp : public Expression @@ -413,23 +426,19 @@ class StringExp : public Expression //Expression *syntaxCopy(); bool equals(RootObject *o); Expression *semantic(Scope *sc); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); size_t length(); - StringExp *toString(); + StringExp *toStringExp(); StringExp *toUTF8(Scope *sc); - Expression *implicitCastTo(Scope *sc, Type *t); - MATCH implicitConvTo(Type *t); - Expression *castTo(Scope *sc, Type *t); int compare(RootObject *obj); int isBool(int result); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); Expression *modifiableLvalue(Scope *sc, Expression *e); unsigned charAt(uinteger_t i); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void toMangleBuffer(OutBuffer *buf); elem *toElem(IRState *irs); dt_t **toDt(dt_t **pdt); + void accept(Visitor *v) { v->visit(this); } }; // Tuple @@ -451,18 +460,12 @@ class TupleExp : public Expression TupleExp(Loc loc, Expressions *exps); TupleExp(Loc loc, TupleDeclaration *tup); Expression *syntaxCopy(); - int apply(apply_fp_t fp, void *param); bool equals(RootObject *o); Expression *semantic(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void checkEscape(); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - Expression *castTo(Scope *sc, Type *t); elem *toElem(IRState *irs); - Expression *doInline(InlineDoState *ids); - Expression *inlineScan(InlineScanState *iss); + void accept(Visitor *v) { v->visit(this); } }; class ArrayLiteralExp : public Expression @@ -475,27 +478,15 @@ class ArrayLiteralExp : public Expression ArrayLiteralExp(Loc loc, Expression *e); Expression *syntaxCopy(); - int apply(apply_fp_t fp, void *param); bool equals(RootObject *o); Expression *semantic(Scope *sc); int isBool(int result); elem *toElem(IRState *irs); - StringExp *toString(); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + StringExp *toStringExp(); void toMangleBuffer(OutBuffer *buf); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - Expression *implicitCastTo(Scope *sc, Type *t); - MATCH implicitConvTo(Type *t); - Expression *castTo(Scope *sc, Type *t); - Expression *inferType(Type *t, int flag = 0, Scope *sc = NULL, TemplateParameters *tparams = NULL); - dt_t **toDt(dt_t **pdt); - void buildArrayIdent(OutBuffer *buf, Expressions *arguments); - Expression *buildArrayLoop(Parameters *fparams); - - Expression *doInline(InlineDoState *ids); - Expression *inlineScan(InlineScanState *iss); + dt_t **toDt(dt_t **pdt); + void accept(Visitor *v) { v->visit(this); } }; class AssocArrayLiteralExp : public Expression @@ -508,20 +499,12 @@ class AssocArrayLiteralExp : public Expression AssocArrayLiteralExp(Loc loc, Expressions *keys, Expressions *values); bool equals(RootObject *o); Expression *syntaxCopy(); - int apply(apply_fp_t fp, void *param); Expression *semantic(Scope *sc); int isBool(int result); elem *toElem(IRState *irs); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void toMangleBuffer(OutBuffer *buf); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - MATCH implicitConvTo(Type *t); - Expression *castTo(Scope *sc, Type *t); - Expression *inferType(Type *t, int flag = 0, Scope *sc = NULL, TemplateParameters *tparams = NULL); - Expression *doInline(InlineDoState *ids); - Expression *inlineScan(InlineScanState *iss); + void accept(Visitor *v) { v->visit(this); } }; // scrubReturnValue is running @@ -551,39 +534,35 @@ class StructLiteralExp : public Expression int fillHoles; // fill alignment 'holes' with zero bool ownedByCtfe; // true = created in CTFE - StructLiteralExp *origin; // pointer to the origin instance of the expression. - // once a new expression is created, origin is set to 'this'. - // anytime when an expression copy is created, 'origin' pointer is set to - // 'origin' pointer value of the original expression. + // pointer to the origin instance of the expression. + // once a new expression is created, origin is set to 'this'. + // anytime when an expression copy is created, 'origin' pointer is set to + // 'origin' pointer value of the original expression. + StructLiteralExp *origin; - StructLiteralExp *inlinecopy; // those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer. - int stageflags; // anytime when recursive function is calling, 'stageflags' marks with bit flag of - // current stage and unmarks before return from this function. - // 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline' - // (with infinite recursion) of this expression. + // those fields need to prevent a infinite recursion when one field of struct initialized with 'this' pointer. + StructLiteralExp *inlinecopy; + + // anytime when recursive function is calling, 'stageflags' marks with bit flag of + // current stage and unmarks before return from this function. + // 'inlinecopy' uses similar 'stageflags' and from multiple evaluation 'doInline' + // (with infinite recursion) of this expression. + int stageflags; StructLiteralExp(Loc loc, StructDeclaration *sd, Expressions *elements, Type *stype = NULL); static StructLiteralExp *create(Loc loc, StructDeclaration *sd, void *elements, Type *stype = NULL); bool equals(RootObject *o); Expression *syntaxCopy(); - int apply(apply_fp_t fp, void *param); Expression *semantic(Scope *sc); Expression *getField(Type *type, unsigned offset); int getFieldIndex(Type *type, unsigned offset); elem *toElem(IRState *irs); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void toMangleBuffer(OutBuffer *buf); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); Expression *addDtorHook(Scope *sc); dt_t **toDt(dt_t **pdt); Symbol *toSymbol(); - MATCH implicitConvTo(Type *t); - Expression *castTo(Scope *sc, Type *t); - int inlineCost3(InlineCostState *ics); - Expression *doInline(InlineDoState *ids); - Expression *inlineScan(InlineScanState *iss); + void accept(Visitor *v) { v->visit(this); } }; class DotIdExp; @@ -595,10 +574,9 @@ class TypeExp : public Expression TypeExp(Loc loc, Type *type); Expression *syntaxCopy(); Expression *semantic(Scope *sc); - int rvalue(bool allowVoid = false); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - Expression *optimize(int result, bool keepLvalue = false); + bool rvalue(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class ScopeExp : public Expression @@ -610,7 +588,7 @@ class ScopeExp : public Expression Expression *syntaxCopy(); Expression *semantic(Scope *sc); elem *toElem(IRState *irs); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void accept(Visitor *v) { v->visit(this); } }; class TemplateExp : public Expression @@ -620,10 +598,10 @@ class TemplateExp : public Expression FuncDeclaration *fd; TemplateExp(Loc loc, TemplateDeclaration *td, FuncDeclaration *fd = NULL); - int rvalue(bool allowVoid = false); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + bool rvalue(); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); + void accept(Visitor *v) { v->visit(this); } }; class NewExp : public Expression @@ -643,17 +621,10 @@ class NewExp : public Expression NewExp(Loc loc, Expression *thisexp, Expressions *newargs, Type *newtype, Expressions *arguments); Expression *syntaxCopy(); - int apply(apply_fp_t fp, void *param); Expression *semantic(Scope *sc); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - Expression *optimize(int result, bool keepLvalue = false); - MATCH implicitConvTo(Type *t); elem *toElem(IRState *irs); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - //int inlineCost3(InlineCostState *ics); - Expression *doInline(InlineDoState *ids); - //Expression *inlineScan(InlineScanState *iss); + void accept(Visitor *v) { v->visit(this); } }; class NewAnonClassExp : public Expression @@ -669,9 +640,8 @@ class NewAnonClassExp : public Expression NewAnonClassExp(Loc loc, Expression *thisexp, Expressions *newargs, ClassDeclaration *cd, Expressions *arguments); Expression *syntaxCopy(); - int apply(apply_fp_t fp, void *param); Expression *semantic(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void accept(Visitor *v) { v->visit(this); } }; class SymbolExp : public Expression @@ -683,6 +653,7 @@ class SymbolExp : public Expression SymbolExp(Loc loc, TOK op, int size, Declaration *var, bool hasOverloads); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; // Offset from symbol @@ -694,17 +665,11 @@ class SymOffExp : public SymbolExp SymOffExp(Loc loc, Declaration *var, dinteger_t offset, bool hasOverloads = false); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void checkEscape(); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - int isConst(); int isBool(int result); - Expression *doInline(InlineDoState *ids); - MATCH implicitConvTo(Type *t); - Expression *castTo(Scope *sc, Type *t); dt_t **toDt(dt_t **pdt); + void accept(Visitor *v) { v->visit(this); } }; // Variable @@ -716,22 +681,17 @@ class VarExp : public SymbolExp static VarExp *create(Loc loc, Declaration *var, bool hasOverloads = false); bool equals(RootObject *o); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - void dump(int indent); char *toChars(); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void checkEscape(); void checkEscapeRef(); int checkModifiable(Scope *sc, int flag); + bool checkReadModifyWrite(); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); Expression *modifiableLvalue(Scope *sc, Expression *e); - dt_t **toDt(dt_t **pdt); - int inlineCost3(InlineCostState *ics); - Expression *doInline(InlineDoState *ids); - //Expression *inlineScan(InlineScanState *iss); + dt_t **toDt(dt_t **pdt); + void accept(Visitor *v) { v->visit(this); } }; // Overload Set @@ -744,7 +704,7 @@ class OverExp : public Expression OverExp(Loc loc, OverloadSet *s); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void accept(Visitor *v) { v->visit(this); } }; // Function/Delegate literal @@ -757,27 +717,24 @@ class FuncExp : public Expression TOK tok; FuncExp(Loc loc, FuncLiteralDeclaration *fd, TemplateDeclaration *td = NULL); + bool rvalue(); void genIdent(Scope *sc); Expression *syntaxCopy(); Expression *semantic(Scope *sc); Expression *semantic(Scope *sc, Expressions *arguments); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - Expression *implicitCastTo(Scope *sc, Type *t); - MATCH implicitConvTo(Type *t); - Expression *castTo(Scope *sc, Type *t); - Expression *inferType(Type *t, int flag = 0, Scope *sc = NULL, TemplateParameters *tparams = NULL); + MATCH matchType(Type *to, Scope *sc, FuncExp **pfe, int flag = 0); char *toChars(); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + elem *toElem(IRState *irs); dt_t **toDt(dt_t **pdt); - - int inlineCost3(InlineCostState *ics); - //Expression *doInline(InlineDoState *ids); - //Expression *inlineScan(InlineScanState *iss); + void accept(Visitor *v) { v->visit(this); } }; // Declaration of a symbol +// D grammar allows declarations only as statements. However in AST representation +// it can be part of any expression. This is used, for example, during internal +// syntax re-writes to inject hidden symbols. class DeclarationExp : public Expression { public: @@ -786,13 +743,9 @@ class DeclarationExp : public Expression DeclarationExp(Loc loc, Dsymbol *declaration); Expression *syntaxCopy(); Expression *semantic(Scope *sc); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); elem *toElem(IRState *irs); - int inlineCost3(InlineCostState *ics); - Expression *doInline(InlineDoState *ids); - Expression *inlineScan(InlineScanState *iss); + void accept(Visitor *v) { v->visit(this); } }; class TypeidExp : public Expression @@ -803,7 +756,7 @@ class TypeidExp : public Expression TypeidExp(Loc loc, RootObject *obj); Expression *syntaxCopy(); Expression *semantic(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void accept(Visitor *v) { v->visit(this); } }; class TraitsExp : public Expression @@ -815,10 +768,7 @@ class TraitsExp : public Expression TraitsExp(Loc loc, Identifier *ident, Objects *args); Expression *syntaxCopy(); Expression *semantic(Scope *sc); - Expression *isTypeX(bool (*fp)(Type *t)); - Expression *isFuncX(bool (*fp)(FuncDeclaration *f)); - Expression *isDeclX(bool (*fp)(Declaration *d)); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void accept(Visitor *v) { v->visit(this); } }; class HaltExp : public Expression @@ -826,9 +776,9 @@ class HaltExp : public Expression public: HaltExp(Loc loc); Expression *semantic(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class IsExp : public Expression @@ -848,7 +798,7 @@ class IsExp : public Expression TOK tok2, TemplateParameters *parameters); Expression *syntaxCopy(); Expression *semantic(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void accept(Visitor *v) { v->visit(this); } }; /****************************************************************/ @@ -861,18 +811,11 @@ class UnaExp : public Expression UnaExp(Loc loc, TOK op, int size, Expression *e1); Expression *syntaxCopy(); - int apply(apply_fp_t fp, void *param); - Expression *semantic(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - Expression *optimize(int result, bool keepLvalue = false); - void dump(int indent); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + Expression *semantic(Scope *sc) = 0; + Expression *unaSemantic(Scope *sc); Expression *resolveLoc(Loc loc, Scope *sc); - Expression *doInline(InlineDoState *ids); - Expression *inlineScan(InlineScanState *iss); - - virtual Expression *op_overload(Scope *sc); + void accept(Visitor *v) { v->visit(this); } }; typedef Expression *(*fp_t)(Type *, Expression *, Expression *); @@ -889,36 +832,17 @@ class BinExp : public Expression BinExp(Loc loc, TOK op, int size, Expression *e1, Expression *e2); Expression *syntaxCopy(); - int apply(apply_fp_t fp, void *param); - Expression *semantic(Scope *sc); - Expression *semanticp(Scope *sc); + Expression *semantic(Scope *sc) = 0; + Expression *binSemantic(Scope *sc); + Expression *binSemanticProp(Scope *sc); Expression *checkComplexOpAssign(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - Expression *scaleFactor(Scope *sc); - Expression *typeCombine(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); int isunsigned(); Expression *incompatibleTypes(); - void dump(int indent); - - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - Expression *interpretCommon(InterState *istate, CtfeGoal goal, fp_t fp); - Expression *interpretCompareCommon(InterState *istate, CtfeGoal goal, fp2_t fp); - Expression *interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_t fp, int post = 0); - Expression *interpretFourPointerRelation(InterState *istate, CtfeGoal goal); - virtual Expression *arrayOp(Scope *sc); - Expression *doInline(InlineDoState *ids); - Expression *inlineScan(InlineScanState *iss); - - Expression *op_overload(Scope *sc); - Expression *compare_overload(Scope *sc, Identifier *id); Expression *reorderSettingAAElem(Scope *sc); - void buildArrayIdent(OutBuffer *buf, Expressions *arguments); - Expression *buildArrayLoop(Parameters *fparams); - elem *toElemBin(IRState *irs, int op); + void accept(Visitor *v) { v->visit(this); } }; class BinAssignExp : public BinExp @@ -930,18 +854,11 @@ class BinAssignExp : public BinExp } Expression *semantic(Scope *sc); - Expression *arrayOp(Scope *sc); - - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - - Expression *op_overload(Scope *sc); - - void buildArrayIdent(OutBuffer *buf, Expressions *arguments); - Expression *buildArrayLoop(Parameters *fparams); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *ex); Expression *modifiableLvalue(Scope *sc, Expression *e); + void accept(Visitor *v) { v->visit(this); } }; /****************************************************************/ @@ -951,7 +868,7 @@ class CompileExp : public UnaExp public: CompileExp(Loc loc, Expression *e); Expression *semantic(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void accept(Visitor *v) { v->visit(this); } }; class FileExp : public UnaExp @@ -959,7 +876,7 @@ class FileExp : public UnaExp public: FileExp(Loc loc, Expression *e); Expression *semantic(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void accept(Visitor *v) { v->visit(this); } }; class AssertExp : public UnaExp @@ -969,15 +886,10 @@ class AssertExp : public UnaExp AssertExp(Loc loc, Expression *e, Expression *msg = NULL); Expression *syntaxCopy(); - int apply(apply_fp_t fp, void *param); Expression *semantic(Scope *sc); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - - Expression *doInline(InlineDoState *ids); - Expression *inlineScan(InlineScanState *iss); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class DotIdExp : public UnaExp @@ -990,8 +902,7 @@ class DotIdExp : public UnaExp Expression *semantic(Scope *sc); Expression *semanticX(Scope *sc); Expression *semanticY(Scope *sc, int flag); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - void dump(int i); + void accept(Visitor *v) { v->visit(this); } }; class DotTemplateExp : public UnaExp @@ -1000,7 +911,8 @@ class DotTemplateExp : public UnaExp TemplateDeclaration *td; DotTemplateExp(Loc loc, Expression *e, TemplateDeclaration *td); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Expression *semantic(Scope *sc); + void accept(Visitor *v) { v->visit(this); } }; class DotVarExp : public UnaExp @@ -1012,14 +924,12 @@ class DotVarExp : public UnaExp DotVarExp(Loc loc, Expression *e, Declaration *var, bool hasOverloads = false); Expression *semantic(Scope *sc); int checkModifiable(Scope *sc, int flag); + bool checkReadModifyWrite(); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); Expression *modifiableLvalue(Scope *sc, Expression *e); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - void dump(int indent); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class DotTemplateInstanceExp : public UnaExp @@ -1028,12 +938,12 @@ class DotTemplateInstanceExp : public UnaExp TemplateInstance *ti; DotTemplateInstanceExp(Loc loc, Expression *e, Identifier *name, Objects *tiargs); + DotTemplateInstanceExp(Loc loc, Expression *e, TemplateInstance *ti); Expression *syntaxCopy(); bool findTempDecl(Scope *sc); Expression *semantic(Scope *sc); Expression *semanticY(Scope *sc, int flag); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - void dump(int indent); + void accept(Visitor *v) { v->visit(this); } }; class DelegateExp : public UnaExp @@ -1044,14 +954,9 @@ class DelegateExp : public UnaExp DelegateExp(Loc loc, Expression *e, FuncDeclaration *func, bool hasOverloads = false); Expression *semantic(Scope *sc); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - MATCH implicitConvTo(Type *t); - Expression *castTo(Scope *sc, Type *t); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - void dump(int indent); - int inlineCost3(InlineCostState *ics); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class DotTypeExp : public UnaExp @@ -1061,8 +966,8 @@ class DotTypeExp : public UnaExp DotTypeExp(Loc loc, Expression *e, Dsymbol *sym); Expression *semantic(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class CallExp : public UnaExp @@ -1081,23 +986,13 @@ class CallExp : public UnaExp static CallExp *create(Loc loc, Expression *e, Expression *earg1); Expression *syntaxCopy(); - int apply(apply_fp_t fp, void *param); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - void dump(int indent); elem *toElem(IRState *irs); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); Expression *addDtorHook(Scope *sc); - Expression *implicitCastTo(Scope *sc, Type *t); - MATCH implicitConvTo(Type *t); - int inlineCost3(InlineCostState *ics); - Expression *doInline(InlineDoState *ids); - Expression *inlineScan(InlineScanState *iss); - Expression *inlineScan(InlineScanState *iss, Expression *eret); + void accept(Visitor *v) { v->visit(this); } }; class AddrExp : public UnaExp @@ -1107,11 +1002,8 @@ class AddrExp : public UnaExp Expression *semantic(Scope *sc); void checkEscape(); elem *toElem(IRState *irs); - MATCH implicitConvTo(Type *t); - Expression *castTo(Scope *sc, Type *t); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); dt_t **toDt(dt_t **pdt); + void accept(Visitor *v) { v->visit(this); } }; class PtrExp : public UnaExp @@ -1125,13 +1017,9 @@ class PtrExp : public UnaExp int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); Expression *modifiableLvalue(Scope *sc, Expression *e); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); elem *toElem(IRState *irs); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - // For operator overloading - Identifier *opId(); + void accept(Visitor *v) { v->visit(this); } }; class NegExp : public UnaExp @@ -1139,15 +1027,9 @@ class NegExp : public UnaExp public: NegExp(Loc loc, Expression *e); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - void buildArrayIdent(OutBuffer *buf, Expressions *arguments); - Expression *buildArrayLoop(Parameters *fparams); - IntRange getIntRange(); - - // For operator overloading - Identifier *opId(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class UAddExp : public UnaExp @@ -1156,8 +1038,7 @@ class UAddExp : public UnaExp UAddExp(Loc loc, Expression *e); Expression *semantic(Scope *sc); - // For operator overloading - Identifier *opId(); + void accept(Visitor *v) { v->visit(this); } }; class ComExp : public UnaExp @@ -1165,15 +1046,9 @@ class ComExp : public UnaExp public: ComExp(Loc loc, Expression *e); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - void buildArrayIdent(OutBuffer *buf, Expressions *arguments); - Expression *buildArrayLoop(Parameters *fparams); - IntRange getIntRange(); - - // For operator overloading - Identifier *opId(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class NotExp : public UnaExp @@ -1181,8 +1056,8 @@ class NotExp : public UnaExp public: NotExp(Loc loc, Expression *e); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class BoolExp : public UnaExp @@ -1190,8 +1065,8 @@ class BoolExp : public UnaExp public: BoolExp(Loc loc, Expression *e, Type *type); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class DeleteExp : public UnaExp @@ -1200,8 +1075,8 @@ class DeleteExp : public UnaExp DeleteExp(Loc loc, Expression *e); Expression *semantic(Scope *sc); Expression *checkToBoolean(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class CastExp : public UnaExp @@ -1215,20 +1090,11 @@ class CastExp : public UnaExp CastExp(Loc loc, Expression *e, unsigned char mod); Expression *syntaxCopy(); Expression *semantic(Scope *sc); - MATCH implicitConvTo(Type *t); - IntRange getIntRange(); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void checkEscape(); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - void buildArrayIdent(OutBuffer *buf, Expressions *arguments); - Expression *buildArrayLoop(Parameters *fparams); - elem *toElem(IRState *irs); - // For operator overloading - Identifier *opId(); - Expression *op_overload(Scope *sc); + elem *toElem(IRState *irs); dt_t **toDt(dt_t **pdt); + void accept(Visitor *v) { v->visit(this); } }; class VectorExp : public UnaExp @@ -1240,9 +1106,9 @@ class VectorExp : public UnaExp VectorExp(Loc loc, Expression *e, Type *t); Expression *syntaxCopy(); Expression *semantic(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); elem *toElem(IRState *irs); dt_t **toDt(dt_t **pdt); + void accept(Visitor *v) { v->visit(this); } }; class SliceExp : public UnaExp @@ -1254,7 +1120,6 @@ class SliceExp : public UnaExp SliceExp(Loc loc, Expression *e1, Expression *lwr, Expression *upr); Expression *syntaxCopy(); - int apply(apply_fp_t fp, void *param); Expression *semantic(Scope *sc); void checkEscape(); void checkEscapeRef(); @@ -1263,19 +1128,9 @@ class SliceExp : public UnaExp Expression *toLvalue(Scope *sc, Expression *e); Expression *modifiableLvalue(Scope *sc, Expression *e); int isBool(int result); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - Type *toStaticArrayType(); - MATCH implicitConvTo(Type *t); - Expression *castTo(Scope *sc, Type *t); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - void dump(int indent); elem *toElem(IRState *irs); - void buildArrayIdent(OutBuffer *buf, Expressions *arguments); - Expression *buildArrayLoop(Parameters *fparams); - Expression *doInline(InlineDoState *ids); - Expression *inlineScan(InlineScanState *iss); + void accept(Visitor *v) { v->visit(this); } }; class ArrayLengthExp : public UnaExp @@ -1283,12 +1138,44 @@ class ArrayLengthExp : public UnaExp public: ArrayLengthExp(Loc loc, Expression *e1); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); elem *toElem(IRState *irs); static Expression *rewriteOpAssign(BinExp *exp); + void accept(Visitor *v) { v->visit(this); } +}; + +class IntervalExp : public Expression +{ +public: + Expression *lwr; + Expression *upr; + + IntervalExp(Loc loc, Expression *lwr, Expression *upr); + Expression *syntaxCopy(); + Expression *semantic(Scope *sc); + void accept(Visitor *v) { v->visit(this); } +}; + +class DelegatePtrExp : public UnaExp +{ +public: + DelegatePtrExp(Loc loc, Expression *e1); + Expression *semantic(Scope *sc); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *e); + elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } +}; + +class DelegateFuncptrExp : public UnaExp +{ +public: + DelegateFuncptrExp(Loc loc, Expression *e1); + Expression *semantic(Scope *sc); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *e); + elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; // e1[a0,a1,a2,a3,...] @@ -1302,18 +1189,11 @@ class ArrayExp : public UnaExp ArrayExp(Loc loc, Expression *e1, Expressions *arguments); Expression *syntaxCopy(); - int apply(apply_fp_t fp, void *param); Expression *semantic(Scope *sc); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - // For operator overloading - Identifier *opId(); - Expression *op_overload(Scope *sc); - - Expression *doInline(InlineDoState *ids); - Expression *inlineScan(InlineScanState *iss); + void accept(Visitor *v) { v->visit(this); } }; /****************************************************************/ @@ -1323,7 +1203,7 @@ class DotExp : public BinExp public: DotExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void accept(Visitor *v) { v->visit(this); } }; class CommaExp : public BinExp @@ -1334,17 +1214,13 @@ class CommaExp : public BinExp void checkEscape(); void checkEscapeRef(); int checkModifiable(Scope *sc, int flag); - IntRange getIntRange(); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); Expression *modifiableLvalue(Scope *sc, Expression *e); int isBool(int result); - MATCH implicitConvTo(Type *t); Expression *addDtorHook(Scope *sc); - Expression *castTo(Scope *sc, Type *t); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class IndexExp : public BinExp @@ -1361,12 +1237,9 @@ class IndexExp : public BinExp int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); Expression *modifiableLvalue(Scope *sc, Expression *e); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - Expression *doInline(InlineDoState *ids); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; /* For both i++ and i-- @@ -1376,10 +1249,8 @@ class PostExp : public BinExp public: PostExp(TOK op, Loc loc, Expression *e); Expression *semantic(Scope *sc); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - Identifier *opId(); // For operator overloading elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; /* For both ++i and --i @@ -1389,7 +1260,7 @@ class PreExp : public UnaExp public: PreExp(TOK op, Loc loc, Expression *e); Expression *semantic(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void accept(Visitor *v) { v->visit(this); } }; class AssignExp : public BinExp @@ -1399,85 +1270,90 @@ class AssignExp : public BinExp AssignExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); + int isLvalue(); + Expression *toLvalue(Scope *sc, Expression *ex); Expression *checkToBoolean(Scope *sc); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - Identifier *opId(); // For operator overloading - void buildArrayIdent(OutBuffer *buf, Expressions *arguments); - Expression *buildArrayLoop(Parameters *fparams); - - Expression *inlineScan(InlineScanState *iss); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class ConstructExp : public AssignExp { public: ConstructExp(Loc loc, Expression *e1, Expression *e2); + void accept(Visitor *v) { v->visit(this); } +}; + +class BlitExp : public AssignExp +{ +public: + BlitExp(Loc loc, Expression *e1, Expression *e2); + void accept(Visitor *v) { v->visit(this); } }; class AddAssignExp : public BinAssignExp { public: AddAssignExp(Loc loc, Expression *e1, Expression *e2); - Identifier *opId(); /* For operator overloading */ elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class MinAssignExp : public BinAssignExp { public: MinAssignExp(Loc loc, Expression *e1, Expression *e2); - Identifier *opId(); /* For operator overloading */ elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class MulAssignExp : public BinAssignExp { public: MulAssignExp(Loc loc, Expression *e1, Expression *e2); - Identifier *opId(); /* For operator overloading */ elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class DivAssignExp : public BinAssignExp { public: DivAssignExp(Loc loc, Expression *e1, Expression *e2); - Identifier *opId(); /* For operator overloading */ elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class ModAssignExp : public BinAssignExp { public: ModAssignExp(Loc loc, Expression *e1, Expression *e2); - Identifier *opId(); /* For operator overloading */ elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class AndAssignExp : public BinAssignExp { public: AndAssignExp(Loc loc, Expression *e1, Expression *e2); - Identifier *opId(); /* For operator overloading */ elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class OrAssignExp : public BinAssignExp { public: OrAssignExp(Loc loc, Expression *e1, Expression *e2); - Identifier *opId(); /* For operator overloading */ elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class XorAssignExp : public BinAssignExp { public: XorAssignExp(Loc loc, Expression *e1, Expression *e2); - Identifier *opId(); /* For operator overloading */ elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class PowAssignExp : public BinAssignExp @@ -1485,32 +1361,32 @@ class PowAssignExp : public BinAssignExp public: PowAssignExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Identifier *opId(); /* For operator overloading */ elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class ShlAssignExp : public BinAssignExp { public: ShlAssignExp(Loc loc, Expression *e1, Expression *e2); - Identifier *opId(); /* For operator overloading */ elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class ShrAssignExp : public BinAssignExp { public: ShrAssignExp(Loc loc, Expression *e1, Expression *e2); - Identifier *opId(); /* For operator overloading */ elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class UshrAssignExp : public BinAssignExp { public: UshrAssignExp(Loc loc, Expression *e1, Expression *e2); - Identifier *opId(); /* For operator overloading */ elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class CatAssignExp : public BinAssignExp @@ -1518,8 +1394,8 @@ class CatAssignExp : public BinAssignExp public: CatAssignExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Identifier *opId(); /* For operator overloading */ elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class AddExp : public BinExp @@ -1527,15 +1403,9 @@ class AddExp : public BinExp public: AddExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - IntRange getIntRange(); - - // For operator overloading - int isCommutative(); - Identifier *opId(); - Identifier *opId_r(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class MinExp : public BinExp @@ -1543,14 +1413,9 @@ class MinExp : public BinExp public: MinExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - IntRange getIntRange(); - - // For operator overloading - Identifier *opId(); - Identifier *opId_r(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class CatExp : public BinExp @@ -1558,14 +1423,9 @@ class CatExp : public BinExp public: CatExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - - // For operator overloading - Identifier *opId(); - Identifier *opId_r(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class MulExp : public BinExp @@ -1573,15 +1433,9 @@ class MulExp : public BinExp public: MulExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - IntRange getIntRange(); - - // For operator overloading - int isCommutative(); - Identifier *opId(); - Identifier *opId_r(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class DivExp : public BinExp @@ -1589,14 +1443,9 @@ class DivExp : public BinExp public: DivExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - IntRange getIntRange(); - - // For operator overloading - Identifier *opId(); - Identifier *opId_r(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class ModExp : public BinExp @@ -1604,14 +1453,9 @@ class ModExp : public BinExp public: ModExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - IntRange getIntRange(); - - // For operator overloading - Identifier *opId(); - Identifier *opId_r(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class PowExp : public BinExp @@ -1619,13 +1463,9 @@ class PowExp : public BinExp public: PowExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - - // For operator overloading - Identifier *opId(); - Identifier *opId_r(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class ShlExp : public BinExp @@ -1633,14 +1473,9 @@ class ShlExp : public BinExp public: ShlExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - IntRange getIntRange(); - - // For operator overloading - Identifier *opId(); - Identifier *opId_r(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class ShrExp : public BinExp @@ -1648,14 +1483,9 @@ class ShrExp : public BinExp public: ShrExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - IntRange getIntRange(); - - // For operator overloading - Identifier *opId(); - Identifier *opId_r(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class UshrExp : public BinExp @@ -1663,14 +1493,9 @@ class UshrExp : public BinExp public: UshrExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - IntRange getIntRange(); - - // For operator overloading - Identifier *opId(); - Identifier *opId_r(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class AndExp : public BinExp @@ -1678,15 +1503,9 @@ class AndExp : public BinExp public: AndExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - IntRange getIntRange(); - - // For operator overloading - int isCommutative(); - Identifier *opId(); - Identifier *opId_r(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class OrExp : public BinExp @@ -1694,16 +1513,9 @@ class OrExp : public BinExp public: OrExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - MATCH implicitConvTo(Type *t); - IntRange getIntRange(); - - // For operator overloading - int isCommutative(); - Identifier *opId(); - Identifier *opId_r(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class XorExp : public BinExp @@ -1711,16 +1523,9 @@ class XorExp : public BinExp public: XorExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - MATCH implicitConvTo(Type *t); - IntRange getIntRange(); - - // For operator overloading - int isCommutative(); - Identifier *opId(); - Identifier *opId_r(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class OrOrExp : public BinExp @@ -1729,9 +1534,8 @@ class OrOrExp : public BinExp OrOrExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); Expression *checkToBoolean(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class AndAndExp : public BinExp @@ -1740,9 +1544,8 @@ class AndAndExp : public BinExp AndAndExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); Expression *checkToBoolean(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class CmpExp : public BinExp @@ -1750,14 +1553,9 @@ class CmpExp : public BinExp public: CmpExp(TOK op, Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - - // For operator overloading - int isCommutative(); - Identifier *opId(); - Expression *op_overload(Scope *sc); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class InExp : public BinExp @@ -1765,22 +1563,18 @@ class InExp : public BinExp public: InExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - - // For operator overloading - Identifier *opId(); - Identifier *opId_r(); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; class RemoveExp : public BinExp { public: RemoveExp(Loc loc, Expression *e1, Expression *e2); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + Expression *semantic(Scope *sc); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; // == and != @@ -1790,14 +1584,9 @@ class EqualExp : public BinExp public: EqualExp(TOK op, Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - - // For operator overloading - int isCommutative(); - Identifier *opId(); - Expression *op_overload(Scope *sc); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; // is and !is @@ -1807,8 +1596,8 @@ class IdentityExp : public BinExp public: IdentityExp(TOK op, Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; /****************************************************************/ @@ -1820,10 +1609,7 @@ class CondExp : public BinExp CondExp(Loc loc, Expression *econd, Expression *e1, Expression *e2); Expression *syntaxCopy(); - int apply(apply_fp_t fp, void *param); Expression *semantic(Scope *sc); - Expression *optimize(int result, bool keepLvalue = false); - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void checkEscape(); void checkEscapeRef(); int checkModifiable(Scope *sc, int flag); @@ -1831,15 +1617,9 @@ class CondExp : public BinExp Expression *toLvalue(Scope *sc, Expression *e); Expression *modifiableLvalue(Scope *sc, Expression *e); Expression *checkToBoolean(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - MATCH implicitConvTo(Type *t); - Expression *castTo(Scope *sc, Type *t); - Expression *inferType(Type *t, int flag = 0, Scope *sc = NULL, TemplateParameters *tparams = NULL); - - Expression *doInline(InlineDoState *ids); - Expression *inlineScan(InlineScanState *iss); elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } }; /****************************************************************/ @@ -1850,7 +1630,7 @@ class DefaultInitExp : public Expression TOK subop; // which of the derived classes this is DefaultInitExp(Loc loc, TOK subop, int size); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void accept(Visitor *v) { v->visit(this); } }; class FileInitExp : public DefaultInitExp @@ -1859,6 +1639,7 @@ class FileInitExp : public DefaultInitExp FileInitExp(Loc loc); Expression *semantic(Scope *sc); Expression *resolveLoc(Loc loc, Scope *sc); + void accept(Visitor *v) { v->visit(this); } }; class LineInitExp : public DefaultInitExp @@ -1867,6 +1648,7 @@ class LineInitExp : public DefaultInitExp LineInitExp(Loc loc); Expression *semantic(Scope *sc); Expression *resolveLoc(Loc loc, Scope *sc); + void accept(Visitor *v) { v->visit(this); } }; class ModuleInitExp : public DefaultInitExp @@ -1875,6 +1657,7 @@ class ModuleInitExp : public DefaultInitExp ModuleInitExp(Loc loc); Expression *semantic(Scope *sc); Expression *resolveLoc(Loc loc, Scope *sc); + void accept(Visitor *v) { v->visit(this); } }; class FuncInitExp : public DefaultInitExp @@ -1883,6 +1666,7 @@ class FuncInitExp : public DefaultInitExp FuncInitExp(Loc loc); Expression *semantic(Scope *sc); Expression *resolveLoc(Loc loc, Scope *sc); + void accept(Visitor *v) { v->visit(this); } }; class PrettyFuncInitExp : public DefaultInitExp @@ -1891,8 +1675,23 @@ class PrettyFuncInitExp : public DefaultInitExp PrettyFuncInitExp(Loc loc); Expression *semantic(Scope *sc); Expression *resolveLoc(Loc loc, Scope *sc); + void accept(Visitor *v) { v->visit(this); } }; +#ifdef IN_GCC + +class WrappedExp : public Expression +{ +public: + elem *e1; + + WrappedExp(Loc loc, elem *e1, Type *type); + elem *toElem(IRState *irs); + void accept(Visitor *v) { v->visit(this); } +}; + +#endif + /****************************************************************/ /* Special values used by the interpreter diff --git a/gcc/d/dfrontend/file.c b/gcc/d/dfrontend/file.c index c0131d3de..369698f31 100644 --- a/gcc/d/dfrontend/file.c +++ b/gcc/d/dfrontend/file.c @@ -1,11 +1,11 @@ -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/file.c + */ #include "file.h" @@ -86,7 +86,7 @@ int File::read() if (len) return 0; // already read the file #if POSIX - off_t size; + size_t size; ssize_t numread; int fd; struct stat buf; @@ -112,7 +112,7 @@ int File::read() printf("\tfstat error, errno = %d\n",errno); goto err2; } - size = buf.st_size; + size = (size_t)buf.st_size; buffer = (unsigned char *) ::malloc(size + 2); if (!buffer) { @@ -227,7 +227,7 @@ int File::mmread() char *name; name = this->name->toChars(); - hFile = CreateFile(name, GENERIC_READ, + hFile = CreateFileA(name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) @@ -235,7 +235,7 @@ int File::mmread() size = GetFileSize(hFile, NULL); //printf(" file created, size %d\n", size); - hFileMap = CreateFileMapping(hFile,NULL,PAGE_READONLY,0,size,NULL); + hFileMap = CreateFileMappingA(hFile,NULL,PAGE_READONLY,0,size,NULL); if (CloseHandle(hFile) != TRUE) goto Lerr; diff --git a/gcc/d/dfrontend/file.h b/gcc/d/dfrontend/file.h index 6cbfb12b4..9bba958e3 100644 --- a/gcc/d/dfrontend/file.h +++ b/gcc/d/dfrontend/file.h @@ -1,11 +1,11 @@ -// Copyright (c) 1999-2011 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/file.h + */ #ifndef FILE_H #define FILE_H diff --git a/gcc/d/dfrontend/filename.c b/gcc/d/dfrontend/filename.c index 64254a980..75ecd7884 100644 --- a/gcc/d/dfrontend/filename.c +++ b/gcc/d/dfrontend/filename.c @@ -1,11 +1,11 @@ -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/filename.c + */ #include "filename.h" @@ -145,8 +145,7 @@ Strings *FileName::splitPath(const char *path) } if (buf.offset) // if path is not empty { - buf.writeByte(0); // to asciiz - array->push(buf.extractData()); + array->push(buf.extractString()); } } while (c); } @@ -623,41 +622,18 @@ int FileName::ensurePathExists(const char *path) */ const char *FileName::canonicalName(const char *name) { -#if __linux__ - // Lovely glibc extension to do it for us - return canonicalize_file_name(name); -#elif POSIX - #if _POSIX_VERSION >= 200809L || defined (__linux__) +#if POSIX // NULL destination buffer is allowed and preferred return realpath(name, NULL); - #else - char *cname = NULL; - #if PATH_MAX - /* PATH_MAX must be defined as a constant in , - * otherwise using it is unsafe due to TOCTOU - */ - size_t path_max = (size_t)PATH_MAX; - if (path_max > 0) - { - /* Need to add one to PATH_MAX because of realpath() buffer overflow bug: - * http://isec.pl/vulnerabilities/isec-0011-wu-ftpd.txt - */ - cname = (char *)malloc(path_max + 1); - if (cname == NULL) - return NULL; - } - #endif - return realpath(name, cname); - #endif #elif _WIN32 /* Apparently, there is no good way to do this on Windows. * GetFullPathName isn't it, but use it anyway. */ - DWORD result = GetFullPathName(name, 0, NULL, NULL); + DWORD result = GetFullPathNameA(name, 0, NULL, NULL); if (result) { char *buf = (char *)malloc(result); - result = GetFullPathName(name, result, buf, NULL); + result = GetFullPathNameA(name, result, buf, NULL); if (result == 0) { ::free(buf); diff --git a/gcc/d/dfrontend/filename.h b/gcc/d/dfrontend/filename.h index a897a60ac..bd43339b7 100644 --- a/gcc/d/dfrontend/filename.h +++ b/gcc/d/dfrontend/filename.h @@ -1,11 +1,11 @@ -// Copyright (c) 1999-2011 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/filename.h + */ #ifndef FILENAME_H #define FILENAME_H diff --git a/gcc/d/dfrontend/func.c b/gcc/d/dfrontend/func.c index cacc35fe4..987cfff54 100644 --- a/gcc/d/dfrontend/func.c +++ b/gcc/d/dfrontend/func.c @@ -1,11 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. + +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/func.c + */ #include #include @@ -27,9 +29,253 @@ #include "target.h" #include "parse.h" #include "rmem.h" +#include "visitor.h" -void functionToCBuffer2(TypeFunction *t, OutBuffer *buf, HdrGenState *hgs, int mod, const char *kind); +void functionToBufferWithIdent(TypeFunction *t, OutBuffer *buf, const char *ident); void genCmain(Scope *sc); +void toBufferShort(Type *t, OutBuffer *buf, HdrGenState *hgs); + +/* A visitor to walk entire statements and provides ability to replace any sub-statements. + */ +class StatementRewriteWalker : public Visitor +{ + /* Point the currently visited statement. + * By using replaceCurrent() method, you can replace AST during walking. + */ + Statement **ps; + void visitStmt(Statement *&s) { ps = &s; s->accept(this); } +public: + void replaceCurrent(Statement *s) { *ps = s; } + + void visit(ErrorStatement *s) { } + void visit(PeelStatement *s) + { + if (s->s) + visitStmt(s->s); + } + void visit(ExpStatement *s) { } + void visit(DtorExpStatement *s) { } + void visit(CompileStatement *s) { } + void visit(CompoundStatement *s) + { + if (s->statements && s->statements->dim) + { + for (size_t i = 0; i < s->statements->dim; i++) + { + if ((*s->statements)[i]) + visitStmt((*s->statements)[i]); + } + } + } + void visit(CompoundDeclarationStatement *s) { visit((CompoundStatement *)s); } + void visit(UnrolledLoopStatement *s) + { + if (s->statements && s->statements->dim) + { + for (size_t i = 0; i < s->statements->dim; i++) + { + if ((*s->statements)[i]) + visitStmt((*s->statements)[i]); + } + } + } + void visit(ScopeStatement *s) + { + if (s->statement) + visitStmt(s->statement); + } + void visit(WhileStatement *s) + { + if (s->body) + visitStmt(s->body); + } + void visit(DoStatement *s) + { + if (s->body) + visitStmt(s->body); + } + void visit(ForStatement *s) + { + if (s->init) + visitStmt(s->init); + if (s->body) + visitStmt(s->body); + } + void visit(ForeachStatement *s) + { + if (s->body) + visitStmt(s->body); + } + void visit(ForeachRangeStatement *s) + { + if (s->body) + visitStmt(s->body); + } + void visit(IfStatement *s) + { + if (s->ifbody) + visitStmt(s->ifbody); + if (s->elsebody) + visitStmt(s->elsebody); + } + void visit(ConditionalStatement *s) { } + void visit(PragmaStatement *s) { } + void visit(StaticAssertStatement *s) { } + void visit(SwitchStatement *s) + { + if (s->body) + visitStmt(s->body); + } + void visit(CaseStatement *s) + { + if (s->statement) + visitStmt(s->statement); + } + void visit(CaseRangeStatement *s) + { + if (s->statement) + visitStmt(s->statement); + } + void visit(DefaultStatement *s) + { + if (s->statement) + visitStmt(s->statement); + } + void visit(GotoDefaultStatement *s) { } + void visit(GotoCaseStatement *s) { } + void visit(SwitchErrorStatement *s) { } + void visit(ReturnStatement *s) { } + void visit(BreakStatement *s) { } + void visit(ContinueStatement *s) { } + void visit(SynchronizedStatement *s) + { + if (s->body) + visitStmt(s->body); + } + void visit(WithStatement *s) + { + if (s->body) + visitStmt(s->body); + } + void visit(TryCatchStatement *s) + { + if (s->body) + visitStmt(s->body); + if (s->catches && s->catches->dim) + { + for (size_t i = 0; i < s->catches->dim; i++) + { + Catch *c = (*s->catches)[i]; + if (c && c->handler) + visitStmt(c->handler); + } + } + } + void visit(TryFinallyStatement *s) + { + if (s->body) + visitStmt(s->body); + if (s->finalbody) + visitStmt(s->finalbody); + } + void visit(OnScopeStatement *s) { } + void visit(ThrowStatement *s) { } + void visit(DebugStatement *s) + { + if (s->statement) + visitStmt(s->statement); + } + void visit(GotoStatement *s) { } + void visit(LabelStatement *s) + { + if (s->statement) + visitStmt(s->statement); + } + void visit(AsmStatement *s) { } +#ifdef IN_GCC + void visit(ExtAsmStatement *s) { } +#endif + void visit(ImportStatement *s) { } +}; + +/* Tweak all return statements and dtor call for nrvo_var, for correct NRVO. + */ +class NrvoWalker : public StatementRewriteWalker +{ +public: + FuncDeclaration *fd; + Scope *sc; + + void visit(ReturnStatement *s) + { + Expression *exp = s->exp; + if (exp) + { + TypeFunction *tf = (TypeFunction *)fd->type; + + /* Bugzilla 10789: + * If NRVO is not possible, all returned lvalues should call their postblits. + */ + if (!fd->nrvo_can && !tf->isref && exp->isLvalue()) + exp = callCpCtor(sc, exp); + + /* Bugzilla 8665: + * If auto function has multiple return statements, returned values + * should be fixed to the determined return type. + */ + if (!fd->tintro && !tf->next->immutableOf()->equals(exp->type->immutableOf())) + { + exp = exp->castTo(sc, tf->next); + exp = exp->optimize(WANTvalue); + } + + s->exp = exp; + } + } + void visit(TryFinallyStatement *s) + { + DtorExpStatement *des; + if (fd->nrvo_can && + s->finalbody && (des = s->finalbody->isDtorExpStatement()) != NULL && + fd->nrvo_var == des->var) + { + /* Normally local variable dtors are called regardless exceptions. + * But for nrvo_var, its dtor should be called only when exception is thrown. + * + * Rewrite: + * try { s->body; } finally { nrvo_var->edtor; } + * // equivalent with: + * // s->body; scope(exit) nrvo_var->edtor; + * as: + * try { s->body; } catch(__o) { nrvo_var->edtor; throw __o; } + * // equivalent with: + * // s->body; scope(failure) nrvo_var->edtor; + */ + Statement *sexception = new DtorExpStatement(Loc(), fd->nrvo_var->edtor, fd->nrvo_var); + Identifier *id = Lexer::uniqueId("__o"); + + Statement *handler = new PeelStatement(sexception); + if (sexception->blockExit(fd, false) & BEfallthru) + { + ThrowStatement *ts = new ThrowStatement(Loc(), new IdentifierExp(Loc(), id)); + ts->internalThrow = true; + handler = new CompoundStatement(Loc(), handler, ts); + } + + Catches *catches = new Catches(); + Catch *ctch = new Catch(Loc(), NULL, id, handler); + ctch->internalCatch = true; + ctch->semantic(sc); // Run semantic to resolve identifier '__o' + catches->push(ctch); + + Statement *s2 = new TryCatchStatement(Loc(), s->body, catches); + replaceCurrent(s2); + s2->accept(this); + } + else + StatementRewriteWalker::visit(s); + } +}; /********************************* FuncDeclaration ****************************/ @@ -41,7 +287,11 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageCla this->storage_class = storage_class; this->type = type; if (type) + { + // Normalize storage_class, because function-type related attributes + // are already set in the 'type' in parsing phase. this->storage_class &= ~(STC_TYPECTOR | STC_FUNCATTR); + } this->loc = loc; this->endloc = endloc; fthrows = NULL; @@ -74,7 +324,6 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageCla inlineNest = 0; ctfeCode = NULL; isArrayOp = 0; - dArrayOp = NULL; semantic3Errors = false; fes = NULL; introducing = 0; @@ -92,7 +341,6 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageCla tookAddressOf = 0; requiresClosure = false; flags = 0; - returns = NULL; gotos = NULL; } @@ -119,7 +367,6 @@ void FuncDeclaration::semantic(Scope *sc) { TypeFunction *f; AggregateDeclaration *ad; - ClassDeclaration *cd; InterfaceDeclaration *id; #if 0 @@ -198,7 +445,7 @@ void FuncDeclaration::semantic(Scope *sc) if (!type->deco) { sc = sc->push(); - sc->stc |= storage_class & STCdisable; // forward to function type + sc->stc |= storage_class & (STCdisable | STCdeprecated); // forward to function type TypeFunction *tf = (TypeFunction *)type; #if 1 /* If the parent is @safe, then this function defaults to safe @@ -223,8 +470,11 @@ void FuncDeclaration::semantic(Scope *sc) #endif if (tf->isref) sc->stc |= STCref; if (tf->isnothrow) sc->stc |= STCnothrow; + if (tf->isnogc) sc->stc |= STCnogc; if (tf->isproperty) sc->stc |= STCproperty; if (tf->purity == PUREfwdref) sc->stc |= STCpure; + if (tf->trust != TRUSTdefault) + sc->stc &= ~(STCsafe | STCsystem | STCtrusted); if (tf->trust == TRUSTsafe) sc->stc |= STCsafe; if (tf->trust == TRUSTsystem) sc->stc |= STCsystem; if (tf->trust == TRUSTtrusted) sc->stc |= STCtrusted; @@ -240,7 +490,8 @@ void FuncDeclaration::semantic(Scope *sc) tret = Type::tvoid; } else - { tret = ad->handle; + { + tret = ad->handleType(); assert(tret); tret = tret->addStorageClass(storage_class | sc->stc); tret = tret->addMod(type->mod); @@ -257,7 +508,7 @@ void FuncDeclaration::semantic(Scope *sc) { OutBuffer buf; MODtoBuffer(&buf, tf->mod); - error("without 'this' cannot be %s", buf.toChars()); + error("without 'this' cannot be %s", buf.peekString()); tf->mod = 0; // remove qualifiers } @@ -325,8 +576,6 @@ void FuncDeclaration::semantic(Scope *sc) type = type->semantic(loc, sc); sc = sc->pop(); } - - storage_class &= ~STCref; if (type->ty != Tfunction) { if (type->ty != Terror) @@ -337,6 +586,23 @@ void FuncDeclaration::semantic(Scope *sc) errors = true; return; } + else + { + // Merge back function attributes into 'originalType'. + // It's used for mangling, ddoc, and json output. + TypeFunction *tfo = (TypeFunction *)originalType; + TypeFunction *tfx = (TypeFunction *)type; + tfo->mod = tfx->mod; + tfo->isref = tfx->isref; + tfo->isnothrow = tfx->isnothrow; + tfo->isnogc = tfx->isnogc; + tfo->isproperty = tfx->isproperty; + tfo->purity = tfx->purity; + tfo->trust = tfx->trust; + + storage_class &= ~(STC_TYPECTOR | STC_FUNCATTR); + } + f = (TypeFunction *)type; size_t nparams = Parameter::dim(f->parameters); @@ -349,7 +615,7 @@ void FuncDeclaration::semantic(Scope *sc) if (isStatic()) sfunc = "static"; else if (protection == PROTprivate || protection == PROTpackage) - sfunc = Pprotectionnames[protection]; + sfunc = protectionToChars(protection); else sfunc = "non-virtual"; error("%s functions cannot be abstract", sfunc); @@ -358,7 +624,7 @@ void FuncDeclaration::semantic(Scope *sc) if (isOverride() && !isVirtual()) { if ((prot() == PROTprivate || prot() == PROTpackage) && isMember()) - error("%s method is not virtual and cannot override", Pprotectionnames[prot()]); + error("%s method is not virtual and cannot override", protectionToChars(prot())); else error("cannot override a non-virtual function"); } @@ -381,39 +647,6 @@ void FuncDeclaration::semantic(Scope *sc) } #endif - StructDeclaration *sd = parent->isStructDeclaration(); - if (sd) - { - if (isCtorDeclaration()) - { - goto Ldone; - } -#if 0 - // Verify no constructors, destructors, etc. - if (isCtorDeclaration() - //||isDtorDeclaration() - //|| isInvariantDeclaration() - //|| isUnitTestDeclaration() - ) - { - error("special member functions not allowed for %ss", sd->kind()); - } - - if (isInvariantDeclaration()) - sd->invs.push(this); - - if (!sd->aggNew) - sd->aggNew = isNewDeclaration(); - - if (isDelete()) - { - if (sd->aggDelete) - error("multiple delete's for struct %s", sd->toChars()); - sd->aggDelete = (DeleteDeclaration *)(this); - } -#endif - } - id = parent->isInterfaceDeclaration(); if (id) { @@ -434,20 +667,23 @@ void FuncDeclaration::semantic(Scope *sc) if (!fbody && (fensure || frequire) && !(id && isVirtual())) error("in and out contracts require function body"); - cd = parent->isClassDeclaration(); - if (cd) + if (StructDeclaration *sd = parent->isStructDeclaration()) + { + if (isCtorDeclaration()) + { + goto Ldone; + } + } + + if (ClassDeclaration *cd = parent->isClassDeclaration()) { - int vi; if (isCtorDeclaration()) { -// ctor = (CtorDeclaration *)this; -// if (!cd->ctor) -// cd->ctor = ctor; goto Ldone; } if (storage_class & STCabstract) - cd->isabstract = 1; + cd->isabstract = true; // if static function, do not put in vtbl[] if (!isVirtual()) @@ -490,8 +726,8 @@ void FuncDeclaration::semantic(Scope *sc) /* Find index of existing function in base class's vtbl[] to override * (the index will be the same as in cd's current vtbl[]) */ - vi = cd->baseClass ? findVtblIndex((Dsymbols*)&cd->baseClass->vtbl, (int)cd->baseClass->vtbl.dim) - : -1; + int vi = cd->baseClass ? findVtblIndex((Dsymbols*)&cd->baseClass->vtbl, (int)cd->baseClass->vtbl.dim) + : -1; bool doesoverride = false; switch (vi) @@ -595,7 +831,8 @@ void FuncDeclaration::semantic(Scope *sc) doesoverride = true; if (!isOverride()) - ::deprecation(loc, "overriding base class function without using override attribute is deprecated (%s overrides %s)", toPrettyChars(), fdv->toPrettyChars()); + ::deprecation(loc, "implicitly overriding base class method %s with %s deprecated; add 'override' attribute", + fdv->toPrettyChars(), toPrettyChars()); if (fdc->toParent() == parent) { @@ -783,10 +1020,10 @@ void FuncDeclaration::semantic(Scope *sc) } // If it's a member template - ClassDeclaration *cd2 = ti->tempdecl->isClassMember(); - if (cd2) + ClassDeclaration *cd = ti->tempdecl->isClassMember(); + if (cd) { - error("cannot use template to add virtual function to class '%s'", cd2->toChars()); + error("cannot use template to add virtual function to class '%s'", cd->toChars()); } } } @@ -841,6 +1078,7 @@ void FuncDeclaration::semantic(Scope *sc) Loc loc = frequire->loc; TypeFunction *tf = new TypeFunction(NULL, Type::tvoid, 0, LINKd); tf->isnothrow = f->isnothrow; + tf->isnogc = f->isnogc; tf->purity = f->purity; tf->trust = f->trust; FuncDeclaration *fd = new FuncDeclaration(loc, loc, @@ -872,6 +1110,7 @@ void FuncDeclaration::semantic(Scope *sc) } TypeFunction *tf = new TypeFunction(arguments, Type::tvoid, 0, LINKd); tf->isnothrow = f->isnothrow; + tf->isnogc = f->isnogc; tf->purity = f->purity; tf->trust = f->trust; FuncDeclaration *fd = new FuncDeclaration(loc, loc, @@ -896,7 +1135,7 @@ void FuncDeclaration::semantic(Scope *sc) if (fbody && (isFuncLiteralDeclaration() || isInstantiated() && !isVirtualMethod() && - !(ti = parent->isTemplateInstance(), ti && !ti->isTemplateMixin() && ti->name != ident))) + !(ti = parent->isTemplateInstance(), ti && !ti->isTemplateMixin() && ti->tempdecl->ident != ident))) { if (f->purity == PUREimpure) // purity not specified flags |= FUNCFLAGpurityInprocess; @@ -906,6 +1145,9 @@ void FuncDeclaration::semantic(Scope *sc) if (!f->isnothrow) flags |= FUNCFLAGnothrowInprocess; + + if (!f->isnogc) + flags |= FUNCFLAGnogcInprocess; } Module::dprogress++; @@ -914,7 +1156,7 @@ void FuncDeclaration::semantic(Scope *sc) /* Save scope for possible later use (if we need the * function internals) */ - scope = new Scope(*sc); + scope = sc->copy(); scope->setNoFree(); static bool printedMain = false; // semantic might run more than once @@ -975,21 +1217,6 @@ void FuncDeclaration::semantic3(Scope *sc) if (!inferRetType && f->next->ty == Terror) return; -#if 0 - // Check the 'throws' clause - if (fthrows) - { - for (size_t i = 0; i < fthrows->dim; i++) - { - Type *t = (*fthrows)[i]; - - t = t->semantic(loc, sc); - if (!t->isClassHandle()) - error("can only throw classes, not %s", t->toChars()); - } - } -#endif - if (!fbody && inferRetType && !type->nextOf()) { error("has no function body with return type inference"); @@ -1035,32 +1262,39 @@ void FuncDeclaration::semantic3(Scope *sc) sc2->stc &= ~(STCauto | STCscope | STCstatic | STCabstract | STCdeprecated | STCoverride | STC_TYPECTOR | STCfinal | STCtls | STCgshared | STCref | - STCproperty | STCsafe | STCtrusted | STCsystem); + STCproperty | STCnothrow | STCpure | STCsafe | STCtrusted | STCsystem); sc2->protection = PROTpublic; sc2->explicitProtection = 0; sc2->structalign = STRUCTALIGN_DEFAULT; if (this->ident != Id::require && this->ident != Id::ensure) sc2->flags = sc->flags & ~SCOPEcontract; + sc2->flags &= ~SCOPEcompile; sc2->tf = NULL; + sc2->os = NULL; sc2->noctor = 0; - sc2->speculative = sc->speculative || isSpeculative() != NULL; sc2->userAttribDecl = NULL; if (sc2->intypeof == 1) sc2->intypeof = 2; sc2->fieldinit = NULL; sc2->fieldinit_dim = 0; - // Declare 'this' - AggregateDeclaration *ad = isThis(); - if (ad) + if (isMember2()) { - if (isFuncLiteralDeclaration() && isNested() && !sc->intypeof) + FuncLiteralDeclaration *fld = isFuncLiteralDeclaration(); + if (fld && !sc->intypeof) { - error("function literals cannot be class members"); - return; + if (fld->tok == TOKreserved) + fld->tok = TOKfunction; + if (isNested()) + { + error("cannot be class members"); + return; + } } - else - assert(!isNested() || sc->intypeof); // can't be both member and nested + assert(!isNested() || sc->intypeof); // can't be both member and nested } + + // Declare 'this' + AggregateDeclaration *ad = isThis(); vthis = declareThis(sc2, ad); // Declare hidden variable _arguments[] and _argptr @@ -1068,7 +1302,8 @@ void FuncDeclaration::semantic3(Scope *sc) { #ifndef IN_GCC if (global.params.is64bit && !global.params.isWindows) - { // Declare save area for varargs registers + { + // Declare save area for varargs registers Type *t = new TypeIdentifier(loc, Id::va_argsave_t); t = t->semantic(loc, sc); if (t == Type::terror) @@ -1088,7 +1323,8 @@ void FuncDeclaration::semantic3(Scope *sc) #endif if (f->linkage == LINKd) - { // Declare _arguments[] + { + // Declare _arguments[] v_arguments = new VarDeclaration(Loc(), Type::typeinfotypelist->type, Id::_arguments_typeinfo, NULL); v_arguments->storage_class |= STCtemp | STCparameter; v_arguments->semantic(sc2); @@ -1104,7 +1340,8 @@ void FuncDeclaration::semantic3(Scope *sc) _arguments->parent = this; } if (f->linkage == LINKd || (f->parameters && Parameter::dim(f->parameters))) - { // Declare _argptr + { + // Declare _argptr Type *t = Type::tvalist; argptr = new VarDeclaration(Loc(), t, Id::_argptr, NULL); argptr->storage_class |= STCtemp; @@ -1119,14 +1356,17 @@ void FuncDeclaration::semantic3(Scope *sc) if (f->parameters) { for (size_t i = 0; i < f->parameters->dim; i++) - { Parameter *arg = (*f->parameters)[i]; + { + Parameter *arg = (*f->parameters)[i]; //printf("[%d] arg->type->ty = %d %s\n", i, arg->type->ty, arg->type->toChars()); if (arg->type->ty == Ttuple) - { TypeTuple *t = (TypeTuple *)arg->type; + { + TypeTuple *t = (TypeTuple *)arg->type; size_t dim = Parameter::dim(t->arguments); for (size_t j = 0; j < dim; j++) - { Parameter *narg = Parameter::getNth(t->arguments, j); + { + Parameter *narg = Parameter::getNth(t->arguments, j); narg->storageClass = arg->storageClass; } } @@ -1139,7 +1379,8 @@ void FuncDeclaration::semantic3(Scope *sc) */ size_t nparams = Parameter::dim(f->parameters); if (nparams) - { /* parameters[] has all the tuples removed, as the back end + { + /* parameters[] has all the tuples removed, as the back end * doesn't know about tuples */ parameters = new VarDeclarations(); @@ -1180,17 +1421,20 @@ void FuncDeclaration::semantic3(Scope *sc) if (f->parameters) { for (size_t i = 0; i < f->parameters->dim; i++) - { Parameter *arg = (*f->parameters)[i]; + { + Parameter *arg = (*f->parameters)[i]; if (!arg->ident) continue; // never used, so ignore if (arg->type->ty == Ttuple) - { TypeTuple *t = (TypeTuple *)arg->type; + { + TypeTuple *t = (TypeTuple *)arg->type; size_t dim = Parameter::dim(t->arguments); Objects *exps = new Objects(); exps->setDim(dim); for (size_t j = 0; j < dim; j++) - { Parameter *narg = Parameter::getNth(t->arguments, j); + { + Parameter *narg = Parameter::getNth(t->arguments, j); assert(narg->ident); VarDeclaration *v = sc2->search(Loc(), narg->ident, NULL)->isVarDeclaration(); assert(v); @@ -1229,17 +1473,42 @@ void FuncDeclaration::semantic3(Scope *sc) } if (inv) { - e = new DsymbolExp(Loc(), inv); + #if 1 + // Workaround for bugzilla 13394: For the correct mangling, + // run attribute inference on inv if needed. + inv->functionSemantic(); + #endif + + //e = new DsymbolExp(Loc(), inv); + //e = new CallExp(Loc(), e); + //e = e->semantic(sc2); + + /* Bugzilla 13113: Currently virtual invariant calls completely + * bypass attribute enforcement. + * Change the behavior of pre-invariant call by following it. + */ + e = new ThisExp(Loc()); + e->type = vthis->type; + e = new DotVarExp(Loc(), e, inv, 0); + e->type = inv->type; e = new CallExp(Loc(), e); - e = e->semantic(sc2); + e->type = Type::tvoid; } } else - { // Call invariant virtually + { + #if 1 + // Workaround for bugzilla 13394: For the correct mangling, + // run attribute inference on inv if needed. + if (ad->isStructDeclaration() && ad->inv) + ad->inv->functionSemantic(); + #endif + + // Call invariant virtually Expression *v = new ThisExp(Loc()); v->type = vthis->type; if (ad->isStructDeclaration()) - v = v->addressOf(sc); + v = v->addressOf(); Expression *se = new StringExp(Loc(), (char *)"null this"); se = se->semantic(sc); se->type = Type::tchar->arrayOf(); @@ -1269,17 +1538,41 @@ void FuncDeclaration::semantic3(Scope *sc) } if (inv) { - e = new DsymbolExp(Loc(), inv); + #if 1 + // Workaround for bugzilla 13394: For the correct mangling, + // run attribute inference on inv if needed. + inv->functionSemantic(); + #endif + + //e = new DsymbolExp(Loc(), inv); + //e = new CallExp(Loc(), e); + //e = e->semantic(sc2); + + /* Bugzilla 13113: As same as pre-invariant in destructor, + * change the behavior of post-invariant call. + */ + e = new ThisExp(Loc()); + e->type = vthis->type; + e = new DotVarExp(Loc(), e, inv, 0); + e->type = inv->type; e = new CallExp(Loc(), e); - e = e->semantic(sc2); + e->type = Type::tvoid; } } else - { // Call invariant virtually + { + #if 1 + // Workaround for bugzilla 13394: For the correct mangling, + // run attribute inference on inv if needed. + if (ad->isStructDeclaration() && ad->inv) + ad->inv->functionSemantic(); + #endif + + // Call invariant virtually Expression *v = new ThisExp(Loc()); v->type = vthis->type; if (ad->isStructDeclaration()) - v = v->addressOf(sc); + v = v->addressOf(); e = new AssertExp(Loc(), v); } if (e) @@ -1289,7 +1582,8 @@ void FuncDeclaration::semantic3(Scope *sc) if (fensure || addPostInvariant()) { if ((fensure && global.params.useOut) || fpostinv) - { returnLabel = new LabelDsymbol(Id::returnLabel); + { + returnLabel = new LabelDsymbol(Id::returnLabel); } // scope of out contract (need for vresult->semantic) @@ -1305,12 +1599,14 @@ void FuncDeclaration::semantic3(Scope *sc) sc2 = sc2->push(sym); AggregateDeclaration *ad2 = isAggregateMember2(); + unsigned *fieldinit = NULL; /* If this is a class constructor */ if (ad2 && isCtorDeclaration()) { - sc2->fieldinit = (unsigned *)mem.malloc(sizeof(unsigned) * ad2->fields.dim); + fieldinit = (unsigned *)mem.malloc(sizeof(unsigned) * ad2->fields.dim); + sc2->fieldinit = fieldinit; sc2->fieldinit_dim = ad2->fields.dim; for (size_t i = 0; i < ad2->fields.dim; i++) { @@ -1320,7 +1616,7 @@ void FuncDeclaration::semantic3(Scope *sc) } } - if (!inferRetType && f->retStyle() != RETstack) + if (!inferRetType && retStyle(f) != RETstack) nrvo_can = 0; fbody = fbody->semantic(sc2); @@ -1336,26 +1632,18 @@ void FuncDeclaration::semantic3(Scope *sc) //type = type->semantic(loc, sc); // Removed with 6902 } } - if (returns && f->next->ty != Tvoid) + if (f->next->ty != Tvoid) { - for (size_t i = 0; i < returns->dim; i++) - { - Expression *exp = (*returns)[i]->exp; - if (!nrvo_can && !f->isref && exp->isLvalue()) - exp = callCpCtor(sc2, exp); - if (!tintro && !f->next->immutableOf()->equals(exp->type->immutableOf())) - { - exp = exp->castTo(sc2, f->next); - exp = exp->optimize(WANTvalue); - } - //printf("[%d] %s %s\n", i, exp->type->toChars(), exp->toChars()); - (*returns)[i]->exp = exp; - } + NrvoWalker nw; + nw.fd = this; + nw.sc = sc2; + fbody->accept(&nw); } assert(type == f); if (isStaticCtorDeclaration()) - { /* It's a static constructor. Ensure that all + { + /* It's a static constructor. Ensure that all * ctor consts were initialized. */ @@ -1368,8 +1656,8 @@ void FuncDeclaration::semantic3(Scope *sc) else { for (size_t i = 0; i < pd->members->dim; i++) - { Dsymbol *s = (*pd->members)[i]; - + { + Dsymbol *s = (*pd->members)[i]; s->checkCtorConstInit(); } } @@ -1377,7 +1665,7 @@ void FuncDeclaration::semantic3(Scope *sc) if (fbody->isErrorStatement()) ; - else if (isCtorDeclaration() && ad2) + else if (ad2 && isCtorDeclaration()) { ClassDeclaration *cd = ad2->isClassDeclaration(); @@ -1398,9 +1686,9 @@ void FuncDeclaration::semantic3(Scope *sc) if (v->isCtorinit() && !v->type->isMutable() && cd) error("missing initializer for %s field %s", MODtoChars(v->type->mod), v->toChars()); else if (v->storage_class & STCnodefaultctor) - error("field %s must be initialized in constructor", v->toChars()); + ::error(loc, "field %s must be initialized in constructor", v->toChars()); else if (v->type->needsNested()) - error("field %s must be initialized in constructor, because it is nested struct", v->toChars()); + ::error(loc, "field %s must be initialized in constructor, because it is nested struct", v->toChars()); } else { @@ -1413,7 +1701,6 @@ void FuncDeclaration::semantic3(Scope *sc) } } } - mem.free(sc2->fieldinit); sc2->fieldinit = NULL; sc2->fieldinit_dim = 0; @@ -1440,12 +1727,15 @@ void FuncDeclaration::semantic3(Scope *sc) } // Check for errors related to 'nothrow'. - int nothrowErrors = global.errors; - int blockexit = fbody->blockExit(f->isnothrow); + unsigned int nothrowErrors = global.errors; + int blockexit = fbody->blockExit(this, f->isnothrow); if (f->isnothrow && (global.errors != nothrowErrors) ) ::error(loc, "%s '%s' is nothrow yet may throw", kind(), toPrettyChars()); if (flags & FUNCFLAGnothrowInprocess) + { + if (type == f) f = (TypeFunction *)f->copy(); f->isnothrow = !(blockexit & BEthrow); + } //printf("callSuper = x%x\n", sc2->callSuper); /* Append: @@ -1463,24 +1753,36 @@ void FuncDeclaration::semantic3(Scope *sc) } } else if (fes) - { // For foreach(){} body, append a return 0; - Expression *e = new IntegerExp(0); - Statement *s = new ReturnStatement(Loc(), e); - fbody = new CompoundStatement(Loc(), fbody, s); + { + // Check for errors related to 'nothrow'. + int nothrowErrors = global.errors; + int blockexit = fbody->blockExit(this, f->isnothrow); + if (f->isnothrow && (global.errors != nothrowErrors) ) + ::error(loc, "%s '%s' is nothrow yet may throw", kind(), toPrettyChars()); + if (flags & FUNCFLAGnothrowInprocess) + { + if (type == f) f = (TypeFunction *)f->copy(); + f->isnothrow = !(blockexit & BEthrow); + } + //printf("callSuper = x%x\n", sc2->callSuper); + + // For foreach(){} body, append a return 0; + if (blockexit & BEfallthru) + { + Expression *e = new IntegerExp(0); + Statement *s = new ReturnStatement(Loc(), e); + fbody = new CompoundStatement(Loc(), fbody, s); + } assert(!returnLabel); } else if (!hasReturnExp && type->nextOf()->ty != Tvoid) error("has no return statement, but is expected to return a value of type %s", type->nextOf()->toChars()); - else if (hasReturnExp & 8) // if inline asm - { - flags &= ~FUNCFLAGnothrowInprocess; - } else { // Check for errors related to 'nothrow'. - int nothrowErrors = global.errors; - int blockexit = fbody->blockExit(f->isnothrow); - if (f->isnothrow && (global.errors != nothrowErrors) ) + unsigned int nothrowErrors = global.errors; + int blockexit = fbody->blockExit(this, f->isnothrow); + if (f->isnothrow && (global.errors != nothrowErrors)) ::error(loc, "%s '%s' is nothrow yet may throw", kind(), toPrettyChars()); if (flags & FUNCFLAGnothrowInprocess) { @@ -1488,33 +1790,34 @@ void FuncDeclaration::semantic3(Scope *sc) f->isnothrow = !(blockexit & BEthrow); } - int offend = blockexit & BEfallthru; - if (type->nextOf()->ty != Tvoid) + const bool inlineAsm = hasReturnExp & 8; + if ((blockexit & BEfallthru) && f->next->ty != Tvoid && !inlineAsm) { - if (offend) - { Expression *e; - error("no return exp; or assert(0); at end of function"); - if (global.params.useAssert && - !global.params.useInline) - { /* Add an assert(0, msg); where the missing return - * should be. - */ - e = new AssertExp( - endloc, - new IntegerExp(0), - new StringExp(loc, (char *)"missing return expression") - ); - } - else - e = new HaltExp(endloc); - e = new CommaExp(Loc(), e, type->nextOf()->defaultInit()); - e = e->semantic(sc2); - Statement *s = new ExpStatement(Loc(), e); - fbody = new CompoundStatement(Loc(), fbody, s); + Expression *e; + error("no return exp; or assert(0); at end of function"); + if (global.params.useAssert && + !global.params.useInline) + { + /* Add an assert(0, msg); where the missing return + * should be. + */ + e = new AssertExp( + endloc, + new IntegerExp(0), + new StringExp(loc, (char *)"missing return expression") + ); } + else + e = new HaltExp(endloc); + e = new CommaExp(Loc(), e, type->nextOf()->defaultInit()); + e = e->semantic(sc2); + Statement *s = new ExpStatement(Loc(), e); + fbody = new CompoundStatement(Loc(), fbody, s); } } + if (fieldinit) + mem.free(fieldinit); sc2 = sc2->pop(); } @@ -1525,7 +1828,8 @@ void FuncDeclaration::semantic3(Scope *sc) * [out] postconditions. */ if (freq) - { /* frequire is composed of the [in] contracts + { + /* frequire is composed of the [in] contracts */ ScopeDsymbol *sym = new ScopeDsymbol(); sym->parent = sc2->scopesym; @@ -1545,7 +1849,8 @@ void FuncDeclaration::semantic3(Scope *sc) } if (fens) - { /* fensure is composed of the [out] contracts + { + /* fensure is composed of the [out] contracts */ if (type->nextOf()->ty == Tvoid && outId) { @@ -1574,12 +1879,16 @@ void FuncDeclaration::semantic3(Scope *sc) fens = NULL; } + if (fbody && fbody->isErrorStatement()) + ; + else { Statements *a = new Statements(); // Merge in initialization of 'out' parameters if (parameters) - { for (size_t i = 0; i < parameters->dim; i++) + { + for (size_t i = 0; i < parameters->dim; i++) { VarDeclaration *v = (*parameters)[i]; if (v->storage_class & STCout) @@ -1595,7 +1904,8 @@ void FuncDeclaration::semantic3(Scope *sc) } if (argptr) - { // Initialize _argptr + { + // Initialize _argptr #ifdef IN_GCC // Handled in FuncDeclaration::toObjFile v_argptr = argptr; @@ -1603,7 +1913,8 @@ void FuncDeclaration::semantic3(Scope *sc) #else Type *t = argptr->type; if (global.params.is64bit && !global.params.isWindows) - { // Initialize _argptr to point to v_argsave + { + // Initialize _argptr to point to v_argsave Expression *e1 = new VarExp(Loc(), argptr); Expression *e = new SymOffExp(Loc(), v_argsave, 6*8 + 8*16); e->type = argptr->type; @@ -1612,7 +1923,8 @@ void FuncDeclaration::semantic3(Scope *sc) a->push(new ExpStatement(Loc(), e)); } else - { // Initialize _argptr to point past non-variadic arg + { + // Initialize _argptr to point past non-variadic arg VarDeclaration *p; unsigned offset = 0; Expression *e; @@ -1641,8 +1953,9 @@ void FuncDeclaration::semantic3(Scope *sc) else p = v_arguments; // last parameter is _arguments[] if (global.params.is64bit && global.params.isWindows) - { offset += Target::ptrsize; - if (p->storage_class & STClazy || p->type->size() > Target::ptrsize) + { + offset += Target::ptrsize; + if (p->storage_class & STClazy || p->type->size() > Target::ptrsize) { /* Necessary to offset the extra level of indirection the Win64 * ABI demands @@ -1657,8 +1970,10 @@ void FuncDeclaration::semantic3(Scope *sc) } } else if (p->storage_class & STClazy) + { // If the last parameter is lazy, it's the size of a delegate offset += Target::ptrsize * 2; + } else offset += p->type->size(); offset = (offset + Target::ptrsize - 1) & ~(Target::ptrsize - 1); // assume stack aligns on pointer size @@ -1722,7 +2037,8 @@ void FuncDeclaration::semantic3(Scope *sc) // Create: return vresult; Expression *e = new VarExp(Loc(), vresult); if (tintro) - { e = e->implicitCastTo(sc, tintro->nextOf()); + { + e = e->implicitCastTo(sc, tintro->nextOf()); e = e->semantic(sc); } ReturnStatement *s = new ReturnStatement(Loc(), e); @@ -1730,16 +2046,18 @@ void FuncDeclaration::semantic3(Scope *sc) } } if (isMain() && type->nextOf()->ty == Tvoid) - { // Add a return 0; statement + { + // Add a return 0; statement Statement *s = new ReturnStatement(Loc(), new IntegerExp(0)); a->push(s); } - fbody = new CompoundStatement(Loc(), a); + Statement *sbody = new CompoundStatement(Loc(), a); /* Append destructor calls for parameters as finally blocks. */ if (parameters) - { for (size_t i = 0; i < parameters->dim; i++) + { + for (size_t i = 0; i < parameters->dim; i++) { VarDeclaration *v = (*parameters)[i]; @@ -1751,19 +2069,20 @@ void FuncDeclaration::semantic3(Scope *sc) Expression *e = v->edtor; if (e) - { Statement *s = new ExpStatement(Loc(), e); + { + Statement *s = new ExpStatement(Loc(), e); s = s->semantic(sc2); - int nothrowErrors = global.errors; + unsigned int nothrowErrors = global.errors; bool isnothrow = f->isnothrow & !(flags & FUNCFLAGnothrowInprocess); - int blockexit = s->blockExit(isnothrow); + int blockexit = s->blockExit(this, isnothrow); if (f->isnothrow && (global.errors != nothrowErrors) ) ::error(loc, "%s '%s' is nothrow yet may throw", kind(), toPrettyChars()); if (flags & FUNCFLAGnothrowInprocess && blockexit & BEthrow) f->isnothrow = false; - if (fbody->blockExit(f->isnothrow) == BEfallthru) - fbody = new CompoundStatement(Loc(), fbody, s); + if (sbody->blockExit(this, f->isnothrow) == BEfallthru) + sbody = new CompoundStatement(Loc(), sbody, s); else - fbody = new TryFinallyStatement(Loc(), fbody, s); + sbody = new TryFinallyStatement(Loc(), sbody, s); } } } @@ -1771,7 +2090,8 @@ void FuncDeclaration::semantic3(Scope *sc) flags &= ~FUNCFLAGnothrowInprocess; if (isSynchronized()) - { /* Wrap the entire function body in a synchronized statement + { + /* Wrap the entire function body in a synchronized statement */ ClassDeclaration *cd = isThis() ? isThis()->isClassDeclaration() : parent->isClassDeclaration(); @@ -1779,7 +2099,7 @@ void FuncDeclaration::semantic3(Scope *sc) { if (!global.params.is64bit && global.params.isWindows && - !isStatic() && !fbody->usesEH() && !global.params.trace) + !isStatic() && !sbody->usesEH() && !global.params.trace) { /* The back end uses the "jmonitor" hack for syncing; * no need to do the sync at this level. @@ -1789,16 +2109,18 @@ void FuncDeclaration::semantic3(Scope *sc) { Expression *vsync; if (isStatic()) - { // The monitor is in the ClassInfo + { + // The monitor is in the ClassInfo vsync = new DotIdExp(loc, new DsymbolExp(loc, cd), Id::classinfo); } else - { // 'this' is the monitor + { + // 'this' is the monitor vsync = new VarExp(loc, vthis); } - fbody = new PeelStatement(fbody); // don't redo semantic() - fbody = new SynchronizedStatement(loc, vsync, fbody); - fbody = fbody->semantic(sc2); + sbody = new PeelStatement(sbody); // don't redo semantic() + sbody = new SynchronizedStatement(loc, vsync, sbody); + sbody = sbody->semantic(sc2); } } else @@ -1806,6 +2128,11 @@ void FuncDeclaration::semantic3(Scope *sc) error("synchronized function %s must be a member of a class", toChars()); } } + + // If declaration has no body, don't set sbody to prevent incorrect codegen. + InterfaceDeclaration *id = parent->isInterfaceDeclaration(); + if (fbody || id && (fdensure || fdrequire) && isVirtual()) + fbody = sbody; } // Fix up forward-referenced gotos @@ -1821,6 +2148,14 @@ void FuncDeclaration::semantic3(Scope *sc) sc2->pop(); } + if (needsClosure()) + { + if (setGC()) + error("@nogc function allocates a closure with the GC"); + else + printGCUsage(loc, "using closure causes GC allocation"); + } + /* If function survived being marked as impure, then it is pure */ if (flags & FUNCFLAGpurityInprocess) @@ -1837,6 +2172,13 @@ void FuncDeclaration::semantic3(Scope *sc) f->trust = TRUSTsafe; } + if (flags & FUNCFLAGnogcInprocess) + { + flags &= ~FUNCFLAGnogcInprocess; + if (type == f) f = (TypeFunction *)f->copy(); + f->isnogc = true; + } + // reset deco to apply inference result to mangled name if (f != type) f->deco = NULL; @@ -1851,12 +2193,12 @@ void FuncDeclaration::semantic3(Scope *sc) sc = sc->pop(); } - /* If this function had speculatively instantiated, error reproduction will be + /* If this function had instantiated with gagging, error reproduction will be * done by TemplateInstance::semantic. * Otherwise, error gagging should be temporarily ungagged by functionSemantic3. */ semanticRun = PASSsemantic3done; - semantic3Errors = (global.errors != nerrors); + semantic3Errors = (global.errors != nerrors) || (fbody && fbody->isErrorStatement()); if (type->ty == Terror) errors = true; //printf("-FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", parent->toChars(), toChars(), sc, loc.toChars()); @@ -1878,7 +2220,7 @@ bool FuncDeclaration::functionSemantic() semantic(scope); global.gag = oldgag; if (spec && global.errors != olderrs) - spec->errors = global.errors - olderrs != 0; + spec->errors = (global.errors - olderrs != 0); if (olderrs != global.errors) // if errors compiling this function return false; } @@ -1889,7 +2231,7 @@ bool FuncDeclaration::functionSemantic() TemplateInstance *ti; if (isInstantiated() && !isVirtualMethod() && - !(ti = parent->isTemplateInstance(), ti && !ti->isTemplateMixin() && ti->name != ident)) + !(ti = parent->isTemplateInstance(), ti && !ti->isTemplateMixin() && ti->tempdecl->ident != ident)) { AggregateDeclaration *ad = isMember2(); if (ad && ad->sizeok != SIZEOKdone) @@ -1912,8 +2254,8 @@ bool FuncDeclaration::functionSemantic3() if (semanticRun < PASSsemantic3 && scope) { /* Forward reference - we need to run semantic3 on this function. - * If errors are gagged, and it's not part of a speculative - * template instance, we need to temporarily ungag errors. + * If errors are gagged, and it's not part of a template instance, + * we need to temporarily ungag errors. */ TemplateInstance *spec = isSpeculative(); unsigned olderrs = global.errors; @@ -1926,7 +2268,7 @@ bool FuncDeclaration::functionSemantic3() // If it is a speculatively-instantiated template, and errors occur, // we need to mark the template as having errors. if (spec && global.errors != olderrs) - spec->errors = global.errors - olderrs != 0; + spec->errors = (global.errors - olderrs != 0); if (olderrs != global.errors) // if errors compiling this function return false; } @@ -1948,7 +2290,7 @@ void FuncDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) bodyToCBuffer(buf, hgs); hgs->autoMember--; } - else if (hgs->tpltMember == 0 && global.params.useInline == 0) + else if (hgs->tpltMember == 0 && !global.params.useInline) buf->writestring(";"); else bodyToCBuffer(buf, hgs); @@ -1960,16 +2302,15 @@ void FuncDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) VarDeclaration *FuncDeclaration::declareThis(Scope *sc, AggregateDeclaration *ad) { - if (ad) - { VarDeclaration *v; - + if (ad && !isFuncLiteralDeclaration()) + { + VarDeclaration *v; { - assert(ad->handle); - Type *thandle = ad->handle; + Type *thandle = ad->handleType(); + assert(thandle); thandle = thandle->addMod(type->mod); thandle = thandle->addStorageClass(storage_class); v = new ThisDeclaration(loc, thandle); - //v = new ThisDeclaration(loc, isCtorDeclaration() ? ad->handle : thandle); v->storage_class |= STCparameter; if (thandle->ty == Tstruct) v->storage_class |= STCref; @@ -2048,7 +2389,7 @@ void FuncDeclaration::bodyToCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring("in"); buf->writenl(); - frequire->toCBuffer(buf, hgs); + ::toCBuffer(frequire, buf, hgs); } // out{} @@ -2057,12 +2398,12 @@ void FuncDeclaration::bodyToCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writestring("out"); if (outId) { - buf->writebyte('('); + buf->writeByte('('); buf->writestring(outId->toChars()); - buf->writebyte(')'); + buf->writeByte(')'); } buf->writenl(); - fensure->toCBuffer(buf, hgs); + ::toCBuffer(fensure, buf, hgs); } if (frequire || fensure) @@ -2071,12 +2412,12 @@ void FuncDeclaration::bodyToCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writenl(); } - buf->writebyte('{'); + buf->writeByte('{'); buf->writenl(); buf->level++; - fbody->toCBuffer(buf, hgs); + ::toCBuffer(fbody, buf, hgs); buf->level--; - buf->writebyte('}'); + buf->writeByte('}'); buf->writenl(); hgs->tpltMember = savetlpt; @@ -2429,7 +2770,8 @@ bool FuncDeclaration::overloadInsert(Dsymbol *s) { printf("type = %s\n", type->toChars()); printf("fd->type = %s\n", fd->type->toChars()); } - if (type && fd->type && // can be NULL for overloaded constructors + // fd->type can be NULL for overloaded constructors + if (type && fd->type && fd->type->covariant(type) && fd->type->mod == type->mod && !isFuncAliasDeclaration()) @@ -2467,7 +2809,21 @@ int overloadApply(Dsymbol *fstart, void *param, int (*fp)(void *, Dsymbol *)) Dsymbol *next; for (d = fstart; d; d = next) { - if (FuncAliasDeclaration *fa = d->isFuncAliasDeclaration()) + if (OverDeclaration *od = d->isOverDeclaration()) + { + if (od->hasOverloads) + { + if (int r = overloadApply(od->aliassym, param, fp)) + return r; + } + else + { + if (int r = (*fp)(param, od->aliassym)) + return r; + } + next = od->overnext; + } + else if (FuncAliasDeclaration *fa = d->isFuncAliasDeclaration()) { if (fa->hasOverloads) { @@ -2621,6 +2977,23 @@ static void MODMatchToBuffer(OutBuffer *buf, unsigned char lhsMod, unsigned char buf->writestring("mutable "); } +/******************************************** + * Returns true if function was declared + * directly or indirectly in a unittest block + */ +bool FuncDeclaration::inUnittest() +{ + Dsymbol *f = this; + do + { + if (f->isUnitTestDeclaration()) + return true; + f = f->toParent(); + } while (f); + + return false; +} + /******************************************** * find function template root in overload list */ @@ -2664,7 +3037,6 @@ MATCH FuncDeclaration::leastAsSpecialized(FuncDeclaration *g) TypeFunction *tf = (TypeFunction *)type; TypeFunction *tg = (TypeFunction *)g->type; size_t nfparams = Parameter::dim(tf->parameters); - size_t ngparams = Parameter::dim(tg->parameters); /* If both functions have a 'this' pointer, and the mods are not * the same and g's is not const, then this is less specialized. @@ -2722,6 +3094,100 @@ MATCH FuncDeclaration::leastAsSpecialized(FuncDeclaration *g) return MATCHnomatch; } +/// Walk through candidate template overloads and print them in the diagnostics. +struct TemplateCandidateWalker +{ + Loc loc; + int numToDisplay; // max num of overloads to print (-v overrides this). + + /// Count template overloads. + struct CountWalker + { + int numOverloads; + + static int fp(void *param, Dsymbol *s) + { + CountWalker *p = (CountWalker *)param; + ++(p->numOverloads); + return 0; + } + }; + + static int fp(void *param, Dsymbol *s) + { + TemplateDeclaration *t = s->isTemplateDeclaration(); + if (!t) return 0; + + TemplateCandidateWalker *p = (TemplateCandidateWalker *)param; + + ::errorSupplemental(t->loc, "%s", t->toPrettyChars()); + + if (!global.params.verbose && --(p->numToDisplay) == 0 && t->overnext) + { + // Too many overloads to sensibly display. + // Just show count of remaining overloads. + CountWalker cw; + cw.numOverloads = 0; + overloadApply(t->overnext, &cw, &CountWalker::fp); + + if (cw.numOverloads > 0) + ::errorSupplemental(p->loc, "... (%d more, -v to show) ...", cw.numOverloads); + + return 1; // stop iterating + } + + return 0; + } +}; + +/// Walk through candidate template overloads and print them in the diagnostics. +struct FuncCandidateWalker +{ + Loc loc; + int numToDisplay; // max num of overloads to print (-v overrides this). + + /// Count function overloads. + struct CountWalker + { + int numOverloads; + + static int fp(void *param, Dsymbol *s) + { + CountWalker *p = (CountWalker *)param; + + if (s->isFuncDeclaration()) + ++(p->numOverloads); + + return 0; + } + }; + + static int fp(void *param, Dsymbol *s) + { + FuncDeclaration *f = s->isFuncDeclaration(); + if (!f) return 0; + + FuncCandidateWalker *p = (FuncCandidateWalker *)param; + + ::errorSupplemental(f->loc, "%s%s", f->toPrettyChars(), + Parameter::argsTypesToChars(((TypeFunction *)f->type)->parameters, ((TypeFunction *)f->type)->varargs)); + + if (!global.params.verbose && --(p->numToDisplay) == 0 && f->overnext) + { + CountWalker cw; + cw.numOverloads = 0; + overloadApply(f->overnext, &cw, &CountWalker::fp); + + if (cw.numOverloads > 0) + ::errorSupplemental(p->loc, "... (%d more, -v to show) ...", cw.numOverloads); + + return 1; // stop iterating + } + + return 0; + } +}; + /******************************************* * Given a symbol that could be either a FuncDeclaration or * a function template, resolve it to a function symbol. @@ -2783,7 +3249,6 @@ FuncDeclaration *resolveFuncCall(Loc loc, Scope *sc, Dsymbol *s, } } -Lerror: /* Failed to find a best match. * Do nothing or print error. */ @@ -2822,51 +3287,60 @@ FuncDeclaration *resolveFuncCall(Loc loc, Scope *sc, Dsymbol *s, if (tthis) tthis->modToBuffer(&fargsBuf); + const int numOverloadsDisplay = 5; // sensible number to display + if (!m.lastf && !(flags & 1)) // no match { - if (td && !fd) // all of overloads are template + if (td && !fd) // all of overloads are templates { ::error(loc, "%s %s.%s cannot deduce function from argument types !(%s)%s, candidates are:", td->kind(), td->parent->toPrettyChars(), td->ident->toChars(), - tiargsBuf.toChars(), fargsBuf.toChars()); + tiargsBuf.peekString(), fargsBuf.peekString()); - // Display candidate template functions - int numToDisplay = 5; // sensible number to display - for (TemplateDeclaration *tdx = td; tdx; tdx = tdx->overnext) - { - ::errorSupplemental(tdx->loc, "%s", tdx->toPrettyChars()); - if (!global.params.verbose && --numToDisplay == 0 && tdx->overnext) - { - // Too many overloads to sensibly display. - // Just show count of remaining overloads. - int remaining = 0; - for (TemplateDeclaration *tdy = tdx->overnext; tdy; tdy = tdy->overnext) - ++remaining; - if (remaining > 0) - ::errorSupplemental(loc, "... (%d more, -v to show) ...", remaining); - break; - } - } + // Display candidate templates (even if there are no multiple overloads) + TemplateCandidateWalker tcw; + tcw.loc = loc; + tcw.numToDisplay = numOverloadsDisplay; + overloadApply(td, &tcw, &TemplateCandidateWalker::fp); } else { assert(fd); + + bool hasOverloads = fd->overnext != NULL; TypeFunction *tf = (TypeFunction *)fd->type; if (tthis && !MODimplicitConv(tthis->mod, tf->mod)) // modifier mismatch { OutBuffer thisBuf, funcBuf; MODMatchToBuffer(&thisBuf, tthis->mod, tf->mod); MODMatchToBuffer(&funcBuf, tf->mod, tthis->mod); - ::error(loc, "%smethod %s is not callable using a %sobject", - funcBuf.toChars(), fd->toPrettyChars(), thisBuf.toChars()); + if (hasOverloads) + ::error(loc, "None of the overloads of '%s' are callable using a %sobject, candidates are:", + fd->ident->toChars(), thisBuf.peekString()); + else + ::error(loc, "%smethod %s is not callable using a %sobject", + funcBuf.peekString(), fd->toPrettyChars(), thisBuf.peekString()); } else { //printf("tf = %s, args = %s\n", tf->deco, (*fargs)[0]->type->deco); - fd->error(loc, "%s%s is not callable using argument types %s", - Parameter::argsTypesToChars(tf->parameters, tf->varargs), - tf->modToChars(), - fargsBuf.toChars()); + if (hasOverloads) + ::error(loc, "None of the overloads of '%s' are callable using argument types %s, candidates are:", + fd->ident->toChars(), fargsBuf.peekString()); + else + fd->error(loc, "%s%s is not callable using argument types %s", + Parameter::argsTypesToChars(tf->parameters, tf->varargs), + tf->modToChars(), + fargsBuf.peekString()); + } + + // Display candidate functions + if (hasOverloads) + { + FuncCandidateWalker fcw; + fcw.loc = loc; + fcw.numToDisplay = numOverloadsDisplay; + overloadApply(fd, &fcw, &FuncCandidateWalker::fp); } } } @@ -2877,11 +3351,11 @@ FuncDeclaration *resolveFuncCall(Loc loc, Scope *sc, Dsymbol *s, const char *lastprms = Parameter::argsTypesToChars(tf1->parameters, tf1->varargs); const char *nextprms = Parameter::argsTypesToChars(tf2->parameters, tf2->varargs); ::error(loc, "%s.%s called with argument types %s matches both:\n" - "\t%s(%d): %s%s\nand:\n\t%s(%d): %s%s", + "%s: %s%s\nand:\n%s: %s%s", s->parent->toPrettyChars(), s->ident->toChars(), - fargsBuf.toChars(), - m.lastf->loc.filename, m.lastf->loc.linnum, m.lastf->toPrettyChars(), lastprms, - m.nextf->loc.filename, m.nextf->loc.linnum, m.nextf->toPrettyChars(), nextprms); + fargsBuf.peekString(), + m.lastf->loc.toChars(), m.lastf->toPrettyChars(), lastprms, + m.nextf->loc.toChars(), m.nextf->toPrettyChars(), nextprms); } return NULL; } @@ -2912,11 +3386,10 @@ LabelDsymbol *FuncDeclaration::searchLabel(Identifier *ident) */ AggregateDeclaration *FuncDeclaration::isThis() -{ AggregateDeclaration *ad; - +{ //printf("+FuncDeclaration::isThis() '%s'\n", toChars()); - ad = NULL; - if ((storage_class & STCstatic) == 0) + AggregateDeclaration *ad = NULL; + if ((storage_class & STCstatic) == 0 && !isFuncLiteralDeclaration()) { ad = isMember2(); } @@ -2925,13 +3398,12 @@ AggregateDeclaration *FuncDeclaration::isThis() } AggregateDeclaration *FuncDeclaration::isMember2() -{ AggregateDeclaration *ad; - +{ //printf("+FuncDeclaration::isMember2() '%s'\n", toChars()); - ad = NULL; + AggregateDeclaration *ad = NULL; for (Dsymbol *s = this; s; s = s->parent) { -//printf("\ts = '%s', parent = '%s', kind = %s\n", s->toChars(), s->parent->toChars(), s->parent->kind()); + //printf("\ts = '%s', parent = '%s', kind = %s\n", s->toChars(), s->parent->toChars(), s->parent->kind()); ad = s->isMember(); if (ad) { @@ -2957,7 +3429,8 @@ AggregateDeclaration *FuncDeclaration::isMember2() */ int FuncDeclaration::getLevel(Loc loc, Scope *sc, FuncDeclaration *fd) -{ int level; +{ + int level; Dsymbol *s; Dsymbol *fdparent; @@ -3005,12 +3478,8 @@ int FuncDeclaration::getLevel(Loc loc, Scope *sc, FuncDeclaration *fd) return level; Lerr: - Dsymbol *p = toParent2(); - while (p->toParent2()->isFuncDeclaration()) - p = p->toParent2(); - // Don't give error if in template constraint - if (!(sc->flags & SCOPEstaticif) && !p->parent->isTemplateDeclaration()) + if (!(sc->flags & SCOPEstaticif)) { const char *xstatic = isStatic() ? "static " : ""; // better diagnostics for static functions @@ -3046,22 +3515,20 @@ void FuncDeclaration::appendState(Statement *s) } } -const char *FuncDeclaration::toPrettyChars() +const char *FuncDeclaration::toPrettyChars(bool QualifyTypes) { if (isMain()) return "D main"; else - return Dsymbol::toPrettyChars(); + return Dsymbol::toPrettyChars(QualifyTypes); } /** for diagnostics, e.g. 'int foo(int x, int y) pure' */ const char *FuncDeclaration::toFullSignature() { OutBuffer buf; - HdrGenState hgs; - functionToCBuffer2((TypeFunction *)type, &buf, &hgs, 0, toChars()); - buf.writeByte(0); - return buf.extractData(); + functionToBufferWithIdent((TypeFunction *)type, &buf, toChars()); + return buf.extractString(); } bool FuncDeclaration::isMain() @@ -3214,6 +3681,11 @@ PURE FuncDeclaration::isPureBypassingInference() return isPure(); } +bool FuncDeclaration::isPureBypassingInferenceX() +{ + return !(flags & FUNCFLAGpurityInprocess) && isPure() != PUREimpure; +} + /************************************** * The function is doing something impure, * so mark it as impure. @@ -3240,10 +3712,7 @@ bool FuncDeclaration::isSafe() bool FuncDeclaration::isSafeBypassingInference() { - if (flags & FUNCFLAGsafetyInprocess) - return false; - else - return isSafe(); + return !(flags & FUNCFLAGsafetyInprocess) && isSafe(); } bool FuncDeclaration::isTrusted() @@ -3271,6 +3740,37 @@ bool FuncDeclaration::setUnsafe() return false; } +bool FuncDeclaration::isNogc() +{ + assert(type->ty == Tfunction); + if (flags & FUNCFLAGnogcInprocess) + setGC(); + return ((TypeFunction *)type)->isnogc; +} + +bool FuncDeclaration::isNogcBypassingInference() +{ + return !(flags & FUNCFLAGnogcInprocess) && isNogc(); +} + +/************************************** + * The function is doing something that may allocate with the GC, + * so mark it as not nogc (not no-how). + * Returns: + * true if function is marked as @nogc, meaning a user error occurred + */ +bool FuncDeclaration::setGC() +{ + if (flags & FUNCFLAGnogcInprocess) + { + flags &= ~FUNCFLAGnogcInprocess; + ((TypeFunction *)type)->isnogc = false; + } + else if (isNogc()) + return true; + return false; +} + /************************************** * Returns an indirect type one step from t. */ @@ -3290,32 +3790,35 @@ Type *getIndirection(Type *t) } /************************************** - * Traverse this and t, and then check the indirections convertibility. + * Returns true if memory reachable through a reference B to a value of type tb, + * which has been constructed with a reference A to a value of type ta + * available, can alias memory reachable from A based on the types involved + * (either directly or via any number of indirections). + * + * Note that this relation is not symmetric in the two arguments. For example, + * a const(int) reference can point to a pre-existing int, but not the other + * way round. */ - -int traverseIndirections(Type *ta, Type *tb, void *p = NULL, bool a2b = true) +bool traverseIndirections(Type *ta, Type *tb, void *p = NULL, bool reversePass = false) { - if (a2b) // check ta appears in tb + Type *source = ta; + Type *target = tb; + if (reversePass) { - //printf("\ttraverse(1) %s appears in %s\n", ta->toChars(), tb->toChars()); - if (ta->constConv(tb)) - return 1; - else if (ta->immutableOf()->equals(tb->immutableOf())) - return 0; - else if (tb->ty == Tvoid && MODimplicitConv(ta->mod, tb->mod)) - return 1; - } - else // check tb appears in ta - { - //printf("\ttraverse(2) %s appears in %s\n", tb->toChars(), ta->toChars()); - if (tb->constConv(ta)) - return 1; - else if (tb->immutableOf()->equals(ta->immutableOf())) - return 0; - else if (ta->ty == Tvoid && MODimplicitConv(tb->mod, ta->mod)) - return 1; + source = tb; + target = ta; } + if (source->constConv(target)) + return true; + else if (target->ty == Tvoid && MODimplicitConv(source->mod, target->mod)) + return true; + + // No direct match, so try breaking up one of the types (starting with tb). + Type *tbb = tb->toBasetype()->baseElemOf(); + if (tbb != tb) + return traverseIndirections(ta, tbb, p, reversePass); + // context date to detect circular look up struct Ctxt { @@ -3324,15 +3827,10 @@ int traverseIndirections(Type *ta, Type *tb, void *p = NULL, bool a2b = true) }; Ctxt *ctxt = (Ctxt *)p; - Type *tbb = tb->toBasetype(); - if (tbb != tb) - return traverseIndirections(ta, tbb, ctxt, a2b); - - tb = tb->baseElemOf(); if (tb->ty == Tclass || tb->ty == Tstruct) { for (Ctxt *c = ctxt; c; c = c->prev) - if (tb == c->type) return 0; + if (tb == c->type) return false; Ctxt c; c.prev = ctxt; c.type = tb; @@ -3342,31 +3840,28 @@ int traverseIndirections(Type *ta, Type *tb, void *p = NULL, bool a2b = true) { VarDeclaration *v = sym->fields[i]; Type *tprmi = v->type->addMod(tb->mod); - if (!(v->storage_class & STCref)) - tprmi = getIndirection(tprmi); - if (!tprmi) - continue; - //printf("\ttb = %s, tprmi = %s\n", tb->toChars(), tprmi->toChars()); - if (traverseIndirections(ta, tprmi, &c, a2b)) - return 1; + if (traverseIndirections(ta, tprmi, &c, reversePass)) + return true; } } else if (tb->ty == Tarray || tb->ty == Taarray || tb->ty == Tpointer) { Type *tind = tb->nextOf(); - if (traverseIndirections(ta, tind, ctxt, a2b)) - return 1; + if (traverseIndirections(ta, tind, ctxt, reversePass)) + return true; } else if (tb->hasPointers()) { // FIXME: function pointer/delegate types should be considered. - return 1; + return true; } - if (a2b) - return traverseIndirections(tb, ta, ctxt, false); - return 0; + // Still no match, so try breaking up ta if we have note done so yet. + if (!reversePass) + return traverseIndirections(tb, ta, ctxt, true); + + return false; } /******************************************** @@ -3420,7 +3915,7 @@ bool FuncDeclaration::parametersIntersect(Type *t) } if (AggregateDeclaration *ad = isCtorDeclaration() ? NULL : isThis()) { - Type *tthis = ad ? ad->getType()->addMod(tf->mod) : NULL; + Type *tthis = ad->getType()->addMod(tf->mod); //printf("\ttthis = %s\n", tthis->toChars()); if (traverseIndirections(tthis, t)) return false; @@ -3452,7 +3947,6 @@ bool FuncDeclaration::addPreInvariant() AggregateDeclaration *ad = isThis(); ClassDeclaration *cd = ad ? ad->isClassDeclaration() : NULL; return (ad && !(cd && cd->isCPPclass()) && - //ad->isClassDeclaration() && global.params.useInvariants && (protection == PROTprotected || protection == PROTpublic || protection == PROTexport) && !naked && @@ -3465,7 +3959,6 @@ bool FuncDeclaration::addPostInvariant() ClassDeclaration *cd = ad ? ad->isClassDeclaration() : NULL; return (ad && !(cd && cd->isCPPclass()) && ad->inv && - //ad->isClassDeclaration() && global.params.useInvariants && (protection == PROTprotected || protection == PROTpublic || protection == PROTexport) && !naked && @@ -3549,7 +4042,7 @@ void FuncDeclaration::checkNestedReference(Scope *sc, Loc loc) if (fdthis != this) { bool found = false; - for (int i = 0; i < siblingCallers.dim; ++i) + for (size_t i = 0; i < siblingCallers.dim; ++i) { if (siblingCallers[i] == fdthis) found = true; @@ -3624,7 +4117,7 @@ bool checkEscapingSiblings(FuncDeclaration *f, FuncDeclaration *outerFunc, void //printf("checkEscapingSiblings(f = %s, outerfunc = %s)\n", f->toChars(), outerFunc->toChars()); bool bAnyClosures = false; - for (int i = 0; i < f->siblingCallers.dim; ++i) + for (size_t i = 0; i < f->siblingCallers.dim; ++i) { FuncDeclaration *g = f->siblingCallers[i]; if (g->isThis() || g->tookAddressOf) @@ -3785,90 +4278,6 @@ bool FuncDeclaration::hasNestedFrameRefs() return false; } -/*********************************************** - * Returns true if this function is not defined in non-root module, nor - * obviously instantiated in non-root module. - * - * Note: ti->instantiatingModule does not stabilize until semantic analysis is completed, - * so don't call this function during semantic analysis to return precise result. - */ - -bool FuncDeclaration::needsCodegen() -{ - assert(semanticRun == PASSsemantic3done); - - for (FuncDeclaration *fd = this; fd; ) - { - if (!fd->isInstantiated() && fd->inNonRoot()) - return false; - if (fd->isNested()) - fd = fd->toParent2()->isFuncDeclaration(); - else - break; - } - - if (global.params.useUnitTests || - global.params.allInst || - /* The issue is that if the importee is compiled with a different -debug - * setting than the importer, the importer may believe it exists - * in the compiled importee when it does not, when the instantiation - * is behind a conditional debug declaration. - */ - global.params.debuglevel) // workaround for Bugzilla 11239 - { - return true; - } - - FuncDeclaration *fd = this; -Lagain: - TemplateInstance *ti = fd->isInstantiated(); - if (ti && ti->instantiatingModule && !ti->instantiatingModule->isRoot()) - { - Module *mi = ti->instantiatingModule; - - // If mi imports any root modules, we still need to generate the code. - for (size_t i = 0; i < Module::amodules.dim; ++i) - { - Module *m = Module::amodules[i]; - m->insearch = 0; - } - bool importsRoot = false; - for (size_t i = 0; i < Module::amodules.dim; ++i) - { - Module *m = Module::amodules[i]; - if (m->isRoot() && mi->imports(m)) - { - importsRoot = true; - break; - } - } - for (size_t i = 0; i < Module::amodules.dim; ++i) - { - Module *m = Module::amodules[i]; - m->insearch = 0; - } - if (!importsRoot) - { - //printf("instantiated by %s %s\n", ti->instantiatingModule->toChars(), ti->toChars()); - return false; - } - } - - if (fd->isNested()) - { - /* Bugzilla 11863: The enclosing function must have its code generated first. - * Therefore if parent is instantiated in non-root, this function also prevent - * code generation. - */ - fd = fd->toParent2()->isFuncDeclaration(); - if (fd) - goto Lagain; - } - //if (AggregateDeclaration *ad = fd->isMember2()) { ... } - - return true; -} - /********************************************* * Return the function's parameter list, and whether * it is variadic or not. @@ -3965,6 +4374,62 @@ bool FuncLiteralDeclaration::isVirtual() return false; } +bool FuncLiteralDeclaration::addPreInvariant() +{ + return false; +} + +bool FuncLiteralDeclaration::addPostInvariant() +{ + return false; +} + +/******************************* + * Modify all expression type of return statements to tret. + * + * On function literals, return type may be modified based on the context type + * after its semantic3 is done, in FuncExp::implicitCastTo. + * + * A function() dg = (){ return new B(); } // OK if is(B : A) == true + * + * If B to A conversion is convariant that requires offseet adjusting, + * all return statements should be adjusted to return expressions typed A. + */ +void FuncLiteralDeclaration::modifyReturns(Scope *sc, Type *tret) +{ + class RetWalker : public StatementRewriteWalker + { + public: + Scope *sc; + Type *tret; + FuncLiteralDeclaration *fld; + + void visit(ReturnStatement *s) + { + Expression *exp = s->exp; + if (exp && !exp->type->equals(tret)) + { + s->exp = exp->castTo(sc, tret); + } + } + }; + + if (semanticRun < PASSsemantic3done) + return; + + // Update the inferred function type to match the new return type. + // This is required so the code generator does not try to cast the + // modified returns back to the original type. + if (inferRetType && type->nextOf() != tret) + ((TypeFunction *)type)->next = tret; + + RetWalker w; + w.sc = sc; + w.tret = tret; + w.fld = this; + fbody->accept(&w); +} + const char *FuncLiteralDeclaration::kind() { // GCC requires the (char*) casts @@ -3973,6 +4438,12 @@ const char *FuncLiteralDeclaration::kind() void FuncLiteralDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { + if (type->ty == Terror) + { + buf->writestring("__error"); + return; + } + if (tok != TOKreserved) { buf->writestring(kind()); @@ -3982,14 +4453,13 @@ void FuncLiteralDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) TypeFunction *tf = (TypeFunction *)type; // Don't print tf->mod, tf->trust, and tf->linkage if (!inferRetType && tf->next) - tf->next->toCBuffer2(buf, hgs, 0); + toBufferShort(tf->next, buf, hgs); Parameter::argsToCBuffer(buf, hgs, tf->parameters, tf->varargs); CompoundStatement *cs = fbody->isCompoundStatement(); Statement *s1; - if (semanticRun >= PASSsemantic3done) + if (semanticRun >= PASSsemantic3done && cs) { - assert(cs); s1 = (*cs->statements)[cs->statements->dim - 1]; } else @@ -4008,15 +4478,15 @@ void FuncLiteralDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) } } -const char *FuncLiteralDeclaration::toPrettyChars() +const char *FuncLiteralDeclaration::toPrettyChars(bool QualifyTypes) { if (parent) { TemplateInstance *ti = parent->isTemplateInstance(); if (ti) - return ti->tempdecl->toPrettyChars(); + return ti->tempdecl->toPrettyChars(QualifyTypes); } - return Dsymbol::toPrettyChars(); + return Dsymbol::toPrettyChars(QualifyTypes); } /********************************* CtorDeclaration ****************************/ @@ -4044,9 +4514,6 @@ Dsymbol *CtorDeclaration::syntaxCopy(Dsymbol *s) void CtorDeclaration::semantic(Scope *sc) { //printf("CtorDeclaration::semantic() %s\n", toChars()); - TypeFunction *tf = (TypeFunction *)type; - assert(tf && tf->ty == Tfunction); - if (scope) { sc = scope; scope = NULL; @@ -4060,6 +4527,12 @@ void CtorDeclaration::semantic(Scope *sc) sc->pop(); + if (errors) + return; + + TypeFunction *tf = (TypeFunction *)type; + assert(tf && tf->ty == Tfunction); + Dsymbol *parent = toParent2(); AggregateDeclaration *ad = parent->isAggregateDeclaration(); @@ -4073,7 +4546,8 @@ void CtorDeclaration::semantic(Scope *sc) if (sd) { if (fbody || !(storage_class & STCdisable)) - { error("default constructor for structs only allowed with @disable and no body"); + { + error("default constructor for structs only allowed with @disable and no body"); storage_class |= STCdisable; fbody = NULL; } @@ -4273,22 +4747,22 @@ void DtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) /********************************* StaticCtorDeclaration ****************************/ -StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc) +StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc, StorageClass stc) : FuncDeclaration(loc, endloc, - Identifier::generateId("_staticCtor"), STCstatic, NULL) + Identifier::generateId("_staticCtor"), STCstatic | stc, NULL) { } -StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc, const char *name) +StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc, const char *name, StorageClass stc) : FuncDeclaration(loc, endloc, - Identifier::generateId(name), STCstatic, NULL) + Identifier::generateId(name), STCstatic | stc, NULL) { } Dsymbol *StaticCtorDeclaration::syntaxCopy(Dsymbol *s) { assert(!s); - StaticCtorDeclaration *scd = new StaticCtorDeclaration(loc, endloc); + StaticCtorDeclaration *scd = new StaticCtorDeclaration(loc, endloc, storage_class); return FuncDeclaration::syntaxCopy(scd); } @@ -4298,12 +4772,28 @@ void StaticCtorDeclaration::semantic(Scope *sc) //printf("StaticCtorDeclaration::semantic()\n"); if (scope) - { sc = scope; + { + sc = scope; scope = NULL; } + if (storage_class & STCshared && !isSharedStaticCtorDeclaration()) + { + ::error(loc, "to create a shared static constructor, use 'shared static this'"); + storage_class &= ~STCshared; + } + + if (storage_class & (STCimmutable | STCconst | STCshared | STCwild)) + { + OutBuffer buf; + StorageClassDeclaration::stcToCBuffer(&buf, storage_class & (STCimmutable | STCconst | STCshared | STCwild)); + ::error(loc, "static constructors cannot be %s", buf.peekString()); + } + + storage_class &= ~STC_TYPECTOR; // remove qualifiers + if (!type) - type = new TypeFunction(NULL, Type::tvoid, false, LINKd); + type = new TypeFunction(NULL, Type::tvoid, false, LINKd, storage_class); /* If the static ctor appears within a template instantiation, * it could get called multiple times by the module constructors @@ -4317,13 +4807,12 @@ void StaticCtorDeclaration::semantic(Scope *sc) * Note that this is not thread safe; should not have threads * during static construction. */ - Identifier *id = Lexer::idPool("__gate"); - VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, id, NULL); + VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, Id::gate, NULL); v->storage_class = STCtemp | (isSharedStaticCtorDeclaration() ? STCstatic : STCtls); Statements *sa = new Statements(); Statement *s = new ExpStatement(Loc(), v); sa->push(s); - Expression *e = new IdentifierExp(Loc(), id); + Expression *e = new IdentifierExp(Loc(), v->ident); e = new AddAssignExp(Loc(), e, new IntegerExp(1)); e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(1)); s = new IfStatement(Loc(), NULL, e, new ReturnStatement(Loc(), NULL), NULL); @@ -4384,15 +4873,15 @@ void StaticCtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) /********************************* SharedStaticCtorDeclaration ****************************/ -SharedStaticCtorDeclaration::SharedStaticCtorDeclaration(Loc loc, Loc endloc) - : StaticCtorDeclaration(loc, endloc, "_sharedStaticCtor") +SharedStaticCtorDeclaration::SharedStaticCtorDeclaration(Loc loc, Loc endloc, StorageClass stc) + : StaticCtorDeclaration(loc, endloc, "_sharedStaticCtor", stc) { } Dsymbol *SharedStaticCtorDeclaration::syntaxCopy(Dsymbol *s) { assert(!s); - SharedStaticCtorDeclaration *scd = new SharedStaticCtorDeclaration(loc, endloc); + SharedStaticCtorDeclaration *scd = new SharedStaticCtorDeclaration(loc, endloc, storage_class); return FuncDeclaration::syntaxCopy(scd); } @@ -4449,13 +4938,12 @@ void StaticDtorDeclaration::semantic(Scope *sc) * Note that this is not thread safe; should not have threads * during static destruction. */ - Identifier *id = Lexer::idPool("__gate"); - VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, id, NULL); + VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, Id::gate, NULL); v->storage_class = STCtemp | (isSharedStaticDtorDeclaration() ? STCstatic : STCtls); Statements *sa = new Statements(); Statement *s = new ExpStatement(Loc(), v); sa->push(s); - Expression *e = new IdentifierExp(Loc(), id); + Expression *e = new IdentifierExp(Loc(), v->ident); e = new AddAssignExp(Loc(), e, new IntegerExp(-1)); e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(0)); s = new IfStatement(Loc(), NULL, e, new ReturnStatement(Loc(), NULL), NULL); @@ -4622,7 +5110,7 @@ static Identifier *unitTestId(Loc loc) { OutBuffer buf; buf.printf("__unittestL%u_", loc.linnum); - return Lexer::uniqueId(buf.toChars()); + return Lexer::uniqueId(buf.peekString()); } UnitTestDeclaration::UnitTestDeclaration(Loc loc, Loc endloc, char *codedoc) @@ -4646,10 +5134,14 @@ void UnitTestDeclaration::semantic(Scope *sc) protection = sc->protection; if (scope) - { sc = scope; + { + sc = scope; scope = NULL; } + if (!isInstantiated() && inNonRoot()) + return; + if (global.params.useUnitTests) { if (!type) diff --git a/gcc/d/dfrontend/gpl.txt b/gcc/d/dfrontend/gpl.txt deleted file mode 100644 index 43cd72c3e..000000000 --- a/gcc/d/dfrontend/gpl.txt +++ /dev/null @@ -1,248 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 1, February 1989 - - Copyright (C) 1989 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The license agreements of most software companies try to keep users -at the mercy of those companies. By contrast, our General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. The -General Public License applies to the Free Software Foundation's -software and to any other program whose authors commit to using it. -You can use it for your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Specifically, the General Public License is designed to make -sure that you have the freedom to give away or sell copies of free -software, that you receive source code or can get it if you want it, -that you can change the software or use pieces of it in new free -programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of a such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must tell them their rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any program or other work which -contains a notice placed by the copyright holder saying it may be -distributed under the terms of this General Public License. The -"Program", below, refers to any such program or work, and a "work based -on the Program" means either the Program or any work containing the -Program or a portion of it, either verbatim or with modifications. Each -licensee is addressed as "you". - - 1. You may copy and distribute verbatim copies of the Program's source -code as you receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice and -disclaimer of warranty; keep intact all the notices that refer to this -General Public License and to the absence of any warranty; and give any -other recipients of the Program a copy of this General Public License -along with the Program. You may charge a fee for the physical act of -transferring a copy. - - 2. You may modify your copy or copies of the Program or any portion of -it, and copy and distribute such modifications under the terms of Paragraph -1 above, provided that you also do the following: - - a) cause the modified files to carry prominent notices stating that - you changed the files and the date of any change; and - - b) cause the whole of any work that you distribute or publish, that - in whole or in part contains the Program or any part thereof, either - with or without modifications, to be licensed at no charge to all - third parties under the terms of this General Public License (except - that you may choose to grant warranty protection to some or all - third parties, at your option). - - c) If the modified program normally reads commands interactively when - run, you must cause it, when started running for such interactive use - in the simplest and most usual way, to print or display an - announcement including an appropriate copyright notice and a notice - that there is no warranty (or else, saying that you provide a - warranty) and that users may redistribute the program under these - conditions, and telling the user how to view a copy of this General - Public License. - - d) You may charge a fee for the physical act of transferring a - copy, and you may at your option offer warranty protection in - exchange for a fee. - -Mere aggregation of another independent work with the Program (or its -derivative) on a volume of a storage or distribution medium does not bring -the other work under the scope of these terms. - - 3. You may copy and distribute the Program (or a portion or derivative of -it, under Paragraph 2) in object code or executable form under the terms of -Paragraphs 1 and 2 above provided that you also do one of the following: - - a) accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of - Paragraphs 1 and 2 above; or, - - b) accompany it with a written offer, valid for at least three - years, to give any third party free (except for a nominal charge - for the cost of distribution) a complete machine-readable copy of the - corresponding source code, to be distributed under the terms of - Paragraphs 1 and 2 above; or, - - c) accompany it with the information you received as to where the - corresponding source code may be obtained. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form alone.) - -Source code for a work means the preferred form of the work for making -modifications to it. For an executable file, complete source code means -all the source code for all modules it contains; but, as a special -exception, it need not include source code for modules which are standard -libraries that accompany the operating system on which the executable -file runs, or for standard header files or definitions files that -accompany that operating system. - - 4. You may not copy, modify, sublicense, distribute or transfer the -Program except as expressly provided under this General Public License. -Any attempt otherwise to copy, modify, sublicense, distribute or transfer -the Program is void, and will automatically terminate your rights to use -the Program under this License. However, parties who have received -copies, or rights to use copies, from you under this General Public -License will not have their licenses terminated so long as such parties -remain in full compliance. - - 5. By copying, distributing or modifying the Program (or any work based -on the Program) you indicate your acceptance of this license to do so, -and all its terms and conditions. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the original -licensor to copy, distribute or modify the Program subject to these -terms and conditions. You may not impose any further restrictions on the -recipients' exercise of the rights granted herein. - - 7. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of the license which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -the license, you may choose any version ever published by the Free Software -Foundation. - - 8. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - Appendix: How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to humanity, the best way to achieve this is to make it -free software which everyone can redistribute and change under these -terms. - - To do so, attach the following notices to the program. It is safest to -attach them to the start of each source file to most effectively convey -the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) 19yy - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 1, or (at your option) - any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) 19xx name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the -appropriate parts of the General Public License. Of course, the -commands you use may be called something other than `show w' and `show -c'; they could even be mouse-clicks or menu items--whatever suits your -program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - program `Gnomovision' (a program to direct compilers to make passes - at assemblers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/gcc/d/dfrontend/hdrgen.c b/gcc/d/dfrontend/hdrgen.c index 89167e47a..1ee1f0e0a 100644 --- a/gcc/d/dfrontend/hdrgen.c +++ b/gcc/d/dfrontend/hdrgen.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2011 by Digital Mars -// All Rights Reserved -// Initial header generation implementation by Dave Fladebo -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Dave Fladebo + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/hdrgen.c + */ // Routines to emit header files @@ -14,6 +15,7 @@ #define LOG 0 +#include #include #include #include @@ -25,12 +27,15 @@ #include "attrib.h" #include "cond.h" +#include "doc.h" #include "enum.h" #include "import.h" #include "module.h" #include "mtype.h" +#include "parse.h" #include "scope.h" #include "staticassert.h" +#include "target.h" #include "template.h" #include "utf.h" #include "version.h" @@ -38,57 +43,2012 @@ #include "declaration.h" #include "aggregate.h" #include "expression.h" +#include "ctfe.h" #include "statement.h" -#include "mtype.h" #include "hdrgen.h" void argsToCBuffer(OutBuffer *buf, Expressions *arguments, HdrGenState *hgs); +void sizeToCBuffer(OutBuffer *buf, HdrGenState *hgs, Expression *e); +void toBufferShort(Type *t, OutBuffer *buf, HdrGenState *hgs); +void expToCBuffer(OutBuffer *buf, HdrGenState *hgs, Expression *e, PREC pr); +void toCBuffer(Module *m, OutBuffer *buf, HdrGenState *hgs); -void Module::genhdrfile() +void genhdrfile(Module *m) { OutBuffer hdrbufr; hdrbufr.doindent = 1; - hdrbufr.printf("// D import file generated from '%s'", srcfile->toChars()); + hdrbufr.printf("// D import file generated from '%s'", m->srcfile->toChars()); hdrbufr.writenl(); HdrGenState hgs; memset(&hgs, 0, sizeof(hgs)); hgs.hdrgen = 1; - toCBuffer(&hdrbufr, &hgs); + toCBuffer(m, &hdrbufr, &hgs); // Transfer image to file - hdrfile->setbuffer(hdrbufr.data, hdrbufr.offset); + m->hdrfile->setbuffer(hdrbufr.data, hdrbufr.offset); hdrbufr.data = NULL; - ensurePathToNameExists(Loc(), hdrfile->toChars()); - writeFile(loc, hdrfile); + ensurePathToNameExists(Loc(), m->hdrfile->toChars()); + writeFile(m->loc, m->hdrfile); } -void Module::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +void toCBuffer(Module *m, OutBuffer *buf, HdrGenState *hgs) { - if (md) + if (m->md) { + if (m->md->isdeprecated) + { + if (m->md->msg) + { + buf->writestring("deprecated("); + toCBuffer(m->md->msg, buf, hgs); + buf->writestring(") "); + } + else + buf->writestring("deprecated "); + } buf->writestring("module "); - buf->writestring(md->toChars()); - buf->writebyte(';'); + buf->writestring(m->md->toChars()); + buf->writeByte(';'); buf->writenl(); } - for (size_t i = 0; i < members->dim; i++) - { Dsymbol *s = (*members)[i]; - - s->toHBuffer(buf, hgs); + for (size_t i = 0; i < m->members->dim; i++) + { + Dsymbol *s = (*m->members)[i]; + s->toCBuffer(buf, hgs); } } - -void Dsymbol::toHBuffer(OutBuffer *buf, HdrGenState *hgs) +class PrettyPrintVisitor : public Visitor { - toCBuffer(buf, hgs); -} +public: + OutBuffer *buf; + HdrGenState *hgs; + unsigned char modMask; + + PrettyPrintVisitor(OutBuffer *buf, HdrGenState *hgs) + : buf(buf), hgs(hgs), modMask(0) + { + } + + void visit(Statement *s) + { + buf->printf("Statement::toCBuffer()"); + buf->writenl(); + assert(0); + } + + void visit(ErrorStatement *s) + { + buf->printf("__error__"); + buf->writenl(); + } + + void visit(ExpStatement *s) + { + if (s->exp) + { + s->exp->toCBuffer(buf, hgs); + if (s->exp->op != TOKdeclaration) + { + buf->writeByte(';'); + if (!hgs->FLinit.init) + buf->writenl(); + } + } + else + { + buf->writeByte(';'); + if (!hgs->FLinit.init) + buf->writenl(); + } + } + + void visit(CompileStatement *s) + { + buf->writestring("mixin("); + s->exp->toCBuffer(buf, hgs); + buf->writestring(");"); + if (!hgs->FLinit.init) + buf->writenl(); + } + + void visit(CompoundStatement *s) + { + for (size_t i = 0; i < s->statements->dim; i++) + { + Statement *sx = (*s->statements)[i]; + if (sx) + sx->accept(this); + } + } + + void visit(CompoundDeclarationStatement *s) + { + bool anywritten = false; + for (size_t i = 0; i < s->statements->dim; i++) + { + Statement *sx = (*s->statements)[i]; + ExpStatement *ds; + if (sx && + (ds = sx->isExpStatement()) != NULL && + ds->exp->op == TOKdeclaration) + { + DeclarationExp *de = (DeclarationExp *)ds->exp; + Declaration *d = de->declaration->isDeclaration(); + assert(d); + VarDeclaration *v = d->isVarDeclaration(); + if (v) + { + /* This essentially copies the part of VarDeclaration::toCBuffer() + * that does not print the type. + * Should refactor this. + */ + if (anywritten) + { + buf->writestring(", "); + buf->writestring(v->ident->toChars()); + } + else + { + StorageClassDeclaration::stcToCBuffer(buf, v->storage_class); + if (v->type) + v->type->toCBuffer(buf, v->ident, hgs); + else + buf->writestring(v->ident->toChars()); + } + + if (v->init) + { buf->writestring(" = "); + ExpInitializer *ie = v->init->isExpInitializer(); + if (ie && (ie->exp->op == TOKconstruct || ie->exp->op == TOKblit)) + ((AssignExp *)ie->exp)->e2->toCBuffer(buf, hgs); + else + v->init->toCBuffer(buf, hgs); + } + } + else + d->toCBuffer(buf, hgs); + anywritten = true; + } + } + buf->writeByte(';'); + if (!hgs->FLinit.init) + buf->writenl(); + } + + void visit(UnrolledLoopStatement *s) + { + buf->writestring("unrolled {"); + buf->writenl(); + buf->level++; + + for (size_t i = 0; i < s->statements->dim; i++) + { + Statement *sx = (*s->statements)[i]; + if (sx) + sx->accept(this); + } + + buf->level--; + buf->writeByte('}'); + buf->writenl(); + } + + void visit(ScopeStatement *s) + { + buf->writeByte('{'); + buf->writenl(); + buf->level++; + + if (s->statement) + s->statement->accept(this); + + buf->level--; + buf->writeByte('}'); + buf->writenl(); + } + + void visit(WhileStatement *s) + { + buf->writestring("while ("); + s->condition->toCBuffer(buf, hgs); + buf->writeByte(')'); + buf->writenl(); + if (s->body) + s->body->accept(this); + } + + void visit(DoStatement *s) + { + buf->writestring("do"); + buf->writenl(); + if (s->body) + s->body->accept(this); + buf->writestring("while ("); + s->condition->toCBuffer(buf, hgs); + buf->writestring(");"); + buf->writenl(); + } + + void visit(ForStatement *s) + { + buf->writestring("for ("); + if (s->init) + { + hgs->FLinit.init++; + s->init->accept(this); + hgs->FLinit.init--; + } + else + buf->writeByte(';'); + if (s->condition) + { + buf->writeByte(' '); + s->condition->toCBuffer(buf, hgs); + } + buf->writeByte(';'); + if (s->increment) + { + buf->writeByte(' '); + s->increment->toCBuffer(buf, hgs); + } + buf->writeByte(')'); + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + buf->level++; + s->body->accept(this); + buf->level--; + buf->writeByte('}'); + buf->writenl(); + } + + void visit(ForeachStatement *s) + { + buf->writestring(Token::toChars(s->op)); + buf->writestring(" ("); + for (size_t i = 0; i < s->arguments->dim; i++) + { + Parameter *a = (*s->arguments)[i]; + if (i) + buf->writestring(", "); + if (a->storageClass & STCref) + buf->writestring((char*)"ref "); + if (a->type) + a->type->toCBuffer(buf, a->ident, hgs); + else + buf->writestring(a->ident->toChars()); + } + buf->writestring("; "); + s->aggr->toCBuffer(buf, hgs); + buf->writeByte(')'); + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + buf->level++; + if (s->body) + s->body->accept(this); + buf->level--; + buf->writeByte('}'); + buf->writenl(); + } + + void visit(ForeachRangeStatement *s) + { + buf->writestring(Token::toChars(s->op)); + buf->writestring(" ("); + + if (s->arg->type) + s->arg->type->toCBuffer(buf, s->arg->ident, hgs); + else + buf->writestring(s->arg->ident->toChars()); + + buf->writestring("; "); + s->lwr->toCBuffer(buf, hgs); + buf->writestring(" .. "); + s->upr->toCBuffer(buf, hgs); + buf->writeByte(')'); + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + buf->level++; + if (s->body) + s->body->accept(this); + buf->level--; + buf->writeByte('}'); + buf->writenl(); + } + + void visit(IfStatement *s) + { + buf->writestring("if ("); + if (s->arg) + { + if (s->arg->type) + s->arg->type->toCBuffer(buf, s->arg->ident, hgs); + else + { + buf->writestring("auto "); + buf->writestring(s->arg->ident->toChars()); + } + buf->writestring(" = "); + } + s->condition->toCBuffer(buf, hgs); + buf->writeByte(')'); + buf->writenl(); + if (!s->ifbody->isScopeStatement()) + buf->level++; + s->ifbody->accept(this); + if (!s->ifbody->isScopeStatement()) + buf->level--; + if (s->elsebody) + { + buf->writestring("else"); + buf->writenl(); + if (!s->elsebody->isScopeStatement()) + buf->level++; + s->elsebody->accept(this); + if (!s->elsebody->isScopeStatement()) + buf->level--; + } + } + + void visit(ConditionalStatement *s) + { + s->condition->toCBuffer(buf, hgs); + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + buf->level++; + if (s->ifbody) + s->ifbody->accept(this); + buf->level--; + buf->writeByte('}'); + buf->writenl(); + if (s->elsebody) + { + buf->writestring("else"); + buf->writenl(); + buf->writeByte('{'); + buf->level++; + buf->writenl(); + s->elsebody->accept(this); + buf->level--; + buf->writeByte('}'); + buf->writenl(); + } + buf->writenl(); + } + + void visit(PragmaStatement *s) + { + buf->writestring("pragma ("); + buf->writestring(s->ident->toChars()); + if (s->args && s->args->dim) + { + buf->writestring(", "); + argsToCBuffer(buf, s->args, hgs); + } + buf->writeByte(')'); + if (s->body) + { + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + buf->level++; + + s->body->accept(this); + + buf->level--; + buf->writeByte('}'); + buf->writenl(); + } + else + { + buf->writeByte(';'); + buf->writenl(); + } + } + + void visit(StaticAssertStatement *s) + { + s->sa->toCBuffer(buf, hgs); + } + + void visit(SwitchStatement *s) + { + buf->writestring(s->isFinal ? "final switch (" : "switch ("); + s->condition->toCBuffer(buf, hgs); + buf->writeByte(')'); + buf->writenl(); + if (s->body) + { + if (!s->body->isScopeStatement()) + { + buf->writeByte('{'); + buf->writenl(); + buf->level++; + s->body->accept(this); + buf->level--; + buf->writeByte('}'); + buf->writenl(); + } + else + { + s->body->accept(this); + } + } + } + + void visit(CaseStatement *s) + { + buf->writestring("case "); + s->exp->toCBuffer(buf, hgs); + buf->writeByte(':'); + buf->writenl(); + s->statement->accept(this); + } + + void visit(CaseRangeStatement *s) + { + buf->writestring("case "); + s->first->toCBuffer(buf, hgs); + buf->writestring(": .. case "); + s->last->toCBuffer(buf, hgs); + buf->writeByte(':'); + buf->writenl(); + s->statement->accept(this); + } + + void visit(DefaultStatement *s) + { + buf->writestring("default:"); + buf->writenl(); + s->statement->accept(this); + } + + void visit(GotoDefaultStatement *s) + { + buf->writestring("goto default;"); + buf->writenl(); + } + + void visit(GotoCaseStatement *s) + { + buf->writestring("goto case"); + if (s->exp) + { + buf->writeByte(' '); + s->exp->toCBuffer(buf, hgs); + } + buf->writeByte(';'); + buf->writenl(); + } + + void visit(SwitchErrorStatement *s) + { + buf->writestring("SwitchErrorStatement::toCBuffer()"); + buf->writenl(); + } + + void visit(ReturnStatement *s) + { + buf->printf("return "); + if (s->exp) + s->exp->toCBuffer(buf, hgs); + buf->writeByte(';'); + buf->writenl(); + } + + void visit(BreakStatement *s) + { + buf->writestring("break"); + if (s->ident) + { + buf->writeByte(' '); + buf->writestring(s->ident->toChars()); + } + buf->writeByte(';'); + buf->writenl(); + } + + void visit(ContinueStatement *s) + { + buf->writestring("continue"); + if (s->ident) + { + buf->writeByte(' '); + buf->writestring(s->ident->toChars()); + } + buf->writeByte(';'); + buf->writenl(); + } + + void visit(SynchronizedStatement *s) + { + buf->writestring("synchronized"); + if (s->exp) + { + buf->writeByte('('); + s->exp->toCBuffer(buf, hgs); + buf->writeByte(')'); + } + if (s->body) + { + buf->writeByte(' '); + s->body->accept(this); + } + } + + void visit(WithStatement *s) + { + buf->writestring("with ("); + s->exp->toCBuffer(buf, hgs); + buf->writestring(")"); + buf->writenl(); + if (s->body) + s->body->accept(this); + } + + void visit(TryCatchStatement *s) + { + buf->writestring("try"); + buf->writenl(); + if (s->body) + s->body->accept(this); + for (size_t i = 0; i < s->catches->dim; i++) + { + Catch *c = (*s->catches)[i]; + visit(c); + } + } + + void visit(TryFinallyStatement *s) + { + buf->writestring("try"); + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + buf->level++; + s->body->accept(this); + buf->level--; + buf->writeByte('}'); + buf->writenl(); + buf->writestring("finally"); + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + buf->level++; + s->finalbody->accept(this); + buf->level--; + buf->writeByte('}'); + buf->writenl(); + } + + void visit(OnScopeStatement *s) + { + buf->writestring(Token::toChars(s->tok)); + buf->writeByte(' '); + s->statement->accept(this); + } + + void visit(ThrowStatement *s) + { + buf->printf("throw "); + s->exp->toCBuffer(buf, hgs); + buf->writeByte(';'); + buf->writenl(); + } + + void visit(DebugStatement *s) + { + if (s->statement) + { + s->statement->accept(this); + } + } + + void visit(GotoStatement *s) + { + buf->writestring("goto "); + buf->writestring(s->ident->toChars()); + buf->writeByte(';'); + buf->writenl(); + } + + void visit(LabelStatement *s) + { + buf->writestring(s->ident->toChars()); + buf->writeByte(':'); + buf->writenl(); + if (s->statement) + s->statement->accept(this); + } + + void visit(AsmStatement *s) + { + buf->writestring("asm { "); + Token *t = s->tokens; + buf->level++; + while (t) + { + buf->writestring(t->toChars()); + if (t->next && + t->value != TOKmin && + t->value != TOKcomma && + t->next->value != TOKcomma && + t->value != TOKlbracket && + t->next->value != TOKlbracket && + t->next->value != TOKrbracket && + t->value != TOKlparen && + t->next->value != TOKlparen && + t->next->value != TOKrparen && + t->value != TOKdot && + t->next->value != TOKdot) + { + buf->writeByte(' '); + } + t = t->next; + } + buf->level--; + buf->writestring("; }"); + buf->writenl(); + } + +#ifdef IN_GCC + void visit(ExtAsmStatement *s) + { + buf->writestring ("gcc asm { "); + + if (s->insn) + buf->writestring (s->insn->toChars()); + + buf->writestring (" : "); + + if (s->args) + { + for (size_t i = 0; i < s->args->dim; i++) + { + Identifier *name = (*s->names)[i]; + Expression *constr = (*s->constraints)[i]; + Expression *arg = (*s->args)[i]; + + if (name) + { + buf->writestring ("["); + buf->writestring (name->toChars()); + buf->writestring ("] "); + } + + if (constr) + { + buf->writestring (constr->toChars()); + buf->writestring (" "); + } + + if (arg) + buf->writestring (arg->toChars()); + + if (i < s->outputargs - 1) + buf->writestring (", "); + else if (i == s->outputargs - 1) + buf->writestring (" : "); + else if (i < s->args->dim - 1) + buf->writestring (", "); + } + } + + if (s->clobbers) + { + buf->writestring (" : "); + for (size_t i = 0; i < s->clobbers->dim; i++) + { + Expression *clobber = (*s->clobbers)[i]; + buf->writestring (clobber->toChars()); + if (i < s->clobbers->dim - 1) + buf->writestring (", "); + } + } + + buf->writestring ("; }"); + buf->writenl(); + } +#endif + + void visit(ImportStatement *s) + { + for (size_t i = 0; i < s->imports->dim; i++) + { + Dsymbol *imp = (*s->imports)[i]; + imp->toCBuffer(buf, hgs); + } + } + + void visit(Catch *c) + { + buf->writestring("catch"); + if (c->type) + { + buf->writeByte('('); + c->type->toCBuffer(buf, c->ident, hgs); + buf->writeByte(')'); + } + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + buf->level++; + if (c->handler) + c->handler->accept(this); + buf->level--; + buf->writeByte('}'); + buf->writenl(); + } + + //////////////////////////////////////////////////////////////////////////// + + void visitWithMask(Type *t, unsigned char mod) + { + unsigned char save = modMask; + modMask = mod; + + // Tuples and functions don't use the type constructor syntax + if (modMask == t->mod || + t->ty == Tfunction || + t->ty == Ttuple) + { + t->accept(this); + } + else + { + unsigned char m = t->mod & ~(t->mod & modMask); + if (m & MODshared) + { + MODtoBuffer(buf, MODshared); + buf->writeByte('('); + } + if (m & MODwild) + { + MODtoBuffer(buf, MODwild); + buf->writeByte('('); + } + if (m & (MODconst | MODimmutable)) + { + MODtoBuffer(buf, m & (MODconst | MODimmutable)); + buf->writeByte('('); + } + + t->accept(this); + + if (m & (MODconst | MODimmutable)) + buf->writeByte(')'); + if (m & MODwild) + buf->writeByte(')'); + if (m & MODshared) + buf->writeByte(')'); + } + modMask = save; + } + + void visit(Type *t) + { + printf("t = %p, ty = %d\n", t, t->ty); + assert(0); + } + + void visit(TypeBasic *t) + { + //printf("TypeBasic::toCBuffer2(modMask = %d, t->mod = %d)\n", modMask, t->mod); + buf->writestring(t->dstring); + } + + void visit(TypeVector *t) + { + //printf("TypeVector::toCBuffer2(modMask = %d, t->mod = %d)\n", modMask, t->mod); + buf->writestring("__vector("); + visitWithMask(t->basetype, t->mod); + buf->writestring(")"); + } + + void visit(TypeSArray *t) + { + visitWithMask(t->next, t->mod); + buf->writeByte('['); + sizeToCBuffer(buf, hgs, t->dim); + buf->writeByte(']'); + } + + void visit(TypeDArray *t) + { + if (t->equals(t->tstring)) + buf->writestring("string"); + else + { + visitWithMask(t->next, t->mod); + buf->writestring("[]"); + } + } + + void visit(TypeAArray *t) + { + visitWithMask(t->next, t->mod); + buf->writeByte('['); + visitWithMask(t->index, 0); + buf->writeByte(']'); + } + + void visit(TypePointer *t) + { + //printf("TypePointer::toCBuffer2() next = %d\n", t->next->ty); + visitWithMask(t->next, t->mod); + if (t->next->ty != Tfunction) + buf->writeByte('*'); + } + + void visit(TypeReference *t) + { + visitWithMask(t->next, t->mod); + buf->writeByte('&'); + } + + void visit(TypeFunction *t) + { + //printf("TypeFunction::toCBuffer2() t = %p, ref = %d\n", t, t->isref); + if (t->inuse) + { + t->inuse = 2; // flag error to caller + return; + } + t->inuse++; + visitFuncIdent(t, "function"); + t->inuse--; + } + + // callback for TypeFunction::attributesApply, prepends spaces + struct PreAppendStrings + { + OutBuffer *buf; + + static int fp(void *param, const char *str) + { + PreAppendStrings *p = (PreAppendStrings *)param; + p->buf->writeByte(' '); + p->buf->writestring(str); + return 0; + } + }; + + void visitFuncIdent(TypeFunction *t, const char *ident) + { + if (t->linkage > LINKd && hgs->ddoc != 1 && !hgs->hdrgen) + { + linkageToBuffer(buf, t->linkage); + buf->writeByte(' '); + } + + if (t->next) + { + visitWithMask(t->next, 0); + buf->writeByte(' '); + } + buf->writestring(ident); + Parameter::argsToCBuffer(buf, hgs, t->parameters, t->varargs); + + /* Use postfix style for attributes + */ + if (modMask != t->mod) + { + t->modToBuffer(buf); + } + + PreAppendStrings pas; + pas.buf = buf; + t->attributesApply(&pas, &PreAppendStrings::fp); + } + + void visit(TypeDelegate *t) + { + visitFuncIdent((TypeFunction *)t->next, "delegate"); + } + + void visitTypeQualifiedHelper(TypeQualified *t) + { + for (size_t i = 0; i < t->idents.dim; i++) + { + RootObject *id = t->idents[i]; + buf->writeByte('.'); + + if (id->dyncast() == DYNCAST_DSYMBOL) + { + TemplateInstance *ti = (TemplateInstance *)id; + ti->toCBuffer(buf, hgs); + } + else + buf->writestring(id->toChars()); + } + } + + void visit(TypeIdentifier *t) + { + buf->writestring(t->ident->toChars()); + visitTypeQualifiedHelper(t); + } + void visit(TypeInstance *t) + { + t->tempinst->toCBuffer(buf, hgs); + visitTypeQualifiedHelper(t); + } + + void visit(TypeTypeof *t) + { + buf->writestring("typeof("); + t->exp->toCBuffer(buf, hgs); + buf->writeByte(')'); + visitTypeQualifiedHelper(t); + } + + void visit(TypeReturn *t) + { + buf->writestring("typeof(return)"); + visitTypeQualifiedHelper(t); + } -/*************************************/ + void visit(TypeEnum *t) + { + buf->writestring(t->sym->toChars()); + } + + void visit(TypeStruct *t) + { + TemplateInstance *ti = t->sym->parent->isTemplateInstance(); + if (ti && ti->toAlias() == t->sym) + buf->writestring((hgs->fullQualification) ? ti->toPrettyChars() : ti->toChars()); + else + buf->writestring((hgs->fullQualification) ? t->sym->toPrettyChars() : t->sym->toChars()); + } + + void visit(TypeClass *t) + { + TemplateInstance *ti = t->sym->parent->isTemplateInstance(); + if (ti && ti->toAlias() == t->sym) + buf->writestring((hgs->fullQualification) ? ti->toPrettyChars() : ti->toChars()); + else + buf->writestring((hgs->fullQualification) ? t->sym->toPrettyChars() : t->sym->toChars()); + } + + void visit(TypeTuple *t) + { + Parameter::argsToCBuffer(buf, hgs, t->arguments, 0); + } + + void visit(TypeSlice *t) + { + visitWithMask(t->next, t->mod); + + buf->writeByte('['); + sizeToCBuffer(buf, hgs, t->lwr); + buf->writestring(" .. "); + sizeToCBuffer(buf, hgs, t->upr); + buf->writeByte(']'); + } + + void visit(TypeNull *t) + { + buf->writestring("typeof(null)"); + } + + //////////////////////////////////////////////////////////////////////////// + + void visit(Expression *e) + { + buf->writestring(Token::toChars(e->op)); + } + + void visit(IntegerExp *e) + { + dinteger_t v = e->toInteger(); + + if (e->type) + { + Type *t = e->type; + L1: + switch (t->ty) + { + case Tenum: + { + TypeEnum *te = (TypeEnum *)t; + buf->printf("cast(%s)", te->sym->toChars()); + t = te->sym->memtype; + goto L1; + } + + case Twchar: // BUG: need to cast(wchar) + case Tdchar: // BUG: need to cast(dchar) + if ((uinteger_t)v > 0xFF) + { + buf->printf("'\\U%08x'", v); + break; + } + case Tchar: + { + size_t o = buf->offset; + if (v == '\'') + buf->writestring("'\\''"); + else if (isprint((int)v) && v != '\\') + buf->printf("'%c'", (int)v); + else + buf->printf("'\\x%02x'", (int)v); + if (hgs->ddoc) + escapeDdocString(buf, o); + break; + } + + case Tint8: + buf->writestring("cast(byte)"); + goto L2; + + case Tint16: + buf->writestring("cast(short)"); + goto L2; + + case Tint32: + L2: + buf->printf("%d", (int)v); + break; + + case Tuns8: + buf->writestring("cast(ubyte)"); + goto L3; + + case Tuns16: + buf->writestring("cast(ushort)"); + goto L3; + + case Tuns32: + L3: + buf->printf("%uu", (unsigned)v); + break; + + case Tint64: + buf->printf("%lldL", v); + break; + + case Tuns64: + L4: + buf->printf("%lluLU", v); + break; + + case Tbool: + buf->writestring((char *)(v ? "true" : "false")); + break; + + case Tpointer: + buf->writestring("cast("); + buf->writestring(t->toChars()); + buf->writeByte(')'); + if (Target::ptrsize == 4) + goto L3; + else if (Target::ptrsize == 8) + goto L4; + else + assert(0); + + default: + /* This can happen if errors, such as + * the type is painted on like in fromConstInitializer(). + */ + if (!global.errors) + { + #ifdef DEBUG + t->print(); + #endif + assert(0); + } + break; + } + } + else if (v & 0x8000000000000000LL) + buf->printf("0x%llx", v); + else + buf->printf("%lld", v); + } + + void visit(ErrorExp *e) + { + buf->writestring("__error"); + } + + void floatToBuffer(Type *type, real_t value) + { + /* In order to get an exact representation, try converting it + * to decimal then back again. If it matches, use it. + * If it doesn't, fall back to hex, which is + * always exact. + * Longest string is for -real.max: + * "-1.18973e+4932\0".length == 17 + * "-0xf.fffffffffffffffp+16380\0".length == 28 + */ + const size_t BUFFER_LEN = 32; + char buffer[BUFFER_LEN]; + ld_sprint(buffer, 'g', value); + assert(strlen(buffer) < BUFFER_LEN); + + real_t r = Port::strtold(buffer, NULL); + if (r != value) // if exact duplication + ld_sprint(buffer, 'a', value); + buf->writestring(buffer); + + if (type) + { + Type *t = type->toBasetype(); + switch (t->ty) + { + case Tfloat32: + case Timaginary32: + case Tcomplex32: + buf->writeByte('F'); + break; + + case Tfloat80: + case Timaginary80: + case Tcomplex80: + buf->writeByte('L'); + break; + + default: + break; + } + if (t->isimaginary()) + buf->writeByte('i'); + } + } + + void visit(RealExp *e) + { + floatToBuffer(e->type, e->value); + } + + void visit(ComplexExp *e) + { + /* Print as: + * (re+imi) + */ + buf->writeByte('('); + floatToBuffer(e->type, creall(e->value)); + buf->writeByte('+'); + floatToBuffer(e->type, cimagl(e->value)); + buf->writestring("i)"); + } + + void visit(IdentifierExp *e) + { + if (hgs->hdrgen || hgs->ddoc) + buf->writestring(e->ident->toHChars2()); + else + buf->writestring(e->ident->toChars()); + } + + void visit(DsymbolExp *e) + { + buf->writestring(e->s->toChars()); + } + + void visit(ThisExp *e) + { + buf->writestring("this"); + } + + void visit(SuperExp *e) + { + buf->writestring("super"); + } + + void visit(NullExp *e) + { + buf->writestring("null"); + } + + void visit(StringExp *e) + { + buf->writeByte('"'); + size_t o = buf->offset; + for (size_t i = 0; i < e->len; i++) + { + unsigned c = e->charAt(i); + switch (c) + { + case '"': + case '\\': + if (!hgs->console) + buf->writeByte('\\'); + default: + if (c <= 0xFF) + { + if (c <= 0x7F && (isprint(c) || hgs->console)) + buf->writeByte(c); + else + buf->printf("\\x%02x", c); + } + else if (c <= 0xFFFF) + buf->printf("\\x%02x\\x%02x", c & 0xFF, c >> 8); + else + buf->printf("\\x%02x\\x%02x\\x%02x\\x%02x", + c & 0xFF, (c >> 8) & 0xFF, (c >> 16) & 0xFF, c >> 24); + break; + } + } + if (hgs->ddoc) + escapeDdocString(buf, o); + buf->writeByte('"'); + if (e->postfix) + buf->writeByte(e->postfix); + } + + void visit(ArrayLiteralExp *e) + { + buf->writeByte('['); + argsToCBuffer(buf, e->elements, hgs); + buf->writeByte(']'); + } + + void visit(AssocArrayLiteralExp *e) + { + buf->writeByte('['); + for (size_t i = 0; i < e->keys->dim; i++) + { + Expression *key = (*e->keys)[i]; + Expression *value = (*e->values)[i]; + + if (i) + buf->writestring(", "); + expToCBuffer(buf, hgs, key, PREC_assign); + buf->writeByte(':'); + expToCBuffer(buf, hgs, value, PREC_assign); + } + buf->writeByte(']'); + } + + void visit(StructLiteralExp *e) + { + buf->writestring(e->sd->toChars()); + buf->writeByte('('); + + // CTFE can generate struct literals that contain an AddrExp pointing + // to themselves, need to avoid infinite recursion: + // struct S { this(int){ this.s = &this; } S* s; } + // const foo = new S(0); + if (e->stageflags & stageToCBuffer) + buf->writestring(""); + else + { + int old = e->stageflags; + e->stageflags |= stageToCBuffer; + argsToCBuffer(buf, e->elements, hgs); + e->stageflags = old; + } + + buf->writeByte(')'); + } + + void visit(TypeExp *e) + { + e->type->toCBuffer(buf, NULL, hgs); + } + + void visit(ScopeExp *e) + { + if (e->sds->isTemplateInstance()) + { + e->sds->toCBuffer(buf, hgs); + } + else if (hgs != NULL && hgs->ddoc) + { + // fixes bug 6491 + Module *module = e->sds->isModule(); + if (module) + buf->writestring(module->md->toChars()); + else + buf->writestring(e->sds->toChars()); + } + else + { + buf->writestring(e->sds->kind()); + buf->writestring(" "); + buf->writestring(e->sds->toChars()); + } + } + + void visit(TemplateExp *e) + { + buf->writestring(e->td->toChars()); + } + + void visit(NewExp *e) + { + if (e->thisexp) + { + expToCBuffer(buf, hgs, e->thisexp, PREC_primary); + buf->writeByte('.'); + } + buf->writestring("new "); + if (e->newargs && e->newargs->dim) + { + buf->writeByte('('); + argsToCBuffer(buf, e->newargs, hgs); + buf->writeByte(')'); + } + e->newtype->toCBuffer(buf, NULL, hgs); + if (e->arguments && e->arguments->dim) + { + buf->writeByte('('); + argsToCBuffer(buf, e->arguments, hgs); + buf->writeByte(')'); + } + } + + void visit(NewAnonClassExp *e) + { + if (e->thisexp) + { + expToCBuffer(buf, hgs, e->thisexp, PREC_primary); + buf->writeByte('.'); + } + buf->writestring("new"); + if (e->newargs && e->newargs->dim) + { + buf->writeByte('('); + argsToCBuffer(buf, e->newargs, hgs); + buf->writeByte(')'); + } + buf->writestring(" class "); + if (e->arguments && e->arguments->dim) + { + buf->writeByte('('); + argsToCBuffer(buf, e->arguments, hgs); + buf->writeByte(')'); + } + //buf->writestring(" { }"); + if (e->cd) + { + e->cd->toCBuffer(buf, hgs); + } + } + + void visit(SymOffExp *e) + { + if (e->offset) + buf->printf("(& %s+%u)", e->var->toChars(), e->offset); + else if (e->var->isTypeInfoDeclaration()) + buf->printf("%s", e->var->toChars()); + else + buf->printf("& %s", e->var->toChars()); + } + + void visit(VarExp *e) + { + buf->writestring(e->var->toChars()); + } + + void visit(OverExp *e) + { + buf->writestring(e->vars->ident->toChars()); + } + + void visit(TupleExp *e) + { + if (e->e0) + { + buf->writeByte('('); + e->e0->toCBuffer(buf, hgs); + buf->writestring(", tuple("); + argsToCBuffer(buf, e->exps, hgs); + buf->writestring("))"); + } + else + { + buf->writestring("tuple("); + argsToCBuffer(buf, e->exps, hgs); + buf->writeByte(')'); + } + } + + void visit(FuncExp *e) + { + e->fd->toCBuffer(buf, hgs); + //buf->writestring(e->fd->toChars()); + } + + void visit(DeclarationExp *e) + { + e->declaration->toCBuffer(buf, hgs); + } + + void visit(TypeidExp *e) + { + buf->writestring("typeid("); + ObjectToCBuffer(buf, hgs, e->obj); + buf->writeByte(')'); + } + + void visit(TraitsExp *e) + { + buf->writestring("__traits("); + buf->writestring(e->ident->toChars()); + if (e->args) + { + for (size_t i = 0; i < e->args->dim; i++) + { + buf->writestring(", ");; + RootObject *oarg = (*e->args)[i]; + ObjectToCBuffer(buf, hgs, oarg); + } + } + buf->writeByte(')'); + } + + void visit(HaltExp *e) + { + buf->writestring("halt"); + } + + void visit(IsExp *e) + { + buf->writestring("is("); + e->targ->toCBuffer(buf, e->id, hgs); + if (e->tok2 != TOKreserved) + { + buf->printf(" %s %s", Token::toChars(e->tok), Token::toChars(e->tok2)); + } + else if (e->tspec) + { + if (e->tok == TOKcolon) + buf->writestring(" : "); + else + buf->writestring(" == "); + e->tspec->toCBuffer(buf, NULL, hgs); + } + if (e->parameters) + { + for (size_t i = 0; i < e->parameters->dim; i++) + { + buf->writestring(", "); + TemplateParameter *tp = (*e->parameters)[i]; + tp->toCBuffer(buf, hgs); + } + } + buf->writeByte(')'); + } + + void visit(UnaExp *e) + { + buf->writestring(Token::toChars(e->op)); + expToCBuffer(buf, hgs, e->e1, precedence[e->op]); + } + + void visit(BinExp *e) + { + expToCBuffer(buf, hgs, e->e1, precedence[e->op]); + buf->writeByte(' '); + buf->writestring(Token::toChars(e->op)); + buf->writeByte(' '); + expToCBuffer(buf, hgs, e->e2, (PREC)(precedence[e->op] + 1)); + } + + void visit(CompileExp *e) + { + buf->writestring("mixin("); + expToCBuffer(buf, hgs, e->e1, PREC_assign); + buf->writeByte(')'); + } + + void visit(FileExp *e) + { + buf->writestring("import("); + expToCBuffer(buf, hgs, e->e1, PREC_assign); + buf->writeByte(')'); + } + + void visit(AssertExp *e) + { + buf->writestring("assert("); + expToCBuffer(buf, hgs, e->e1, PREC_assign); + if (e->msg) + { + buf->writestring(", "); + expToCBuffer(buf, hgs, e->msg, PREC_assign); + } + buf->writeByte(')'); + } + + void visit(DotIdExp *e) + { + //printf("DotIdExp::toCBuffer()\n"); + expToCBuffer(buf, hgs, e->e1, PREC_primary); + buf->writeByte('.'); + buf->writestring(e->ident->toChars()); + } + + void visit(DotTemplateExp *e) + { + expToCBuffer(buf, hgs, e->e1, PREC_primary); + buf->writeByte('.'); + buf->writestring(e->td->toChars()); + } + + void visit(DotVarExp *e) + { + expToCBuffer(buf, hgs, e->e1, PREC_primary); + buf->writeByte('.'); + buf->writestring(e->var->toChars()); + } + + void visit(DotTemplateInstanceExp *e) + { + expToCBuffer(buf, hgs, e->e1, PREC_primary); + buf->writeByte('.'); + e->ti->toCBuffer(buf, hgs); + } + + void visit(DelegateExp *e) + { + buf->writeByte('&'); + if (!e->func->isNested()) + { + expToCBuffer(buf, hgs, e->e1, PREC_primary); + buf->writeByte('.'); + } + buf->writestring(e->func->toChars()); + } + + void visit(DotTypeExp *e) + { + expToCBuffer(buf, hgs, e->e1, PREC_primary); + buf->writeByte('.'); + buf->writestring(e->sym->toChars()); + } + + void visit(CallExp *e) + { + if (e->e1->op == TOKtype) + { + /* Avoid parens around type to prevent forbidden cast syntax: + * (sometype)(arg1) + * This is ok since types in constructor calls + * can never depend on parens anyway + */ + e->e1->toCBuffer(buf, hgs); + } + else + expToCBuffer(buf, hgs, e->e1, precedence[e->op]); + buf->writeByte('('); + argsToCBuffer(buf, e->arguments, hgs); + buf->writeByte(')'); + } + + void visit(PtrExp *e) + { + buf->writeByte('*'); + expToCBuffer(buf, hgs, e->e1, precedence[e->op]); + } + + void visit(DeleteExp *e) + { + buf->writestring("delete "); + expToCBuffer(buf, hgs, e->e1, precedence[e->op]); + } + + void visit(CastExp *e) + { + buf->writestring("cast("); + if (e->to) + e->to->toCBuffer(buf, NULL, hgs); + else + { + MODtoBuffer(buf, e->mod); + } + buf->writeByte(')'); + expToCBuffer(buf, hgs, e->e1, precedence[e->op]); + } + + void visit(VectorExp *e) + { + buf->writestring("cast("); + e->to->toCBuffer(buf, NULL, hgs); + buf->writeByte(')'); + expToCBuffer(buf, hgs, e->e1, precedence[e->op]); + } + + void visit(SliceExp *e) + { + expToCBuffer(buf, hgs, e->e1, precedence[e->op]); + buf->writeByte('['); + if (e->upr || e->lwr) + { + if (e->lwr) + sizeToCBuffer(buf, hgs, e->lwr); + else + buf->writeByte('0'); + buf->writestring(".."); + if (e->upr) + sizeToCBuffer(buf, hgs, e->upr); + else + buf->writestring("$"); + } + buf->writeByte(']'); + } + + void visit(ArrayLengthExp *e) + { + expToCBuffer(buf, hgs, e->e1, PREC_primary); + buf->writestring(".length"); + } + + void visit(IntervalExp *e) + { + expToCBuffer(buf, hgs, e->lwr, PREC_assign); + buf->writestring(".."); + expToCBuffer(buf, hgs, e->upr, PREC_assign); + } + + void visit(DelegatePtrExp *e) + { + expToCBuffer(buf, hgs, e->e1, PREC_primary); + buf->writestring(".ptr"); + } + + void visit(DelegateFuncptrExp *e) + { + expToCBuffer(buf, hgs, e->e1, PREC_primary); + buf->writestring(".funcptr"); + } + + void visit(ArrayExp *e) + { + expToCBuffer(buf, hgs, e->e1, PREC_primary); + buf->writeByte('['); + argsToCBuffer(buf, e->arguments, hgs); + buf->writeByte(']'); + } + + void visit(DotExp *e) + { + expToCBuffer(buf, hgs, e->e1, PREC_primary); + buf->writeByte('.'); + expToCBuffer(buf, hgs, e->e2, PREC_primary); + } + + void visit(IndexExp *e) + { + expToCBuffer(buf, hgs, e->e1, PREC_primary); + buf->writeByte('['); + sizeToCBuffer(buf, hgs, e->e2); + buf->writeByte(']'); + } + + void visit(PostExp *e) + { + expToCBuffer(buf, hgs, e->e1, precedence[e->op]); + buf->writestring(Token::toChars(e->op)); + } + + void visit(PreExp *e) + { + buf->writestring(Token::toChars(e->op)); + expToCBuffer(buf, hgs, e->e1, precedence[e->op]); + } + + void visit(RemoveExp *e) + { + expToCBuffer(buf, hgs, e->e1, PREC_primary); + buf->writestring(".remove("); + expToCBuffer(buf, hgs, e->e2, PREC_assign); + buf->writestring(")"); + } + + void visit(CondExp *e) + { + expToCBuffer(buf, hgs, e->econd, PREC_oror); + buf->writestring(" ? "); + expToCBuffer(buf, hgs, e->e1, PREC_expr); + buf->writestring(" : "); + expToCBuffer(buf, hgs, e->e2, PREC_cond); + } + + void visit(DefaultInitExp *e) + { + buf->writestring(Token::toChars(e->subop)); + } + + void visit(ClassReferenceExp *e) + { + buf->writestring(e->value->toChars()); + } + +#ifdef IN_GCC + void visit(WrappedExp *e) + { + buf->writestring(""); + } +#endif +}; + +void toCBuffer(Statement *s, OutBuffer *buf, HdrGenState *hgs) +{ + PrettyPrintVisitor v(buf, hgs); + s->accept(&v); +} + +void toCBuffer(Type *t, OutBuffer *buf, Identifier *ident, HdrGenState *hgs) +{ + if (t->ty == Tfunction) + { + functionToBufferFull((TypeFunction *)t, buf, ident, hgs, NULL); + return; + } + if (t->ty == Terror) + { + buf->writestring("_error_"); + return; + } + + toBufferShort(t, buf, hgs); + if (ident) + { + buf->writeByte(' '); + buf->writestring(ident->toChars()); + } +} + +// Bypass the special printing of function and error types +void toBufferShort(Type *t, OutBuffer *buf, HdrGenState *hgs) +{ + PrettyPrintVisitor v(buf, hgs); + v.visitWithMask(t, 0); +} + +void trustToBuffer(OutBuffer *buf, TRUST trust) +{ + const char *p = trustToChars(trust); + if (p) + buf->writestring(p); +} + +const char *trustToChars(TRUST trust) +{ + switch (trust) + { + case TRUSTdefault: return NULL; + case TRUSTsystem: return "@system"; + case TRUSTtrusted: return "@trusted"; + case TRUSTsafe: return "@safe"; + default: assert(0); + } + return NULL; // never reached +} + +void linkageToBuffer(OutBuffer *buf, LINK linkage) +{ + const char *p = linkageToChars(linkage); + if (p) + { + buf->writestring("extern ("); + buf->writestring(p); + buf->writeByte(')'); + } +} + +const char *linkageToChars(LINK linkage) +{ + switch (linkage) + { + case LINKdefault: return NULL; + case LINKd: return "D"; + case LINKc: return "C"; + case LINKcpp: return "C++"; + case LINKwindows: return "Windows"; + case LINKpascal: return "Pascal"; + default: assert(0); + } + return NULL; // never reached +} + +void protectionToBuffer(OutBuffer *buf, PROT prot) +{ + const char *p = protectionToChars(prot); + if (p) + buf->writestring(p); +} + +const char *protectionToChars(PROT prot) +{ + switch (prot) + { + case PROTundefined: return NULL; + case PROTnone: return "none"; + case PROTprivate: return "private"; + case PROTpackage: return "package"; + case PROTprotected: return "protected"; + case PROTpublic: return "public"; + case PROTexport: return "export"; + default: assert(0); + } + return NULL; // never reached +} + +// callback for TypeFunction::attributesApply, avoids 'ref' in ctors and appends spaces +struct PostAppendStrings +{ + bool isCtor; + OutBuffer *buf; + + static int fp(void *param, const char *str) + { + PostAppendStrings *p = (PostAppendStrings *)param; + + // don't write 'ref' for ctors + if (p->isCtor && strcmp(str, "ref") == 0) + return 0; + + p->buf->writestring(str); + p->buf->writeByte(' '); + return 0; + } +}; + +// Print the full function signature with correct ident, attributes and template args +void functionToBufferFull(TypeFunction *tf, OutBuffer *buf, Identifier *ident, + HdrGenState* hgs, TemplateDeclaration *td) +{ + //printf("TypeFunction::toCBuffer() this = %p\n", this); + if (tf->inuse) + { + tf->inuse = 2; // flag error to caller + return; + } + tf->inuse++; + + /* Use 'storage class' style for attributes + */ + if (tf->mod) + { + MODtoBuffer(buf, tf->mod); + buf->writeByte(' '); + } + + PostAppendStrings pas; + pas.isCtor = (ident == Id::ctor); + pas.buf = buf; + tf->attributesApply(&pas, &PostAppendStrings::fp); + + if (tf->linkage > LINKd && hgs->ddoc != 1 && !hgs->hdrgen) + { + linkageToBuffer(buf, tf->linkage); + buf->writeByte(' '); + } + + if (ident && ident->toHChars2() != ident->toChars()) + { + } + else if (tf->next) + { + toBufferShort(tf->next, buf, hgs); + if (ident) + buf->writeByte(' '); + } + else if (hgs->ddoc) + buf->writestring("auto "); + + if (ident) + buf->writestring(ident->toHChars2()); + + if (td) + { + buf->writeByte('('); + for (size_t i = 0; i < td->origParameters->dim; i++) + { + TemplateParameter *tp = (*td->origParameters)[i]; + if (i) + buf->writestring(", "); + tp->toCBuffer(buf, hgs); + } + buf->writeByte(')'); + } + Parameter::argsToCBuffer(buf, hgs, tf->parameters, tf->varargs); + tf->inuse--; +} + +// ident is inserted before the argument list and will be "function" or "delegate" for a type +void functionToBufferWithIdent(TypeFunction *t, OutBuffer *buf, const char *ident) +{ + HdrGenState hgs; + PrettyPrintVisitor v(buf, &hgs); + v.visitFuncIdent(t, ident); +} + +void toCBuffer(Expression *e, OutBuffer *buf, HdrGenState *hgs) +{ + PrettyPrintVisitor v(buf, hgs); + e->accept(&v); +} + +void sizeToCBuffer(OutBuffer *buf, HdrGenState *hgs, Expression *e) +{ + if (e->type == Type::tsize_t) + { + Expression *ex = (e->op == TOKcast ? ((CastExp *)e)->e1 : e); + ex = ex->optimize(WANTvalue); + + dinteger_t uval = ex->op == TOKint64 ? ex->toInteger() : (dinteger_t)-1; + if ((sinteger_t)uval >= 0) + { + dinteger_t sizemax; + if (Target::ptrsize == 4) + sizemax = 0xFFFFFFFFUL; + else if (Target::ptrsize == 8) + sizemax = 0xFFFFFFFFFFFFFFFFULL; + else + assert(0); + if (uval <= sizemax && uval <= 0x7FFFFFFFFFFFFFFFULL) + { + buf->printf("%llu", uval); + return; + } + } + } + expToCBuffer(buf, hgs, e, PREC_assign); +} + + +/************************************************** + * Write expression out to buf, but wrap it + * in ( ) if its precedence is less than pr. + */ + +void expToCBuffer(OutBuffer *buf, HdrGenState *hgs, Expression *e, PREC pr) +{ +#ifdef DEBUG + if (precedence[e->op] == PREC_zero) + printf("precedence not defined for token '%s'\n",Token::tochars[e->op]); +#endif + assert(precedence[e->op] != PREC_zero); + assert(pr != PREC_zero); + + //if (precedence[e->op] == 0) e->print(); + /* Despite precedence, we don't allow aop] < pr || + (pr == PREC_rel && precedence[e->op] == pr)) + { + buf->writeByte('('); + e->toCBuffer(buf, hgs); + buf->writeByte(')'); + } + else + e->toCBuffer(buf, hgs); +} + +/************************************************** + * Write out argument list to buf. + */ + +void argsToCBuffer(OutBuffer *buf, Expressions *expressions, HdrGenState *hgs) +{ + if (expressions) + { + for (size_t i = 0; i < expressions->dim; i++) + { Expression *e = (*expressions)[i]; + + if (i) + buf->writestring(", "); + if (e) + expToCBuffer(buf, hgs, e, PREC_assign); + } + } +} diff --git a/gcc/d/dfrontend/hdrgen.h b/gcc/d/dfrontend/hdrgen.h index cce63383b..67a34e014 100644 --- a/gcc/d/dfrontend/hdrgen.h +++ b/gcc/d/dfrontend/hdrgen.h @@ -1,15 +1,18 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2006 by Digital Mars -// All Rights Reserved -// initial header generation implementation by Dave Fladebo -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Dave Fladebo + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/expression.h + */ #include // memset() +void genhdrfile(Module *m); + struct HdrGenState { int hdrgen; // 1 if generating header file @@ -24,6 +27,7 @@ struct HdrGenState int inArrExp; int emitInst; int autoMember; + bool fullQualification; // fully qualify types when printing struct { @@ -33,4 +37,6 @@ struct HdrGenState Scope* scope; // Scope when generating ddoc HdrGenState() { memset(this, 0, sizeof(HdrGenState)); } -}; \ No newline at end of file +}; + +void functionToBufferFull(TypeFunction *tf, OutBuffer *buf, Identifier *ident, HdrGenState* hgs, TemplateDeclaration *td); diff --git a/gcc/d/dfrontend/identifier.c b/gcc/d/dfrontend/identifier.c index 94e532906..4ef461287 100644 --- a/gcc/d/dfrontend/identifier.c +++ b/gcc/d/dfrontend/identifier.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2006 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/identifier.c + */ #include #include @@ -32,12 +33,12 @@ Identifier *Identifier::create(const char *string, int value) bool Identifier::equals(RootObject *o) { - return this == o || memcmp(string,o->toChars(),len+1) == 0; + return this == o || strncmp(string,o->toChars(),len+1) == 0; } int Identifier::compare(RootObject *o) { - return memcmp(string, o->toChars(), len + 1); + return strncmp(string, o->toChars(), len + 1); } char *Identifier::toChars() @@ -60,11 +61,11 @@ const char *Identifier::toHChars2() { p = toChars(); if (*p == '_') { - if (memcmp(p, "_staticCtor", 11) == 0) + if (strncmp(p, "_staticCtor", 11) == 0) p = "static this"; - else if (memcmp(p, "_staticDtor", 11) == 0) + else if (strncmp(p, "_staticDtor", 11) == 0) p = "static ~this"; - else if (memcmp(p, "__invariant", 11) == 0) + else if (strncmp(p, "__invariant", 11) == 0) p = "invariant"; } } @@ -97,7 +98,6 @@ Identifier *Identifier::generateId(const char *prefix, size_t i) buf.writestring(prefix); buf.printf("%llu", (ulonglong)i); - char *id = buf.toChars(); - buf.data = NULL; + char *id = buf.peekString(); return Lexer::idPool(id); } diff --git a/gcc/d/dfrontend/identifier.h b/gcc/d/dfrontend/identifier.h index 21b622087..477bb5581 100644 --- a/gcc/d/dfrontend/identifier.h +++ b/gcc/d/dfrontend/identifier.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2006 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/identifier.h + */ #ifndef DMD_IDENTIFIER_H #define DMD_IDENTIFIER_H diff --git a/gcc/d/dfrontend/idgen.c b/gcc/d/dfrontend/idgen.c index 3f6bddfbd..f912a6b54 100644 --- a/gcc/d/dfrontend/idgen.c +++ b/gcc/d/dfrontend/idgen.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/idgen.c + */ // Program to generate string files in d data structures. // Saves much tedious typing, and eliminates typo problems. @@ -43,7 +44,6 @@ Msgtable msgtable[] = { "require", "__require" }, { "ensure", "__ensure" }, { "init" }, - { "size" }, { "__sizeof", "sizeof" }, { "__xalignof", "alignof" }, { "mangleof" }, @@ -64,28 +64,25 @@ Msgtable msgtable[] = { "typeinfo" }, { "outer" }, { "Exception" }, - { "AssociativeArray" }, { "RTInfo" }, { "Throwable" }, { "Error" }, { "withSym", "__withSym" }, { "result", "__result" }, { "returnLabel", "__returnLabel" }, - { "delegate" }, { "line" }, { "empty", "" }, { "p" }, { "q" }, - { "coverage", "__coverage" }, { "__vptr" }, { "__monitor" }, + { "gate", "__gate" }, { "TypeInfo" }, { "TypeInfo_Class" }, { "TypeInfo_Interface" }, { "TypeInfo_Struct" }, { "TypeInfo_Enum" }, - { "TypeInfo_Typedef" }, { "TypeInfo_Pointer" }, { "TypeInfo_Vector" }, { "TypeInfo_Array" }, @@ -102,11 +99,11 @@ Msgtable msgtable[] = { "_arguments_typeinfo" }, { "_arguments" }, { "_argptr" }, - { "_match" }, { "destroy" }, { "postblit" }, { "xopEquals", "__xopEquals" }, { "xopCmp", "__xopCmp" }, + { "xtoHash", "__xtoHash" }, { "LINE", "__LINE__" }, { "FILE", "__FILE__" }, @@ -149,10 +146,9 @@ Msgtable msgtable[] = { "sort" }, { "reverse" }, - { "dup" }, - { "idup" }, { "property" }, + { "nogc" }, { "safe" }, { "trusted" }, { "system" }, @@ -216,8 +212,6 @@ Msgtable msgtable[] = { "sliceass", "opSliceAssign" }, { "call", "opCall" }, { "cast", "opCast" }, - { "match", "opMatch" }, - { "next", "opNext" }, { "opIn" }, { "opIn_r" }, { "opStar" }, @@ -250,7 +244,6 @@ Msgtable msgtable[] = { "FpopFront", "popFront" }, { "FpopBack", "popBack" }, - { "adDup", "_adDupT" }, { "adReverse", "_adReverse" }, // For internal functions @@ -276,7 +269,7 @@ Msgtable msgtable[] = { "getmembers", "getMembers" }, // Special functions - { "__alloca", "alloca" }, // has to be mapped because alloca is #defined if _MSC_VER + { "__alloca", "alloca" }, { "main" }, { "WinMain" }, { "DllMain" }, @@ -345,6 +338,7 @@ Msgtable msgtable[] = { "parameters" }, { "getAliasThis" }, { "getAttributes" }, + { "getFunctionAttributes" }, { "getUnitTests" }, { "getVirtualIndex" } }; diff --git a/gcc/d/dfrontend/impcnvgen.c b/gcc/d/dfrontend/impcnvgen.c index 298ed3792..c24fc1388 100644 --- a/gcc/d/dfrontend/impcnvgen.c +++ b/gcc/d/dfrontend/impcnvgen.c @@ -1,11 +1,13 @@ -// Copyright (c) 1999-2006 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/impcnvgen.c + */ #include #include diff --git a/gcc/d/dfrontend/imphint.c b/gcc/d/dfrontend/imphint.c index 24f9373a0..eda1d643d 100644 --- a/gcc/d/dfrontend/imphint.c +++ b/gcc/d/dfrontend/imphint.c @@ -1,13 +1,14 @@ +/* Compiler implementation of the D programming language + * Copyright (c) 2010-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/imphint.c + */ -// Compiler implementation of the D programming language -// Copyright (c) 2010 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. #include #include diff --git a/gcc/d/dfrontend/import.c b/gcc/d/dfrontend/import.c index e05e9e7b8..3a5c8041d 100644 --- a/gcc/d/dfrontend/import.c +++ b/gcc/d/dfrontend/import.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/import.c + */ #include #include @@ -52,15 +53,21 @@ Import::Import(Loc loc, Identifiers *packages, Identifier *id, Identifier *alias this->mod = NULL; // Set symbol name (bracketed) - // import [cstdio] = std.stdio; if (aliasId) + { + // import [cstdio] = std.stdio; this->ident = aliasId; - // import [std].stdio; + } else if (packages && packages->dim) + { + // import [std].stdio; this->ident = (*packages)[0]; - // import [foo]; + } else + { + // import [foo]; this->ident = id; + } } void Import::addAlias(Identifier *name, Identifier *alias) @@ -135,11 +142,11 @@ void Import::load(Scope *sc) else assert(p->isPkgMod == PKGmodule); } - else if (p->isPkgMod == PKGmodule) + else { - mod = p->mod; + mod = p->isPackageMod(); } - if (p->isPkgMod != PKGmodule) + if (!mod) { ::error(loc, "can only import from a module, not from package %s.%s", p->toPrettyChars(), id->toChars()); @@ -183,6 +190,15 @@ void Import::importAll(Scope *sc) load(sc); if (mod) // if successfully loaded module { + if (mod->md && mod->md->isdeprecated) + { + Expression *msg = mod->md->msg; + if (StringExp *se = msg ? msg->toStringExp() : NULL) + mod->deprecation(loc, "is deprecated - %s", se->string); + else + mod->deprecation(loc, "is deprecated"); + } + mod->importAll(NULL); if (!isstatic && !aliasId && !names.dim) @@ -271,10 +287,10 @@ void Import::semantic(Scope *sc) sc = sc->pop(); } + // object self-imports itself, so skip that (Bugzilla 7547) + // don't list pseudo modules __entrypoint.d, __main.d (Bugzilla 11117, 11164) if (global.params.moduleDeps != NULL && - // object self-imports itself, so skip that (Bugzilla 7547) !(id == Id::object && sc->module->ident == Id::object) && - // don't list pseudo modules __entrypoint.d, __main.d (Bugzilla 11117, 11164) sc->module->ident != Id::entrypoint && strcmp(sc->module->ident->string, "__main") != 0) { @@ -302,7 +318,8 @@ void Import::semantic(Scope *sc) // use protection instead of sc->protection because it couldn't be // resolved yet, see the comment above - ProtDeclaration::protectionToCBuffer(ob, protection); + protectionToBuffer(ob, protection); + ob->writeByte(' '); if (isstatic) StorageClassDeclaration::stcToCBuffer(ob, STCstatic); ob->writestring(": "); @@ -322,14 +339,14 @@ void Import::semantic(Scope *sc) escapePath(ob, mod->srcfile->toChars()); else ob->writestring("???"); - ob->writebyte(')'); + ob->writeByte(')'); for (size_t i = 0; i < names.dim; i++) { if (i == 0) - ob->writebyte(':'); + ob->writeByte(':'); else - ob->writebyte(','); + ob->writeByte(','); Identifier *name = names[i]; Identifier *alias = aliases[i]; diff --git a/gcc/d/dfrontend/import.h b/gcc/d/dfrontend/import.h index 6701c8350..001450f87 100644 --- a/gcc/d/dfrontend/import.h +++ b/gcc/d/dfrontend/import.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/import.h + */ #ifndef DMD_IMPORT_H #define DMD_IMPORT_H @@ -42,15 +43,14 @@ class Import : public Dsymbol Identifiers names; Identifiers aliases; - Import(Loc loc, Identifiers *packages, Identifier *id, Identifier *aliasId, - int isstatic); - void addAlias(Identifier *name, Identifier *alias); - - AliasDeclarations aliasdecls; // corresponding AliasDeclarations for alias=name pairs - Module *mod; Package *pkg; // leftmost package/module + AliasDeclarations aliasdecls; // corresponding AliasDeclarations for alias=name pairs + + Import(Loc loc, Identifiers *packages, Identifier *id, Identifier *aliasId, + int isstatic); + void addAlias(Identifier *name, Identifier *alias); const char *kind(); PROT prot(); Dsymbol *syntaxCopy(Dsymbol *s); // copy only syntax trees @@ -59,7 +59,7 @@ class Import : public Dsymbol void semantic(Scope *sc); void semantic2(Scope *sc); Dsymbol *toAlias(); - int addMember(Scope *sc, ScopeDsymbol *s, int memnum); + int addMember(Scope *sc, ScopeDsymbol *sds, int memnum); Dsymbol *search(Loc loc, Identifier *ident, int flags = IgnoreNone); bool overloadInsert(Dsymbol *s); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); diff --git a/gcc/d/dfrontend/init.c b/gcc/d/dfrontend/init.c index f2e2132ae..8652e4e6e 100644 --- a/gcc/d/dfrontend/init.c +++ b/gcc/d/dfrontend/init.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/init.c + */ #include #include @@ -31,32 +32,16 @@ Initializer::Initializer(Loc loc) this->loc = loc; } -Initializer *Initializer::syntaxCopy() -{ - return this; -} - -Initializer *Initializer::semantic(Scope *sc, Type *t, NeedInterpret needInterpret) -{ - return this; -} - -Type *Initializer::inferType(Scope *sc) -{ - error(loc, "cannot infer type from initializer"); - return Type::terror; -} - Initializers *Initializer::arraySyntaxCopy(Initializers *ai) -{ Initializers *a = NULL; - +{ + Initializers *a = NULL; if (ai) { a = new Initializers(); a->setDim(ai->dim); for (size_t i = 0; i < a->dim; i++) - { Initializer *e = (*ai)[i]; - + { + Initializer *e = (*ai)[i]; e = e->syntaxCopy(); (*a)[i] = e; } @@ -70,8 +55,7 @@ char *Initializer::toChars() OutBuffer buf; toCBuffer(&buf, &hgs); - buf.writebyte(0); - return buf.extractData(); + return buf.extractString(); } /********************************** ErrorInitializer ***************************/ @@ -81,12 +65,15 @@ ErrorInitializer::ErrorInitializer() { } - Initializer *ErrorInitializer::syntaxCopy() { return this; } +Initializer *ErrorInitializer::inferType(Scope *sc) +{ + return this; +} Initializer *ErrorInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInterpret) { @@ -94,19 +81,16 @@ Initializer *ErrorInitializer::semantic(Scope *sc, Type *t, NeedInterpret needIn return this; } - Expression *ErrorInitializer::toExpression(Type *t) { return new ErrorExp(); } - void ErrorInitializer::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring("__error__"); } - /********************************** VoidInitializer ***************************/ VoidInitializer::VoidInitializer(Loc loc) @@ -115,12 +99,16 @@ VoidInitializer::VoidInitializer(Loc loc) type = NULL; } - Initializer *VoidInitializer::syntaxCopy() { return new VoidInitializer(loc); } +Initializer *VoidInitializer::inferType(Scope *sc) +{ + error(loc, "cannot infer type from void initializer"); + return new ErrorInitializer(); +} Initializer *VoidInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInterpret) { @@ -129,19 +117,16 @@ Initializer *VoidInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInt return this; } - Expression *VoidInitializer::toExpression(Type *t) { return NULL; } - void VoidInitializer::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring("void"); } - /********************************** StructInitializer *************************/ StructInitializer::StructInitializer(Loc loc) @@ -160,9 +145,9 @@ Initializer *StructInitializer::syntaxCopy() { ai->field[i] = field[i]; - Initializer *init = value[i]; - init = init->syntaxCopy(); - ai->value[i] = init; + Initializer *iz = value[i]; + iz = iz->syntaxCopy(); + ai->value[i] = iz; } return ai; } @@ -174,6 +159,12 @@ void StructInitializer::addInit(Identifier *field, Initializer *value) this->value.push(value); } +Initializer *StructInitializer::inferType(Scope *sc) +{ + error(loc, "cannot infer type from struct initializer"); + return new ErrorInitializer(); +} + Initializer *StructInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInterpret) { //printf("StructInitializer::semantic(t = %s) %s\n", t->toChars(), toChars()); @@ -318,7 +309,7 @@ Expression *StructInitializer::toExpression(Type *t) void StructInitializer::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { //printf("StructInitializer::toCBuffer()\n"); - buf->writebyte('{'); + buf->writeByte('{'); for (size_t i = 0; i < field.dim; i++) { if (i > 0) @@ -327,13 +318,13 @@ void StructInitializer::toCBuffer(OutBuffer *buf, HdrGenState *hgs) if (id) { buf->writestring(id->toChars()); - buf->writebyte(':'); + buf->writeByte(':'); } Initializer *iz = value[i]; if (iz) iz->toCBuffer(buf, hgs); } - buf->writebyte('}'); + buf->writeByte('}'); } /********************************** ArrayInitializer ************************************/ @@ -343,7 +334,7 @@ ArrayInitializer::ArrayInitializer(Loc loc) { dim = 0; type = NULL; - sem = 0; + sem = false; } Initializer *ArrayInitializer::syntaxCopy() @@ -356,14 +347,15 @@ Initializer *ArrayInitializer::syntaxCopy() ai->index.setDim(index.dim); ai->value.setDim(value.dim); for (size_t i = 0; i < ai->value.dim; i++) - { Expression *e = index[i]; + { + Expression *e = index[i]; if (e) e = e->syntaxCopy(); ai->index[i] = e; - Initializer *init = value[i]; - init = init->syntaxCopy(); - ai->value[i] = init; + Initializer *iz = value[i]; + iz = iz->syntaxCopy(); + ai->value[i] = iz; } return ai; } @@ -376,6 +368,89 @@ void ArrayInitializer::addInit(Expression *index, Initializer *value) type = NULL; } +bool ArrayInitializer::isAssociativeArray() +{ + for (size_t i = 0; i < value.dim; i++) + { + if (index[i]) + return true; + } + return false; +} + +Initializer *ArrayInitializer::inferType(Scope *sc) +{ + //printf("ArrayInitializer::inferType() %s\n", toChars()); + Expressions *keys = NULL; + Expressions *values; + if (isAssociativeArray()) + { + keys = new Expressions(); + keys->setDim(value.dim); + values = new Expressions(); + values->setDim(value.dim); + + for (size_t i = 0; i < value.dim; i++) + { + Expression *e = index[i]; + if (!e) + goto Lno; + (*keys)[i] = e; + + Initializer *iz = value[i]; + if (!iz) + goto Lno; + iz = iz->inferType(sc); + if (iz->isErrorInitializer()) + return iz; + assert(iz->isExpInitializer()); + (*values)[i] = ((ExpInitializer *)iz)->exp; + assert((*values)[i]->op != TOKerror); + } + + Expression *e = new AssocArrayLiteralExp(loc, keys, values); + ExpInitializer *ei = new ExpInitializer(loc, e); + return ei->inferType(sc); + } + else + { + Expressions *elements = new Expressions(); + elements->setDim(value.dim); + elements->zero(); + + for (size_t i = 0; i < value.dim; i++) + { + assert(!index[i]); // already asserted by isAssociativeArray() + + Initializer *iz = value[i]; + if (!iz) + goto Lno; + iz = iz->inferType(sc); + if (iz->isErrorInitializer()) + return iz; + assert(iz->isExpInitializer()); + (*elements)[i] = ((ExpInitializer *)iz)->exp; + assert((*elements)[i]->op != TOKerror); + } + + Expression *e = new ArrayLiteralExp(loc, elements); + ExpInitializer *ei = new ExpInitializer(loc, e); + return ei->inferType(sc); + } +Lno: + if (keys) + { + delete keys; + delete values; + error(loc, "not an associative array initializer"); + } + else + { + error(loc, "cannot infer type from array initializer"); + } + return new ErrorInitializer(); +} + Initializer *ArrayInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInterpret) { size_t length; @@ -385,7 +460,7 @@ Initializer *ArrayInitializer::semantic(Scope *sc, Type *t, NeedInterpret needIn //printf("ArrayInitializer::semantic(%s)\n", t->toChars()); if (sem) // if semantic() already run return this; - sem = 1; + sem = true; t = t->toBasetype(); switch (t->ty) { @@ -438,7 +513,7 @@ Initializer *ArrayInitializer::semantic(Scope *sc, Type *t, NeedInterpret needIn Initializer *val = value[i]; ExpInitializer *ei = val->isExpInitializer(); if (ei && !idx) - ei->expandTuples = 1; + ei->expandTuples = true; val = val->semantic(sc, t->nextOf(), needInterpret); if (val->isErrorInitializer()) errors = true; @@ -467,7 +542,8 @@ Initializer *ArrayInitializer::semantic(Scope *sc, Type *t, NeedInterpret needIn length++; if (length == 0) - { error(loc, "array dimension overflow"); + { + error(loc, "array dimension overflow"); goto Lerr; } if (length > dim) @@ -486,7 +562,8 @@ Initializer *ArrayInitializer::semantic(Scope *sc, Type *t, NeedInterpret needIn goto Lerr; if ((uinteger_t) dim * t->nextOf()->size() >= amax) - { error(loc, "array dimension %u exceeds max of %u", (unsigned) dim, (unsigned)(amax / t->nextOf()->size())); + { + error(loc, "array dimension %u exceeds max of %u", (unsigned) dim, (unsigned)(amax / t->nextOf()->size())); goto Lerr; } return this; @@ -587,7 +664,8 @@ Expression *ArrayInitializer::toExpression(Type *tx) } for (size_t i = 0; i < edim; i++) - { Expression *e = (*elements)[i]; + { + Expression *e = (*elements)[i]; if (e->op == TOKerror) return e; } @@ -601,7 +679,6 @@ Expression *ArrayInitializer::toExpression(Type *tx) return NULL; } - /******************************** * If possible, convert array initializer to associative array initializer. */ @@ -642,68 +719,9 @@ Expression *ArrayInitializer::toAssocArrayLiteral() return new ErrorExp(); } -int ArrayInitializer::isAssociativeArray() -{ - for (size_t i = 0; i < value.dim; i++) - { - if (index[i]) - return 1; - } - return 0; -} - -Type *ArrayInitializer::inferType(Scope *sc) -{ - //printf("ArrayInitializer::inferType() %s\n", toChars()); - assert(0); - return NULL; -#if 0 - type = Type::terror; - for (size_t i = 0; i < value.dim; i++) - { - if (index.data[i]) - goto Laa; - } - for (size_t i = 0; i < value.dim; i++) - { - Initializer *iz = (Initializer *)value.data[i]; - if (iz) - { Type *t = iz->inferType(sc); - if (i == 0) - { /* BUG: This gets the type from the first element. - * Fix to use all the elements to figure out the type. - */ - t = new TypeSArray(t, new IntegerExp(value.dim)); - t = t->semantic(loc, sc); - type = t; - } - } - } - return type; - -Laa: - /* It's possibly an associative array initializer. - * BUG: inferring type from first member. - */ - Initializer *iz = (Initializer *)value.data[0]; - Expression *indexinit = (Expression *)index.data[0]; - if (iz && indexinit) - { Type *t = iz->inferType(sc); - indexinit = indexinit->semantic(sc); - Type *indext = indexinit->type; - t = new TypeAArray(t, indext); - type = t->semantic(loc, sc); - } - else - error(loc, "cannot infer type from this array initializer"); - return type; -#endif -} - - void ArrayInitializer::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { - buf->writebyte('['); + buf->writeByte('['); for (size_t i = 0; i < index.dim; i++) { if (i > 0) @@ -712,23 +730,22 @@ void ArrayInitializer::toCBuffer(OutBuffer *buf, HdrGenState *hgs) if (ex) { ex->toCBuffer(buf, hgs); - buf->writebyte(':'); + buf->writeByte(':'); } Initializer *iz = value[i]; if (iz) iz->toCBuffer(buf, hgs); } - buf->writebyte(']'); + buf->writeByte(']'); } - /********************************** ExpInitializer ************************************/ ExpInitializer::ExpInitializer(Loc loc, Expression *exp) : Initializer(loc) { this->exp = exp; - this->expandTuples = 0; + this->expandTuples = false; } Initializer *ExpInitializer::syntaxCopy() @@ -736,6 +753,7 @@ Initializer *ExpInitializer::syntaxCopy() return new ExpInitializer(loc, exp->syntaxCopy()); } +#if 1 // should be removed and rely on ctfeInterpreter() bool arrayHasNonConstPointers(Expressions *elems); bool hasNonConstPointers(Expression *e) @@ -804,14 +822,69 @@ bool hasNonConstPointers(Expression *e) bool arrayHasNonConstPointers(Expressions *elems) { for (size_t i = 0; i < elems->dim; i++) - { Expression *e = (*elems)[i]; + { + Expression *e = (*elems)[i]; if (e && hasNonConstPointers(e)) return true; } return false; } +#endif +Initializer *ExpInitializer::inferType(Scope *sc) +{ + //printf("ExpInitializer::inferType() %s\n", toChars()); + exp = exp->semantic(sc); + exp = resolveProperties(sc, exp); + if (exp->op == TOKimport) + { + ScopeExp *se = (ScopeExp *)exp; + TemplateInstance *ti = se->sds->isTemplateInstance(); + if (ti && ti->semanticRun == PASSsemantic && !ti->aliasdecl) + se->error("cannot infer type from %s %s, possible circular dependency", se->sds->kind(), se->toChars()); + else + se->error("cannot infer type from %s %s", se->sds->kind(), se->toChars()); + return new ErrorInitializer(); + } + + // Give error for overloaded function addresses + if (exp->op == TOKsymoff) + { + SymOffExp *se = (SymOffExp *)exp; + if (se->hasOverloads && !se->var->isFuncDeclaration()->isUnique()) + { + exp->error("cannot infer type from overloaded function symbol %s", exp->toChars()); + return new ErrorInitializer(); + } + } + if (exp->op == TOKdelegate) + { + DelegateExp *se = (DelegateExp *)exp; + if (se->hasOverloads && + se->func->isFuncDeclaration() && + !se->func->isFuncDeclaration()->isUnique()) + { + exp->error("cannot infer type from overloaded function symbol %s", exp->toChars()); + return new ErrorInitializer(); + } + } + if (exp->op == TOKaddress) + { + AddrExp *ae = (AddrExp *)exp; + if (ae->e1->op == TOKoverloadset) + { + exp->error("cannot infer type from overloaded function symbol %s", exp->toChars()); + return new ErrorInitializer(); + } + } + + if (exp->op == TOKerror) + return new ErrorInitializer(); + if (!exp->type) + return new ErrorInitializer(); + return this; +} Initializer *ExpInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInterpret) { @@ -823,11 +896,22 @@ Initializer *ExpInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInte if (exp->op == TOKerror) return new ErrorInitializer(); - int olderrors = global.errors; + unsigned int olderrors = global.errors; if (needInterpret) + { + // If the result will be implicitly cast, move the cast into CTFE + // to avoid premature truncation of polysemous types. + // eg real [] x = [1.1, 2.2]; should use real precision. + if (exp->implicitConvTo(t)) + { + exp = exp->implicitCastTo(sc, t); + } exp = exp->ctfeInterpret(); + } else + { exp = exp->optimize(WANTvalue); + } if (!global.gag && olderrors != global.errors) return this; // Failed, suppress duplicate error messages @@ -847,9 +931,7 @@ Initializer *ExpInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInte Type *tb = t->toBasetype(); Type *ti = exp->type->toBasetype(); - if (exp->op == TOKtuple && - expandTuples && - !exp->implicitConvTo(t)) + if (exp->op == TOKtuple && expandTuples && !exp->implicitConvTo(t)) return new ExpInitializer(loc, exp); /* Look for case of initializing a static array with a too-short @@ -859,8 +941,8 @@ Initializer *ExpInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInte * literal. */ if (exp->op == TOKstring && tb->ty == Tsarray && ti->ty == Tsarray) - { StringExp *se = (StringExp *)exp; - + { + StringExp *se = (StringExp *)exp; if (!se->committed && se->type->ty == Tsarray && ((TypeSArray *)se->type)->dim->toInteger() < ((TypeSArray *)t)->dim->toInteger()) @@ -877,7 +959,8 @@ Initializer *ExpInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInte { StructDeclaration *sd = ((TypeStruct *)tb)->sym; if (sd->ctor) - { // Rewrite as S().ctor(exp) + { + // Rewrite as S().ctor(exp) Expression *e; e = new StructLiteralExp(loc, sd, NULL); e = new DotIdExp(loc, e, Id::ctor); @@ -903,7 +986,38 @@ Initializer *ExpInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInte t = tb->nextOf(); } - exp = exp->implicitCastTo(sc, t); + if (exp->implicitConvTo(t)) + { + exp = exp->implicitCastTo(sc, t); + } + else + { + // Look for mismatch of compile-time known length to emit + // better diagnostic message, as same as AssignExp::semantic. + if (tb->ty == Tsarray && + exp->implicitConvTo(tb->nextOf()->arrayOf()) > MATCHnomatch) + { + uinteger_t dim1 = ((TypeSArray *)tb)->dim->toInteger(); + uinteger_t dim2 = dim1; + if (exp->op == TOKarrayliteral) + { + ArrayLiteralExp *ale = (ArrayLiteralExp *)exp; + dim2 = ale->elements ? ale->elements->dim : 0; + } + else if (exp->op == TOKslice) + { + Type *tx = toStaticArrayType((SliceExp *)exp); + if (tx) + dim2 = ((TypeSArray *)tx)->dim->toInteger(); + } + if (dim1 != dim2) + { + exp->error("mismatched array lengths, %d and %d", (int)dim1, (int)dim2); + exp = new ErrorExp(); + } + } + exp = exp->implicitCastTo(sc, t); + } if (exp->op == TOKerror) return this; L1: @@ -915,84 +1029,30 @@ Initializer *ExpInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInte return this; } -Type *ExpInitializer::inferType(Scope *sc) -{ - //printf("ExpInitializer::inferType() %s\n", toChars()); - exp = exp->semantic(sc); - exp = resolveProperties(sc, exp); - if (exp->op == TOKimport) - { ScopeExp *se = (ScopeExp *)exp; - TemplateInstance *ti = se->sds->isTemplateInstance(); - if (ti && ti->semanticRun == PASSsemantic && !ti->aliasdecl) - se->error("cannot infer type from %s %s, possible circular dependency", se->sds->kind(), se->toChars()); - else - se->error("cannot infer type from %s %s", se->sds->kind(), se->toChars()); - return Type::terror; - } - - // Give error for overloaded function addresses - if (exp->op == TOKsymoff) - { - SymOffExp *se = (SymOffExp *)exp; - if (se->hasOverloads && !se->var->isFuncDeclaration()->isUnique()) - { - exp->error("cannot infer type from overloaded function symbol %s", exp->toChars()); - return Type::terror; - } - } - if (exp->op == TOKdelegate) - { - DelegateExp *se = (DelegateExp *)exp; - if (se->hasOverloads && - se->func->isFuncDeclaration() && - !se->func->isFuncDeclaration()->isUnique()) - { - exp->error("cannot infer type from overloaded function symbol %s", exp->toChars()); - return Type::terror; - } - } - if (exp->op == TOKaddress) - { - AddrExp *ae = (AddrExp *)exp; - if (ae->e1->op == TOKoverloadset) - { - exp->error("cannot infer type from overloaded function symbol %s", exp->toChars()); - return Type::terror; - } - } - - Type *t = exp->type; - if (!t) - t = Initializer::inferType(sc); - return t; -} - Expression *ExpInitializer::toExpression(Type *t) { if (t) { + //printf("ExpInitializer::toExpression(t = %s) exp = %s\n", t->toChars(), exp->toChars()); Type *tb = t->toBasetype(); - if (tb->ty == Tsarray && exp->implicitConvTo(tb->nextOf())) + Expression *e = (exp->op == TOKconstruct || exp->op == TOKblit) ? ((AssignExp *)exp)->e2 : exp; + if (tb->ty == Tsarray && e->implicitConvTo(tb->nextOf())) { TypeSArray *tsa = (TypeSArray *)tb; size_t d = (size_t)tsa->dim->toInteger(); Expressions *elements = new Expressions(); elements->setDim(d); for (size_t i = 0; i < d; i++) - (*elements)[i] = exp; - ArrayLiteralExp *ae = new ArrayLiteralExp(exp->loc, elements); + (*elements)[i] = e; + ArrayLiteralExp *ae = new ArrayLiteralExp(e->loc, elements); ae->type = t; - exp = ae; + return ae; } } return exp; } - void ExpInitializer::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { exp->toCBuffer(buf, hgs); } - - - diff --git a/gcc/d/dfrontend/init.h b/gcc/d/dfrontend/init.h index 79dedd3e8..58a368a50 100644 --- a/gcc/d/dfrontend/init.h +++ b/gcc/d/dfrontend/init.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/init.h + */ #ifndef INIT_H #define INIT_H @@ -15,6 +16,7 @@ #include "mars.h" #include "arraytypes.h" +#include "visitor.h" class Identifier; class Expression; @@ -41,16 +43,20 @@ class Initializer : public RootObject Loc loc; Initializer(Loc loc); - virtual Initializer *syntaxCopy(); + virtual Initializer *syntaxCopy() = 0; + static Initializers *arraySyntaxCopy(Initializers *ai); + + /* Translates to an expression to infer type. + * Returns ExpInitializer or ErrorInitializer. + */ + virtual Initializer *inferType(Scope *sc) = 0; + // needInterpret is INITinterpret if must be a manifest constant, 0 if not. - virtual Initializer *semantic(Scope *sc, Type *t, NeedInterpret needInterpret); - virtual Type *inferType(Scope *sc); + virtual Initializer *semantic(Scope *sc, Type *t, NeedInterpret needInterpret) = 0; virtual Expression *toExpression(Type *t = NULL) = 0; virtual void toCBuffer(OutBuffer *buf, HdrGenState *hgs) = 0; char *toChars(); - static Initializers *arraySyntaxCopy(Initializers *ai); - virtual dt_t *toDt(); virtual ErrorInitializer *isErrorInitializer() { return NULL; } @@ -58,6 +64,7 @@ class Initializer : public RootObject virtual StructInitializer *isStructInitializer() { return NULL; } virtual ArrayInitializer *isArrayInitializer() { return NULL; } virtual ExpInitializer *isExpInitializer() { return NULL; } + virtual void accept(Visitor *v) { v->visit(this); } }; class VoidInitializer : public Initializer @@ -67,6 +74,7 @@ class VoidInitializer : public Initializer VoidInitializer(Loc loc); Initializer *syntaxCopy(); + Initializer *inferType(Scope *sc); Initializer *semantic(Scope *sc, Type *t, NeedInterpret needInterpret); Expression *toExpression(Type *t = NULL); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); @@ -74,6 +82,7 @@ class VoidInitializer : public Initializer dt_t *toDt(); virtual VoidInitializer *isVoidInitializer() { return this; } + void accept(Visitor *v) { v->visit(this); } }; class ErrorInitializer : public Initializer @@ -81,11 +90,13 @@ class ErrorInitializer : public Initializer public: ErrorInitializer(); Initializer *syntaxCopy(); + Initializer *inferType(Scope *sc); Initializer *semantic(Scope *sc, Type *t, NeedInterpret needInterpret); Expression *toExpression(Type *t = NULL); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); virtual ErrorInitializer *isErrorInitializer() { return this; } + void accept(Visitor *v) { v->visit(this); } }; class StructInitializer : public Initializer @@ -97,6 +108,7 @@ class StructInitializer : public Initializer StructInitializer(Loc loc); Initializer *syntaxCopy(); void addInit(Identifier *field, Initializer *value); + Initializer *inferType(Scope *sc); Initializer *semantic(Scope *sc, Type *t, NeedInterpret needInterpret); Expression *toExpression(Type *t = NULL); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); @@ -104,6 +116,7 @@ class StructInitializer : public Initializer dt_t *toDt(); StructInitializer *isStructInitializer() { return this; } + void accept(Visitor *v) { v->visit(this); } }; class ArrayInitializer : public Initializer @@ -113,14 +126,14 @@ class ArrayInitializer : public Initializer Initializers value; // of Initializer *'s size_t dim; // length of array being initialized Type *type; // type that array will be used to initialize - int sem; // !=0 if semantic() is run + bool sem; // true if semantic() is run ArrayInitializer(Loc loc); Initializer *syntaxCopy(); void addInit(Expression *index, Initializer *value); + bool isAssociativeArray(); + Initializer *inferType(Scope *sc); Initializer *semantic(Scope *sc, Type *t, NeedInterpret needInterpret); - int isAssociativeArray(); - Type *inferType(Scope *sc); Expression *toExpression(Type *t = NULL); Expression *toAssocArrayLiteral(); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); @@ -128,24 +141,26 @@ class ArrayInitializer : public Initializer dt_t *toDt(); ArrayInitializer *isArrayInitializer() { return this; } + void accept(Visitor *v) { v->visit(this); } }; class ExpInitializer : public Initializer { public: Expression *exp; - int expandTuples; + bool expandTuples; ExpInitializer(Loc loc, Expression *exp); Initializer *syntaxCopy(); + Initializer *inferType(Scope *sc); Initializer *semantic(Scope *sc, Type *t, NeedInterpret needInterpret); - Type *inferType(Scope *sc); Expression *toExpression(Type *t = NULL); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); dt_t *toDt(); virtual ExpInitializer *isExpInitializer() { return this; } + void accept(Visitor *v) { v->visit(this); } }; #endif diff --git a/gcc/d/dfrontend/inline.c b/gcc/d/dfrontend/inline.c index 8808a6dd1..5634dbbc1 100644 --- a/gcc/d/dfrontend/inline.c +++ b/gcc/d/dfrontend/inline.c @@ -1,11 +1,13 @@ -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/inline.c + */ // Routines to perform function inlining @@ -24,6 +26,14 @@ #include "statement.h" #include "mtype.h" #include "scope.h" +#include "attrib.h" +#include "template.h" +#include "module.h" + +static Expression *expandInline(FuncDeclaration *fd, FuncDeclaration *parent, + Expression *eret, Expression *ethis, Expressions *arguments, Statement **ps); +bool walkPostorder(Expression *e, StoppableVisitor *v); +int canInline(FuncDeclaration *fd, int hasthis, int hdrscan, int statementsToo); /* ========== Compute cost of inlining =============== */ @@ -31,14 +41,6 @@ * if it is too complex to be worth inlining or not. */ -struct InlineCostState -{ - int nested; - int hasthis; - int hdrscan; // !=0 if inline scan for 'header' content - FuncDeclaration *fd; -}; - const int COST_MAX = 250; const int STATEMENT_COST = 0x1000; const int STATEMENT_COST_MAX = 250 * 0x1000; @@ -49,285 +51,333 @@ const int STATEMENT_COST_MAX = 250 * 0x1000; bool tooCostly(int cost) { return ((cost & (STATEMENT_COST - 1)) >= COST_MAX); } -int expressionInlineCost(Expression *e, InlineCostState *ics); - -int Statement::inlineCost(InlineCostState *ics) +class InlineCostVisitor : public Visitor { - //printf("Statement::inlineCost = %d\n", COST_MAX); - //printf("%p\n", isScopeStatement()); - //printf("%s\n", toChars()); - return COST_MAX; // default is we can't inline it -} +public: + int nested; + int hasthis; + int hdrscan; // !=0 if inline scan for 'header' content + bool allowAlloca; + FuncDeclaration *fd; + int cost; -int ExpStatement::inlineCost(InlineCostState *ics) -{ - return expressionInlineCost(exp, ics); - //return exp ? exp->inlineCost(ics) : 0; -} + InlineCostVisitor() + { + nested = 0; + hasthis = 0; + hdrscan = 0; + allowAlloca = false; + fd = NULL; + cost = 0; + } + + InlineCostVisitor(InlineCostVisitor *icv) + { + nested = icv->nested; + hasthis = icv->hasthis; + hdrscan = icv->hdrscan; + allowAlloca = icv->allowAlloca; + fd = icv->fd; + cost = 0; // zero start for subsequent AST + } + + void visit(Statement *s) + { + //printf("Statement::inlineCost = %d\n", COST_MAX); + //printf("%p\n", s->isScopeStatement()); + //printf("%s\n", s->toChars()); + cost += COST_MAX; // default is we can't inline it + } -int CompoundStatement::inlineCost(InlineCostState *ics) -{ int cost = 0; + void visit(ExpStatement *s) + { + expressionInlineCost(s->exp); + } - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; - if (s) + void visit(CompoundStatement *s) + { + InlineCostVisitor icv(this); + for (size_t i = 0; i < s->statements->dim; i++) { - cost += s->inlineCost(ics); - if (tooCostly(cost)) - break; + Statement *s2 = (*s->statements)[i]; + if (s2) + { + s2->accept(&icv); + if (tooCostly(icv.cost)) + break; + } } + cost += icv.cost; } - //printf("CompoundStatement::inlineCost = %d\n", cost); - return cost; -} - -int UnrolledLoopStatement::inlineCost(InlineCostState *ics) -{ int cost = 0; - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; - if (s) + void visit(UnrolledLoopStatement *s) + { + InlineCostVisitor icv(this); + for (size_t i = 0; i < s->statements->dim; i++) { - cost += s->inlineCost(ics); - if (tooCostly(cost)) - break; + Statement *s2 = (*s->statements)[i]; + if (s2) + { + s2->accept(&icv); + if (tooCostly(icv.cost)) + break; + } } + cost += icv.cost; } - return cost; -} -int ScopeStatement::inlineCost(InlineCostState *ics) -{ - return statement ? 1 + statement->inlineCost(ics) : 1; -} + void visit(ScopeStatement *s) + { + cost++; + if (s->statement) + s->statement->accept(this); + } -int IfStatement::inlineCost(InlineCostState *ics) -{ - int cost; + void visit(IfStatement *s) + { + /* Can't declare variables inside ?: expressions, so + * we cannot inline if a variable is declared. + */ + if (s->arg) + { + cost = COST_MAX; + return; + } - /* Can't declare variables inside ?: expressions, so - * we cannot inline if a variable is declared. - */ - if (arg) - return COST_MAX; + expressionInlineCost(s->condition); - cost = expressionInlineCost(condition, ics); + /* Specifically allow: + * if (condition) + * return exp1; + * else + * return exp2; + * Otherwise, we can't handle return statements nested in if's. + */ - /* Specifically allow: - * if (condition) - * return exp1; - * else - * return exp2; - * Otherwise, we can't handle return statements nested in if's. - */ + if (s->elsebody && s->ifbody && + s->ifbody->isReturnStatement() && + s->elsebody->isReturnStatement()) + { + s->ifbody->accept(this); + s->elsebody->accept(this); + //printf("cost = %d\n", cost); + } + else + { + nested += 1; + if (s->ifbody) + s->ifbody->accept(this); + if (s->elsebody) + s->elsebody->accept(this); + nested -= 1; + } + //printf("IfStatement::inlineCost = %d\n", cost); + } - if (elsebody && ifbody && - ifbody->isReturnStatement() && - elsebody->isReturnStatement()) + void visit(ReturnStatement *s) { - cost += ifbody->inlineCost(ics); - cost += elsebody->inlineCost(ics); - //printf("cost = %d\n", cost); + // Can't handle return statements nested in if's + if (nested) + { + cost = COST_MAX; + } + else + { + expressionInlineCost(s->exp); + } } - else + + void visit(ImportStatement *s) { - ics->nested += 1; - if (ifbody) - cost += ifbody->inlineCost(ics); - if (elsebody) - cost += elsebody->inlineCost(ics); - ics->nested -= 1; } - //printf("IfStatement::inlineCost = %d\n", cost); - return cost; -} -int ReturnStatement::inlineCost(InlineCostState *ics) -{ - // Can't handle return statements nested in if's - if (ics->nested) - return COST_MAX; - return expressionInlineCost(exp, ics); -} + void visit(ForStatement *s) + { + cost += STATEMENT_COST; + if (s->init) + s->init->accept(this); + if (s->condition) + s->condition->accept(this); + if (s->increment) + s->increment->accept(this); + if (s->body) + s->body->accept(this); + //printf("ForStatement: inlineCost = %d\n", cost); + } -int ImportStatement::inlineCost(InlineCostState *ics) -{ - return 0; -} + void visit(ThrowStatement *s) + { + cost += STATEMENT_COST; + s->exp->accept(this); + } -int ForStatement::inlineCost(InlineCostState *ics) -{ - //return COST_MAX; - int cost = STATEMENT_COST; - if (init) - cost += init->inlineCost(ics); - if (condition) - cost += expressionInlineCost(condition, ics); - if (increment) - cost += expressionInlineCost(increment, ics); - if (body) - cost += body->inlineCost(ics); - //printf("ForStatement: inlineCost = %d\n", cost); - return cost; -} + /* -------------------------- */ + void expressionInlineCost(Expression *e) + { + //printf("expressionInlineCost()\n"); + //e->print(); + if (e) + { + class LambdaInlineCost : public StoppableVisitor + { + InlineCostVisitor *icv; + public: + LambdaInlineCost(InlineCostVisitor *icv) : icv(icv) {} -/* -------------------------- */ + void visit(Expression *e) + { + e->accept(icv); + stop = icv->cost >= COST_MAX; + } + }; -struct ICS2 -{ - int cost; - InlineCostState *ics; -}; + InlineCostVisitor icv(this); + LambdaInlineCost lic(&icv); + walkPostorder(e, &lic); + cost += icv.cost; + } + } -int lambdaInlineCost(Expression *e, void *param) -{ - ICS2 *ics2 = (ICS2 *)param; - ics2->cost += e->inlineCost3(ics2->ics); - return (ics2->cost >= COST_MAX); -} + void visit(Expression *e) + { + cost++; + } -int expressionInlineCost(Expression *e, InlineCostState *ics) -{ - //printf("expressionInlineCost()\n"); - //e->dump(0); - ICS2 ics2; - ics2.cost = 0; - ics2.ics = ics; - if (e) - e->apply(&lambdaInlineCost, &ics2); - return ics2.cost; -} + void visit(VarExp *e) + { + //printf("VarExp::inlineCost3() %s\n", toChars()); + Type *tb = e->type->toBasetype(); + if (tb->ty == Tstruct) + { + StructDeclaration *sd = ((TypeStruct *)tb)->sym; + if (sd->isNested()) + { + /* An inner struct will be nested inside another function hierarchy than where + * we're inlining into, so don't inline it. + * At least not until we figure out how to 'move' the struct to be nested + * locally. Example: + * struct S(alias pred) { void unused_func(); } + * void abc() { int w; S!(w) m; } + * void bar() { abc(); } + */ + cost = COST_MAX; + return; + } + } + FuncDeclaration *fd = e->var->isFuncDeclaration(); + if (fd && fd->isNested()) // see Bugzilla 7199 for test case + cost = COST_MAX; + else + cost++; + } -int Expression::inlineCost3(InlineCostState *ics) -{ - return 1; -} + void visit(ThisExp *e) + { + //printf("ThisExp::inlineCost3() %s\n", toChars()); + if (!fd) + { + cost = COST_MAX; + return; + } + if (!hdrscan) + { + if (fd->isNested() || !hasthis) + { + cost = COST_MAX; + return; + } + } + cost++; + } -int VarExp::inlineCost3(InlineCostState *ics) -{ - //printf("VarExp::inlineCost3() %s\n", toChars()); - Type *tb = type->toBasetype(); - if (tb->ty == Tstruct) - { - StructDeclaration *sd = ((TypeStruct *)tb)->sym; - if (sd->isNested()) - /* An inner struct will be nested inside another function hierarchy than where - * we're inlining into, so don't inline it. - * At least not until we figure out how to 'move' the struct to be nested - * locally. Example: - * struct S(alias pred) { void unused_func(); } - * void abc() { int w; S!(w) m; } - * void bar() { abc(); } - */ - return COST_MAX; + void visit(StructLiteralExp *e) + { + //printf("StructLiteralExp::inlineCost3() %s\n", toChars()); + if (e->sd->isNested()) + cost = COST_MAX; + else + cost++; } - FuncDeclaration *fd = var->isFuncDeclaration(); - if (fd && fd->isNested()) // see Bugzilla 7199 for test case - return COST_MAX; - return 1; -} -int ThisExp::inlineCost3(InlineCostState *ics) -{ - //printf("ThisExp::inlineCost3() %s\n", toChars()); - FuncDeclaration *fd = ics->fd; - if (!fd) - return COST_MAX; - if (!ics->hdrscan) - if (fd->isNested() || !ics->hasthis) - return COST_MAX; - return 1; -} + void visit(FuncExp *e) + { + //printf("FuncExp::inlineCost3()\n"); + // Right now, this makes the function be output to the .obj file twice. + cost = COST_MAX; + } -int StructLiteralExp::inlineCost3(InlineCostState *ics) -{ - //printf("StructLiteralExp::inlineCost3() %s\n", toChars()); - if (sd->isNested()) - return COST_MAX; - return 1; -} + void visit(DelegateExp *e) + { + //printf("DelegateExp::inlineCost3()\n"); + cost = COST_MAX; + } -int FuncExp::inlineCost3(InlineCostState *ics) -{ - //printf("FuncExp::inlineCost3()\n"); - // Right now, this makes the function be output to the .obj file twice. - return COST_MAX; -} + void visit(DeclarationExp *e) + { + //printf("DeclarationExp::inlineCost3()\n"); + VarDeclaration *vd = e->declaration->isVarDeclaration(); + if (vd) + { + TupleDeclaration *td = vd->toAlias()->isTupleDeclaration(); + if (td) + { + cost = COST_MAX; // finish DeclarationExp::doInline + return; + } + if (!hdrscan && vd->isDataseg()) + { + cost = COST_MAX; + return; + } -int DelegateExp::inlineCost3(InlineCostState *ics) -{ - //printf("DelegateExp::inlineCost3()\n"); - return COST_MAX; -} + if (vd->edtor) + { + // if destructor required + // needs work to make this work + cost = COST_MAX; + return; + } + // Scan initializer (vd->init) + if (vd->init) + { + ExpInitializer *ie = vd->init->isExpInitializer(); -int DeclarationExp::inlineCost3(InlineCostState *ics) -{ int cost = 0; - VarDeclaration *vd; - - //printf("DeclarationExp::inlineCost3()\n"); - vd = declaration->isVarDeclaration(); - if (vd) - { - TupleDeclaration *td = vd->toAlias()->isTupleDeclaration(); - if (td) - { -#if 1 - return COST_MAX; // finish DeclarationExp::doInline -#else - for (size_t i = 0; i < td->objects->dim; i++) - { RootObject *o = (*td->objects)[i]; - if (o->dyncast() != DYNCAST_EXPRESSION) - return COST_MAX; - Expression *eo = (Expression *)o; - if (eo->op != TOKdsymbol) - return COST_MAX; + if (ie) + { + expressionInlineCost(ie->exp); + } } - return td->objects->dim; -#endif + cost += 1; } - if (!ics->hdrscan && vd->isDataseg()) - return COST_MAX; - cost += 1; - if (vd->edtor) // if destructor required - return COST_MAX; // needs work to make this work - // Scan initializer (vd->init) - if (vd->init) + // These can contain functions, which when copied, get output twice. + if (e->declaration->isStructDeclaration() || + e->declaration->isClassDeclaration() || + e->declaration->isFuncDeclaration() || + e->declaration->isAttribDeclaration() || + e->declaration->isTemplateMixin()) { - ExpInitializer *ie = vd->init->isExpInitializer(); - - if (ie) - { - cost += expressionInlineCost(ie->exp, ics); - } + cost = COST_MAX; + return; } - } - - // These can contain functions, which when copied, get output twice. - if (declaration->isStructDeclaration() || - declaration->isClassDeclaration() || - declaration->isFuncDeclaration() || - declaration->isTypedefDeclaration() || - declaration->isAttribDeclaration() || - declaration->isTemplateMixin()) - return COST_MAX; - - //printf("DeclarationExp::inlineCost3('%s')\n", toChars()); - return cost; -} - -int CallExp::inlineCost3(InlineCostState *ics) -{ - //printf("CallExp::inlineCost3() %s\n", toChars()); - // Bugzilla 3500: super.func() calls must be devirtualized, and the inliner - // can't handle that at present. - if (e1->op == TOKdotvar && ((DotVarExp *)e1)->e1->op == TOKsuper) - return COST_MAX; - return 1; -} + //printf("DeclarationExp::inlineCost3('%s')\n", toChars()); + } + void visit(CallExp *e) + { + //printf("CallExp::inlineCost3() %s\n", toChars()); + // Bugzilla 3500: super.func() calls must be devirtualized, and the inliner + // can't handle that at present. + if (e->e1->op == TOKdotvar && ((DotVarExp *)e->e1)->e1->op == TOKsuper) + cost = COST_MAX; + else if (e->f && e->f->ident == Id::__alloca && e->f->linkage == LINKc && !allowAlloca) + cost = COST_MAX; // inlining alloca may cause stack overflows + else + cost++; + } +}; /* ======================== Perform the inlining ============================== */ @@ -348,1203 +398,1252 @@ struct InlineDoState // inline result bool foundReturn; }; -/* -------------------------------------------------------------------- */ -Statement *Statement::doInlineStatement(InlineDoState *ids) -{ - assert(0); - return NULL; // default is we can't inline it -} +Expression *doInline(Statement *s, InlineDoState *ids); +Expression *doInline(Expression *e, InlineDoState *ids); -Statement *ExpStatement::doInlineStatement(InlineDoState *ids) -{ -#if LOG - if (exp) printf("ExpStatement::doInlineStatement() '%s'\n", exp->toChars()); -#endif - return new ExpStatement(loc, exp ? exp->doInline(ids) : NULL); -} +/* -------------------------------------------------------------------- */ -Statement *CompoundStatement::doInlineStatement(InlineDoState *ids) +Statement *inlineAsStatement(Statement *s, InlineDoState *ids) { - //printf("CompoundStatement::doInlineStatement() %d\n", statements->dim); - Statements *as = new Statements(); - as->reserve(statements->dim); - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; - if (s) + class InlineAsStatement : public Visitor + { + public: + InlineDoState *ids; + Statement *result; + + InlineAsStatement(InlineDoState *ids) + : ids(ids) { - as->push(s->doInlineStatement(ids)); - if (ids->foundReturn) - break; + result = NULL; } - else - as->push(NULL); - } - return new CompoundStatement(loc, as); -} -Statement *UnrolledLoopStatement::doInlineStatement(InlineDoState *ids) -{ - //printf("UnrolledLoopStatement::doInlineStatement() %d\n", statements->dim); - Statements *as = new Statements(); - as->reserve(statements->dim); - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; - if (s) + void visit(Statement *s) { - as->push(s->doInlineStatement(ids)); - if (ids->foundReturn) - break; + assert(0); // default is we can't inline it } - else - as->push(NULL); - } - return new UnrolledLoopStatement(loc, as); -} - -Statement *ScopeStatement::doInlineStatement(InlineDoState *ids) -{ - //printf("ScopeStatement::doInlineStatement() %d\n", statements->dim); - return statement ? new ScopeStatement(loc, statement->doInlineStatement(ids)) : this; -} - -Statement *IfStatement::doInlineStatement(InlineDoState *ids) -{ - assert(!arg); - - Expression *condition = this->condition ? this->condition->doInline(ids) : NULL; - Statement *ifbody = this->ifbody ? this->ifbody->doInlineStatement(ids) : NULL; - bool bodyReturn = ids->foundReturn; - ids->foundReturn = false; - Statement *elsebody = this->elsebody ? this->elsebody->doInlineStatement(ids) : NULL; - ids->foundReturn = ids->foundReturn && bodyReturn; + void visit(ExpStatement *s) + { + #if LOG + if (s->exp) printf("ExpStatement::inlineAsStatement() '%s'\n", s->exp->toChars()); + #endif + result = new ExpStatement(s->loc, s->exp ? doInline(s->exp, ids) : NULL); + } - return new IfStatement(loc, arg, condition, ifbody, elsebody); -} - -Statement *ReturnStatement::doInlineStatement(InlineDoState *ids) -{ - //printf("ReturnStatement::doInlineStatement() '%s'\n", exp ? exp->toChars() : ""); - ids->foundReturn = true; - return new ReturnStatement(loc, exp ? exp->doInline(ids) : NULL); -} - -Statement *ImportStatement::doInlineStatement(InlineDoState *ids) -{ - return NULL; -} + void visit(CompoundStatement *s) + { + //printf("CompoundStatement::inlineAsStatement() %d\n", s->statements->dim); + Statements *as = new Statements(); + as->reserve(s->statements->dim); + for (size_t i = 0; i < s->statements->dim; i++) + { + Statement *sx = (*s->statements)[i]; + if (sx) + { + as->push(inlineAsStatement(sx, ids)); + if (ids->foundReturn) + break; + } + else + as->push(NULL); + } + result = new CompoundStatement(s->loc, as); + } -Statement *ForStatement::doInlineStatement(InlineDoState *ids) -{ - //printf("ForStatement::doInlineStatement()\n"); - Statement *init = this->init ? this->init->doInlineStatement(ids) : NULL; - Expression *condition = this->condition ? this->condition->doInline(ids) : NULL; - Expression *increment = this->increment ? this->increment->doInline(ids) : NULL; - Statement *body = this->body ? this->body->doInlineStatement(ids) : NULL; - return new ForStatement(loc, init, condition, increment, body); -} + void visit(UnrolledLoopStatement *s) + { + //printf("UnrolledLoopStatement::inlineAsStatement() %d\n", s->statements->dim); + Statements *as = new Statements(); + as->reserve(s->statements->dim); + for (size_t i = 0; i < s->statements->dim; i++) + { + Statement *sx = (*s->statements)[i]; + if (sx) + { + as->push(inlineAsStatement(sx, ids)); + if (ids->foundReturn) + break; + } + else + as->push(NULL); + } + result = new UnrolledLoopStatement(s->loc, as); + } -/* -------------------------------------------------------------------- */ + void visit(ScopeStatement *s) + { + //printf("ScopeStatement::inlineAsStatement() %d\n", s->statement->dim); + result = s->statement ? new ScopeStatement(s->loc, inlineAsStatement(s->statement, ids)) : s; + } -Expression *Statement::doInline(InlineDoState *ids) -{ - printf("Statement::doInline()\n%s\n", toChars()); - fflush(stdout); - assert(0); - return NULL; // default is we can't inline it -} + void visit(IfStatement *s) + { + assert(!s->arg); -Expression *ExpStatement::doInline(InlineDoState *ids) -{ -#if LOG - if (exp) printf("ExpStatement::doInline() '%s'\n", exp->toChars()); -#endif - return exp ? exp->doInline(ids) : NULL; -} + Expression *condition = s->condition ? doInline(s->condition, ids) : NULL; + Statement *ifbody = s->ifbody ? inlineAsStatement(s->ifbody, ids) : NULL; + bool bodyReturn = ids->foundReturn; + ids->foundReturn = false; + Statement *elsebody = s->elsebody ? inlineAsStatement(s->elsebody, ids) : NULL; + ids->foundReturn = ids->foundReturn && bodyReturn; -Expression *CompoundStatement::doInline(InlineDoState *ids) -{ - Expression *e = NULL; + result = new IfStatement(s->loc, s->arg, condition, ifbody, elsebody); + } - //printf("CompoundStatement::doInline() %d\n", statements->dim); - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; - if (s) + void visit(ReturnStatement *s) { - Expression *e2 = s->doInline(ids); - e = Expression::combine(e, e2); - if (ids->foundReturn) - break; - + //printf("ReturnStatement::inlineAsStatement() '%s'\n", s->exp ? s->exp->toChars() : ""); + ids->foundReturn = true; + result = new ReturnStatement(s->loc, s->exp ? doInline(s->exp, ids) : NULL); } - } - return e; -} - -Expression *UnrolledLoopStatement::doInline(InlineDoState *ids) -{ - Expression *e = NULL; - //printf("UnrolledLoopStatement::doInline() %d\n", statements->dim); - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; - if (s) + void visit(ImportStatement *s) { - Expression *e2 = s->doInline(ids); - e = Expression::combine(e, e2); - if (ids->foundReturn) - break; + result = NULL; } - } - return e; -} -Expression *ScopeStatement::doInline(InlineDoState *ids) -{ - return statement ? statement->doInline(ids) : NULL; -} - -Expression *IfStatement::doInline(InlineDoState *ids) -{ - Expression *econd; - Expression *e1; - Expression *e2; - Expression *e; - - assert(!arg); - econd = condition->doInline(ids); - assert(econd); - if (ifbody) - { - e1 = ifbody->doInline(ids); - } - else - e1 = NULL; - bool bodyReturn = ids->foundReturn; - ids->foundReturn = false; - if (elsebody) - e2 = elsebody->doInline(ids); - else - e2 = NULL; - if (e1 && e2) - { - e = new CondExp(econd->loc, econd, e1, e2); - e->type = e1->type; - if (e->type->ty == Ttuple) + void visit(ForStatement *s) { - e1->type = Type::tvoid; - e2->type = Type::tvoid; - e->type = Type::tvoid; + //printf("ForStatement::inlineAsStatement()\n"); + Statement *init = s->init ? inlineAsStatement(s->init, ids) : NULL; + Expression *condition = s->condition ? doInline(s->condition, ids) : NULL; + Expression *increment = s->increment ? doInline(s->increment, ids) : NULL; + Statement *body = s->body ? inlineAsStatement(s->body, ids) : NULL; + result = new ForStatement(s->loc, init, condition, increment, body); } - } - else if (e1) - { - e = new AndAndExp(econd->loc, econd, e1); - e->type = Type::tvoid; - } - else if (e2) - { - e = new OrOrExp(econd->loc, econd, e2); - e->type = Type::tvoid; - } - else - { - e = econd; - } - ids->foundReturn = ids->foundReturn && bodyReturn; - return e; -} -Expression *ReturnStatement::doInline(InlineDoState *ids) -{ - //printf("ReturnStatement::doInline() '%s'\n", exp ? exp->toChars() : ""); - ids->foundReturn = true; - return exp ? exp->doInline(ids) : NULL; -} + void visit(ThrowStatement *s) + { + //printf("ThrowStatement::inlineAsStatement() '%s'\n", s->exp->toChars()); + result = new ThrowStatement(s->loc, doInline(s->exp, ids)); + } + }; -Expression *ImportStatement::doInline(InlineDoState *ids) -{ - return NULL; + InlineAsStatement v(ids); + s->accept(&v); + return v.result; } -/* --------------------------------------------------------------- */ - -/****************************** - * Perform doInline() on an array of Expressions. - */ - -Expressions *arrayExpressiondoInline(Expressions *a, InlineDoState *ids) -{ Expressions *newa = NULL; +/* -------------------------------------------------------------------- */ - if (a) +Expression *doInline(Statement *s, InlineDoState *ids) +{ + class InlineStatement : public Visitor { - newa = new Expressions(); - newa->setDim(a->dim); + public: + InlineDoState *ids; + Expression *result; - for (size_t i = 0; i < a->dim; i++) - { Expression *e = (*a)[i]; - - if (e) - e = e->doInline(ids); - (*newa)[i] = e; + InlineStatement(InlineDoState *ids) + : ids(ids) + { + result = NULL; } - } - return newa; -} -Expression *Expression::doInline(InlineDoState *ids) -{ - //printf("Expression::doInline(%s): %s\n", Token::toChars(op), toChars()); - return copy(); -} + void visit(Statement *s) + { + printf("Statement::doInline()\n%s\n", s->toChars()); + fflush(stdout); + assert(0); // default is we can't inline it + } -Expression *SymOffExp::doInline(InlineDoState *ids) -{ - //printf("SymOffExp::doInline(%s)\n", toChars()); - for (size_t i = 0; i < ids->from.dim; i++) - { - if (var == ids->from[i]) + void visit(ExpStatement *s) { - SymOffExp *se = (SymOffExp *)copy(); - se->var = (Declaration *)ids->to[i]; - return se; + #if LOG + if (s->exp) printf("ExpStatement::doInline() '%s'\n", s->exp->toChars()); + #endif + result = s->exp ? doInline(s->exp, ids) : NULL; } - } - return this; -} -Expression *VarExp::doInline(InlineDoState *ids) -{ - //printf("VarExp::doInline(%s)\n", toChars()); - for (size_t i = 0; i < ids->from.dim; i++) - { - if (var == ids->from[i]) - { - VarExp *ve = (VarExp *)copy(); - ve->var = (Declaration *)ids->to[i]; - return ve; - } - } - if (ids->fd && var == ids->fd->vthis) - { - VarExp *ve = new VarExp(loc, ids->vthis); - ve->type = type; - return ve; - } - - /* Inlining context pointer access for nested referenced variables. - * For example: - * auto fun() { - * int i = 40; - * auto foo() { - * int g = 2; - * struct Result { - * auto bar() { return i + g; } - * } - * return Result(); - * } - * return foo(); - * } - * auto t = fun(); - * 'i' and 'g' are nested referenced variables in Result.bar(), so: - * auto x = t.bar(); - * should be inlined to: - * auto x = *(t.vthis.vthis + i->voffset) + *(t.vthis + g->voffset) - */ - VarDeclaration *v = var->isVarDeclaration(); - if (v && v->nestedrefs.dim && ids->vthis) - { - Dsymbol *s = ids->fd; - FuncDeclaration *fdv = v->toParent()->isFuncDeclaration(); - assert(fdv); - Expression *ve = new VarExp(loc, ids->vthis); - ve->type = ids->vthis->type; - while (s != fdv) - { - FuncDeclaration *f = s->isFuncDeclaration(); - if (AggregateDeclaration *ad = s->isThis()) - { - assert(ad->vthis); - ve = new DotVarExp(loc, ve, ad->vthis); - ve->type = ad->vthis->type; - s = ad->toParent2(); - } - else if (f && f->isNested()) + void visit(CompoundStatement *s) + { + //printf("CompoundStatement::doInline() %d\n", s->statements->dim); + for (size_t i = 0; i < s->statements->dim; i++) { - assert(f->vthis); - if (f->hasNestedFrameRefs()) + Statement *sx = (*s->statements)[i]; + if (sx) { - ve = new DotVarExp(loc, ve, f->vthis); - ve->type = f->vthis->type; + Expression *e = doInline(sx, ids); + result = Expression::combine(result, e); + if (ids->foundReturn) + break; + } - s = f->toParent2(); } - else - assert(0); - assert(s); } - ve = new DotVarExp(loc, ve, v); - ve->type = v->type; - //printf("\t==> ve = %s, type = %s\n", ve->toChars(), ve->type->toChars()); - return ve; - } - - return this; -} - -Expression *ThisExp::doInline(InlineDoState *ids) -{ - //if (!ids->vthis) - //error("no 'this' when inlining %s", ids->parent->toChars()); - if (!ids->vthis) - { - return this; - } - - VarExp *ve = new VarExp(loc, ids->vthis); - ve->type = type; - return ve; -} - -Expression *SuperExp::doInline(InlineDoState *ids) -{ - assert(ids->vthis); - - VarExp *ve = new VarExp(loc, ids->vthis); - ve->type = type; - return ve; -} -Expression *DeclarationExp::doInline(InlineDoState *ids) -{ - //printf("DeclarationExp::doInline(%s)\n", toChars()); - VarDeclaration *vd = declaration->isVarDeclaration(); - if (vd) - { -#if 0 - // Need to figure this out before inlining can work for tuples - TupleDeclaration *td = vd->toAlias()->isTupleDeclaration(); - if (td) - { - for (size_t i = 0; i < td->objects->dim; i++) - { DsymbolExp *se = (*td->objects)[i]; - assert(se->op == TOKdsymbol); - se->s; - } - return st->objects->dim; - } -#endif - if (vd->isStatic()) - ; - else + void visit(UnrolledLoopStatement *s) { - VarDeclaration *vto; - if (ids->fd && vd == ids->fd->nrvo_var) + //printf("UnrolledLoopStatement::doInline() %d\n", s->statements->dim); + for (size_t i = 0; i < s->statements->dim; i++) { - for (size_t i = 0; i < ids->from.dim; i++) + Statement *sx = (*s->statements)[i]; + if (sx) { - if (vd == ids->from[i]) - { - vto = (VarDeclaration *)ids->to[i]; - Expression *e; - if (vd->init && !vd->init->isVoidInitializer()) - { - e = vd->init->toExpression(); - assert(e); - e = e->doInline(ids); - } - else - e = new IntegerExp(vd->init->loc, 0, Type::tint32); - return e; - } + Expression *e = doInline(sx, ids); + result = Expression::combine(result, e); + if (ids->foundReturn) + break; + } } - vto = new VarDeclaration(vd->loc, vd->type, vd->ident, vd->init); - memcpy((void *)vto, (void *)vd, sizeof(VarDeclaration)); - vto->parent = ids->parent; - vto->csym = NULL; - vto->isym = NULL; + } - ids->from.push(vd); - ids->to.push(vto); + void visit(ScopeStatement *s) + { + result = s->statement ? doInline(s->statement, ids) : NULL; + } - L1: - if (vd->init) + void visit(IfStatement *s) + { + assert(!s->arg); + Expression *econd = doInline(s->condition, ids); + assert(econd); + Expression *e1 = s->ifbody ? doInline(s->ifbody, ids) : NULL; + bool bodyReturn = ids->foundReturn; + ids->foundReturn = false; + Expression *e2 = s->elsebody ? doInline(s->elsebody, ids) : NULL; + if (e1 && e2) { - if (vd->init->isVoidInitializer()) - { - vto->init = new VoidInitializer(vd->init->loc); - } - else + result = new CondExp(econd->loc, econd, e1, e2); + result->type = e1->type; + if (result->type->ty == Ttuple) { - Expression *e = vd->init->toExpression(); - assert(e); - vto->init = new ExpInitializer(e->loc, e->doInline(ids)); + e1->type = Type::tvoid; + e2->type = Type::tvoid; + result->type = Type::tvoid; } } - DeclarationExp *de = (DeclarationExp *)copy(); - de->declaration = (Dsymbol *) (void *)vto; - return de; + else if (e1) + { + result = new AndAndExp(econd->loc, econd, e1); + result->type = Type::tvoid; + } + else if (e2) + { + result = new OrOrExp(econd->loc, econd, e2); + result->type = Type::tvoid; + } + else + { + result = econd; + } + ids->foundReturn = ids->foundReturn && bodyReturn; } - } - /* This needs work, like DeclarationExp::toElem(), if we are - * to handle TemplateMixin's. For now, we just don't inline them. - */ - return Expression::doInline(ids); -} - -Expression *NewExp::doInline(InlineDoState *ids) -{ - //printf("NewExp::doInline(): %s\n", toChars()); - NewExp *ne = (NewExp *)copy(); - - if (thisexp) - ne->thisexp = thisexp->doInline(ids); - ne->newargs = arrayExpressiondoInline(ne->newargs, ids); - ne->arguments = arrayExpressiondoInline(ne->arguments, ids); - return ne; -} - -Expression *UnaExp::doInline(InlineDoState *ids) -{ - UnaExp *ue = (UnaExp *)copy(); - - ue->e1 = e1->doInline(ids); - return ue; -} - -Expression *AssertExp::doInline(InlineDoState *ids) -{ - AssertExp *ae = (AssertExp *)copy(); - - ae->e1 = e1->doInline(ids); - if (msg) - ae->msg = msg->doInline(ids); - return ae; -} - -Expression *BinExp::doInline(InlineDoState *ids) -{ - BinExp *be = (BinExp *)copy(); - - be->e1 = e1->doInline(ids); - be->e2 = e2->doInline(ids); - return be; -} - -Expression *CallExp::doInline(InlineDoState *ids) -{ - CallExp *ce; - - ce = (CallExp *)copy(); - ce->e1 = e1->doInline(ids); - ce->arguments = arrayExpressiondoInline(arguments, ids); - return ce; -} - - -Expression *IndexExp::doInline(InlineDoState *ids) -{ - IndexExp *are = (IndexExp *)copy(); - - are->e1 = e1->doInline(ids); - - if (lengthVar) - { //printf("lengthVar\n"); - VarDeclaration *vd = lengthVar; - ExpInitializer *ie; - ExpInitializer *ieto; - VarDeclaration *vto; - vto = new VarDeclaration(vd->loc, vd->type, vd->ident, vd->init); - memcpy((void*)vto, (void*)vd, sizeof(VarDeclaration)); - vto->parent = ids->parent; - vto->csym = NULL; - vto->isym = NULL; - - ids->from.push(vd); - ids->to.push(vto); + void visit(ReturnStatement *s) + { + //printf("ReturnStatement::doInline() '%s'\n", s->exp ? s->exp->toChars() : ""); + ids->foundReturn = true; + result = s->exp ? doInline(s->exp, ids) : NULL; + } - if (vd->init && !vd->init->isVoidInitializer()) + void visit(ImportStatement *s) { - ie = vd->init->isExpInitializer(); - assert(ie); - ieto = new ExpInitializer(ie->loc, ie->exp->doInline(ids)); - vto->init = ieto; } + }; - are->lengthVar = (VarDeclaration *) (void *)vto; - } - are->e2 = e2->doInline(ids); - return are; + InlineStatement v(ids); + s->accept(&v); + return v.result; } +/* --------------------------------------------------------------- */ -Expression *SliceExp::doInline(InlineDoState *ids) +Expression *doInline(Expression *e, InlineDoState *ids) { - SliceExp *are = (SliceExp *)copy(); + class InlineExpression : public Visitor + { + public: + InlineDoState *ids; + Expression *result; - are->e1 = e1->doInline(ids); + InlineExpression(InlineDoState *ids) + : ids(ids) + { + result = NULL; + } - if (lengthVar) - { //printf("lengthVar\n"); - VarDeclaration *vd = lengthVar; - ExpInitializer *ie; - ExpInitializer *ieto; - VarDeclaration *vto; + /****************************** + * Perform doInline() on an array of Expressions. + */ + Expressions *arrayExpressiondoInline(Expressions *a) + { + if (!a) + return NULL; - vto = new VarDeclaration(vd->loc, vd->type, vd->ident, vd->init); - memcpy((void*)vto, (void*)vd, sizeof(VarDeclaration)); - vto->parent = ids->parent; - vto->csym = NULL; - vto->isym = NULL; + Expressions *newa = new Expressions(); + newa->setDim(a->dim); - ids->from.push(vd); - ids->to.push(vto); + for (size_t i = 0; i < a->dim; i++) + { + Expression *e = (*a)[i]; + if (e) + e = doInline(e, ids); + (*newa)[i] = e; + } + return newa; + } - if (vd->init && !vd->init->isVoidInitializer()) + void visit(Expression *e) { - ie = vd->init->isExpInitializer(); - assert(ie); - ieto = new ExpInitializer(ie->loc, ie->exp->doInline(ids)); - vto->init = ieto; + //printf("Expression::doInline(%s): %s\n", Token::toChars(e->op), e->toChars()); + result = e->copy(); } - are->lengthVar = (VarDeclaration *) (void *)vto; - } - if (lwr) - are->lwr = lwr->doInline(ids); - if (upr) - are->upr = upr->doInline(ids); - return are; -} + void visit(SymOffExp *e) + { + //printf("SymOffExp::doInline(%s)\n", e->toChars()); + for (size_t i = 0; i < ids->from.dim; i++) + { + if (e->var == ids->from[i]) + { + SymOffExp *se = (SymOffExp *)e->copy(); + se->var = (Declaration *)ids->to[i]; + result = se; + return; + } + } + result = e; + } + void visit(VarExp *e) + { + //printf("VarExp::doInline(%s)\n", e->toChars()); + for (size_t i = 0; i < ids->from.dim; i++) + { + if (e->var == ids->from[i]) + { + VarExp *ve = (VarExp *)e->copy(); + ve->var = (Declaration *)ids->to[i]; + result = ve; + return; + } + } + if (ids->fd && e->var == ids->fd->vthis) + { + result = new VarExp(e->loc, ids->vthis); + result->type = e->type; + return; + } -Expression *TupleExp::doInline(InlineDoState *ids) -{ - TupleExp *ce; + /* Inlining context pointer access for nested referenced variables. + * For example: + * auto fun() { + * int i = 40; + * auto foo() { + * int g = 2; + * struct Result { + * auto bar() { return i + g; } + * } + * return Result(); + * } + * return foo(); + * } + * auto t = fun(); + * 'i' and 'g' are nested referenced variables in Result.bar(), so: + * auto x = t.bar(); + * should be inlined to: + * auto x = *(t.vthis.vthis + i->voffset) + *(t.vthis + g->voffset) + */ + VarDeclaration *v = e->var->isVarDeclaration(); + if (v && v->nestedrefs.dim && ids->vthis) + { + Dsymbol *s = ids->fd; + FuncDeclaration *fdv = v->toParent()->isFuncDeclaration(); + assert(fdv); + result = new VarExp(e->loc, ids->vthis); + result->type = ids->vthis->type; + while (s != fdv) + { + FuncDeclaration *f = s->isFuncDeclaration(); + if (AggregateDeclaration *ad = s->isThis()) + { + assert(ad->vthis); + result = new DotVarExp(e->loc, result, ad->vthis); + result->type = ad->vthis->type; + s = ad->toParent2(); + } + else if (f && f->isNested()) + { + assert(f->vthis); + if (f->hasNestedFrameRefs()) + { + result = new DotVarExp(e->loc, result, f->vthis); + result->type = f->vthis->type; + } + s = f->toParent2(); + } + else + assert(0); + assert(s); + } + result = new DotVarExp(e->loc, result, v); + result->type = v->type; + //printf("\t==> result = %s, type = %s\n", result->toChars(), result->type->toChars()); + return; + } - ce = (TupleExp *)copy(); - if (e0) - ce->e0 = e0->doInline(ids); - ce->exps = arrayExpressiondoInline(exps, ids); - return ce; -} + result = e; + } + void visit(ThisExp *e) + { + //if (!ids->vthis) + //e->error("no 'this' when inlining %s", ids->parent->toChars()); + if (!ids->vthis) + { + result = e; + return; + } -Expression *ArrayLiteralExp::doInline(InlineDoState *ids) -{ - ArrayLiteralExp *ce; + result = new VarExp(e->loc, ids->vthis); + result->type = e->type; + } - ce = (ArrayLiteralExp *)copy(); - ce->elements = arrayExpressiondoInline(elements, ids); - return ce; -} + void visit(SuperExp *e) + { + assert(ids->vthis); + result = new VarExp(e->loc, ids->vthis); + result->type = e->type; + } -Expression *AssocArrayLiteralExp::doInline(InlineDoState *ids) -{ - AssocArrayLiteralExp *ce; + void visit(DeclarationExp *e) + { + //printf("DeclarationExp::doInline(%s)\n", e->toChars()); + if (VarDeclaration *vd = e->declaration->isVarDeclaration()) + { + #if 0 + // Need to figure this out before inlining can work for tuples + TupleDeclaration *td = vd->toAlias()->isTupleDeclaration(); + if (td) + { + for (size_t i = 0; i < td->objects->dim; i++) + { + DsymbolExp *se = (*td->objects)[i]; + assert(se->op == TOKdsymbol); + se->s; + } + result = st->objects->dim; + return; + } + #endif + if (!vd->isStatic()) + { + if (ids->fd && vd == ids->fd->nrvo_var) + { + for (size_t i = 0; i < ids->from.dim; i++) + { + if (vd == ids->from[i]) + { + if (vd->init && !vd->init->isVoidInitializer()) + { + result = vd->init->toExpression(); + assert(result); + result = doInline(result, ids); + } + else + result = new IntegerExp(vd->init->loc, 0, Type::tint32); + return; + } + } + } + VarDeclaration *vto = new VarDeclaration(vd->loc, vd->type, vd->ident, vd->init); + memcpy((void *)vto, (void *)vd, sizeof(VarDeclaration)); + vto->parent = ids->parent; + vto->csym = NULL; + vto->isym = NULL; - ce = (AssocArrayLiteralExp *)copy(); - ce->keys = arrayExpressiondoInline(keys, ids); - ce->values = arrayExpressiondoInline(values, ids); - return ce; -} + ids->from.push(vd); + ids->to.push(vto); + if (vd->init) + { + if (vd->init->isVoidInitializer()) + { + vto->init = new VoidInitializer(vd->init->loc); + } + else + { + Expression *ei = vd->init->toExpression(); + assert(ei); + vto->init = new ExpInitializer(ei->loc, doInline(ei, ids)); + } + } + DeclarationExp *de = (DeclarationExp *)e->copy(); + de->declaration = vto; + result = de; + return; + } + } + /* This needs work, like DeclarationExp::toElem(), if we are + * to handle TemplateMixin's. For now, we just don't inline them. + */ + visit((Expression *)e); + } -Expression *StructLiteralExp::doInline(InlineDoState *ids) -{ - if(inlinecopy) return inlinecopy; - StructLiteralExp *ce; - ce = (StructLiteralExp *)copy(); - inlinecopy = ce; - ce->elements = arrayExpressiondoInline(elements, ids); - inlinecopy = NULL; - return ce; -} + void visit(NewExp *e) + { + //printf("NewExp::doInline(): %s\n", e->toChars()); + NewExp *ne = (NewExp *)e->copy(); + + if (e->thisexp) + ne->thisexp = doInline(e->thisexp, ids); + ne->newargs = arrayExpressiondoInline(e->newargs); + ne->arguments = arrayExpressiondoInline(e->arguments); + result = ne; + } + void visit(UnaExp *e) + { + UnaExp *ue = (UnaExp *)e->copy(); -Expression *ArrayExp::doInline(InlineDoState *ids) -{ - ArrayExp *ce; + ue->e1 = doInline(e->e1, ids); + result = ue; + } - ce = (ArrayExp *)copy(); - ce->e1 = e1->doInline(ids); - ce->arguments = arrayExpressiondoInline(arguments, ids); - return ce; -} + void visit(AssertExp *e) + { + AssertExp *ae = (AssertExp *)e->copy(); + ae->e1 = doInline(e->e1, ids); + if (e->msg) + ae->msg = doInline(e->msg, ids); + result = ae; + } -Expression *CondExp::doInline(InlineDoState *ids) -{ - CondExp *ce = (CondExp *)copy(); + void visit(BinExp *e) + { + BinExp *be = (BinExp *)e->copy(); - ce->econd = econd->doInline(ids); - ce->e1 = e1->doInline(ids); - ce->e2 = e2->doInline(ids); - return ce; -} + be->e1 = doInline(e->e1, ids); + be->e2 = doInline(e->e2, ids); + result = be; + } + void visit(CallExp *e) + { + CallExp *ce = (CallExp *)e->copy(); -/* ========== Walk the parse trees, and inline expand functions ============= */ + ce->e1 = doInline(e->e1, ids); + ce->arguments = arrayExpressiondoInline(e->arguments); + result = ce; + } -/* Walk the trees, looking for functions to inline. - * Inline any that can be. - */ + void visit(IndexExp *e) + { + IndexExp *are = (IndexExp *)e->copy(); -struct InlineScanState -{ - FuncDeclaration *fd; // function being scanned -}; + are->e1 = doInline(e->e1, ids); -Statement *Statement::inlineScan(InlineScanState *iss) -{ - return this; -} + if (e->lengthVar) + { + //printf("lengthVar\n"); + VarDeclaration *vd = e->lengthVar; -Statement *ExpStatement::inlineScan(InlineScanState *iss) -{ -#if LOG - printf("ExpStatement::inlineScan(%s)\n", toChars()); -#endif - if (exp) - { - exp = exp->inlineScan(iss); + VarDeclaration *vto = new VarDeclaration(vd->loc, vd->type, vd->ident, vd->init); + memcpy((void*)vto, (void*)vd, sizeof(VarDeclaration)); + vto->parent = ids->parent; + vto->csym = NULL; + vto->isym = NULL; - /* See if we can inline as a statement rather than as - * an Expression. - */ - if (exp && exp->op == TOKcall) - { - CallExp *ce = (CallExp *)exp; - if (ce->e1->op == TOKvar) - { - VarExp *ve = (VarExp *)ce->e1; - FuncDeclaration *fd = ve->var->isFuncDeclaration(); + ids->from.push(vd); + ids->to.push(vto); - if (fd && fd != iss->fd && fd->canInline(0, 0, 1)) + if (vd->init && !vd->init->isVoidInitializer()) { - Statement *s; - fd->expandInline(iss, NULL, NULL, ce->arguments, &s); - return s; + ExpInitializer *ie = vd->init->isExpInitializer(); + assert(ie); + vto->init = new ExpInitializer(ie->loc, doInline(ie->exp, ids));; } + + are->lengthVar = vto; } + are->e2 = doInline(e->e2, ids); + result = are; } - } - return this; -} - -Statement *CompoundStatement::inlineScan(InlineScanState *iss) -{ - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; - if (s) - (*statements)[i] = s->inlineScan(iss); - } - return this; -} - -Statement *UnrolledLoopStatement::inlineScan(InlineScanState *iss) -{ - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; - if (s) - (*statements)[i] = s->inlineScan(iss); - } - return this; -} - -Statement *ScopeStatement::inlineScan(InlineScanState *iss) -{ - if (statement) - statement = statement->inlineScan(iss); - return this; -} - -Statement *WhileStatement::inlineScan(InlineScanState *iss) -{ - condition = condition->inlineScan(iss); - body = body ? body->inlineScan(iss) : NULL; - return this; -} + void visit(SliceExp *e) + { + SliceExp *are = (SliceExp *)e->copy(); -Statement *DoStatement::inlineScan(InlineScanState *iss) -{ - body = body ? body->inlineScan(iss) : NULL; - condition = condition->inlineScan(iss); - return this; -} + are->e1 = doInline(e->e1, ids); + if (e->lengthVar) + { + //printf("lengthVar\n"); + VarDeclaration *vd = e->lengthVar; -Statement *ForStatement::inlineScan(InlineScanState *iss) -{ - if (init) - init = init->inlineScan(iss); - if (condition) - condition = condition->inlineScan(iss); - if (increment) - increment = increment->inlineScan(iss); - if (body) - body = body->inlineScan(iss); - return this; -} + VarDeclaration *vto = new VarDeclaration(vd->loc, vd->type, vd->ident, vd->init); + memcpy((void*)vto, (void*)vd, sizeof(VarDeclaration)); + vto->parent = ids->parent; + vto->csym = NULL; + vto->isym = NULL; + ids->from.push(vd); + ids->to.push(vto); -Statement *ForeachStatement::inlineScan(InlineScanState *iss) -{ - aggr = aggr->inlineScan(iss); - if (body) - body = body->inlineScan(iss); - return this; -} + if (vd->init && !vd->init->isVoidInitializer()) + { + ExpInitializer *ie = vd->init->isExpInitializer(); + assert(ie); + vto->init = new ExpInitializer(ie->loc, doInline(ie->exp, ids));; + } + are->lengthVar = vto; + } + if (e->lwr) + are->lwr = doInline(e->lwr, ids); + if (e->upr) + are->upr = doInline(e->upr, ids); + result = are; + } -Statement *ForeachRangeStatement::inlineScan(InlineScanState *iss) -{ - lwr = lwr->inlineScan(iss); - upr = upr->inlineScan(iss); - if (body) - body = body->inlineScan(iss); - return this; -} + void visit(TupleExp *e) + { + TupleExp *ce = (TupleExp *)e->copy(); + if (e->e0) + ce->e0 = doInline(e->e0, ids); + ce->exps = arrayExpressiondoInline(e->exps); + result = ce; + } -Statement *IfStatement::inlineScan(InlineScanState *iss) -{ - condition = condition->inlineScan(iss); - if (ifbody) - ifbody = ifbody->inlineScan(iss); - if (elsebody) - elsebody = elsebody->inlineScan(iss); - return this; -} + void visit(ArrayLiteralExp *e) + { + ArrayLiteralExp *ce = (ArrayLiteralExp *)e->copy(); + ce->elements = arrayExpressiondoInline(e->elements); + result = ce; + } -Statement *SwitchStatement::inlineScan(InlineScanState *iss) -{ - //printf("SwitchStatement::inlineScan()\n"); - condition = condition->inlineScan(iss); - body = body ? body->inlineScan(iss) : NULL; - if (sdefault) - sdefault = (DefaultStatement *)sdefault->inlineScan(iss); - if (cases) - { - for (size_t i = 0; i < cases->dim; i++) - { CaseStatement *s; + void visit(AssocArrayLiteralExp *e) + { + AssocArrayLiteralExp *ce = (AssocArrayLiteralExp *)e->copy(); - s = (*cases)[i]; - (*cases)[i] = (CaseStatement *)s->inlineScan(iss); + ce->keys = arrayExpressiondoInline(e->keys); + ce->values = arrayExpressiondoInline(e->values); + result = ce; } - } - return this; -} + void visit(StructLiteralExp *e) + { + if (e->inlinecopy) + { + result = e->inlinecopy; + return; + } + StructLiteralExp *ce = (StructLiteralExp *)e->copy(); -Statement *CaseStatement::inlineScan(InlineScanState *iss) -{ - //printf("CaseStatement::inlineScan()\n"); - exp = exp->inlineScan(iss); - if (statement) - statement = statement->inlineScan(iss); - return this; -} - + e->inlinecopy = ce; + ce->elements = arrayExpressiondoInline(e->elements); + e->inlinecopy = NULL; + result = ce; + } -Statement *DefaultStatement::inlineScan(InlineScanState *iss) -{ - if (statement) - statement = statement->inlineScan(iss); - return this; -} + void visit(ArrayExp *e) + { + ArrayExp *ce = (ArrayExp *)e->copy(); + ce->e1 = doInline(e->e1, ids); + ce->arguments = arrayExpressiondoInline(e->arguments); + result = ce; + } -Statement *ReturnStatement::inlineScan(InlineScanState *iss) -{ - //printf("ReturnStatement::inlineScan()\n"); - if (exp) - exp = exp->inlineScan(iss); - return this; -} + void visit(CondExp *e) + { + CondExp *ce = (CondExp *)e->copy(); + ce->econd = doInline(e->econd, ids); + ce->e1 = doInline(e->e1, ids); + ce->e2 = doInline(e->e2, ids); + result = ce; + } + }; -Statement *SynchronizedStatement::inlineScan(InlineScanState *iss) -{ - if (exp) - exp = exp->inlineScan(iss); - if (body) - body = body->inlineScan(iss); - return this; + InlineExpression v(ids); + e->accept(&v); + return v.result; } +/* ========== Walk the parse trees, and inline expand functions ============= */ + +/* Walk the trees, looking for functions to inline. + * Inline any that can be. + */ -Statement *WithStatement::inlineScan(InlineScanState *iss) +class InlineScanVisitor : public Visitor { - if (exp) - exp = exp->inlineScan(iss); - if (body) - body = body->inlineScan(iss); - return this; -} +public: + FuncDeclaration *parent; // function being scanned + // As the visit method cannot return a value, these variables + // are used to pass the result from 'visit' back to 'inlineScan' + Statement *result; + Expression *eresult; + InlineScanVisitor() + { + this->parent = NULL; + this->result = NULL; + this->eresult = NULL; + } -Statement *TryCatchStatement::inlineScan(InlineScanState *iss) -{ - if (body) - body = body->inlineScan(iss); - if (catches) + void visit(Statement *s) { - for (size_t i = 0; i < catches->dim; i++) - { Catch *c = (*catches)[i]; + } + + void visit(ExpStatement *s) + { + #if LOG + printf("ExpStatement::inlineScan(%s)\n", s->toChars()); + #endif + if (s->exp) + { + inlineScan(&s->exp); + + /* See if we can inline as a statement rather than as + * an Expression. + */ + if (s->exp && s->exp->op == TOKcall) + { + CallExp *ce = (CallExp *)s->exp; + if (ce->e1->op == TOKvar) + { + VarExp *ve = (VarExp *)ce->e1; + FuncDeclaration *fd = ve->var->isFuncDeclaration(); - if (c->handler) - c->handler = c->handler->inlineScan(iss); + if (fd && fd != parent && canInline(fd, 0, 0, 1)) + { + expandInline(fd, parent, NULL, NULL, ce->arguments, &result); + } + } + } } } - return this; -} - -Statement *TryFinallyStatement::inlineScan(InlineScanState *iss) -{ - if (body) - body = body->inlineScan(iss); - if (finalbody) - finalbody = finalbody->inlineScan(iss); - return this; -} + void visit(CompoundStatement *s) + { + for (size_t i = 0; i < s->statements->dim; i++) + { + inlineScan(&(*s->statements)[i]); + } + } + void visit(UnrolledLoopStatement *s) + { + for (size_t i = 0; i < s->statements->dim; i++) + { + inlineScan(&(*s->statements)[i]); + } + } -Statement *ThrowStatement::inlineScan(InlineScanState *iss) -{ - if (exp) - exp = exp->inlineScan(iss); - return this; -} + void visit(ScopeStatement *s) + { + inlineScan(&s->statement); + } + void visit(WhileStatement *s) + { + inlineScan(&s->condition); + inlineScan(&s->body); + } -Statement *LabelStatement::inlineScan(InlineScanState *iss) -{ - if (statement) - statement = statement->inlineScan(iss); - return this; -} + void visit(DoStatement *s) + { + inlineScan(&s->body); + inlineScan(&s->condition); + } -/* -------------------------- */ + void visit(ForStatement *s) + { + inlineScan(&s->init); + inlineScan(&s->condition); + inlineScan(&s->increment); + inlineScan(&s->body); + } -void arrayInlineScan(InlineScanState *iss, Expressions *arguments) -{ - if (arguments) + void visit(ForeachStatement *s) { - for (size_t i = 0; i < arguments->dim; i++) - { Expression *e = (*arguments)[i]; + inlineScan(&s->aggr); + inlineScan(&s->body); + } - if (e) - { - e = e->inlineScan(iss); - (*arguments)[i] = e; - } - } + void visit(ForeachRangeStatement *s) + { + inlineScan(&s->lwr); + inlineScan(&s->upr); + inlineScan(&s->body); } -} -Expression *Expression::inlineScan(InlineScanState *iss) -{ - return this; -} + void visit(IfStatement *s) + { + inlineScan(&s->condition); + inlineScan(&s->ifbody); + inlineScan(&s->elsebody); + } -Expression *scanVar(Dsymbol *s, InlineScanState *iss) -{ - //printf("scanVar(%s %s)\n", s->kind(), s->toPrettyChars()); - VarDeclaration *vd = s->isVarDeclaration(); - if (vd) + void visit(SwitchStatement *s) { - TupleDeclaration *td = vd->toAlias()->isTupleDeclaration(); - if (td) - { - for (size_t i = 0; i < td->objects->dim; i++) - { - DsymbolExp *se = (DsymbolExp *)(*td->objects)[i]; - assert(se->op == TOKdsymbol); - scanVar(se->s, iss); // TODO - } - } - else if (vd->init) + //printf("SwitchStatement::inlineScan()\n"); + inlineScan(&s->condition); + inlineScan(&s->body); + Statement *sdefault = s->sdefault; + inlineScan(&sdefault); + s->sdefault = (DefaultStatement *)sdefault; + if (s->cases) { - if (ExpInitializer *ie = vd->init->isExpInitializer()) + for (size_t i = 0; i < s->cases->dim; i++) { - Expression *e = ie->exp->inlineScan(iss); - if (vd->init != ie) // DeclareExp with vd appears in e - return e; - ie->exp = e; + Statement *scase = (*s->cases)[i]; + inlineScan(&scase); + (*s->cases)[i] = (CaseStatement *)scase; } } } - else + + void visit(CaseStatement *s) { - s->inlineScan(); + //printf("CaseStatement::inlineScan()\n"); + inlineScan(&s->exp); + inlineScan(&s->statement); } - return NULL; -} -Expression *DeclarationExp::inlineScan(InlineScanState *iss) -{ - //printf("DeclarationExp::inlineScan()\n"); - Expression *e = scanVar(declaration, iss); - return e ? e : this; -} + void visit(DefaultStatement *s) + { + inlineScan(&s->statement); + } -Expression *UnaExp::inlineScan(InlineScanState *iss) -{ - e1 = e1->inlineScan(iss); - return this; -} + void visit(ReturnStatement *s) + { + //printf("ReturnStatement::inlineScan()\n"); + inlineScan(&s->exp); + } -Expression *AssertExp::inlineScan(InlineScanState *iss) -{ - e1 = e1->inlineScan(iss); - if (msg) - msg = msg->inlineScan(iss); - return this; -} + void visit(SynchronizedStatement *s) + { + inlineScan(&s->exp); + inlineScan(&s->body); + } -Expression *BinExp::inlineScan(InlineScanState *iss) -{ - e1 = e1->inlineScan(iss); - e2 = e2->inlineScan(iss); - return this; -} + void visit(WithStatement *s) + { + inlineScan(&s->exp); + inlineScan(&s->body); + } -Expression *AssignExp::inlineScan(InlineScanState *iss) -{ - if (op == TOKconstruct && e2->op == TOKcall) + void visit(TryCatchStatement *s) { - CallExp *ce = (CallExp *)e2; - if (ce->f && ce->f->nrvo_var) // NRVO + inlineScan(&s->body); + if (s->catches) { - if (e1->op == TOKvar) - { - /* Inlining: - * S s = foo(); // initializing by rvalue - * S s = S(1); // constrcutor call - */ - Declaration *d = ((VarExp *)e1)->var; - if (d->storage_class & (STCout | STCref)) // refinit - goto L1; - } - else - { - /* Inlining: - * this.field = foo(); // inside constructor - */ - e1 = e1->inlineScan(iss); - } - - Expression *e = ce->inlineScan(iss, e1); - if (e != ce) + for (size_t i = 0; i < s->catches->dim; i++) { - //printf("call with nrvo: %s ==> %s\n", toChars(), e->toChars()); - return e; + Catch *c = (*s->catches)[i]; + inlineScan(&c->handler); } } } -L1: - return BinExp::inlineScan(iss); -} -Expression *CallExp::inlineScan(InlineScanState *iss) -{ - return inlineScan(iss, NULL); -} + void visit(TryFinallyStatement *s) + { + inlineScan(&s->body); + inlineScan(&s->finalbody); + } -Expression *CallExp::inlineScan(InlineScanState *iss, Expression *eret) -{ - Expression *e = this; + void visit(ThrowStatement *s) + { + inlineScan(&s->exp); + } - //printf("CallExp::inlineScan()\n"); - e1 = e1->inlineScan(iss); - arrayInlineScan(iss, arguments); + void visit(LabelStatement *s) + { + inlineScan(&s->statement); + } - if (e1->op == TOKvar) + void inlineScan(Statement **s) { - VarExp *ve = (VarExp *)e1; - FuncDeclaration *fd = ve->var->isFuncDeclaration(); + if (!*s) return; + Statement *save = result; + result = *s; + (*s)->accept(this); + *s = result; + result = save; + } - if (fd && fd != iss->fd && fd->canInline(0, 0, 0)) + /* -------------------------- */ + + void arrayInlineScan(Expressions *arguments) + { + if (arguments) { - Expression *ex = fd->expandInline(iss, eret, NULL, arguments, NULL); - if (ex) - e = ex; + for (size_t i = 0; i < arguments->dim; i++) + { + inlineScan(&(*arguments)[i]); + } } } - else if (e1->op == TOKdotvar) + + void visit(Expression *e) { - DotVarExp *dve = (DotVarExp *)e1; - FuncDeclaration *fd = dve->var->isFuncDeclaration(); + } - if (fd && fd != iss->fd && fd->canInline(1, 0, 0)) + Expression *scanVar(Dsymbol *s) + { + //printf("scanVar(%s %s)\n", s->kind(), s->toPrettyChars()); + VarDeclaration *vd = s->isVarDeclaration(); + if (vd) { - if (dve->e1->op == TOKcall && - dve->e1->type->toBasetype()->ty == Tstruct) + TupleDeclaration *td = vd->toAlias()->isTupleDeclaration(); + if (td) { - /* To create ethis, we'll need to take the address - * of dve->e1, but this won't work if dve->e1 is - * a function call. - */ - ; + for (size_t i = 0; i < td->objects->dim; i++) + { + DsymbolExp *se = (DsymbolExp *)(*td->objects)[i]; + assert(se->op == TOKdsymbol); + scanVar(se->s); // TODO + } } - else + else if (vd->init) { - Expression *ex = fd->expandInline(iss, eret, dve->e1, arguments, NULL); - if (ex) - e = ex; + if (ExpInitializer *ie = vd->init->isExpInitializer()) + { + Expression *e = ie->exp; + inlineScan(&e); + if (vd->init != ie) // DeclareExp with vd appears in e + return e; + ie->exp = e; + } } } + else + { + s->accept(this); + } + return NULL; } - if (e && type->ty != Tvoid && - !type->equals(e->type) && - e->type->hasWild() && !type->hasWild()) + void visit(DeclarationExp *e) { - e = e->copy(); - e->type = type; + //printf("DeclarationExp::inlineScan()\n"); + Expression *ed = scanVar(e->declaration); + if (ed) + eresult = ed; } - return e; -} + void visit(UnaExp *e) + { + inlineScan(&e->e1); + } -Expression *SliceExp::inlineScan(InlineScanState *iss) -{ - e1 = e1->inlineScan(iss); - if (lwr) - lwr = lwr->inlineScan(iss); - if (upr) - upr = upr->inlineScan(iss); - return this; -} + void visit(AssertExp *e) + { + inlineScan(&e->e1); + inlineScan(&e->msg); + } + void visit(BinExp *e) + { + inlineScan(&e->e1); + inlineScan(&e->e2); + } -Expression *TupleExp::inlineScan(InlineScanState *iss) -{ Expression *e = this; + void visit(AssignExp *e) + { + if (e->op == TOKconstruct && e->e2->op == TOKcall) + { + CallExp *ce = (CallExp *)e->e2; + if (ce->f && ce->f->nrvo_var) // NRVO + { + if (e->e1->op == TOKvar) + { + /* Inlining: + * S s = foo(); // initializing by rvalue + * S s = S(1); // constrcutor call + */ + Declaration *d = ((VarExp *)e->e1)->var; + if (d->storage_class & (STCout | STCref)) // refinit + goto L1; + } + else + { + /* Inlining: + * this.field = foo(); // inside constructor + */ + inlineScan(&e->e1); + } - //printf("TupleExp::inlineScan()\n"); - if (e0) - e0->inlineScan(iss); - arrayInlineScan(iss, exps); + visitCallExp(ce, e->e1); + if (eresult) + { + //printf("call with nrvo: %s ==> %s\n", e->toChars(), eresult->toChars()); + return; + } + } + } + L1: + visit((BinExp *)e); + } - return e; -} + void visit(CallExp *e) + { + visitCallExp(e, NULL); + } + void visitCallExp(CallExp *e, Expression *eret) + { + //printf("CallExp::inlineScan()\n"); + inlineScan(&e->e1); + arrayInlineScan(e->arguments); -Expression *ArrayLiteralExp::inlineScan(InlineScanState *iss) -{ Expression *e = this; + if (e->e1->op == TOKvar) + { + VarExp *ve = (VarExp *)e->e1; + FuncDeclaration *fd = ve->var->isFuncDeclaration(); - //printf("ArrayLiteralExp::inlineScan()\n"); - arrayInlineScan(iss, elements); + if (fd && fd != parent && canInline(fd, 0, 0, 0)) + { + Expression *ex = expandInline(fd, parent, eret, NULL, e->arguments, NULL); + if (ex) + { + eresult = ex; + if (global.params.verbose) + fprintf(global.stdmsg, "inlined %s =>\n %s\n", fd->toPrettyChars(), parent->toPrettyChars()); + } + } + } + else if (e->e1->op == TOKdotvar) + { + DotVarExp *dve = (DotVarExp *)e->e1; + FuncDeclaration *fd = dve->var->isFuncDeclaration(); - return e; -} + if (fd && fd != parent && canInline(fd, 1, 0, 0)) + { + if (dve->e1->op == TOKcall && + dve->e1->type->toBasetype()->ty == Tstruct) + { + /* To create ethis, we'll need to take the address + * of dve->e1, but this won't work if dve->e1 is + * a function call. + */ + ; + } + else + { + Expression *ex = expandInline(fd, parent, eret, dve->e1, e->arguments, NULL); + if (ex) + { + eresult = ex; + if (global.params.verbose) + fprintf(global.stdmsg, "inlined %s =>\n %s\n", fd->toPrettyChars(), parent->toPrettyChars()); + } + } + } + } + if (eresult && e->type->ty != Tvoid) + { + Expression *ex = eresult; + while (ex->op == TOKcomma) + { + ex->type = e->type; + ex = ((CommaExp *)ex)->e2; + } + ex->type = e->type; + } + } -Expression *AssocArrayLiteralExp::inlineScan(InlineScanState *iss) -{ Expression *e = this; + void visit(SliceExp *e) + { + inlineScan(&e->e1); + inlineScan(&e->lwr); + inlineScan(&e->upr); + } - //printf("AssocArrayLiteralExp::inlineScan()\n"); - arrayInlineScan(iss, keys); - arrayInlineScan(iss, values); + void visit(TupleExp *e) + { + //printf("TupleExp::inlineScan()\n"); + inlineScan(&e->e0); + arrayInlineScan(e->exps); + } - return e; -} + void visit(ArrayLiteralExp *e) + { + //printf("ArrayLiteralExp::inlineScan()\n"); + arrayInlineScan(e->elements); + } + void visit(AssocArrayLiteralExp *e) + { + //printf("AssocArrayLiteralExp::inlineScan()\n"); + arrayInlineScan(e->keys); + arrayInlineScan(e->values); + } -Expression *StructLiteralExp::inlineScan(InlineScanState *iss) -{ Expression *e = this; + void visit(StructLiteralExp *e) + { + //printf("StructLiteralExp::inlineScan()\n"); + if (e->stageflags & stageInlineScan) return; + int old = e->stageflags; + e->stageflags |= stageInlineScan; + arrayInlineScan(e->elements); + e->stageflags = old; + } - //printf("StructLiteralExp::inlineScan()\n"); - if(stageflags & stageInlineScan) return e; - int old = stageflags; - stageflags |= stageInlineScan; - arrayInlineScan(iss, elements); - stageflags = old; - return e; -} + void visit(ArrayExp *e) + { + //printf("ArrayExp::inlineScan()\n"); + inlineScan(&e->e1); + arrayInlineScan(e->arguments); + } + + void visit(CondExp *e) + { + inlineScan(&e->econd); + inlineScan(&e->e1); + inlineScan(&e->e2); + } + void inlineScan(Expression **e) + { + if (!*e) return; + Expression *save = eresult; + eresult = *e; + (*e)->accept(this); + assert(eresult); + *e = eresult; + eresult = save; + } -Expression *ArrayExp::inlineScan(InlineScanState *iss) -{ Expression *e = this; + /************************************* + * Look for function inlining possibilities. + */ - //printf("ArrayExp::inlineScan()\n"); - e1 = e1->inlineScan(iss); - arrayInlineScan(iss, arguments); + void visit(Dsymbol *d) + { + // Most Dsymbols aren't functions + } - return e; -} + void visit(FuncDeclaration *fd) + { + #if LOG + printf("FuncDeclaration::inlineScan('%s')\n", fd->toPrettyChars()); + #endif + if (fd->isUnitTestDeclaration() && !global.params.useUnitTests) + return; + + FuncDeclaration *oldparent = parent; + parent = fd; + if (fd->fbody && !fd->naked) + { + fd->inlineNest++; + inlineScan(&fd->fbody); + fd->inlineNest--; + } + parent = oldparent; + } + void visit(AttribDeclaration *d) + { + Dsymbols *decls = d->include(NULL, NULL); -Expression *CondExp::inlineScan(InlineScanState *iss) -{ - econd = econd->inlineScan(iss); - e1 = e1->inlineScan(iss); - e2 = e2->inlineScan(iss); - return this; -} + if (decls) + { + for (size_t i = 0; i < decls->dim; i++) + { + Dsymbol *s = (*decls)[i]; + //printf("AttribDeclaration::inlineScan %s\n", s->toChars()); + s->accept(this); + } + } + } + void visit(AggregateDeclaration *ad) + { + //printf("AggregateDeclaration::inlineScan(%s)\n", toChars()); + if (ad->members) + { + for (size_t i = 0; i < ad->members->dim; i++) + { + Dsymbol *s = (*ad->members)[i]; + //printf("inline scan aggregate symbol '%s'\n", s->toChars()); + s->accept(this); + } + } + } -/* ========== =============== */ + void visit(TemplateInstance *ti) + { + #if LOG + printf("TemplateInstance::inlineScan('%s')\n", ti->toChars()); + #endif + if (!ti->errors && ti->members) + { + for (size_t i = 0; i < ti->members->dim; i++) + { + Dsymbol *s = (*ti->members)[i]; + s->accept(this); + } + } + } +}; -void FuncDeclaration::inlineScan() +// scan for functions to inline +void inlineScan(Module *m) { - InlineScanState iss; + if (m->semanticRun != PASSsemantic3done) + return; + m->semanticRun = PASSinline; -#if LOG - printf("FuncDeclaration::inlineScan('%s')\n", toPrettyChars()); -#endif - memset(&iss, 0, sizeof(iss)); - iss.fd = this; - if (fbody && !naked) + // Note that modules get their own scope, from scratch. + // This is so regardless of where in the syntax a module + // gets imported, it is unaffected by context. + //printf("Module = %p\n", m->sc.scopesym); + + for (size_t i = 0; i < m->members->dim; i++) { - inlineNest++; - fbody = fbody->inlineScan(&iss); - inlineNest--; + Dsymbol *s = (*m->members)[i]; + //if (global.params.verbose) + // fprintf(global.stdmsg, "inline scan symbol %s\n", s->toChars()); + InlineScanVisitor v; + s->accept(&v); } + m->semanticRun = PASSinlinedone; } -int FuncDeclaration::canInline(int hasthis, int hdrscan, int statementsToo) +int canInline(FuncDeclaration *fd, int hasthis, int hdrscan, int statementsToo) { - InlineCostState ics; int cost; #define CANINLINE_LOG 0 #if CANINLINE_LOG - printf("FuncDeclaration::canInline(hasthis = %d, statementsToo = %d, '%s')\n", hasthis, statementsToo, toPrettyChars()); + printf("FuncDeclaration::canInline(hasthis = %d, statementsToo = %d, '%s')\n", hasthis, statementsToo, fd->toPrettyChars()); #endif - if (needThis() && !hasthis) + if (fd->needThis() && !hasthis) return 0; - if (inlineNest || (semanticRun < PASSsemantic3 && !hdrscan)) + if (fd->inlineNest || (fd->semanticRun < PASSsemantic3 && !hdrscan)) { #if CANINLINE_LOG - printf("\t1: no, inlineNest = %d, semanticRun = %d\n", inlineNest, semanticRun); + printf("\t1: no, inlineNest = %d, semanticRun = %d\n", fd->inlineNest, fd->semanticRun); #endif return 0; } -#if 1 - switch (statementsToo ? inlineStatusStmt : inlineStatusExp) + switch (statementsToo ? fd->inlineStatusStmt : fd->inlineStatusExp) { case ILSyes: #if CANINLINE_LOG - printf("\t1: yes %s\n", toChars()); + printf("\t1: yes %s\n", fd->toChars()); #endif return 1; case ILSno: #if CANINLINE_LOG - printf("\t1: no %s\n", toChars()); + printf("\t1: no %s\n", fd->toChars()); #endif return 0; @@ -1554,11 +1653,11 @@ int FuncDeclaration::canInline(int hasthis, int hdrscan, int statementsToo) default: assert(0); } -#endif - if (type) - { assert(type->ty == Tfunction); - TypeFunction *tf = (TypeFunction *)type; + if (fd->type) + { + assert(fd->type->ty == Tfunction); + TypeFunction *tf = (TypeFunction *)fd->type; if (tf->varargs == 1) // no variadic parameter lists goto Lno; @@ -1567,7 +1666,7 @@ int FuncDeclaration::canInline(int hasthis, int hdrscan, int statementsToo) * No statement inlining for non-voids. */ if (tf->next && tf->next->ty != Tvoid && - (!(hasReturnExp & 1) || statementsToo) && + (!(fd->hasReturnExp & 1) || statementsToo) && !hdrscan) goto Lno; } @@ -1576,45 +1675,36 @@ int FuncDeclaration::canInline(int hasthis, int hdrscan, int statementsToo) // return; // to: // return this; - if ( - !fbody || - ident == Id::ensure || // ensure() has magic properties the inliner loses - (ident == Id::require && // require() has magic properties too - toParent()->isFuncDeclaration() && // see bug 7699 - toParent()->isFuncDeclaration()->needThis()) || + // ensure() has magic properties the inliner loses + // require() has magic properties too + // see bug 7699 + // no nested references to this frame + if (!fd->fbody || + fd->ident == Id::ensure || + (fd->ident == Id::require && + fd->toParent()->isFuncDeclaration() && + fd->toParent()->isFuncDeclaration()->needThis()) || !hdrscan && ( - isSynchronized() || - isImportedSymbol() || - hasNestedFrameRefs() || // no nested references to this frame - (isVirtual() && !isFinalFunc()) + fd->isSynchronized() || + fd->isImportedSymbol() || + fd->hasNestedFrameRefs() || + (fd->isVirtual() && !fd->isFinalFunc()) )) { goto Lno; } -#if 0 - /* If any parameters are Tsarray's (which are passed by reference) - * or out parameters (also passed by reference), don't do inlining. - */ - if (parameters) { - for (size_t i = 0; i < parameters->dim; i++) - { - VarDeclaration *v = (*parameters)[i]; - if (v->type->toBasetype()->ty == Tsarray) - goto Lno; - } + InlineCostVisitor icv; + icv.hasthis = hasthis; + icv.fd = fd; + icv.hdrscan = hdrscan; + fd->fbody->accept(&icv); + cost = icv.cost; } -#endif - - memset(&ics, 0, sizeof(ics)); - ics.hasthis = hasthis; - ics.fd = this; - ics.hdrscan = hdrscan; - cost = fbody->inlineCost(&ics); #if CANINLINE_LOG - printf("cost = %d for %s\n", cost, toChars()); + printf("cost = %d for %s\n", cost, fd->toChars()); #endif if (tooCostly(cost)) goto Lno; @@ -1625,22 +1715,24 @@ int FuncDeclaration::canInline(int hasthis, int hdrscan, int statementsToo) { // Don't modify inlineStatus for header content scan if (statementsToo) - inlineStatusStmt = ILSyes; + fd->inlineStatusStmt = ILSyes; else - inlineStatusExp = ILSyes; + fd->inlineStatusExp = ILSyes; - inlineScan(); // Don't scan recursively for header content scan + InlineScanVisitor v; + fd->accept(&v); // Don't scan recursively for header content scan - if (inlineStatusExp == ILSuninitialized) + if (fd->inlineStatusExp == ILSuninitialized) { // Need to redo cost computation, as some statements or expressions have been inlined - memset(&ics, 0, sizeof(ics)); - ics.hasthis = hasthis; - ics.fd = this; - ics.hdrscan = hdrscan; - cost = fbody->inlineCost(&ics); + InlineCostVisitor icv; + icv.hasthis = hasthis; + icv.fd = fd; + icv.hdrscan = hdrscan; + fd->fbody->accept(&icv); + cost = icv.cost; #if CANINLINE_LOG - printf("recomputed cost = %d for %s\n", cost, toChars()); + printf("recomputed cost = %d for %s\n", cost, fd->toChars()); #endif if (tooCostly(cost)) goto Lno; @@ -1648,44 +1740,45 @@ int FuncDeclaration::canInline(int hasthis, int hdrscan, int statementsToo) goto Lno; if (statementsToo) - inlineStatusStmt = ILSyes; + fd->inlineStatusStmt = ILSyes; else - inlineStatusExp = ILSyes; + fd->inlineStatusExp = ILSyes; } } #if CANINLINE_LOG - printf("\t2: yes %s\n", toChars()); + printf("\t2: yes %s\n", fd->toChars()); #endif return 1; Lno: if (!hdrscan) // Don't modify inlineStatus for header content scan - { if (statementsToo) - inlineStatusStmt = ILSno; + { + if (statementsToo) + fd->inlineStatusStmt = ILSno; else - inlineStatusExp = ILSno; + fd->inlineStatusExp = ILSno; } #if CANINLINE_LOG - printf("\t2: no %s\n", toChars()); + printf("\t2: no %s\n", fd->toChars()); #endif return 0; } -Expression *FuncDeclaration::expandInline(InlineScanState *iss, +static Expression *expandInline(FuncDeclaration *fd, FuncDeclaration *parent, Expression *eret, Expression *ethis, Expressions *arguments, Statement **ps) { InlineDoState ids; Expression *e = NULL; Statements *as = NULL; - TypeFunction *tf = (TypeFunction*)type; + TypeFunction *tf = (TypeFunction*)fd->type; #if LOG || CANINLINE_LOG printf("FuncDeclaration::expandInline('%s')\n", toChars()); #endif memset(&ids, 0, sizeof(ids)); - ids.parent = iss->fd; - ids.fd = this; + ids.parent = parent; + ids.fd = fd; if (ps) as = new Statements(); @@ -1703,20 +1796,20 @@ Expression *FuncDeclaration::expandInline(InlineScanState *iss, /* Inlining: * this.field = foo(); // inside constructor */ - vret = new VarDeclaration(loc, eret->type, Lexer::uniqueId("_satmp"), NULL); + vret = new VarDeclaration(fd->loc, eret->type, Lexer::uniqueId("_satmp"), NULL); vret->storage_class |= STCtemp | STCforeach | STCref; vret->linkage = LINKd; - vret->parent = iss->fd; + vret->parent = parent; Expression *de; - de = new DeclarationExp(loc, vret); + de = new DeclarationExp(fd->loc, vret); de->type = Type::tvoid; e = Expression::combine(e, de); Expression *ex; - ex = new VarExp(loc, vret); + ex = new VarExp(fd->loc, vret); ex->type = vret->type; - ex = new ConstructExp(loc, ex, eret); + ex = new ConstructExp(fd->loc, ex, eret); ex->type = vret->type; e = Expression::combine(e, ex); } @@ -1742,7 +1835,7 @@ Expression *FuncDeclaration::expandInline(InlineScanState *iss, else vthis->storage_class = STCin; vthis->linkage = LINKd; - vthis->parent = iss->fd; + vthis->parent = parent; ve = new VarExp(vthis->loc, vthis); ve->type = vthis->type; @@ -1766,23 +1859,23 @@ Expression *FuncDeclaration::expandInline(InlineScanState *iss, e = Expression::combine(e, de); } - if (!ps && nrvo_var) + if (!ps && fd->nrvo_var) { if (vret) { - ids.from.push(nrvo_var); + ids.from.push(fd->nrvo_var); ids.to.push(vret); } else { Identifier* tmp = Identifier::generateId("__nrvoretval"); - VarDeclaration* vd = new VarDeclaration(loc, nrvo_var->type, tmp, NULL); + VarDeclaration* vd = new VarDeclaration(fd->loc, fd->nrvo_var->type, tmp, NULL); assert(!tf->isref); vd->storage_class = STCtemp | STCrvalue; vd->linkage = tf->linkage; - vd->parent = iss->fd; + vd->parent = parent; - ids.from.push(nrvo_var); + ids.from.push(fd->nrvo_var); ids.to.push(vd); Expression *de = new DeclarationExp(Loc(), vd); @@ -1792,11 +1885,11 @@ Expression *FuncDeclaration::expandInline(InlineScanState *iss, } if (arguments && arguments->dim) { - assert(parameters->dim == arguments->dim); + assert(fd->parameters->dim == arguments->dim); for (size_t i = 0; i < arguments->dim; i++) { - VarDeclaration *vfrom = (*parameters)[i]; + VarDeclaration *vfrom = (*fd->parameters)[i]; VarDeclaration *vto; Expression *arg = (*arguments)[i]; ExpInitializer *ei; @@ -1807,15 +1900,18 @@ Expression *FuncDeclaration::expandInline(InlineScanState *iss, vto = new VarDeclaration(vfrom->loc, vfrom->type, vfrom->ident, ei); vto->storage_class |= vfrom->storage_class & (STCtemp | STCin | STCout | STClazy | STCref); vto->linkage = vfrom->linkage; - vto->parent = iss->fd; + vto->parent = parent; //printf("vto = '%s', vto->storage_class = x%x\n", vto->toChars(), vto->storage_class); - //printf("vto->parent = '%s'\n", iss->fd->toChars()); + //printf("vto->parent = '%s'\n", parent->toChars()); ve = new VarExp(vto->loc, vto); //ve->type = vto->type; ve->type = arg->type; - ei->exp = new ConstructExp(vto->loc, ve, arg); + if (vfrom->storage_class & (STCout | STCref)) + ei->exp = new ConstructExp(vto->loc, ve, arg); + else + ei->exp = new BlitExp(vto->loc, ve, arg); ei->exp->type = ve->type; //ve->type->print(); //arg->type->print(); @@ -1835,21 +1931,21 @@ Expression *FuncDeclaration::expandInline(InlineScanState *iss, { if (e) as->push(new ExpStatement(Loc(), e)); - inlineNest++; - Statement *s = fbody->doInlineStatement(&ids); + fd->inlineNest++; + Statement *s = inlineAsStatement(fd->fbody, &ids); as->push(s); *ps = new ScopeStatement(Loc(), new CompoundStatement(Loc(), as)); - inlineNest--; + fd->inlineNest--; } else { - inlineNest++; - Expression *eb = fbody->doInline(&ids); + fd->inlineNest++; + Expression *eb = doInline(fd->fbody, &ids); e = Expression::combine(e, eb); - inlineNest--; + fd->inlineNest--; //eb->type->print(); //eb->print(); - //eb->dump(0); + //eb->print(); // Bugzilla 11322: if (tf->isref) @@ -1868,24 +1964,24 @@ Expression *FuncDeclaration::expandInline(InlineScanState *iss, * the returned reference is exactly same as vthis, and the 'this' variable * already exists at the caller side. */ - if (tf->next->ty == Tstruct && !nrvo_var && !isCtorDeclaration()) + if (tf->next->ty == Tstruct && !fd->nrvo_var && !fd->isCtorDeclaration()) { /* Generate a new variable to hold the result and initialize it with the * inlined body of the function: * tret __inlineretval = e; */ - ExpInitializer* ei = new ExpInitializer(loc, e); + ExpInitializer* ei = new ExpInitializer(fd->loc, e); Identifier* tmp = Identifier::generateId("__inlineretval"); - VarDeclaration* vd = new VarDeclaration(loc, tf->next, tmp, ei); + VarDeclaration* vd = new VarDeclaration(fd->loc, tf->next, tmp, ei); vd->storage_class = (tf->isref ? STCref : 0) | STCtemp | STCrvalue; vd->linkage = tf->linkage; - vd->parent = iss->fd; + vd->parent = parent; - VarExp *ve = new VarExp(loc, vd); + VarExp *ve = new VarExp(fd->loc, vd); ve->type = tf->next; - ei->exp = new ConstructExp(loc, ve, e); + ei->exp = new ConstructExp(fd->loc, ve, e); ei->exp->type = ve->type; DeclarationExp* de = new DeclarationExp(Loc(), vd); @@ -1902,55 +1998,46 @@ Expression *FuncDeclaration::expandInline(InlineScanState *iss, // Need to reevaluate whether parent can now be inlined // in expressions, as we might have inlined statements - iss->fd->inlineStatusExp = ILSuninitialized; + parent->inlineStatusExp = ILSuninitialized; return e; } -void UnitTestDeclaration::inlineScan() -{ - if (global.params.useUnitTests) - { - FuncDeclaration::inlineScan(); - } -} - /**************************************************** * Perform the "inline copying" of a default argument for a function parameter. */ -Expression *Expression::inlineCopy(Scope *sc) +Expression *inlineCopy(Expression *e, Scope *sc) { -#if 0 /* See Bugzilla 2935 for explanation of why just a copy() is broken */ - return copy(); -#else - if (op == TOKdelegate) - { DelegateExp *de = (DelegateExp *)this; + //return e->copy(); + + if (e->op == TOKdelegate) + { + DelegateExp *de = (DelegateExp *)e; if (de->func->isNested()) - { /* See Bugzilla 4820 + { + /* See Bugzilla 4820 * Defer checking until later if we actually need the 'this' pointer */ - Expression *e = de->copy(); - return e; + return de->copy(); } } - InlineCostState ics; - - memset(&ics, 0, sizeof(ics)); - ics.hdrscan = 1; // so DeclarationExp:: will work on 'statics' which are not - int cost = expressionInlineCost(this, &ics); + InlineCostVisitor icv; + icv.hdrscan = 1; + icv.allowAlloca = true; + icv.expressionInlineCost(e); + int cost = icv.cost; if (cost >= COST_MAX) - { error("cannot inline default argument %s", toChars()); + { + e->error("cannot inline default argument %s", e->toChars()); return new ErrorExp(); } InlineDoState ids; memset(&ids, 0, sizeof(ids)); ids.parent = sc->parent; - Expression *e = doInline(&ids); - return e; -#endif + return doInline(e, &ids); } diff --git a/gcc/d/dfrontend/interpret.c b/gcc/d/dfrontend/interpret.c index f6c59f489..2a3423d22 100644 --- a/gcc/d/dfrontend/interpret.c +++ b/gcc/d/dfrontend/interpret.c @@ -1,11 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. + +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/interpret.c + */ #include #include @@ -31,9 +33,7 @@ #include "port.h" #include "ctfe.h" -#ifdef IN_GCC -#include "d-dmd-gcc.h" -#endif +bool walkPostorder(Expression *e, StoppableVisitor *v); #define LOG 0 #define LOGASSIGN 0 @@ -102,12 +102,14 @@ struct InterState InterState *caller; // calling function's InterState FuncDeclaration *fd; // function being interpreted Statement *start; // if !=NULL, start execution at this statement - Statement *gotoTarget; /* target of EXP_GOTO_INTERPRET result; also - * target of labelled EXP_BREAK_INTERPRET or - * EXP_CONTINUE_INTERPRET. (NULL if no label). - */ - bool awaitingLvalueReturn; // Support for ref return values: - // Any return to this function should return an lvalue. + /* target of EXP_GOTO_INTERPRET result; also + * target of labelled EXP_BREAK_INTERPRET or + * EXP_CONTINUE_INTERPRET. (NULL if no label). + */ + Statement *gotoTarget; + // Support for ref return values: + // Any return to this function should return an lvalue. + bool awaitingLvalueReturn; InterState(); }; @@ -137,7 +139,6 @@ size_t CtfeStack::maxStackUsage() void CtfeStack::startFrame(Expression *thisexp) { - size_t oldframe = framepointer; frames.push((void *)(size_t)(framepointer)); savedThis.push(localThis); framepointer = stackPointer(); @@ -226,11 +227,7 @@ void CtfeStack::popAll(size_t stackpointer) void CtfeStack::saveGlobalConstant(VarDeclaration *v, Expression *e) { -#if DMDV2 assert( v->init && (v->isConst() || v->isImmutable() || v->storage_class & STCmanifest) && !v->isCTFE()); -#else - assert( v->init && v->isConst() && !v->isCTFE()); -#endif v->ctfeAdrOnStack = (int)globalValues.dim; globalValues.push(e); } @@ -260,16 +257,13 @@ void printCtfePerformanceStats() #endif } - -Expression * resolveReferences(Expression *e); -Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal); VarDeclaration *findParentVar(Expression *e); Expression *evaluateIfBuiltin(InterState *istate, Loc loc, FuncDeclaration *fd, Expressions *arguments, Expression *pthis); +Expression *evaluatePostblits(InterState *istate, ArrayLiteralExp *ale, size_t lwr, size_t upr); Expression *scrubReturnValue(Loc loc, Expression *e); - /************************************* * CTFE-object code for a single function * @@ -292,401 +286,434 @@ struct CompiledCtfeFunction //printf("%s CTFE declare %s\n", v->loc.toChars(), v->toChars()); ++numVars; } - static int walkAllVars(Expression *e, void *_this); + void onExpression(Expression *e) { - e->apply(&walkAllVars, this); + class VarWalker : public StoppableVisitor + { + public: + CompiledCtfeFunction *ccf; + + VarWalker(CompiledCtfeFunction *ccf) + : ccf(ccf) + { + } + + void visit(Expression *e) + { + } + + void visit(ErrorExp *e) + { + // Currently there's a front-end bug: silent errors + // can occur inside delegate literals inside is(typeof()). + // Suppress the check in this case. + if (global.gag && ccf->func) + { + stop = 1; + return; + } + + ::error(e->loc, "CTFE internal error: ErrorExp in %s\n", ccf->func ? ccf->func->loc.toChars() : ccf->callingloc.toChars()); + assert(0); + } + + void visit(DeclarationExp *e) + { + VarDeclaration *v = e->declaration->isVarDeclaration(); + if (!v) + return; + TupleDeclaration *td = v->toAlias()->isTupleDeclaration(); + if (td) + { + if (!td->objects) + return; + for(size_t i= 0; i < td->objects->dim; ++i) + { + RootObject *o = td->objects->tdata()[i]; + Expression *ex = isExpression(o); + DsymbolExp *s = (ex && ex->op == TOKdsymbol) ? (DsymbolExp *)ex : NULL; + assert(s); + VarDeclaration *v2 = s->s->isVarDeclaration(); + assert(v2); + if (!v2->isDataseg() || v2->isCTFE()) + ccf->onDeclaration(v2); + } + } + else if (!(v->isDataseg() || v->storage_class & STCmanifest) || v->isCTFE()) + ccf->onDeclaration(v); + Dsymbol *s = v->toAlias(); + if (s == v && !v->isStatic() && v->init) + { + ExpInitializer *ie = v->init->isExpInitializer(); + if (ie) + ccf->onExpression(ie->exp); + } + } + + void visit(IndexExp *e) + { + if (e->lengthVar) + ccf->onDeclaration(e->lengthVar); + } + + void visit(SliceExp *e) + { + if (e->lengthVar) + ccf->onDeclaration(e->lengthVar); + } + }; + + VarWalker v(this); + walkPostorder(e, &v); } }; -int CompiledCtfeFunction::walkAllVars(Expression *e, void *_this) +class CtfeCompiler : public Visitor { - CompiledCtfeFunction *ccf = (CompiledCtfeFunction *)_this; - if (e->op == TOKerror) +public: + CompiledCtfeFunction *ccf; + + CtfeCompiler(CompiledCtfeFunction *ccf) + : ccf(ccf) { - // Currently there's a front-end bug: silent errors - // can occur inside delegate literals inside is(typeof()). - // Suppress the check in this case. - if (global.gag && ccf->func) - return 1; + } - e->error("CTFE internal error: ErrorExp in %s\n", ccf->func ? ccf->func->loc.toChars() : ccf->callingloc.toChars()); + void visit(Statement *s) + { + #if LOGCOMPILE + printf("%s Statement::ctfeCompile %s\n", s->loc.toChars(), s->toChars()); + #endif assert(0); } - if (e->op == TOKdeclaration) + + void visit(ExpStatement *s) + { + #if LOGCOMPILE + printf("%s ExpStatement::ctfeCompile\n", s->loc.toChars()); + #endif + if (s->exp) + ccf->onExpression(s->exp); + } + + void visit(CompoundStatement *s) { - DeclarationExp *decl = (DeclarationExp *)e; - VarDeclaration *v = decl->declaration->isVarDeclaration(); - if (!v) - return 0; - TupleDeclaration *td = v->toAlias()->isTupleDeclaration(); - if (td) + #if LOGCOMPILE + printf("%s CompoundStatement::ctfeCompile\n", s->loc.toChars()); + #endif + for (size_t i = 0; i < s->statements->dim; i++) { - if (!td->objects) - return 0; - for(size_t i= 0; i < td->objects->dim; ++i) - { - RootObject *o = td->objects->tdata()[i]; - Expression *ex = isExpression(o); - DsymbolExp *s = (ex && ex->op == TOKdsymbol) ? (DsymbolExp *)ex : NULL; - assert(s); - VarDeclaration *v2 = s->s->isVarDeclaration(); - assert(v2); - if (!v2->isDataseg() || v2->isCTFE()) - ccf->onDeclaration(v2); - } + Statement *sx = (*s->statements)[i]; + if (sx) + ctfeCompile(sx); } - else if (!(v->isDataseg() || v->storage_class & STCmanifest) || v->isCTFE()) - ccf->onDeclaration(v); - Dsymbol *s = v->toAlias(); - if (s == v && !v->isStatic() && v->init) + } + + void visit(UnrolledLoopStatement *s) + { + #if LOGCOMPILE + printf("%s UnrolledLoopStatement::ctfeCompile\n", s->loc.toChars()); + #endif + for (size_t i = 0; i < s->statements->dim; i++) { - ExpInitializer *ie = v->init->isExpInitializer(); - if (ie) - ccf->onExpression(ie->exp); + Statement *sx = (*s->statements)[i]; + if (sx) + ctfeCompile(sx); } } - else if (e->op == TOKindex && ((IndexExp *)e)->lengthVar) - ccf->onDeclaration( ((IndexExp *)e)->lengthVar); - else if (e->op == TOKslice && ((SliceExp *)e)->lengthVar) - ccf->onDeclaration( ((SliceExp *)e)->lengthVar); - return 0; -} - -void Statement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s Statement::ctfeCompile %s\n", loc.toChars(), toChars()); -#endif - assert(0); -} -void ExpStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s ExpStatement::ctfeCompile\n", loc.toChars()); -#endif - if (exp) - ccf->onExpression(exp); -} + void visit(IfStatement *s) + { + #if LOGCOMPILE + printf("%s IfStatement::ctfeCompile\n", s->loc.toChars()); + #endif -void CompoundStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s CompoundStatement::ctfeCompile\n", loc.toChars()); -#endif - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; - if (s) - s->ctfeCompile(ccf); + ccf->onExpression(s->condition); + if (s->ifbody) + ctfeCompile(s->ifbody); + if (s->elsebody) + ctfeCompile(s->elsebody); } -} -void UnrolledLoopStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s UnrolledLoopStatement::ctfeCompile\n", loc.toChars()); -#endif - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; - if (s) - s->ctfeCompile(ccf); + void visit(ScopeStatement *s) + { + #if LOGCOMPILE + printf("%s ScopeStatement::ctfeCompile\n", s->loc.toChars()); + #endif + if (s->statement) + ctfeCompile(s->statement); } -} - -void IfStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s IfStatement::ctfeCompile\n", loc.toChars()); -#endif - - ccf->onExpression(condition); - if (ifbody) - ifbody->ctfeCompile(ccf); - if (elsebody) - elsebody->ctfeCompile(ccf); -} - -void ScopeStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s ScopeStatement::ctfeCompile\n", loc.toChars()); -#endif - if (statement) - statement->ctfeCompile(ccf); -} - -void OnScopeStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s OnScopeStatement::ctfeCompile\n", loc.toChars()); -#endif - // rewritten to try/catch/finally - assert(0); -} - -void DoStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s DoStatement::ctfeCompile\n", loc.toChars()); -#endif - ccf->onExpression(condition); - if (body) - body->ctfeCompile(ccf); -} -void WhileStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s WhileStatement::ctfeCompile\n", loc.toChars()); -#endif - // rewritten to ForStatement - assert(0); -} + void visit(OnScopeStatement *s) + { + #if LOGCOMPILE + printf("%s OnScopeStatement::ctfeCompile\n", s->loc.toChars()); + #endif + // rewritten to try/catch/finally + assert(0); + } -void ForStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s ForStatement::ctfeCompile\n", loc.toChars()); -#endif + void visit(DoStatement *s) + { + #if LOGCOMPILE + printf("%s DoStatement::ctfeCompile\n", s->loc.toChars()); + #endif + ccf->onExpression(s->condition); + if (s->body) + ctfeCompile(s->body); + } - if (init) - init->ctfeCompile(ccf); - if (condition) - ccf->onExpression(condition); - if (increment) - ccf->onExpression(increment); - if (body) - body->ctfeCompile(ccf); -} + void visit(WhileStatement *s) + { + #if LOGCOMPILE + printf("%s WhileStatement::ctfeCompile\n", s->loc.toChars()); + #endif + // rewritten to ForStatement + assert(0); + } -void ForeachStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s ForeachStatement::ctfeCompile\n", loc.toChars()); -#endif - // rewritten for ForStatement - assert(0); -} + void visit(ForStatement *s) + { + #if LOGCOMPILE + printf("%s ForStatement::ctfeCompile\n", s->loc.toChars()); + #endif + if (s->init) + ctfeCompile(s->init); + if (s->condition) + ccf->onExpression(s->condition); + if (s->increment) + ccf->onExpression(s->increment); + if (s->body) + ctfeCompile(s->body); + } -void SwitchStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s SwitchStatement::ctfeCompile\n", loc.toChars()); -#endif - ccf->onExpression(condition); - // Note that the body contains the the Case and Default - // statements, so we only need to compile the expressions - for (size_t i = 0; i < cases->dim; i++) + void visit(ForeachStatement *s) { - ccf->onExpression((*cases)[i]->exp); + #if LOGCOMPILE + printf("%s ForeachStatement::ctfeCompile\n", s->loc.toChars()); + #endif + // rewritten for ForStatement + assert(0); } - if (body) - body->ctfeCompile(ccf); -} -void CaseStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s CaseStatement::ctfeCompile\n", loc.toChars()); -#endif - if (statement) - statement->ctfeCompile(ccf); -} + void visit(SwitchStatement *s) + { + #if LOGCOMPILE + printf("%s SwitchStatement::ctfeCompile\n", s->loc.toChars()); + #endif + ccf->onExpression(s->condition); + // Note that the body contains the the Case and Default + // statements, so we only need to compile the expressions + for (size_t i = 0; i < s->cases->dim; i++) + { + ccf->onExpression((*s->cases)[i]->exp); + } + if (s->body) + ctfeCompile(s->body); + } -void DefaultStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s DefaultStatement::ctfeCompile\n", loc.toChars()); -#endif - if (statement) - statement->ctfeCompile(ccf); -} + void visit(CaseStatement *s) + { + #if LOGCOMPILE + printf("%s CaseStatement::ctfeCompile\n", s->loc.toChars()); + #endif + if (s->statement) + ctfeCompile(s->statement); + } -void GotoDefaultStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s GotoDefaultStatement::ctfeCompile\n", loc.toChars()); -#endif -} + void visit(DefaultStatement *s) + { + #if LOGCOMPILE + printf("%s DefaultStatement::ctfeCompile\n", s->loc.toChars()); + #endif + if (s->statement) + ctfeCompile(s->statement); + } -void GotoCaseStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s GotoCaseStatement::ctfeCompile\n", loc.toChars()); -#endif -} + void visit(GotoDefaultStatement *s) + { + #if LOGCOMPILE + printf("%s GotoDefaultStatement::ctfeCompile\n", s->loc.toChars()); + #endif + } -void SwitchErrorStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s SwitchErrorStatement::ctfeCompile\n", loc.toChars()); -#endif -} + void visit(GotoCaseStatement *s) + { + #if LOGCOMPILE + printf("%s GotoCaseStatement::ctfeCompile\n", s->loc.toChars()); + #endif + } -void ReturnStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s ReturnStatement::ctfeCompile\n", loc.toChars()); -#endif - if (exp) - ccf->onExpression(exp); -} + void visit(SwitchErrorStatement *s) + { + #if LOGCOMPILE + printf("%s SwitchErrorStatement::ctfeCompile\n", s->loc.toChars()); + #endif + } -void BreakStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s BreakStatement::ctfeCompile\n", loc.toChars()); -#endif -} + void visit(ReturnStatement *s) + { + #if LOGCOMPILE + printf("%s ReturnStatement::ctfeCompile\n", s->loc.toChars()); + #endif + if (s->exp) + ccf->onExpression(s->exp); + } -void ContinueStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s ContinueStatement::ctfeCompile\n", loc.toChars()); -#endif -} + void visit(BreakStatement *s) + { + #if LOGCOMPILE + printf("%s BreakStatement::ctfeCompile\n", s->loc.toChars()); + #endif + } -void WithStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s WithStatement::ctfeCompile\n", loc.toChars()); -#endif - // If it is with(Enum) {...}, just execute the body. - if (exp->op == TOKimport || exp->op == TOKtype) - {} - else + void visit(ContinueStatement *s) { - ccf->onDeclaration(wthis); - ccf->onExpression(exp); + #if LOGCOMPILE + printf("%s ContinueStatement::ctfeCompile\n", s->loc.toChars()); + #endif } - if (body) - body->ctfeCompile(ccf); -} -void TryCatchStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s TryCatchStatement::ctfeCompile\n", loc.toChars()); -#endif - if (body) - body->ctfeCompile(ccf); - for (size_t i = 0; i < catches->dim; i++) + void visit(WithStatement *s) { - Catch *ca = (*catches)[i]; - if (ca->var) - ccf->onDeclaration(ca->var); - if (ca->handler) - ca->handler->ctfeCompile(ccf); + #if LOGCOMPILE + printf("%s WithStatement::ctfeCompile\n", s->loc.toChars()); + #endif + // If it is with(Enum) {...}, just execute the body. + if (s->exp->op == TOKimport || s->exp->op == TOKtype) + { + } + else + { + ccf->onDeclaration(s->wthis); + ccf->onExpression(s->exp); + } + if (s->body) + ctfeCompile(s->body); } -} -void TryFinallyStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s TryFinallyStatement::ctfeCompile\n", loc.toChars()); -#endif - if (body) - body->ctfeCompile(ccf); - if (finalbody) - finalbody->ctfeCompile(ccf); -} + void visit(TryCatchStatement *s) + { + #if LOGCOMPILE + printf("%s TryCatchStatement::ctfeCompile\n", s->loc.toChars()); + #endif + if (s->body) + ctfeCompile(s->body); + for (size_t i = 0; i < s->catches->dim; i++) + { + Catch *ca = (*s->catches)[i]; + if (ca->var) + ccf->onDeclaration(ca->var); + if (ca->handler) + ctfeCompile(ca->handler); + } + } -void ThrowStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s ThrowStatement::ctfeCompile\n", loc.toChars()); -#endif - ccf->onExpression(exp); -} + void visit(TryFinallyStatement *s) + { + #if LOGCOMPILE + printf("%s TryFinallyStatement::ctfeCompile\n", s->loc.toChars()); + #endif + if (s->body) + ctfeCompile(s->body); + if (s->finalbody) + ctfeCompile(s->finalbody); + } -void GotoStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s GotoStatement::ctfeCompile\n", loc.toChars()); -#endif -} + void visit(ThrowStatement *s) + { + #if LOGCOMPILE + printf("%s ThrowStatement::ctfeCompile\n", s->loc.toChars()); + #endif + ccf->onExpression(s->exp); + } -void LabelStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s LabelStatement::ctfeCompile\n", loc.toChars()); -#endif - if (statement) - statement->ctfeCompile(ccf); -} + void visit(GotoStatement *s) + { + #if LOGCOMPILE + printf("%s GotoStatement::ctfeCompile\n", s->loc.toChars()); + #endif + } -#if DMDV2 -void ImportStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s ImportStatement::ctfeCompile\n", loc.toChars()); -#endif - // Contains no variables or executable code -} + void visit(LabelStatement *s) + { + #if LOGCOMPILE + printf("%s LabelStatement::ctfeCompile\n", s->loc.toChars()); + #endif + if (s->statement) + ctfeCompile(s->statement); + } -void ForeachRangeStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s ForeachRangeStatement::ctfeCompile\n", loc.toChars()); -#endif - // rewritten for ForStatement - assert(0); -} + void visit(ImportStatement *s) + { + #if LOGCOMPILE + printf("%s ImportStatement::ctfeCompile\n", s->loc.toChars()); + #endif + // Contains no variables or executable code + } -#endif + void visit(ForeachRangeStatement *s) + { + #if LOGCOMPILE + printf("%s ForeachRangeStatement::ctfeCompile\n", s->loc.toChars()); + #endif + // rewritten for ForStatement + assert(0); + } -void AsmStatement::ctfeCompile(CompiledCtfeFunction *ccf) -{ -#if LOGCOMPILE - printf("%s AsmStatement::ctfeCompile\n", loc.toChars()); -#endif - // we can't compile asm statements -} + void visit(AsmStatement *s) + { + #if LOGCOMPILE + printf("%s AsmStatement::ctfeCompile\n", s->loc.toChars()); + #endif + // we can't compile asm statements + } #ifdef IN_GCC -// CTFE compile extended asm statement. - -void -ExtAsmStatement::ctfeCompile (CompiledCtfeFunction *) -{ -#if LOGCOMPILE - printf("%s ExtAsmStatement::ctfeCompile\n", loc.toChars()); -#endif - // We can't compile extended asm statements. -} + void visit(ExtAsmStatement *s) + { + #if LOGCOMPILE + printf("%s ExtAsmStatement::ctfeCompile\n", s->loc.toChars()); + #endif + // we can't compile ext asm statements + } #endif + void ctfeCompile(Statement *s) + { + s->accept(this); + } +}; + /************************************* * Compile this function for CTFE. * At present, this merely allocates variables. */ -void FuncDeclaration::ctfeCompile() +void ctfeCompile(FuncDeclaration *fd) { #if LOGCOMPILE - printf("\n%s FuncDeclaration::ctfeCompile %s\n", loc.toChars(), toChars()); + printf("\n%s FuncDeclaration::ctfeCompile %s\n", fd->loc.toChars(), fd->toChars()); #endif - assert(!ctfeCode); - assert(!semantic3Errors); - assert(semanticRun == PASSsemantic3done); + assert(!fd->ctfeCode); + assert(!fd->semantic3Errors); + assert(fd->semanticRun == PASSsemantic3done); - ctfeCode = new CompiledCtfeFunction(this); - if (parameters) + fd->ctfeCode = new CompiledCtfeFunction(fd); + if (fd->parameters) { - Type *tb = type->toBasetype(); + Type *tb = fd->type->toBasetype(); assert(tb->ty == Tfunction); - TypeFunction *tf = (TypeFunction *)tb; - for (size_t i = 0; i < parameters->dim; i++) + for (size_t i = 0; i < fd->parameters->dim; i++) { - Parameter *arg = Parameter::getNth(tf->parameters, i); - VarDeclaration *v = (*parameters)[i]; - ctfeCode->onDeclaration(v); + VarDeclaration *v = (*fd->parameters)[i]; + fd->ctfeCode->onDeclaration(v); } } - if (vresult) - ctfeCode->onDeclaration(vresult); - fbody->ctfeCompile(ctfeCode); + if (fd->vresult) + fd->ctfeCode->onDeclaration(fd->vresult); + CtfeCompiler v(fd->ctfeCode); + v.ctfeCompile(fd->fbody); } /************************************* @@ -694,24 +721,32 @@ void FuncDeclaration::ctfeCompile() * Entry point for CTFE. * A compile-time result is required. Give an error if not possible */ -Expression *Expression::ctfeInterpret() +Expression *ctfeInterpret(Expression *e) { - if (type == Type::terror) - return this; + if (e->op == TOKerror) + return e; + //assert(e->type->ty != Terror); // FIXME + if (e->type->ty == Terror) + return new ErrorExp(); + + unsigned olderrors = global.errors; // This code is outside a function, but still needs to be compiled // (there are compiler-generated temporary variables such as __dollar). // However, this will only be run once and can then be discarded. CompiledCtfeFunction ctfeCodeGlobal(NULL); - ctfeCodeGlobal.callingloc = loc; - ctfeCodeGlobal.onExpression(this); + ctfeCodeGlobal.callingloc = e->loc; + ctfeCodeGlobal.onExpression(e); - Expression *e = interpret(NULL); - if (e != EXP_CANT_INTERPRET) - e = scrubReturnValue(loc, e); - if (e == EXP_CANT_INTERPRET) - e = new ErrorExp(); - return e; + Expression *result = e->interpret(NULL); + if (result != EXP_CANT_INTERPRET) + result = scrubReturnValue(e->loc, result); + if (result == EXP_CANT_INTERPRET) + { + assert(global.errors != olderrors); + result = new ErrorExp(); + } + return result; } /* Run CTFE on the expression, but allow the expression to be a TypeExp @@ -776,32 +811,32 @@ Expression *ctfeInterpretForPragmaMsg(Expression *e) * or EXP_VOID_INTERPRET if function returned void. */ -Expression *FuncDeclaration::interpret(InterState *istate, Expressions *arguments, Expression *thisarg) +Expression *interpret(FuncDeclaration *fd, InterState *istate, Expressions *arguments, Expression *thisarg) { #if LOG - printf("\n********\n%s FuncDeclaration::interpret(istate = %p) %s\n", loc.toChars(), istate, toChars()); + printf("\n********\n%s FuncDeclaration::interpret(istate = %p) %s\n", fd->loc.toChars(), istate, fd->toChars()); #endif - if (semanticRun == PASSsemantic3) + if (fd->semanticRun == PASSsemantic3) { - error("circular dependency. Functions cannot be interpreted while being compiled"); + fd->error("circular dependency. Functions cannot be interpreted while being compiled"); return EXP_CANT_INTERPRET; } - if (!functionSemantic3()) + if (!fd->functionSemantic3()) return EXP_CANT_INTERPRET; - if (semanticRun < PASSsemantic3done) + if (fd->semanticRun < PASSsemantic3done) return EXP_CANT_INTERPRET; // CTFE-compile the function - if (!ctfeCode) - ctfeCompile(); + if (!fd->ctfeCode) + ctfeCompile(fd); - Type *tb = type->toBasetype(); + Type *tb = fd->type->toBasetype(); assert(tb->ty == Tfunction); TypeFunction *tf = (TypeFunction *)tb; if (tf->varargs && arguments && - ((parameters && arguments->dim != parameters->dim) || (!parameters && arguments->dim))) + ((fd->parameters && arguments->dim != fd->parameters->dim) || (!fd->parameters && arguments->dim))) { - error("C-style variadic functions are not yet implemented in CTFE"); + fd->error("C-style variadic functions are not yet implemented in CTFE"); return EXP_CANT_INTERPRET; } @@ -809,17 +844,19 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument // except for delegates. (Note that the 'this' pointer may be null). // Func literals report isNested() even if they are in global scope, // so we need to check that the parent is a function. - if (isNested() && toParent2()->isFuncDeclaration() && !thisarg && istate) + if (fd->isNested() && fd->toParent2()->isFuncDeclaration() && !thisarg && istate) thisarg = ctfeStack.getThis(); size_t dim = 0; - if (needThis() && !thisarg) - { // error, no this. Prevent segfault. - error("need 'this' to access member %s", toChars()); + if (fd->needThis() && !thisarg) + { + // error, no this. Prevent segfault. + fd->error("need 'this' to access member %s", fd->toChars()); return EXP_CANT_INTERPRET; } if (thisarg && !istate) - { // Check that 'this' aleady has a value + { + // Check that 'this' aleady has a value if (thisarg->interpret(istate) == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; } @@ -832,20 +869,22 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument if (arguments) { dim = arguments->dim; - assert(!dim || (parameters && (parameters->dim == dim))); + assert(!dim || (fd->parameters && (fd->parameters->dim == dim))); /* Evaluate all the arguments to the function, * store the results in eargs[] */ eargs.setDim(dim); for (size_t i = 0; i < dim; i++) - { Expression *earg = (*arguments)[i]; + { + Expression *earg = (*arguments)[i]; Parameter *arg = Parameter::getNth(tf->parameters, i); if (arg->storageClass & (STCout | STCref)) { if (!istate && (arg->storageClass & STCout)) - { // initializing an out parameter involves writing to it. + { + // initializing an out parameter involves writing to it. earg->error("global %s cannot be passed as an 'out' parameter at compile time", earg->toChars()); return EXP_CANT_INTERPRET; } @@ -860,7 +899,8 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument { } else - { /* Value parameters + { + /* Value parameters */ Type *ta = arg->type->toBasetype(); if (ta->ty == Tsarray && earg->op == TOKaddress) @@ -875,15 +915,10 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument --evaluatingArgs; if (earg == EXP_CANT_INTERPRET) return earg; -#if DMDV2 /* Struct literals are passed by value, but we don't need to * copy them if they are passed as const */ - bool needcopy = !(arg->storageClass & (STCconst | STCimmutable)); -#else - bool needcopy = true; -#endif - if (earg->op == TOKstructliteral && needcopy) + if (earg->op == TOKstructliteral && !(arg->storageClass & (STCconst | STCimmutable))) earg = copyLiteral(earg); } if (earg->op == TOKthrownexception) @@ -902,16 +937,17 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument InterState istatex; istatex.caller = istate; - istatex.fd = this; + istatex.fd = fd; ctfeStack.startFrame(thisarg); if (arguments) { for (size_t i = 0; i < dim; i++) - { Expression *earg = eargs[i]; + { + Expression *earg = eargs[i]; Parameter *arg = Parameter::getNth(tf->parameters, i); - VarDeclaration *v = (*parameters)[i]; + VarDeclaration *v = (*fd->parameters)[i]; #if LOG printf("arg[%d] = %s\n", i, earg->toChars()); #endif @@ -921,7 +957,7 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument VarDeclaration *v2 = ve->var->isVarDeclaration(); if (!v2) { - error("cannot interpret %s as a ref parameter", ve->toChars()); + fd->error("cannot interpret %s as a ref parameter", ve->toChars()); return EXP_CANT_INTERPRET; } /* The push() isn't a variable we'll use, it's just a place @@ -932,12 +968,13 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument int oldadr = v2->ctfeAdrOnStack; ctfeStack.push(v); v->ctfeAdrOnStack = oldadr; - assert(v2->hasValue()); + assert(hasValue(v2)); } else - { // Value parameters and non-trivial references + { + // Value parameters and non-trivial references ctfeStack.push(v); - v->setValueWithoutChecking(earg); + setValueWithoutChecking(v, earg); } #if LOG || LOGASSIGN printf("interpreted arg[%d] = %s\n", i, earg->toChars()); @@ -946,8 +983,8 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument } } - if (vresult) - ctfeStack.push(vresult); + if (fd->vresult) + ctfeStack.push(fd->vresult); // Enter the function ++CtfeStatus::callDepth; @@ -958,13 +995,14 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument while (1) { if (CtfeStatus::callDepth > CTFE_RECURSION_LIMIT) - { // This is a compiler error. It must not be suppressed. + { + // This is a compiler error. It must not be suppressed. global.gag = 0; - error("CTFE recursion limit exceeded"); + fd->error("CTFE recursion limit exceeded"); e = EXP_CANT_INTERPRET; break; } - e = fbody->interpret(&istatex); + e = fd->fbody->interpret(&istatex); if (e == EXP_CANT_INTERPRET) { #if LOG @@ -974,7 +1012,7 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument if (istatex.start) { - error("CTFE internal error: failed to resume at statement %s", istatex.start->toChars()); + fd->error("CTFE internal error: failed to resume at statement %s", istatex.start->toChars()); return EXP_CANT_INTERPRET; } @@ -994,13 +1032,34 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument } assert(e != EXP_CONTINUE_INTERPRET && e != EXP_BREAK_INTERPRET); + /* Bugzilla 7887: If the returned reference is a ref parameter of fd, + * peel off the local indirection. + */ + if (tf->isref && e->op == TOKvar) + { + VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); + assert(v); + if ((v->storage_class & STCref) && (v->storage_class & STCparameter) && + fd == v->parent) + { + for (size_t i = 0; i < dim; i++) + { + if ((*fd->parameters)[i] == v) + { + e = eargs[i]; + break; + } + } + } + } + // Leave the function --CtfeStatus::callDepth; ctfeStack.endFrame(); // If fell off the end of a void function, return void - if (!e && type->toBasetype()->nextOf()->ty == Tvoid) + if (!e && tf->next->ty == Tvoid) return EXP_VOID_INTERPRET; // If result is void, return void @@ -1019,5073 +1078,5609 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument return e; } -/******************************** Statement ***************************/ +class Interpreter : public Visitor +{ +public: + InterState *istate; + CtfeGoal goal; -/*********************************** - * Interpret the statement. - * Returns: - * NULL continue to next statement - * EXP_CANT_INTERPRET cannot interpret statement at compile time - * !NULL expression from return statement, or thrown exception - */ + Expression *result; -Expression *Statement::interpret(InterState *istate) -{ -#if LOG - printf("%s Statement::interpret()\n", loc.toChars()); -#endif - if (istate->start) - { if (istate->start != this) - return NULL; - istate->start = NULL; + Interpreter(InterState *istate, CtfeGoal goal) + : istate(istate), goal(goal) + { + result = NULL; } - error("Statement %s cannot be interpreted at compile time", this->toChars()); - return EXP_CANT_INTERPRET; -} + /******************************** Statement ***************************/ -Expression *ExpStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s ExpStatement::interpret(%s)\n", loc.toChars(), exp ? exp->toChars() : ""); -#endif - if (istate->start) - { if (istate->start != this) - return NULL; - istate->start = NULL; + void visit(Statement *s) + { + #if LOG + printf("%s Statement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start) + { + if (istate->start != s) + return; + istate->start = NULL; + } + + s->error("Statement %s cannot be interpreted at compile time", s->toChars()); + result = EXP_CANT_INTERPRET; } - if (exp) + void visit(ExpStatement *s) { - Expression *e = exp->interpret(istate, ctfeNeedNothing); - if (e == EXP_CANT_INTERPRET) + #if LOG + printf("%s ExpStatement::interpret(%s)\n", s->loc.toChars(), s->exp ? s->exp->toChars() : ""); + #endif + if (istate->start) { - //printf("-ExpStatement::interpret(): %p\n", e); - return EXP_CANT_INTERPRET; + if (istate->start != s) + return; + istate->start = NULL; } - if (e && e!= EXP_VOID_INTERPRET && e->op == TOKthrownexception) - return e; - } - return NULL; -} -Expression *CompoundStatement::interpret(InterState *istate) -{ Expression *e = NULL; + if (s->exp) + { + Expression *e = s->exp->interpret(istate, ctfeNeedNothing); + if (e == EXP_CANT_INTERPRET) + { + //printf("-ExpStatement::interpret(): %p\n", e); + result = EXP_CANT_INTERPRET; + return; + } + if (e && e != EXP_VOID_INTERPRET && e->op == TOKthrownexception) + { + result = e; + return; + } + } + } -#if LOG - printf("%s CompoundStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start == this) - istate->start = NULL; - if (statements) + void visit(CompoundStatement *s) { - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; + #if LOG + printf("%s CompoundStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start == s) + istate->start = NULL; + Expression *e = NULL; + if (s->statements) + { + for (size_t i = 0; i < s->statements->dim; i++) + { + Statement *sx = (*s->statements)[i]; + if (sx) + { + e = sx->interpret(istate); + if (e) + break; + } + } + } + #if LOG + printf("%s -CompoundStatement::interpret() %p\n", s->loc.toChars(), e); + #endif + result = e; + } - if (s) + void visit(UnrolledLoopStatement *s) + { + #if LOG + printf("%s UnrolledLoopStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start == s) + istate->start = NULL; + Expression *e = NULL; + if (s->statements) + { + for (size_t i = 0; i < s->statements->dim; i++) { - e = s->interpret(istate); + Statement *sx = (*s->statements)[i]; + + e = sx->interpret(istate); + if (e == EXP_CANT_INTERPRET) + break; + if (e == EXP_CONTINUE_INTERPRET) + { + if (istate->gotoTarget && istate->gotoTarget != s) + break; // continue at higher level + istate->gotoTarget = NULL; + e = NULL; + continue; + } + if (e == EXP_BREAK_INTERPRET) + { + if (!istate->gotoTarget || istate->gotoTarget == s) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level + break; + } if (e) break; } } + result = e; } -#if LOG - printf("%s -CompoundStatement::interpret() %p\n", loc.toChars(), e); -#endif - return e; -} -Expression *UnrolledLoopStatement::interpret(InterState *istate) -{ Expression *e = NULL; - -#if LOG - printf("%s UnrolledLoopStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start == this) - istate->start = NULL; - if (statements) + void visit(IfStatement *s) { - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; + #if LOG + printf("%s IfStatement::interpret(%s)\n", s->loc.toChars(), s->condition->toChars()); + #endif - e = s->interpret(istate); - if (e == EXP_CANT_INTERPRET) - break; - if (e == EXP_CONTINUE_INTERPRET) + if (istate->start == s) + istate->start = NULL; + if (istate->start) + { + Expression *e = NULL; + if (s->ifbody) + e = s->ifbody->interpret(istate); + if (exceptionOrCantInterpret(e)) { - if (istate->gotoTarget && istate->gotoTarget != this) - break; // continue at higher level - istate->gotoTarget = NULL; - e = NULL; - continue; + result = e; + return; } - if (e == EXP_BREAK_INTERPRET) + if (istate->start && s->elsebody) + e = s->elsebody->interpret(istate); + result = e; + return; + } + + Expression *e = s->condition->interpret(istate); + assert(e); + //if (e == EXP_CANT_INTERPRET) printf("cannot interpret\n"); + if (e != EXP_CANT_INTERPRET && (e && e->op != TOKthrownexception)) + { + if (isTrueBool(e)) + e = s->ifbody ? s->ifbody->interpret(istate) : NULL; + else if (e->isBool(false)) + e = s->elsebody ? s->elsebody->interpret(istate) : NULL; + else { - if (!istate->gotoTarget || istate->gotoTarget == this) - { - istate->gotoTarget = NULL; - e = NULL; - } // else break at a higher level - break; + e = EXP_CANT_INTERPRET; } - if (e) - break; } + result = e; } - return e; -} - -Expression *IfStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s IfStatement::interpret(%s)\n", loc.toChars(), condition->toChars()); -#endif - if (istate->start == this) - istate->start = NULL; - if (istate->start) + void visit(ScopeStatement *s) { - Expression *e = NULL; - if (ifbody) - e = ifbody->interpret(istate); - if (exceptionOrCantInterpret(e)) - return e; - if (istate->start && elsebody) - e = elsebody->interpret(istate); - return e; + #if LOG + printf("%s ScopeStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start == s) + istate->start = NULL; + result = s->statement ? s->statement->interpret(istate) : NULL; } - Expression *e = condition->interpret(istate); - assert(e); - //if (e == EXP_CANT_INTERPRET) printf("cannot interpret\n"); - if (e != EXP_CANT_INTERPRET && (e && e->op != TOKthrownexception)) + /** + Given an expression e which is about to be returned from the current + function, generate an error if it contains pointers to local variables. + Return true if it is safe to return, false if an error was generated. + + Only checks expressions passed by value (pointers to local variables + may already be stored in members of classes, arrays, or AAs which + were passed as mutable function parameters). + */ + + static bool stopPointersEscaping(Loc loc, Expression *e) { - if (isTrueBool(e)) - e = ifbody ? ifbody->interpret(istate) : NULL; - else if (e->isBool(false)) - e = elsebody ? elsebody->interpret(istate) : NULL; - else + if (!e->type->hasPointers()) + return true; + if (isPointer(e->type)) { - e = EXP_CANT_INTERPRET; + Expression *x = e; + if (e->op == TOKaddress) + x = ((AddrExp *)e)->e1; + VarDeclaration *v; + while (x->op == TOKvar && + (v = ((VarExp *)x)->var->isVarDeclaration()) != NULL) + { + if (v->storage_class & STCref) + { + x = getValue(v); + if (e->op == TOKaddress) + ((AddrExp *)e)->e1 = x; + continue; + } + if (ctfeStack.isInCurrentFrame(v)) + { + error(loc, "returning a pointer to a local stack variable"); + return false; + } + else + break; + } + // TODO: If it is a TOKdotvar or TOKindex, we should check that it is not + // pointing to a local struct or static array. + } + if (e->op == TOKstructliteral) + { + StructLiteralExp *se = (StructLiteralExp *)e; + return stopPointersEscapingFromArray(loc, se->elements); + } + if (e->op == TOKarrayliteral) + { + return stopPointersEscapingFromArray(loc, ((ArrayLiteralExp *)e)->elements); } + if (e->op == TOKassocarrayliteral) + { + AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e; + if (!stopPointersEscapingFromArray(loc, aae->keys)) + return false; + return stopPointersEscapingFromArray(loc, aae->values); + } + return true; } - return e; -} -Expression *ScopeStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s ScopeStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start == this) - istate->start = NULL; - return statement ? statement->interpret(istate) : NULL; -} + // Check all members of an array for escaping local variables. Return false if error + static bool stopPointersEscapingFromArray(Loc loc, Expressions *elems) + { + for (size_t i = 0; i < elems->dim; i++) + { + Expression *m = (*elems)[i]; + if (!m) + continue; + if (m) + if ( !stopPointersEscaping(loc, m) ) + return false; + } + return true; + } + void visit(ReturnStatement *s) + { + #if LOG + printf("%s ReturnStatement::interpret(%s)\n", s->loc.toChars(), s->exp ? s->exp->toChars() : ""); + #endif + if (istate->start) + { + if (istate->start != s) + return; + istate->start = NULL; + } -/** - Given an expression e which is about to be returned from the current - function, generate an error if it contains pointers to local variables. - Return true if it is safe to return, false if an error was generated. + if (!s->exp) + { + result = EXP_VOID_INTERPRET; + return; + } + assert(istate && istate->fd && istate->fd->type); - Only checks expressions passed by value (pointers to local variables - may already be stored in members of classes, arrays, or AAs which - were passed as mutable function parameters). -*/ -bool stopPointersEscapingFromArray(Loc loc, Expressions *elems); + /* If the function returns a ref AND it's been called from an assignment, + * we need to return an lvalue. Otherwise, just do an (rvalue) interpret. + */ + if (istate->fd->type && istate->fd->type->ty == Tfunction) + { + TypeFunction *tf = (TypeFunction *)istate->fd->type; + if (tf->isref && istate->caller && istate->caller->awaitingLvalueReturn) + { + // We need to return an lvalue + Expression *e = s->exp->interpret(istate, ctfeNeedLvalueRef); + if (e == EXP_CANT_INTERPRET) + s->error("ref return %s is not yet supported in CTFE", s->exp->toChars()); + result = e; + return; + } + if (tf->next && (tf->next->ty == Tdelegate) && istate->fd->closureVars.dim > 0) + { + // To support this, we need to copy all the closure vars + // into the delegate literal. + s->error("closures are not yet supported in CTFE"); + result = EXP_CANT_INTERPRET; + return; + } + } -bool stopPointersEscaping(Loc loc, Expression *e) -{ - if (!e->type->hasPointers()) - return true; - if (isPointer(e->type)) - { - Expression *x = e; - if (e->op == TOKaddress) - x = ((AddrExp *)e)->e1; - VarDeclaration *v; - while (x->op == TOKvar && - (v = ((VarExp *)x)->var->isVarDeclaration()) != NULL) + // We need to treat pointers specially, because TOKsymoff can be used to + // return a value OR a pointer + Expression *e; + if (isPointer(s->exp->type)) { - if (v->storage_class & STCref) + e = s->exp->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e)) { - x = v->getValue(); - if (e->op == TOKaddress) - ((AddrExp *)e)->e1 = x; - continue; + result = e; + return; } - if (ctfeStack.isInCurrentFrame(v)) + } + else + { + e = s->exp->interpret(istate); + if (exceptionOrCantInterpret(e)) { - error(loc, "returning a pointer to a local stack variable"); - return false; + result = e; + return; } - else - break; } - // TODO: If it is a TOKdotvar or TOKindex, we should check that it is not - // pointing to a local struct or static array. - } - if (e->op == TOKstructliteral) - { - StructLiteralExp *se = (StructLiteralExp *)e; - return stopPointersEscapingFromArray(loc, se->elements); - } - if (e->op == TOKarrayliteral) - { - return stopPointersEscapingFromArray(loc, ((ArrayLiteralExp *)e)->elements); - } - if (e->op == TOKassocarrayliteral) - { - AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e; - if (!stopPointersEscapingFromArray(loc, aae->keys)) - return false; - return stopPointersEscapingFromArray(loc, aae->values); - } - return true; -} -// Check all members of an array for escaping local variables. Return false if error -bool stopPointersEscapingFromArray(Loc loc, Expressions *elems) -{ - for (size_t i = 0; i < elems->dim; i++) - { - Expression *m = (*elems)[i]; - if (!m) - continue; - if (m) - if ( !stopPointersEscaping(loc, m) ) - return false; - } - return true; -} + // Disallow returning pointers to stack-allocated variables (bug 7876) + if (!stopPointersEscaping(s->loc, e)) + { + result = EXP_CANT_INTERPRET; + return; + } -bool scrubArray(Loc loc, Expressions *elems, bool structlit = false); + if (needToCopyLiteral(e)) + e = copyLiteral(e); + #if LOGASSIGN + printf("RETURN %s\n", s->loc.toChars()); + showCtfeExpr(e); + #endif + result = e; + } -/* All results destined for use outside of CTFE need to have their CTFE-specific - * features removed. - * In particular, all slices must be resolved. - */ -Expression *scrubReturnValue(Loc loc, Expression *e) -{ - if (e->op == TOKclassreference) + void visit(BreakStatement *s) { - StructLiteralExp *se = ((ClassReferenceExp*)e)->value; - se->ownedByCtfe = false; - if (!(se->stageflags & stageScrub)) + #if LOG + printf("%s BreakStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start) { - int old = se->stageflags; - se->stageflags |= stageScrub; - if (!scrubArray(loc, se->elements, true)) - return EXP_CANT_INTERPRET; - se->stageflags = old; + if (istate->start != s) + return; + istate->start = NULL; + } + + if (s->ident) + { + LabelDsymbol *label = istate->fd->searchLabel(s->ident); + assert(label && label->statement); + LabelStatement *ls = label->statement; + Statement *target; + if (ls->gotoTarget) + target = ls->gotoTarget; + else + { + target = ls->statement; + if (target->isScopeStatement()) + target = target->isScopeStatement()->statement; + } + istate->gotoTarget = target; + result = EXP_BREAK_INTERPRET; + return; + } + else + { + istate->gotoTarget = NULL; + result = EXP_BREAK_INTERPRET; + return; } } - if (e->op == TOKvoid) + + void visit(ContinueStatement *s) { - error(loc, "uninitialized variable '%s' cannot be returned from CTFE", ((VoidInitExp *)e)->var->toChars()); - e = new ErrorExp(); + #if LOG + printf("%s ContinueStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start) + { + if (istate->start != s) + return; + istate->start = NULL; + } + + if (s->ident) + { + LabelDsymbol *label = istate->fd->searchLabel(s->ident); + assert(label && label->statement); + LabelStatement *ls = label->statement; + Statement *target; + if (ls->gotoTarget) + target = ls->gotoTarget; + else + { + target = ls->statement; + if (target->isScopeStatement()) + target = target->isScopeStatement()->statement; + } + istate->gotoTarget = target; + result = EXP_CONTINUE_INTERPRET; + return; + } + else + { + result = EXP_CONTINUE_INTERPRET; + return; + } } - if (e->op == TOKslice) + + void visit(WhileStatement *s) { - e = resolveSlice(e); + #if LOG + printf("WhileStatement::interpret()\n"); + #endif + assert(0); // rewritten to ForStatement } - if (e->op == TOKstructliteral) + + void visit(DoStatement *s) { - StructLiteralExp *se = (StructLiteralExp *)e; - se->ownedByCtfe = false; - if (!(se->stageflags & stageScrub)) + #if LOG + printf("%s DoStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start == s) + istate->start = NULL; + Expression *e; + + while (1) { - int old = se->stageflags; - se->stageflags |= stageScrub; - if (!scrubArray(loc, se->elements, true)) - return EXP_CANT_INTERPRET; - se->stageflags = old; + bool wasGoto = !!istate->start; + e = s->body ? s->body->interpret(istate) : NULL; + if (e == EXP_CANT_INTERPRET) + break; + if (wasGoto && istate->start) + return; + if (e == EXP_BREAK_INTERPRET) + { + if (!istate->gotoTarget || istate->gotoTarget == s) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level + break; + } + if (e && e != EXP_CONTINUE_INTERPRET) + break; + if (istate->gotoTarget && istate->gotoTarget != s) + break; // continue at a higher level + + istate->gotoTarget = NULL; + e = s->condition->interpret(istate); + if (exceptionOrCantInterpret(e)) + break; + if (!e->isConst()) + { + e = EXP_CANT_INTERPRET; + break; + } + if (isTrueBool(e)) + { + } + else if (e->isBool(false)) + { + e = NULL; + break; + } + else + assert(0); } + result = e; } - if (e->op == TOKstring) + + void visit(ForStatement *s) { - ((StringExp *)e)->ownedByCtfe = false; + #if LOG + printf("%s ForStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start == s) + istate->start = NULL; + Expression *e; + + if (s->init) + { + e = s->init->interpret(istate); + if (exceptionOrCantInterpret(e)) + { + result = e; + return; + } + assert(!e); + } + while (1) + { + if (s->condition && !istate->start) + { + e = s->condition->interpret(istate); + if (exceptionOrCantInterpret(e)) + break; + if (e->isBool(false)) + { + e = NULL; + break; + } + assert(isTrueBool(e)); + } + + bool wasGoto = !!istate->start; + e = s->body ? s->body->interpret(istate) : NULL; + if (e == EXP_CANT_INTERPRET) + break; + if (wasGoto && istate->start) + return; + + if (e == EXP_BREAK_INTERPRET) + { + if (!istate->gotoTarget || istate->gotoTarget == s) + { + istate->gotoTarget = NULL; + e = NULL; + } // else break at a higher level + break; + } + if (e && e != EXP_CONTINUE_INTERPRET) + break; + + if (istate->gotoTarget && istate->gotoTarget != s) + break; // continue at a higher level + istate->gotoTarget = NULL; + + if (s->increment) + { + e = s->increment->interpret(istate); + if (e == EXP_CANT_INTERPRET) + break; + } + } + result = e; } - if (e->op == TOKarrayliteral) + + void visit(ForeachStatement *s) { - ((ArrayLiteralExp *)e)->ownedByCtfe = false; - if (!scrubArray(loc, ((ArrayLiteralExp *)e)->elements)) - return EXP_CANT_INTERPRET; + assert(0); // rewritten to ForStatement } - if (e->op == TOKassocarrayliteral) + + void visit(ForeachRangeStatement *s) { - AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e; - aae->ownedByCtfe = false; - if (!scrubArray(loc, aae->keys)) - return EXP_CANT_INTERPRET; - if (!scrubArray(loc, aae->values)) - return EXP_CANT_INTERPRET; - aae->type = toBuiltinAAType(aae->type); + assert(0); // rewritten to ForStatement } - return e; -} -// Return true if every element is either void, -// or is an array literal or struct literal of void elements. -bool isEntirelyVoid(Expressions *elems) -{ - for (size_t i = 0; i < elems->dim; i++) + void visit(SwitchStatement *s) { - Expression *m = (*elems)[i]; - // It can be NULL for performance reasons, - // see StructLiteralExp::interpret(). - if (!m) - continue; + #if LOG + printf("%s SwitchStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start == s) + istate->start = NULL; + Expression *e = NULL; - if (!(m->op == TOKvoid) && - !(m->op == TOKarrayliteral && isEntirelyVoid(((ArrayLiteralExp *)m)->elements)) && - !(m->op == TOKstructliteral && isEntirelyVoid(((StructLiteralExp *)m)->elements))) + if (istate->start) { - return false; + e = s->body ? s->body->interpret(istate) : NULL; + if (istate->start) + return; + if (e == EXP_CANT_INTERPRET) + { + result = e; + return; + } + if (e == EXP_BREAK_INTERPRET) + { + if (!istate->gotoTarget || istate->gotoTarget == s) + { + istate->gotoTarget = NULL; + return; + } + // else break at a higher level + } + result = e; + return; } - } - return true; -} -// Scrub all members of an array. Return false if error -bool scrubArray(Loc loc, Expressions *elems, bool structlit) -{ - for (size_t i = 0; i < elems->dim; i++) - { - Expression *m = (*elems)[i]; - // It can be NULL for performance reasons, - // see StructLiteralExp::interpret(). - if (!m) - continue; - // A struct .init may contain void members. - // Static array members are a weird special case (bug 10994). - if (structlit && - ((m->op == TOKvoid) || - (m->op == TOKarrayliteral && m->type->ty == Tsarray && isEntirelyVoid(((ArrayLiteralExp *)m)->elements)) || - (m->op == TOKstructliteral && isEntirelyVoid(((StructLiteralExp *)m)->elements))) - ) + Expression *econdition = s->condition->interpret(istate); + if (exceptionOrCantInterpret(econdition)) { - m = NULL; + result = econdition; + return; } - else + + Statement *scase = NULL; + if (s->cases) { - m = scrubReturnValue(loc, m); - if (m == EXP_CANT_INTERPRET) - return false; + for (size_t i = 0; i < s->cases->dim; i++) + { + CaseStatement *cs = (*s->cases)[i]; + Expression * caseExp = cs->exp->interpret(istate); + if (exceptionOrCantInterpret(caseExp)) + { + result = caseExp; + return; + } + int eq = ctfeEqual(caseExp->loc, TOKequal, econdition, caseExp); + if (eq) + { + scase = cs; + break; + } + } } - (*elems)[i] = m; - } - return true; -} - - -Expression *ReturnStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s ReturnStatement::interpret(%s)\n", loc.toChars(), exp ? exp->toChars() : ""); -#endif - if (istate->start) - { if (istate->start != this) - return NULL; - istate->start = NULL; - } - - if (!exp) - return EXP_VOID_INTERPRET; - assert(istate && istate->fd && istate->fd->type); -#if DMDV2 - /* If the function returns a ref AND it's been called from an assignment, - * we need to return an lvalue. Otherwise, just do an (rvalue) interpret. - */ - if (istate->fd->type && istate->fd->type->ty==Tfunction) - { - TypeFunction *tf = (TypeFunction *)istate->fd->type; - if (tf->isref && istate->caller && istate->caller->awaitingLvalueReturn) - { // We need to return an lvalue - Expression *e = exp->interpret(istate, ctfeNeedLvalue); - if (e == EXP_CANT_INTERPRET) - error("ref return %s is not yet supported in CTFE", exp->toChars()); - return e; + if (!scase) + { + if (s->hasNoDefault) + s->error("no default or case for %s in switch statement", econdition->toChars()); + scase = s->sdefault; } - if (tf->next && (tf->next->ty == Tdelegate) && istate->fd->closureVars.dim > 0) + + assert(scase); + istate->start = scase; + e = s->body ? s->body->interpret(istate) : NULL; + assert(!istate->start); + if (e == EXP_BREAK_INTERPRET) { - // To support this, we need to copy all the closure vars - // into the delegate literal. - error("closures are not yet supported in CTFE"); - return EXP_CANT_INTERPRET; + if (!istate->gotoTarget || istate->gotoTarget == s) + { + istate->gotoTarget = NULL; + e = NULL; + } + // else break at a higher level } + result = e; } -#endif - // We need to treat pointers specially, because TOKsymoff can be used to - // return a value OR a pointer - Expression *e; - if ( isPointer(exp->type) ) - { e = exp->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(e)) - return e; - } - else + + void visit(CaseStatement *s) { - e = exp->interpret(istate); - if (exceptionOrCantInterpret(e)) - return e; + #if LOG + printf("%s CaseStatement::interpret(%s) this = %p\n", s->loc.toChars(), s->exp->toChars(), s); + #endif + if (istate->start == s) + istate->start = NULL; + if (s->statement) + result = s->statement->interpret(istate); } - // Disallow returning pointers to stack-allocated variables (bug 7876) - - if (!stopPointersEscaping(loc, e)) - return EXP_CANT_INTERPRET; - - if (needToCopyLiteral(e)) - e = copyLiteral(e); -#if LOGASSIGN - printf("RETURN %s\n", loc.toChars()); - showCtfeExpr(e); -#endif - return e; -} - -Expression *BreakStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s BreakStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start) - { if (istate->start != this) - return NULL; - istate->start = NULL; + void visit(DefaultStatement *s) + { + #if LOG + printf("%s DefaultStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start == s) + istate->start = NULL; + if (s->statement) + result = s->statement->interpret(istate); } - if (ident) + void visit(GotoStatement *s) { - LabelDsymbol *label = istate->fd->searchLabel(ident); - assert(label && label->statement); - LabelStatement *ls = label->statement; - Statement *s; - if (ls->gotoTarget) - s = ls->gotoTarget; - else + #if LOG + printf("%s GotoStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start) { - s = ls->statement; - if (s->isScopeStatement()) - s = s->isScopeStatement()->statement; + if (istate->start != s) + return; + istate->start = NULL; } - istate->gotoTarget = s; - return EXP_BREAK_INTERPRET; + + assert(s->label && s->label->statement); + istate->gotoTarget = s->label->statement; + result = EXP_GOTO_INTERPRET; } - else + + void visit(GotoCaseStatement *s) { - istate->gotoTarget = NULL; - return EXP_BREAK_INTERPRET; - } -} + #if LOG + printf("%s GotoCaseStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start) + { + if (istate->start != s) + return; + istate->start = NULL; + } -Expression *ContinueStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s ContinueStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start) - { if (istate->start != this) - return NULL; - istate->start = NULL; + assert(s->cs); + istate->gotoTarget = s->cs; + result = EXP_GOTO_INTERPRET; } - if (ident) + void visit(GotoDefaultStatement *s) { - LabelDsymbol *label = istate->fd->searchLabel(ident); - assert(label && label->statement); - LabelStatement *ls = label->statement; - Statement *s; - if (ls->gotoTarget) - s = ls->gotoTarget; - else + #if LOG + printf("%s GotoDefaultStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start) { - s = ls->statement; - if (s->isScopeStatement()) - s = s->isScopeStatement()->statement; + if (istate->start != s) + return; + istate->start = NULL; } - istate->gotoTarget = s; - return EXP_CONTINUE_INTERPRET; - } - else - return EXP_CONTINUE_INTERPRET; -} -Expression *WhileStatement::interpret(InterState *istate) -{ -#if LOG - printf("WhileStatement::interpret()\n"); -#endif - assert(0); // rewritten to ForStatement - return NULL; -} + assert(s->sw && s->sw->sdefault); + istate->gotoTarget = s->sw->sdefault; + result = EXP_GOTO_INTERPRET; + } -Expression *DoStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s DoStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start == this) - istate->start = NULL; - Expression *e; + void visit(LabelStatement *s) + { + #if LOG + printf("%s LabelStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start == s) + istate->start = NULL; + result = s->statement ? s->statement->interpret(istate) : NULL; + } - while (1) + void visit(TryCatchStatement *s) { - bool wasGoto = !!istate->start; - e = body ? body->interpret(istate) : NULL; - if (e == EXP_CANT_INTERPRET) - break; - if (wasGoto && istate->start) - return NULL; - if (e == EXP_BREAK_INTERPRET) + #if LOG + printf("%s TryCatchStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start == s) + istate->start = NULL; + if (istate->start) { - if (!istate->gotoTarget || istate->gotoTarget == this) + Expression *e = NULL; + if (s->body) + e = s->body->interpret(istate); + for (size_t i = 0; !e && istate->start && i < s->catches->dim; i++) { - istate->gotoTarget = NULL; - e = NULL; - } // else break at a higher level - break; + Catch *ca = (*s->catches)[i]; + if (ca->handler) + e = ca->handler->interpret(istate); + } + result = e; + return; } - if (e && e != EXP_CONTINUE_INTERPRET) - break; - if (istate->gotoTarget && istate->gotoTarget != this) - break; // continue at a higher level - istate->gotoTarget = NULL; - e = condition->interpret(istate); - if (exceptionOrCantInterpret(e)) - break; - if (!e->isConst()) - { e = EXP_CANT_INTERPRET; - break; + Expression *e = s->body ? s->body->interpret(istate) : NULL; + if (e == EXP_CANT_INTERPRET) + { + result = e; + return; } - if (isTrueBool(e)) + if (!exceptionOrCantInterpret(e)) { + result = e; + return; } - else if (e->isBool(false)) - { e = NULL; - break; + // An exception was thrown + ThrownExceptionExp *ex = (ThrownExceptionExp *)e; + Type *extype = ex->thrown->originalClass()->type; + // Search for an appropriate catch clause. + for (size_t i = 0; i < s->catches->dim; i++) + { + Catch *ca = (*s->catches)[i]; + Type *catype = ca->type; + + if (catype->equals(extype) || catype->isBaseOf(extype, NULL)) + { + // Execute the handler + if (ca->var) + { + ctfeStack.push(ca->var); + setValue(ca->var, ex->thrown); + } + if (ca->handler) + { + e = ca->handler->interpret(istate); + if (e == EXP_GOTO_INTERPRET) + { + InterState istatex = *istate; + istatex.start = istate->gotoTarget; // set starting statement + istatex.gotoTarget = NULL; + Expression *eh = ca->handler->interpret(&istatex); + if (!istatex.start) + { + istate->gotoTarget = NULL; + e = eh; + } + } + } + else + e = NULL; + result = e; + return; + } } - else - assert(0); + result = e; } - return e; -} -Expression *ForStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s ForStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start == this) - istate->start = NULL; - Expression *e; + static bool isAnErrorException(ClassDeclaration *cd) + { + return cd == ClassDeclaration::errorException || ClassDeclaration::errorException->isBaseOf(cd, NULL); + } - if (init) + static ThrownExceptionExp *chainExceptions(ThrownExceptionExp *oldest, ThrownExceptionExp *newest) { - e = init->interpret(istate); - if (exceptionOrCantInterpret(e)) - return e; - assert(!e); + #if LOG + printf("Collided exceptions %s %s\n", oldest->thrown->toChars(), newest->thrown->toChars()); + #endif + // Little sanity check to make sure it's really a Throwable + ClassReferenceExp *boss = oldest->thrown; + assert((*boss->value->elements)[4]->type->ty == Tclass); + ClassReferenceExp *collateral = newest->thrown; + if (isAnErrorException(collateral->originalClass()) + && !isAnErrorException(boss->originalClass())) + { // The new exception bypass the existing chain + assert((*collateral->value->elements)[5]->type->ty == Tclass); + (*collateral->value->elements)[5] = boss; + return newest; + } + while ((*boss->value->elements)[4]->op == TOKclassreference) + { + boss = (ClassReferenceExp *)(*boss->value->elements)[4]; + } + (*boss->value->elements)[4] = collateral; + return oldest; } - while (1) + + void visit(TryFinallyStatement *s) { - if (condition && !istate->start) + #if LOG + printf("%s TryFinallyStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start == s) + istate->start = NULL; + if (istate->start) { - e = condition->interpret(istate); - if (exceptionOrCantInterpret(e)) - break; - if (!e->isConst()) - { e = EXP_CANT_INTERPRET; - break; - } - if (e->isBool(false)) - { e = NULL; - break; - } - assert( isTrueBool(e) ); + Expression *e = NULL; + if (s->body) + e = s->body->interpret(istate); + // Jump into/out from finalbody is disabled in semantic analysis. + // and jump inside will be handled by the ScopeStatement == finalbody. + result = e; + return; } - bool wasGoto = !!istate->start; - e = body ? body->interpret(istate) : NULL; + Expression *e = s->body ? s->body->interpret(istate) : NULL; if (e == EXP_CANT_INTERPRET) - break; - if (wasGoto && istate->start) - return NULL; - - if (e == EXP_BREAK_INTERPRET) { - if (!istate->gotoTarget || istate->gotoTarget == this) - { - istate->gotoTarget = NULL; - e = NULL; - } // else break at a higher level - break; + result = e; + return; } - if (e && e != EXP_CONTINUE_INTERPRET) - break; + Expression *second = s->finalbody ? s->finalbody->interpret(istate) : NULL; + if (second == EXP_CANT_INTERPRET) + { + result = second; + return; + } + if (exceptionOrCantInterpret(second)) + { + // Check for collided exceptions + if (exceptionOrCantInterpret(e)) + e = chainExceptions((ThrownExceptionExp *)e, (ThrownExceptionExp *)second); + else + e = second; + } + result = e; + } - if (istate->gotoTarget && istate->gotoTarget != this) - break; // continue at a higher level - istate->gotoTarget = NULL; + void visit(ThrowStatement *s) + { + #if LOG + printf("%s ThrowStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start) + { + if (istate->start != s) + return; + istate->start = NULL; + } - if (increment) + Expression *e = s->exp->interpret(istate); + if (exceptionOrCantInterpret(e)) { - e = increment->interpret(istate); - if (e == EXP_CANT_INTERPRET) - break; + result = e; + return; } + assert(e->op == TOKclassreference); + result = new ThrownExceptionExp(s->loc, (ClassReferenceExp *)e); } - return e; -} -Expression *ForeachStatement::interpret(InterState *istate) -{ - assert(0); // rewritten to ForStatement - return NULL; -} + void visit(OnScopeStatement *s) + { + assert(0); + } -#if DMDV2 -Expression *ForeachRangeStatement::interpret(InterState *istate) -{ - assert(0); // rewritten to ForStatement - return NULL; -} -#endif + void visit(WithStatement *s) + { + #if LOG + printf("%s WithStatement::interpret()\n", s->loc.toChars()); + #endif -Expression *SwitchStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s SwitchStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start == this) - istate->start = NULL; - Expression *e = NULL; + // If it is with(Enum) {...}, just execute the body. + if (s->exp->op == TOKimport || s->exp->op == TOKtype) + { + result = s->body ? s->body->interpret(istate) : EXP_VOID_INTERPRET; + return; + } - if (istate->start) - { - e = body ? body->interpret(istate) : NULL; if (istate->start) - return NULL; - if (e == EXP_CANT_INTERPRET) - return e; - if (e == EXP_BREAK_INTERPRET) { - if (!istate->gotoTarget || istate->gotoTarget == this) - { - istate->gotoTarget = NULL; - return NULL; - } // else break at a higher level + if (istate->start != s) + return; + istate->start = NULL; } - return e; - } - - Expression *econdition = condition->interpret(istate); - if (exceptionOrCantInterpret(econdition)) - return econdition; - - Statement *s = NULL; - if (cases) - { - for (size_t i = 0; i < cases->dim; i++) + Expression *e = s->exp->interpret(istate); + if (exceptionOrCantInterpret(e)) { - CaseStatement *cs = (*cases)[i]; - Expression * caseExp = cs->exp->interpret(istate); - if (exceptionOrCantInterpret(caseExp)) - return caseExp; - int eq = ctfeEqual(caseExp->loc, TOKequal, econdition, caseExp); - if (eq) - { s = cs; - break; + result = e; + return; + } + if (s->wthis->type->ty == Tpointer && s->exp->type->ty != Tpointer) + { + e = new AddrExp(s->loc, e); + e->type = s->wthis->type; + } + ctfeStack.push(s->wthis); + setValue(s->wthis, e); + if (s->body) + { + e = s->body->interpret(istate); + if (e == EXP_GOTO_INTERPRET) + { + InterState istatex = *istate; + istatex.start = istate->gotoTarget; // set starting statement + istatex.gotoTarget = NULL; + Expression *ex = s->body->interpret(&istatex); + if (!istatex.start) + { + istate->gotoTarget = NULL; + e = ex; + } } } - } - if (!s) - { if (hasNoDefault) - error("no default or case for %s in switch statement", econdition->toChars()); - s = sdefault; + else + e = EXP_VOID_INTERPRET; + ctfeStack.pop(s->wthis); + result = e; } - assert(s); - istate->start = s; - e = body ? body->interpret(istate) : NULL; - assert(!istate->start); - if (e == EXP_BREAK_INTERPRET) + void visit(AsmStatement *s) { - if (!istate->gotoTarget || istate->gotoTarget == this) + #if LOG + printf("%s AsmStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start) { - istate->gotoTarget = NULL; - e = NULL; - } // else break at a higher level + if (istate->start != s) + return; + istate->start = NULL; + } + + s->error("asm statements cannot be interpreted at compile time"); + result = EXP_CANT_INTERPRET; } - return e; -} -Expression *CaseStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s CaseStatement::interpret(%s) this = %p\n", loc.toChars(), exp->toChars(), this); -#endif - if (istate->start == this) - istate->start = NULL; - if (statement) - return statement->interpret(istate); - else - return NULL; -} +#ifdef IN_GCC + void visit(ExtAsmStatement *s) + { + #if LOG + printf("%s ExtAsmStatement::interpret()\n", s->loc.toChars()); + #endif + if (istate->start) + { + if (istate->start != s) + return; + istate->start = NULL; + } -Expression *DefaultStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s DefaultStatement::interpret()\n", loc.toChars()); + s->error("extended asm statements cannot be interpreted at compile time"); + result = EXP_CANT_INTERPRET; + } #endif - if (istate->start == this) - istate->start = NULL; - if (statement) - return statement->interpret(istate); - else - return NULL; -} -Expression *GotoStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s GotoStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start) - { if (istate->start != this) - return NULL; - istate->start = NULL; + void visit(ImportStatement *s) + { + #if LOG + printf("ImportStatement::interpret()\n"); + #endif + if (istate->start) + { + if (istate->start != s) + return; + istate->start = NULL; + } } - assert(label && label->statement); - istate->gotoTarget = label->statement; - return EXP_GOTO_INTERPRET; -} + /******************************** Expression ***************************/ -Expression *GotoCaseStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s GotoCaseStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start) - { if (istate->start != this) - return NULL; - istate->start = NULL; + void visit(Expression *e) + { + #if LOG + printf("%s Expression::interpret() %s\n", e->loc.toChars(), e->toChars()); + printf("type = %s\n", e->type->toChars()); + e->print(); + #endif + e->error("Cannot interpret %s at compile time", e->toChars()); + result = EXP_CANT_INTERPRET; } - assert(cs); - istate->gotoTarget = cs; - return EXP_GOTO_INTERPRET; -} + void visit(ThisExp *e) + { + Expression *localThis = ctfeStack.getThis(); + if (localThis && localThis->op == TOKstructliteral) + { + result = localThis; + return; + } + if (localThis) + { + result = localThis->interpret(istate, goal); + return; + } + e->error("value of 'this' is not known at compile time"); + result = EXP_CANT_INTERPRET; + } -Expression *GotoDefaultStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s GotoDefaultStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start) - { if (istate->start != this) - return NULL; - istate->start = NULL; + void visit(NullExp *e) + { + result = e; } - assert(sw && sw->sdefault); - istate->gotoTarget = sw->sdefault; - return EXP_GOTO_INTERPRET; -} + void visit(IntegerExp *e) + { + #if LOG + printf("%s IntegerExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + result = e; + } -Expression *LabelStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s LabelStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start == this) - istate->start = NULL; - return statement ? statement->interpret(istate) : NULL; -} + void visit(RealExp *e) + { + #if LOG + printf("%s RealExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + result = e; + } + void visit(ComplexExp *e) + { + result = e; + } -Expression *TryCatchStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s TryCatchStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start == this) - istate->start = NULL; - if (istate->start) + void visit(StringExp *e) { - Expression *e = NULL; - if (body) - e = body->interpret(istate); - for (size_t i = 0; !e && istate->start && i < catches->dim; i++) - { - Catch *ca = (*catches)[i]; - if (ca->handler) - e = ca->handler->interpret(istate); - } - return e; + #if LOG + printf("%s StringExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + /* Attempts to modify string literals are prevented + * in BinExp::interpretAssignCommon. + */ + result = e; } - Expression *e = body ? body->interpret(istate) : NULL; - if (e == EXP_CANT_INTERPRET) - return e; - if (!exceptionOrCantInterpret(e)) - return e; - // An exception was thrown - ThrownExceptionExp *ex = (ThrownExceptionExp *)e; - Type *extype = ex->thrown->originalClass()->type; - // Search for an appropriate catch clause. - for (size_t i = 0; i < catches->dim; i++) + void visit(FuncExp *e) { - Catch *ca = (*catches)[i]; - Type *catype = ca->type; + #if LOG + printf("%s FuncExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + result = e; + } - if (catype->equals(extype) || catype->isBaseOf(extype, NULL)) + void visit(SymOffExp *e) + { + #if LOG + printf("%s SymOffExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + if (e->var->isFuncDeclaration() && e->offset == 0) { - // Execute the handler - if (ca->var) + result = e; + return; + } + if (isTypeInfo_Class(e->type) && e->offset == 0) + { + result = e; + return; + } + if (e->type->ty != Tpointer) + { + // Probably impossible + e->error("Cannot interpret %s at compile time", e->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + Type *pointee = ((TypePointer *)e->type)->next; + if (e->var->isThreadlocal()) + { + e->error("cannot take address of thread-local variable %s at compile time", e->var->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + // Check for taking an address of a shared variable. + // If the shared variable is an array, the offset might not be zero. + Type *fromType = NULL; + if (e->var->type->ty == Tarray || e->var->type->ty == Tsarray) + { + fromType = ((TypeArray *)(e->var->type))->next; + } + if (e->var->isDataseg() && ( + (e->offset == 0 && isSafePointerCast(e->var->type, pointee)) || + (fromType && isSafePointerCast(fromType, pointee)) + )) + { + result = e; + return; + } + Expression *val = getVarExp(e->loc, istate, e->var, goal); + if (val == EXP_CANT_INTERPRET) + { + result = val; + return; + } + if (val->type->ty == Tarray || val->type->ty == Tsarray) + { + // Check for unsupported type painting operations + Type *elemtype = ((TypeArray *)(val->type))->next; + + // It's OK to cast from fixed length to dynamic array, eg &int[3] to int[]* + if (val->type->ty == Tsarray && pointee->ty == Tarray + && elemtype->size() == pointee->nextOf()->size()) { - ctfeStack.push(ca->var); - ca->var->setValue(ex->thrown); + result = new AddrExp(e->loc, val); + result->type = e->type; + return; } - if (ca->handler) + if (!isSafePointerCast(elemtype, pointee) ) { - e = ca->handler->interpret(istate); - if (e == EXP_GOTO_INTERPRET) + // It's also OK to cast from &string to string*. + if (e->offset == 0 && isSafePointerCast(e->var->type, pointee) ) { - InterState istatex = *istate; - istatex.start = istate->gotoTarget; // set starting statement - istatex.gotoTarget = NULL; - Expression *eh = ca->handler->interpret(&istatex); - if (!istatex.start) - { - istate->gotoTarget = NULL; - e = eh; - } + result = new VarExp(e->loc, e->var); + result->type = e->type; + return; } + e->error("reinterpreting cast from %s to %s is not supported in CTFE", + val->type->toChars(), e->type->toChars()); + result = EXP_CANT_INTERPRET; + return; } - else - e = NULL; - return e; - } - } - return e; -} - -bool isAnErrorException(ClassDeclaration *cd) -{ - return cd == ClassDeclaration::errorException || ClassDeclaration::errorException->isBaseOf(cd, NULL); -} - -ThrownExceptionExp *chainExceptions(ThrownExceptionExp *oldest, ThrownExceptionExp *newest) -{ -#if LOG - printf("Collided exceptions %s %s\n", oldest->thrown->toChars(), newest->thrown->toChars()); -#endif -#if DMDV2 - // Little sanity check to make sure it's really a Throwable - ClassReferenceExp *boss = oldest->thrown; - assert((*boss->value->elements)[4]->type->ty == Tclass); - ClassReferenceExp *collateral = newest->thrown; - if (isAnErrorException(collateral->originalClass()) - && !isAnErrorException(boss->originalClass())) - { // The new exception bypass the existing chain - assert((*collateral->value->elements)[5]->type->ty == Tclass); - (*collateral->value->elements)[5] = boss; - return newest; - } - while ((*boss->value->elements)[4]->op == TOKclassreference) - { - boss = (ClassReferenceExp *)(*boss->value->elements)[4]; - } - (*boss->value->elements)[4] = collateral; - return oldest; -#else - // for D1, the newest exception just clobbers the older one - return newest; -#endif -} + dinteger_t sz = pointee->size(); + dinteger_t indx = e->offset / sz; + assert(sz * indx == e->offset); + Expression *aggregate = NULL; + if (val->op == TOKarrayliteral || val->op == TOKstring) + aggregate = val; + else if (val->op == TOKslice) + { + aggregate = ((SliceExp *)val)->e1; + Expression *lwr = ((SliceExp *)val)->lwr->interpret(istate); + indx += lwr->toInteger(); + } + if (aggregate) + { + IntegerExp *ofs = new IntegerExp(e->loc, indx, Type::tsize_t); + result = new IndexExp(e->loc, aggregate, ofs); + result->type = e->type; + return; + } + } + else if (e->offset == 0 && isSafePointerCast(e->var->type, pointee) ) + { + // Create a CTFE pointer &var + VarExp *ve = new VarExp(e->loc, e->var); + ve->type = e->var->type; + result = new AddrExp(e->loc, ve); + result->type = e->type; + return; + } -Expression *TryFinallyStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s TryFinallyStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start == this) - istate->start = NULL; - if (istate->start) - { - Expression *e = NULL; - if (body) - e = body->interpret(istate); - // Jump into/out from finalbody is disabled in semantic analysis. - // and jump inside will be handled by the ScopeStatement == finalbody. - return e; + e->error("Cannot convert &%s to %s at compile time", e->var->type->toChars(), e->type->toChars()); + result = EXP_CANT_INTERPRET; } - Expression *e = body ? body->interpret(istate) : NULL; - if (e == EXP_CANT_INTERPRET) - return e; - Expression *second = finalbody ? finalbody->interpret(istate) : NULL; - if (second == EXP_CANT_INTERPRET) - return second; - if (exceptionOrCantInterpret(second)) - { // Check for collided exceptions - if (exceptionOrCantInterpret(e)) - e = chainExceptions((ThrownExceptionExp *)e, (ThrownExceptionExp *)second); - else - e = second; + void visit(AddrExp *e) + { + #if LOG + printf("%s AddrExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + if (e->e1->op == TOKvar && ((VarExp *)e->e1)->var->isDataseg()) + { + // Normally this is already done by optimize() + // Do it here in case optimize(0) wasn't run before CTFE + result = new SymOffExp(e->loc, ((VarExp *)e->e1)->var, 0); + result->type = e->type; + return; + } + // For reference types, we need to return an lvalue ref. + TY tb = e->e1->type->toBasetype()->ty; + bool needRef = (tb == Tarray || tb == Taarray || tb == Tclass); + result = e->e1->interpret(istate, needRef ? ctfeNeedLvalueRef : ctfeNeedLvalue); + if (exceptionOrCantInterpret(result)) + return; + // Return a simplified address expression + result = new AddrExp(e->loc, result); + result->type = e->type; } - return e; -} -Expression *ThrowStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s ThrowStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start) - { if (istate->start != this) - return NULL; - istate->start = NULL; - } + void visit(DelegateExp *e) + { + #if LOG + printf("%s DelegateExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + // TODO: Really we should create a CTFE-only delegate expression + // of a pointer and a funcptr. - Expression *e = exp->interpret(istate); - if (exceptionOrCantInterpret(e)) - return e; - assert(e->op == TOKclassreference); - return new ThrownExceptionExp(loc, (ClassReferenceExp *)e); -} + // If it is &nestedfunc, just return it + // TODO: We should save the context pointer + if (e->e1->op == TOKvar && ((VarExp *)e->e1)->var->isFuncDeclaration()) + { + result = e; + return; + } -Expression *OnScopeStatement::interpret(InterState *istate) -{ - assert(0); - return EXP_CANT_INTERPRET; -} + // If it has already been CTFE'd, just return it + if (e->e1->op == TOKstructliteral || e->e1->op == TOKclassreference) + { + result = e; + return; + } -Expression *WithStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s WithStatement::interpret()\n", loc.toChars()); -#endif + // Else change it into &structliteral.func or &classref.func + result = e->e1->interpret(istate, ctfeNeedLvalue); - // If it is with(Enum) {...}, just execute the body. - if (exp->op == TOKimport || exp->op == TOKtype) - return body ? body->interpret(istate) : EXP_VOID_INTERPRET; + if (exceptionOrCantInterpret(result)) + return; - if (istate->start) - { if (istate->start != this) - return NULL; - istate->start = NULL; + result = new DelegateExp(e->loc, result, e->func); + result->type = e->type; } - Expression *e = exp->interpret(istate); - if (exceptionOrCantInterpret(e)) - return e; - if (wthis->type->ty == Tpointer && exp->type->ty != Tpointer) - { - e = new AddrExp(loc, e); - e->type = wthis->type; - } - ctfeStack.push(wthis); - wthis->setValue(e); - if (body) + + // ------------------------------------------------------------- + // Remove out, ref, and this + // ------------------------------------------------------------- + // The variable used in a dotvar, index, or slice expression, + // after 'out', 'ref', and 'this' have been removed. + static Expression *resolveReferences(Expression *e) { - e = body->interpret(istate); - if (e == EXP_GOTO_INTERPRET) + for(;;) { - InterState istatex = *istate; - istatex.start = istate->gotoTarget; // set starting statement - istatex.gotoTarget = NULL; - Expression *ex = body->interpret(&istatex); - if (!istatex.start) + if (e->op == TOKthis) { - istate->gotoTarget = NULL; - e = ex; + Expression *thisval = ctfeStack.getThis(); + assert(thisval); + assert(e != thisval); + e = thisval; + continue; + } + if (e->op == TOKvar) + { + VarExp *ve = (VarExp *)e; + VarDeclaration *v = ve->var->isVarDeclaration(); + assert(v); + if (v->type->ty == Tpointer) + break; + if (v->ctfeAdrOnStack == (size_t)-1) // If not on the stack, can't possibly be a ref. + break; + Expression *val = getValue(v); + if (val && (val->op == TOKslice)) + { + SliceExp *se = (SliceExp *)val; + if (se->e1->op == TOKarrayliteral || se->e1->op == TOKassocarrayliteral || se->e1->op == TOKstring) + break; + e = val; + continue; + } + else if (val && (val->op == TOKindex || val->op == TOKdotvar + || val->op == TOKthis || val->op == TOKvar)) + { + e = val; + continue; + } } + break; } + return e; } - else - e = EXP_VOID_INTERPRET; - ctfeStack.pop(wthis); - return e; -} -Expression *AsmStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s AsmStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start) - { if (istate->start != this) - return NULL; - istate->start = NULL; - } + static Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal) + { + Expression *e = EXP_CANT_INTERPRET; + VarDeclaration *v = d->isVarDeclaration(); + SymbolDeclaration *s = d->isSymbolDeclaration(); + if (v) + { + /* Magic variable __ctfe always returns true when interpreting + */ + if (v->ident == Id::ctfe) + return new IntegerExp(loc, 1, Type::tbool); - error("asm statements cannot be interpreted at compile time"); - return EXP_CANT_INTERPRET; -} + if (!v->originalType && v->scope) // semantic() not yet run + { + v->semantic (v->scope); + if (v->type->ty == Terror) + return EXP_CANT_INTERPRET; + } -#ifdef IN_GCC -Expression *ExtAsmStatement::interpret(InterState *istate) -{ -#if LOG - printf("%s ExtAsmStatement::interpret()\n", loc.toChars()); -#endif - if (istate->start) - { if (istate->start != this) - return NULL; - istate->start = NULL; - } + if ((v->isConst() || v->isImmutable() || v->storage_class & STCmanifest) && + !hasValue(v) && + v->init && !v->isCTFE()) + { + if(v->scope) + v->init = v->init->semantic(v->scope, v->type, INITinterpret); // might not be run on aggregate members + e = v->init->toExpression(v->type); + if (v->inuse) + { + error(loc, "circular initialization of %s", v->toChars()); + return EXP_CANT_INTERPRET; + } - error("extended asm statements cannot be interpreted at compile time"); - return EXP_CANT_INTERPRET; -} -#endif + if (e && (e->op == TOKconstruct || e->op == TOKblit)) + { + AssignExp *ae = (AssignExp *)e; + e = ae->e2; + v->inuse++; + e = e->interpret(istate, ctfeNeedAnyValue); + v->inuse--; + if (e == EXP_CANT_INTERPRET && !global.gag && !CtfeStatus::stackTraceCallsToSuppress) + errorSupplemental(loc, "while evaluating %s.init", v->toChars()); + if (exceptionOrCantInterpret(e)) + return e; + e->type = v->type; + } + else + { + if (e && !e->type) + e->type = v->type; + if (e && e->op != TOKerror) + { + v->inuse++; + e = e->interpret(istate, ctfeNeedAnyValue); + v->inuse--; + } + if (e == EXP_CANT_INTERPRET && !global.gag && !CtfeStatus::stackTraceCallsToSuppress) + errorSupplemental(loc, "while evaluating %s.init", v->toChars()); + } + if (e && e != EXP_CANT_INTERPRET && e->op != TOKthrownexception) + { + e = copyLiteral(e); + if (v->isDataseg() || (v->storage_class & STCmanifest )) + ctfeStack.saveGlobalConstant(v, e); + } + } + else if (v->isCTFE() && !hasValue(v)) + { + if (v->init && v->type->size() != 0) + { + if (v->init->isVoidInitializer()) + { + // var should have been initialized when it was created + error(loc, "CTFE internal error - trying to access uninitialized var"); + assert(0); + e = EXP_CANT_INTERPRET; + } + else + { + e = v->init->toExpression(); + e = e->interpret(istate); + } + } + else + e = v->type->defaultInitLiteral(e->loc); + } + else if (!(v->isDataseg() || v->storage_class & STCmanifest) && !v->isCTFE() && !istate) + { + error(loc, "variable %s cannot be read at compile time", v->toChars()); + return EXP_CANT_INTERPRET; + } + else + { + e = hasValue(v) ? getValue(v) : NULL; + if (!e && !v->isCTFE() && v->isDataseg()) + { + error(loc, "static variable %s cannot be read at compile time", v->toChars()); + e = EXP_CANT_INTERPRET; + } + else if (!e) + { + assert(!(v->init && v->init->isVoidInitializer())); + // CTFE initiated from inside a function + error(loc, "variable %s cannot be read at compile time", v->toChars()); + return EXP_CANT_INTERPRET; + } + else if (exceptionOrCantInterpret(e)) + return e; + else if (goal == ctfeNeedLvalue && v->isRef() && e->op == TOKindex) + { + // If it is a foreach ref, resolve the index into a constant + IndexExp *ie = (IndexExp *)e; + Expression *w = ie->e2->interpret(istate); + if (w != ie->e2) + { + e = new IndexExp(ie->loc, ie->e1, w); + e->type = ie->type; + } + return e; + } + else if ((goal == ctfeNeedLvalue) + || e->op == TOKstring || e->op == TOKstructliteral || e->op == TOKarrayliteral + || e->op == TOKassocarrayliteral || e->op == TOKslice + || e->type->toBasetype()->ty == Tpointer) + return e; // it's already an Lvalue + else if (e->op == TOKvoid) + { + VoidInitExp *ve = (VoidInitExp *)e; + error(loc, "cannot read uninitialized variable %s in ctfe", v->toPrettyChars()); + errorSupplemental(ve->var->loc, "%s was uninitialized and used before set", ve->var->toChars()); + e = EXP_CANT_INTERPRET; + } + else + e = e->interpret(istate, goal); + } + if (!e) + e = EXP_CANT_INTERPRET; + } + else if (s) + { + // Struct static initializers, for example + e = s->dsym->type->defaultInitLiteral(loc); + if (e->op == TOKerror) + error(loc, "CTFE failed because of previous errors in %s.init", s->toChars()); + e = e->semantic(NULL); + if (e->op == TOKerror) + e = EXP_CANT_INTERPRET; + else // Convert NULL to VoidExp + e = e->interpret(istate, goal); + } + else + error(loc, "cannot interpret declaration %s at compile time", d->toChars()); + return e; + } -#if DMDV2 -Expression *ImportStatement::interpret(InterState *istate) -{ -#if LOG - printf("ImportStatement::interpret()\n"); -#endif - if (istate->start) - { if (istate->start != this) - return NULL; - istate->start = NULL; + void visit(VarExp *e) + { + #if LOG + printf("%s VarExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + if (goal == ctfeNeedLvalueRef) + { + VarDeclaration *v = e->var->isVarDeclaration(); + if (v && !v->isDataseg() && !v->isCTFE() && !istate) + { + e->error("variable %s cannot be referenced at compile time", v->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + else if (v && !hasValue(v)) + { + if (!v->isCTFE() && v->isDataseg()) + e->error("static variable %s cannot be referenced at compile time", v->toChars()); + else // CTFE initiated from inside a function + e->error("variable %s cannot be read at compile time", v->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + else if (v && hasValue(v) && getValue(v)->op == TOKvar) + { + // A ref of a reference, is the original reference + result = getValue(v); + return; + } + result = e; + return; + } + result = getVarExp(e->loc, istate, e->var, goal); + // A VarExp may include an implicit cast. It must be done explicitly. + if (result != EXP_CANT_INTERPRET && result->op != TOKthrownexception) + result = paintTypeOntoLiteral(e->type, result); } -; - return NULL; -} -#endif -/******************************** Expression ***************************/ + void visit(DeclarationExp *e) + { + #if LOG + printf("%s DeclarationExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + VarDeclaration *v = e->declaration->isVarDeclaration(); + if (v) + { + if (v->toAlias()->isTupleDeclaration()) + { + result = NULL; -Expression *Expression::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s Expression::interpret() %s\n", loc.toChars(), toChars()); - printf("type = %s\n", type->toChars()); - dump(0); -#endif - error("Cannot interpret %s at compile time", toChars()); - return EXP_CANT_INTERPRET; -} + // Reserve stack space for all tuple members + TupleDeclaration *td = v->toAlias()->isTupleDeclaration(); + if (!td->objects) + return; + for(size_t i= 0; i < td->objects->dim; ++i) + { + RootObject * o = (*td->objects)[i]; + Expression *ex = isExpression(o); + DsymbolExp *s = (ex && ex->op == TOKdsymbol) ? (DsymbolExp *)ex : NULL; + VarDeclaration *v2 = s ? s->s->isVarDeclaration() : NULL; + assert(v2); + if (!v2->isDataseg() || v2->isCTFE()) + { + ctfeStack.push(v2); + if (v2->init) + { + ExpInitializer *ie = v2->init->isExpInitializer(); + if (ie) + { + setValue(v2, ie->exp->interpret(istate, goal)); + } + else if (v2->init->isVoidInitializer()) + { + setValue(v2, voidInitLiteral(v2->type, v2)); + } + else + { + e->error("Declaration %s is not yet implemented in CTFE", e->toChars()); + result = EXP_CANT_INTERPRET; + } + } + } + } + return; + } + if (!(v->isDataseg() || v->storage_class & STCmanifest) || v->isCTFE()) + ctfeStack.push(v); + Dsymbol *s = v->toAlias(); + if (s == v && !v->isStatic() && v->init) + { + ExpInitializer *ie = v->init->isExpInitializer(); + if (ie) + result = ie->exp->interpret(istate, goal); + else if (v->init->isVoidInitializer()) + { + result = voidInitLiteral(v->type, v); + // There is no AssignExp for void initializers, + // so set it here. + setValue(v, result); + } + else + { + e->error("Declaration %s is not yet implemented in CTFE", e->toChars()); + result = EXP_CANT_INTERPRET; + } + } + else if (s == v && !v->init && v->type->size() == 0) + { + // Zero-length arrays don't need an initializer + result = v->type->defaultInitLiteral(e->loc); + } + else if (s == v && (v->isConst() || v->isImmutable()) && v->init) + { + result = v->init->toExpression(); + if (!result) + result = EXP_CANT_INTERPRET; + else if (!result->type) + result->type = v->type; + } + else if (s->isTupleDeclaration() && !v->init) + result = NULL; + else if (v->isStatic()) + result = NULL; // Just ignore static variables which aren't read or written yet + else + { + e->error("Variable %s cannot be modified at compile time", v->toChars()); + result = EXP_CANT_INTERPRET; + } + } + else if (e->declaration->isAttribDeclaration() || + e->declaration->isTemplateMixin() || + e->declaration->isTupleDeclaration()) + { + // Check for static struct declarations, which aren't executable + AttribDeclaration *ad = e->declaration->isAttribDeclaration(); + if (ad && ad->decl && ad->decl->dim == 1) + { + Dsymbol *s = (*ad->decl)[0]; + if (s->isAggregateDeclaration() || + s->isTemplateDeclaration()) + { + result = NULL; + return; // static (template) struct declaration. Nothing to do. + } + } -Expression *ThisExp::interpret(InterState *istate, CtfeGoal goal) -{ - Expression *localThis = ctfeStack.getThis(); - if (localThis && localThis->op == TOKstructliteral) - return localThis; - if (localThis) - return localThis->interpret(istate, goal); - error("value of 'this' is not known at compile time"); - return EXP_CANT_INTERPRET; -} + // These can be made to work, too lazy now + e->error("Declaration %s is not yet implemented in CTFE", e->toChars()); + result = EXP_CANT_INTERPRET; + } + else + { + // Others should not contain executable code, so are trivial to evaluate + result = NULL; + } + #if LOG + printf("-DeclarationExp::interpret(%s): %p\n", e->toChars(), result); + #endif + } -Expression *NullExp::interpret(InterState *istate, CtfeGoal goal) -{ - return this; -} + void visit(TupleExp *e) + { + #if LOG + printf("%s TupleExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + Expressions *expsx = NULL; -Expression *IntegerExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s IntegerExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - return this; -} + if (e->e0) + { + if (e->e0->interpret(istate) == EXP_CANT_INTERPRET) + { + result = EXP_CANT_INTERPRET; + return; + } + } -Expression *RealExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s RealExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - return this; -} + for (size_t i = 0; i < e->exps->dim; i++) + { + Expression *exp = (*e->exps)[i]; + Expression *ex = exp->interpret(istate); + if (exceptionOrCantInterpret(ex)) + { + result = ex; + return; + } -Expression *ComplexExp::interpret(InterState *istate, CtfeGoal goal) -{ - return this; -} + // A tuple of assignments can contain void (Bug 5676). + if (goal == ctfeNeedNothing) + continue; + if (ex == EXP_VOID_INTERPRET) + { + e->error("ICE: void element %s in tuple", exp->toChars()); + assert(0); + } -Expression *StringExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s StringExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - /* In both D1 and D2, attempts to modify string literals are prevented - * in BinExp::interpretAssignCommon. - * In D2, we also disallow casts of read-only literals to mutable, - * though it isn't strictly necessary. - */ -#if 0 //DMDV2 - // Fixed-length char arrays always get duped later anyway. - if (type->ty == Tsarray) - return this; - if (!(((TypeNext *)type)->next->toBasetype()->mod & (MODconst | MODimmutable))) - { // It seems this happens only when there has been an explicit cast - error("cannot cast a read-only string literal to mutable in CTFE"); - return EXP_CANT_INTERPRET; + /* If any changes, do Copy On Write + */ + if (ex != exp) + { + if (!expsx) + { + expsx = new Expressions(); + ++CtfeStatus::numArrayAllocs; + expsx->setDim(e->exps->dim); + for (size_t j = 0; j < i; j++) + { + (*expsx)[j] = (*e->exps)[j]; + } + } + (*expsx)[i] = ex; + } + } + if (expsx) + { + TupleExp *te = new TupleExp(e->loc, expsx); + expandTuples(te->exps); + te->type = new TypeTuple(te->exps); + result = te; + return; + } + result = e; + return; } -#endif - return this; -} - -Expression *FuncExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s FuncExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - return this; -} -Expression *SymOffExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s SymOffExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - if (var->isFuncDeclaration() && offset == 0) + void visit(ArrayLiteralExp *e) { - return this; - } - if (isTypeInfo_Class(type) && offset == 0) - { - return this; - } - if (type->ty != Tpointer) - { // Probably impossible - error("Cannot interpret %s at compile time", toChars()); - return EXP_CANT_INTERPRET; + #if LOG + printf("%s ArrayLiteralExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + if (e->ownedByCtfe) // We've already interpreted all the elements + { + result = e; + return; + } + Expressions *expsx = NULL; + if (e->elements) + { + for (size_t i = 0; i < e->elements->dim; i++) + { + Expression *exp = (*e->elements)[i]; + + if (exp->op == TOKindex) // segfault bug 6250 + assert(((IndexExp*)exp)->e1 != e); + Expression *ex = exp->interpret(istate); + if (exceptionOrCantInterpret(ex)) + { + result = ex; + return; + } + + /* If any changes, do Copy On Write + */ + if (ex != exp) + { + if (!expsx) + { + expsx = new Expressions(); + ++CtfeStatus::numArrayAllocs; + expsx->setDim(e->elements->dim); + for (size_t j = 0; j < e->elements->dim; j++) + { + (*expsx)[j] = (*e->elements)[j]; + } + } + (*expsx)[i] = ex; + } + } + } + if (e->elements && expsx) + { + expandTuples(expsx); + if (expsx->dim != e->elements->dim) + { + e->error("Internal Compiler Error: Invalid array literal"); + result = EXP_CANT_INTERPRET; + return; + } + ArrayLiteralExp *ae = new ArrayLiteralExp(e->loc, expsx); + ae->type = e->type; + result = copyLiteral(ae); + return; + } + if (((TypeNext *)e->type)->next->mod & (MODconst | MODimmutable)) + { + // If it's immutable, we don't need to dup it + result = e; + return; + } + result = copyLiteral(e); } - Type *pointee = ((TypePointer *)type)->next; - if ( var->isThreadlocal()) + + void visit(AssocArrayLiteralExp *e) { - error("cannot take address of thread-local variable %s at compile time", var->toChars()); - return EXP_CANT_INTERPRET; + Expressions *keysx = e->keys; + Expressions *valuesx = e->values; + + #if LOG + printf("%s AssocArrayLiteralExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + if (e->ownedByCtfe) // We've already interpreted all the elements + { + result = e; + return; + } + for (size_t i = 0; i < e->keys->dim; i++) + { + Expression *ekey = (*e->keys)[i]; + Expression *evalue = (*e->values)[i]; + + Expression *ex = ekey->interpret(istate); + if (exceptionOrCantInterpret(ex)) + { + result = ex; + return; + } + + /* If any changes, do Copy On Write + */ + if (ex != ekey) + { + if (keysx == e->keys) + keysx = (Expressions *)e->keys->copy(); + (*keysx)[i] = ex; + } + + ex = evalue->interpret(istate); + if (exceptionOrCantInterpret(ex)) + { + result = ex; + return; + } + + /* If any changes, do Copy On Write + */ + if (ex != evalue) + { + if (valuesx == e->values) + valuesx = (Expressions *)e->values->copy(); + (*valuesx)[i] = ex; + } + } + if (keysx != e->keys) + expandTuples(keysx); + if (valuesx != e->values) + expandTuples(valuesx); + if (keysx->dim != valuesx->dim) + { + e->error("Internal Compiler Error: invalid AA"); + result = EXP_CANT_INTERPRET; + return; + } + + /* Remove duplicate keys + */ + for (size_t i = 1; i < keysx->dim; i++) + { + Expression *ekey = (*keysx)[i - 1]; + for (size_t j = i; j < keysx->dim; j++) + { + Expression *ekey2 = (*keysx)[j]; + int eq = ctfeEqual(e->loc, TOKequal, ekey, ekey2); + if (eq) // if a match + { + // Remove ekey + if (keysx == e->keys) + keysx = (Expressions *)e->keys->copy(); + if (valuesx == e->values) + valuesx = (Expressions *)e->values->copy(); + keysx->remove(i - 1); + valuesx->remove(i - 1); + i -= 1; // redo the i'th iteration + break; + } + } + } + + if (keysx != e->keys || valuesx != e->values) + { + AssocArrayLiteralExp *ae; + ae = new AssocArrayLiteralExp(e->loc, keysx, valuesx); + ae->type = e->type; + ae->ownedByCtfe = true; + result = ae; + return; + } + result = e; } - // Check for taking an address of a shared variable. - // If the shared variable is an array, the offset might not be zero. - Type *fromType = NULL; - if (var->type->ty == Tarray || var->type->ty == Tsarray) + + void visit(StructLiteralExp *e) { - fromType = ((TypeArray *)(var->type))->next; + #if LOG + printf("%s StructLiteralExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + if (e->ownedByCtfe) + { + result = e; + return; + } + + size_t elemdim = e->elements ? e->elements->dim : 0; + Expressions *expsx = NULL; + for (size_t i = 0; i < e->sd->fields.dim; i++) + { + Expression *ex = NULL; + Expression *exp = NULL; + if (i >= elemdim) + { + /* If a nested struct has no initialized hidden pointer, + * set it to null to match the runtime behaviour. + */ + if (i == e->sd->fields.dim - 1 && e->sd->isNested()) + { + // Context field has not been filled + ex = new NullExp(e->loc); + ex->type = e->sd->fields[i]->type; + } + } + else + { + exp = (*e->elements)[i]; + if (!exp) + { + /* Ideally, we'd convert NULL members into void expressions. + * The problem is that the VoidExp will be removed when we + * leave CTFE, causing another memory allocation if we use this + * same struct literal again. + * + * ex = voidInitLiteral(sd->fields[i]->type, sd->fields[i]); + */ + ex = NULL; + } + else + { + ex = exp->interpret(istate); + if (exceptionOrCantInterpret(ex)) + { + result = ex; + return; + } + } + } + + /* If any changes, do Copy On Write + */ + if (ex != exp) + { + if (!expsx) + { + expsx = new Expressions(); + ++CtfeStatus::numArrayAllocs; + expsx->setDim(e->sd->fields.dim); + for (size_t j = 0; j < e->elements->dim; j++) + { + (*expsx)[j] = (*e->elements)[j]; + } + } + (*expsx)[i] = ex; + } + } + + if (e->elements && expsx) + { + expandTuples(expsx); + if (expsx->dim != e->sd->fields.dim) + { + e->error("Internal Compiler Error: invalid struct literal"); + result = EXP_CANT_INTERPRET; + return; + } + StructLiteralExp *se = new StructLiteralExp(e->loc, e->sd, expsx); + se->type = e->type; + se->ownedByCtfe = true; + result = se; + return; + } + result = copyLiteral(e); } - if ( var->isDataseg() && ( - (offset == 0 && isSafePointerCast(var->type, pointee)) || - (fromType && isSafePointerCast(fromType, pointee)) - )) + + // Create an array literal of type 'newtype' with dimensions given by + // 'arguments'[argnum..$] + static Expression *recursivelyCreateArrayLiteral(Loc loc, Type *newtype, InterState *istate, + Expressions *arguments, int argnum) { - return this; + Expression *lenExpr = (((*arguments)[argnum]))->interpret(istate); + if (exceptionOrCantInterpret(lenExpr)) + return lenExpr; + size_t len = (size_t)(lenExpr->toInteger()); + Type *elemType = ((TypeArray *)newtype)->next; + if (elemType->ty == Tarray && argnum < arguments->dim - 1) + { + Expression *elem = recursivelyCreateArrayLiteral(loc, elemType, istate, + arguments, argnum + 1); + if (exceptionOrCantInterpret(elem)) + return elem; + + Expressions *elements = new Expressions(); + elements->setDim(len); + for (size_t i = 0; i < len; i++) + (*elements)[i] = copyLiteral(elem); + ArrayLiteralExp *ae = new ArrayLiteralExp(loc, elements); + ae->type = newtype; + ae->ownedByCtfe = true; + return ae; + } + assert(argnum == arguments->dim - 1); + if (elemType->ty == Tchar || elemType->ty == Twchar || elemType->ty == Tdchar) + return createBlockDuplicatedStringLiteral(loc, newtype, + (unsigned)(elemType->defaultInitLiteral(loc)->toInteger()), + len, (unsigned char)elemType->size()); + return createBlockDuplicatedArrayLiteral(loc, newtype, + elemType->defaultInitLiteral(loc), len); } - Expression *val = getVarExp(loc, istate, var, goal); - if (val == EXP_CANT_INTERPRET) - return val; - if (val->type->ty == Tarray || val->type->ty == Tsarray) + + void visit(NewExp *e) { - // Check for unsupported type painting operations - Type *elemtype = ((TypeArray *)(val->type))->next; + #if LOG + printf("%s NewExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + if (e->newtype->ty == Tarray && e->arguments) + { + result = recursivelyCreateArrayLiteral(e->loc, e->newtype, istate, e->arguments, 0); + return; + } - // It's OK to cast from fixed length to dynamic array, eg &int[3] to int[]* - if (val->type->ty == Tsarray && pointee->ty == Tarray - && elemtype->size() == pointee->nextOf()->size()) + if (e->newtype->toBasetype()->ty == Tstruct) { - Expression *e = new AddrExp(loc, val); - e->type = type; - return e; + if (e->member) + { + Expression *se = e->newtype->defaultInitLiteral(e->loc); + result = interpret(e->member, istate, e->arguments, se); + } + else + { + StructDeclaration *sd = ((TypeStruct *)e->newtype->toBasetype())->sym; + Expressions *exps = new Expressions(); + exps->reserve(sd->fields.dim); + if (e->arguments) + { + exps->setDim(e->arguments->dim); + for (size_t i = 0; i < exps->dim; i++) + { + Expression *ex = (*e->arguments)[i]; + if (ex) + ex = ex->interpret(istate); + if (exceptionOrCantInterpret(ex)) + { + result = ex; + return; + } + (*exps)[i] = ex; + } + } + sd->fill(e->loc, exps, false); + + StructLiteralExp *se = new StructLiteralExp(e->loc, sd, exps, e->newtype); + se->type = e->newtype; + se->ownedByCtfe = true; + result = se->interpret(istate); + } + if (exceptionOrCantInterpret(result)) + { + result = EXP_CANT_INTERPRET; + return; + } + result = new AddrExp(e->loc, copyLiteral(result)); + result->type = e->type; + return; + } + if (e->newtype->toBasetype()->ty == Tclass) + { + ClassDeclaration *cd = ((TypeClass *)e->newtype->toBasetype())->sym; + size_t totalFieldCount = 0; + for (ClassDeclaration *c = cd; c; c = c->baseClass) + totalFieldCount += c->fields.dim; + Expressions *elems = new Expressions; + elems->setDim(totalFieldCount); + size_t fieldsSoFar = totalFieldCount; + for (ClassDeclaration *c = cd; c; c = c->baseClass) + { + fieldsSoFar -= c->fields.dim; + for (size_t i = 0; i < c->fields.dim; i++) + { + VarDeclaration *v = c->fields[i]; + if (v->inuse) + { + e->error("circular reference to '%s'", v->toPrettyChars()); + result = EXP_CANT_INTERPRET; + return; + } + Expression *m; + if (v->init) + { + if (v->init->isVoidInitializer()) + m = voidInitLiteral(v->type, v); + else + m = v->getConstInitializer(true); + } + else + m = v->type->defaultInitLiteral(e->loc); + if (exceptionOrCantInterpret(m)) + { + result = m; + return; + } + (*elems)[fieldsSoFar+i] = copyLiteral(m); + } + } + // Hack: we store a ClassDeclaration instead of a StructDeclaration. + // We probably won't get away with this. + StructLiteralExp *se = new StructLiteralExp(e->loc, (StructDeclaration *)cd, elems, e->newtype); + se->ownedByCtfe = true; + Expression *eref = new ClassReferenceExp(e->loc, se, e->type); + if (e->member) + { + // Call constructor + if (!e->member->fbody) + { + Expression *ctorfail = evaluateIfBuiltin(istate, e->loc, e->member, e->arguments, eref); + if (ctorfail && exceptionOrCantInterpret(ctorfail)) + { + result = ctorfail; + return; + } + if (ctorfail) + { + result = eref; + return; + } + e->member->error("%s cannot be constructed at compile time, because the constructor has no available source code", e->newtype->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + Expression *ctorfail = interpret(e->member, istate, e->arguments, eref); + if (exceptionOrCantInterpret(ctorfail)) + { + result = ctorfail; + return; + } + } + result = eref; + return; } - if ( !isSafePointerCast(elemtype, pointee) ) - { // It's also OK to cast from &string to string*. - if ( offset == 0 && isSafePointerCast(var->type, pointee) ) + if (e->newtype->toBasetype()->isscalar()) + { + Expression *newval; + if (e->arguments && e->arguments->dim) + newval = (*e->arguments)[0]->interpret(istate); + else + newval = e->newtype->defaultInitLiteral(e->loc); + if (exceptionOrCantInterpret(newval)) { - VarExp *ve = new VarExp(loc, var); - ve->type = type; - return ve; + result = newval; + return; } - error("reinterpreting cast from %s to %s is not supported in CTFE", - val->type->toChars(), type->toChars()); - return EXP_CANT_INTERPRET; + + /* Create &[newval][0] + */ + Expressions *elements = new Expressions(); + elements->setDim(1); + (*elements)[0] = copyLiteral(newval); + ArrayLiteralExp *ae = new ArrayLiteralExp(e->loc, elements); + ae->type = e->newtype->arrayOf(); + ae->ownedByCtfe = true; + + result = new IndexExp(e->loc, ae, new IntegerExp(Loc(), 0, Type::tsize_t)); + result->type = e->newtype; + + result = new AddrExp(e->loc, result); + result->type = e->type; + return; } + e->error("Cannot interpret %s at compile time", e->toChars()); + result = EXP_CANT_INTERPRET; + } - dinteger_t sz = pointee->size(); - dinteger_t indx = offset/sz; - assert(sz * indx == offset); - Expression *aggregate = NULL; - if (val->op == TOKarrayliteral || val->op == TOKstring) - aggregate = val; - else if (val->op == TOKslice) + void visit(UnaExp *e) + { + #if LOG + printf("%s UnaExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + if (e->op == TOKdottype) { - aggregate = ((SliceExp *)val)->e1; - Expression *lwr = ((SliceExp *)val)->lwr->interpret(istate); - indx += lwr->toInteger(); + e->error("Internal Compiler Error: CTFE DotType: %s", e->toChars()); + result = EXP_CANT_INTERPRET; + return; } - if (aggregate) + Expression *e1 = e->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) { - IntegerExp *ofs = new IntegerExp(loc, indx, Type::tsize_t); - IndexExp *ie = new IndexExp(loc, aggregate, ofs); - ie->type = type; - return ie; + result = e1; + return; + } + switch(e->op) + { + case TOKneg: result = Neg(e->type, e1); break; + case TOKtilde: result = Com(e->type, e1); break; + case TOKnot: result = Not(e->type, e1); break; + case TOKtobool: result = Bool(e->type, e1); break; + case TOKvector: result = e; break; // do nothing + default: assert(0); } - } - else if ( offset == 0 && isSafePointerCast(var->type, pointee) ) - { - // Create a CTFE pointer &var - VarExp *ve = new VarExp(loc, var); - ve->type = var->type; - AddrExp *re = new AddrExp(loc, ve); - re->type = type; - return re; } - error("Cannot convert &%s to %s at compile time", var->type->toChars(), type->toChars()); - return EXP_CANT_INTERPRET; -} - -Expression *AddrExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s AddrExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - if (e1->op == TOKvar && ((VarExp *)e1)->var->isDataseg()) - { // Normally this is already done by optimize() - // Do it here in case optimize(0) wasn't run before CTFE - SymOffExp *se = new SymOffExp(loc, ((VarExp *)e1)->var, 0); - se->type = type; - return se; - } - // For reference types, we need to return an lvalue ref. - TY tb = e1->type->toBasetype()->ty; - bool needRef = (tb == Tarray || tb == Taarray || tb == Tclass); - Expression *e = e1->interpret(istate, needRef ? ctfeNeedLvalueRef : ctfeNeedLvalue); - if (exceptionOrCantInterpret(e)) - return e; - // Return a simplified address expression - e = new AddrExp(loc, e); - e->type = type; - return e; -} + void interpretCommon(BinExp *e, fp_t fp) + { + #if LOG + printf("%s BinExp::interpretCommon() %s\n", e->loc.toChars(), e->toChars()); + #endif + if (e->e1->type->ty == Tpointer && e->e2->type->ty == Tpointer && e->op == TOKmin) + { + Expression *e1 = e->e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e1)) + { + result = e1; + return; + } + Expression *e2 = e->e2->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e2)) + { + result = e2; + return; + } + result = pointerDifference(e->loc, e->type, e1, e2); + return; + } + if (e->e1->type->ty == Tpointer && e->e2->type->isintegral()) + { + Expression *e1 = e->e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e1)) + { + result = e1; + return; + } + Expression *e2 = e->e2->interpret(istate); + if (exceptionOrCantInterpret(e2)) + { + result = e2; + return; + } + result = pointerArithmetic(e->loc, e->op, e->type, e1, e2); + return; + } + if (e->e2->type->ty == Tpointer && e->e1->type->isintegral() && e->op == TOKadd) + { + Expression *e1 = e->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + { + result = e1; + return; + } + Expression *e2 = e->e2->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e2)) + { + result = e2; + return; + } + result = pointerArithmetic(e->loc, e->op, e->type, e2, e1); + return; + } + if (e->e1->type->ty == Tpointer || e->e2->type->ty == Tpointer) + { + e->error("pointer expression %s cannot be interpreted at compile time", e->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + Expression *e1 = e->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + { + result = e1; + return; + } + if (e1->isConst() != 1) + { + e->error("Internal Compiler Error: non-constant value %s", e->e1->toChars()); + result = EXP_CANT_INTERPRET; + return; + } -Expression *DelegateExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s DelegateExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - // TODO: Really we should create a CTFE-only delegate expression - // of a pointer and a funcptr. + Expression *e2 = e->e2->interpret(istate); + if (exceptionOrCantInterpret(e2)) + { + result = e2; + return; + } + if (e2->isConst() != 1) + { + e->error("Internal Compiler Error: non-constant value %s", e->e2->toChars()); + result = EXP_CANT_INTERPRET; + return; + } - // If it is &nestedfunc, just return it - // TODO: We should save the context pointer - if (e1->op == TOKvar && ((VarExp *)e1)->var->isFuncDeclaration()) - return this; + if (e->op == TOKshr || e->op == TOKshl || e->op == TOKushr) + { + sinteger_t i2 = e2->toInteger(); + d_uns64 sz = e1->type->size() * 8; + if (i2 < 0 || i2 >= sz) + { + e->error("shift by %lld is outside the range 0..%llu", i2, (ulonglong)sz - 1); + result = EXP_CANT_INTERPRET; + return; + } + } + result = (*fp)(e->type, e1, e2); + if (result == EXP_CANT_INTERPRET) + e->error("%s cannot be interpreted at compile time", e->toChars()); + } - // If it has already been CTFE'd, just return it - if (e1->op == TOKstructliteral || e1->op == TOKclassreference) - return this; + void interpretCompareCommon(BinExp *e, fp2_t fp) + { + #if LOG + printf("%s BinExp::interpretCompareCommon() %s\n", e->loc.toChars(), e->toChars()); + #endif + if (e->e1->type->ty == Tpointer && e->e2->type->ty == Tpointer) + { + Expression *e1 = e->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + { + result = e1; + return; + } + Expression *e2 = e->e2->interpret(istate); + if (exceptionOrCantInterpret(e2)) + { + result = e2; + return; + } + dinteger_t ofs1, ofs2; + Expression *agg1 = getAggregateFromPointer(e1, &ofs1); + Expression *agg2 = getAggregateFromPointer(e2, &ofs2); + int cmp = comparePointers(e->loc, e->op, e->type, agg1, ofs1, agg2, ofs2); + if (cmp == -1) + { + char dir = (e->op == TOKgt || e->op == TOKge) ? '<' : '>'; + e->error("The ordering of pointers to unrelated memory blocks is indeterminate in CTFE." + " To check if they point to the same memory block, use both > and < inside && or ||, " + "eg (%s && %s %c= %s + 1)", + e->toChars(), e->e1->toChars(), dir, e->e2->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + result = new IntegerExp(e->loc, cmp, e->type); + return; + } + Expression *e1 = e->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + { + result = e1; + return; + } + if (!isCtfeComparable(e1)) + { + e->error("cannot compare %s at compile time", e1->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + Expression *e2 = e->e2->interpret(istate); + if (exceptionOrCantInterpret(e2)) + { + result = e2; + return; + } + if (!isCtfeComparable(e2)) + { + e->error("cannot compare %s at compile time", e2->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + int cmp = (*fp)(e->loc, e->op, e1, e2); + result = new IntegerExp(e->loc, cmp, e->type); + } + + void visit(BinExp *e) + { + switch(e->op) + { + case TOKadd: interpretCommon(e, &Add); return; + case TOKmin: interpretCommon(e, &Min); return; + case TOKmul: interpretCommon(e, &Mul); return; + case TOKdiv: interpretCommon(e, &Div); return; + case TOKmod: interpretCommon(e, &Mod); return; + case TOKshl: interpretCommon(e, &Shl); return; + case TOKshr: interpretCommon(e, &Shr); return; + case TOKushr: interpretCommon(e, &Ushr); return; + case TOKand: interpretCommon(e, &And); return; + case TOKor: interpretCommon(e, &Or); return; + case TOKxor: interpretCommon(e, &Xor); return; + case TOKpow: interpretCommon(e, &Pow); return; + case TOKequal: + case TOKnotequal: + interpretCompareCommon(e, &ctfeEqual); + return; + case TOKidentity: + case TOKnotidentity: + interpretCompareCommon(e, &ctfeIdentity); + return; + case TOKlt: + case TOKle: + case TOKgt: + case TOKge: + case TOKleg: + case TOKlg: + case TOKunord: + case TOKue: + case TOKug: + case TOKuge: + case TOKul: + case TOKule: + interpretCompareCommon(e, &ctfeCmp); + return; + default: + printf("be = '%s' %s at [%s]\n", Token::toChars(e->op), e->toChars(), e->loc.toChars()); + assert(0); + return; + } + } - // Else change it into &structliteral.func or &classref.func - Expression *e = e1->interpret(istate, ctfeNeedLvalue); + /* Helper functions for BinExp::interpretAssignCommon + */ - if (exceptionOrCantInterpret(e)) - return e; + // Returns the variable which is eventually modified, or NULL if an rvalue. + // thisval is the current value of 'this'. + static VarDeclaration * findParentVar(Expression *e) + { + for (;;) + { + e = resolveReferences(e); + if (e->op == TOKvar) + break; + if (e->op == TOKindex) + e = ((IndexExp*)e)->e1; + else if (e->op == TOKdotvar) + e = ((DotVarExp *)e)->e1; + else if (e->op == TOKdotti) + e = ((DotTemplateInstanceExp *)e)->e1; + else if (e->op == TOKslice) + e = ((SliceExp*)e)->e1; + else + return NULL; + } + VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); + assert(v); + return v; + } - e = new DelegateExp(loc, e, func); - e->type = type; - return e; -} + void interpretAssignCommon(BinExp *e, fp_t fp, int post = 0) + { + #if LOG + printf("%s BinExp::interpretAssignCommon() %s\n", e->loc.toChars(), e->toChars()); + #endif + result = EXP_CANT_INTERPRET; + Expression *e1 = e->e1; + if (!istate) + { + e->error("value of %s is not known at compile time", e1->toChars()); + return; + } + ++CtfeStatus::numAssignments; + /* Before we begin, we need to know if this is a reference assignment + * (dynamic array, AA, or class) or a value assignment. + * Determining this for slice assignments are tricky: we need to know + * if it is a block assignment (a[] = e) rather than a direct slice + * assignment (a[] = b[]). Note that initializers of multi-dimensional + * static arrays can have 2D block assignments (eg, int[7][7] x = 6;). + * So we need to recurse to determine if it is a block assignment. + */ + bool isBlockAssignment = false; + if (e1->op == TOKslice) + { + // a[] = e can have const e. So we compare the naked types. + Type *desttype = e1->type->toBasetype(); + Type *srctype = e->e2->type->toBasetype()->castMod(0); + while (desttype->ty == Tsarray || desttype->ty == Tarray) + { + desttype = ((TypeArray *)desttype)->next; + desttype = desttype->toBasetype()->castMod(0); + if (srctype->equals(desttype)) + { + isBlockAssignment = true; + break; + } + } + } + // If it is a reference type (eg, an array), we need an lvalue. + // If it is a reference variable (such as happens in foreach), we + // need an lvalue reference. For example if x, y are int[], then + // y[0..4] = x[0..4] is an rvalue assignment (all copies in the + // slice are duplicated) + // y = x[0..4] is an lvalue assignment (if x[0] changes later, + // y[0] will also change) + // ref int [] z = x is an lvalueref assignment (if x itself changes, + // z will also change) + bool wantRef = false; + bool wantLvalueRef = false; + + // e = *x is never a reference, because *x is always a value + if (!fp && e->e1->type->toBasetype()->equals(e->e2->type->toBasetype()) && + (e1->type->toBasetype()->ty == Tarray || isAssocArray(e1->type) + || e1->type->toBasetype()->ty == Tclass) + && e->e2->op != TOKstar) + { + wantRef = true; + // If it is assignment from a ref parameter, it's not a ref assignment + if (e->e2->op == TOKvar) + { + VarDeclaration *v = ((VarExp *)e->e2)->var->isVarDeclaration(); + if (v && (v->storage_class & (STCref | STCout))) + wantRef = false; + } + } + if (isBlockAssignment && (e->e2->type->toBasetype()->ty == Tarray)) + { + wantRef = true; + } + // If it is a construction of a ref variable, it is a ref assignment + // (in fact, it is an lvalue reference assignment). + if (e->op == TOKconstruct && e->e1->op == TOKvar + && ((VarExp*)e->e1)->var->storage_class & STCref) + { + wantRef = true; + wantLvalueRef = true; + } + if (fp) + { + while (e1->op == TOKcast) + { + CastExp *ce = (CastExp *)e1; + e1 = ce->e1; + } + } + if (exceptionOrCantInterpret(e1)) + { + result = e1; + return; + } -// ------------------------------------------------------------- -// Remove out, ref, and this -// ------------------------------------------------------------- -// The variable used in a dotvar, index, or slice expression, -// after 'out', 'ref', and 'this' have been removed. -Expression * resolveReferences(Expression *e) -{ - for(;;) - { - if (e->op == TOKthis) + // First, deal with this = e; and call() = e; + if (e1->op == TOKthis) { - Expression *thisval = ctfeStack.getThis(); - assert(thisval); - assert(e != thisval); - e = thisval; - continue; + e1 = ctfeStack.getThis(); } - if (e->op == TOKvar) + if (e1->op == TOKcall) { - VarExp *ve = (VarExp *)e; - VarDeclaration *v = ve->var->isVarDeclaration(); - assert(v); - if (v->type->ty == Tpointer) - break; - if (v->ctfeAdrOnStack == (size_t)-1) // If not on the stack, can't possibly be a ref. - break; - Expression *val = v->getValue(); - if (val && (val->op == TOKslice)) + bool oldWaiting = istate->awaitingLvalueReturn; + istate->awaitingLvalueReturn = true; + e1 = e1->interpret(istate); + istate->awaitingLvalueReturn = oldWaiting; + if (exceptionOrCantInterpret(e1)) { - SliceExp *se = (SliceExp *)val; - if (se->e1->op == TOKarrayliteral || se->e1->op == TOKassocarrayliteral || se->e1->op == TOKstring) - break; - e = val; - continue; + result = e1; + return; } - else if (val && (val->op==TOKindex || val->op == TOKdotvar - || val->op == TOKthis || val->op == TOKvar)) + if (e1->op == TOKarrayliteral || e1->op == TOKstring) { - e = val; - continue; + // f() = e2, when f returns an array, is always a slice assignment. + // Convert into arr[0..arr.length] = e2 + e1 = new SliceExp(e->loc, e1, + new IntegerExp(e->loc, 0, Type::tsize_t), + ArrayLength(Type::tsize_t, e1)); + e1->type = e->type; + } + } + if (e1->op == TOKstar) + { + e1 = e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e1)) + { + result = e1; + return; + } + if (!(e1->op == TOKvar || e1->op == TOKdotvar || e1->op == TOKindex + || e1->op == TOKslice || e1->op == TOKstructliteral)) + { + e->error("cannot dereference invalid pointer %s", + e->e1->toChars()); + result = EXP_CANT_INTERPRET; + return; } } - break; - } - return e; -} - -Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal) -{ - Expression *e = EXP_CANT_INTERPRET; - VarDeclaration *v = d->isVarDeclaration(); - SymbolDeclaration *s = d->isSymbolDeclaration(); - if (v) - { -#if DMDV2 - /* Magic variable __ctfe always returns true when interpreting - */ - if (v->ident == Id::ctfe) - return new IntegerExp(loc, 1, Type::tbool); - if (!v->originalType && v->scope) // semantic() not yet run + if (!(e1->op == TOKarraylength || e1->op == TOKvar || e1->op == TOKdotvar + || e1->op == TOKindex || e1->op == TOKslice || e1->op == TOKstructliteral)) { - v->semantic (v->scope); - if (v->type->ty == Terror) - return EXP_CANT_INTERPRET; + e->error("CTFE internal error: unsupported assignment %s", e->toChars()); + result = EXP_CANT_INTERPRET; + return; } - bool doinit = (v->isConst() || v->isImmutable() || v->storage_class & STCmanifest) - && !v->hasValue(); -#else - bool doinit = v->isConst(); -#endif - if (doinit && v->init && !v->isCTFE()) + Expression * newval = NULL; + + if (!wantRef) { - if(v->scope) - v->init = v->init->semantic(v->scope, v->type, INITinterpret); // might not be run on aggregate members - e = v->init->toExpression(v->type); - if (v->inuse) + // We need to treat pointers specially, because TOKsymoff can be used to + // return a value OR a pointer + assert(e1); + assert(e1->type); + if (isPointer(e1->type) && (e->e2->op == TOKsymoff || e->e2->op == TOKaddress || e->e2->op == TOKvar)) + newval = e->e2->interpret(istate, ctfeNeedLvalue); + else + newval = e->e2->interpret(istate); + if (exceptionOrCantInterpret(newval)) { - error(loc, "circular initialization of %s", v->toChars()); - return EXP_CANT_INTERPRET; + result = newval; + return; } + } + // ---------------------------------------------------- + // Deal with read-modify-write assignments. + // Set 'newval' to the final assignment value + // Also determine the return value (except for slice + // assignments, which are more complicated) + // ---------------------------------------------------- - if (e && (e->op == TOKconstruct || e->op == TOKblit)) - { AssignExp *ae = (AssignExp *)e; - e = ae->e2; - v->inuse++; - e = e->interpret(istate, ctfeNeedAnyValue); - v->inuse--; - if (e == EXP_CANT_INTERPRET && !global.gag && !CtfeStatus::stackTraceCallsToSuppress) - errorSupplemental(loc, "while evaluating %s.init", v->toChars()); - if (exceptionOrCantInterpret(e)) - return e; - e->type = v->type; + if (fp || e1->op == TOKarraylength) + { + // If it isn't a simple assignment, we need the existing value + Expression * oldval = e1->interpret(istate); + if (exceptionOrCantInterpret(oldval)) + { + result = oldval; + return; } - else + while (oldval->op == TOKvar) { - if (e && !e->type) - e->type = v->type; - if (e && e->op != TOKerror) + oldval = resolveReferences(oldval); + oldval = oldval->interpret(istate); + if (exceptionOrCantInterpret(oldval)) { - v->inuse++; - e = e->interpret(istate, ctfeNeedAnyValue); - v->inuse--; + result = oldval; + return; } - if (e == EXP_CANT_INTERPRET && !global.gag && !CtfeStatus::stackTraceCallsToSuppress) - errorSupplemental(loc, "while evaluating %s.init", v->toChars()); } - if (e && e != EXP_CANT_INTERPRET && e->op != TOKthrownexception) + + if (fp) { - e = copyLiteral(e); - if (v->isDataseg() || (v->storage_class & STCmanifest )) - ctfeStack.saveGlobalConstant(v, e); + // ~= can create new values (see bug 6052) + if (e->op == TOKcatass) + { + // We need to dup it. We can skip this if it's a dynamic array, + // because it gets copied later anyway + if (newval->type->ty != Tarray) + newval = copyLiteral(newval); + if (newval->op == TOKslice) + newval = resolveSlice(newval); + // It becomes a reference assignment + wantRef = true; + } + if (oldval->op == TOKslice) + oldval = resolveSlice(oldval); + if (e->e1->type->ty == Tpointer && e->e2->type->isintegral() + && (e->op == TOKaddass || e->op == TOKminass || + e->op == TOKplusplus || e->op == TOKminusminus)) + { + oldval = e->e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(oldval)) + { + result = oldval; + return; + } + newval = e->e2->interpret(istate); + if (exceptionOrCantInterpret(newval)) + { + result = newval; + return; + } + newval = pointerArithmetic(e->loc, e->op, e->type, oldval, newval); + } + else if (e->e1->type->ty == Tpointer) + { + e->error("pointer expression %s cannot be interpreted at compile time", e->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + else + { + newval = (*fp)(e->type, oldval, newval); + } + if (newval == EXP_CANT_INTERPRET) + { + e->error("Cannot interpret %s at compile time", e->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + if (exceptionOrCantInterpret(newval)) + { + result = newval; + return; + } + // Determine the return value + result = ctfeCast(e->loc, e->type, e->type, post ? oldval : newval); + if (exceptionOrCantInterpret(result)) + return; } - } - else if (v->isCTFE() && !v->hasValue()) - { - if (v->init && v->type->size() != 0) + else + result = newval; + if (e1->op == TOKarraylength) { - if (v->init->isVoidInitializer()) + size_t oldlen = (size_t)oldval->toInteger(); + size_t newlen = (size_t)newval->toInteger(); + if (oldlen == newlen) // no change required -- we're done! + return; + // Now change the assignment from arr.length = n into arr = newval + e1 = ((ArrayLengthExp *)e1)->e1; + if (oldlen != 0) { - // var should have been initialized when it was created - error(loc, "CTFE internal error - trying to access uninitialized var"); - assert(0); - e = EXP_CANT_INTERPRET; + // Get the old array literal. + oldval = e1->interpret(istate); + while (oldval->op == TOKvar) + { + oldval = resolveReferences(oldval); + oldval = oldval->interpret(istate); + } + } + Type *t = e1->type->toBasetype(); + if (t->ty == Tarray) + { + newval = changeArrayLiteralLength(e->loc, (TypeArray *)t, oldval, + oldlen, newlen); + // We have changed it into a reference assignment + // Note that returnValue is still the new length. + wantRef = true; + if (e1->op == TOKstar) + { + // arr.length+=n becomes (t=&arr, *(t).length=*(t).length+n); + e1 = e1->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e1)) + { + result = e1; + return; + } + } } else { - e = v->init->toExpression(); - e = e->interpret(istate); + e->error("%s is not yet supported at compile time", e->toChars()); + result = EXP_CANT_INTERPRET; + return; } + } - else - e = v->type->defaultInitLiteral(loc); } - else if (!(v->isDataseg() || v->storage_class & STCmanifest) && !v->isCTFE() && !istate) - { error(loc, "variable %s cannot be read at compile time", v->toChars()); - return EXP_CANT_INTERPRET; - } - else - { e = v->hasValue() ? v->getValue() : NULL; - if (!e && !v->isCTFE() && v->isDataseg()) - { error(loc, "static variable %s cannot be read at compile time", v->toChars()); - e = EXP_CANT_INTERPRET; - } - else if (!e) - { assert(!(v->init && v->init->isVoidInitializer())); - // CTFE initiated from inside a function - error(loc, "variable %s cannot be read at compile time", v->toChars()); - return EXP_CANT_INTERPRET; - } - else if (exceptionOrCantInterpret(e)) - return e; - else if (goal == ctfeNeedLvalue && v->isRef() && e->op == TOKindex) - { // If it is a foreach ref, resolve the index into a constant - IndexExp *ie = (IndexExp *)e; - Expression *w = ie->e2->interpret(istate); - if (w != ie->e2) + else if (!wantRef && e1->op != TOKslice) + { + /* Look for special case of struct being initialized with 0. + */ + if (e->type->toBasetype()->ty == Tstruct && newval->op == TOKint64) + { + newval = e->type->defaultInitLiteral(e->loc); + if (newval->op != TOKstructliteral) { - e = new IndexExp(ie->loc, ie->e1, w); - e->type = ie->type; + e->error("nested structs with constructors are not yet supported in CTFE (Bug 6419)"); + result = EXP_CANT_INTERPRET; + return; } - return e; } - else if ((goal == ctfeNeedLvalue) - || e->op == TOKstring || e->op == TOKstructliteral || e->op == TOKarrayliteral - || e->op == TOKassocarrayliteral || e->op == TOKslice - || e->type->toBasetype()->ty == Tpointer) - return e; // it's already an Lvalue - else if (e->op == TOKvoid) + newval = ctfeCast(e->loc, e->type, e->type, newval); + if (exceptionOrCantInterpret(newval)) { - VoidInitExp *ve = (VoidInitExp *)e; - error(loc, "cannot read uninitialized variable %s in ctfe", v->toPrettyChars()); - errorSupplemental(ve->var->loc, "%s was uninitialized and used before set", ve->var->toChars()); - e = EXP_CANT_INTERPRET; + result = newval; + return; } - else - e = e->interpret(istate, goal); - } - if (!e) - e = EXP_CANT_INTERPRET; - } - else if (s) - { // Struct static initializers, for example - e = s->dsym->type->defaultInitLiteral(loc); - if (e->op == TOKerror) - error(loc, "CTFE failed because of previous errors in %s.init", s->toChars()); - e = e->semantic(NULL); - if (e->op == TOKerror) - e = EXP_CANT_INTERPRET; - else // Convert NULL to VoidExp - e = e->interpret(istate, goal); - } - else - error(loc, "cannot interpret declaration %s at compile time", d->toChars()); - return e; -} - -Expression *VarExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s VarExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - if (goal == ctfeNeedLvalueRef) - { - VarDeclaration *v = var->isVarDeclaration(); - if (v && !v->isDataseg() && !v->isCTFE() && !istate) - { error("variable %s cannot be referenced at compile time", v->toChars()); - return EXP_CANT_INTERPRET; + result = newval; } - else if (v && !v->hasValue()) + if (exceptionOrCantInterpret(newval)) { - if (!v->isCTFE() && v->isDataseg()) - error("static variable %s cannot be referenced at compile time", v->toChars()); - else // CTFE initiated from inside a function - error("variable %s cannot be read at compile time", v->toChars()); - return EXP_CANT_INTERPRET; + result = newval; + return; } - else if (v && v->hasValue() && v->getValue()->op == TOKvar) - { // A ref of a reference, is the original reference - return v->getValue(); + + // ------------------------------------------------- + // Make sure destination can be modified + // ------------------------------------------------- + // Make sure we're not trying to modify a global or static variable + // We do this by locating the ultimate parent variable which gets modified. + VarDeclaration * ultimateVar = findParentVar(e1); + if (ultimateVar && ultimateVar->isDataseg() && !ultimateVar->isCTFE()) + { + // Can't modify global or static data + e->error("%s cannot be modified at compile time", ultimateVar->toChars()); + result = EXP_CANT_INTERPRET; + return; } - return this; - } - Expression *e = getVarExp(loc, istate, var, goal); - // A VarExp may include an implicit cast. It must be done explicitly. - if (e != EXP_CANT_INTERPRET && e->op != TOKthrownexception) - e = paintTypeOntoLiteral(type, e); - return e; -} -Expression *DeclarationExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s DeclarationExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - Expression *e; - VarDeclaration *v = declaration->isVarDeclaration(); - if (v) - { - if (v->toAlias()->isTupleDeclaration()) - { // Reserve stack space for all tuple members - TupleDeclaration *td =v->toAlias()->isTupleDeclaration(); - if (!td->objects) - return NULL; - for(size_t i= 0; i < td->objects->dim; ++i) + e1 = resolveReferences(e1); + + // Unless we have a simple var assignment, we're + // only modifying part of the variable. So we need to make sure + // that the parent variable exists. + if (e1->op != TOKvar && ultimateVar && !getValue(ultimateVar)) + setValue(ultimateVar, copyLiteral(ultimateVar->type->defaultInitLiteral(e->loc))); + + // --------------------------------------- + // Deal with reference assignment + // (We already have 'newval' for arraylength operations) + // --------------------------------------- + if (wantRef && !fp && e->e1->op != TOKarraylength) + { + CtfeGoal e2goal; + if (wantLvalueRef) + e2goal = ctfeNeedLvalueRef; // for internal ref variable initializing + else if (e->e2->type->ty == Tarray || e->e2->type->ty == Tclass) + e2goal = ctfeNeedRvalue; // for assignment of reference types + else + e2goal = ctfeNeedLvalue; // other types + newval = e->e2->interpret(istate, e2goal); + if (exceptionOrCantInterpret(newval)) { - RootObject * o = (*td->objects)[i]; - Expression *ex = isExpression(o); - DsymbolExp *s = (ex && ex->op == TOKdsymbol) ? (DsymbolExp *)ex : NULL; - VarDeclaration *v2 = s ? s->s->isVarDeclaration() : NULL; - assert(v2); - if (!v2->isDataseg() || v2->isCTFE()) - ctfeStack.push(v2); + result = newval; + return; } - return NULL; - } - if (!(v->isDataseg() || v->storage_class & STCmanifest) || v->isCTFE()) - ctfeStack.push(v); - Dsymbol *s = v->toAlias(); -#if DMDV2 - bool constinit = (v->isConst() || v->isImmutable()); -#else - bool constinit = v->isConst(); -#endif - if (s == v && !v->isStatic() && v->init) + + // If it is an assignment from a array function parameter passed by + // reference, resolve the reference. (This should NOT happen for + // non-reference types). + if (newval->op == TOKvar && (newval->type->ty == Tarray || + newval->type->ty == Tclass)) + { + newval = newval->interpret(istate); + } + + if (newval->op == TOKassocarrayliteral || newval->op == TOKstring || + newval->op == TOKarrayliteral) + { + if (needToCopyLiteral(newval)) + newval = copyLiteral(newval); + } + + // Get the value to return. Note that 'newval' is an Lvalue, + // so if we need an Rvalue, we have to interpret again. + if (goal == ctfeNeedRvalue) + result = newval->interpret(istate); + else + result = newval; + } + + // --------------------------------------- + // Deal with AA index assignment + // --------------------------------------- + /* This needs special treatment if the AA doesn't exist yet. + * There are two special cases: + * (1) If the AA is itself an index of another AA, we may need to create + * multiple nested AA literals before we can insert the new value. + * (2) If the ultimate AA is null, no insertion happens at all. Instead, we + * create nested AA literals, and change it into a assignment. + */ + if (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) { - ExpInitializer *ie = v->init->isExpInitializer(); - if (ie) - e = ie->exp->interpret(istate, goal); - else if (v->init->isVoidInitializer()) + IndexExp *ie = (IndexExp *)e1; + int depth = 0; // how many nested AA indices are there? + while (ie->e1->op == TOKindex && ((IndexExp *)ie->e1)->e1->type->toBasetype()->ty == Taarray) { - e = v->type->voidInitLiteral(v); - // There is no AssignExp for void initializers, - // so set it here. - v->setValue(e); + ie = (IndexExp *)ie->e1; + ++depth; + } + Expression *aggregate = resolveReferences(ie->e1); + Expression *oldagg = aggregate; + // Get the AA to be modified. (We do an LvalueRef interpret, unless it + // is a simple ref parameter -- in which case, we just want the value) + aggregate = aggregate->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(aggregate)) + { + result = aggregate; + return; + } + if (aggregate->op == TOKassocarrayliteral) + { // Normal case, ultimate parent AA already exists + // We need to walk from the deepest index up, checking that an AA literal + // already exists on each level. + Expression *index = ((IndexExp *)e1)->e2->interpret(istate); + if (exceptionOrCantInterpret(index)) + { + result = index; + return; + } + if (index->op == TOKslice) // only happens with AA assignment + index = resolveSlice(index); + AssocArrayLiteralExp *existingAA = (AssocArrayLiteralExp *)aggregate; + while (depth > 0) + { + // Walk the syntax tree to find the indexExp at this depth + IndexExp *xe = (IndexExp *)e1; + for (int d= 0; d < depth; ++d) + xe = (IndexExp *)xe->e1; + + Expression *indx = xe->e2->interpret(istate); + if (exceptionOrCantInterpret(indx)) + { + result = indx; + return; + } + if (indx->op == TOKslice) // only happens with AA assignment + indx = resolveSlice(indx); + + // Look up this index in it up in the existing AA, to get the next level of AA. + AssocArrayLiteralExp *newAA = (AssocArrayLiteralExp *)findKeyInAA(e->loc, existingAA, indx); + if (exceptionOrCantInterpret(newAA)) + { + result = newAA; + return; + } + if (!newAA) + { + // Doesn't exist yet, create an empty AA... + Expressions *valuesx = new Expressions(); + Expressions *keysx = new Expressions(); + newAA = new AssocArrayLiteralExp(e->loc, keysx, valuesx); + newAA->type = xe->type; + newAA->ownedByCtfe = true; + //... and insert it into the existing AA. + existingAA->keys->push(indx); + existingAA->values->push(newAA); + } + existingAA = newAA; + --depth; + } + if (assignAssocArrayElement(e->loc, existingAA, index, newval) == EXP_CANT_INTERPRET) + { + result = EXP_CANT_INTERPRET; + return; + } + return; } else { - error("Declaration %s is not yet implemented in CTFE", toChars()); - e = EXP_CANT_INTERPRET; + /* The AA is currently null. 'aggregate' is actually a reference to + * whatever contains it. It could be anything: var, dotvarexp, ... + * We rewrite the assignment from: aggregate[i][j] = newval; + * into: aggregate = [i:[j: newval]]; + */ + while (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) + { + Expression *index = ((IndexExp *)e1)->e2->interpret(istate); + if (exceptionOrCantInterpret(index)) + { + result = index; + return; + } + if (index->op == TOKslice) // only happens with AA assignment + index = resolveSlice(index); + Expressions *valuesx = new Expressions(); + Expressions *keysx = new Expressions(); + valuesx->push(newval); + keysx->push(index); + AssocArrayLiteralExp *newaae = new AssocArrayLiteralExp(e->loc, keysx, valuesx); + newaae->ownedByCtfe = true; + newaae->type = ((IndexExp *)e1)->e1->type; + newval = newaae; + e1 = ((IndexExp *)e1)->e1; + } + // We must return to the original aggregate, in case it was a reference + wantRef = true; + e1 = oldagg; + // fall through -- let the normal assignment logic take care of it } } - else if (s == v && !v->init && v->type->size()==0) - { // Zero-length arrays don't need an initializer - e = v->type->defaultInitLiteral(loc); - } - else if (s == v && constinit && v->init) - { e = v->init->toExpression(); - if (!e) - e = EXP_CANT_INTERPRET; - else if (!e->type) - e->type = v->type; + + // --------------------------------------- + // Deal with dotvar expressions + // --------------------------------------- + // Because structs are not reference types, dotvar expressions can be + // collapsed into a single assignment. + if (!wantRef && e1->op == TOKdotvar) + { + // Strip of all of the leading dotvars, unless it is a CTFE dotvar + // pointer or reference + // (in which case, we already have the lvalue). + DotVarExp *dve = (DotVarExp *)e1; + bool isCtfePointer = (dve->e1->op == TOKstructliteral) + && ((StructLiteralExp *)(dve->e1))->ownedByCtfe; + if (!isCtfePointer) + { + e1 = e1->interpret(istate, isPointer(e->type) ? ctfeNeedLvalueRef : ctfeNeedLvalue); + if (exceptionOrCantInterpret(e1)) + { + result = e1; + return; + } + } } - else if (s->isTupleDeclaration() && !v->init) - e = NULL; - else if (v->isStatic()) - e = NULL; // Just ignore static variables which aren't read or written yet + #if LOGASSIGN + if (wantRef) + printf("REF ASSIGN: %s=%s\n", e1->toChars(), newval->toChars()); else + printf("ASSIGN: %s=%s\n", e1->toChars(), newval->toChars()); + showCtfeExpr(newval); + #endif + + /* Assignment to variable of the form: + * v = newval + */ + if (e1->op == TOKvar) { - error("Variable %s cannot be modified at compile time", v->toChars()); - e = EXP_CANT_INTERPRET; - } - } - else if (declaration->isAttribDeclaration() || - declaration->isTemplateMixin() || - declaration->isTupleDeclaration()) - { // Check for static struct declarations, which aren't executable - AttribDeclaration *ad = declaration->isAttribDeclaration(); - if (ad && ad->decl && ad->decl->dim == 1) - { - Dsymbol *s = (*ad->decl)[0]; - if (s->isAggregateDeclaration() || - s->isTemplateDeclaration()) + VarExp *ve = (VarExp *)e1; + VarDeclaration *v = ve->var->isVarDeclaration(); + if (wantRef) + { + setValueNull(v); + setValue(v, newval); + } + else if (e1->type->toBasetype()->ty == Tstruct) + { + // In-place modification + if (newval->op != TOKstructliteral) + { + e->error("CTFE internal error assigning struct"); + result = EXP_CANT_INTERPRET; + return; + } + newval = copyLiteral(newval); + if (getValue(v)) + assignInPlace(getValue(v), newval); + else + setValue(v, newval); + } + else { - return NULL; // static (template) struct declaration. Nothing to do. + TY tyE1 = e1->type->toBasetype()->ty; + if (tyE1 == Tsarray && newval->op == TOKslice) + { + // Newly set value is non-ref static array, + // so making new ArrayLiteralExp is legitimate. + newval = resolveSlice(newval); + ((ArrayLiteralExp *)newval)->ownedByCtfe = true; + } + if (tyE1 == Tarray || tyE1 == Taarray) + { + // arr op= arr + setValue(v, newval); + } + else + { + setValue(v, newval); + if (e->op != TOKblit && tyE1 == Tsarray && e->e2->isLvalue()) + { + assert(newval->op == TOKarrayliteral); + ArrayLiteralExp *ale = (ArrayLiteralExp *)newval; + if (Expression *x = evaluatePostblits(istate, ale, 0, ale->elements->dim)) + { + result = x; + return; + } + } + } } } - - // These can be made to work, too lazy now - error("Declaration %s is not yet implemented in CTFE", toChars()); - e = EXP_CANT_INTERPRET; - } - else - { // Others should not contain executable code, so are trivial to evaluate - e = NULL; - } -#if LOG - printf("-DeclarationExp::interpret(%s): %p\n", toChars(), e); -#endif - return e; -} - -Expression *TupleExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s TupleExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - Expressions *expsx = NULL; - - if (e0) - { - if (e0->interpret(istate) == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; - } - - for (size_t i = 0; i < exps->dim; i++) - { Expression *e = (*exps)[i]; - Expression *ex; - - ex = e->interpret(istate); - if (exceptionOrCantInterpret(ex)) - return ex; - - // A tuple of assignments can contain void (Bug 5676). - if (goal == ctfeNeedNothing) - continue; - if (ex == EXP_VOID_INTERPRET) + else if (e1->op == TOKstructliteral && newval->op == TOKstructliteral) { - error("ICE: void element %s in tuple", e->toChars()); - assert(0); + /* Assignment to complete struct of the form: + * e1 = newval + * (e1 was a ref parameter, or was created via TOKstar dereferencing). + */ + assignInPlace(e1, newval); + return; } - - /* If any changes, do Copy On Write - */ - if (ex != e) + else if (e1->op == TOKdotvar) { - if (!expsx) - { expsx = new Expressions(); - ++CtfeStatus::numArrayAllocs; - expsx->setDim(exps->dim); - for (size_t j = 0; j < i; j++) + /* Assignment to member variable of the form: + * e.v = newval + */ + Expression *exx = ((DotVarExp *)e1)->e1; + if (wantRef && exx->op != TOKstructliteral) + { + exx = exx->interpret(istate); + if (exceptionOrCantInterpret(exx)) + { + result = exx; + return; + } + } + if (exx->op != TOKstructliteral && exx->op != TOKclassreference) + { + e->error("CTFE internal error: Dotvar assignment"); + result = EXP_CANT_INTERPRET; + return; + } + VarDeclaration *member = ((DotVarExp *)e1)->var->isVarDeclaration(); + if (!member) + { + e->error("CTFE internal error: Dotvar assignment"); + result = EXP_CANT_INTERPRET; + return; + } + StructLiteralExp *se = exx->op == TOKstructliteral + ? (StructLiteralExp *)exx + : ((ClassReferenceExp *)exx)->value; + int fieldi = exx->op == TOKstructliteral + ? findFieldIndexByName(se->sd, member) + : ((ClassReferenceExp *)exx)->findFieldIndexByName(member); + if (fieldi == -1) + { + e->error("CTFE internal error: cannot find field %s in %s", member->toChars(), exx->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + assert(fieldi >= 0 && fieldi < se->elements->dim); + // If it's a union, set all other members of this union to void + if (exx->op == TOKstructliteral) + { + assert(se->sd); + int unionStart = se->sd->firstFieldInUnion(fieldi); + int unionSize = se->sd->numFieldsInUnion(fieldi); + for(int i = unionStart; i < unionStart + unionSize; ++i) { - (*expsx)[j] = (*exps)[j]; + if (i == fieldi) + continue; + Expression **exp = &(*se->elements)[i]; + if ((*exp)->op != TOKvoid) + *exp = voidInitLiteral((*exp)->type, member); } } - (*expsx)[i] = ex; - } - } - if (expsx) - { TupleExp *te = new TupleExp(loc, expsx); - expandTuples(te->exps); - te->type = new TypeTuple(te->exps); - return te; - } - return this; -} - -Expression *ArrayLiteralExp::interpret(InterState *istate, CtfeGoal goal) -{ Expressions *expsx = NULL; - -#if LOG - printf("%s ArrayLiteralExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - if (ownedByCtfe) // We've already interpreted all the elements - return this; - if (elements) - { - for (size_t i = 0; i < elements->dim; i++) - { Expression *e = (*elements)[i]; - Expression *ex; - - if (e->op == TOKindex) // segfault bug 6250 - assert( ((IndexExp*)e)->e1 != this); - ex = e->interpret(istate); - if (exceptionOrCantInterpret(ex)) - return ex; - /* If any changes, do Copy On Write - */ - if (ex != e) + if (newval->op == TOKstructliteral) + assignInPlace((*se->elements)[fieldi], newval); + else + (*se->elements)[fieldi] = newval; + return; + } + else if (e1->op == TOKindex) + { + if (!interpretAssignToIndex(e->loc, (IndexExp *)e1, newval, + wantRef, e)) { - if (!expsx) - { expsx = new Expressions(); - ++CtfeStatus::numArrayAllocs; - expsx->setDim(elements->dim); - for (size_t j = 0; j < elements->dim; j++) - { - (*expsx)[j] = (*elements)[j]; - } - } - (*expsx)[i] = ex; + result = EXP_CANT_INTERPRET; } + return; } - } - if (elements && expsx) - { - expandTuples(expsx); - if (expsx->dim != elements->dim) + else if (e1->op == TOKslice) { - error("Internal Compiler Error: Invalid array literal"); - return EXP_CANT_INTERPRET; + // Note that slice assignments don't support things like ++, so + // we don't need to remember 'returnValue'. + result = interpretAssignToSlice(e->loc, (SliceExp *)e1, + newval, wantRef, isBlockAssignment, e); + return; } - ArrayLiteralExp *ae = new ArrayLiteralExp(loc, expsx); - ae->type = type; - return copyLiteral(ae); - } -#if DMDV2 - if (((TypeNext *)type)->next->mod & (MODconst | MODimmutable)) - { // If it's immutable, we don't need to dup it - return this; - } -#endif - return copyLiteral(this); -} - -Expression *AssocArrayLiteralExp::interpret(InterState *istate, CtfeGoal goal) -{ Expressions *keysx = keys; - Expressions *valuesx = values; - -#if LOG - printf("%s AssocArrayLiteralExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - if (ownedByCtfe) // We've already interpreted all the elements - return copyLiteral(this); - for (size_t i = 0; i < keys->dim; i++) - { - Expression *ekey = (*keys)[i]; - Expression *evalue = (*values)[i]; - Expression *ex; - - ex = ekey->interpret(istate); - if (exceptionOrCantInterpret(ex)) - return ex; - - /* If any changes, do Copy On Write - */ - if (ex != ekey) + else if (e1->op == TOKarrayliteral && e1->type->toBasetype()->ty == Tsarray) { - if (keysx == keys) - keysx = (Expressions *)keys->copy(); - (*keysx)[i] = ex; + // Bugzilla 12212: Support direct assignment of static arrays. + // Rewrite as: (e1[] = newval) + SliceExp *se = new SliceExp(e1->loc, e1, NULL, NULL); + result = interpretAssignToSlice(e->loc, se, + newval, wantRef, isBlockAssignment, e); + return; } - - ex = evalue->interpret(istate); - if (exceptionOrCantInterpret(ex)) - return ex; - - /* If any changes, do Copy On Write - */ - if (ex != evalue) + else { - if (valuesx == values) - valuesx = (Expressions *)values->copy(); - (*valuesx)[i] = ex; + e->error("%s cannot be evaluated at compile time", e->toChars()); } } - if (keysx != keys) - expandTuples(keysx); - if (valuesx != values) - expandTuples(valuesx); - if (keysx->dim != valuesx->dim) - { - error("Internal Compiler Error: invalid AA"); - return EXP_CANT_INTERPRET; - } - /* Remove duplicate keys + /************* + * Deal with assignments of the form + * aggregate[ie] = newval + * where aggregate and newval have already been interpreted + * + * Return true if OK, false if error occured */ - for (size_t i = 1; i < keysx->dim; i++) - { - Expression *ekey = (*keysx)[i - 1]; - for (size_t j = i; j < keysx->dim; j++) - { - Expression *ekey2 = (*keysx)[j]; - int eq = ctfeEqual(loc, TOKequal, ekey, ekey2); - if (eq) // if a match - { - // Remove ekey - if (keysx == keys) - keysx = (Expressions *)keys->copy(); - if (valuesx == values) - valuesx = (Expressions *)values->copy(); - keysx->remove(i - 1); - valuesx->remove(i - 1); - i -= 1; // redo the i'th iteration - break; - } - } - } - - if (keysx != keys || valuesx != values) + bool interpretAssignToIndex(Loc loc, + IndexExp *ie, Expression *newval, bool wantRef, + BinExp *originalExp) { - AssocArrayLiteralExp *ae; - ae = new AssocArrayLiteralExp(loc, keysx, valuesx); - ae->type = type; - ae->ownedByCtfe = true; - return ae; - } - return this; -} - -Expression *StructLiteralExp::interpret(InterState *istate, CtfeGoal goal) -{ Expressions *expsx = NULL; - -#if LOG - printf("%s StructLiteralExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - if (ownedByCtfe) - return copyLiteral(this); - - size_t elemdim = elements ? elements->dim : 0; + /* Assignment to array element of the form: + * aggregate[i] = newval + * aggregate is not AA (AAs were dealt with already). + */ + assert(ie->e1->type->toBasetype()->ty != Taarray); + uinteger_t destarraylen = 0; - for (size_t i = 0; i < sd->fields.dim; i++) - { Expression *e = NULL; - Expression *ex = NULL; - if (i >= elemdim) + // Set the $ variable, and find the array literal to modify + if (ie->e1->type->toBasetype()->ty != Tpointer) { - /* If a nested struct has no initialized hidden pointer, - * set it to null to match the runtime behaviour. - */ - if (i == sd->fields.dim - 1 && sd->isNested()) - { // Context field has not been filled - ex = new NullExp(loc); - ex->type = sd->fields[i]->type; + Expression *oldval = ie->e1->interpret(istate); + if (oldval->op == TOKnull) + { + originalExp->error("cannot index null array %s", ie->e1->toChars()); + return false; } - } - else - { - e = (*elements)[i]; - if (!e) + if (oldval->op != TOKarrayliteral && oldval->op != TOKstring + && oldval->op != TOKslice) { - /* Ideally, we'd convert NULL members into void expressions. - * The problem is that the VoidExp will be removed when we - * leave CTFE, causing another memory allocation if we use this - * same struct literal again. - * - * ex = sd->fields[i]->type->voidInitLiteral(sd->fields[i]); - */ - ex = NULL; + originalExp->error("cannot determine length of %s at compile time", + ie->e1->toChars()); + return false; } - else + destarraylen = resolveArrayLength(oldval); + if (ie->lengthVar) { - ex = e->interpret(istate); - if (exceptionOrCantInterpret(ex)) - return ex; + IntegerExp *dollarExp = new IntegerExp(loc, destarraylen, Type::tsize_t); + ctfeStack.push(ie->lengthVar); + setValue(ie->lengthVar, dollarExp); } } + Expression *index = ie->e2->interpret(istate); + if (ie->lengthVar) + ctfeStack.pop(ie->lengthVar); // $ is defined only inside [] + if (exceptionOrCantInterpret(index)) + return false; - /* If any changes, do Copy On Write - */ - if (ex != e) + assert (index->op != TOKslice); // only happens with AA assignment + + ArrayLiteralExp *existingAE = NULL; + StringExp *existingSE = NULL; + + Expression *aggregate = resolveReferences(ie->e1); + + // Set the index to modify, and check that it is in range + dinteger_t indexToModify = index->toInteger(); + if (ie->e1->type->toBasetype()->ty == Tpointer) { - if (!expsx) - { expsx = new Expressions(); - ++CtfeStatus::numArrayAllocs; - expsx->setDim(sd->fields.dim); - for (size_t j = 0; j < elements->dim; j++) + dinteger_t ofs; + aggregate = aggregate->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(aggregate)) + return false; + if (aggregate->op == TOKnull) + { + originalExp->error("cannot index through null pointer %s", ie->e1->toChars()); + return false; + } + if (aggregate->op == TOKint64) + { + originalExp->error("cannot index through invalid pointer %s of value %s", + ie->e1->toChars(), aggregate->toChars()); + return false; + } + aggregate = getAggregateFromPointer(aggregate, &ofs); + indexToModify += ofs; + if (aggregate->op != TOKslice && aggregate->op != TOKstring && + aggregate->op != TOKarrayliteral && aggregate->op != TOKassocarrayliteral) + { + if (aggregate->op == TOKsymoff) + { + originalExp->error("mutable variable %s cannot be modified at compile time, even through a pointer", ((SymOffExp *)aggregate)->var->toChars()); + return false; + } + if (indexToModify != 0) { - (*expsx)[j] = (*elements)[j]; + originalExp->error("pointer index [%lld] lies outside memory block [0..1]", indexToModify); + return false; } + // It is equivalent to *aggregate = newval. + // Aggregate could be varexp, a dotvar, ... + // TODO: we could support this + originalExp->error("indexed assignment of non-array pointers is not yet supported at compile time; use *%s = %s instead", + ie->e1->toChars(), originalExp->e2->toChars()); + return false; } - (*expsx)[i] = ex; + destarraylen = resolveArrayLength(aggregate); } - } - - if (elements && expsx) - { - expandTuples(expsx); - if (expsx->dim != sd->fields.dim) + if (indexToModify >= destarraylen) { - error("Internal Compiler Error: invalid struct literal"); - return EXP_CANT_INTERPRET; + originalExp->error("array index %lld is out of bounds [0..%lld]", indexToModify, + destarraylen); + return false; } - StructLiteralExp *se = new StructLiteralExp(loc, sd, expsx); - se->type = type; - se->ownedByCtfe = true; - return se; - } - return copyLiteral(this); -} - -// Create an array literal of type 'newtype' with dimensions given by -// 'arguments'[argnum..$] -Expression *recursivelyCreateArrayLiteral(Loc loc, Type *newtype, InterState *istate, - Expressions *arguments, int argnum) -{ - Expression *lenExpr = (((*arguments)[argnum]))->interpret(istate); - if (exceptionOrCantInterpret(lenExpr)) - return lenExpr; - size_t len = (size_t)(lenExpr->toInteger()); - Type *elemType = ((TypeArray *)newtype)->next; - if (elemType->ty == Tarray && argnum < arguments->dim - 1) - { - Expression *elem = recursivelyCreateArrayLiteral(loc, elemType, istate, - arguments, argnum + 1); - if (exceptionOrCantInterpret(elem)) - return elem; - - Expressions *elements = new Expressions(); - elements->setDim(len); - for (size_t i = 0; i < len; i++) - (*elements)[i] = copyLiteral(elem); - ArrayLiteralExp *ae = new ArrayLiteralExp(loc, elements); - ae->type = newtype; - ae->ownedByCtfe = true; - return ae; - } - assert(argnum == arguments->dim - 1); - if (elemType->ty == Tchar || elemType->ty == Twchar - || elemType->ty == Tdchar) - return createBlockDuplicatedStringLiteral(loc, newtype, - (unsigned)(elemType->defaultInitLiteral(loc)->toInteger()), - len, (unsigned char)elemType->size()); - return createBlockDuplicatedArrayLiteral(loc, newtype, - elemType->defaultInitLiteral(loc), - len); -} -Expression *NewExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s NewExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - if (newtype->ty == Tarray && arguments) - return recursivelyCreateArrayLiteral(loc, newtype, istate, arguments, 0); - - if (newtype->toBasetype()->ty == Tstruct) - { - Expression *se = newtype->defaultInitLiteral(loc); -#if DMDV2 - if (member) + /* The only possible indexable LValue aggregates are array literals, and + * slices of array literals. + */ + if (aggregate->op == TOKindex || aggregate->op == TOKdotvar || + aggregate->op == TOKslice || aggregate->op == TOKcall || + aggregate->op == TOKstar || aggregate->op == TOKcast) { - int olderrors = global.errors; - member->interpret(istate, arguments, se); - if (olderrors != global.errors) + aggregate = aggregate->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(aggregate)) + return false; + // The array could be an index of an AA. Resolve it if so. + if (aggregate->op == TOKindex && + ((IndexExp *)aggregate)->e1->op == TOKassocarrayliteral) { - error("cannot evaluate %s at compile time", toChars()); - return EXP_CANT_INTERPRET; + IndexExp *ix = (IndexExp *)aggregate; + aggregate = findKeyInAA(loc, (AssocArrayLiteralExp *)ix->e1, ix->e2); + if (!aggregate) + { + originalExp->error("key %s not found in associative array %s", + ix->e2->toChars(), ix->e1->toChars()); + return false; + } + if (exceptionOrCantInterpret(aggregate)) + return false; } } -#else // The above code would fail on D1 because it doesn't use STRUCTTHISREF, - // but that's OK because D1 doesn't have struct constructors anyway. - assert(!member); -#endif - Expression *e = new AddrExp(loc, copyLiteral(se)); - e->type = type; - return e; - } - if (newtype->toBasetype()->ty == Tclass) - { - ClassDeclaration *cd = ((TypeClass *)newtype->toBasetype())->sym; - size_t totalFieldCount = 0; - for (ClassDeclaration *c = cd; c; c = c->baseClass) - totalFieldCount += c->fields.dim; - Expressions *elems = new Expressions; - elems->setDim(totalFieldCount); - size_t fieldsSoFar = totalFieldCount; - for (ClassDeclaration *c = cd; c; c = c->baseClass) + if (aggregate->op == TOKvar) { - fieldsSoFar -= c->fields.dim; - for (size_t i = 0; i < c->fields.dim; i++) + VarExp *ve = (VarExp *)aggregate; + VarDeclaration *v = ve->var->isVarDeclaration(); + aggregate = getValue(v); + if (aggregate->op == TOKnull) { - VarDeclaration *v = c->fields[i]; - Expression *m; - if (v->inuse) - { - error("circular reference to '%s'", v->toPrettyChars()); - return EXP_CANT_INTERPRET; - } - if (v->init) - { - if (v->init->isVoidInitializer()) - m = v->type->voidInitLiteral(v); - else - m = v->getConstInitializer(true); - } - else - m = v->type->defaultInitLiteral(loc); - if (exceptionOrCantInterpret(m)) - return m; - (*elems)[fieldsSoFar+i] = copyLiteral(m); - } - } - // Hack: we store a ClassDeclaration instead of a StructDeclaration. - // We probably won't get away with this. - StructLiteralExp *se = new StructLiteralExp(loc, (StructDeclaration *)cd, elems, newtype); - se->ownedByCtfe = true; - Expression *e = new ClassReferenceExp(loc, se, type); - if (member) - { // Call constructor - if (!member->fbody) - { - Expression *ctorfail = evaluateIfBuiltin(istate, loc, member, arguments, e); - if (ctorfail && exceptionOrCantInterpret(ctorfail)) - return ctorfail; - if (ctorfail) - return e; - member->error("%s cannot be constructed at compile time, because the constructor has no available source code", newtype->toChars()); - return EXP_CANT_INTERPRET; + // This would be a runtime segfault + originalExp->error("cannot index null array %s", v->toChars()); + return false; } - Expression * ctorfail = member->interpret(istate, arguments, e); - if (exceptionOrCantInterpret(ctorfail)) - return ctorfail; } - return e; - } - error("Cannot interpret %s at compile time", toChars()); - return EXP_CANT_INTERPRET; -} - -Expression *UnaExp::interpret(InterState *istate, CtfeGoal goal) -{ Expression *e; - Expression *e1; - -#if LOG - printf("%s UnaExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - if (op == TOKdottype) - { - error("Internal Compiler Error: CTFE DotType: %s", toChars()); - return EXP_CANT_INTERPRET; - } - e1 = this->e1->interpret(istate); - if (exceptionOrCantInterpret(e1)) - return e1; - switch(op) - { - case TOKneg: e = Neg(type, e1); break; - case TOKtilde: e = Com(type, e1); break; - case TOKnot: e = Not(type, e1); break; - case TOKtobool: e = Bool(type, e1); break; - case TOKvector: e = this; break; // do nothing - default: assert(0); - } - return e; -} - - -Expression *BinExp::interpretCommon(InterState *istate, CtfeGoal goal, fp_t fp) -{ Expression *e; - Expression *e1; - Expression *e2; - -#if LOG - printf("%s BinExp::interpretCommon() %s\n", loc.toChars(), toChars()); -#endif - if (this->e1->type->ty == Tpointer && this->e2->type->ty == Tpointer && op == TOKmin) - { - e1 = this->e1->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(e1)) - return e1; - e2 = this->e2->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(e2)) - return e2; - return pointerDifference(loc, type, e1, e2); - } - if (this->e1->type->ty == Tpointer && this->e2->type->isintegral()) - { - e1 = this->e1->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(e1)) - return e1; - e2 = this->e2->interpret(istate); - if (exceptionOrCantInterpret(e2)) - return e2; - return pointerArithmetic(loc, op, type, e1, e2); - } - if (this->e2->type->ty == Tpointer && this->e1->type->isintegral() && op==TOKadd) - { - e1 = this->e1->interpret(istate); - if (exceptionOrCantInterpret(e1)) - return e1; - e2 = this->e2->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(e2)) - return e1; - return pointerArithmetic(loc, op, type, e2, e1); - } - if (this->e1->type->ty == Tpointer || this->e2->type->ty == Tpointer) - { - error("pointer expression %s cannot be interpreted at compile time", toChars()); - return EXP_CANT_INTERPRET; - } - e1 = this->e1->interpret(istate); - if (exceptionOrCantInterpret(e1)) - return e1; - if (e1->isConst() != 1) - { - error("Internal Compiler Error: non-constant value %s", this->e1->toChars()); - return EXP_CANT_INTERPRET; - } - - e2 = this->e2->interpret(istate); - if (exceptionOrCantInterpret(e2)) - return e2; - if (e2->isConst() != 1) - { - error("Internal Compiler Error: non-constant value %s", this->e2->toChars()); - return EXP_CANT_INTERPRET; - } - - if (op == TOKshr || op == TOKshl || op == TOKushr) - { - sinteger_t i2 = e2->toInteger(); - d_uns64 sz = e1->type->size() * 8; - if (i2 < 0 || i2 >= sz) - { error("shift by %lld is outside the range 0..%llu", i2, (ulonglong)sz - 1); - return EXP_CANT_INTERPRET; + if (aggregate->op == TOKslice) + { + SliceExp *sexp = (SliceExp *)aggregate; + aggregate = sexp->e1; + Expression *lwr = sexp->lwr->interpret(istate); + indexToModify += lwr->toInteger(); } - } - e = (*fp)(type, e1, e2); - if (e == EXP_CANT_INTERPRET) - error("%s cannot be interpreted at compile time", toChars()); - return e; -} - -Expression *BinExp::interpretCompareCommon(InterState *istate, CtfeGoal goal, fp2_t fp) -{ - Expression *e1; - Expression *e2; - -#if LOG - printf("%s BinExp::interpretCompareCommon() %s\n", loc.toChars(), toChars()); -#endif - if (this->e1->type->ty == Tpointer && this->e2->type->ty == Tpointer) - { - e1 = this->e1->interpret(istate); - if (exceptionOrCantInterpret(e1)) - return e1; - e2 = this->e2->interpret(istate); - if (exceptionOrCantInterpret(e2)) - return e2; - dinteger_t ofs1, ofs2; - Expression *agg1 = getAggregateFromPointer(e1, &ofs1); - Expression *agg2 = getAggregateFromPointer(e2, &ofs2); - int cmp = comparePointers(loc, op, type, agg1, ofs1, agg2, ofs2); - if (cmp == -1) - { - char dir = (op == TOKgt || op == TOKge) ? '<' : '>'; - error("The ordering of pointers to unrelated memory blocks is indeterminate in CTFE." - " To check if they point to the same memory block, use both > and < inside && or ||, " - "eg (%s && %s %c= %s + 1)", - toChars(), this->e1->toChars(), dir, this->e2->toChars()); - return EXP_CANT_INTERPRET; - } - return new IntegerExp(loc, cmp, type); - } - e1 = this->e1->interpret(istate); - if (exceptionOrCantInterpret(e1)) - return e1; - if (!isCtfeComparable(e1)) - { - error("cannot compare %s at compile time", e1->toChars()); - return EXP_CANT_INTERPRET; - } - e2 = this->e2->interpret(istate); - if (exceptionOrCantInterpret(e2)) - return e2; - if (!isCtfeComparable(e2)) - { - error("cannot compare %s at compile time", e2->toChars()); - return EXP_CANT_INTERPRET; - } - int cmp = (*fp)(loc, op, e1, e2); - return new IntegerExp(loc, cmp, type); -} - -Expression *BinExp::interpret(InterState *istate, CtfeGoal goal) -{ - switch(op) - { - case TOKadd: return interpretCommon(istate, goal, &Add); - case TOKmin: return interpretCommon(istate, goal, &Min); - case TOKmul: return interpretCommon(istate, goal, &Mul); - case TOKdiv: return interpretCommon(istate, goal, &Div); - case TOKmod: return interpretCommon(istate, goal, &Mod); - case TOKshl: return interpretCommon(istate, goal, &Shl); - case TOKshr: return interpretCommon(istate, goal, &Shr); - case TOKushr: return interpretCommon(istate, goal, &Ushr); - case TOKand: return interpretCommon(istate, goal, &And); - case TOKor: return interpretCommon(istate, goal, &Or); - case TOKxor: return interpretCommon(istate, goal, &Xor); -#if DMDV2 - case TOKpow: return interpretCommon(istate, goal, &Pow); -#endif - case TOKequal: - case TOKnotequal: - return interpretCompareCommon(istate, goal, &ctfeEqual); - case TOKidentity: - case TOKnotidentity: - return interpretCompareCommon(istate, goal, &ctfeIdentity); - case TOKlt: - case TOKle: - case TOKgt: - case TOKge: - case TOKleg: - case TOKlg: - case TOKunord: - case TOKue: - case TOKug: - case TOKuge: - case TOKul: - case TOKule: - return interpretCompareCommon(istate, goal, &ctfeCmp); - default: - assert(0); - return NULL; - } -} - -/* Helper functions for BinExp::interpretAssignCommon - */ - -// Returns the variable which is eventually modified, or NULL if an rvalue. -// thisval is the current value of 'this'. -VarDeclaration * findParentVar(Expression *e) -{ - for (;;) - { - e = resolveReferences(e); - if (e->op == TOKvar) - break; - if (e->op == TOKindex) - e = ((IndexExp*)e)->e1; - else if (e->op == TOKdotvar) - e = ((DotVarExp *)e)->e1; - else if (e->op == TOKdotti) - e = ((DotTemplateInstanceExp *)e)->e1; - else if (e->op == TOKslice) - e = ((SliceExp*)e)->e1; + if (aggregate->op == TOKarrayliteral) + existingAE = (ArrayLiteralExp *)aggregate; + else if (aggregate->op == TOKstring) + existingSE = (StringExp *)aggregate; else - return NULL; - } - VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); - assert(v); - return v; -} - -Expression *interpretAssignToSlice(InterState *istate, CtfeGoal goal, Loc loc, - SliceExp *sexp, Expression *newval, bool wantRef, bool isBlockAssignment, - BinExp *originalExpression); - -bool interpretAssignToIndex(InterState *istate, Loc loc, - IndexExp *ie, Expression *newval, bool wantRef, - BinExp *originalExp); - -Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_t fp, int post) -{ -#if LOG - printf("%s BinExp::interpretAssignCommon() %s\n", loc.toChars(), toChars()); -#endif - Expression *returnValue = EXP_CANT_INTERPRET; - Expression *e1 = this->e1; - if (!istate) - { - error("value of %s is not known at compile time", e1->toChars()); - return returnValue; - } - ++CtfeStatus::numAssignments; - /* Before we begin, we need to know if this is a reference assignment - * (dynamic array, AA, or class) or a value assignment. - * Determining this for slice assignments are tricky: we need to know - * if it is a block assignment (a[] = e) rather than a direct slice - * assignment (a[] = b[]). Note that initializers of multi-dimensional - * static arrays can have 2D block assignments (eg, int[7][7] x = 6;). - * So we need to recurse to determine if it is a block assignment. - */ - bool isBlockAssignment = false; - if (e1->op == TOKslice) - { - // a[] = e can have const e. So we compare the naked types. - Type *desttype = e1->type->toBasetype(); -#if DMDV2 - Type *srctype = e2->type->toBasetype()->castMod(0); -#else - Type *srctype = e2->type->toBasetype(); -#endif - while ( desttype->ty == Tsarray || desttype->ty == Tarray) { - desttype = ((TypeArray *)desttype)->next; -#if DMDV2 - desttype = desttype->toBasetype()->castMod(0); -#endif - if (srctype->equals(desttype)) - { - isBlockAssignment = true; - break; - } + originalExp->error("CTFE internal compiler error %s", aggregate->toChars()); + return false; } - } - // If it is a reference type (eg, an array), we need an lvalue. - // If it is a reference variable (such as happens in foreach), we - // need an lvalue reference. For example if x, y are int[], then - // y[0..4] = x[0..4] is an rvalue assignment (all copies in the - // slice are duplicated) - // y = x[0..4] is an lvalue assignment (if x[0] changes later, - // y[0] will also change) - // ref int [] z = x is an lvalueref assignment (if x itself changes, - // z will also change) - bool wantRef = false; - bool wantLvalueRef = false; - - if (!fp && this->e1->type->toBasetype()->equals(this->e2->type->toBasetype()) && - (e1->type->toBasetype()->ty == Tarray || isAssocArray(e1->type) - || e1->type->toBasetype()->ty == Tclass) - // e = *x is never a reference, because *x is always a value - && this->e2->op != TOKstar - ) - { -#if DMDV2 - wantRef = true; -#else - /* D1 doesn't have const in the type system. But there is still a - * vestigal const in the form of static const variables. - * Problematic code like: - * const int [] x = [1,2,3]; - * int [] y = x; - * can be dealt with by making this a non-ref assign (y = x.dup). - * Otherwise it's a big mess. - */ - VarDeclaration * targetVar = findParentVar(e2); - if (!(targetVar && targetVar->isConst())) - wantRef = true; - // slice assignment of static arrays is not reference assignment - if ((e1->op==TOKslice) && ((SliceExp *)e1)->e1->type->ty == Tsarray) - wantRef = false; -#endif - // If it is assignment from a ref parameter, it's not a ref assignment - if (this->e2->op == TOKvar) + if (!wantRef && newval->op == TOKslice) { - VarDeclaration *v = ((VarExp *)this->e2)->var->isVarDeclaration(); - if (v && (v->storage_class & (STCref | STCout))) - wantRef = false; + newval = resolveSlice(newval); + if (newval == EXP_CANT_INTERPRET) + { + originalExp->error("Compiler error: CTFE index assign %s", originalExp->toChars()); + assert(0); + } } - } - if (isBlockAssignment && (e2->type->toBasetype()->ty == Tarray || e2->type->toBasetype()->ty == Tsarray)) - { - wantRef = true; - } - // If it is a construction of a ref variable, it is a ref assignment - // (in fact, it is an lvalue reference assignment). - if (op == TOKconstruct && this->e1->op==TOKvar - && ((VarExp*)this->e1)->var->storage_class & STCref) - { - wantRef = true; - wantLvalueRef = true; - } - - if (fp) - { - while (e1->op == TOKcast) - { CastExp *ce = (CastExp *)e1; - e1 = ce->e1; + if (wantRef && newval->op == TOKindex + && ((IndexExp *)newval)->e1 == aggregate) + { // It's a circular reference, resolve it now + newval = newval->interpret(istate); } - } - if (exceptionOrCantInterpret(e1)) - return e1; - // First, deal with this = e; and call() = e; - if (e1->op == TOKthis) - { - e1 = ctfeStack.getThis(); - } - if (e1->op == TOKcall) - { - bool oldWaiting = istate->awaitingLvalueReturn; - istate->awaitingLvalueReturn = true; - e1 = e1->interpret(istate); - istate->awaitingLvalueReturn = oldWaiting; - if (exceptionOrCantInterpret(e1)) - return e1; - if (e1->op == TOKarrayliteral || e1->op == TOKstring) + if (existingAE) { - // f() = e2, when f returns an array, is always a slice assignment. - // Convert into arr[0..arr.length] = e2 - e1 = new SliceExp(loc, e1, - new IntegerExp(Loc(), 0, Type::tsize_t), - ArrayLength(Type::tsize_t, e1)); - e1->type = type; + if (newval->op == TOKstructliteral) + assignInPlace((*existingAE->elements)[(size_t)indexToModify], newval); + else + (*existingAE->elements)[(size_t)indexToModify] = newval; + return true; } - } - if (e1->op == TOKstar) - { - e1 = e1->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(e1)) - return e1; - if (!(e1->op == TOKvar || e1->op == TOKdotvar || e1->op == TOKindex - || e1->op == TOKslice || e1->op == TOKstructliteral)) + if (existingSE) + { + utf8_t *s = (utf8_t *)existingSE->string; + if (!existingSE->ownedByCtfe) + { + originalExp->error("cannot modify read-only string literal %s", ie->e1->toChars()); + return false; + } + dinteger_t value = newval->toInteger(); + switch (existingSE->sz) + { + case 1: s[(size_t)indexToModify] = (utf8_t)value; break; + case 2: ((unsigned short *)s)[(size_t)indexToModify] = (unsigned short)value; break; + case 4: ((unsigned *)s)[(size_t)indexToModify] = (unsigned)value; break; + default: + assert(0); + break; + } + return true; + } + else { - error("cannot dereference invalid pointer %s", - this->e1->toChars()); - return EXP_CANT_INTERPRET; + originalExp->error("Index assignment %s is not yet supported in CTFE ", originalExp->toChars()); + return false; } + return true; } - if (!(e1->op == TOKarraylength || e1->op == TOKvar || e1->op == TOKdotvar - || e1->op == TOKindex || e1->op == TOKslice || e1->op == TOKstructliteral)) - { - error("CTFE internal error: unsupported assignment %s", toChars()); - return EXP_CANT_INTERPRET; - } - - Expression * newval = NULL; - - if (!wantRef) - { // We need to treat pointers specially, because TOKsymoff can be used to - // return a value OR a pointer - assert(e1); - assert(e1->type); - if ( isPointer(e1->type) && (e2->op == TOKsymoff || e2->op==TOKaddress || e2->op==TOKvar)) - newval = this->e2->interpret(istate, ctfeNeedLvalue); - else - newval = this->e2->interpret(istate); - if (exceptionOrCantInterpret(newval)) - return newval; - } - // ---------------------------------------------------- - // Deal with read-modify-write assignments. - // Set 'newval' to the final assignment value - // Also determine the return value (except for slice - // assignments, which are more complicated) - // ---------------------------------------------------- + /************* + * Deal with assignments of the form + * dest[] = newval + * dest[low..upp] = newval + * where newval has already been interpreted + * + * This could be a slice assignment or a block assignment, and + * dest could be either an array literal, or a string. + * + * Returns EXP_CANT_INTERPRET on failure. If there are no errors, + * it returns aggregate[low..upp], except that as an optimisation, + * if goal == ctfeNeedNothing, it will return NULL + */ - if (fp || e1->op == TOKarraylength) + Expression *interpretAssignToSlice(Loc loc, + SliceExp *sexp, Expression *newval, bool wantRef, bool isBlockAssignment, + BinExp *originalExp) { - // If it isn't a simple assignment, we need the existing value - Expression * oldval = e1->interpret(istate); - if (exceptionOrCantInterpret(oldval)) - return oldval; - while (oldval->op == TOKvar) + Expression *e2 = originalExp->e2; + + // ------------------------------ + // aggregate[] = newval + // aggregate[low..upp] = newval + // ------------------------------ + // Set the $ variable + Expression *oldval = sexp->e1; + bool assignmentToSlicedPointer = false; + if (isPointer(oldval->type)) { - oldval = resolveReferences(oldval); - oldval = oldval->interpret(istate); + // Slicing a pointer + oldval = oldval->interpret(istate, ctfeNeedLvalue); if (exceptionOrCantInterpret(oldval)) return oldval; + dinteger_t ofs; + oldval = getAggregateFromPointer(oldval, &ofs); + assignmentToSlicedPointer = true; } + else + oldval = oldval->interpret(istate); - if (fp) + if (oldval->op != TOKarrayliteral && oldval->op != TOKstring + && oldval->op != TOKslice && oldval->op != TOKnull) { - // ~= can create new values (see bug 6052) - if (op == TOKcatass) + if (oldval->op == TOKsymoff) { - // We need to dup it. We can skip this if it's a dynamic array, - // because it gets copied later anyway - if (newval->type->ty != Tarray) - newval = copyLiteral(newval); - if (newval->op == TOKslice) - newval = resolveSlice(newval); - // It becomes a reference assignment - wantRef = true; - } - if (oldval->op == TOKslice) - oldval = resolveSlice(oldval); - if (this->e1->type->ty == Tpointer && this->e2->type->isintegral() - && (op==TOKaddass || op == TOKminass || - op == TOKplusplus || op == TOKminusminus)) - { - oldval = this->e1->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(oldval)) - return oldval; - newval = this->e2->interpret(istate); - if (exceptionOrCantInterpret(newval)) - return newval; - newval = pointerArithmetic(loc, op, type, oldval, newval); + originalExp->error("pointer %s cannot be sliced at compile time (it points to a static variable)", sexp->e1->toChars()); + return EXP_CANT_INTERPRET; } - else if (this->e1->type->ty == Tpointer) + if (assignmentToSlicedPointer) { - error("pointer expression %s cannot be interpreted at compile time", toChars()); - return EXP_CANT_INTERPRET; + originalExp->error("pointer %s cannot be sliced at compile time (it does not point to an array)", + sexp->e1->toChars()); } else + originalExp->error("CTFE ICE: cannot resolve array length"); + return EXP_CANT_INTERPRET; + } + uinteger_t dollar = resolveArrayLength(oldval); + if (sexp->lengthVar) + { + Expression *arraylen = new IntegerExp(loc, dollar, Type::tsize_t); + ctfeStack.push(sexp->lengthVar); + setValue(sexp->lengthVar, arraylen); + } + + Expression *upper = NULL; + Expression *lower = NULL; + if (sexp->upr) + upper = sexp->upr->interpret(istate); + if (exceptionOrCantInterpret(upper)) + { + if (sexp->lengthVar) + ctfeStack.pop(sexp->lengthVar); // $ is defined only in [L..U] + return upper; + } + if (sexp->lwr) + lower = sexp->lwr->interpret(istate); + if (sexp->lengthVar) + ctfeStack.pop(sexp->lengthVar); // $ is defined only in [L..U] + if (exceptionOrCantInterpret(lower)) + return lower; + + unsigned dim = (unsigned)dollar; + size_t upperbound = (size_t)(upper ? upper->toInteger() : dim); + int lowerbound = (int)(lower ? lower->toInteger() : 0); + + if (!assignmentToSlicedPointer && (((int)lowerbound < 0) || (upperbound > dim))) + { + originalExp->error("Array bounds [0..%d] exceeded in slice [%d..%d]", + dim, lowerbound, upperbound); + return EXP_CANT_INTERPRET; + } + if (upperbound == lowerbound) + return newval; + + Expression *aggregate = resolveReferences(sexp->e1); + sinteger_t firstIndex = lowerbound; + + ArrayLiteralExp *existingAE = NULL; + StringExp *existingSE = NULL; + + /* The only possible slicable LValue aggregates are array literals, + * and slices of array literals. + */ + if (aggregate->op == TOKindex || aggregate->op == TOKdotvar || + aggregate->op == TOKslice || aggregate->op == TOKcast || + aggregate->op == TOKstar || aggregate->op == TOKcall) + { + aggregate = aggregate->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(aggregate)) + return aggregate; + // The array could be an index of an AA. Resolve it if so. + if (aggregate->op == TOKindex && + ((IndexExp *)aggregate)->e1->op == TOKassocarrayliteral) { - newval = (*fp)(type, oldval, newval); + IndexExp *ix = (IndexExp *)aggregate; + aggregate = findKeyInAA(loc, (AssocArrayLiteralExp *)ix->e1, ix->e2); + if (!aggregate) + { + originalExp->error("key %s not found in associative array %s", + ix->e2->toChars(), ix->e1->toChars()); + return EXP_CANT_INTERPRET; + } + if (exceptionOrCantInterpret(aggregate)) + return aggregate; } - if (newval == EXP_CANT_INTERPRET) + } + if (aggregate->op == TOKvar) + { + VarExp *ve = (VarExp *)(aggregate); + VarDeclaration *v = ve->var->isVarDeclaration(); + aggregate = getValue(v); + } + if (aggregate->op == TOKslice) + { + // Slice of a slice --> change the bounds + SliceExp *sexpold = (SliceExp *)aggregate; + sinteger_t hi = upperbound + sexpold->lwr->toInteger(); + firstIndex = lowerbound + sexpold->lwr->toInteger(); + if (hi > sexpold->upr->toInteger()) { - error("Cannot interpret %s at compile time", toChars()); + originalExp->error("slice [%d..%d] exceeds array bounds [0..%lld]", + lowerbound, upperbound, + sexpold->upr->toInteger() - sexpold->lwr->toInteger()); return EXP_CANT_INTERPRET; } - if (exceptionOrCantInterpret(newval)) - return newval; - // Determine the return value - returnValue = ctfeCast(loc, type, type, post ? oldval : newval); - if (exceptionOrCantInterpret(returnValue)) - return returnValue; + aggregate = sexpold->e1; } - else - returnValue = newval; - if (e1->op == TOKarraylength) - { - size_t oldlen = (size_t)oldval->toInteger(); - size_t newlen = (size_t)newval->toInteger(); - if (oldlen == newlen) // no change required -- we're done! - return returnValue; - // Now change the assignment from arr.length = n into arr = newval - e1 = ((ArrayLengthExp *)e1)->e1; - if (oldlen != 0) - { // Get the old array literal. - oldval = e1->interpret(istate); - while (oldval->op == TOKvar) - { oldval = resolveReferences(oldval); - oldval = oldval->interpret(istate); - } - } - Type *t = e1->type->toBasetype(); - if (t->ty == Tarray) + if ( isPointer(aggregate->type) ) + { + // Slicing a pointer --> change the bounds + aggregate = sexp->e1->interpret(istate, ctfeNeedLvalue); + dinteger_t ofs; + aggregate = getAggregateFromPointer(aggregate, &ofs); + if (aggregate->op == TOKnull) { - newval = changeArrayLiteralLength(loc, (TypeArray *)t, oldval, - oldlen, newlen); - // We have changed it into a reference assignment - // Note that returnValue is still the new length. - wantRef = true; - if (e1->op == TOKstar) - { // arr.length+=n becomes (t=&arr, *(t).length=*(t).length+n); - e1 = e1->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(e1)) - return e1; - } + originalExp->error("cannot slice null pointer %s", sexp->e1->toChars()); + return EXP_CANT_INTERPRET; } - else + sinteger_t hi = upperbound + ofs; + firstIndex = lowerbound + ofs; + if (firstIndex < 0 || hi > dim) { - error("%s is not yet supported at compile time", toChars()); + originalExp->error("slice [lld..%lld] exceeds memory block bounds [0..%lld]", + firstIndex, hi, dim); return EXP_CANT_INTERPRET; } - } - } - else if (!wantRef && e1->op != TOKslice) - { /* Look for special case of struct being initialized with 0. - */ - if (type->toBasetype()->ty == Tstruct && newval->op == TOKint64) + if (aggregate->op == TOKarrayliteral) + existingAE = (ArrayLiteralExp *)aggregate; + else if (aggregate->op == TOKstring) + existingSE = (StringExp *)aggregate; + if (existingSE && !existingSE->ownedByCtfe) + { + originalExp->error("cannot modify read-only string literal %s", sexp->e1->toChars()); + return EXP_CANT_INTERPRET; + } + + if (!wantRef && newval->op == TOKslice) { - newval = type->defaultInitLiteral(loc); - if (newval->op != TOKstructliteral) + Expression *orignewval = newval; + newval = resolveSlice(newval); + if (newval == EXP_CANT_INTERPRET) { - error("nested structs with constructors are not yet supported in CTFE (Bug 6419)"); - return EXP_CANT_INTERPRET; + originalExp->error("Compiler error: CTFE slice %s", orignewval->toChars()); + assert(0); } } - newval = ctfeCast(loc, type, type, newval); - if (exceptionOrCantInterpret(newval)) - return newval; - returnValue = newval; - } - if (exceptionOrCantInterpret(newval)) - return newval; - - // ------------------------------------------------- - // Make sure destination can be modified - // ------------------------------------------------- - // Make sure we're not trying to modify a global or static variable - // We do this by locating the ultimate parent variable which gets modified. - VarDeclaration * ultimateVar = findParentVar(e1); - if (ultimateVar && ultimateVar->isDataseg() && !ultimateVar->isCTFE()) - { // Can't modify global or static data - error("%s cannot be modified at compile time", ultimateVar->toChars()); - return EXP_CANT_INTERPRET; - } - - e1 = resolveReferences(e1); - - // Unless we have a simple var assignment, we're - // only modifying part of the variable. So we need to make sure - // that the parent variable exists. - if (e1->op != TOKvar && ultimateVar && !ultimateVar->getValue()) - ultimateVar->setValue(copyLiteral(ultimateVar->type->defaultInitLiteral(loc))); - - // --------------------------------------- - // Deal with reference assignment - // (We already have 'newval' for arraylength operations) - // --------------------------------------- - if (wantRef && !fp && this->e1->op != TOKarraylength) - { - newval = this->e2->interpret(istate, - wantLvalueRef ? ctfeNeedLvalueRef : ctfeNeedLvalue); - if (exceptionOrCantInterpret(newval)) - return newval; - // If it is an assignment from a array function parameter passed by - // reference, resolve the reference. (This should NOT happen for - // non-reference types). - if (newval->op == TOKvar && (newval->type->ty == Tarray || - newval->type->ty == Tclass)) + if (wantRef && newval->op == TOKindex + && ((IndexExp *)newval)->e1 == aggregate) { + // It's a circular reference, resolve it now newval = newval->interpret(istate); } - if (newval->op == TOKassocarrayliteral || newval->op == TOKstring || - newval->op == TOKarrayliteral) + // For slice assignment, we check that the lengths match. + size_t srclen = 0; + if (newval->op == TOKarrayliteral) + srclen = ((ArrayLiteralExp *)newval)->elements->dim; + else if (newval->op == TOKstring) + srclen = ((StringExp *)newval)->len; + if (!isBlockAssignment && srclen != (upperbound - lowerbound)) { - if (needToCopyLiteral(newval)) - newval = copyLiteral(newval); + originalExp->error("Array length mismatch assigning [0..%d] to [%d..%d]", srclen, lowerbound, upperbound); + return EXP_CANT_INTERPRET; } - // Get the value to return. Note that 'newval' is an Lvalue, - // so if we need an Rvalue, we have to interpret again. - if (goal == ctfeNeedRvalue) - returnValue = newval->interpret(istate); - else - returnValue = newval; - } - - // --------------------------------------- - // Deal with AA index assignment - // --------------------------------------- - /* This needs special treatment if the AA doesn't exist yet. - * There are two special cases: - * (1) If the AA is itself an index of another AA, we may need to create - * multiple nested AA literals before we can insert the new value. - * (2) If the ultimate AA is null, no insertion happens at all. Instead, we - * create nested AA literals, and change it into a assignment. - */ - if (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) - { - IndexExp *ie = (IndexExp *)e1; - int depth = 0; // how many nested AA indices are there? - while (ie->e1->op == TOKindex && ((IndexExp *)ie->e1)->e1->type->toBasetype()->ty == Taarray) + if (!isBlockAssignment && newval->op == TOKarrayliteral && existingAE) { - ie = (IndexExp *)ie->e1; - ++depth; + Expressions *oldelems = existingAE->elements; + Expressions *newelems = ((ArrayLiteralExp *)newval)->elements; + Type *elemtype = existingAE->type->nextOf(); + for (size_t j = 0; j < newelems->dim; j++) + { + (*oldelems)[(size_t)(j + firstIndex)] = paintTypeOntoLiteral(elemtype, (*newelems)[j]); + } + if (originalExp->op != TOKblit && originalExp->e2->isLvalue()) + { + Expression *x = evaluatePostblits(istate, existingAE, 0, oldelems->dim); + if (exceptionOrCantInterpret(x)) + return x; + } + return newval; } - Expression *aggregate = resolveReferences(ie->e1); - Expression *oldagg = aggregate; - // Get the AA to be modified. (We do an LvalueRef interpret, unless it - // is a simple ref parameter -- in which case, we just want the value) - aggregate = aggregate->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(aggregate)) - return aggregate; - if (aggregate->op == TOKassocarrayliteral) - { // Normal case, ultimate parent AA already exists - // We need to walk from the deepest index up, checking that an AA literal - // already exists on each level. - Expression *index = ((IndexExp *)e1)->e2->interpret(istate); - if (exceptionOrCantInterpret(index)) - return index; - if (index->op == TOKslice) // only happens with AA assignment - index = resolveSlice(index); - AssocArrayLiteralExp *existingAA = (AssocArrayLiteralExp *)aggregate; - while (depth > 0) - { // Walk the syntax tree to find the indexExp at this depth - IndexExp *xe = (IndexExp *)e1; - for (int d= 0; d < depth; ++d) - xe = (IndexExp *)xe->e1; - - Expression *indx = xe->e2->interpret(istate); - if (exceptionOrCantInterpret(indx)) - return indx; - if (indx->op == TOKslice) // only happens with AA assignment - indx = resolveSlice(indx); - - // Look up this index in it up in the existing AA, to get the next level of AA. - AssocArrayLiteralExp *newAA = (AssocArrayLiteralExp *)findKeyInAA(loc, existingAA, indx); - if (exceptionOrCantInterpret(newAA)) - return newAA; - if (!newAA) - { // Doesn't exist yet, create an empty AA... - Expressions *valuesx = new Expressions(); - Expressions *keysx = new Expressions(); - newAA = new AssocArrayLiteralExp(loc, keysx, valuesx); - newAA->type = xe->type; - newAA->ownedByCtfe = true; - //... and insert it into the existing AA. - existingAA->keys->push(indx); - existingAA->values->push(newAA); + else if (newval->op == TOKstring && existingSE) + { + sliceAssignStringFromString((StringExp *)existingSE, (StringExp *)newval, (size_t)firstIndex); + return newval; + } + else if (newval->op == TOKstring && existingAE + && existingAE->type->nextOf()->isintegral()) + { + /* Mixed slice: it was initialized as an array literal of chars/integers. + * Now a slice of it is being set with a string. + */ + sliceAssignArrayLiteralFromString(existingAE, (StringExp *)newval, (size_t)firstIndex); + return newval; + } + else if (newval->op == TOKarrayliteral && existingSE) + { + /* Mixed slice: it was initialized as a string literal. + * Now a slice of it is being set with an array literal. + */ + sliceAssignStringFromArrayLiteral(existingSE, (ArrayLiteralExp *)newval, (size_t)firstIndex); + return newval; + } + else if (existingSE) + { + // String literal block slice assign + dinteger_t value = newval->toInteger(); + utf8_t *s = (utf8_t *)existingSE->string; + for (size_t j = 0; j < upperbound-lowerbound; j++) + { + switch (existingSE->sz) + { + case 1: s[(size_t)(j+firstIndex)] = (utf8_t)value; break; + case 2: ((unsigned short *)s)[(size_t)(j+firstIndex)] = (unsigned short)value; break; + case 4: ((unsigned *)s)[(size_t)(j+firstIndex)] = (unsigned)value; break; + default: + assert(0); + break; } - existingAA = newAA; - --depth; } - if (assignAssocArrayElement(loc, existingAA, index, newval) == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; - return returnValue; - } - else - { /* The AA is currently null. 'aggregate' is actually a reference to - * whatever contains it. It could be anything: var, dotvarexp, ... - * We rewrite the assignment from: aggregate[i][j] = newval; - * into: aggregate = [i:[j: newval]]; + if (goal == ctfeNeedNothing) + return NULL; // avoid creating an unused literal + SliceExp *retslice = new SliceExp(loc, existingSE, + new IntegerExp(loc, firstIndex, Type::tsize_t), + new IntegerExp(loc, firstIndex + upperbound-lowerbound, Type::tsize_t)); + retslice->type = originalExp->type; + return retslice->interpret(istate); + } + else if (existingAE) + { + /* Block assignment, initialization of static arrays + * x[] = e + * x may be a multidimensional static array. (Note that this + * only happens with array literals, never with strings). */ - while (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) + Expressions * w = existingAE->elements; + assert( existingAE->type->ty == Tsarray || + existingAE->type->ty == Tarray); + Type *desttype = ((TypeArray *)existingAE->type)->next->toBasetype()->castMod(0); + bool directblk = (e2->type->toBasetype()->castMod(0))->equals(desttype); + bool cow = !(newval->op == TOKstructliteral || newval->op == TOKarrayliteral + || newval->op == TOKstring); + for (size_t j = 0; j < upperbound-lowerbound; j++) { - Expression *index = ((IndexExp *)e1)->e2->interpret(istate); - if (exceptionOrCantInterpret(index)) - return index; - if (index->op == TOKslice) // only happens with AA assignment - index = resolveSlice(index); - Expressions *valuesx = new Expressions(); - Expressions *keysx = new Expressions(); - valuesx->push(newval); - keysx->push(index); - AssocArrayLiteralExp *newaae = new AssocArrayLiteralExp(loc, keysx, valuesx); - newaae->ownedByCtfe = true; - newaae->type = ((IndexExp *)e1)->e1->type; - newval = newaae; - e1 = ((IndexExp *)e1)->e1; - } - // We must return to the original aggregate, in case it was a reference - wantRef = true; - e1 = oldagg; - // fall through -- let the normal assignment logic take care of it + if (!directblk) + { + // Multidimensional array block assign + recursiveBlockAssign((ArrayLiteralExp *)(*w)[(size_t)(j+firstIndex)], newval, wantRef); + } + else + { + if (wantRef || cow) + (*existingAE->elements)[(size_t)(j+firstIndex)] = newval; + else + assignInPlace((*existingAE->elements)[(size_t)(j+firstIndex)], newval); + } + } + if (!wantRef && !cow && originalExp->op != TOKblit && originalExp->e2->isLvalue()) + { + Expression *x = evaluatePostblits(istate, existingAE, (size_t)firstIndex, (size_t)(firstIndex+upperbound-lowerbound)); + if (exceptionOrCantInterpret(x)) + return x; + } + if (goal == ctfeNeedNothing) + return NULL; // avoid creating an unused literal + SliceExp *retslice = new SliceExp(loc, existingAE, + new IntegerExp(loc, firstIndex, Type::tsize_t), + new IntegerExp(loc, firstIndex + upperbound-lowerbound, Type::tsize_t)); + retslice->type = originalExp->type; + return retslice->interpret(istate); + } + else + { + originalExp->error("Slice operation %s = %s cannot be evaluated at compile time", sexp->toChars(), newval->toChars()); + return EXP_CANT_INTERPRET; } } - // --------------------------------------- - // Deal with dotvar expressions - // --------------------------------------- - // Because structs are not reference types, dotvar expressions can be - // collapsed into a single assignment. - if (!wantRef && e1->op == TOKdotvar) + void visit(AssignExp *e) + { + interpretAssignCommon(e, NULL); + } + + void visit(BinAssignExp *e) { - // Strip of all of the leading dotvars, unless it is a CTFE dotvar - // pointer or reference - // (in which case, we already have the lvalue). - DotVarExp *dve = (DotVarExp *)e1; - bool isCtfePointer = (dve->e1->op == TOKstructliteral) - && ((StructLiteralExp *)(dve->e1))->ownedByCtfe; - if (!isCtfePointer) + switch(e->op) { - e1 = e1->interpret(istate, isPointer(type) ? ctfeNeedLvalueRef : ctfeNeedLvalue); - if (exceptionOrCantInterpret(e1)) - return e1; + case TOKaddass: interpretAssignCommon(e, &Add); return; + case TOKminass: interpretAssignCommon(e, &Min); return; + case TOKcatass: interpretAssignCommon(e, &ctfeCat); return; + case TOKmulass: interpretAssignCommon(e, &Mul); return; + case TOKdivass: interpretAssignCommon(e, &Div); return; + case TOKmodass: interpretAssignCommon(e, &Mod); return; + case TOKshlass: interpretAssignCommon(e, &Shl); return; + case TOKshrass: interpretAssignCommon(e, &Shr); return; + case TOKushrass: interpretAssignCommon(e, &Ushr); return; + case TOKandass: interpretAssignCommon(e, &And); return; + case TOKorass: interpretAssignCommon(e, &Or); return; + case TOKxorass: interpretAssignCommon(e, &Xor); return; + case TOKpowass: interpretAssignCommon(e, &Pow); return; + default: + assert(0); + return; } } -#if LOGASSIGN - if (wantRef) - printf("REF ASSIGN: %s=%s\n", e1->toChars(), newval->toChars()); - else - printf("ASSIGN: %s=%s\n", e1->toChars(), newval->toChars()); - showCtfeExpr(newval); -#endif - /* Assignment to variable of the form: - * v = newval + void visit(PostExp *e) + { + #if LOG + printf("%s PostExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + if (e->op == TOKplusplus) + interpretAssignCommon(e, &Add, 1); + else + interpretAssignCommon(e, &Min, 1); + #if LOG + if (result == EXP_CANT_INTERPRET) + printf("PostExp::interpret() CANT\n"); + #endif + } + + /* Return 1 if e is a p1 > p2 or p1 >= p2 pointer comparison; + * -1 if e is a p1 < p2 or p1 <= p2 pointer comparison; + * 0 otherwise + */ + static int isPointerCmpExp(Expression *e, Expression **p1, Expression **p2) + { + int ret = 1; + while (e->op == TOKnot) + { ret *= -1; + e = ((NotExp *)e)->e1; + } + switch(e->op) + { + case TOKlt: + case TOKle: + ret *= -1; + /* fall through */ + case TOKgt: + case TOKge: + *p1 = ((BinExp *)e)->e1; + *p2 = ((BinExp *)e)->e2; + if ( !(isPointer((*p1)->type) && isPointer((*p2)->type)) ) + ret = 0; + break; + default: + ret = 0; + break; + } + return ret; + } + + /** Negate a relational operator, eg >= becomes < */ - if (e1->op == TOKvar) + static TOK reverseRelation(TOK op) { - VarExp *ve = (VarExp *)e1; - VarDeclaration *v = ve->var->isVarDeclaration(); - if (wantRef) + switch(op) + { + case TOKge: return TOKlt; + case TOKgt: return TOKle; + case TOKle: return TOKgt; + case TOKlt: return TOKge; + default: + return assert(0), TOKreserved; + } + } + + /** If this is a four pointer relation, evaluate it, else return NULL. + * + * This is an expression of the form (p1 > q1 && p2 < q2) or (p1 < q1 || p2 > q2) + * where p1, p2 are expressions yielding pointers to memory block p, + * and q1, q2 are expressions yielding pointers to memory block q. + * This expression is valid even if p and q are independent memory + * blocks and are therefore not normally comparable; the && form returns true + * if [p1..p2] lies inside [q1..q2], and false otherwise; the || form returns + * true if [p1..p2] lies outside [q1..q2], and false otherwise. + * + * Within the expression, any ordering of p1, p2, q1, q2 is permissible; + * the comparison operators can be any of >, <, <=, >=, provided that + * both directions (p > q and p < q) are checked. Additionally the + * relational sub-expressions can be negated, eg + * ( !(q1 < p1) && p2 <= q2 ) is valid. + */ + void interpretFourPointerRelation(BinExp *e) + { + assert(e->op == TOKandand || e->op == TOKoror); + + /* It can only be an isInside expression, if both e1 and e2 are + * directional pointer comparisons. + * Note that this check can be made statically; it does not depends on + * any runtime values. This allows a JIT implementation to compile a + * special AndAndPossiblyInside, keeping the normal AndAnd case efficient. + */ + + // Save the pointer expressions and the comparison directions, + // so we can use them later. + Expression *p1 = NULL, *p2 = NULL, *p3 = NULL, *p4 = NULL; + int dir1 = isPointerCmpExp(e->e1, &p1, &p2); + int dir2 = isPointerCmpExp(e->e2, &p3, &p4); + if (dir1 == 0 || dir2 == 0) + { + result = NULL; + return; + } + + //printf("FourPointerRelation %s\n", toChars()); + + // Evaluate the first two pointers + p1 = p1->interpret(istate); + if (exceptionOrCantInterpret(p1)) + { + result = p1; + return; + } + p2 = p2->interpret(istate); + if (exceptionOrCantInterpret(p2)) { - v->setValueNull(); - v->setValue(newval); + result = p2; + return; } - else if (e1->type->toBasetype()->ty == Tstruct) + dinteger_t ofs1, ofs2; + Expression *agg1 = getAggregateFromPointer(p1, &ofs1); + Expression *agg2 = getAggregateFromPointer(p2, &ofs2); + + if ( !pointToSameMemoryBlock(agg1, agg2) + && agg1->op != TOKnull && agg2->op != TOKnull) { - // In-place modification - if (newval->op != TOKstructliteral) + // Here it is either CANT_INTERPRET, + // or an IsInside comparison returning false. + p3 = p3->interpret(istate); + if (p3 == EXP_CANT_INTERPRET) { - error("CTFE internal error assigning struct"); - return EXP_CANT_INTERPRET; + result = p3; + return; } - newval = copyLiteral(newval); - if (v->getValue()) - assignInPlace(v->getValue(), newval); + // Note that it is NOT legal for it to throw an exception! + Expression *except = NULL; + if (exceptionOrCantInterpret(p3)) + except = p3; else - v->setValue(newval); - } - else - { - TY tyE1 = e1->type->toBasetype()->ty; - if (tyE1 == Tarray || tyE1 == Taarray) - { // arr op= arr - v->setValue(newval); + { + p4 = p4->interpret(istate); + if (p4 == EXP_CANT_INTERPRET) + { + result = p4; + return; + } + if (exceptionOrCantInterpret(p4)) + except = p4; + } + if (except) + { + e->error("Comparison %s of pointers to unrelated memory blocks remains " + "indeterminate at compile time " + "because exception %s was thrown while evaluating %s", + e->e1->toChars(), except->toChars(), e->e2->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + dinteger_t ofs3,ofs4; + Expression *agg3 = getAggregateFromPointer(p3, &ofs3); + Expression *agg4 = getAggregateFromPointer(p4, &ofs4); + // The valid cases are: + // p1 > p2 && p3 > p4 (same direction, also for < && <) + // p1 > p2 && p3 < p4 (different direction, also < && >) + // Changing any > into >= doesnt affect the result + if ( (dir1 == dir2 && pointToSameMemoryBlock(agg1, agg4) + && pointToSameMemoryBlock(agg2, agg3)) + || (dir1 != dir2 && pointToSameMemoryBlock(agg1, agg3) + && pointToSameMemoryBlock(agg2, agg4)) ) + { // it's a legal two-sided comparison + result = new IntegerExp(e->loc, (e->op == TOKandand) ? 0 : 1, e->type); + return; + } + // It's an invalid four-pointer comparison. Either the second + // comparison is in the same direction as the first, or else + // more than two memory blocks are involved (either two independent + // invalid comparisons are present, or else agg3 == agg4). + e->error("Comparison %s of pointers to unrelated memory blocks is " + "indeterminate at compile time, even when combined with %s.", + e->e1->toChars(), e->e2->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + // The first pointer expression didn't need special treatment, so we + // we need to interpret the entire expression exactly as a normal && or ||. + // This is easy because we haven't evaluated e2 at all yet, and we already + // know it will return a bool. + // But we mustn't evaluate the pointer expressions in e1 again, in case + // they have side-effects. + bool nott = false; + Expression *ex = e->e1; + while (ex->op == TOKnot) + { + nott = !nott; + ex = ((NotExp *)ex)->e1; + } + TOK cmpop = ex->op; + if (nott) + cmpop = reverseRelation(cmpop); + int cmp = comparePointers(e->loc, cmpop, e->e1->type, agg1, ofs1, agg2, ofs2); + // We already know this is a valid comparison. + assert(cmp >= 0); + if ((e->op == TOKandand && cmp == 1) || (e->op == TOKoror && cmp == 0)) + { + result = e->e2->interpret(istate); + return; + } + result = new IntegerExp(e->loc, (e->op == TOKandand) ? 0 : 1, e->type); + } + + void visit(AndAndExp *e) + { + #if LOG + printf("%s AndAndExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + + // Check for an insidePointer expression, evaluate it if so + interpretFourPointerRelation(e); + if (result) + return; + + result = e->e1->interpret(istate); + if (exceptionOrCantInterpret(result)) + return; + + int res; + if (result != EXP_CANT_INTERPRET) + { + if (result->isBool(false)) + res = 0; + else if (isTrueBool(result)) + { + result = e->e2->interpret(istate); + if (exceptionOrCantInterpret(result)) + return; + if (result == EXP_VOID_INTERPRET) + { + assert(e->type->ty == Tvoid); + result = NULL; + return; + } + if (result->isBool(false)) + res = 0; + else if (isTrueBool(result)) + res = 1; + else + { + result->error("%s does not evaluate to a boolean", result->toChars()); + result = EXP_CANT_INTERPRET; + } } else { - v->setValue(newval); + result->error("%s cannot be interpreted as a boolean", result->toChars()); + result = EXP_CANT_INTERPRET; } } + if (result != EXP_CANT_INTERPRET && goal != ctfeNeedNothing) + result = new IntegerExp(e->loc, res, e->type); } - else if (e1->op == TOKstructliteral && newval->op == TOKstructliteral) - { - /* Assignment to complete struct of the form: - * e1 = newval - * (e1 was a ref parameter, or was created via TOKstar dereferencing). - */ - assignInPlace(e1, newval); - return returnValue; - } - else if (e1->op == TOKdotvar) + + void visit(OrOrExp *e) { - /* Assignment to member variable of the form: - * e.v = newval - */ - Expression *exx = ((DotVarExp *)e1)->e1; - if (wantRef && exx->op != TOKstructliteral) + #if LOG + printf("%s OrOrExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + + // Check for an insidePointer expression, evaluate it if so + interpretFourPointerRelation(e); + if (result) + return; + + result = e->e1->interpret(istate); + if (exceptionOrCantInterpret(result)) + return; + + int res; + if (result != EXP_CANT_INTERPRET) { - exx = exx->interpret(istate); - if (exceptionOrCantInterpret(exx)) - return exx; + if (isTrueBool(result)) + res = 1; + else if (result->isBool(false)) + { + result = e->e2->interpret(istate); + if (exceptionOrCantInterpret(result)) + return; + + if (result == EXP_VOID_INTERPRET) + { + assert(e->type->ty == Tvoid); + result = NULL; + return; + } + if (result != EXP_CANT_INTERPRET) + { + if (result->isBool(false)) + res = 0; + else if (isTrueBool(result)) + res = 1; + else + { + result->error("%s cannot be interpreted as a boolean", result->toChars()); + result = EXP_CANT_INTERPRET; + } + } + } + else + { + result->error("%s cannot be interpreted as a boolean", result->toChars()); + result = EXP_CANT_INTERPRET; + } } - if (exx->op != TOKstructliteral && exx->op != TOKclassreference) + if (result != EXP_CANT_INTERPRET && goal != ctfeNeedNothing) + result = new IntegerExp(e->loc, res, e->type); + } + + // Print a stack trace, starting from callingExp which called fd. + // To shorten the stack trace, try to detect recursion. + void showCtfeBackTrace(CallExp * callingExp, FuncDeclaration *fd) + { + if (CtfeStatus::stackTraceCallsToSuppress > 0) { - error("CTFE internal error: Dotvar assignment"); - return EXP_CANT_INTERPRET; + --CtfeStatus::stackTraceCallsToSuppress; + return; } - VarDeclaration *member = ((DotVarExp *)e1)->var->isVarDeclaration(); - if (!member) + errorSupplemental(callingExp->loc, "called from here: %s", callingExp->toChars()); + // Quit if it's not worth trying to compress the stack trace + if (CtfeStatus::callDepth < 6 || global.params.verbose) + return; + // Recursion happens if the current function already exists in the call stack. + int numToSuppress = 0; + int recurseCount = 0; + int depthSoFar = 0; + InterState *lastRecurse = istate; + for (InterState * cur = istate; cur; cur = cur->caller) { - error("CTFE internal error: Dotvar assignment"); - return EXP_CANT_INTERPRET; + if (cur->fd == fd) + { + ++recurseCount; + numToSuppress = depthSoFar; + lastRecurse = cur; + } + ++depthSoFar; } - StructLiteralExp *se = exx->op == TOKstructliteral - ? (StructLiteralExp *)exx - : ((ClassReferenceExp *)exx)->value; - int fieldi = exx->op == TOKstructliteral - ? findFieldIndexByName(se->sd, member) - : ((ClassReferenceExp *)exx)->findFieldIndexByName(member); - if (fieldi == -1) + // We need at least three calls to the same function, to make compression worthwhile + if (recurseCount < 2) + return; + // We found a useful recursion. Print all the calls involved in the recursion + errorSupplemental(fd->loc, "%d recursive calls to function %s", recurseCount, fd->toChars()); + for (InterState *cur = istate; cur->fd != fd; cur = cur->caller) { - error("CTFE internal error: cannot find field %s in %s", member->toChars(), exx->toChars()); - return EXP_CANT_INTERPRET; + errorSupplemental(cur->fd->loc, "recursively called from function %s", cur->fd->toChars()); } - assert(fieldi >= 0 && fieldi < se->elements->dim); - // If it's a union, set all other members of this union to void - if (exx->op == TOKstructliteral) + // We probably didn't enter the recursion in this function. + // Go deeper to find the real beginning. + InterState * cur = istate; + while (lastRecurse->caller && cur->fd == lastRecurse->caller->fd) { - assert(se->sd); - int unionStart = se->sd->firstFieldInUnion(fieldi); - int unionSize = se->sd->numFieldsInUnion(fieldi); - for(int i = unionStart; i < unionStart + unionSize; ++i) - { if (i == fieldi) - continue; - Expression **el = &(*se->elements)[i]; - if ((*el)->op != TOKvoid) - *el = (*el)->type->voidInitLiteral(member); - } + cur = cur->caller; + lastRecurse = lastRecurse->caller; + ++numToSuppress; } - - if (newval->op == TOKstructliteral) - assignInPlace((*se->elements)[fieldi], newval); - else - (*se->elements)[fieldi] = newval; - return returnValue; - } - else if (e1->op == TOKindex) - { - if ( !interpretAssignToIndex(istate, loc, (IndexExp *)e1, newval, - wantRef, this)) - return EXP_CANT_INTERPRET; - return returnValue; - } - else if (e1->op == TOKslice) - { - // Note that slice assignments don't support things like ++, so - // we don't need to remember 'returnValue'. - return interpretAssignToSlice(istate, goal, loc, (SliceExp *)e1, - newval, wantRef, isBlockAssignment, this); + CtfeStatus::stackTraceCallsToSuppress = numToSuppress; } - else - { - error("%s cannot be evaluated at compile time", toChars()); - } - return returnValue; -} - -/************* - * Deal with assignments of the form - * aggregate[ie] = newval - * where aggregate and newval have already been interpreted - * - * Return true if OK, false if error occured - */ -bool interpretAssignToIndex(InterState *istate, Loc loc, - IndexExp *ie, Expression *newval, bool wantRef, - BinExp *originalExp) -{ - /* Assignment to array element of the form: - * aggregate[i] = newval - * aggregate is not AA (AAs were dealt with already). - */ - assert(ie->e1->type->toBasetype()->ty != Taarray); - uinteger_t destarraylen = 0; - // Set the $ variable, and find the array literal to modify - if (ie->e1->type->toBasetype()->ty != Tpointer) + void visit(CallExp *e) { - Expression *oldval = ie->e1->interpret(istate); - if (oldval->op == TOKnull) + #if LOG + printf("%s CallExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + + Expression * pthis = NULL; + FuncDeclaration *fd = NULL; + Expression *ecall = e->e1; + if (ecall->op == TOKcall) { - originalExp->error("cannot index null array %s", ie->e1->toChars()); - return false; + ecall = e->e1->interpret(istate); + if (exceptionOrCantInterpret(ecall)) + { + result = ecall; + return; + } + } + if (ecall->op == TOKstar) + { + // Calling a function pointer + Expression * pe = ((PtrExp*)ecall)->e1; + if (pe->op == TOKvar) + { + VarDeclaration *vd = ((VarExp *)((PtrExp*)ecall)->e1)->var->isVarDeclaration(); + if (vd && hasValue(vd) && getValue(vd)->op == TOKsymoff) + fd = ((SymOffExp *)getValue(vd))->var->isFuncDeclaration(); + else + { + ecall = getVarExp(e->loc, istate, vd, goal); + if (exceptionOrCantInterpret(ecall)) + { + result = ecall; + return; + } + + if (ecall->op == TOKsymoff) + fd = ((SymOffExp *)ecall)->var->isFuncDeclaration(); + } + } + else if (pe->op == TOKsymoff) + fd = ((SymOffExp *)pe)->var->isFuncDeclaration(); + else + ecall = ((PtrExp*)ecall)->e1->interpret(istate); + } - if (oldval->op != TOKarrayliteral && oldval->op != TOKstring - && oldval->op != TOKslice) + if (exceptionOrCantInterpret(ecall)) { - originalExp->error("cannot determine length of %s at compile time", - ie->e1->toChars()); - return false; + result = ecall; + return; } - destarraylen = resolveArrayLength(oldval); - if (ie->lengthVar) + + if (ecall->op == TOKindex) { - IntegerExp *dollarExp = new IntegerExp(loc, destarraylen, Type::tsize_t); - ctfeStack.push(ie->lengthVar); - ie->lengthVar->setValue(dollarExp); + ecall = e->e1->interpret(istate); + if (exceptionOrCantInterpret(ecall)) + { + result = ecall; + return; + } } - } - Expression *index = ie->e2->interpret(istate); - if (ie->lengthVar) - ctfeStack.pop(ie->lengthVar); // $ is defined only inside [] - if (exceptionOrCantInterpret(index)) - return false; - - assert (index->op != TOKslice); // only happens with AA assignment - - ArrayLiteralExp *existingAE = NULL; - StringExp *existingSE = NULL; - Expression *aggregate = resolveReferences(ie->e1); + if (ecall->op == TOKdotvar && !((DotVarExp*)ecall)->var->isFuncDeclaration()) + { + ecall = e->e1->interpret(istate); + if (exceptionOrCantInterpret(ecall)) + { + result = ecall; + return; + } + } - // Set the index to modify, and check that it is in range - dinteger_t indexToModify = index->toInteger(); - if (ie->e1->type->toBasetype()->ty == Tpointer) - { - dinteger_t ofs; - aggregate = aggregate->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(aggregate)) - return false; - if (aggregate->op == TOKnull) + if (ecall->op == TOKdotvar) { - originalExp->error("cannot index through null pointer %s", ie->e1->toChars()); - return false; + // Calling a member function + pthis = ((DotVarExp*)e->e1)->e1; + fd = ((DotVarExp*)e->e1)->var->isFuncDeclaration(); } - if (aggregate->op == TOKint64) + else if (ecall->op == TOKvar) { - originalExp->error("cannot index through invalid pointer %s of value %s", - ie->e1->toChars(), aggregate->toChars()); - return false; + VarDeclaration *vd = ((VarExp *)ecall)->var->isVarDeclaration(); + if (vd && hasValue(vd)) + ecall = getValue(vd); + else // Calling a function + fd = ((VarExp *)e->e1)->var->isFuncDeclaration(); } - aggregate = getAggregateFromPointer(aggregate, &ofs); - indexToModify += ofs; - if (aggregate->op != TOKslice && aggregate->op != TOKstring && - aggregate->op != TOKarrayliteral && aggregate->op != TOKassocarrayliteral) + if (ecall->op == TOKdelegate) { - if (aggregate->op == TOKsymoff) - { - originalExp->error("mutable variable %s cannot be modified at compile time, even through a pointer", ((SymOffExp *)aggregate)->var->toChars()); - return false; - } - if (indexToModify != 0) - { - originalExp->error("pointer index [%lld] lies outside memory block [0..1]", indexToModify); - return false; - } - // It is equivalent to *aggregate = newval. - // Aggregate could be varexp, a dotvar, ... - // TODO: we could support this - originalExp->error("indexed assignment of non-array pointers is not yet supported at compile time; use *%s = %s instead", - ie->e1->toChars(), originalExp->e2->toChars()); - return false; + // Calling a delegate + fd = ((DelegateExp *)ecall)->func; + pthis = ((DelegateExp *)ecall)->e1; } - destarraylen = resolveArrayLength(aggregate); - } - if (indexToModify >= destarraylen) - { - originalExp->error("array index %lld is out of bounds [0..%lld]", indexToModify, - destarraylen); - return false; - } - - /* The only possible indexable LValue aggregates are array literals, and - * slices of array literals. - */ - if (aggregate->op == TOKindex || aggregate->op == TOKdotvar || - aggregate->op == TOKslice || aggregate->op == TOKcall || - aggregate->op == TOKstar || aggregate->op == TOKcast) - { - aggregate = aggregate->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(aggregate)) - return false; - // The array could be an index of an AA. Resolve it if so. - if (aggregate->op == TOKindex && - ((IndexExp *)aggregate)->e1->op == TOKassocarrayliteral) + else if (ecall->op == TOKfunction) { - IndexExp *ix = (IndexExp *)aggregate; - aggregate = findKeyInAA(loc, (AssocArrayLiteralExp *)ix->e1, ix->e2); - if (!aggregate) - { - originalExp->error("key %s not found in associative array %s", - ix->e2->toChars(), ix->e1->toChars()); - return false; - } - if (exceptionOrCantInterpret(aggregate)) - return false; + // Calling a delegate literal + fd = ((FuncExp*)ecall)->fd; } - } - if (aggregate->op == TOKvar) - { - VarExp *ve = (VarExp *)aggregate; - VarDeclaration *v = ve->var->isVarDeclaration(); - aggregate = v->getValue(); - if (aggregate->op == TOKnull) + else if (ecall->op == TOKstar && ((PtrExp*)ecall)->e1->op == TOKfunction) { - // This would be a runtime segfault - originalExp->error("cannot index null array %s", v->toChars()); - return false; + // Calling a function literal + fd = ((FuncExp*)((PtrExp*)ecall)->e1)->fd; } - } - if (aggregate->op == TOKslice) - { - SliceExp *sexp = (SliceExp *)aggregate; - aggregate = sexp->e1; - Expression *lwr = sexp->lwr->interpret(istate); - indexToModify += lwr->toInteger(); - } - if (aggregate->op == TOKarrayliteral) - existingAE = (ArrayLiteralExp *)aggregate; - else if (aggregate->op == TOKstring) - existingSE = (StringExp *)aggregate; - else - { - originalExp->error("CTFE internal compiler error %s", aggregate->toChars()); - return false; - } - if (!wantRef && newval->op == TOKslice) - { - newval = resolveSlice(newval); - if (newval == EXP_CANT_INTERPRET) + else if (ecall->op == TOKdelegatefuncptr) { - originalExp->error("Compiler error: CTFE index assign %s", originalExp->toChars()); - assert(0); + // delegate.funcptr() + e->error("cannot evaulate %s at compile time", e->toChars()); + result = EXP_CANT_INTERPRET; + return; } - } - if (wantRef && newval->op == TOKindex - && ((IndexExp *)newval)->e1 == aggregate) - { // It's a circular reference, resolve it now - newval = newval->interpret(istate); - } - if (existingAE) - { - if (newval->op == TOKstructliteral) - assignInPlace((*existingAE->elements)[(size_t)indexToModify], newval); - else - (*existingAE->elements)[(size_t)indexToModify] = newval; - return true; - } - if (existingSE) - { - utf8_t *s = (utf8_t *)existingSE->string; - if (!existingSE->ownedByCtfe) + TypeFunction *tf = fd ? (TypeFunction *)(fd->type) : NULL; + if (!tf) { - originalExp->error("cannot modify read-only string literal %s", ie->e1->toChars()); - return false; + // This should never happen, it's an internal compiler error. + //printf("ecall=%s %d %d\n", ecall->toChars(), ecall->op, TOKcall); + if (ecall->op == TOKidentifier) + e->error("cannot evaluate %s at compile time. Circular reference?", e->toChars()); + else + e->error("CTFE internal error: cannot evaluate %s at compile time", e->toChars()); + result = EXP_CANT_INTERPRET; + return; } - dinteger_t value = newval->toInteger(); - switch (existingSE->sz) + if (!fd) { - case 1: s[(size_t)indexToModify] = (utf8_t)value; break; - case 2: ((unsigned short *)s)[(size_t)indexToModify] = (unsigned short)value; break; - case 4: ((unsigned *)s)[(size_t)indexToModify] = (unsigned)value; break; - default: - assert(0); - break; + e->error("cannot evaluate %s at compile time", e->toChars()); + result = EXP_CANT_INTERPRET; + return; } - return true; - } - else - { - originalExp->error("Index assignment %s is not yet supported in CTFE ", originalExp->toChars()); - return false; - } - return true; -} + if (pthis) + { + // Member function call + if (pthis->op == TOKcomma) + pthis = pthis->interpret(istate); + if (exceptionOrCantInterpret(pthis)) + { + result = pthis; + return; + } + // Evaluate 'this' + Expression *oldpthis = pthis; + if (pthis->op != TOKvar) + pthis = pthis->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(pthis)) + { + result = pthis; + return; + } + if (fd->isVirtual()) + { + // Make a virtual function call. + Expression *thisval = pthis; + if (pthis->op == TOKvar) + { + VarDeclaration *vthis = ((VarExp*)thisval)->var->isVarDeclaration(); + assert(vthis); + thisval = getVarExp(e->loc, istate, vthis, ctfeNeedLvalue); + if (exceptionOrCantInterpret(thisval)) + { + result = thisval; + return; + } + // If it is a reference, resolve it + if (thisval->op != TOKnull && thisval->op != TOKclassreference) + thisval = pthis->interpret(istate); + } + else if (pthis->op == TOKsymoff) + { + VarDeclaration *vthis = ((SymOffExp*)thisval)->var->isVarDeclaration(); + assert(vthis); + thisval = getVarExp(e->loc, istate, vthis, ctfeNeedLvalue); + if (exceptionOrCantInterpret(thisval)) + { + result = thisval; + return; + } + } -/************* - * Deal with assignments of the form - * dest[] = newval - * dest[low..upp] = newval - * where newval has already been interpreted - * - * This could be a slice assignment or a block assignment, and - * dest could be either an array literal, or a string. - * - * Returns EXP_CANT_INTERPRET on failure. If there are no errors, - * it returns aggregate[low..upp], except that as an optimisation, - * if goal == ctfeNeedNothing, it will return NULL - */ + // Get the function from the vtable of the original class + if (thisval && thisval->op == TOKnull) + { + e->error("function call through null class reference %s", pthis->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + ClassDeclaration *cd; + if (oldpthis->op == TOKsuper) + { + assert(oldpthis->type->ty == Tclass); + cd = ((TypeClass *)oldpthis->type)->sym; + } + else + { + assert(thisval && thisval->op == TOKclassreference); + cd = ((ClassReferenceExp *)thisval)->originalClass(); + } + // We can't just use the vtable index to look it up, because + // vtables for interfaces don't get populated until the glue layer. + fd = cd->findFunc(fd->ident, (TypeFunction *)fd->type); -Expression *interpretAssignToSlice(InterState *istate, CtfeGoal goal, Loc loc, - SliceExp *sexp, Expression *newval, bool wantRef, bool isBlockAssignment, - BinExp *originalExp) -{ - Expression *e2 = originalExp->e2; - - // ------------------------------ - // aggregate[] = newval - // aggregate[low..upp] = newval - // ------------------------------ - // Set the $ variable - Expression *oldval = sexp->e1; - bool assignmentToSlicedPointer = false; - if (isPointer(oldval->type)) - { // Slicing a pointer - oldval = oldval->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(oldval)) - return oldval; - dinteger_t ofs; - oldval = getAggregateFromPointer(oldval, &ofs); - assignmentToSlicedPointer = true; - } - else - oldval = oldval->interpret(istate); + assert(fd); + } + } + if (fd && fd->semanticRun >= PASSsemantic3done && fd->semantic3Errors) + { + e->error("CTFE failed because of previous errors in %s", fd->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + // Check for built-in functions + result = evaluateIfBuiltin(istate, e->loc, fd, e->arguments, pthis); + if (result) + return; - if (oldval->op != TOKarrayliteral && oldval->op != TOKstring - && oldval->op != TOKslice && oldval->op != TOKnull) - { - if (oldval->op == TOKsymoff) + if (!fd->fbody) { - originalExp->error("pointer %s cannot be sliced at compile time (it points to a static variable)", sexp->e1->toChars()); - return EXP_CANT_INTERPRET; + e->error("%s cannot be interpreted at compile time," + " because it has no available source code", fd->toChars()); + result = EXP_CANT_INTERPRET; + return; } - if (assignmentToSlicedPointer) + result = interpret(fd, istate, e->arguments, pthis); + if (result == EXP_CANT_INTERPRET) { - originalExp->error("pointer %s cannot be sliced at compile time (it does not point to an array)", - sexp->e1->toChars()); + // Print a stack trace. + if (!global.gag) + showCtfeBackTrace(e, fd); + } + else if (result == EXP_VOID_INTERPRET) + ; + else if (result->op != TOKthrownexception) + { + result = paintTypeOntoLiteral(e->type, result); + result->loc = e->loc; } - else - originalExp->error("CTFE ICE: cannot resolve array length"); - return EXP_CANT_INTERPRET; - } - uinteger_t dollar = resolveArrayLength(oldval); - if (sexp->lengthVar) - { - Expression *arraylen = new IntegerExp(loc, dollar, Type::tsize_t); - ctfeStack.push(sexp->lengthVar); - sexp->lengthVar->setValue(arraylen); } - Expression *upper = NULL; - Expression *lower = NULL; - if (sexp->upr) - upper = sexp->upr->interpret(istate); - if (exceptionOrCantInterpret(upper)) + void visit(CommaExp *e) { - if (sexp->lengthVar) - ctfeStack.pop(sexp->lengthVar); // $ is defined only in [L..U] - return upper; - } - if (sexp->lwr) - lower = sexp->lwr->interpret(istate); - if (sexp->lengthVar) - ctfeStack.pop(sexp->lengthVar); // $ is defined only in [L..U] - if (exceptionOrCantInterpret(lower)) - return lower; - - unsigned dim = (unsigned)dollar; - size_t upperbound = (size_t)(upper ? upper->toInteger() : dim); - int lowerbound = (int)(lower ? lower->toInteger() : 0); + #if LOG + printf("%s CommaExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif - if (!assignmentToSlicedPointer && (((int)lowerbound < 0) || (upperbound > dim))) - { - originalExp->error("Array bounds [0..%d] exceeded in slice [%d..%d]", - dim, lowerbound, upperbound); - return EXP_CANT_INTERPRET; - } - if (upperbound == lowerbound) - return newval; + CommaExp * firstComma = e; + while (firstComma->e1->op == TOKcomma) + firstComma = (CommaExp *)firstComma->e1; - Expression *aggregate = resolveReferences(sexp->e1); - sinteger_t firstIndex = lowerbound; + // If it creates a variable, and there's no context for + // the variable to be created in, we need to create one now. + InterState istateComma; + if (!istate && firstComma->e1->op == TOKdeclaration) + { + ctfeStack.startFrame(NULL); + istate = &istateComma; + } - ArrayLiteralExp *existingAE = NULL; - StringExp *existingSE = NULL; + result = EXP_CANT_INTERPRET; - /* The only possible slicable LValue aggregates are array literals, - * and slices of array literals. - */ - if (aggregate->op == TOKindex || aggregate->op == TOKdotvar || - aggregate->op == TOKslice || aggregate->op == TOKcast || - aggregate->op == TOKstar || aggregate->op == TOKcall) - { - aggregate = aggregate->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(aggregate)) - return aggregate; - // The array could be an index of an AA. Resolve it if so. - if (aggregate->op == TOKindex && - ((IndexExp *)aggregate)->e1->op == TOKassocarrayliteral) - { - IndexExp *ix = (IndexExp *)aggregate; - aggregate = findKeyInAA(loc, (AssocArrayLiteralExp *)ix->e1, ix->e2); - if (!aggregate) - { - originalExp->error("key %s not found in associative array %s", - ix->e2->toChars(), ix->e1->toChars()); - return EXP_CANT_INTERPRET; + // If the comma returns a temporary variable, it needs to be an lvalue + // (this is particularly important for struct constructors) + if (e->e1->op == TOKdeclaration && e->e2->op == TOKvar + && ((DeclarationExp *)e->e1)->declaration == ((VarExp*)e->e2)->var + && ((VarExp*)e->e2)->var->storage_class & STCctfe) // same as Expression::isTemp + { + VarExp* ve = (VarExp *)e->e2; + VarDeclaration *v = ve->var->isVarDeclaration(); + ctfeStack.push(v); + if (!v->init && !getValue(v)) + { + setValue(v, copyLiteral(v->type->defaultInitLiteral(e->loc))); } - if (exceptionOrCantInterpret(aggregate)) - return aggregate; + if (!getValue(v)) + { + Expression *newval = v->init->toExpression(); + // Bug 4027. Copy constructors are a weird case where the + // initializer is a void function (the variable is modified + // through a reference parameter instead). + newval = newval->interpret(istate); + if (exceptionOrCantInterpret(newval)) + { + if (istate == &istateComma) + ctfeStack.endFrame(); + result = newval; + return; + } + if (newval != EXP_VOID_INTERPRET) + { + // v isn't necessarily null. + setValueWithoutChecking(v, copyLiteral(newval)); + } + } + if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) + result = e->e2; + else + result = e->e2->interpret(istate, goal); } - } - if (aggregate->op == TOKvar) - { - VarExp *ve = (VarExp *)(aggregate); - VarDeclaration *v = ve->var->isVarDeclaration(); - aggregate = v->getValue(); - } - if (aggregate->op == TOKslice) - { // Slice of a slice --> change the bounds - SliceExp *sexpold = (SliceExp *)aggregate; - sinteger_t hi = upperbound + sexpold->lwr->toInteger(); - firstIndex = lowerbound + sexpold->lwr->toInteger(); - if (hi > sexpold->upr->toInteger()) + else { - originalExp->error("slice [%d..%d] exceeds array bounds [0..%lld]", - lowerbound, upperbound, - sexpold->upr->toInteger() - sexpold->lwr->toInteger()); - return EXP_CANT_INTERPRET; + result = e->e1->interpret(istate, ctfeNeedNothing); + if (!exceptionOrCantInterpret(result)) + result = e->e2->interpret(istate, goal); } - aggregate = sexpold->e1; + // If we created a temporary stack frame, end it now. + if (istate == &istateComma) + ctfeStack.endFrame(); } - if ( isPointer(aggregate->type) ) - { // Slicing a pointer --> change the bounds - aggregate = sexp->e1->interpret(istate, ctfeNeedLvalue); - dinteger_t ofs; - aggregate = getAggregateFromPointer(aggregate, &ofs); - if (aggregate->op == TOKnull) + + void visit(CondExp *e) + { + #if LOG + printf("%s CondExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + if (isPointer(e->econd->type)) { - originalExp->error("cannot slice null pointer %s", sexp->e1->toChars()); - return EXP_CANT_INTERPRET; + result = e->econd->interpret(istate); + if (exceptionOrCantInterpret(result)) + return; + if (result->op != TOKnull) + result = new IntegerExp(e->loc, 1, Type::tbool); } - sinteger_t hi = upperbound + ofs; - firstIndex = lowerbound + ofs; - if (firstIndex < 0 || hi > dim) + else + result = e->econd->interpret(istate); + if (exceptionOrCantInterpret(result)) + return; + if (isTrueBool(result)) + result = e->e1->interpret(istate, goal); + else if (result->isBool(false)) + result = e->e2->interpret(istate, goal); + else { - originalExp->error("slice [lld..%lld] exceeds memory block bounds [0..%lld]", - firstIndex, hi, dim); - return EXP_CANT_INTERPRET; + e->error("%s does not evaluate to boolean result at compile time", + e->econd->toChars()); + result = EXP_CANT_INTERPRET; } } - if (aggregate->op == TOKarrayliteral) - existingAE = (ArrayLiteralExp *)aggregate; - else if (aggregate->op == TOKstring) - existingSE = (StringExp *)aggregate; - if (existingSE && !existingSE->ownedByCtfe) - { originalExp->error("cannot modify read-only string literal %s", sexp->e1->toChars()); - return EXP_CANT_INTERPRET; - } - if (!wantRef && newval->op == TOKslice) + void visit(ArrayLengthExp *e) { - Expression *orignewval = newval; - newval = resolveSlice(newval); - if (newval == EXP_CANT_INTERPRET) + #if LOG + printf("%s ArrayLengthExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + Expression *e1 = e->e1->interpret(istate); + assert(e1); + if (exceptionOrCantInterpret(e1)) { - originalExp->error("Compiler error: CTFE slice %s", orignewval->toChars()); - assert(0); + result = e1; + return; + } + if (e1->op == TOKstring || e1->op == TOKarrayliteral || e1->op == TOKslice + || e1->op == TOKassocarrayliteral || e1->op == TOKnull) + { + result = new IntegerExp(e->loc, resolveArrayLength(e1), e->type); + } + else + { + e->error("%s cannot be evaluated at compile time", e->toChars()); + result = EXP_CANT_INTERPRET; } - } - if (wantRef && newval->op == TOKindex - && ((IndexExp *)newval)->e1 == aggregate) - { // It's a circular reference, resolve it now - newval = newval->interpret(istate); } - // For slice assignment, we check that the lengths match. - size_t srclen = 0; - if (newval->op == TOKarrayliteral) - srclen = ((ArrayLiteralExp *)newval)->elements->dim; - else if (newval->op == TOKstring) - srclen = ((StringExp *)newval)->len; - if (!isBlockAssignment && srclen != (upperbound - lowerbound)) + void visit(DelegatePtrExp *e) { - originalExp->error("Array length mismatch assigning [0..%d] to [%d..%d]", srclen, lowerbound, upperbound); - return EXP_CANT_INTERPRET; + #if LOG + printf("%s DelegatePtrExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + Expression *e1 = e->e1->interpret(istate); + assert(e1); + if (exceptionOrCantInterpret(e1)) + { + result = e1; + return; + } + e->error("%s cannot be evaluated at compile time", e->toChars()); + result = EXP_CANT_INTERPRET; } - if (!isBlockAssignment && newval->op == TOKarrayliteral && existingAE) + void visit(DelegateFuncptrExp *e) { - Expressions *oldelems = existingAE->elements; - Expressions *newelems = ((ArrayLiteralExp *)newval)->elements; - Type *elemtype = existingAE->type->nextOf(); - for (size_t j = 0; j < newelems->dim; j++) + #if LOG + printf("%s DelegateFuncptrExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + Expression *e1 = e->e1->interpret(istate); + assert(e1); + if (exceptionOrCantInterpret(e1)) { - (*oldelems)[(size_t)(j + firstIndex)] = paintTypeOntoLiteral(elemtype, (*newelems)[j]); + result = e1; + return; } - return newval; + e->error("%s cannot be evaluated at compile time", e->toChars()); + result = EXP_CANT_INTERPRET; } - else if (newval->op == TOKstring && existingSE) + + void visit(IndexExp *e) { - sliceAssignStringFromString((StringExp *)existingSE, (StringExp *)newval, (size_t)firstIndex); - return newval; - } - else if (newval->op == TOKstring && existingAE - && existingAE->type->isString()) - { /* Mixed slice: it was initialized as an array literal of chars. - * Now a slice of it is being set with a string. - */ - sliceAssignArrayLiteralFromString(existingAE, (StringExp *)newval, (size_t)firstIndex); - return newval; - } - else if (newval->op == TOKarrayliteral && existingSE) - { /* Mixed slice: it was initialized as a string literal. - * Now a slice of it is being set with an array literal. - */ - sliceAssignStringFromArrayLiteral(existingSE, (ArrayLiteralExp *)newval, (size_t)firstIndex); - return newval; - } - else if (existingSE) - { // String literal block slice assign - dinteger_t value = newval->toInteger(); - utf8_t *s = (utf8_t *)existingSE->string; - for (size_t j = 0; j < upperbound-lowerbound; j++) + #if LOG + printf("%s IndexExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + if (e->e1->type->toBasetype()->ty == Tpointer) { - switch (existingSE->sz) + // Indexing a pointer. Note that there is no $ in this case. + Expression *e1 = e->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) { - case 1: s[(size_t)(j+firstIndex)] = (utf8_t)value; break; - case 2: ((unsigned short *)s)[(size_t)(j+firstIndex)] = (unsigned short)value; break; - case 4: ((unsigned *)s)[(size_t)(j+firstIndex)] = (unsigned)value; break; - default: - assert(0); - break; + result = e1; + return; } - } - if (goal == ctfeNeedNothing) - return NULL; // avoid creating an unused literal - SliceExp *retslice = new SliceExp(loc, existingSE, - new IntegerExp(loc, firstIndex, Type::tsize_t), - new IntegerExp(loc, firstIndex + upperbound-lowerbound, Type::tsize_t)); - retslice->type = originalExp->type; - return retslice->interpret(istate); - } - else if (existingAE) - { - /* Block assignment, initialization of static arrays - * x[] = e - * x may be a multidimensional static array. (Note that this - * only happens with array literals, never with strings). - */ - Expressions * w = existingAE->elements; - assert( existingAE->type->ty == Tsarray || - existingAE->type->ty == Tarray); -#if DMDV2 - Type *desttype = ((TypeArray *)existingAE->type)->next->toBasetype()->castMod(0); - bool directblk = (e2->type->toBasetype()->castMod(0))->equals(desttype); -#else - Type *desttype = ((TypeArray *)existingAE->type)->next; - bool directblk = (e2->type->toBasetype())->equals(desttype); -#endif - bool cow = !(newval->op == TOKstructliteral || newval->op == TOKarrayliteral - || newval->op == TOKstring); - for (size_t j = 0; j < upperbound-lowerbound; j++) - { - if (!directblk) - // Multidimensional array block assign - recursiveBlockAssign((ArrayLiteralExp *)(*w)[(size_t)(j+firstIndex)], newval, wantRef); - else + Expression *e2 = e->e2->interpret(istate); + if (exceptionOrCantInterpret(e2)) { - if (wantRef || cow) - (*existingAE->elements)[(size_t)(j+firstIndex)] = newval; - else - assignInPlace((*existingAE->elements)[(size_t)(j+firstIndex)], newval); + result = e2; + return; } - } - if (goal == ctfeNeedNothing) - return NULL; // avoid creating an unused literal - SliceExp *retslice = new SliceExp(loc, existingAE, - new IntegerExp(loc, firstIndex, Type::tsize_t), - new IntegerExp(loc, firstIndex + upperbound-lowerbound, Type::tsize_t)); - retslice->type = originalExp->type; - return retslice->interpret(istate); - } - else - { - originalExp->error("Slice operation %s = %s cannot be evaluated at compile time", sexp->toChars(), newval->toChars()); - return EXP_CANT_INTERPRET; - } -} - -Expression *AssignExp::interpret(InterState *istate, CtfeGoal goal) -{ - return interpretAssignCommon(istate, goal, NULL); -} - -Expression *BinAssignExp::interpret(InterState *istate, CtfeGoal goal) -{ - switch(op) - { - case TOKaddass: return interpretAssignCommon(istate, goal, &Add); - case TOKminass: return interpretAssignCommon(istate, goal, &Min); - case TOKcatass: return interpretAssignCommon(istate, goal, &ctfeCat); - case TOKmulass: return interpretAssignCommon(istate, goal, &Mul); - case TOKdivass: return interpretAssignCommon(istate, goal, &Div); - case TOKmodass: return interpretAssignCommon(istate, goal, &Mod); - case TOKshlass: return interpretAssignCommon(istate, goal, &Shl); - case TOKshrass: return interpretAssignCommon(istate, goal, &Shr); - case TOKushrass: return interpretAssignCommon(istate, goal, &Ushr); - case TOKandass: return interpretAssignCommon(istate, goal, &And); - case TOKorass: return interpretAssignCommon(istate, goal, &Or); - case TOKxorass: return interpretAssignCommon(istate, goal, &Xor); -#if DMDV2 - case TOKpowass: return interpretAssignCommon(istate, goal, &Pow); -#endif - default: - assert(0); - return NULL; - } -} - -Expression *PostExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s PostExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - Expression *e; - if (op == TOKplusplus) - e = interpretAssignCommon(istate, goal, &Add, 1); - else - e = interpretAssignCommon(istate, goal, &Min, 1); -#if LOG - if (e == EXP_CANT_INTERPRET) - printf("PostExp::interpret() CANT\n"); -#endif - return e; -} - -/* Return 1 if e is a p1 > p2 or p1 >= p2 pointer comparison; - * -1 if e is a p1 < p2 or p1 <= p2 pointer comparison; - * 0 otherwise - */ -int isPointerCmpExp(Expression *e, Expression **p1, Expression **p2) -{ - int ret = 1; - while (e->op == TOKnot) - { ret *= -1; - e = ((NotExp *)e)->e1; - } - switch(e->op) - { - case TOKlt: - case TOKle: - ret *= -1; - /* fall through */ - case TOKgt: - case TOKge: - *p1 = ((BinExp *)e)->e1; - *p2 = ((BinExp *)e)->e2; - if ( !(isPointer((*p1)->type) && isPointer((*p2)->type)) ) - ret = 0; - break; - default: - ret = 0; - break; - } - return ret; -} - -/** Negate a relational operator, eg >= becomes < - */ -TOK reverseRelation(TOK op) -{ - switch(op) - { - case TOKge: return TOKlt; - case TOKgt: return TOKle; - case TOKle: return TOKgt; - case TOKlt: return TOKge; - default: - return assert(0), TOKreserved; - } -} - -/** If this is a four pointer relation, evaluate it, else return NULL. - * - * This is an expression of the form (p1 > q1 && p2 < q2) or (p1 < q1 || p2 > q2) - * where p1, p2 are expressions yielding pointers to memory block p, - * and q1, q2 are expressions yielding pointers to memory block q. - * This expression is valid even if p and q are independent memory - * blocks and are therefore not normally comparable; the && form returns true - * if [p1..p2] lies inside [q1..q2], and false otherwise; the || form returns - * true if [p1..p2] lies outside [q1..q2], and false otherwise. - * - * Within the expression, any ordering of p1, p2, q1, q2 is permissible; - * the comparison operators can be any of >, <, <=, >=, provided that - * both directions (p > q and p < q) are checked. Additionally the - * relational sub-expressions can be negated, eg - * ( !(q1 < p1) && p2 <= q2 ) is valid. - */ -Expression *BinExp::interpretFourPointerRelation(InterState *istate, CtfeGoal goal) -{ - assert(op == TOKandand || op == TOKoror); + sinteger_t indx = e2->toInteger(); - /* It can only be an isInside expression, if both e1 and e2 are - * directional pointer comparisons. - * Note that this check can be made statically; it does not depends on - * any runtime values. This allows a JIT implementation to compile a - * special AndAndPossiblyInside, keeping the normal AndAnd case efficient. - */ + dinteger_t ofs; + Expression *agg = getAggregateFromPointer(e1, &ofs); - // Save the pointer expressions and the comparison directions, - // so we can use them later. - Expression *p1, *p2, *p3, *p4; - int dir1 = isPointerCmpExp(e1, &p1, &p2); - int dir2 = isPointerCmpExp(e2, &p3, &p4); - if ( dir1 == 0 || dir2 == 0 ) - return NULL; + if (agg->op == TOKnull) + { + e->error("cannot index null pointer %s", e->e1->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + if (agg->op == TOKarrayliteral || agg->op == TOKstring) + { + dinteger_t len = ArrayLength(Type::tsize_t, agg)->toInteger(); + //Type *pointee = ((TypePointer *)agg->type)->next; + if ((sinteger_t)(indx + ofs) < 0 || (indx+ofs) > len) + { + e->error("pointer index [%lld] exceeds allocated memory block [0..%lld]", + indx+ofs, len); + result = EXP_CANT_INTERPRET; + return; + } + if (goal == ctfeNeedLvalueRef) + { + // if we need a reference, IndexExp shouldn't be interpreting + // the expression to a value, it should stay as a reference + result = new IndexExp(e->loc, agg, + ofs ? new IntegerExp(e->loc, indx + ofs, e2->type) : e2); + result->type = e->type; + return; + } + result = ctfeIndex(e->loc, e->type, agg, indx+ofs); + return; + } + else + { + // Pointer to a non-array variable + if (agg->op == TOKsymoff) + { + e->error("mutable variable %s cannot be read at compile time, even through a pointer", ((SymOffExp *)agg)->var->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + if ((indx + ofs) != 0) + { + e->error("pointer index [%lld] lies outside memory block [0..1]", + indx+ofs); + result = EXP_CANT_INTERPRET; + return; + } + if (goal == ctfeNeedLvalueRef) + { + result = paintTypeOntoLiteral(e->type, agg); + return; + } + result = agg->interpret(istate); + return; + } + } + Expression *e1 = e->e1; + if (!(e1->op == TOKarrayliteral && ((ArrayLiteralExp *)e1)->ownedByCtfe) && + !(e1->op == TOKassocarrayliteral && ((AssocArrayLiteralExp *)e1)->ownedByCtfe)) + e1 = e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + { + result = e1; + return; + } - //printf("FourPointerRelation %s\n", toChars()); - - // Evaluate the first two pointers - p1 = p1->interpret(istate); - if (exceptionOrCantInterpret(p1)) - return p1; - p2 = p2->interpret(istate); - if (exceptionOrCantInterpret(p1)) - return p1; - dinteger_t ofs1, ofs2; - Expression *agg1 = getAggregateFromPointer(p1, &ofs1); - Expression *agg2 = getAggregateFromPointer(p2, &ofs2); - - if ( !pointToSameMemoryBlock(agg1, agg2) - && agg1->op != TOKnull && agg2->op != TOKnull) - { // Here it is either CANT_INTERPRET, - // or an IsInside comparison returning false. - p3 = p3->interpret(istate); - if (p3 == EXP_CANT_INTERPRET) - return p3; - // Note that it is NOT legal for it to throw an exception! - Expression *except = NULL; - if (exceptionOrCantInterpret(p3)) - except = p3; - else + if (e1->op == TOKnull) + { + if (goal == ctfeNeedLvalue && e1->type->ty == Taarray && e->modifiable) + { + result = paintTypeOntoLiteral(e->type, e1); + return; + } + e->error("cannot index null array %s", e->e1->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + /* Set the $ variable. + * Note that foreach uses indexing but doesn't need $ + */ + if (e->lengthVar && (e1->op == TOKstring || e1->op == TOKarrayliteral + || e1->op == TOKslice)) { - p4 = p4->interpret(istate); - if (p4 == EXP_CANT_INTERPRET) - return p4; - if (exceptionOrCantInterpret(p4)) - except = p4; - } - if (except) - { error("Comparison %s of pointers to unrelated memory blocks remains " - "indeterminate at compile time " - "because exception %s was thrown while evaluating %s", - this->e1->toChars(), except->toChars(), this->e2->toChars()); - return EXP_CANT_INTERPRET; + uinteger_t dollar = resolveArrayLength(e1); + Expression *dollarExp = new IntegerExp(e->loc, dollar, Type::tsize_t); + ctfeStack.push(e->lengthVar); + setValue(e->lengthVar, dollarExp); } - dinteger_t ofs3,ofs4; - Expression *agg3 = getAggregateFromPointer(p3, &ofs3); - Expression *agg4 = getAggregateFromPointer(p4, &ofs4); - // The valid cases are: - // p1 > p2 && p3 > p4 (same direction, also for < && <) - // p1 > p2 && p3 < p4 (different direction, also < && >) - // Changing any > into >= doesnt affect the result - if ( (dir1 == dir2 && pointToSameMemoryBlock(agg1, agg4) - && pointToSameMemoryBlock(agg2, agg3)) - || (dir1 != dir2 && pointToSameMemoryBlock(agg1, agg3) - && pointToSameMemoryBlock(agg2, agg4)) ) - { // it's a legal two-sided comparison - return new IntegerExp(loc, (op == TOKandand) ? 0 : 1, type); - } - // It's an invalid four-pointer comparison. Either the second - // comparison is in the same direction as the first, or else - // more than two memory blocks are involved (either two independent - // invalid comparisons are present, or else agg3 == agg4). - error("Comparison %s of pointers to unrelated memory blocks is " - "indeterminate at compile time, even when combined with %s.", - e1->toChars(), e2->toChars()); - return EXP_CANT_INTERPRET; - } - // The first pointer expression didn't need special treatment, so we - // we need to interpret the entire expression exactly as a normal && or ||. - // This is easy because we haven't evaluated e2 at all yet, and we already - // know it will return a bool. - // But we mustn't evaluate the pointer expressions in e1 again, in case - // they have side-effects. - bool nott = false; - Expression *e = e1; - while (e->op == TOKnot) - { nott= !nott; - e = ((NotExp *)e)->e1; - } - TOK cmpop = e->op; - if (nott) - cmpop = reverseRelation(cmpop); - int cmp = comparePointers(loc, cmpop, e1->type, agg1, ofs1, agg2, ofs2); - // We already know this is a valid comparison. - assert(cmp >= 0); - if ( (op == TOKandand && cmp == 1) || (op == TOKoror && cmp == 0) ) - return e2->interpret(istate); - return new IntegerExp(loc, (op == TOKandand) ? 0 : 1, type); -} - -Expression *AndAndExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s AndAndExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - - // Check for an insidePointer expression, evaluate it if so - Expression *e = interpretFourPointerRelation(istate, goal); - if (e) - return e; - - e = e1->interpret(istate); - if (exceptionOrCantInterpret(e)) - return e; - int result; - if (e != EXP_CANT_INTERPRET) - { - if (e->isBool(false)) - result = 0; - else if (isTrueBool(e)) + Expression *e2 = e->e2->interpret(istate); + if (e->lengthVar) + ctfeStack.pop(e->lengthVar); // $ is defined only inside [] + if (exceptionOrCantInterpret(e2)) { - e = e2->interpret(istate); - if (exceptionOrCantInterpret(e)) - return e; - if (e == EXP_VOID_INTERPRET) + result = e2; + return; + } + if (e1->op == TOKslice && e2->op == TOKint64) + { + // Simplify index of slice: agg[lwr..upr][indx] --> agg[indx'] + uinteger_t indx = e2->toInteger(); + uinteger_t ilo = ((SliceExp *)e1)->lwr->toInteger(); + uinteger_t iup = ((SliceExp *)e1)->upr->toInteger(); + + if (indx > iup - ilo) { - assert(type->ty == Tvoid); - return NULL; + e->error("index %llu exceeds array length %llu", indx, iup - ilo); + result = EXP_CANT_INTERPRET; + return; } - if (e->isBool(false)) - result = 0; - else if (isTrueBool(e)) - result = 1; - else + indx += ilo; + e1 = ((SliceExp *)e1)->e1; + e2 = new IntegerExp(e2->loc, indx, e2->type); + } + if ((goal == ctfeNeedLvalue && e->type->ty != Taarray && e->type->ty != Tarray + && e->type->ty != Tsarray && e->type->ty != Tstruct && e->type->ty != Tclass) + || (goal == ctfeNeedLvalueRef && e->type->ty != Tsarray && e->type->ty != Tstruct) + ) + { // Pointer or reference of a scalar type + result = new IndexExp(e->loc, e1, e2); + result->type = e->type; + return; + } + if (e1->op == TOKassocarrayliteral) + { + if (e2->op == TOKslice) + e2 = resolveSlice(e2); + result = findKeyInAA(e->loc, (AssocArrayLiteralExp *)e1, e2); + if (!result) { - e->error("%s does not evaluate to a boolean", e->toChars()); - e = EXP_CANT_INTERPRET; + e->error("key %s not found in associative array %s", + e2->toChars(), e->e1->toChars()); + result = EXP_CANT_INTERPRET; + return; } } else { - e->error("%s cannot be interpreted as a boolean", e->toChars()); - e = EXP_CANT_INTERPRET; + if (e2->op != TOKint64) + { + e1->error("CTFE internal error: non-integral index [%s]", e->e2->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + result = ctfeIndex(e->loc, e->type, e1, e2->toInteger()); + } + if (exceptionOrCantInterpret(result)) + return; + if (goal == ctfeNeedRvalue && (result->op == TOKslice || e->op == TOKdotvar)) + result = result->interpret(istate); + if (goal == ctfeNeedRvalue && result->op == TOKvoid) + { + e->error("%s is used before initialized", e->toChars()); + errorSupplemental(result->loc, "originally uninitialized here"); + result = EXP_CANT_INTERPRET; + return; } + result = paintTypeOntoLiteral(e->type, result); } - if (e != EXP_CANT_INTERPRET && goal != ctfeNeedNothing) - e = new IntegerExp(loc, result, type); - return e; -} - -Expression *OrOrExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s OrOrExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - - // Check for an insidePointer expression, evaluate it if so - Expression *e = interpretFourPointerRelation(istate, goal); - if (e) - return e; - e = e1->interpret(istate); - if (exceptionOrCantInterpret(e)) - return e; - - int result; - if (e != EXP_CANT_INTERPRET) + void visit(SliceExp *e) { - if (isTrueBool(e)) - result = 1; - else if (e->isBool(false)) + #if LOG + printf("%s SliceExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + + if (e->e1->type->toBasetype()->ty == Tpointer) { - e = e2->interpret(istate); - if (exceptionOrCantInterpret(e)) - return e; + // Slicing a pointer. Note that there is no $ in this case. + Expression *e1 = e->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + { + result = e1; + return; + } + if (e1->op == TOKint64) + { + e->error("cannot slice invalid pointer %s of value %s", + e->e1->toChars(), e1->toChars()); + result = EXP_CANT_INTERPRET; + return; + } - if (e == EXP_VOID_INTERPRET) + /* Evaluate lower and upper bounds of slice + */ + Expression *lwr = e->lwr->interpret(istate); + if (exceptionOrCantInterpret(lwr)) { - assert(type->ty == Tvoid); - return NULL; + result = lwr; + return; } - if (e != EXP_CANT_INTERPRET) + Expression *upr = e->upr->interpret(istate); + if (exceptionOrCantInterpret(upr)) { - if (e->isBool(false)) - result = 0; - else if (isTrueBool(e)) - result = 1; - else + result = upr; + return; + } + uinteger_t ilwr = lwr->toInteger(); + uinteger_t iupr = upr->toInteger(); + dinteger_t ofs; + Expression *agg = getAggregateFromPointer(e1, &ofs); + ilwr += ofs; + iupr += ofs; + if (agg->op == TOKnull) + { + if (iupr == ilwr) { - e->error("%s cannot be interpreted as a boolean", e->toChars()); - e = EXP_CANT_INTERPRET; + result = new NullExp(e->loc); + result->type = e->type; + return; } + e->error("cannot slice null pointer %s", e->e1->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + if (agg->op == TOKsymoff) + { + e->error("slicing pointers to static variables is not supported in CTFE"); + result = EXP_CANT_INTERPRET; + return; + } + if (agg->op != TOKarrayliteral && agg->op != TOKstring) + { + e->error("pointer %s cannot be sliced at compile time (it does not point to an array)", + e->e1->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + assert(agg->op == TOKarrayliteral || agg->op == TOKstring); + dinteger_t len = ArrayLength(Type::tsize_t, agg)->toInteger(); + //Type *pointee = ((TypePointer *)agg->type)->next; + if (iupr > (len + 1) || iupr < ilwr) + { + e->error("pointer slice [%lld..%lld] exceeds allocated memory block [0..%lld]", + ilwr, iupr, len); + result = EXP_CANT_INTERPRET; + return; + } + if (ofs != 0) + { + lwr = new IntegerExp(e->loc, ilwr, lwr->type); + upr = new IntegerExp(e->loc, iupr, upr->type); } + result = new SliceExp(e->loc, agg, lwr, upr); + result->type = e->type; + return; } + Expression *e1; + if (goal == ctfeNeedRvalue && e->e1->op == TOKstring) + e1 = e->e1; // Will get duplicated anyway else + e1 = e->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) { - e->error("%s cannot be interpreted as a boolean", e->toChars()); - e = EXP_CANT_INTERPRET; + result = e1; + return; } - } - if (e != EXP_CANT_INTERPRET && goal != ctfeNeedNothing) - e = new IntegerExp(loc, result, type); - return e; -} - -// Print a stack trace, starting from callingExp which called fd. -// To shorten the stack trace, try to detect recursion. -void showCtfeBackTrace(InterState *istate, CallExp * callingExp, FuncDeclaration *fd) -{ - if (CtfeStatus::stackTraceCallsToSuppress > 0) - { - --CtfeStatus::stackTraceCallsToSuppress; - return; - } - errorSupplemental(callingExp->loc, "called from here: %s", callingExp->toChars()); - // Quit if it's not worth trying to compress the stack trace - if (CtfeStatus::callDepth < 6 || global.params.verbose) - return; - // Recursion happens if the current function already exists in the call stack. - int numToSuppress = 0; - int recurseCount = 0; - int depthSoFar = 0; - InterState *lastRecurse = istate; - for (InterState * cur = istate; cur; cur = cur->caller) - { - if (cur->fd == fd) - { ++recurseCount; - numToSuppress = depthSoFar; - lastRecurse = cur; - } - ++depthSoFar; - } - // We need at least three calls to the same function, to make compression worthwhile - if (recurseCount < 2) - return; - // We found a useful recursion. Print all the calls involved in the recursion - errorSupplemental(fd->loc, "%d recursive calls to function %s", recurseCount, fd->toChars()); - for (InterState *cur = istate; cur->fd != fd; cur = cur->caller) - { - errorSupplemental(cur->fd->loc, "recursively called from function %s", cur->fd->toChars()); - } - // We probably didn't enter the recursion in this function. - // Go deeper to find the real beginning. - InterState * cur = istate; - while (lastRecurse->caller && cur->fd == lastRecurse->caller->fd) - { - cur = cur->caller; - lastRecurse = lastRecurse->caller; - ++numToSuppress; - } - CtfeStatus::stackTraceCallsToSuppress = numToSuppress; -} - -Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) -{ Expression *e = EXP_CANT_INTERPRET; - -#if LOG - printf("%s CallExp::interpret() %s\n", loc.toChars(), toChars()); -#endif + if (e1->op == TOKvar) + e1 = e1->interpret(istate); - Expression * pthis = NULL; - FuncDeclaration *fd = NULL; - Expression *ecall = e1; - if (ecall->op == TOKcall) - { - ecall = e1->interpret(istate); - if (exceptionOrCantInterpret(ecall)) - return ecall; - } - if (ecall->op == TOKstar) - { // Calling a function pointer - Expression * pe = ((PtrExp*)ecall)->e1; - if (pe->op == TOKvar) { - VarDeclaration *vd = ((VarExp *)((PtrExp*)ecall)->e1)->var->isVarDeclaration(); - if (vd && vd->hasValue() && vd->getValue()->op == TOKsymoff) - fd = ((SymOffExp *)vd->getValue())->var->isFuncDeclaration(); - else + if (!e->lwr) + { + if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) { - ecall = getVarExp(loc, istate, vd, goal); - if (exceptionOrCantInterpret(ecall)) - return ecall; - - if (ecall->op == TOKsymoff) - fd = ((SymOffExp *)ecall)->var->isFuncDeclaration(); + result = e1; + return; } + result = paintTypeOntoLiteral(e->type, e1); + return; } - else if (pe->op == TOKsymoff) - fd = ((SymOffExp *)pe)->var->isFuncDeclaration(); - else - ecall = ((PtrExp*)ecall)->e1->interpret(istate); - - } - if (exceptionOrCantInterpret(ecall)) - return ecall; - if (ecall->op == TOKindex) - { ecall = e1->interpret(istate); - if (exceptionOrCantInterpret(ecall)) - return ecall; - } - - if (ecall->op == TOKdotvar && !((DotVarExp*)ecall)->var->isFuncDeclaration()) - { ecall = e1->interpret(istate); - if (exceptionOrCantInterpret(ecall)) - return ecall; - } + /* Set the $ variable + */ + if (e1->op != TOKarrayliteral && e1->op != TOKstring && + e1->op != TOKnull && e1->op != TOKslice) + { + e->error("Cannot determine length of %s at compile time", e1->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + uinteger_t dollar = resolveArrayLength(e1); + if (e->lengthVar) + { + IntegerExp *dollarExp = new IntegerExp(e->loc, dollar, Type::tsize_t); + ctfeStack.push(e->lengthVar); + setValue(e->lengthVar, dollarExp); + } - if (ecall->op == TOKdotvar) - { // Calling a member function - pthis = ((DotVarExp*)e1)->e1; - fd = ((DotVarExp*)e1)->var->isFuncDeclaration(); - } - else if (ecall->op == TOKvar) - { - VarDeclaration *vd = ((VarExp *)ecall)->var->isVarDeclaration(); - if (vd && vd->hasValue()) - ecall = vd->getValue(); - else // Calling a function - fd = ((VarExp *)e1)->var->isFuncDeclaration(); - } - if (ecall->op == TOKdelegate) - { // Calling a delegate - fd = ((DelegateExp *)ecall)->func; - pthis = ((DelegateExp *)ecall)->e1; - } - else if (ecall->op == TOKfunction) - { // Calling a delegate literal - fd = ((FuncExp*)ecall)->fd; - } - else if (ecall->op == TOKstar && ((PtrExp*)ecall)->e1->op==TOKfunction) - { // Calling a function literal - fd = ((FuncExp*)((PtrExp*)ecall)->e1)->fd; - } + /* Evaluate lower and upper bounds of slice + */ + Expression *lwr = e->lwr->interpret(istate); + if (exceptionOrCantInterpret(lwr)) + { + if (e->lengthVar) + ctfeStack.pop(e->lengthVar);; // $ is defined only inside [L..U] + result = lwr; + return; + } + Expression *upr = e->upr->interpret(istate); + if (e->lengthVar) + ctfeStack.pop(e->lengthVar); // $ is defined only inside [L..U] + if (exceptionOrCantInterpret(upr)) + { + result = upr; + return; + } - TypeFunction *tf = fd ? (TypeFunction *)(fd->type) : NULL; - if (!tf) - { // This should never happen, it's an internal compiler error. - //printf("ecall=%s %d %d\n", ecall->toChars(), ecall->op, TOKcall); - if (ecall->op == TOKidentifier) - error("cannot evaluate %s at compile time. Circular reference?", toChars()); - else - error("CTFE internal error: cannot evaluate %s at compile time", toChars()); - return EXP_CANT_INTERPRET; - } - if (!fd) - { - error("cannot evaluate %s at compile time", toChars()); - return EXP_CANT_INTERPRET; - } - if (pthis) - { // Member function call - if (pthis->op == TOKcomma) - pthis = pthis->interpret(istate); - if (exceptionOrCantInterpret(pthis)) - return pthis; - // Evaluate 'this' - Expression *oldpthis = pthis; - if (pthis->op != TOKvar) - pthis = pthis->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(pthis)) - return pthis; - if (fd->isVirtual()) - { // Make a virtual function call. - Expression *thisval = pthis; - if (pthis->op == TOKvar) - { - VarDeclaration *vthis = ((VarExp*)thisval)->var->isVarDeclaration(); - assert(vthis); - thisval = getVarExp(loc, istate, vthis, ctfeNeedLvalue); - if (exceptionOrCantInterpret(thisval)) - return thisval; - // If it is a reference, resolve it - if (thisval->op != TOKnull && thisval->op != TOKclassreference) - thisval = pthis->interpret(istate); - } - else if (pthis->op == TOKsymoff) - { - VarDeclaration *vthis = ((SymOffExp*)thisval)->var->isVarDeclaration(); - assert(vthis); - thisval = getVarExp(loc, istate, vthis, ctfeNeedLvalue); - if (exceptionOrCantInterpret(thisval)) - return thisval; - } - - // Get the function from the vtable of the original class - ClassDeclaration *cd; - if (thisval && thisval->op == TOKnull) - { - error("function call through null class reference %s", pthis->toChars()); - return EXP_CANT_INTERPRET; + uinteger_t ilwr = lwr->toInteger(); + uinteger_t iupr = upr->toInteger(); + if (e1->op == TOKnull) + { + if (ilwr== 0 && iupr == 0) + { + result = e1; + return; } - if (oldpthis->op == TOKsuper) - { assert(oldpthis->type->ty == Tclass); - cd = ((TypeClass *)oldpthis->type)->sym; + e1->error("slice [%llu..%llu] is out of bounds", ilwr, iupr); + result = EXP_CANT_INTERPRET; + return; + } + if (e1->op == TOKslice) + { + SliceExp *se = (SliceExp *)e1; + // Simplify slice of slice: + // aggregate[lo1..up1][lwr..upr] ---> aggregate[lwr'..upr'] + uinteger_t lo1 = se->lwr->toInteger(); + uinteger_t up1 = se->upr->toInteger(); + if (ilwr > iupr || iupr > up1 - lo1) + { + e->error("slice[%llu..%llu] exceeds array bounds[%llu..%llu]", + ilwr, iupr, lo1, up1); + result = EXP_CANT_INTERPRET; + return; } - else + ilwr += lo1; + iupr += lo1; + result = new SliceExp(e->loc, se->e1, + new IntegerExp(e->loc, ilwr, lwr->type), + new IntegerExp(e->loc, iupr, upr->type)); + result->type = e->type; + return; + } + if (e1->op == TOKarrayliteral + || e1->op == TOKstring) + { + if (iupr < ilwr || iupr > dollar) { - assert(thisval && thisval->op == TOKclassreference); - cd = ((ClassReferenceExp *)thisval)->originalClass(); + e->error("slice [%lld..%lld] exceeds array bounds [0..%lld]", + ilwr, iupr, dollar); + result = EXP_CANT_INTERPRET; + return; } - // We can't just use the vtable index to look it up, because - // vtables for interfaces don't get populated until the glue layer. - fd = cd->findFunc(fd->ident, (TypeFunction *)fd->type); - - assert(fd); } + result = new SliceExp(e->loc, e1, lwr, upr); + result->type = e->type; } - if (fd && fd->semanticRun >= PASSsemantic3done && fd->semantic3Errors) - { - error("CTFE failed because of previous errors in %s", fd->toChars()); - return EXP_CANT_INTERPRET; - } - // Check for built-in functions - Expression *eresult = evaluateIfBuiltin(istate, loc, fd, arguments, pthis); - if (eresult) - return eresult; - // Inline .dup. Special case because it needs the return type. - if (!pthis && fd->ident == Id::adDup && arguments && arguments->dim == 2) + void visit(InExp *e) { - e = (*arguments)[1]; - e = e->interpret(istate); - if (exceptionOrCantInterpret(e)) - return e; - if (e != EXP_CANT_INTERPRET) + #if LOG + printf("%s InExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + Expression *e1 = e->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) { - if (e->op == TOKslice) - e= resolveSlice(e); - e = paintTypeOntoLiteral(type, copyLiteral(e)); + result = e1; + return; } - return e; - } - if (fd->dArrayOp) - return fd->dArrayOp->interpret(istate, arguments, pthis); - if (!fd->fbody) - { - error("%s cannot be interpreted at compile time," - " because it has no available source code", fd->toChars()); - return EXP_CANT_INTERPRET; - } - eresult = fd->interpret(istate, arguments, pthis); - if (eresult == EXP_CANT_INTERPRET) - { - // Print a stack trace. - if (!global.gag) - showCtfeBackTrace(istate, this, fd); - } - else if (eresult == EXP_VOID_INTERPRET) - ; - else if (eresult->op != TOKthrownexception) - { - eresult = paintTypeOntoLiteral(type, eresult); - eresult->loc = loc; + Expression *e2 = e->e2->interpret(istate); + if (exceptionOrCantInterpret(e2)) + { + result = e2; + return; + } + if (e2->op == TOKnull) + { + result = new NullExp(e->loc, e->type); + return; + } + if (e2->op != TOKassocarrayliteral) + { + e->error(" %s cannot be interpreted at compile time", e->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + if (e1->op == TOKslice) + e1 = resolveSlice(e1); + result = findKeyInAA(e->loc, (AssocArrayLiteralExp *)e2, e1); + if (exceptionOrCantInterpret(result)) + return; + if (!result) + { + result = new NullExp(e->loc, e->type); + return; + } + result = new IndexExp(e->loc, e2, e1); + result->type = e->type; } - return eresult; -} - -Expression *CommaExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s CommaExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - CommaExp * firstComma = this; - while (firstComma->e1->op == TOKcomma) - firstComma = (CommaExp *)firstComma->e1; - - // If it creates a variable, and there's no context for - // the variable to be created in, we need to create one now. - InterState istateComma; - if (!istate && firstComma->e1->op == TOKdeclaration) + void visit(CatExp *e) { - ctfeStack.startFrame(NULL); - istate = &istateComma; + #if LOG + printf("%s CatExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + Expression *e1 = e->e1->interpret(istate); + if (exceptionOrCantInterpret(e1)) + { + result = e1; + return; + } + if (e1->op == TOKslice) + { + e1 = resolveSlice(e1); + } + Expression *e2 = e->e2->interpret(istate); + if (exceptionOrCantInterpret(e2)) + { + result = e2; + return; + } + if (e2->op == TOKslice) + e2 = resolveSlice(e2); + result = ctfeCat(e->type, e1, e2); + if (result == EXP_CANT_INTERPRET) + { + e->error("%s cannot be interpreted at compile time", e->toChars()); + return; + } + // We know we still own it, because we interpreted both e1 and e2 + if (result->op == TOKarrayliteral) + ((ArrayLiteralExp *)result)->ownedByCtfe = true; + if (result->op == TOKstring) + ((StringExp *)result)->ownedByCtfe = true; } - Expression *e = EXP_CANT_INTERPRET; - // If the comma returns a temporary variable, it needs to be an lvalue - // (this is particularly important for struct constructors) - if (e1->op == TOKdeclaration && e2->op == TOKvar - && ((DeclarationExp *)e1)->declaration == ((VarExp*)e2)->var - && ((VarExp*)e2)->var->storage_class & STCctfe) // same as Expression::isTemp + void visit(CastExp *e) { - VarExp* ve = (VarExp *)e2; - VarDeclaration *v = ve->var->isVarDeclaration(); - ctfeStack.push(v); - if (!v->init && !v->getValue()) + #if LOG + printf("%s CastExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + Expression *e1 = e->e1->interpret(istate, goal); + if (exceptionOrCantInterpret(e1)) { - v->setValue(copyLiteral(v->type->defaultInitLiteral(loc))); + result = e1; + return; } - if (!v->getValue()) { - Expression *newval = v->init->toExpression(); - // Bug 4027. Copy constructors are a weird case where the - // initializer is a void function (the variable is modified - // through a reference parameter instead). - newval = newval->interpret(istate); - if (exceptionOrCantInterpret(newval)) + // If the expression has been cast to void, do nothing. + if (e->to->ty == Tvoid && goal == ctfeNeedNothing) + { + result = e1; + return; + } + if (e->to->ty == Tpointer && e1->op != TOKnull) + { + Type *pointee = ((TypePointer *)e->type)->next; + // Implement special cases of normally-unsafe casts + if (e1->op == TOKint64) { - if (istate == &istateComma) - ctfeStack.endFrame(); - return newval; + // Happens with Windows HANDLEs, for example. + result = paintTypeOntoLiteral(e->to, e1); + return; } - if (newval != EXP_VOID_INTERPRET) + bool castBackFromVoid = false; + if (e1->type->ty == Tarray || e1->type->ty == Tsarray || e1->type->ty == Tpointer) { - // v isn't necessarily null. - v->setValueWithoutChecking(copyLiteral(newval)); + // Check for unsupported type painting operations + // For slices, we need the type being sliced, + // since it may have already been type painted + Type *elemtype = e1->type->nextOf(); + if (e1->op == TOKslice) + elemtype = ((SliceExp *)e1)->e1->type->nextOf(); + // Allow casts from X* to void *, and X** to void** for any X. + // But don't allow cast from X* to void**. + // So, we strip all matching * from source and target to find X. + // Allow casts to X* from void* only if the 'void' was originally an X; + // we check this later on. + Type *ultimatePointee = pointee; + Type *ultimateSrc = elemtype; + while (ultimatePointee->ty == Tpointer && ultimateSrc->ty == Tpointer) + { + ultimatePointee = ultimatePointee->nextOf(); + ultimateSrc = ultimateSrc->nextOf(); + } + if (ultimatePointee->ty != Tvoid && ultimateSrc->ty != Tvoid + && !isSafePointerCast(elemtype, pointee)) + { + e->error("reinterpreting cast from %s* to %s* is not supported in CTFE", + elemtype->toChars(), pointee->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + if (ultimateSrc->ty == Tvoid) + castBackFromVoid = true; } - } - if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) - e = e2; - else - e = e2->interpret(istate, goal); - } - else - { - e = e1->interpret(istate, ctfeNeedNothing); - if (!exceptionOrCantInterpret(e)) - e = e2->interpret(istate, goal); - } - // If we created a temporary stack frame, end it now. - if (istate == &istateComma) - ctfeStack.endFrame(); - return e; -} - -Expression *CondExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s CondExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - Expression *e; - if ( isPointer(econd->type) ) - { - e = econd->interpret(istate); - if (exceptionOrCantInterpret(e)) - return e; - if (e->op != TOKnull) - e = new IntegerExp(loc, 1, Type::tbool); - } - else - e = econd->interpret(istate); - if (exceptionOrCantInterpret(e)) - return e; - if (isTrueBool(e)) - e = e1->interpret(istate, goal); - else if (e->isBool(false)) - e = e2->interpret(istate, goal); - else - { - error("%s does not evaluate to boolean result at compile time", - econd->toChars()); - e = EXP_CANT_INTERPRET; - } - return e; -} - -Expression *ArrayLengthExp::interpret(InterState *istate, CtfeGoal goal) -{ Expression *e; - Expression *e1; - -#if LOG - printf("%s ArrayLengthExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - e1 = this->e1->interpret(istate); - assert(e1); - if (exceptionOrCantInterpret(e1)) - return e1; - if (e1->op == TOKstring || e1->op == TOKarrayliteral || e1->op == TOKslice - || e1->op == TOKassocarrayliteral || e1->op == TOKnull) - { - e = new IntegerExp(loc, resolveArrayLength(e1), type); - } - else - { - error("%s cannot be evaluated at compile time", toChars()); - return EXP_CANT_INTERPRET; - } - return e; -} + if (e1->op == TOKslice) + { + if ( ((SliceExp *)e1)->e1->op == TOKnull) + { + result = paintTypeOntoLiteral(e->type, ((SliceExp *)e1)->e1); + return; + } + result = new IndexExp(e->loc, ((SliceExp *)e1)->e1, ((SliceExp *)e1)->lwr); + result->type = e->type; + return; + } + if (e1->op == TOKarrayliteral || e1->op == TOKstring) + { + result = new IndexExp(e->loc, e1, new IntegerExp(e->loc, 0, Type::tsize_t)); + result->type = e->type; + return; + } + if (e1->op == TOKindex && !((IndexExp *)e1)->e1->type->equals(e1->type)) + { + // type painting operation + IndexExp *ie = (IndexExp *)e1; + result = new IndexExp(e1->loc, ie->e1, ie->e2); + if (castBackFromVoid) + { + // get the original type. For strings, it's just the type... + Type *origType = ie->e1->type->nextOf(); + // ..but for arrays of type void*, it's the type of the element + Expression *xx = NULL; + if (ie->e1->op == TOKarrayliteral && ie->e2->op == TOKint64) + { + ArrayLiteralExp *ale = (ArrayLiteralExp *)ie->e1; + size_t indx = (size_t)ie->e2->toInteger(); + if (indx < ale->elements->dim) + xx = (*ale->elements)[indx]; + } + if (xx && xx->op == TOKindex) + origType = ((IndexExp *)xx)->e1->type->nextOf(); + else if (xx && xx->op == TOKaddress) + origType= ((AddrExp *)xx)->e1->type; + else if (xx && xx->op == TOKvar) + origType = ((VarExp *)xx)->var->type; + if (!isSafePointerCast(origType, pointee)) + { + e->error("using void* to reinterpret cast from %s* to %s* is not supported in CTFE", + origType->toChars(), pointee->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + } + result->type = e->type; + return; + } + if (e1->op == TOKaddress) + { + Type *origType = ((AddrExp *)e1)->e1->type; + if (isSafePointerCast(origType, pointee)) + { + result = new AddrExp(e->loc, ((AddrExp *)e1)->e1); + result->type = e->type; + return; + } + } + if (e1->op == TOKvar || e1->op == TOKsymoff) + { + // type painting operation + Type *origType = (e1->op == TOKvar) ? ((VarExp *)e1)->var->type : + ((SymOffExp *)e1)->var->type; + if (castBackFromVoid && !isSafePointerCast(origType, pointee)) + { + e->error("using void* to reinterpret cast from %s* to %s* is not supported in CTFE", + origType->toChars(), pointee->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + if (e1->op == TOKvar) + result = new VarExp(e->loc, ((VarExp *)e1)->var); + else + result = new SymOffExp(e->loc, ((SymOffExp *)e1)->var, ((SymOffExp *)e1)->offset); + result->type = e->to; + return; + } -Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal) -{ - Expression *e1 = NULL; - Expression *e2; + // Check if we have a null pointer (eg, inside a struct) + e1 = e1->interpret(istate); + if (e1->op != TOKnull) + { + e->error("pointer cast from %s to %s is not supported at compile time", + e1->type->toChars(), e->to->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + } + if (e->to->ty == Tarray && e1->op == TOKslice) + { + // Note that the slice may be void[], so when checking for dangerous + // casts, we need to use the original type, which is se->e1. + SliceExp *se = (SliceExp *)e1; + if (!isSafePointerCast(se->e1->type->nextOf(), e->to->nextOf())) + { + e->error("array cast from %s to %s is not supported at compile time", + se->e1->type->toChars(), e->to->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + e1 = new SliceExp(e1->loc, se->e1, se->lwr, se->upr); + e1->type = e->to; + result = e1; + return; + } + // Disallow array type painting, except for conversions between built-in + // types of identical size. + if ((e->to->ty == Tsarray || e->to->ty == Tarray) && + (e1->type->ty == Tsarray || e1->type->ty == Tarray) && + !isSafePointerCast(e1->type->nextOf(), e->to->nextOf()) ) + { + e->error("array cast from %s to %s is not supported at compile time", e1->type->toChars(), e->to->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + if (e->to->ty == Tsarray && e1->op == TOKslice) + e1 = resolveSlice(e1); + if (e->to->toBasetype()->ty == Tbool && e1->type->ty == Tpointer) + { + result = new IntegerExp(e->loc, e1->op != TOKnull, e->to); + return; + } + result = ctfeCast(e->loc, e->type, e->to, e1); + } -#if LOG - printf("%s IndexExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - if (this->e1->type->toBasetype()->ty == Tpointer) + void visit(AssertExp *e) { - // Indexing a pointer. Note that there is no $ in this case. - e1 = this->e1->interpret(istate); + #if LOG + printf("%s AssertExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + Expression *e1 = e->e1->interpret(istate); if (exceptionOrCantInterpret(e1)) - return e1; - e2 = this->e2->interpret(istate); - if (exceptionOrCantInterpret(e2)) - return e2; - sinteger_t indx = e2->toInteger(); + { + result = e1; + return; + } + if (isTrueBool(e1)) + { + } + else if (e1->isBool(false)) + { + if (e->msg) + { + result = e->msg->interpret(istate); + if (exceptionOrCantInterpret(result)) + return; + e->error("%s", result->toChars()); + } + else + e->error("%s failed", e->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + else + { + e->error("%s is not a compile-time boolean expression", e1->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + result = e1; + return; + } - dinteger_t ofs; - Expression *agg = getAggregateFromPointer(e1, &ofs); + void visit(PtrExp *e) + { + #if LOG + printf("%s PtrExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif - if (agg->op == TOKnull) + // Check for int<->float and long<->double casts. + if ( e->e1->op == TOKsymoff && ((SymOffExp *)e->e1)->offset == 0 + && isFloatIntPaint(e->type, ((SymOffExp *)e->e1)->var->type) ) { - error("cannot index null pointer %s", this->e1->toChars()); - return EXP_CANT_INTERPRET; + // *(cast(int*)&v, where v is a float variable + result = paintFloatInt(getVarExp(e->loc, istate, ((SymOffExp *)e->e1)->var, ctfeNeedRvalue), + e->type); + return; } - if ( agg->op == TOKarrayliteral || agg->op == TOKstring) + else if (e->e1->op == TOKcast && ((CastExp *)e->e1)->e1->op == TOKaddress) { - dinteger_t len = ArrayLength(Type::tsize_t, agg)->toInteger(); - //Type *pointee = ((TypePointer *)agg->type)->next; - if ((sinteger_t)(indx + ofs) < 0 || (indx+ofs) > len) + // *(cast(int *))&x where x is a float expression + Expression *x = ((AddrExp *)(((CastExp *)e->e1)->e1))->e1; + if (isFloatIntPaint(e->type, x->type)) { - error("pointer index [%lld] exceeds allocated memory block [0..%lld]", - indx+ofs, len); - return EXP_CANT_INTERPRET; + result = paintFloatInt(x->interpret(istate), e->type); + return; } - if (goal == ctfeNeedLvalueRef) + } + + // Constant fold *(&structliteral + offset) + if (e->e1->op == TOKadd) + { + AddExp *ae = (AddExp *)e->e1; + if (ae->e1->op == TOKaddress && ae->e2->op == TOKint64) { - // if we need a reference, IndexExp shouldn't be interpreting - // the expression to a value, it should stay as a reference - Expression *e = new IndexExp(loc, agg, - ofs ? new IntegerExp(loc,indx + ofs, e2->type) : e2); - e->type = type; - return e; + AddrExp *ade = (AddrExp *)ae->e1; + Expression *ex = ade->e1; + ex = ex->interpret(istate); + if (exceptionOrCantInterpret(ex)) + { + result = ex; + return; + } + if (ex->op == TOKstructliteral) + { + StructLiteralExp *se = (StructLiteralExp *)ex; + dinteger_t offset = ae->e2->toInteger(); + result = se->getField(e->type, (unsigned)offset); + if (!result) + result = EXP_CANT_INTERPRET; + return; + } } - return ctfeIndex(loc, type, agg, indx+ofs); + result = Ptr(e->type, e->e1); } else - { // Pointer to a non-array variable - if (agg->op == TOKsymoff) + { + // Check for .classinfo, which is lowered in the semantic pass into **(class). + if (e->e1->op == TOKstar && e->e1->type->ty == Tpointer && isTypeInfo_Class(e->e1->type->nextOf())) { - error("mutable variable %s cannot be read at compile time, even through a pointer", ((SymOffExp *)agg)->var->toChars()); - return EXP_CANT_INTERPRET; + result = (((PtrExp *)e->e1)->e1)->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(result)) + return; + if (result->op == TOKnull) + { + e->error("Null pointer dereference evaluating typeid. '%s' is null", ((PtrExp *)e->e1)->e1->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + if (result->op != TOKclassreference) + { + e->error("CTFE internal error determining classinfo"); + result = EXP_CANT_INTERPRET; + return; + } + ClassDeclaration *cd = ((ClassReferenceExp *)result)->originalClass(); + assert(cd); + + // Create the classinfo, if it doesn't yet exist. + // TODO: This belongs in semantic, CTFE should not have to do this. + if (!cd->vclassinfo) + cd->vclassinfo = new TypeInfoClassDeclaration(cd->type); + result = new SymOffExp(e->loc, cd->vclassinfo, 0); + result->type = e->type; + return; } - if ((indx + ofs) != 0) + + // It's possible we have an array bounds error. We need to make sure it + // errors with this line number, not the one where the pointer was set. + result = e->e1->interpret(istate); + if (exceptionOrCantInterpret(result)) + return; + + if (!(result->op == TOKvar || result->op == TOKdotvar || result->op == TOKindex + || result->op == TOKslice || result->op == TOKaddress)) { - error("pointer index [%lld] lies outside memory block [0..1]", - indx+ofs); - return EXP_CANT_INTERPRET; + if (result->op == TOKsymoff) + e->error("cannot dereference pointer to static variable %s at compile time", ((SymOffExp *)result)->var->toChars()); + else + e->error("dereference of invalid pointer '%s'", result->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + if (goal != ctfeNeedLvalue && goal != ctfeNeedLvalueRef) + { + if (result->op == TOKindex && result->type->ty == Tpointer) + { + IndexExp *ie = (IndexExp *)result; + // Is this a real index to an array of pointers, or just a CTFE pointer? + // If the index has the same levels of indirection, it's an index + int srcLevels = 0; + int destLevels = 0; + for(Type *xx = ie->e1->type; xx->ty == Tpointer; xx = xx->nextOf()) + ++srcLevels; + for(Type *xx = result->type->nextOf(); xx->ty == Tpointer; xx = xx->nextOf()) + ++destLevels; + bool isGenuineIndex = (srcLevels == destLevels); + + if ((ie->e1->op == TOKarrayliteral || ie->e1->op == TOKstring) + && ie->e2->op == TOKint64) + { + Expression *dollar = ArrayLength(Type::tsize_t, ie->e1); + dinteger_t len = dollar->toInteger(); + dinteger_t indx = ie->e2->toInteger(); + assert(indx >=0 && indx <= len); // invalid pointer + if (indx == len) + { + e->error("dereference of pointer %s one past end of memory block limits [0..%lld]", + e->toChars(), len); + result = EXP_CANT_INTERPRET; + return; + } + result = ctfeIndex(e->loc, e->type, ie->e1, indx); + if (isGenuineIndex) + { + if (result->op == TOKindex) + result = result->interpret(istate, goal); + else if (result->op == TOKaddress) + result = paintTypeOntoLiteral(e->type, ((AddrExp *)result)->e1); + } + return; + } + if (ie->e1->op == TOKassocarrayliteral) + { + result = findKeyInAA(e->loc, (AssocArrayLiteralExp *)ie->e1, ie->e2); + assert(result != EXP_CANT_INTERPRET); + result = paintTypeOntoLiteral(e->type, result); + if (isGenuineIndex) + { + if (result->op == TOKindex) + result = result->interpret(istate, goal); + else if (result->op == TOKaddress) + result = paintTypeOntoLiteral(e->type, ((AddrExp *)result)->e1); + } + return; + } + } + if (result->op == TOKstructliteral) + return; + + if (result->op == TOKaddress) + { + // We're changing *&e to e. + result = ((AddrExp *)result)->e1; + } + result = result->interpret(istate, goal); + if (exceptionOrCantInterpret(result)) + return; + } + else if (result->op == TOKaddress) + { + result = ((AddrExp*)result)->e1; // *(&x) ==> x } - if (goal == ctfeNeedLvalueRef) + else if (result->op == TOKnull) { - return paintTypeOntoLiteral(type, agg); + e->error("dereference of null pointer '%s'", e->e1->toChars()); + result = EXP_CANT_INTERPRET; + return; } - return agg->interpret(istate); + result = paintTypeOntoLiteral(e->type, result); } - } - e1 = this->e1; - if (!(e1->op == TOKarrayliteral && ((ArrayLiteralExp *)e1)->ownedByCtfe) && - !(e1->op == TOKassocarrayliteral && ((AssocArrayLiteralExp *)e1)->ownedByCtfe)) - e1 = e1->interpret(istate); - if (exceptionOrCantInterpret(e1)) - return e1; - if (e1->op == TOKnull) - { - if (goal == ctfeNeedLvalue && e1->type->ty == Taarray && modifiable) - return paintTypeOntoLiteral(type, e1); - error("cannot index null array %s", this->e1->toChars()); - return EXP_CANT_INTERPRET; - } - /* Set the $ variable. - * Note that foreach uses indexing but doesn't need $ - */ - if (lengthVar && (e1->op == TOKstring || e1->op == TOKarrayliteral - || e1->op == TOKslice)) - { - uinteger_t dollar = resolveArrayLength(e1); - Expression *dollarExp = new IntegerExp(loc, dollar, Type::tsize_t); - ctfeStack.push(lengthVar); - lengthVar->setValue(dollarExp); + #if LOG + if (result == EXP_CANT_INTERPRET) + printf("PtrExp::interpret() %s = EXP_CANT_INTERPRET\n", e->toChars()); + #endif } - e2 = this->e2->interpret(istate); - if (lengthVar) - ctfeStack.pop(lengthVar); // $ is defined only inside [] - if (exceptionOrCantInterpret(e2)) - return e2; - if (e1->op == TOKslice && e2->op == TOKint64) + void visit(DotVarExp *e) { - // Simplify index of slice: agg[lwr..upr][indx] --> agg[indx'] - uinteger_t indx = e2->toInteger(); - uinteger_t ilo = ((SliceExp *)e1)->lwr->toInteger(); - uinteger_t iup = ((SliceExp *)e1)->upr->toInteger(); + #if LOG + printf("%s DotVarExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif - if (indx > iup - ilo) - { - error("index %llu exceeds array length %llu", indx, iup - ilo); - return EXP_CANT_INTERPRET; - } - indx += ilo; - e1 = ((SliceExp *)e1)->e1; - e2 = new IntegerExp(e2->loc, indx, e2->type); - } - Expression *e = NULL; - if ((goal == ctfeNeedLvalue && type->ty != Taarray && type->ty != Tarray - && type->ty != Tsarray && type->ty != Tstruct && type->ty != Tclass) - || (goal == ctfeNeedLvalueRef && type->ty != Tsarray && type->ty != Tstruct) - ) - { // Pointer or reference of a scalar type - e = new IndexExp(loc, e1, e2); - e->type = type; - return e; - } - if (e1->op == TOKassocarrayliteral) - { - if (e2->op == TOKslice) - e2 = resolveSlice(e2); - e = findKeyInAA(loc, (AssocArrayLiteralExp *)e1, e2); - if (!e) + result = EXP_CANT_INTERPRET; + Expression *ex = e->e1->interpret(istate); + if (exceptionOrCantInterpret(ex)) { - error("key %s not found in associative array %s", - e2->toChars(), this->e1->toChars()); - return EXP_CANT_INTERPRET; + result = ex; + return; } - } - else - { - if (e2->op != TOKint64) + if (ex != EXP_CANT_INTERPRET) { - e1->error("CTFE internal error: non-integral index [%s]", this->e2->toChars()); - return EXP_CANT_INTERPRET; - } - e = ctfeIndex(loc, type, e1, e2->toInteger()); - } - if (exceptionOrCantInterpret(e)) - return e; - if (goal == ctfeNeedRvalue && (e->op == TOKslice || e->op == TOKdotvar)) - e = e->interpret(istate); - if (goal == ctfeNeedRvalue && e->op == TOKvoid) - { - error("%s is used before initialized", toChars()); - errorSupplemental(e->loc, "originally uninitialized here"); - return EXP_CANT_INTERPRET; - } - e = paintTypeOntoLiteral(type, e); - return e; -} - + if (ex->op == TOKaddress) + ex = ((AddrExp *)ex)->e1; -Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal) -{ - Expression *e1; - Expression *lwr; - Expression *upr; + VarDeclaration *v = e->var->isVarDeclaration(); + if (!v) + { + e->error("CTFE internal error: %s", e->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + if (ex->op == TOKnull) + { + if (ex->type->toBasetype()->ty == Tclass) + e->error("class '%s' is null and cannot be dereferenced", e->e1->toChars()); + else + e->error("dereference of null pointer '%s'", e->e1->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + if (ex->op == TOKstructliteral || ex->op == TOKclassreference) + { + StructLiteralExp *se; + int i; -#if LOG - printf("%s SliceExp::interpret() %s\n", loc.toChars(), toChars()); -#endif + // We can't use getField, because it makes a copy + if (ex->op == TOKclassreference) + { + se = ((ClassReferenceExp *)ex)->value; + i = ((ClassReferenceExp *)ex)->findFieldIndexByName(v); + } + else + { + se = (StructLiteralExp *)ex; + i = findFieldIndexByName(se->sd, v); + } + if (i == -1) + { + e->error("couldn't find field %s of type %s in %s", v->toChars(), e->type->toChars(), se->toChars()); + result = EXP_CANT_INTERPRET; + return; + } + result = (*se->elements)[i]; + if (!result) + { + e->error("Internal Compiler Error: Null field %s", v->toChars()); + result = EXP_CANT_INTERPRET; + return; + } - if (this->e1->type->toBasetype()->ty == Tpointer) - { - // Slicing a pointer. Note that there is no $ in this case. - e1 = this->e1->interpret(istate); - if (exceptionOrCantInterpret(e1)) - return e1; - if (e1->op == TOKint64) - { - error("cannot slice invalid pointer %s of value %s", - this->e1->toChars(), e1->toChars()); - return EXP_CANT_INTERPRET; + if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) + { + // If it is an lvalue literal, return it... + if (result->op == TOKstructliteral) + return; + if ((e->type->ty == Tsarray || goal == ctfeNeedLvalue) && ( + result->op == TOKarrayliteral || + result->op == TOKassocarrayliteral || result->op == TOKstring || + result->op == TOKclassreference || result->op == TOKslice)) + return; + /* Element is an allocated pointer, which was created in + * CastExp. + */ + if (goal == ctfeNeedLvalue && result->op == TOKindex && + result->type->equals(e->type) && + isPointer(e->type) ) + return; + // ...Otherwise, just return the (simplified) dotvar expression + result = new DotVarExp(e->loc, ex, v); + result->type = e->type; + return; + } + // If it is an rvalue literal, return it... + if (result->op == TOKstructliteral || result->op == TOKarrayliteral || + result->op == TOKassocarrayliteral || result->op == TOKstring) + return; + if (result->op == TOKvoid) + { + VoidInitExp *ve = (VoidInitExp *)result; + const char *s = ve->var->toChars(); + if (v->overlapped) + { + e->error("Reinterpretation through overlapped field %s is not allowed in CTFE", s); + result = EXP_CANT_INTERPRET; + return; + } + e->error("cannot read uninitialized variable %s in CTFE", s); + result = EXP_CANT_INTERPRET; + return; + } + if (isPointer(e->type)) + { + result = paintTypeOntoLiteral(e->type, result); + return; + } + if (result->op == TOKvar) + { + // Don't typepaint twice, since that might cause an erroneous copy + result = getVarExp(e->loc, istate, ((VarExp *)result)->var, goal); + if (result != EXP_CANT_INTERPRET && result->op != TOKthrownexception) + result = paintTypeOntoLiteral(e->type, result); + return; + } + result = result->interpret(istate, goal); + return; + } + else + e->error("%s.%s is not yet implemented at compile time", e->e1->toChars(), e->var->toChars()); } - /* Evaluate lower and upper bounds of slice - */ - lwr = this->lwr->interpret(istate); - if (exceptionOrCantInterpret(lwr)) - return lwr; - upr = this->upr->interpret(istate); - if (exceptionOrCantInterpret(upr)) - return upr; - uinteger_t ilwr; - uinteger_t iupr; - ilwr = lwr->toInteger(); - iupr = upr->toInteger(); - Expression *e; - dinteger_t ofs; - Expression *agg = getAggregateFromPointer(e1, &ofs); - ilwr += ofs; - iupr += ofs; - if (agg->op == TOKnull) + #if LOG + if (result == EXP_CANT_INTERPRET) + printf("DotVarExp::interpret() %s = EXP_CANT_INTERPRET\n", e->toChars()); + #endif + } + + void visit(RemoveExp *e) + { + #if LOG + printf("%s RemoveExp::interpret() %s\n", e->loc.toChars(), e->toChars()); + #endif + Expression *agg = e->e1->interpret(istate); + if (exceptionOrCantInterpret(agg)) { - if (iupr == ilwr) - { - e = new NullExp(loc); - e->type = type; - return e; - } - error("cannot slice null pointer %s", this->e1->toChars()); - return EXP_CANT_INTERPRET; + result = agg; + return; } - if (agg->op == TOKsymoff) + Expression *index = e->e2->interpret(istate); + if (exceptionOrCantInterpret(index)) { - error("slicing pointers to static variables is not supported in CTFE"); - return EXP_CANT_INTERPRET; + result = index; + return; } - if (agg->op != TOKarrayliteral && agg->op != TOKstring) + if (agg->op == TOKnull) { - error("pointer %s cannot be sliced at compile time (it does not point to an array)", - this->e1->toChars()); - return EXP_CANT_INTERPRET; + result = EXP_VOID_INTERPRET; + return; } - assert(agg->op == TOKarrayliteral || agg->op == TOKstring); - dinteger_t len = ArrayLength(Type::tsize_t, agg)->toInteger(); - //Type *pointee = ((TypePointer *)agg->type)->next; - if (iupr > (len + 1) || iupr < ilwr) + assert(agg->op == TOKassocarrayliteral); + AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)agg; + Expressions *keysx = aae->keys; + Expressions *valuesx = aae->values; + size_t removed = 0; + for (size_t j = 0; j < valuesx->dim; ++j) { - error("pointer slice [%lld..%lld] exceeds allocated memory block [0..%lld]", - ilwr, iupr, len); - return EXP_CANT_INTERPRET; - } - if (ofs != 0) - { lwr = new IntegerExp(loc, ilwr, lwr->type); - upr = new IntegerExp(loc, iupr, upr->type); + Expression *ekey = (*keysx)[j]; + int eq = ctfeEqual(e->loc, TOKequal, ekey, index); + if (eq) + ++removed; + else if (removed != 0) + { + (*keysx)[j - removed] = ekey; + (*valuesx)[j - removed] = (*valuesx)[j]; + } } - e = new SliceExp(loc, agg, lwr, upr); - e->type = type; - return e; + valuesx->dim = valuesx->dim - removed; + keysx->dim = keysx->dim - removed; + result = new IntegerExp(e->loc, removed ? 1 : 0, Type::tbool); } - if (goal == ctfeNeedRvalue && this->e1->op == TOKstring) - e1 = this->e1; // Will get duplicated anyway - else - e1 = this->e1->interpret(istate); - if (exceptionOrCantInterpret(e1)) - return e1; - if (e1->op == TOKvar) - e1 = e1->interpret(istate); - if (!this->lwr) + void visit(ClassReferenceExp *e) { - if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) - return e1; - return paintTypeOntoLiteral(type, e1); + //printf("ClassReferenceExp::interpret() %s\n", e->value->toChars()); + result = e; } - /* Set the $ variable - */ - if (e1->op != TOKarrayliteral && e1->op != TOKstring && - e1->op != TOKnull && e1->op != TOKslice) - { - error("Cannot determine length of %s at compile time", e1->toChars()); - return EXP_CANT_INTERPRET; - } - uinteger_t dollar = resolveArrayLength(e1); - if (lengthVar) + void visit(VoidInitExp *e) { - IntegerExp *dollarExp = new IntegerExp(loc, dollar, Type::tsize_t); - ctfeStack.push(lengthVar); - lengthVar->setValue(dollarExp); + e->error("CTFE internal error: trying to read uninitialized variable"); + assert(0); + result = EXP_CANT_INTERPRET; } - /* Evaluate lower and upper bounds of slice - */ - lwr = this->lwr->interpret(istate); - if (exceptionOrCantInterpret(lwr)) - { - if (lengthVar) - ctfeStack.pop(lengthVar);; // $ is defined only inside [L..U] - return lwr; - } - upr = this->upr->interpret(istate); - if (lengthVar) - ctfeStack.pop(lengthVar); // $ is defined only inside [L..U] - if (exceptionOrCantInterpret(upr)) - return upr; - - Expression *e; - uinteger_t ilwr; - uinteger_t iupr; - ilwr = lwr->toInteger(); - iupr = upr->toInteger(); - if (e1->op == TOKnull) - { - if (ilwr== 0 && iupr == 0) - return e1; - e1->error("slice [%llu..%llu] is out of bounds", ilwr, iupr); - return EXP_CANT_INTERPRET; - } - if (e1->op == TOKslice) + void visit(ThrownExceptionExp *e) { - SliceExp *se = (SliceExp *)e1; - // Simplify slice of slice: - // aggregate[lo1..up1][lwr..upr] ---> aggregate[lwr'..upr'] - uinteger_t lo1 = se->lwr->toInteger(); - uinteger_t up1 = se->upr->toInteger(); - if (ilwr > iupr || iupr > up1 - lo1) - { - error("slice[%llu..%llu] exceeds array bounds[%llu..%llu]", - ilwr, iupr, lo1, up1); - return EXP_CANT_INTERPRET; - } - ilwr += lo1; - iupr += lo1; - e = new SliceExp(loc, se->e1, - new IntegerExp(loc, ilwr, lwr->type), - new IntegerExp(loc, iupr, upr->type)); - e->type = type; - return e; - } - if (e1->op == TOKarrayliteral - || e1->op == TOKstring) - { - if (iupr < ilwr || iupr > dollar) - { - error("slice [%lld..%lld] exceeds array bounds [0..%lld]", - ilwr, iupr, dollar); - return EXP_CANT_INTERPRET; - } + assert(0); // This should never be interpreted + result = e; } - e = new SliceExp(loc, e1, lwr, upr); - e->type = type; - return e; -} -Expression *InExp::interpret(InterState *istate, CtfeGoal goal) -{ Expression *e = EXP_CANT_INTERPRET; +}; -#if LOG - printf("%s InExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - Expression *e1 = this->e1->interpret(istate); - if (exceptionOrCantInterpret(e1)) - return e1; - Expression *e2 = this->e2->interpret(istate); - if (exceptionOrCantInterpret(e2)) - return e2; - if (e2->op == TOKnull) - return new NullExp(loc, type); - if (e2->op != TOKassocarrayliteral) - { - error(" %s cannot be interpreted at compile time", toChars()); - return EXP_CANT_INTERPRET; - } - if (e1->op == TOKslice) - e1 = resolveSlice(e1); - e = findKeyInAA(loc, (AssocArrayLiteralExp *)e2, e1); - if (exceptionOrCantInterpret(e)) - return e; - if (!e) - return new NullExp(loc, type); - e = new IndexExp(loc, e2, e1); - e->type = type; - return e; +Expression *interpret(Expression *e, InterState *istate, CtfeGoal goal) +{ + Interpreter v(istate, goal); + e->accept(&v); + return v.result; } -Expression *CatExp::interpret(InterState *istate, CtfeGoal goal) -{ Expression *e; - Expression *e1; - Expression *e2; +/*********************************** + * Interpret the statement. + * Returns: + * NULL continue to next statement + * EXP_CANT_INTERPRET cannot interpret statement at compile time + * !NULL expression from return statement, or thrown exception + */ -#if LOG - printf("%s CatExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - e1 = this->e1->interpret(istate); - if (exceptionOrCantInterpret(e1)) - return e1; - if (e1->op == TOKslice) - { - e1 = resolveSlice(e1); - } - e2 = this->e2->interpret(istate); - if (exceptionOrCantInterpret(e2)) - return e2; - if (e2->op == TOKslice) - e2 = resolveSlice(e2); - e = ctfeCat(type, e1, e2); - if (e == EXP_CANT_INTERPRET) - { error("%s cannot be interpreted at compile time", toChars()); - return e; - } - // We know we still own it, because we interpreted both e1 and e2 - if (e->op == TOKarrayliteral) - ((ArrayLiteralExp *)e)->ownedByCtfe = true; - if (e->op == TOKstring) - ((StringExp *)e)->ownedByCtfe = true; - return e; +Expression *interpret(Statement *s, InterState *istate) +{ + Interpreter v(istate, ctfeNeedNothing); + s->accept(&v); + return v.result; } +bool scrubArray(Loc loc, Expressions *elems, bool structlit = false); -Expression *CastExp::interpret(InterState *istate, CtfeGoal goal) -{ Expression *e; - Expression *e1; - -#if LOG - printf("%s CastExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - e1 = this->e1->interpret(istate, goal); - if (exceptionOrCantInterpret(e1)) - return e1; - // If the expression has been cast to void, do nothing. - if (to->ty == Tvoid && goal == ctfeNeedNothing) - return e1; - if (to->ty == Tpointer && e1->op != TOKnull) - { - Type *pointee = ((TypePointer *)type)->next; - // Implement special cases of normally-unsafe casts -#if DMDV2 - if (pointee->ty == Taarray && e1->op == TOKaddress - && isAssocArray(((AddrExp*)e1)->e1->type)) - { // cast from template AA pointer to true AA pointer is OK. - return paintTypeOntoLiteral(to, e1); - } -#endif - if (e1->op == TOKint64) - { // Happens with Windows HANDLEs, for example. - return paintTypeOntoLiteral(to, e1); - } - bool castBackFromVoid = false; - if (e1->type->ty == Tarray || e1->type->ty == Tsarray || e1->type->ty == Tpointer) - { - // Check for unsupported type painting operations - // For slices, we need the type being sliced, - // since it may have already been type painted - Type *elemtype = e1->type->nextOf(); - if (e1->op == TOKslice) - elemtype = ((SliceExp *)e1)->e1->type->nextOf(); - // Allow casts from X* to void *, and X** to void** for any X. - // But don't allow cast from X* to void**. - // So, we strip all matching * from source and target to find X. - // Allow casts to X* from void* only if the 'void' was originally an X; - // we check this later on. - Type *ultimatePointee = pointee; - Type *ultimateSrc = elemtype; - while (ultimatePointee->ty == Tpointer && ultimateSrc->ty == Tpointer) - { - ultimatePointee = ultimatePointee->nextOf(); - ultimateSrc = ultimateSrc->nextOf(); - } - if (ultimatePointee->ty != Tvoid && ultimateSrc->ty != Tvoid - && !isSafePointerCast(elemtype, pointee)) - { - error("reinterpreting cast from %s* to %s* is not supported in CTFE", - elemtype->toChars(), pointee->toChars()); - return EXP_CANT_INTERPRET; - } - if (ultimateSrc->ty == Tvoid) - castBackFromVoid = true; - } - - if (e1->op == TOKslice) - { - if ( ((SliceExp *)e1)->e1->op == TOKnull) - { - return paintTypeOntoLiteral(type, ((SliceExp *)e1)->e1); - } - e = new IndexExp(loc, ((SliceExp *)e1)->e1, ((SliceExp *)e1)->lwr); - e->type = type; - return e; - } - if (e1->op == TOKarrayliteral || e1->op == TOKstring) - { - e = new IndexExp(loc, e1, new IntegerExp(loc, 0, Type::tsize_t)); - e->type = type; - return e; - } - if (e1->op == TOKindex && !((IndexExp *)e1)->e1->type->equals(e1->type)) - { // type painting operation - IndexExp *ie = (IndexExp *)e1; - e = new IndexExp(e1->loc, ie->e1, ie->e2); - if (castBackFromVoid) - { - // get the original type. For strings, it's just the type... - Type *origType = ie->e1->type->nextOf(); - // ..but for arrays of type void*, it's the type of the element - Expression *xx = NULL; - if (ie->e1->op == TOKarrayliteral && ie->e2->op == TOKint64) - { ArrayLiteralExp *ale = (ArrayLiteralExp *)ie->e1; - size_t indx = (size_t)ie->e2->toInteger(); - if (indx < ale->elements->dim) - xx = (*ale->elements)[indx]; - } - if (xx && xx->op == TOKindex) - origType = ((IndexExp *)xx)->e1->type->nextOf(); - else if (xx && xx->op == TOKaddress) - origType= ((AddrExp *)xx)->e1->type; - else if (xx && xx->op == TOKvar) - origType = ((VarExp *)xx)->var->type; - if (!isSafePointerCast(origType, pointee)) - { - error("using void* to reinterpret cast from %s* to %s* is not supported in CTFE", - origType->toChars(), pointee->toChars()); - return EXP_CANT_INTERPRET; - } - } - e->type = type; - return e; - } - if (e1->op == TOKaddress) - { - Type *origType = ((AddrExp *)e1)->e1->type; - if (isSafePointerCast(origType, pointee)) - { - e = new AddrExp(loc, ((AddrExp *)e1)->e1); - e->type = type; - return e; - } - } - if (e1->op == TOKvar || e1->op == TOKsymoff) - { // type painting operation - Type *origType = (e1->op == TOKvar) ? ((VarExp *)e1)->var->type : - ((SymOffExp *)e1)->var->type; - if (castBackFromVoid && !isSafePointerCast(origType, pointee)) - { - error("using void* to reinterpret cast from %s* to %s* is not supported in CTFE", - origType->toChars(), pointee->toChars()); - return EXP_CANT_INTERPRET; - } - if (e1->op == TOKvar) - e = new VarExp(loc, ((VarExp *)e1)->var); - else - e = new SymOffExp(loc, ((SymOffExp *)e1)->var, ((SymOffExp *)e1)->offset); - e->type = to; - return e; - } - - // Check if we have a null pointer (eg, inside a struct) - e1 = e1->interpret(istate); - if (e1->op != TOKnull) - { - error("pointer cast from %s to %s is not supported at compile time", - e1->type->toChars(), to->toChars()); - return EXP_CANT_INTERPRET; - } - } - if (to->ty == Tarray && e1->op == TOKslice) - { // Note that the slice may be void[], so when checking for dangerous - // casts, we need to use the original type, which is se->e1. - SliceExp *se = (SliceExp *)e1; - if ( !isSafePointerCast( se->e1->type->nextOf(), to->nextOf() ) ) +/* All results destined for use outside of CTFE need to have their CTFE-specific + * features removed. + * In particular, all slices must be resolved. + */ +Expression *scrubReturnValue(Loc loc, Expression *e) +{ + if (e->op == TOKclassreference) + { + StructLiteralExp *se = ((ClassReferenceExp*)e)->value; + se->ownedByCtfe = false; + if (!(se->stageflags & stageScrub)) { - error("array cast from %s to %s is not supported at compile time", - se->e1->type->toChars(), to->toChars()); - return EXP_CANT_INTERPRET; + int old = se->stageflags; + se->stageflags |= stageScrub; + if (!scrubArray(loc, se->elements, true)) + return EXP_CANT_INTERPRET; + se->stageflags = old; } - e1 = new SliceExp(e1->loc, se->e1, se->lwr, se->upr); - e1->type = to; - return e1; } - // Disallow array type painting, except for conversions between built-in - // types of identical size. - if ((to->ty == Tsarray || to->ty == Tarray) && - (e1->type->ty == Tsarray || e1->type->ty == Tarray) && - !isSafePointerCast(e1->type->nextOf(), to->nextOf()) ) + if (e->op == TOKvoid) { - error("array cast from %s to %s is not supported at compile time", e1->type->toChars(), to->toChars()); - return EXP_CANT_INTERPRET; + error(loc, "uninitialized variable '%s' cannot be returned from CTFE", ((VoidInitExp *)e)->var->toChars()); + e = new ErrorExp(); } - if (to->ty == Tsarray && e1->op == TOKslice) - e1 = resolveSlice(e1); - if (to->toBasetype()->ty == Tbool && e1->type->ty==Tpointer) + if (e->op == TOKslice) { - return new IntegerExp(loc, e1->op != TOKnull, to); + e = resolveSlice(e); } - return ctfeCast(loc, type, to, e1); -} - -Expression *AssertExp::interpret(InterState *istate, CtfeGoal goal) -{ Expression *e; - Expression *e1; - -#if LOG - printf("%s AssertExp::interpret() %s\n", loc.toChars(), toChars()); -#endif -#if DMDV2 - e1 = this->e1->interpret(istate); -#else - // Deal with pointers (including compiler-inserted assert(&this, "null this")) - if ( isPointer(this->e1->type) ) + if (e->op == TOKstructliteral) { - e1 = this->e1->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(e1)) - return e1; - if (e1->op != TOKnull) - return new IntegerExp(loc, 1, Type::tbool); + StructLiteralExp *se = (StructLiteralExp *)e; + se->ownedByCtfe = false; + if (!(se->stageflags & stageScrub)) + { + int old = se->stageflags; + se->stageflags |= stageScrub; + if (!scrubArray(loc, se->elements, true)) + return EXP_CANT_INTERPRET; + se->stageflags = old; + } } - else - e1 = this->e1->interpret(istate); -#endif - if (exceptionOrCantInterpret(e1)) - return e1; - if (isTrueBool(e1)) + if (e->op == TOKstring) { + ((StringExp *)e)->ownedByCtfe = false; } - else if (e1->isBool(false)) + if (e->op == TOKarrayliteral) { - if (msg) - { - e = msg->interpret(istate); - if (exceptionOrCantInterpret(e)) - return e; - error("%s", e->toChars()); - } - else - error("%s failed", toChars()); - return EXP_CANT_INTERPRET; + ((ArrayLiteralExp *)e)->ownedByCtfe = false; + if (!scrubArray(loc, ((ArrayLiteralExp *)e)->elements)) + return EXP_CANT_INTERPRET; } - else + if (e->op == TOKassocarrayliteral) { - error("%s is not a compile-time boolean expression", e1->toChars()); - return EXP_CANT_INTERPRET; + AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e; + aae->ownedByCtfe = false; + if (!scrubArray(loc, aae->keys)) + return EXP_CANT_INTERPRET; + if (!scrubArray(loc, aae->values)) + return EXP_CANT_INTERPRET; + aae->type = toBuiltinAAType(aae->type); } - return e1; + return e; } -Expression *PtrExp::interpret(InterState *istate, CtfeGoal goal) -{ Expression *e = EXP_CANT_INTERPRET; - -#if LOG - printf("%s PtrExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - - // Check for int<->float and long<->double casts. - - if ( e1->op == TOKsymoff && ((SymOffExp *)e1)->offset == 0 - && isFloatIntPaint(type, ((SymOffExp *)e1)->var->type) ) - { // *(cast(int*)&v, where v is a float variable - return paintFloatInt(getVarExp(loc, istate, ((SymOffExp *)e1)->var, ctfeNeedRvalue), - type); - } - else if (e1->op == TOKcast && ((CastExp *)e1)->e1->op == TOKaddress) - { // *(cast(int *))&x where x is a float expression - Expression *x = ((AddrExp *)(((CastExp *)e1)->e1))->e1; - if ( isFloatIntPaint(type, x->type) ) - return paintFloatInt(x->interpret(istate), type); - } - - // Constant fold *(&structliteral + offset) - if (e1->op == TOKadd) - { AddExp *ae = (AddExp *)e1; - if (ae->e1->op == TOKaddress && ae->e2->op == TOKint64) - { AddrExp *ade = (AddrExp *)ae->e1; - Expression *ex = ade->e1; - ex = ex->interpret(istate); - if (exceptionOrCantInterpret(ex)) - return ex; - if (ex->op == TOKstructliteral) - { StructLiteralExp *se = (StructLiteralExp *)ex; - dinteger_t offset = ae->e2->toInteger(); - e = se->getField(type, (unsigned)offset); - if (!e) - e = EXP_CANT_INTERPRET; - return e; - } - } - e = Ptr(type, e1); - } - else +// Return true if every element is either void, +// or is an array literal or struct literal of void elements. +bool isEntirelyVoid(Expressions *elems) +{ + for (size_t i = 0; i < elems->dim; i++) { -#if DMDV2 -#else // this is required for D1, where structs return *this instead of 'this'. - if (e1->op == TOKthis) - { - if (ctfeStack.getThis()) - return ctfeStack.getThis()->interpret(istate); - goto Ldone; - } -#endif - // Check for .classinfo, which is lowered in the semantic pass into **(class). - if (e1->op == TOKstar && e1->type->ty == Tpointer && isTypeInfo_Class(e1->type->nextOf())) - { - e = (((PtrExp *)e1)->e1)->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(e)) - return e; - if (e->op == TOKnull) - { - error("Null pointer dereference evaluating typeid. '%s' is null", ((PtrExp *)e1)->e1->toChars()); - return EXP_CANT_INTERPRET; - } - if (e->op != TOKclassreference) - { error("CTFE internal error determining classinfo"); - return EXP_CANT_INTERPRET; - } - ClassDeclaration *cd = ((ClassReferenceExp *)e)->originalClass(); - assert(cd); - - // Create the classinfo, if it doesn't yet exist. - // TODO: This belongs in semantic, CTFE should not have to do this. - if (!cd->vclassinfo) - cd->vclassinfo = new TypeInfoClassDeclaration(cd->type); - e = new SymOffExp(loc, cd->vclassinfo, 0); - e->type = type; - return e; - } - // It's possible we have an array bounds error. We need to make sure it - // errors with this line number, not the one where the pointer was set. - e = e1->interpret(istate, ctfeNeedLvalue); - if (exceptionOrCantInterpret(e)) - return e; - if (!(e->op == TOKvar || e->op == TOKdotvar || e->op == TOKindex - || e->op == TOKslice || e->op == TOKaddress)) - { - if (e->op == TOKsymoff) - error("cannot dereference pointer to static variable %s at compile time", ((SymOffExp *)e)->var->toChars()); - else - error("dereference of invalid pointer '%s'", e->toChars()); - return EXP_CANT_INTERPRET; - } - if (goal != ctfeNeedLvalue && goal != ctfeNeedLvalueRef) - { - if (e->op == TOKindex && e->type->ty == Tpointer) - { - IndexExp *ie = (IndexExp *)e; - // Is this a real index to an array of pointers, or just a CTFE pointer? - // If the index has the same levels of indirection, it's an index - int srcLevels = 0; - int destLevels = 0; - for(Type *xx = ie->e1->type; xx->ty == Tpointer; xx = xx->nextOf()) - ++srcLevels; - for(Type *xx = e->type->nextOf(); xx->ty == Tpointer; xx = xx->nextOf()) - ++destLevels; - bool isGenuineIndex = (srcLevels == destLevels); - - if ((ie->e1->op == TOKarrayliteral || ie->e1->op == TOKstring) - && ie->e2->op == TOKint64) - { - Expression *dollar = ArrayLength(Type::tsize_t, ie->e1); - dinteger_t len = dollar->toInteger(); - dinteger_t indx = ie->e2->toInteger(); - assert(indx >=0 && indx <= len); // invalid pointer - if (indx == len) - { - error("dereference of pointer %s one past end of memory block limits [0..%lld]", - toChars(), len); - return EXP_CANT_INTERPRET; - } - e = ctfeIndex(loc, type, ie->e1, indx); - if (isGenuineIndex) - { - if (e->op == TOKindex) - e = e->interpret(istate, goal); - else if (e->op == TOKaddress) - e = paintTypeOntoLiteral(type, ((AddrExp *)e)->e1); - } - return e; - } - if (ie->e1->op == TOKassocarrayliteral) - { - e = findKeyInAA(loc, (AssocArrayLiteralExp *)ie->e1, ie->e2); - assert(e != EXP_CANT_INTERPRET); - e = paintTypeOntoLiteral(type, e); - if (isGenuineIndex) - { - if (e->op == TOKindex) - e = e->interpret(istate, goal); - else if (e->op == TOKaddress) - e = paintTypeOntoLiteral(type, ((AddrExp *)e)->e1); - } - return e; - } - } - if (e->op == TOKstructliteral) - return e; - e = e1->interpret(istate, goal); - if (e->op == TOKaddress) - { - e = ((AddrExp*)e)->e1; - // We're changing *&e to e. - // We needed the AddrExp to deal with type painting expressions - // we couldn't otherwise express. Now that the type painting is - // undone, we must simplify them. This applies to references - // (which will be a DotVarExp or IndexExp) and to local structs - // (which will be a VarExp). - - // We sometimes use DotVarExp and IndexExp to represent pointers, - // so in that case, they shouldn't be simplified. - - bool isCtfePtr = (e->op == TOKdotvar || e->op == TOKindex) - && isPointer(e->type); + Expression *m = (*elems)[i]; + // It can be NULL for performance reasons, + // see StructLiteralExp::interpret(). + if (!m) + continue; - // We also must not simplify if it is already a struct Literal - // or array literal, because it has already been interpreted. - if ( !isCtfePtr && e->op != TOKstructliteral && - e->op != TOKassocarrayliteral && e->op != TOKarrayliteral) - { - e = e->interpret(istate, goal); - } - } - else if (e->op == TOKvar) - { - e = e->interpret(istate, goal); - } - if (exceptionOrCantInterpret(e)) - return e; - } - else if (e->op == TOKaddress) - e = ((AddrExp*)e)->e1; // *(&x) ==> x - else if (e->op == TOKnull) + if (!(m->op == TOKvoid) && + !(m->op == TOKarrayliteral && isEntirelyVoid(((ArrayLiteralExp *)m)->elements)) && + !(m->op == TOKstructliteral && isEntirelyVoid(((StructLiteralExp *)m)->elements))) { - error("dereference of null pointer '%s'", e1->toChars()); - return EXP_CANT_INTERPRET; + return false; } - e = paintTypeOntoLiteral(type, e); } - -Ldone: -#if LOG - if (e == EXP_CANT_INTERPRET) - printf("PtrExp::interpret() %s = EXP_CANT_INTERPRET\n", toChars()); -#endif - return e; + return true; } -Expression *DotVarExp::interpret(InterState *istate, CtfeGoal goal) -{ Expression *e = EXP_CANT_INTERPRET; - -#if LOG - printf("%s DotVarExp::interpret() %s\n", loc.toChars(), toChars()); -#endif +// Scrub all members of an array. Return false if error +bool scrubArray(Loc loc, Expressions *elems, bool structlit) +{ + for (size_t i = 0; i < elems->dim; i++) + { + Expression *m = (*elems)[i]; + // It can be NULL for performance reasons, + // see StructLiteralExp::interpret(). + if (!m) + continue; - Expression *ex = e1->interpret(istate); - if (exceptionOrCantInterpret(ex)) - return ex; - if (ex != EXP_CANT_INTERPRET) - { - #if DMDV2 - // Special case for template AAs: AA.var returns the AA itself. - // ie AA.p ----> AA. This is a hack, to get around the - // corresponding hack in the AA druntime implementation. - if (isAssocArray(ex->type)) - return ex; - #endif - if (ex->op == TOKaddress) - ex = ((AddrExp *)ex)->e1; - - VarDeclaration *v = var->isVarDeclaration(); - if (!v) - { - error("CTFE internal error: %s", toChars()); - return EXP_CANT_INTERPRET; - } - if (ex->op == TOKnull) + // A struct .init may contain void members. + // Static array members are a weird special case (bug 10994). + if (structlit && + ((m->op == TOKvoid) || + (m->op == TOKarrayliteral && m->type->ty == Tsarray && isEntirelyVoid(((ArrayLiteralExp *)m)->elements)) || + (m->op == TOKstructliteral && isEntirelyVoid(((StructLiteralExp *)m)->elements))) + ) { - if (ex->type->toBasetype()->ty == Tclass) - error("class '%s' is null and cannot be dereferenced", e1->toChars()); - else - error("dereference of null pointer '%s'", e1->toChars()); - return EXP_CANT_INTERPRET; + m = NULL; } - if (ex->op == TOKstructliteral || ex->op == TOKclassreference) + else { - StructLiteralExp *se; - int i; - - // We can't use getField, because it makes a copy - if (ex->op == TOKclassreference) - { - se = ((ClassReferenceExp *)ex)->value; - i = ((ClassReferenceExp *)ex)->findFieldIndexByName(v); - } - else - { - se = (StructLiteralExp *)ex; - i = findFieldIndexByName(se->sd, v); - } -#if DMDV1 - if (se->sd->hasUnions) - { - error("Unions with overlapping fields are not yet supported in CTFE"); - return EXP_CANT_INTERPRET; - } -#endif - if (i == -1) - { - error("couldn't find field %s of type %s in %s", v->toChars(), type->toChars(), se->toChars()); - return EXP_CANT_INTERPRET; - } - e = (*se->elements)[i]; - if (!e) - { - error("Internal Compiler Error: Null field %s", v->toChars()); - return EXP_CANT_INTERPRET; - } - - if (goal == ctfeNeedLvalue || goal == ctfeNeedLvalueRef) - { - // If it is an lvalue literal, return it... - if (e->op == TOKstructliteral) - return e; - if ((type->ty == Tsarray || goal == ctfeNeedLvalue) && ( - e->op == TOKarrayliteral || - e->op == TOKassocarrayliteral || e->op == TOKstring || - e->op == TOKclassreference || e->op == TOKslice)) - return e; - /* Element is an allocated pointer, which was created in - * CastExp. - */ - if (goal == ctfeNeedLvalue && e->op == TOKindex && - e->type->equals(type) && - isPointer(type) ) - return e; - // ...Otherwise, just return the (simplified) dotvar expression - e = new DotVarExp(loc, ex, v); - e->type = type; - return e; - } - // If it is an rvalue literal, return it... - if (e->op == TOKstructliteral || e->op == TOKarrayliteral || - e->op == TOKassocarrayliteral || e->op == TOKstring) - return e; - if (e->op == TOKvoid) - { - VoidInitExp *ve = (VoidInitExp *)e; - const char *s = ve->var->toChars(); -#if DMDV2 - if (v->overlapped) - { - error("Reinterpretation through overlapped field %s is not allowed in CTFE", s); - return EXP_CANT_INTERPRET; - } -#endif - error("cannot read uninitialized variable %s in CTFE", s); - return EXP_CANT_INTERPRET; - } - if (isPointer(type)) - { - return paintTypeOntoLiteral(type, e); - } - if (e->op == TOKvar) - { - // Don't typepaint twice, since that might cause an erroneous copy - e = getVarExp(loc, istate, ((VarExp *)e)->var, goal); - if (e != EXP_CANT_INTERPRET && e->op != TOKthrownexception) - e = paintTypeOntoLiteral(type, e); - return e; - } - return e->interpret(istate, goal); + m = scrubReturnValue(loc, m); + if (m == EXP_CANT_INTERPRET) + return false; } - else - error("%s.%s is not yet implemented at compile time", e1->toChars(), var->toChars()); + (*elems)[i] = m; } - -#if LOG - if (e == EXP_CANT_INTERPRET) - printf("DotVarExp::interpret() %s = EXP_CANT_INTERPRET\n", toChars()); -#endif - return e; -} - -Expression *RemoveExp::interpret(InterState *istate, CtfeGoal goal) -{ -#if LOG - printf("%s RemoveExp::interpret() %s\n", loc.toChars(), toChars()); -#endif - Expression *agg = e1->interpret(istate); - if (exceptionOrCantInterpret(agg)) - return agg; - Expression *index = e2->interpret(istate); - if (exceptionOrCantInterpret(index)) - return index; - if (agg->op == TOKnull) - return EXP_VOID_INTERPRET; - assert(agg->op == TOKassocarrayliteral); - AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)agg; - Expressions *keysx = aae->keys; - Expressions *valuesx = aae->values; - size_t removed = 0; - for (size_t j = 0; j < valuesx->dim; ++j) - { - Expression *ekey = (*keysx)[j]; - int eq = ctfeEqual(loc, TOKequal, ekey, index); - if (eq) - ++removed; - else if (removed != 0) - { - (*keysx)[j - removed] = ekey; - (*valuesx)[j - removed] = (*valuesx)[j]; - } - } - valuesx->dim = valuesx->dim - removed; - keysx->dim = keysx->dim - removed; - return new IntegerExp(loc, removed?1:0, Type::tbool); + return true; } @@ -6192,12 +6787,12 @@ Expression *interpret_aaApply(InterState *istate, Expression *aa, Expression *de args[numParams - 1] = evalue; if (numParams == 2) args[0] = ekey; - eresult = fd->interpret(istate, &args, pthis); + eresult = interpret(fd, istate, &args, pthis); if (exceptionOrCantInterpret(eresult)) return eresult; assert(eresult->op == TOKint64); - if (((IntegerExp *)eresult)->value != 0) + if (((IntegerExp *)eresult)->getInteger() != 0) return eresult; } return eresult; @@ -6233,7 +6828,7 @@ Expression *foreachApplyUtf(InterState *istate, Expression *str, Expression *del assert(fd && fd->fbody); assert(fd->parameters); size_t numParams = fd->parameters->dim; - assert(numParams == 1 || numParams==2); + assert(numParams == 1 || numParams == 2); Type *charType = (*fd->parameters)[numParams-1]->type; Type *indexType = numParams == 2 ? (*fd->parameters)[0]->type : Type::tsize_t; @@ -6290,7 +6885,7 @@ Expression *foreachApplyUtf(InterState *istate, Expression *str, Expression *del { Expression * r = (*ale->elements)[indx]; assert(r->op == TOKint64); - utf8_t x = (utf8_t)(((IntegerExp *)r)->value); + utf8_t x = (utf8_t)(((IntegerExp *)r)->getInteger()); if ( (x & 0xC0) != 0x80) break; ++buflen; @@ -6298,11 +6893,11 @@ Expression *foreachApplyUtf(InterState *istate, Expression *str, Expression *del } else buflen = (indx + 4 > len) ? len - indx : 4; - for (int i = 0; i < buflen; ++i) + for (size_t i = 0; i < buflen; ++i) { Expression * r = (*ale->elements)[indx + i]; assert(r->op == TOKint64); - utf8buf[i] = (utf8_t)(((IntegerExp *)r)->value); + utf8buf[i] = (utf8_t)(((IntegerExp *)r)->getInteger()); } n = 0; errmsg = utf_decodeChar(&utf8buf[0], buflen, &n, &rawvalue); @@ -6314,7 +6909,7 @@ Expression *foreachApplyUtf(InterState *istate, Expression *str, Expression *del buflen = 1; Expression * r = (*ale->elements)[indx]; assert(r->op == TOKint64); - unsigned short x = (unsigned short)(((IntegerExp *)r)->value); + unsigned short x = (unsigned short)(((IntegerExp *)r)->getInteger()); if (indx > 0 && x >= 0xDC00 && x <= 0xDFFF) { --indx; @@ -6323,11 +6918,11 @@ Expression *foreachApplyUtf(InterState *istate, Expression *str, Expression *del } else buflen = (indx + 2 > len) ? len - indx : 2; - for (int i=0; i < buflen; ++i) + for (size_t i=0; i < buflen; ++i) { Expression * r = (*ale->elements)[indx + i]; assert(r->op == TOKint64); - utf16buf[i] = (unsigned short)(((IntegerExp *)r)->value); + utf16buf[i] = (unsigned short)(((IntegerExp *)r)->getInteger()); } n = 0; errmsg = utf_decodeWchar(&utf16buf[0], buflen, &n, &rawvalue); @@ -6339,7 +6934,7 @@ Expression *foreachApplyUtf(InterState *istate, Expression *str, Expression *del Expression * r = (*ale->elements)[indx]; assert(r->op == TOKint64); - rawvalue = (dchar_t)((IntegerExp *)r)->value; + rawvalue = (dchar_t)((IntegerExp *)r)->getInteger(); n = 1; } break; @@ -6360,7 +6955,7 @@ Expression *foreachApplyUtf(InterState *istate, Expression *str, Expression *del { // find the start of the string utf8_t *s = (utf8_t *)se->string; --indx; - while (indx > 0 && ((s[indx]&0xC0)==0x80)) + while (indx > 0 && ((s[indx]&0xC0) == 0x80)) --indx; saveindx = indx; } @@ -6447,11 +7042,11 @@ Expression *foreachApplyUtf(InterState *istate, Expression *str, Expression *del args[numParams - 1] = val; - eresult = fd->interpret(istate, &args, pthis); + eresult = interpret(fd, istate, &args, pthis); if (exceptionOrCantInterpret(eresult)) return eresult; assert(eresult->op == TOKint64); - if (((IntegerExp *)eresult)->value != 0) + if (((IntegerExp *)eresult)->getInteger() != 0) return eresult; } } @@ -6466,21 +7061,9 @@ Expression *evaluateIfBuiltin(InterState *istate, Loc loc, { Expression *e = NULL; size_t nargs = arguments ? arguments->dim : 0; -#if DMDV2 - if (pthis && isAssocArray(pthis->type)) - { - if (fd->ident == Id::length && nargs==0) - return interpret_length(istate, pthis); - else if (fd->ident == Id::keys && nargs==0) - return interpret_keys(istate, pthis, returnedArrayType(fd)); - else if (fd->ident == Id::values && nargs==0) - return interpret_values(istate, pthis, returnedArrayType(fd)); - else if (fd->ident == Id::rehash && nargs==0) - return pthis->interpret(istate, ctfeNeedLvalue); // rehash is a no-op - } if (!pthis) { - if (fd->isBuiltin() == BUILTINyes) + if (isBuiltin(fd) == BUILTINyes) { Expressions args; args.setDim(nargs); for (size_t i = 0; i < args.dim; i++) @@ -6499,17 +7082,6 @@ Expression *evaluateIfBuiltin(InterState *istate, Loc loc, } } } - - if (!pthis) - { - Expression *firstarg = nargs > 0 ? (Expression *)(arguments->data[0]) : NULL; - if (nargs==3 && isAssocArray(firstarg->type) && !strcmp(fd->ident->string, "_aaApply")) - return interpret_aaApply(istate, firstarg, (Expression *)(arguments->data[2])); - if (nargs==3 && isAssocArray(firstarg->type) &&!strcmp(fd->ident->string, "_aaApply2")) - return interpret_aaApply(istate, firstarg, (Expression *)(arguments->data[2])); - } -#endif -#if DMDV1 if (!pthis) { Expression *firstarg = nargs > 0 ? (Expression *)(arguments->data[0]) : NULL; @@ -6518,20 +7090,18 @@ Expression *evaluateIfBuiltin(InterState *istate, Loc loc, TypeAArray *firstAAtype = (TypeAArray *)firstarg->type; if (fd->ident == Id::aaLen && nargs == 1) return interpret_length(istate, firstarg); - else if (fd->ident == Id::aaKeys) - return interpret_keys(istate, firstarg, new DArray(firstAAtype->index)); - else if (fd->ident == Id::aaValues) - return interpret_values(istate, firstarg, new DArray(firstAAtype->nextOf())); - else if (nargs==2 && fd->ident == Id::aaRehash) - return firstarg->interpret(istate, ctfeNeedLvalue); //no-op - else if (nargs==3 && !strcmp(fd->ident->string, "_aaApply")) + else if (nargs == 3 && !strcmp(fd->ident->string, "_aaApply")) return interpret_aaApply(istate, firstarg, (Expression *)(arguments->data[2])); - else if (nargs==3 && !strcmp(fd->ident->string, "_aaApply2")) + else if (nargs == 3 && !strcmp(fd->ident->string, "_aaApply2")) return interpret_aaApply(istate, firstarg, (Expression *)(arguments->data[2])); + else if (nargs == 1 && !strcmp(fd->ident->string, "keys") && !strcmp(fd->toParent2()->ident->string, "object")) + return interpret_keys(istate, firstarg, firstAAtype->index->arrayOf()); + else if (nargs == 1 && !strcmp(fd->ident->string, "values") && !strcmp(fd->toParent2()->ident->string, "object")) + return interpret_values(istate, firstarg, firstAAtype->nextOf()->arrayOf()); + else if (nargs == 1 && !strcmp(fd->ident->string, "rehash") && !strcmp(fd->toParent2()->ident->string, "object")) + return firstarg->interpret(istate, ctfeNeedLvalue); } } -#endif -#if DMDV2 if (pthis && !fd->fbody && fd->isCtorDeclaration() && fd->parent && fd->parent->parent && fd->parent->parent->ident == Id::object) { if (pthis->op == TOKclassreference && fd->parent->ident == Id::Throwable) @@ -6539,7 +7109,7 @@ Expression *evaluateIfBuiltin(InterState *istate, Loc loc, // But we might need some magic if stack tracing gets added to druntime. StructLiteralExp *se = ((ClassReferenceExp *)pthis)->value; assert(arguments->dim <= se->elements->dim); - for (int i = 0; i < arguments->dim; ++i) + for (size_t i = 0; i < arguments->dim; ++i) { e = (*arguments)[i]->interpret(istate); if (exceptionOrCantInterpret(e)) @@ -6549,7 +7119,6 @@ Expression *evaluateIfBuiltin(InterState *istate, Loc loc, return EXP_VOID_INTERPRET; } } -#endif if (nargs == 1 && !pthis && (fd->ident == Id::criticalenter || fd->ident == Id::criticalexit)) { // Support synchronized{} as a no-op @@ -6580,37 +7149,65 @@ Expression *evaluateIfBuiltin(InterState *istate, Loc loc, return e; } +Expression *evaluatePostblits(InterState *istate, ArrayLiteralExp *ale, size_t lwr, size_t upr) +{ + Type *telem = ale->type->nextOf()->baseElemOf(); + if (telem->ty != Tstruct) + return NULL; + StructDeclaration *sd = ((TypeStruct *)telem)->sym; + if (sd->postblit) + { + for (size_t i = lwr; i < upr; i++) + { + Expression *e = (*ale->elements)[i]; + if (e->op == TOKarrayliteral) + { + ArrayLiteralExp *alex = (ArrayLiteralExp *)e; + e = evaluatePostblits(istate, alex, 0, alex->elements->dim); + } + else + { + // e.__postblit() + assert(e->op == TOKstructliteral); + e = interpret(sd->postblit, istate, NULL, e); + } + if (exceptionOrCantInterpret(e)) + return e; + } + } + return NULL; +} + /*************************** CTFE Sanity Checks ***************************/ /* Setter functions for CTFE variable values. * These functions exist to check for compiler CTFE bugs. */ -bool VarDeclaration::hasValue() +bool hasValue(VarDeclaration *vd) { - if (ctfeAdrOnStack == (size_t)-1) + if (vd->ctfeAdrOnStack == (size_t)-1) return false; - return NULL != getValue(); + return NULL != getValue(vd); } -Expression *VarDeclaration::getValue() +Expression *getValue(VarDeclaration *vd) { - return ctfeStack.getValue(this); + return ctfeStack.getValue(vd); } -void VarDeclaration::setValueNull() +void setValueNull(VarDeclaration *vd) { - ctfeStack.setValue(this, NULL); + ctfeStack.setValue(vd, NULL); } // Don't check for validity -void VarDeclaration::setValueWithoutChecking(Expression *newval) +void setValueWithoutChecking(VarDeclaration *vd, Expression *newval) { - ctfeStack.setValue(this, newval); + ctfeStack.setValue(vd, newval); } -void VarDeclaration::setValue(Expression *newval) +void setValue(VarDeclaration *vd, Expression *newval) { assert(isCtfeValueValid(newval)); - ctfeStack.setValue(this, newval); + ctfeStack.setValue(vd, newval); } - diff --git a/gcc/d/dfrontend/intrange.c b/gcc/d/dfrontend/intrange.c index 0cd7cc2af..db590b5bc 100644 --- a/gcc/d/dfrontend/intrange.c +++ b/gcc/d/dfrontend/intrange.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2011 by Digital Mars -// All Rights Reserved -// written by KennyTM -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by KennyTM + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/intrange.c + */ #include "intrange.h" #include "mars.h" diff --git a/gcc/d/dfrontend/intrange.h b/gcc/d/dfrontend/intrange.h index 359e99bc2..f0488d8fa 100644 --- a/gcc/d/dfrontend/intrange.h +++ b/gcc/d/dfrontend/intrange.h @@ -1,13 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by KennyTM -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. - +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by KennyTM + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/intrange.h + */ #ifndef DMD_SXNUM_H #define DMD_SXNUM_H diff --git a/gcc/d/dfrontend/json.c b/gcc/d/dfrontend/json.c index cdc5a8dce..91599dffa 100644 --- a/gcc/d/dfrontend/json.c +++ b/gcc/d/dfrontend/json.c @@ -1,11 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. + +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/json.c + */ // This implements the JSON capability. @@ -360,7 +362,7 @@ class ToJsonVisitor : public Visitor } } - void property(const char *name, Loc *loc) + void property(const char *linename, const char *charname, Loc *loc) { if (loc) { @@ -375,7 +377,11 @@ class ToJsonVisitor : public Visitor } if (loc->linnum) - property(name, loc->linnum); + { + property(linename, loc->linnum); + if (loc->charnum) + property(charname, loc->charnum); + } } } @@ -445,11 +451,17 @@ class ToJsonVisitor : public Visitor } if (s->prot() != PROTpublic) - property("protection", Pprotectionnames[s->prot()]); + property("protection", protectionToChars(s->prot())); + + if (EnumMember *em = s->isEnumMember()) + { + if (em->origValue) + property("value", em->origValue->toChars()); + } property("comment", (const char *)s->comment); - property("line", &s->loc); + property("line", "char", &s->loc); } void jsonProperties(Declaration *d) @@ -543,9 +555,9 @@ class ToJsonVisitor : public Visitor property("kind", s->kind()); property("comment", (const char *)s->comment); - property("line", &s->loc); + property("line", "char", &s->loc); if (s->prot() != PROTpublic) - property("protection", Pprotectionnames[s->prot()]); + property("protection", protectionToChars(s->prot())); if (s->aliasId) property("alias", s->aliasId->toChars()); @@ -629,17 +641,6 @@ class ToJsonVisitor : public Visitor objectEnd(); } - void visit(TypedefDeclaration *d) - { - objectStart(); - - jsonProperties(d); - - property("base", "baseDeco", d->basetype); - - objectEnd(); - } - void visit(AggregateDeclaration *d) { objectStart(); @@ -691,7 +692,7 @@ class ToJsonVisitor : public Visitor if (tf && tf->ty == Tfunction) property("parameters", tf->parameters); - property("endline", &d->endloc); + property("endline", "endchar", &d->endloc); if (d->foverrides.dim) { diff --git a/gcc/d/dfrontend/json.h b/gcc/d/dfrontend/json.h index 4e80300dc..0d560db28 100644 --- a/gcc/d/dfrontend/json.h +++ b/gcc/d/dfrontend/json.h @@ -1,13 +1,13 @@ - -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/json.h + */ #ifndef DMD_JSON_H #define DMD_JSON_H diff --git a/gcc/d/dfrontend/lexer.c b/gcc/d/dfrontend/lexer.c index c47207c2b..56be45906 100644 --- a/gcc/d/dfrontend/lexer.c +++ b/gcc/d/dfrontend/lexer.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/lexer.c + */ /* Lexical Analyzer */ @@ -69,17 +70,17 @@ static void cmtable_init() const char *Token::tochars[TOKMAX]; -void *Token::operator new(size_t size) -{ Token *t; - +Token *Token::alloc() +{ if (Lexer::freelist) { - t = Lexer::freelist; + Token *t = Lexer::freelist; Lexer::freelist = t->next; + t->next = NULL; return t; } - return ::operator new(size); + return new Token(); } #ifdef DEBUG @@ -175,11 +176,29 @@ const char *Token::toChars() } buf.writeByte('"'); if (postfix) - buf.writeByte('"'); + buf.writeByte(postfix); + p = buf.extractString(); + } + break; + + case TOKxstring: + { + OutBuffer buf; + buf.writeByte('x'); + buf.writeByte('"'); + for (size_t i = 0; i < len; i++) + { + if (i) + buf.writeByte(' '); + buf.printf("%02x", ustring[i]); + } + buf.writeByte('"'); + if (postfix) + buf.writeByte(postfix); buf.writeByte(0); p = (char *)buf.extractData(); - } break; + } case TOKidentifier: case TOKenum: @@ -228,13 +247,14 @@ Lexer::Lexer(Module *mod, const utf8_t *base, size_t begoffset, size_t endoffset, int doDocComment, int commentToken) { - scanloc = Loc(mod, 1); + scanloc = Loc(mod, 1, 1); //printf("Lexer::Lexer(%p,%d)\n",base,length); //printf("lexer.mod = %p, %p\n", mod, this->loc.mod); memset(&token,0,sizeof(token)); this->base = base; this->end = base + endoffset; p = base + begoffset; + line = p; this->mod = mod; this->doDocComment = doDocComment; this->anyToken = 0; @@ -276,11 +296,23 @@ Lexer::Lexer(Module *mod, } break; } - scanloc.linnum = 2; + endOfLine(); } } +void Lexer::endOfLine() +{ + scanloc.linnum++; + line = p; +} + +Loc Lexer::loc() +{ + scanloc.charnum = (unsigned)(1 + p-line); + return scanloc; +} + void Lexer::error(const char *format, ...) { va_list ap; @@ -329,7 +361,7 @@ Token *Lexer::peek(Token *ct) t = ct->next; else { - t = new Token(); + t = Token::alloc(); scan(t); ct->next = t; } @@ -449,7 +481,7 @@ int Lexer::isValidIdentifier(const char *p) void Lexer::scan(Token *t) { unsigned lastLine = scanloc.linnum; - unsigned linnum; + Loc startLoc; t->blockComment = NULL; t->lineComment = NULL; @@ -457,7 +489,7 @@ void Lexer::scan(Token *t) { t->ptr = p; //printf("p = %p, *p = '%c'\n",p,*p); - t->loc = scanloc; + t->loc = loc(); switch (*p) { case 0: @@ -475,12 +507,12 @@ void Lexer::scan(Token *t) case '\r': p++; if (*p != '\n') // if CR stands by itself - scanloc.linnum++; + endOfLine(); continue; // skip white space case '\n': p++; - scanloc.linnum++; + endOfLine(); continue; // skip white space case '0': case '1': case '2': case '3': case '4': @@ -690,7 +722,7 @@ void Lexer::scan(Token *t) case '*': p++; - linnum = scanloc.linnum; + startLoc = loc(); while (1) { while (1) @@ -701,21 +733,21 @@ void Lexer::scan(Token *t) break; case '\n': - scanloc.linnum++; + endOfLine(); p++; continue; case '\r': p++; if (*p != '\n') - scanloc.linnum++; + endOfLine(); continue; case 0: case 0x1A: error("unterminated /* */ comment"); p = end; - t->loc = scanloc; + t->loc = loc(); t->value = TOKeof; return; @@ -723,7 +755,7 @@ void Lexer::scan(Token *t) if (c & 0x80) { unsigned u = decodeUTF(); if (u == PS || u == LS) - scanloc.linnum++; + endOfLine(); } p++; continue; @@ -736,19 +768,18 @@ void Lexer::scan(Token *t) } if (commentToken) { - t->loc.filename = scanloc.filename; - t->loc.linnum = linnum; + t->loc = startLoc; t->value = TOKcomment; return; } else if (doDocComment && t->ptr[2] == '*' && p - 4 != t->ptr) { // if /** but not /**/ - getDocComment(t, lastLine == linnum); + getDocComment(t, lastLine == startLoc.linnum); } continue; case '/': // do // style comments - linnum = scanloc.linnum; + startLoc = loc(); while (1) { utf8_t c = *++p; switch (c) @@ -766,15 +797,14 @@ void Lexer::scan(Token *t) if (commentToken) { p = end; - t->loc.filename = scanloc.filename; - t->loc.linnum = linnum; + t->loc = startLoc; t->value = TOKcomment; return; } if (doDocComment && t->ptr[2] == '/') - getDocComment(t, lastLine == linnum); + getDocComment(t, lastLine == startLoc.linnum); p = end; - t->loc = scanloc; + t->loc = loc(); t->value = TOKeof; return; @@ -792,23 +822,22 @@ void Lexer::scan(Token *t) if (commentToken) { p++; - scanloc.linnum++; - t->loc.filename = scanloc.filename; - t->loc.linnum = linnum; + endOfLine(); + t->loc = startLoc; t->value = TOKcomment; return; } if (doDocComment && t->ptr[2] == '/') - getDocComment(t, lastLine == linnum); + getDocComment(t, lastLine == startLoc.linnum); p++; - scanloc.linnum++; + endOfLine(); continue; case '+': { int nest; - linnum = scanloc.linnum; + startLoc = loc(); p++; nest = 1; while (1) @@ -837,11 +866,11 @@ void Lexer::scan(Token *t) case '\r': p++; if (*p != '\n') - scanloc.linnum++; + endOfLine(); continue; case '\n': - scanloc.linnum++; + endOfLine(); p++; continue; @@ -849,7 +878,7 @@ void Lexer::scan(Token *t) case 0x1A: error("unterminated /+ +/ comment"); p = end; - t->loc = scanloc; + t->loc = loc(); t->value = TOKeof; return; @@ -857,7 +886,7 @@ void Lexer::scan(Token *t) if (c & 0x80) { unsigned u = decodeUTF(); if (u == PS || u == LS) - scanloc.linnum++; + endOfLine(); } p++; continue; @@ -866,14 +895,13 @@ void Lexer::scan(Token *t) } if (commentToken) { - t->loc.filename = scanloc.filename; - t->loc.linnum = linnum; + t->loc = startLoc; t->value = TOKcomment; return; } if (doDocComment && t->ptr[2] == '+' && p - 4 != t->ptr) { // if /++ but not /++/ - getDocComment(t, lastLine == linnum); + getDocComment(t, lastLine == startLoc.linnum); } continue; } @@ -1160,7 +1188,7 @@ void Lexer::scan(Token *t) if (c == PS || c == LS) { - scanloc.linnum++; + endOfLine(); p++; continue; } @@ -1305,7 +1333,7 @@ unsigned Lexer::escapeSequence() TOK Lexer::wysiwygStringConstant(Token *t, int tc) { unsigned c; - Loc start = scanloc; + Loc start = loc(); p++; stringbuffer.reset(); @@ -1315,14 +1343,14 @@ TOK Lexer::wysiwygStringConstant(Token *t, int tc) switch (c) { case '\n': - scanloc.linnum++; + endOfLine(); break; case '\r': if (*p == '\n') continue; // ignore c = '\n'; // treat EndOfLine as \n character - scanloc.linnum++; + endOfLine(); break; case 0: @@ -1352,7 +1380,7 @@ TOK Lexer::wysiwygStringConstant(Token *t, int tc) unsigned u = decodeUTF(); p++; if (u == PS || u == LS) - scanloc.linnum++; + endOfLine(); stringbuffer.writeUTF8(u); continue; } @@ -1370,9 +1398,9 @@ TOK Lexer::wysiwygStringConstant(Token *t, int tc) TOK Lexer::hexStringConstant(Token *t) { unsigned c; - Loc start = scanloc; + Loc start = loc(); unsigned n = 0; - unsigned v; + unsigned v = ~0; // dead assignment, needed to suppress warning p++; stringbuffer.reset(); @@ -1392,7 +1420,7 @@ TOK Lexer::hexStringConstant(Token *t) continue; // ignore // Treat isolated '\r' as if it were a '\n' case '\n': - scanloc.linnum++; + endOfLine(); continue; case 0: @@ -1401,7 +1429,7 @@ TOK Lexer::hexStringConstant(Token *t) t->ustring = (utf8_t *)""; t->len = 0; t->postfix = 0; - return TOKstring; + return TOKxstring; case '"': if (n & 1) @@ -1413,7 +1441,7 @@ TOK Lexer::hexStringConstant(Token *t) t->ustring = (utf8_t *)mem.malloc(stringbuffer.offset); memcpy(t->ustring, stringbuffer.data, stringbuffer.offset); stringPostfix(t); - return TOKstring; + return TOKxstring; default: if (c >= '0' && c <= '9') @@ -1427,7 +1455,7 @@ TOK Lexer::hexStringConstant(Token *t) unsigned u = decodeUTF(); p++; if (u == PS || u == LS) - scanloc.linnum++; + endOfLine(); else error("non-hex character \\u%04x", u); } @@ -1461,11 +1489,11 @@ TOK Lexer::hexStringConstant(Token *t) TOK Lexer::delimitedStringConstant(Token *t) { unsigned c; - Loc start = scanloc; + Loc start = loc(); unsigned delimleft = 0; unsigned delimright = 0; unsigned nest = 1; - unsigned nestcount; + unsigned nestcount = ~0; // dead assignment, needed to suppress warning Identifier *hereid = NULL; unsigned blankrol = 0; unsigned startline = 0; @@ -1480,7 +1508,7 @@ TOK Lexer::delimitedStringConstant(Token *t) { case '\n': Lnextline: - scanloc.linnum++; + endOfLine(); startline = 1; if (blankrol) { blankrol = 0; @@ -1619,7 +1647,7 @@ TOK Lexer::delimitedStringConstant(Token *t) TOK Lexer::tokenStringConstant(Token *t) { unsigned nest = 1; - Loc start = scanloc; + Loc start = loc(); const utf8_t *pstart = ++p; while (1) @@ -1669,7 +1697,7 @@ TOK Lexer::tokenStringConstant(Token *t) TOK Lexer::escapeStringConstant(Token *t, int wide) { unsigned c; - Loc start = scanloc; + Loc start = loc(); p++; stringbuffer.reset(); @@ -1694,14 +1722,14 @@ TOK Lexer::escapeStringConstant(Token *t, int wide) } break; case '\n': - scanloc.linnum++; + endOfLine(); break; case '\r': if (*p == '\n') continue; // ignore c = '\n'; // treat EndOfLine as \n character - scanloc.linnum++; + endOfLine(); break; case '"': @@ -1728,7 +1756,7 @@ TOK Lexer::escapeStringConstant(Token *t, int wide) c = decodeUTF(); if (c == LS || c == PS) { c = '\n'; - scanloc.linnum++; + endOfLine(); } p++; stringbuffer.writeUTF8(c); @@ -1774,7 +1802,7 @@ TOK Lexer::charConstant(Token *t, int wide) break; case '\n': L1: - scanloc.linnum++; + endOfLine(); case '\r': case 0: case 0x1A: @@ -1847,6 +1875,7 @@ TOK Lexer::number(Token *t) uinteger_t n = 0; // unsigned >=64 bit integer type int d; bool err = false; + bool overflow = false; c = *p; if (c == '0') @@ -1875,15 +1904,11 @@ TOK Lexer::number(Token *t) break; case '.': - if (p[1] == '.' || // if ".." - (isalpha(p[1]) || p[1] == '_' || // if ".identifier" - (p[1] & 0x80) // if ".unicode" - ) - ) - { - goto Ldone; // regard . as start of separate token - } - goto Lreal; + if (p[1] == '.') + goto Ldone; // if ".." + if (isalpha(p[1]) || p[1] == '_' || p[1] & 0x80) + goto Ldone; // if ".identifier" or ".unicode" + goto Lreal; // '.' is part of current token case 'i': case 'f': @@ -1961,16 +1986,11 @@ TOK Lexer::number(Token *t) goto Ldone; case '.': - if (p[1] == '.' || // if ".." - base == 10 && - (isalpha(p[1]) || p[1] == '_' || // if ".identifier" - (p[1] & 0x80) // if ".unicode" - ) - ) - { - goto Ldone; // regard . as start of separate token - } - goto Lreal; // otherwise as part of a floating point literal + if (p[1] == '.') + goto Ldone; // if ".." + if (base == 10 && (isalpha(p[1]) || p[1] == '_' || p[1] & 0x80)) + goto Ldone; // if ".identifier" or ".unicode" + goto Lreal; // otherwise as part of a floating point literal case 'p': case 'P': @@ -1988,24 +2008,28 @@ TOK Lexer::number(Token *t) } uinteger_t n2 = n * base; - if ((n2 / base != n || n2 + d < n) && !err) + if ((n2 / base != n || n2 + d < n)) { - error("integer overflow"); - err = true; + overflow = true; } n = n2 + d; + // if n needs more than 64 bits if (sizeof(n) > 8 && - n > 0xFFFFFFFFFFFFFFFFULL && // if n needs more than 64 bits - !err) + n > 0xFFFFFFFFFFFFFFFFULL) { - error("integer overflow"); - err = true; + overflow = true; } } Ldone: + if (overflow && !err) + { + error("integer overflow"); + err = true; + } + enum FLAGS { FLAGS_none = 0, @@ -2309,7 +2333,7 @@ void Lexer::poundLine() Token tok; int linnum; char *filespec = NULL; - Loc loc = this->scanloc; + Loc loc = this->loc(); scan(&tok); if (tok.value == TOKint32v || tok.value == TOKint64v) @@ -2477,6 +2501,30 @@ void Lexer::getDocComment(Token *t, unsigned lineComment) break; } + /* Remove leading spaces until start of the comment + */ + int linestart = 0; + if (ct == '/') + { + while (q < qend && (*q == ' ' || *q == '\t')) + ++q; + } + else if (q < qend) + { + if (*q == '\r') + { + ++q; + if (q < qend && *q == '\n') + ++q; + linestart = 1; + } + else if (*q == '\n') + { + ++q; + linestart = 1; + } + } + /* Remove trailing row of ****'s or ++++'s */ if (ct != '/') @@ -2492,7 +2540,6 @@ void Lexer::getDocComment(Token *t, unsigned lineComment) * Canonicalize it into buf[]. */ OutBuffer buf; - int linestart = 0; for (; q < qend; q++) { @@ -2550,6 +2597,14 @@ void Lexer::getDocComment(Token *t, unsigned lineComment) buf.writeByte(c); } + /* Trim trailing whitespace (if the last line does not have newline) + */ + if (buf.offset && (buf.data[buf.offset - 1] == ' ' || buf.data[buf.offset - 1] == '\t')) + { + while (buf.offset && (buf.data[buf.offset - 1] == ' ' || buf.data[buf.offset - 1] == '\t')) + buf.offset--; + } + // Always end with a newline if (!buf.offset || buf.data[buf.offset - 1] != '\n') buf.writeByte('\n'); @@ -2581,19 +2636,29 @@ const utf8_t *Lexer::combineComments(const utf8_t *c1, const utf8_t *c2) const utf8_t *c = c2; if (c1) - { c = c1; + { + c = c1; if (c2) - { size_t len1 = strlen((char *)c1); + { + size_t len1 = strlen((char *)c1); size_t len2 = strlen((char *)c2); - utf8_t *p = (utf8_t *)mem.malloc(len1 + 1 + len2 + 1); - memcpy(p, c1, len1); + int insertNewLine = 0; if (len1 && c1[len1 - 1] != '\n') - { p[len1] = '\n'; - len1++; + { + ++len1; + insertNewLine = 1; } - memcpy(p + len1, c2, len2); - p[len1 + len2] = 0; + + utf8_t *p = (utf8_t *)mem.malloc(len1 + 1 + len2 + 1); + memcpy(p, c1, len1 - insertNewLine); + if (insertNewLine) + p[len1 - 1] = '\n'; + + p[len1] = '\n'; + + memcpy(p + len1 + 1, c2, len2); + p[len1 + 1 + len2] = 0; c = p; } } @@ -2755,9 +2820,7 @@ static Keyword keywords[] = { "invariant", TOKinvariant }, { "unittest", TOKunittest }, { "version", TOKversion }, - //{ "manifest", TOKmanifest }, - // Added after 1.0 { "__argTypes", TOKargTypes }, { "__parameters", TOKparameters }, { "ref", TOKref }, @@ -2943,4 +3006,3 @@ void unittest_lexer() } #endif - diff --git a/gcc/d/dfrontend/lexer.h b/gcc/d/dfrontend/lexer.h index d752b68dc..feef131c0 100644 --- a/gcc/d/dfrontend/lexer.h +++ b/gcc/d/dfrontend/lexer.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/lexer.h + */ #ifndef DMD_LEXER_H #define DMD_LEXER_H @@ -71,7 +72,10 @@ enum TOK TOKstructliteral, TOKclassreference, TOKthrownexception, + TOKdelegateptr, + TOKdelegatefuncptr, +// 54 // Operators TOKlt, TOKgt, TOKle, TOKge, @@ -80,11 +84,12 @@ enum TOK TOKindex, TOKis, TOKtobool, -// 60 +// 65 // NCEG floating point compares // !<>= <> <>= !> !>= !< !<= !<> TOKunord,TOKlg,TOKleg,TOKule,TOKul,TOKuge,TOKug,TOKue, +// 73 TOKshl, TOKshr, TOKshlass, TOKshrass, TOKushr, TOKushrass, @@ -100,7 +105,7 @@ enum TOK TOKquestion, TOKandand, TOKoror, TOKpreplusplus, TOKpreminusminus, -// 106 +// 112 // Numeric literals TOKint32v, TOKuns32v, TOKint64v, TOKuns64v, @@ -111,7 +116,7 @@ enum TOK TOKcharv, TOKwcharv, TOKdcharv, // Leaf operators - TOKidentifier, TOKstring, + TOKidentifier, TOKstring, TOKxstring, TOKthis, TOKsuper, TOKhalt, TOKtuple, TOKerror, @@ -128,14 +133,14 @@ enum TOK TOKcomplex32, TOKcomplex64, TOKcomplex80, TOKchar, TOKwchar, TOKdchar, TOKbool, -// 152 +// 157 // Aggregates TOKstruct, TOKclass, TOKinterface, TOKunion, TOKenum, TOKimport, TOKtypedef, TOKalias, TOKoverride, TOKdelegate, TOKfunction, TOKmixin, TOKalign, TOKextern, TOKprivate, TOKprotected, TOKpublic, TOKexport, - TOKstatic, /*TOKvirtual,*/ TOKfinal, TOKconst, TOKabstract, TOKvolatile, + TOKstatic, TOKfinal, TOKconst, TOKabstract, TOKvolatile, TOKdebug, TOKdeprecated, TOKin, TOKout, TOKinout, TOKlazy, TOKauto, TOKpackage, TOKmanifest, TOKimmutable, @@ -177,6 +182,8 @@ enum TOK TOKvector, TOKpound, + TOKinterval, + TOKMAX }; @@ -211,7 +218,7 @@ struct Token }; static const char *tochars[TOKMAX]; - static void *operator new(size_t sz); + static Token *alloc(); Token() : next(NULL) {} int isKeyword(); @@ -234,6 +241,7 @@ class Lexer const utf8_t *base; // pointer to start of buffer const utf8_t *end; // past end of buffer const utf8_t *p; // current character + const utf8_t *line; // start of current line Token token; Module *mod; int doDocComment; // collect doc comment information @@ -265,6 +273,7 @@ class Lexer void stringPostfix(Token *t); TOK number(Token *t); TOK inreal(Token *t); + Loc loc(); void error(const char *format, ...); void error(Loc loc, const char *format, ...); void deprecation(const char *format, ...); @@ -274,6 +283,9 @@ class Lexer static int isValidIdentifier(const char *p); static const utf8_t *combineComments(const utf8_t *c1, const utf8_t *c2); + +private: + void endOfLine(); }; #endif /* DMD_LEXER_H */ diff --git a/gcc/d/dfrontend/macro.c b/gcc/d/dfrontend/macro.c index 2203c2089..daa2ff860 100644 --- a/gcc/d/dfrontend/macro.c +++ b/gcc/d/dfrontend/macro.c @@ -1,11 +1,13 @@ -// Copyright (c) 1999-2006 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/macro.c + */ /* Simple macro text processor. */ @@ -120,7 +122,7 @@ size_t extractArgN(const utf8_t *p, size_t end, const utf8_t **pmarg, size_t *pm Largstart: #if 1 // Skip first space, if any, to find the start of the macro argument - if (v < end && isspace(p[v])) + if (n != 1 && v < end && isspace(p[v])) v++; #else // Skip past spaces to find the start of the macro argument @@ -278,7 +280,13 @@ void Macro::expand(OutBuffer *buf, size_t start, size_t *pend, const utf8_t *marg; size_t marglen; - extractArgN(arg, arglen, &marg, &marglen, n); + if (n == 0) + { + marg = arg; + marglen = arglen; + } + else + extractArgN(arg, arglen, &marg, &marglen, n); if (marglen == 0) { // Just remove macro invocation //printf("Replacing '$%c' with '%.*s'\n", p[u + 1], marglen, marg); @@ -346,7 +354,7 @@ void Macro::expand(OutBuffer *buf, size_t start, size_t *pend, * beginning of macro argument (marg). */ for (v = u + 2; v < end; v+=utfStride(p+v)) - { utf8_t c = p[v]; + { if (!isIdTail(p+v)) { // We've gone past the end of the macro name. diff --git a/gcc/d/dfrontend/macro.h b/gcc/d/dfrontend/macro.h index ea5e4aa01..aee1ea224 100644 --- a/gcc/d/dfrontend/macro.h +++ b/gcc/d/dfrontend/macro.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2011 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/macro.h + */ #ifndef DMD_MACRO_H #define DMD_MACRO_H 1 diff --git a/gcc/d/dfrontend/mangle.c b/gcc/d/dfrontend/mangle.c index 9f606e589..2df1d8d04 100644 --- a/gcc/d/dfrontend/mangle.c +++ b/gcc/d/dfrontend/mangle.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2010 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/mangle.c + */ #include #include @@ -25,115 +26,105 @@ #include "module.h" char *toCppMangle(Dsymbol *s); +void toBuffer(OutBuffer *buf, const char *id, Dsymbol *s); -/****************************************************************************** - * isv : for the enclosing auto functions of an inner class/struct type. - * An aggregate type which defined inside auto function, it might - * become Voldemort Type so its object might be returned. - * This flag is necessary due to avoid mutual mangling - * between return type and enclosing scope. See bugzilla 8847. - */ -char *mangle(Declaration *sthis, bool isv) +void mangleFunc(OutBuffer *buf, FuncDeclaration *fd, bool inParent) { - OutBuffer buf; - char *id; - Dsymbol *s; + //printf("deco = '%s'\n", fd->type->deco ? fd->type->deco : "null"); + //printf("fd->type = %s\n", fd->type->toChars()); + if (fd->needThis() || fd->isNested()) + buf->writeByte(Type::needThisPrefix()); + if (inParent) + { + TypeFunction *tfx = (TypeFunction *)fd->type; + TypeFunction *tf = (TypeFunction *)fd->originalType; + + // replace with the actual parameter types + Parameters *prms = tf->parameters; + tf->parameters = tfx->parameters; - //printf("::mangle(%s)\n", sthis->toChars()); - s = sthis; - do + // do not mangle return type + Type *tret = tf->next; + tf->next = NULL; + + tf->toDecoBuffer(buf, 0); + + tf->parameters = prms; + tf->next = tret; + } + else if (fd->type->deco) { - //printf("mangle: s = %p, '%s', parent = %p\n", s, s->toChars(), s->parent); - if (s->getIdent()) + buf->writestring(fd->type->deco); + } + else + { + printf("[%s] %s %s\n", fd->loc.toChars(), fd->toChars(), fd->type->toChars()); + assert(0); // don't mangle function until semantic3 done. + } +} + +void mangleParent(OutBuffer *buf, Dsymbol *s) +{ + Dsymbol *p; + if (TemplateInstance *ti = s->isTemplateInstance()) + p = ti->isTemplateMixin() ? ti->parent : ti->tempdecl->parent; + else + p = s->parent; + + if (p) + { + mangleParent(buf, p); + + if (p->getIdent()) { - FuncDeclaration *fd = s->isFuncDeclaration(); - if (s != sthis && fd) - { - id = mangle(fd, isv); - buf.prependstring(id); - goto L1; - } - else - { - id = s->ident->toChars(); - size_t len = strlen(id); - char tmp[sizeof(len) * 3 + 1]; - buf.prependstring(id); - sprintf(tmp, "%d", (int)len); - buf.prependstring(tmp); - } + const char *id = p->ident->toChars(); + toBuffer(buf, id, s); + + if (FuncDeclaration *f = p->isFuncDeclaration()) + mangleFunc(buf, f, true); } else - buf.prependstring("0"); + buf->writeByte('0'); + } +} - TemplateInstance *ti = s->isTemplateInstance(); - if (ti && !ti->isTemplateMixin()) - s = ti->tempdecl->parent; - else - s = s->parent; - } while (s); - -// buf.prependstring("_D"); -L1: - //printf("deco = '%s'\n", sthis->type->deco ? sthis->type->deco : "null"); - //printf("sthis->type = %s\n", sthis->type->toChars()); - FuncDeclaration *fd = sthis->isFuncDeclaration(); - if (fd && (fd->needThis() || fd->isNested())) - buf.writeByte(Type::needThisPrefix()); - if (isv && fd && (fd->inferRetType || getFuncTemplateDecl(fd))) +void mangleDecl(OutBuffer *buf, Declaration *sthis) +{ + mangleParent(buf, sthis); + + assert(sthis->ident); + const char *id = sthis->ident->toChars(); + toBuffer(buf, id, sthis); + + if (FuncDeclaration *fd = sthis->isFuncDeclaration()) { -#if DDMD - TypeFunction *tfn = (TypeFunction *)sthis->type->copy(); - TypeFunction *tfo = (TypeFunction *)sthis->originalType; - tfn->purity = tfo->purity; - tfn->isnothrow = tfo->isnothrow; - tfn->isproperty = tfo->isproperty; - tfn->isref = fd->storage_class & STCauto ? false : tfo->isref; - tfn->trust = tfo->trust; - tfn->next = NULL; // do not mangle return type - tfn->toDecoBuffer(&buf, 0); -#else - TypeFunction tfn = *(TypeFunction *)sthis->type; - TypeFunction *tfo = (TypeFunction *)sthis->originalType; - tfn.purity = tfo->purity; - tfn.isnothrow = tfo->isnothrow; - tfn.isproperty = tfo->isproperty; - tfn.isref = fd->storage_class & STCauto ? false : tfo->isref; - tfn.trust = tfo->trust; - tfn.next = NULL; // do not mangle return type - tfn.toDecoBuffer(&buf, 0); -#endif + mangleFunc(buf, fd, false); } else if (sthis->type->deco) - buf.writestring(sthis->type->deco); - else { -#ifdef DEBUG - if (!fd->inferRetType) - printf("%s\n", fd->toChars()); -#endif - assert(fd && fd->inferRetType && fd->type->ty == Tfunction); - TypeFunction *tf = (TypeFunction *)sthis->type; - Type *tn = tf->next; - tf->next = NULL; // do not mangle undetermined return type - tf->toDecoBuffer(&buf, 0); - tf->next = tn; + buf->writestring(sthis->type->deco); } - - id = buf.toChars(); - buf.data = NULL; - return id; + else + assert(0); } -const char *Declaration::mangle(bool isv) +class Mangler : public Visitor { - char *p; +public: + const char *result; - //printf("Declaration::mangle(this = %p, '%s', parent = '%s', linkage = %d)\n", this, toChars(), parent ? parent->toChars() : "null", linkage); - if (!parent || parent->isModule() || linkage == LINKcpp) // if at global scope + Mangler() + { + result = NULL; + } + + void visit(Declaration *d) + { + //printf("Declaration::mangle(this = %p, '%s', parent = '%s', linkage = %d)\n", + // d, d->toChars(), d->parent ? d->parent->toChars() : "null", d->linkage); + if (!d->parent || d->parent->isModule() || d->linkage == LINKcpp) // if at global scope { - // If it's not a D declaration, no mangling - switch (linkage) + switch (d->linkage) { case LINKd: break; @@ -141,234 +132,263 @@ const char *Declaration::mangle(bool isv) case LINKc: case LINKwindows: case LINKpascal: - p = ident->toChars(); - goto Lret; + result = d->ident->toChars(); + break; case LINKcpp: - p = toCppMangle(this); - goto Lret; + result = toCppMangle(d); + break; case LINKdefault: - error("forward declaration"); - p = ident->toChars(); - goto Lret; + d->error("forward declaration"); + result = d->ident->toChars(); + break; default: - fprintf(stderr, "'%s', linkage = %d\n", toChars(), linkage); + fprintf(stderr, "'%s', linkage = %d\n", d->toChars(), d->linkage); assert(0); } } + + if (!result) { - p = ::mangle(this, isv); - OutBuffer buf; - buf.writestring("_D"); - buf.writestring(p); - p = buf.toChars(); - buf.data = NULL; + OutBuffer buf; + buf.writestring("_D"); + mangleDecl(&buf, d); + result = buf.extractString(); } - //printf("Declaration::mangle(this = %p, '%s', parent = '%s', linkage = %d) = %s\n", this, toChars(), parent ? parent->toChars() : "null", linkage, p); -Lret: -#ifdef DEBUG - size_t len = strlen(p); + #ifdef DEBUG + assert(result); + size_t len = strlen(result); assert(len > 0); - //printf("mangle: '%s' => '%s'\n", toChars(), p); for (size_t i = 0; i < len; i++) { - assert(p[i] == '_' || - p[i] == '@' || - isalnum(p[i]) || p[i] & 0x80); + assert(result[i] == '_' || + result[i] == '@' || + result[i] == '?' || + result[i] == '$' || + isalnum(result[i]) || result[i] & 0x80); } -#endif - return p; -} + #endif + } -/****************************************************************************** - * Normally FuncDeclaration and FuncAliasDeclaration have overloads. - * If and only if there is no overloads, mangle() could return - * exact mangled name. - * - * module test; - * void foo(long) {} // _D4test3fooFlZv - * void foo(string) {} // _D4test3fooFAyaZv - * - * // from FuncDeclaration::mangle(). - * pragma(msg, foo.mangleof); // prints unexact mangled name "4test3foo" - * // by calling Dsymbol::mangle() - * - * // from FuncAliasDeclaration::mangle() - * pragma(msg, __traits(getOverloads, test, "foo")[0].mangleof); // "_D4test3fooFlZv" - * pragma(msg, __traits(getOverloads, test, "foo")[1].mangleof); // "_D4test3fooFAyaZv" - * - * If a function has no overloads, .mangleof property still returns exact mangled name. - * - * void bar() {} - * pragma(msg, bar.mangleof); // still prints "_D4test3barFZv" - * // by calling FuncDeclaration::mangleExact(). - */ -const char *FuncDeclaration::mangle(bool isv) -{ - return isUnique() ? mangleExact(isv) : Dsymbol::mangle(isv); -} -// ditto -const char *FuncAliasDeclaration::mangle(bool isv) -{ - FuncDeclaration *f = toAliasFunc(); - FuncAliasDeclaration *fa = f->isFuncAliasDeclaration(); - if (!hasOverloads && !fa) - return f->mangleExact(isv); - if (fa) - return fa->mangle(isv); - return Dsymbol::mangle(isv); -} + /****************************************************************************** + * Normally FuncDeclaration and FuncAliasDeclaration have overloads. + * If and only if there is no overloads, mangle() could return + * exact mangled name. + * + * module test; + * void foo(long) {} // _D4test3fooFlZv + * void foo(string) {} // _D4test3fooFAyaZv + * + * // from FuncDeclaration::mangle(). + * pragma(msg, foo.mangleof); // prints unexact mangled name "4test3foo" + * // by calling Dsymbol::mangle() + * + * // from FuncAliasDeclaration::mangle() + * pragma(msg, __traits(getOverloads, test, "foo")[0].mangleof); // "_D4test3fooFlZv" + * pragma(msg, __traits(getOverloads, test, "foo")[1].mangleof); // "_D4test3fooFAyaZv" + * + * If a function has no overloads, .mangleof property still returns exact mangled name. + * + * void bar() {} + * pragma(msg, bar.mangleof); // still prints "_D4test3barFZv" + * // by calling FuncDeclaration::mangleExact(). + */ + void visit(FuncDeclaration *fd) + { + if (fd->isUnique()) + mangleExact(fd); + else + visit((Dsymbol *)fd); + } -/****************************************************************************** - * Returns exact mangled name of function. - */ -const char *FuncDeclaration::mangleExact(bool isv) -{ - assert(!isFuncAliasDeclaration()); + // ditto + void visit(FuncAliasDeclaration *fd) + { + FuncDeclaration *f = fd->toAliasFunc(); + FuncAliasDeclaration *fa = f->isFuncAliasDeclaration(); + if (!fd->hasOverloads && !fa) + { + mangleExact(f); + return; + } + if (fa) + { + fa->accept(this); + return; + } + visit((Dsymbol *)fd); + } + + void visit(OverDeclaration *od) + { + if (od->overnext) + { + visit((Dsymbol *)od); + return; + } - if (mangleOverride) - return mangleOverride; + if (FuncDeclaration *fd = od->aliassym->isFuncDeclaration()) + { + if (!od->hasOverloads || fd->isUnique()) + { + mangleExact(fd); + return; + } + } + if (TemplateDeclaration *td = od->aliassym->isTemplateDeclaration()) + { + if (!od->hasOverloads || td->overnext == NULL) + { + td->accept(this); + return; + } + } + visit((Dsymbol *)od); + } - if (isMain()) - return (char *)"_Dmain"; + void mangleExact(FuncDeclaration *fd) + { + assert(!fd->isFuncAliasDeclaration()); - if (isWinMain() || isDllMain() || ident == Id::tls_get_addr) - return ident->toChars(); + if (fd->mangleOverride) + { + result = fd->mangleOverride; + return; + } - assert(this); - return Declaration::mangle(isv); -} + if (fd->isMain()) + { + result = "_Dmain"; + return; + } -const char *VarDeclaration::mangle(bool isv) -{ - if (mangleOverride) - return mangleOverride; + if (fd->isWinMain() || fd->isDllMain() || fd->ident == Id::tls_get_addr) + { + result = fd->ident->toChars(); + return; + } - return Declaration::mangle(); -} + visit((Declaration *)fd); + } -const char *TypedefDeclaration::mangle(bool isv) -{ - //printf("TypedefDeclaration::mangle() '%s'\n", toChars()); - return Dsymbol::mangle(isv); -} + void visit(VarDeclaration *vd) + { + if (vd->mangleOverride) + { + result = vd->mangleOverride; + return; + } + visit((Declaration *)vd); + } -const char *AggregateDeclaration::mangle(bool isv) -{ - //printf("AggregateDeclaration::mangle() '%s'\n", toChars()); - if (Dsymbol *p = toParent2()) - { if (FuncDeclaration *fd = p->isFuncDeclaration()) - { // This might be the Voldemort Type - const char *id = Dsymbol::mangle(fd->inferRetType || getFuncTemplateDecl(fd)); - //printf("isv ad %s, %s\n", toChars(), id); - return id; + void visit(AggregateDeclaration *ad) + { + ClassDeclaration *cd = ad->isClassDeclaration(); + Dsymbol *parentsave = ad->parent; + if (cd) + { + /* These are reserved to the compiler, so keep simple + * names for them. + */ + if (cd->ident == Id::Exception && cd->parent->ident == Id::object || + cd->ident == Id::TypeInfo || + cd->ident == Id::TypeInfo_Struct || + cd->ident == Id::TypeInfo_Class || + cd->ident == Id::TypeInfo_Tuple || + cd == ClassDeclaration::object || + cd == Type::typeinfoclass || + cd == Module::moduleinfo || + strncmp(cd->ident->toChars(), "TypeInfo_", 9) == 0) + { + // Don't mangle parent + ad->parent = NULL; + } } + + visit((Dsymbol *)ad); + + ad->parent = parentsave; } - return Dsymbol::mangle(isv); -} + void visit(TemplateInstance *ti) + { + #if 0 + printf("TemplateInstance::mangle() %p %s", ti, ti->toChars()); + if (ti->parent) + printf(" parent = %s %s", ti->parent->kind(), ti->parent->toChars()); + printf("\n"); + #endif -const char *StructDeclaration::mangle(bool isv) -{ - //printf("StructDeclaration::mangle() '%s'\n", toChars()); - return AggregateDeclaration::mangle(isv); -} + OutBuffer buf; + if (!ti->tempdecl) + ti->error("is not defined"); + else + mangleParent(&buf, ti); -const char *ClassDeclaration::mangle(bool isv) -{ - Dsymbol *parentsave = parent; + ti->getIdent(); + const char *id = ti->ident ? ti->ident->toChars() : ti->toChars(); + toBuffer(&buf, id, ti); + id = buf.extractString(); - //printf("ClassDeclaration::mangle() %s.%s\n", parent->toChars(), toChars()); + //printf("TemplateInstance::mangle() %s = %s\n", ti->toChars(), ti->id); + result = id; + } - /* These are reserved to the compiler, so keep simple - * names for them. - */ - if (ident == Id::Exception) - { if (parent->ident == Id::object) - parent = NULL; + void visit(Dsymbol *s) + { + #if 0 + printf("Dsymbol::mangle() '%s'", s->toChars()); + if (s->parent) + printf(" parent = %s %s", s->parent->kind(), s->parent->toChars()); + printf("\n"); + #endif + + OutBuffer buf; + mangleParent(&buf, s); + + char *id = s->ident ? s->ident->toChars() : s->toChars(); + toBuffer(&buf, id, s); + id = buf.extractString(); + + //printf("Dsymbol::mangle() %s = %s\n", s->toChars(), id); + result = id; } - else if (ident == Id::TypeInfo || -// ident == Id::Exception || - ident == Id::TypeInfo_Struct || - ident == Id::TypeInfo_Class || - ident == Id::TypeInfo_Typedef || - ident == Id::TypeInfo_Tuple || - this == object || - this == Type::typeinfoclass || - this == Module::moduleinfo || - memcmp(ident->toChars(), "TypeInfo_", 9) == 0 - ) - parent = NULL; - - const char *id = AggregateDeclaration::mangle(isv); - parent = parentsave; - return id; -} +}; +const char *mangle(Dsymbol *s) +{ + Mangler v; + s->accept(&v); + return v.result; +} -const char *TemplateInstance::mangle(bool isv) +/****************************************************************************** + * Returns exact mangled name of function. + */ +const char *mangleExact(FuncDeclaration *fd) { - OutBuffer buf; - -#if 0 - printf("TemplateInstance::mangle() %p %s", this, toChars()); - if (parent) - printf(" parent = %s %s", parent->kind(), parent->toChars()); - printf("\n"); -#endif - getIdent(); - const char *id = ident ? ident->toChars() : toChars(); - if (!tempdecl) - error("is not defined"); - else - { - Dsymbol *par = isTemplateMixin() ? parent : tempdecl->parent; - if (par) - { - const char *p = par->mangle(isv); - if (p[0] == '_' && p[1] == 'D') - p += 2; - buf.writestring(p); - } - } - buf.printf("%llu%s", (ulonglong)strlen(id), id); - id = buf.toChars(); - buf.data = NULL; - //printf("TemplateInstance::mangle() %s = %s\n", toChars(), id); - return id; + Mangler v; + v.mangleExact(fd); + return v.result; } +/************************************************************ + * Write length prefixed string to buf. + */ -const char *Dsymbol::mangle(bool isv) +void toBuffer(OutBuffer *buf, const char *id, Dsymbol *s) { - OutBuffer buf; - char *id; - -#if 0 - printf("Dsymbol::mangle() '%s'", toChars()); - if (parent) - printf(" parent = %s %s", parent->kind(), parent->toChars()); - printf("\n"); -#endif - id = ident ? ident->toChars() : toChars(); - if (parent) + size_t len = strlen(id); + if (len >= 8 * 1024 * 1024) // 8 megs ought be enough for anyone + s->error("excessive length %llu for symbol, possible recursive expansion?", len); + else { - FuncDeclaration *f = parent->isFuncDeclaration(); - const char *p = f ? f->mangleExact(isv) : parent->mangle(isv); - if (p[0] == '_' && p[1] == 'D') - p += 2; - buf.writestring(p); + buf->printf("%llu", (ulonglong)len); + buf->write(id, len); } - buf.printf("%llu%s", (ulonglong)strlen(id), id); - id = buf.toChars(); - buf.data = NULL; - //printf("Dsymbol::mangle() %s = %s\n", toChars(), id); - return id; } - - diff --git a/gcc/d/dfrontend/mars.h b/gcc/d/dfrontend/mars.h index 17787d21a..9f998fbd9 100644 --- a/gcc/d/dfrontend/mars.h +++ b/gcc/d/dfrontend/mars.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/mars.h + */ #ifndef DMD_MARS_H #define DMD_MARS_H @@ -85,57 +86,57 @@ typedef Array Strings; // Put command line switches in here struct Param { - char obj; // write object file - char link; // perform link - char dll; // generate shared dynamic library - char lib; // write library file instead of object file(s) - char multiobj; // break one object file into multiple ones - char oneobj; // write one object file instead of multiple ones + bool obj; // write object file + bool link; // perform link + bool dll; // generate shared dynamic library + bool lib; // write library file instead of object file(s) + bool multiobj; // break one object file into multiple ones + bool oneobj; // write one object file instead of multiple ones bool trace; // insert profiling hooks - char quiet; // suppress non-error messages - char verbose; // verbose compile - char vtls; // identify thread local variables - char vfield; // identify non-mutable field variables + bool verbose; // verbose compile + bool showColumns; // print character (column) numbers in diagnostics + bool vtls; // identify thread local variables + char vgc; // identify gc usage + bool vfield; // identify non-mutable field variables char symdebug; // insert debug symbolic information bool alwaysframe; // always emit standard stack frame bool optimize; // run optimizer - char map; // generate linker .map file + bool map; // generate linker .map file bool is64bit; // generate 64 bit code - char isLP64; // generate code for LP64 - char isLinux; // generate code for linux - char isOSX; // generate code for Mac OSX - char isWindows; // generate code for Windows - char isFreeBSD; // generate code for FreeBSD - char isOpenBSD; // generate code for OpenBSD - char isSolaris; // generate code for Solaris - char scheduler; // which scheduler to use + bool isLP64; // generate code for LP64 + bool isLinux; // generate code for linux + bool isOSX; // generate code for Mac OSX + bool isWindows; // generate code for Windows + bool isFreeBSD; // generate code for FreeBSD + bool isOpenBSD; // generate code for OpenBSD + bool isSolaris; // generate code for Solaris char useDeprecated; // 0: don't allow use of deprecated features // 1: silently allow use of deprecated features // 2: warn about the use of deprecated features - char useAssert; // generate runtime code for assert()'s - char useInvariants; // generate class invariant checks - char useIn; // generate precondition checks - char useOut; // generate postcondition checks + bool useAssert; // generate runtime code for assert()'s + bool useInvariants; // generate class invariant checks + bool useIn; // generate precondition checks + bool useOut; // generate postcondition checks char useArrayBounds; // 0: no array bounds checks // 1: array bounds checks for safe functions only // 2: array bounds checks for all functions - char noboundscheck; // no array bounds checking at all bool stackstomp; // add stack stomping code - char useSwitchError; // check for switches without a default - char useUnitTests; // generate unittest code - char useInline; // inline expand functions - char release; // build release version - char preservePaths; // !=0 means don't strip path from source file + bool useSwitchError; // check for switches without a default + bool useUnitTests; // generate unittest code + bool useInline; // inline expand functions + bool release; // build release version + bool preservePaths; // true means don't strip path from source file char warnings; // 0: enable warnings // 1: warnings as errors // 2: informational warnings (no errors) bool pic; // generate position-independent-code for shared libs + bool color; // use ANSI colors in console output bool cov; // generate code coverage data unsigned char covPercent; // 0..100 code coverage percentage required bool nofloat; // code should not pull in floating point support - char ignoreUnsupportedPragmas; // rather than error on them - char enforcePropertySyntax; - char betterC; // be a "better C" compiler; no dependency on D runtime + bool ignoreUnsupportedPragmas; // rather than error on them + bool enforcePropertySyntax; + bool betterC; // be a "better C" compiler; no dependency on D runtime bool addMain; // add a default main() function bool allInst; // generate code for all template instantiations @@ -146,17 +147,17 @@ struct Param const char *objname; // .obj file output name const char *libname; // .lib file output name - char doDocComments; // process embedded documentation comments + bool doDocComments; // process embedded documentation comments const char *docdir; // write documentation file to docdir directory const char *docname; // write documentation file to docname Strings *ddocfiles; // macro include files for Ddoc - char doHdrGeneration; // process embedded documentation comments + bool doHdrGeneration; // process embedded documentation comments const char *hdrdir; // write 'header' file to docdir directory const char *hdrname; // write 'header' file to docname - char doXGeneration; // write JSON file - const char *xfilename; // write JSON file to xfilename + bool doJsonGeneration; // write JSON file + const char *jsonfilename; // write JSON file to jsonfilename unsigned debuglevel; // debug level Strings *debugids; // debug identifiers @@ -179,15 +180,15 @@ struct Param // Hidden debug switches char debuga; - char debugb; - char debugc; - char debugf; - char debugr; + bool debugb; + bool debugc; + bool debugf; + bool debugr; char debugw; - char debugx; - char debugy; + bool debugx; + bool debugy; - char run; // run resulting executable + bool run; // run resulting executable size_t runargs_length; const char** runargs; // arguments for executable @@ -195,6 +196,7 @@ struct Param Strings *objfiles; Strings *linkswitches; Strings *libfiles; + Strings *dllfiles; const char *deffile; const char *resfile; const char *exefile; @@ -210,14 +212,6 @@ typedef unsigned structalign_t; #define STRUCTALIGN_DEFAULT ((structalign_t) ~0) // magic value means "match whatever the underlying C compiler does" // other values are all powers of 2 -struct Ungag -{ - unsigned oldgag; - - Ungag(unsigned old) : oldgag(old) {} - ~Ungag(); -}; - struct Global { const char *mars_ext; @@ -248,13 +242,8 @@ struct Global unsigned gag; // !=0 means gag reporting of errors & warnings unsigned gaggedErrors; // number of errors reported while gagged - /* Gagging can either be speculative (is(typeof()), etc) - * or because of forward references + /* Start gagging. Return the current number of gagged errors */ - unsigned speculativeGag; // == gag means gagging is for is(typeof); - bool isSpeculativeGagging(); - - // Start gagging. Return the current number of gagged errors unsigned startGagging(); /* End gagging, restoring the old gagged state. @@ -312,14 +301,16 @@ struct Loc { const char *filename; unsigned linnum; + unsigned charnum; Loc() { linnum = 0; + charnum = 0; filename = NULL; } - Loc(Module *mod, unsigned linnum); + Loc(Module *mod, unsigned linnum, unsigned charnum); char *toChars(); bool equals(const Loc& loc); @@ -335,6 +326,10 @@ enum LINK LINKpascal, }; +// in hdrgen.c +void linkageToBuffer(OutBuffer *buf, LINK linkage); +const char *linkageToChars(LINK linkage); + enum DYNCAST { DYNCAST_OBJECT, diff --git a/gcc/d/dfrontend/module.c b/gcc/d/dfrontend/module.c index 8239a9d11..d35a0e8e4 100644 --- a/gcc/d/dfrontend/module.c +++ b/gcc/d/dfrontend/module.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/module.c + */ #include #include @@ -21,6 +22,7 @@ #include "import.h" #include "dsymbol.h" #include "hdrgen.h" +#include "expression.h" #include "lexer.h" #ifdef IN_GCC @@ -57,9 +59,13 @@ Module::Module(const char *filename, Identifier *ident, int doDocComment, int do numlines = 0; members = NULL; isDocFile = 0; + isPackageFile = false; needmoduleinfo = 0; selfimports = 0; insearch = 0; + searchCacheIdent = NULL; + searchCacheSymbol = NULL; + searchCacheFlags = 0; decldefs = NULL; massert = NULL; munittest = NULL; @@ -192,16 +198,14 @@ const char *Module::kind() } Module *Module::load(Loc loc, Identifiers *packages, Identifier *ident) -{ Module *m; - char *filename; - +{ //printf("Module::load(ident = '%s')\n", ident->toChars()); // Build module filename by turning: // foo.bar.baz // into: // foo\bar\baz - filename = ident->toChars(); + char *filename = ident->toChars(); if (packages && packages->dim) { OutBuffer buf; @@ -221,7 +225,7 @@ Module *Module::load(Loc loc, Identifiers *packages, Identifier *ident) filename = (char *)buf.extractData(); } - m = new Module(filename, ident, 0, 0); + Module *m = new Module(filename, ident, 0, 0); m->loc = loc; /* Look for the source file @@ -236,7 +240,8 @@ Module *Module::load(Loc loc, Identifiers *packages, Identifier *ident) if (packages) { for (size_t i = 0; i < packages->dim; i++) - { Identifier *pid = (*packages)[i]; + { + Identifier *pid = (*packages)[i]; fprintf(global.stdmsg, "%s.", pid->toChars()); } } @@ -331,6 +336,8 @@ void Module::parse() char *srcname = srcfile->name->toChars(); //printf("Module::parse(srcname = '%s')\n", srcname); + isPackageFile = (strcmp(srcfile->name->name(), "package.d") == 0); + utf8_t *buf = (utf8_t *)srcfile->buffer; size_t buflen = srcfile->len; @@ -557,8 +564,7 @@ void Module::parse() // Insert module into the symbol table Dsymbol *s = this; - bool isPackageMod = strcmp(srcfile->name->name(), "package.d") == 0; - if (isPackageMod) + if (isPackageFile) { /* If the source tree is as follows: * pkg/ @@ -594,7 +600,7 @@ void Module::parse() if (Module *mprev = prev->isModule()) { if (strcmp(srcname, mprev->srcfile->toChars()) == 0) - error(loc, "from file %s must be imported as module '%s'", + error(loc, "from file %s must be imported with 'import %s;'", srcname, toPrettyChars()); else error(loc, "from file %s conflicts with another module %s from file %s", @@ -602,7 +608,7 @@ void Module::parse() } else if (Package *pkg = prev->isPackage()) { - if (pkg->isPkgMod == PKGunknown && isPackageMod) + if (pkg->isPkgMod == PKGunknown && isPackageFile) { /* If the previous inserted Package is not yet determined as package.d, * link it to the actual module. @@ -611,7 +617,7 @@ void Module::parse() pkg->mod = this; } else - error(pkg->loc, "from file %s conflicts with package name %s", + error(md ? md->loc : loc, "from file %s conflicts with package name %s", srcname, pkg->toChars()); } else @@ -637,6 +643,14 @@ void Module::importAll(Scope *prevsc) return; } + if (md && md->msg) + { + if (StringExp *se = md->msg->toStringExp()) + md->msg = se; + else + md->msg->error("string expected, not '%s'", md->msg->toChars()); + } + /* Note that modules get their own scope, from scratch. * This is so regardless of where in the syntax a module * gets imported, it is unaffected by context. @@ -661,7 +675,7 @@ void Module::importAll(Scope *prevsc) for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; - s->addMember(NULL, sc->scopesym, 1); + s->addMember(sc, sc->scopesym, 1); } } // anything else should be run after addMember, so version/debug symbols are defined @@ -707,34 +721,6 @@ void Module::semantic() //printf("Module = %p, linkage = %d\n", sc->scopesym, sc->linkage); -#if 0 - // Add import of "object" if this module isn't "object" - if (ident != Id::object) - { - Import *im = new Import(0, NULL, Id::object, NULL, 0); - members->shift(im); - } - - // Add all symbols into module's symbol table - symtab = new DsymbolTable(); - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (Dsymbol *)members->data[i]; - s->addMember(NULL, sc->scopesym, 1); - } - - /* Set scope for the symbols so that if we forward reference - * a symbol, it can possibly be resolved on the spot. - * If this works out well, it can be extended to all modules - * before any semantic() on any of them. - */ - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (Dsymbol *)members->data[i]; - s->setScope(sc); - } -#endif - // Pass 1 semantic routines: do public side of the definition for (size_t i = 0; i < members->dim; i++) { @@ -756,16 +742,6 @@ void Module::semantic() void Module::semantic2() { - if (deferred.dim) - { - for (size_t i = 0; i < deferred.dim; i++) - { - Dsymbol *sd = deferred[i]; - - sd->error("unable to resolve forward reference in definition"); - } - return; - } //printf("Module::semantic2('%s'): parent = %p\n", toChars(), parent); if (semanticRun != PASSsemanticdone) // semantic() not completed yet - could be recursive call return; @@ -816,27 +792,6 @@ void Module::semantic3() semanticRun = PASSsemantic3done; } -void Module::inlineScan() -{ - if (semanticRun != PASSsemantic3done) - return; - semanticRun = PASSinline; - - // Note that modules get their own scope, from scratch. - // This is so regardless of where in the syntax a module - // gets imported, it is unaffected by context. - //printf("Module = %p\n", sc.scopesym); - - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (*members)[i]; - //if (global.params.verbose) - // fprintf(global.stdmsg, "inline scan symbol %s\n", s->toChars()); - s->inlineScan(); - } - semanticRun = PASSinlinedone; -} - /**************************************************** */ @@ -882,18 +837,47 @@ Dsymbol *Module::search(Loc loc, Identifier *ident, int flags) */ //printf("%s Module::search('%s', flags = %d) insearch = %d\n", toChars(), ident->toChars(), flags, insearch); - Dsymbol *s; if (insearch) - s = NULL; - else + return NULL; + if (searchCacheIdent == ident && searchCacheFlags == flags) + { + //printf("%s Module::search('%s', flags = %d) insearch = %d searchCacheSymbol = %s\n", + // toChars(), ident->toChars(), flags, insearch, searchCacheSymbol ? searchCacheSymbol->toChars() : "null"); + return searchCacheSymbol; + } + + unsigned int errors = global.errors; + + insearch = 1; + Dsymbol *s = ScopeDsymbol::search(loc, ident, flags); + insearch = 0; + + if (errors == global.errors) { - insearch = 1; - s = ScopeDsymbol::search(loc, ident, flags); - insearch = 0; + // Bugzilla 10752: We can cache the result only when it does not cause + // access error so the side-effect should be reproduced in later search. + searchCacheIdent = ident; + searchCacheSymbol = s; + searchCacheFlags = flags; } return s; } +Dsymbol *Module::symtabInsert(Dsymbol *s) +{ + searchCacheIdent = NULL; // symbol is inserted, so invalidate cache + return Package::symtabInsert(s); +} + +void Module::clearCache() +{ + for (size_t i = 0; i < amodules.dim; i++) + { + Module *m = amodules[i]; + m->searchCacheIdent = NULL; + } +} + /******************************************* * Can't run semantic on s now, try again later. */ @@ -1064,6 +1048,8 @@ ModuleDeclaration::ModuleDeclaration(Loc loc, Identifiers *packages, Identifier this->packages = packages; this->id = id; this->safe = safe; + this->isdeprecated = false; + this->msg = NULL; } char *ModuleDeclaration::toChars() @@ -1080,8 +1066,7 @@ char *ModuleDeclaration::toChars() } } buf.writestring(id->toChars()); - buf.writeByte(0); - return (char *)buf.extractData(); + return buf.extractString(); } /* =========================== Package ===================== */ @@ -1099,6 +1084,15 @@ const char *Package::kind() return "package"; } +Module *Package::isPackageMod() +{ + if (isPkgMod == PKGmodule) + { + return mod; + } + return NULL; +} + /**************************************************** * Input: * packages[] the pkg1.pkg2 of pkg1.pkg2.mod @@ -1251,5 +1245,3 @@ const char *lookForSourceFile(const char *filename) } return NULL; } - - diff --git a/gcc/d/dfrontend/module.h b/gcc/d/dfrontend/module.h index d49b03e93..21b0eb4e1 100644 --- a/gcc/d/dfrontend/module.h +++ b/gcc/d/dfrontend/module.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/module.h + */ #ifndef DMD_MODULE_H #define DMD_MODULE_H @@ -52,9 +53,11 @@ class Package : public ScopeDsymbol Package *isPackage() { return this; } - virtual void semantic(Scope *) { } + void semantic(Scope *) { } Dsymbol *search(Loc loc, Identifier *ident, int flags = IgnoreNone); void accept(Visitor *v) { v->visit(this); } + + Module *isPackageMod(); }; class Module : public Package @@ -81,12 +84,16 @@ class Module : public Package unsigned errors; // if any errors in file unsigned numlines; // number of lines in source file int isDocFile; // if it is a documentation input file, not D source + bool isPackageFile; // if it is a package.d int needmoduleinfo; int selfimports; // 0: don't know, 1: does not, 2: does int selfImports(); // returns !=0 if module imports itself int insearch; + Identifier *searchCacheIdent; + Dsymbol *searchCacheSymbol; // cached value of search + int searchCacheFlags; // cached flags Module *importedFrom; // module from command line we're imported from, // i.e. a module that will be taken all the @@ -116,7 +123,6 @@ class Module : public Package static Module *load(Loc loc, Identifiers *packages, Identifier *ident); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); const char *kind(); File *setOutfile(const char *name, const char *dir, const char *arg, const char *ext); void setDocfile(); @@ -126,18 +132,18 @@ class Module : public Package void semantic(); // semantic analysis void semantic2(); // pass 2 semantic analysis void semantic3(); // pass 3 semantic analysis - void inlineScan(); // scan for functions to inline - void genhdrfile(); // generate D import file - void genobjfile(int multiobj); + void genobjfile(bool multiobj); + void genhelpers(bool iscomdat); void gensymfile(); - void gendocfile(); int needModuleInfo(); Dsymbol *search(Loc loc, Identifier *ident, int flags = IgnoreNone); + Dsymbol *symtabInsert(Dsymbol *s); void deleteObjFile(); static void addDeferredSemantic(Dsymbol *s); static void runDeferredSemantic(); static void addDeferredSemantic3(Dsymbol *s); static void runDeferredSemantic3(); + static void clearCache(); int imports(Module *m); bool isRoot() { return this->importedFrom == this; } @@ -169,8 +175,6 @@ class Module : public Package Symbol *toModuleArray(); // get module array bounds function - elem *toEfilename(); - Symbol *toSymbol(); void genmoduleinfo(); @@ -185,6 +189,8 @@ struct ModuleDeclaration Identifier *id; Identifiers *packages; // array of Identifier's representing packages bool safe; + bool isdeprecated; // if it is a deprecated module + Expression *msg; ModuleDeclaration(Loc loc, Identifiers *packages, Identifier *id, bool safe); diff --git a/gcc/d/dfrontend/mtype.c b/gcc/d/dfrontend/mtype.c index e0f25bde8..ccd62449f 100644 --- a/gcc/d/dfrontend/mtype.c +++ b/gcc/d/dfrontend/mtype.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/mtype.c + */ #define __C99FEATURES__ 1 // Needed on Solaris for NaN and more #define __USE_ISOC99 1 // so signbit() gets defined @@ -44,7 +45,7 @@ #include "hdrgen.h" FuncDeclaration *hasThis(Scope *sc); -void sizeToCBuffer(OutBuffer *buf, HdrGenState *hgs, Expression *e); +void toCBuffer(Type *t, OutBuffer *buf, Identifier *ident, HdrGenState *hgs); #define LOGDOTEXP 0 // log ::dotExp() #define LOGDEFAULTINIT 0 // log ::defaultInit() @@ -64,7 +65,6 @@ ClassDeclaration *Type::dtypeinfo; ClassDeclaration *Type::typeinfoclass; ClassDeclaration *Type::typeinfointerface; ClassDeclaration *Type::typeinfostruct; -ClassDeclaration *Type::typeinfotypedef; ClassDeclaration *Type::typeinfopointer; ClassDeclaration *Type::typeinfoarray; ClassDeclaration *Type::typeinfostaticarray; @@ -79,7 +79,6 @@ ClassDeclaration *Type::typeinfoinvariant; ClassDeclaration *Type::typeinfoshared; ClassDeclaration *Type::typeinfowild; -TemplateDeclaration *Type::associativearray; TemplateDeclaration *Type::rtinfo; Type *Type::tvoid; @@ -174,9 +173,9 @@ bool Type::equals(RootObject *o) { Type *t = (Type *)o; //printf("Type::equals(%s, %s)\n", toChars(), t->toChars()); - if (this == o || - ((t && deco == t->deco) && // deco strings are unique - deco != NULL)) // and semantic() has been run + // deco strings are unique + // and semantic() has been run + if (this == o || ((t && deco == t->deco) && deco != NULL)) { //printf("deco = '%s', t->deco = '%s'\n", deco, t->deco); return true; @@ -208,7 +207,6 @@ void Type::init() sizeTy[Tinstance] = sizeof(TypeInstance); sizeTy[Ttypeof] = sizeof(TypeTypeof); sizeTy[Tenum] = sizeof(TypeEnum); - sizeTy[Ttypedef] = sizeof(TypeTypedef); sizeTy[Tstruct] = sizeof(TypeStruct); sizeTy[Tclass] = sizeof(TypeClass); sizeTy[Ttuple] = sizeof(TypeTuple); @@ -227,7 +225,6 @@ void Type::init() mangleChar[Tclass] = 'C'; mangleChar[Tstruct] = 'S'; mangleChar[Tenum] = 'E'; - mangleChar[Ttypedef] = 'T'; mangleChar[Tdelegate] = 'D'; mangleChar[Tnone] = 'n'; @@ -329,7 +326,7 @@ void Type::init() tvoidptr = tvoid->pointerTo(); tstring = tchar->immutableOf()->arrayOf(); - tvalist = tvoid->pointerTo(); + tvalist = Target::va_listType(); if (global.params.isLP64) { @@ -1040,11 +1037,25 @@ Type *Type::addSTC(StorageClass stc) return t; } +/************************************ + * Convert MODxxxx to STCxxx + */ + +StorageClass ModToStc(unsigned mod) +{ + StorageClass stc = 0; + if (mod & MODimmutable) stc |= STCimmutable; + if (mod & MODconst) stc |= STCconst; + if (mod & MODwild) stc |= STCwild; + if (mod & MODshared) stc |= STCshared; + return stc; +} + /************************************ * Apply MODxxxx bits to existing type. */ -Type *Type::castMod(unsigned mod) +Type *Type::castMod(MOD mod) { Type *t; switch (mod) @@ -1097,7 +1108,7 @@ Type *Type::castMod(unsigned mod) * a shared type => "shared const" */ -Type *Type::addMod(unsigned mod) +Type *Type::addMod(MOD mod) { /* Add anything to immutable, and it remains immutable */ @@ -1205,7 +1216,7 @@ Type *Type::addStorageClass(StorageClass stc) { /* Just translate to MOD bits and let addMod() do the work */ - unsigned mod = 0; + MOD mod = 0; if (stc & STCimmutable) mod = MODimmutable; @@ -1296,6 +1307,8 @@ Type *Type::aliasthisOf() else if (d->isFuncDeclaration()) { FuncDeclaration *fd = resolveFuncCall(Loc(), NULL, d, NULL, this, NULL, 1); + if (fd && fd->errors) + return Type::terror; if (fd && !fd->type->nextOf() && !fd->functionSemantic()) fd = NULL; if (fd) @@ -1319,6 +1332,8 @@ Type *Type::aliasthisOf() { assert(td->scope); FuncDeclaration *fd = resolveFuncCall(Loc(), NULL, td, NULL, this, NULL, 1); + if (fd && fd->errors) + return Type::terror; if (fd && fd->functionSemantic()) { Type *t = fd->type->nextOf(); @@ -1333,7 +1348,7 @@ Type *Type::aliasthisOf() return NULL; } -int Type::checkAliasThisRec() +bool Type::checkAliasThisRec() { Type *tb = toBasetype(); AliasThisRec* pflag; @@ -1342,7 +1357,7 @@ int Type::checkAliasThisRec() else if (tb->ty == Tclass) pflag = &((TypeClass *)tb)->att; else - return 0; + return false; AliasThisRec flag = (AliasThisRec)(*pflag & RECtypeMask); if (flag == RECfwdref) @@ -1372,10 +1387,10 @@ Type *Type::toBasetype() /*************************** * Return !=0 if modfrom can be implicitly converted to modto */ -int MODimplicitConv(unsigned char modfrom, unsigned char modto) +bool MODimplicitConv(MOD modfrom, MOD modto) { if (modfrom == modto) - return 1; + return true; //printf("MODimplicitConv(from = %x, to = %x)\n", modfrom, modto); #define X(m, n) (((m) << 4) | (n)) @@ -1389,10 +1404,10 @@ int MODimplicitConv(unsigned char modfrom, unsigned char modto) case X(MODimmutable, MODconst): case X(MODimmutable, MODwildconst): - return 1; + return true; default: - return 0; + return false; } #undef X } @@ -1400,10 +1415,10 @@ int MODimplicitConv(unsigned char modfrom, unsigned char modto) /*************************** * Return !=0 if a method of type '() modfrom' can call a method of type '() modto'. */ -int MODmethodConv(unsigned char modfrom, unsigned char modto) +bool MODmethodConv(MOD modfrom, MOD modto) { if (MODimplicitConv(modfrom, modto)) - return 1; + return true; #define X(m, n) (((m) << 4) | (n)) switch (X(modfrom, modto)) @@ -1414,10 +1429,10 @@ int MODmethodConv(unsigned char modfrom, unsigned char modto) case X(MODshared, MODshared|MODwild): case X(MODshared|MODimmutable, MODshared|MODwild): case X(MODshared|MODconst, MODshared|MODwild): - return 1; + return true; default: - return 0; + return false; } #undef X } @@ -1425,13 +1440,13 @@ int MODmethodConv(unsigned char modfrom, unsigned char modto) /*************************** * Merge mod bits to form common mod. */ -unsigned char MODmerge(unsigned char mod1, unsigned char mod2) +MOD MODmerge(MOD mod1, MOD mod2) { if (mod1 == mod2) return mod1; //printf("MODmerge(1 = %x, 2 = %x)\n", mod1, mod2); - unsigned char result = 0; + MOD result = 0; if ((mod1 | mod2) & MODshared) { // If either type is shared, the result will be shared @@ -1459,7 +1474,7 @@ unsigned char MODmerge(unsigned char mod1, unsigned char mod2) /********************************* * Mangling for mod. */ -void MODtoDecoBuffer(OutBuffer *buf, unsigned char mod) +void MODtoDecoBuffer(OutBuffer *buf, MOD mod) { switch (mod) { case 0: @@ -1496,7 +1511,7 @@ void MODtoDecoBuffer(OutBuffer *buf, unsigned char mod) /********************************* * Store modifier name into buf. */ -void MODtoBuffer(OutBuffer *buf, unsigned char mod) +void MODtoBuffer(OutBuffer *buf, MOD mod) { switch (mod) { @@ -1546,19 +1561,18 @@ void MODtoBuffer(OutBuffer *buf, unsigned char mod) /********************************* * Return modifier name. */ -char *MODtoChars(unsigned char mod) +char *MODtoChars(MOD mod) { OutBuffer buf; buf.reserve(16); MODtoBuffer(&buf, mod); - buf.writebyte(0); - return buf.extractData(); + return buf.extractString(); } /******************************** * Name mangling. * Input: - * flag 0x100 do not do const/invariant + * flag 0x100 do not do modifiers */ void Type::toDecoBuffer(OutBuffer *buf, int flag) @@ -1581,60 +1595,23 @@ char *Type::toChars() HdrGenState hgs; toCBuffer(&buf, NULL, &hgs); - buf.writebyte(0); - return buf.extractData(); + return buf.extractString(); } -void Type::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs) +char *Type::toPrettyChars(bool QualifyTypes) { - toCBuffer2(buf, hgs, 0); - if (ident) - { - buf->writeByte(' '); - buf->writestring(ident->toChars()); - } -} + OutBuffer buf; + buf.reserve(16); + HdrGenState hgs; + hgs.fullQualification = QualifyTypes; -void Type::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - if (mod != this->mod) - { - toCBuffer3(buf, hgs, mod); - return; - } - buf->writestring(toChars()); + toCBuffer(&buf, NULL, &hgs); + return buf.extractString(); } -void Type::toCBuffer3(OutBuffer *buf, HdrGenState *hgs, int mod) +void Type::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs) { - if (mod != this->mod) - { - unsigned char m = this->mod & ~(this->mod & mod); - if (m & MODshared) - { - MODtoBuffer(buf, MODshared); - buf->writeByte('('); - } - if (m & MODwild) - { - MODtoBuffer(buf, MODwild); - buf->writeByte('('); - } - if (m & (MODconst | MODimmutable)) - { - MODtoBuffer(buf, m & (MODconst | MODimmutable)); - buf->writeByte('('); - } - - toCBuffer2(buf, hgs, this->mod); - - if (m & (MODconst | MODimmutable)) - buf->writeByte(')'); - if (m & MODwild) - buf->writeByte(')'); - if (m & MODshared) - buf->writeByte(')'); - } + ::toCBuffer(this, buf, ident, hgs); } /********************************* @@ -1657,8 +1634,24 @@ char *Type::modToChars() OutBuffer buf; buf.reserve(16); modToBuffer(&buf); - buf.writebyte(0); - return buf.extractData(); + return buf.extractString(); +} + +/** For each active modifier (MODconst, MODimmutable, etc) call fp with a +void* for the work param and a string representation of the attribute. */ +int Type::modifiersApply(void *param, int (*fp)(void *, const char *)) +{ + static unsigned char modsArr[] = { MODconst, MODimmutable, MODwild, MODshared }; + + for (size_t idx = 0; idx < 4; ++idx) + { + if (mod & modsArr[idx]) + { + if (int res = fp(param, MODtoChars(modsArr[idx]))) + return res; + } + } + return 0; } /************************************ @@ -1719,7 +1712,8 @@ Type *stripDefaultArgs(Type *t) Parameters *args = N::stripParams(tt->arguments); if (args == tt->arguments) goto Lnot; - t = new TypeTuple(args); + t = t->copy(); + ((TypeTuple *)t)->arguments = args; } else if (t->ty == Tenum) { @@ -1847,12 +1841,12 @@ ClassDeclaration *Type::isClassHandle() return NULL; } -int Type::isscope() +bool Type::isscope() { return false; } -int Type::isString() +bool Type::isString() { return false; } @@ -1865,12 +1859,12 @@ int Type::isString() * a = b; * ? */ -int Type::isAssignable() +bool Type::isAssignable() { return true; } -int Type::checkBoolean() +bool Type::checkBoolean() { return isscalar(); } @@ -1879,7 +1873,7 @@ int Type::checkBoolean() * true if when type goes out of scope, it needs a destructor applied. * Only applies to value types, not ref types. */ -int Type::needsDestruction() +bool Type::needsDestruction() { return false; } @@ -1926,9 +1920,9 @@ Expression *Type::defaultInitLiteral(Loc loc) return defaultInit(loc); } -int Type::isZeroInit(Loc loc) +bool Type::isZeroInit(Loc loc) { - return 0; // assume not + return false; // assume not } int Type::isBaseOf(Type *t, int *poffset) @@ -1976,7 +1970,7 @@ MATCH Type::constConv(Type *to) * Return MOD bits matching this type to wild parameter type (tprm). */ -unsigned Type::deduceWild(Type *t, bool isRef) +unsigned char Type::deduceWild(Type *t, bool isRef) { //printf("Type::deduceWild this = '%s', tprm = '%s'\n", toChars(), tprm->toChars()); @@ -1985,7 +1979,12 @@ unsigned Type::deduceWild(Type *t, bool isRef) if (isImmutable()) return MODimmutable; else if (isWildConst()) - return MODwildconst; + { + if (t->isWildConst()) + return MODwild; + else + return MODwildconst; + } else if (isWild()) return MODwild; else if (isConst()) @@ -2003,7 +2002,7 @@ Type *Type::unqualify(unsigned m) Type *t = mutableOf()->unSharedOf(); Type *tn = nextOf(); - if (tn && tn->ty != Tfunction/*!(ty == Tpointer && tn->ty == Tfunction)*/) + if (tn && tn->ty != Tfunction) { Type *utn = tn->unqualify(m); if (utn != tn) @@ -2143,6 +2142,7 @@ Type *TypeFunction::substWildTo(unsigned) TypeFunction *t = new TypeFunction(params, tret, varargs, linkage); t->mod = ((mod & MODwild) ? (mod & ~MODwild) | MODconst : mod); t->isnothrow = isnothrow; + t->isnogc = isnogc; t->purity = purity; t->isproperty = isproperty; t->isref = isref; @@ -2226,7 +2226,7 @@ Expression *Type::getProperty(Loc loc, Identifier *ident, int flag) else { Dsymbol *s = NULL; - if (ty == Tstruct || ty == Tclass || ty == Tenum || ty == Ttypedef) + if (ty == Tstruct || ty == Tclass || ty == Tenum) s = toDsymbol(NULL); if (s) s = s->search_correct(ident); @@ -2476,20 +2476,6 @@ void Type::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, *ps = NULL; } -/******************************* - * tparams == NULL: - * If one of the subtypes of this type is a TypeIdentifier, - * i.e. it's an unresolved type, return that type. - * tparams != NULL: - * Only when the TypeIdentifier is one of template parameters, - * return that type. - */ - -Type *Type::reliesOnTident(TemplateParameters *tparams) -{ - return NULL; -} - /*************************************** * Return !=0 if the type or any of its subtypes is wild. */ @@ -2582,11 +2568,6 @@ Type *TypeError::syntaxCopy() return this; } -void TypeError::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs) -{ - buf->writestring("_error_"); -} - d_uns64 TypeError::size(Loc loc) { return SIZE_INVALID; } Expression *TypeError::getProperty(Loc loc, Identifier *ident, int flag) { return new ErrorExp(); } Expression *TypeError::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag) { return new ErrorExp(); } @@ -2616,14 +2597,12 @@ void TypeNext::checkDeprecated(Loc loc, Scope *sc) next->checkDeprecated(loc, sc); } - -Type *TypeNext::reliesOnTident(TemplateParameters *tparams) -{ - return next->reliesOnTident(tparams); -} - int TypeNext::hasWild() { + if (ty == Tfunction) + return 0; + if (ty == Tdelegate) + return Type::hasWild(); return mod & MODwild || (next && next->hasWild()); } @@ -2666,8 +2645,6 @@ Type *TypeNext::makeConst() t->next = next->constOf(); } } - if (ty == Taarray) - ((TypeAArray *)t)->impl = NULL; // lazily recompute it //printf("TypeNext::makeConst() returns %p, %s\n", t, t->toChars()); return t; } @@ -2686,9 +2663,6 @@ Type *TypeNext::makeImmutable() { t->next = next->immutableOf(); } - if (ty == Taarray) - ((TypeAArray *)t)->impl = NULL; // lazily recompute it - //printf("TypeNext::makeImmutable() returns %p, %s\n", t, t->toChars()); return t; } @@ -2719,8 +2693,6 @@ Type *TypeNext::makeShared() t->next = next->sharedOf(); } } - if (ty == Taarray) - ((TypeAArray *)t)->impl = NULL; // lazily recompute it //printf("TypeNext::makeShared() returns %p, %s\n", t, t->toChars()); return t; } @@ -2742,8 +2714,6 @@ Type *TypeNext::makeSharedConst() else t->next = next->sharedConstOf(); } - if (ty == Taarray) - ((TypeAArray *)t)->impl = NULL; // lazily recompute it //printf("TypeNext::makeSharedConst() returns %p, %s\n", t, t->toChars()); return t; } @@ -2775,8 +2745,6 @@ Type *TypeNext::makeWild() t->next = next->wildOf(); } } - if (ty == Taarray) - ((TypeAArray *)t)->impl = NULL; // lazily recompute it //printf("TypeNext::makeWild() returns %p, %s\n", t, t->toChars()); return t; } @@ -2798,8 +2766,6 @@ Type *TypeNext::makeWildConst() else t->next = next->wildConstOf(); } - if (ty == Taarray) - ((TypeAArray *)t)->impl = NULL; // lazily recompute it //printf("TypeNext::makeWildConst() returns %p, %s\n", t, t->toChars()); return t; } @@ -2821,8 +2787,6 @@ Type *TypeNext::makeSharedWild() else t->next = next->sharedWildOf(); } - if (ty == Taarray) - ((TypeAArray *)t)->impl = NULL; // lazily recompute it //printf("TypeNext::makeSharedWild() returns %p, %s\n", t, t->toChars()); return t; } @@ -2841,10 +2805,6 @@ Type *TypeNext::makeSharedWildConst() { t->next = next->sharedWildConstOf(); } - if (ty == Taarray) - { - ((TypeAArray *)t)->impl = NULL; // lazily recompute it - } //printf("TypeNext::makeSharedWildConst() returns %p, %s\n", t, t->toChars()); return t; } @@ -2857,8 +2817,6 @@ Type *TypeNext::makeMutable() { t->next = next->mutableOf(); } - if (ty == Taarray) - ((TypeAArray *)t)->impl = NULL; // lazily recompute it //printf("TypeNext::makeMutable() returns %p, %s\n", t, t->toChars()); return t; } @@ -2890,12 +2848,12 @@ MATCH TypeNext::constConv(Type *to) return m; } -unsigned TypeNext::deduceWild(Type *t, bool isRef) +unsigned char TypeNext::deduceWild(Type *t, bool isRef) { if (ty == Tfunction) return 0; - unsigned wm; + unsigned char wm; Type *tn = t->nextOf(); if (!isRef && (ty == Tarray || ty == Tpointer) && tn) @@ -3060,16 +3018,6 @@ char *TypeBasic::toChars() return Type::toChars(); } -void TypeBasic::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - //printf("TypeBasic::toCBuffer2(mod = %d, this->mod = %d)\n", mod, this->mod); - if (mod != this->mod) - { toCBuffer3(buf, hgs, mod); - return; - } - buf->writestring(dstring); -} - d_uns64 TypeBasic::size(Loc loc) { unsigned size; @@ -3187,7 +3135,7 @@ Expression *TypeBasic::getProperty(Loc loc, Identifier *ident, int flag) case Tcomplex80: case Timaginary80: case Tfloat80: - deprecation(loc, "min property is deprecated, use min_normal instead"); + deprecation(loc, ".min property is deprecated, use .min_normal instead"); goto Lmin_normal; } } @@ -3496,7 +3444,7 @@ Expression *TypeBasic::defaultInit(Loc loc) return new IntegerExp(loc, value, this); } -int TypeBasic::isZeroInit(Loc loc) +bool TypeBasic::isZeroInit(Loc loc) { switch (ty) { @@ -3512,9 +3460,9 @@ int TypeBasic::isZeroInit(Loc loc) case Tcomplex32: case Tcomplex64: case Tcomplex80: - return 0; // no + return false; // no default: - return 1; // yes + return true; // yes } } @@ -3665,7 +3613,7 @@ Type *TypeVector::syntaxCopy() Type *TypeVector::semantic(Loc loc, Scope *sc) { - int errors = global.errors; + unsigned int errors = global.errors; basetype = basetype->semantic(loc, sc); if (errors != global.errors) return terror; @@ -3704,7 +3652,7 @@ TypeBasic *TypeVector::elementType() return tb; } -int TypeVector::checkBoolean() +bool TypeVector::checkBoolean() { return false; } @@ -3714,18 +3662,6 @@ char *TypeVector::toChars() return Type::toChars(); } -void TypeVector::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - //printf("TypeVector::toCBuffer2(mod = %d, this->mod = %d)\n", mod, this->mod); - if (mod != this->mod) - { toCBuffer3(buf, hgs, mod); - return; - } - buf->writestring("__vector("); - basetype->toCBuffer2(buf, hgs, this->mod); - buf->writestring(")"); -} - void TypeVector::toDecoBuffer(OutBuffer *buf, int flag) { if (flag != mod && flag != 0x100) @@ -3758,7 +3694,10 @@ Expression *TypeVector::dotExp(Scope *sc, Expression *e, Identifier *ident, int #endif if (ident == Id::array) { - e = e->castTo(sc, basetype); + //e = e->castTo(sc, basetype); + // Keep lvalue-ness + e = e->copy(); + e->type = basetype; return e; } if (ident == Id::offsetof || ident == Id::offset || ident == Id::stringof) @@ -3780,7 +3719,7 @@ Expression *TypeVector::defaultInitLiteral(Loc loc) return basetype->defaultInitLiteral(loc); } -int TypeVector::isZeroInit(Loc loc) +bool TypeVector::isZeroInit(Loc loc) { return basetype->isZeroInit(loc); } @@ -3816,11 +3755,6 @@ MATCH TypeVector::implicitConvTo(Type *to) return MATCHnomatch; } -Type *TypeVector::reliesOnTident(TemplateParameters *tparams) -{ - return basetype->reliesOnTident(tparams); -} - /***************************** TypeArray *****************************/ TypeArray::TypeArray(TY ty, Type *next) @@ -3838,8 +3772,7 @@ Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int f if (e->op == TOKtype) { - if (ident == Id::sort || ident == Id::reverse || - ident == Id::dup || ident == Id::idup) + if (ident == Id::sort || ident == Id::reverse) { e->error("%s is not an expression", e->toChars()); return new ErrorExp(); @@ -3862,7 +3795,7 @@ Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int f Parameters *args = new Parameters; Type *next = n->ty == Twchar ? Type::twchar : Type::tchar; Type *arrty = next->arrayOf(); - args->push(new Parameter(STCin, arrty, NULL, NULL)); + args->push(new Parameter(0, arrty, NULL, NULL)); reverseFd[i] = FuncDeclaration::genCfunc(args, arrty, reverseName[i]); } @@ -3883,7 +3816,7 @@ Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int f Parameters *args = new Parameters; Type *next = n->ty == Twchar ? Type::twchar : Type::tchar; Type *arrty = next->arrayOf(); - args->push(new Parameter(STCin, arrty, NULL, NULL)); + args->push(new Parameter(0, arrty, NULL, NULL)); sortFd[i] = FuncDeclaration::genCfunc(args, arrty, sortName[i]); } @@ -3894,68 +3827,31 @@ Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int f e = new CallExp(e->loc, ec, arguments); e->type = next->arrayOf(); } - else if (ident == Id::reverse || ident == Id::dup || ident == Id::idup) + else if (ident == Id::reverse) { Expression *ec; FuncDeclaration *fd; Expressions *arguments; dinteger_t size = next->size(e->loc); - int dup; - Expression *olde = e; assert(size); - dup = (ident == Id::dup || ident == Id::idup); - - if (dup) { - static FuncDeclaration *adDup_fd = NULL; - if (!adDup_fd) { - Parameters* args = new Parameters; - args->push(new Parameter(STCin, Type::dtypeinfo->type, NULL, NULL)); - args->push(new Parameter(STCin, Type::tvoid->arrayOf(), NULL, NULL)); - adDup_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), Id::adDup); - } - fd = adDup_fd; - } else { - static FuncDeclaration *adReverse_fd = NULL; - if (!adReverse_fd) { - Parameters* args = new Parameters; - args->push(new Parameter(STCin, Type::tvoid->arrayOf(), NULL, NULL)); - args->push(new Parameter(STCin, Type::tsize_t, NULL, NULL)); - adReverse_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), Id::adReverse); - } - fd = adReverse_fd; + + static FuncDeclaration *adReverse_fd = NULL; + if (!adReverse_fd) { + Parameters* args = new Parameters; + args->push(new Parameter(0, Type::tvoid->arrayOf(), NULL, NULL)); + args->push(new Parameter(0, Type::tsize_t, NULL, NULL)); + adReverse_fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), Id::adReverse); } + fd = adReverse_fd; ec = new VarExp(Loc(), fd); e = e->castTo(sc, n->arrayOf()); // convert to dynamic array arguments = new Expressions(); - if (dup) - arguments->push(getTypeInfo(sc)); arguments->push(e); - if (!dup) - arguments->push(new IntegerExp(Loc(), size, Type::tsize_t)); + arguments->push(new IntegerExp(Loc(), size, Type::tsize_t)); e = new CallExp(e->loc, ec, arguments); - if (ident == Id::idup) - { Type *einv = next->immutableOf(); - if (next->implicitConvTo(einv) < MATCHconst) - { error(e->loc, "cannot implicitly convert element type %s to immutable in %s.idup", - next->toChars(), olde->toChars()); - goto Lerror; - } - e->type = einv->arrayOf(); - } - else if (ident == Id::dup) - { - Type *emut = next->mutableOf(); - if (next->implicitConvTo(emut) < MATCHconst) - { error(e->loc, "cannot implicitly convert element type %s to mutable in %s.dup", - next->toChars(), olde->toChars()); - goto Lerror; - } - e->type = emut->arrayOf(); - } - else - e->type = next->mutableOf()->arrayOf(); + e->type = next->mutableOf()->arrayOf(); } else if (ident == Id::sort) { @@ -3965,16 +3861,17 @@ Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int f if (!fd) { Parameters* args = new Parameters; - args->push(new Parameter(STCin, Type::tvoid->arrayOf(), NULL, NULL)); - args->push(new Parameter(STCin, Type::dtypeinfo->type, NULL, NULL)); + args->push(new Parameter(0, Type::tvoid->arrayOf(), NULL, NULL)); + args->push(new Parameter(0, Type::dtypeinfo->type, NULL, NULL)); fd = FuncDeclaration::genCfunc(args, Type::tvoid->arrayOf(), "_adSort"); } ec = new VarExp(Loc(), fd); e = e->castTo(sc, n->arrayOf()); // convert to dynamic array arguments = new Expressions(); arguments->push(e); + // don't convert to dynamic array arguments->push(n->ty == Tsarray - ? n->getTypeInfo(sc) // don't convert to dynamic array + ? n->getTypeInfo(sc) : n->getInternalTypeInfo(sc)); e = new CallExp(e->loc, ec, arguments); e->type = next->arrayOf(); @@ -4119,7 +4016,9 @@ void TypeSArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol if (d >= td->objects->dim) { error(loc, "tuple index %llu exceeds length %u", d, td->objects->dim); - goto Ldefault; + *ps = NULL; + *pt = Type::terror; + return; } RootObject *o = (*td->objects)[(size_t)d]; if (o->dyncast() == DYNCAST_DSYMBOL) @@ -4208,19 +4107,24 @@ Type *TypeSArray::semantic(Loc loc, Scope *sc) if (dim) { dinteger_t n, n2; - int errors = global.errors; + unsigned int errors = global.errors; dim = semanticLength(sc, tbn, dim); if (errors != global.errors) goto Lerror; dim = dim->optimize(WANTvalue); dim = dim->ctfeInterpret(); + if (dim->op == TOKerror) + goto Lerror; errors = global.errors; dinteger_t d1 = dim->toInteger(); if (errors != global.errors) goto Lerror; + dim = dim->implicitCastTo(sc, tsize_t); dim = dim->optimize(WANTvalue); + if (dim->op == TOKerror) + goto Lerror; errors = global.errors; dinteger_t d2 = dim->toInteger(); if (errors != global.errors) @@ -4312,18 +4216,6 @@ void TypeSArray::toDecoBuffer(OutBuffer *buf, int flag) next->toDecoBuffer(buf, (flag & 0x100) ? flag : mod); } -void TypeSArray::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - if (mod != this->mod) - { toCBuffer3(buf, hgs, mod); - return; - } - next->toCBuffer2(buf, hgs, this->mod); - buf->writeByte('['); - sizeToCBuffer(buf, hgs, dim); - buf->writeByte(']'); -} - Expression *TypeSArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag) { #if LOGDOTEXP @@ -4358,7 +4250,7 @@ structalign_t TypeSArray::alignment() return next->alignment(); } -int TypeSArray::isString() +bool TypeSArray::isString() { TY nty = next->toBasetype()->ty; return nty == Tchar || nty == Twchar || nty == Tdchar; @@ -4452,12 +4344,12 @@ Expression *TypeSArray::defaultInit(Loc loc) return next->defaultInit(loc); } -int TypeSArray::isZeroInit(Loc loc) +bool TypeSArray::isZeroInit(Loc loc) { return next->isZeroInit(loc); } -int TypeSArray::needsDestruction() +bool TypeSArray::needsDestruction() { return next->needsDestruction(); } @@ -4512,8 +4404,10 @@ int TypeSArray::hasPointers() //return false; if (next->ty == Tvoid) + { // Arrays of void contain arbitrary data, which may include pointers return true; + } else return next->hasPointers(); } @@ -4616,20 +4510,6 @@ void TypeDArray::toDecoBuffer(OutBuffer *buf, int flag) next->toDecoBuffer(buf, (flag & 0x100) ? 0 : mod); } -void TypeDArray::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - if (mod != this->mod) - { toCBuffer3(buf, hgs, mod); - return; - } - if (equals(tstring)) - buf->writestring("string"); - else - { next->toCBuffer2(buf, hgs, this->mod); - buf->writestring("[]"); - } -} - Expression *TypeDArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag) { #if LOGDOTEXP @@ -4666,7 +4546,7 @@ Expression *TypeDArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int return e; } -int TypeDArray::isString() +bool TypeDArray::isString() { TY nty = next->toBasetype()->ty; return nty == Tchar || nty == Twchar || nty == Tdchar; @@ -4727,12 +4607,12 @@ Expression *TypeDArray::defaultInit(Loc loc) return new NullExp(loc, this); } -int TypeDArray::isZeroInit(Loc loc) +bool TypeDArray::isZeroInit(Loc loc) { - return 1; + return true; } -int TypeDArray::checkBoolean() +bool TypeDArray::checkBoolean() { return true; } @@ -4749,7 +4629,6 @@ TypeAArray::TypeAArray(Type *t, Type *index) : TypeArray(Taarray, t) { this->index = index; - this->impl = NULL; this->loc = Loc(); this->sc = NULL; } @@ -4779,7 +4658,7 @@ Type *TypeAArray::syntaxCopy() d_uns64 TypeAArray::size(Loc loc) { - return Target::ptrsize /* * 2*/; + return Target::ptrsize; } @@ -4847,18 +4726,112 @@ printf("index->ito->ito = x%x\n", index->ito->ito); error(loc, "can't have associative array key of %s", index->toBasetype()->toChars()); case Terror: return Type::terror; - case Tstruct: + default: + break; + } + Type *tbase = index->baseElemOf(); + while (tbase->ty == Tarray) + tbase = tbase->nextOf()->baseElemOf(); + if (tbase->ty == Tstruct) + { + /* AA's need typeid(index).equals() and getHash(). Issue error if not correctly set up. + */ + StructDeclaration *sd = ((TypeStruct *)tbase)->sym; + if (sd->scope) + sd->semantic(NULL); + + // duplicate a part of StructDeclaration::semanticTypeInfoMembers + if (sd->xeq && + sd->xeq->scope && + sd->xeq->semanticRun < PASSsemantic3done) + { + unsigned errors = global.startGagging(); + sd->xeq->semantic3(sd->xeq->scope); + if (global.endGagging(errors)) + sd->xeq = sd->xerreq; + } + + //printf("AA = %s, key: xeq = %p, xhash = %p\n", toChars(), sd->xeq, sd->xhash); + const char *s = (index->toBasetype()->ty != Tstruct) ? "bottom of " : ""; + if (!sd->xeq) + { + // If sd->xhash != NULL: + // sd or its fields have user-defined toHash. + // AA assumes that its result is consistent with bitwise equality. + // else: + // bitwise equality & hashing + } + else if (sd->xeq == sd->xerreq) + { + if (search_function(sd, Id::eq)) + { + error(loc, "%sAA key type %s does not have 'bool opEquals(ref const %s) const'", + s, sd->toChars(), sd->toChars()); + } + else + { + error(loc, "%sAA key type %s does not support const equality", + s, sd->toChars()); + } + return Type::terror; + } + else if (!sd->xhash) + { + if (search_function(sd, Id::eq)) + { + error(loc, "%sAA key type %s should have 'size_t toHash() const nothrow @safe' if opEquals defined", + s, sd->toChars()); + } + else + { + error(loc, "%sAA key type %s supports const equality but doesn't support const hashing", + s, sd->toChars()); + } + return Type::terror; + } + else { - /* AA's need opCmp. Issue error if not correctly set up. + // defined equality & hashing + assert(sd->xeq && sd->xhash); + + /* xeq and xhash may be implicitly defined by compiler. For example: + * struct S { int[] arr; } + * With 'arr' field equality and hashing, compiler will implicitly + * generate functions for xopEquals and xtoHash in TypeInfo_Struct. */ - TypeStruct *ts = (TypeStruct *)index->toBasetype(); - if (ts->sym->xcmp == ts->sym->xerrcmp) + } + } + else if (tbase->ty == Tclass && !((TypeClass *)tbase)->sym->isInterfaceDeclaration()) + { + ClassDeclaration *cd = ((TypeClass *)tbase)->sym; + if (cd->scope) + cd->semantic(NULL); + + if (!ClassDeclaration::object) + { + error(Loc(), "missing or corrupt object.d"); + fatal(); + } + + static FuncDeclaration *feq = NULL; + static FuncDeclaration *fcmp = NULL; + static FuncDeclaration *fhash = NULL; + if (!feq) feq = search_function(ClassDeclaration::object, Id::eq)->isFuncDeclaration(); + if (!fcmp) fcmp = search_function(ClassDeclaration::object, Id::cmp)->isFuncDeclaration(); + if (!fhash) fhash = search_function(ClassDeclaration::object, Id::tohash)->isFuncDeclaration(); + assert(fcmp && feq && fhash); + + if (feq ->vtblIndex < cd->vtbl.dim && cd->vtbl[feq ->vtblIndex] == feq) + { + #if 1 + if (fcmp->vtblIndex < cd->vtbl.dim && cd->vtbl[fcmp->vtblIndex] != fcmp) { - error(loc, "associative array key type %s does not have 'const int opCmp(ref const %s)' member function", - index->toBasetype()->toChars(), ts->sym->toChars()); - return Type::terror; + const char *s = (index->toBasetype()->ty != Tclass) ? "bottom of " : ""; + error(loc, "%sAA key type %s now requires equality rather than comparison", + s, cd->toChars()); + errorSupplemental(loc, "Please override Object.opEquals and toHash."); } - break; + #endif } } next = next->semantic(loc,sc)->merge2(); @@ -4881,73 +4854,6 @@ printf("index->ito->ito = x%x\n", index->ito->ito); return merge(); } -StructDeclaration *TypeAArray::getImpl() -{ - // Do it lazily - if (!impl) - { - Type *index = this->index; - Type *next = this->next; - if (index->reliesOnTident() || next->reliesOnTident()) - { - error(loc, "cannot create associative array %s", toChars()); - index = terror; - next = terror; - - // Head off future failures - StructDeclaration *s = new StructDeclaration(Loc(), NULL); - s->type = terror; - impl = s; - return impl; - } - /* This is really a proxy for the template instance AssocArray!(index, next) - * But the instantiation can fail if it is a template specialization field - * which has Tident's instead of real types. - */ - Objects *tiargs = new Objects(); - tiargs->push(index->substWildTo(MODconst)); // hack for bug7757 - tiargs->push(next ->substWildTo(MODconst)); // hack for bug7757 - - // Create AssociativeArray!(index, next) -#if 1 - if (! Type::associativearray) - { - ObjectNotFound(Id::AssociativeArray); - } - TemplateInstance *ti = new TemplateInstance(loc, Type::associativearray, tiargs); -#else - //Expression *e = new IdentifierExp(loc, Id::object); - Expression *e = new IdentifierExp(loc, Id::empty); - //e = new DotIdExp(loc, e, Id::object); - DotTemplateInstanceExp *dti = new DotTemplateInstanceExp(loc, - e, - Id::AssociativeArray, - tiargs); - dti->semantic(sc); - TemplateInstance *ti = dti->ti; -#endif - // Instantiate on the root module of import dependency graph. - Module *mi = sc->module->importedFrom; - Scope *scx = sc->push(mi); - scx->module = mi; - scx->tinst = NULL; - assert(scx->instantiatingModule() == mi); - ti->semantic(scx); - ti->semantic2(scx); - ti->semantic3(scx); - scx->pop(); - impl = ti->toAlias()->isStructDeclaration(); -#ifdef DEBUG - if (!impl) - { Dsymbol *s = ti->toAlias(); - printf("%s %s\n", s->kind(), s->toChars()); - } -#endif - assert(impl); - } - return impl; -} - void TypeAArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid) { //printf("TypeAArray::resolve() %s\n", toChars()); @@ -4983,17 +4889,22 @@ Expression *TypeAArray::dotExp(Scope *sc, Expression *e, Identifier *ident, int #if LOGDOTEXP printf("TypeAArray::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars()); #endif - if (ident != Id::__sizeof && - ident != Id::__xalignof && - ident != Id::init && - ident != Id::mangleof && - ident != Id::stringof && - ident != Id::offsetof) + if (ident == Id::length) { - Type *t = getImpl()->type; - e = e->copy(); - e->type = t; - e = t->dotExp(sc, e, ident, flag); + static FuncDeclaration *fd_aaLen = NULL; + if (fd_aaLen == NULL) + { + Parameters *fparams = new Parameters(); + fparams->push(new Parameter(STCin, this, NULL, NULL)); + fd_aaLen = FuncDeclaration::genCfunc(fparams, Type::tsize_t, Id::aaLen); + TypeFunction *tf = (TypeFunction *)fd_aaLen->type; + tf->purity = PUREconst; + tf->isnothrow = true; + tf->isnogc = false; + } + Expression *ev = new VarExp(e->loc, fd_aaLen); + e = new CallExp(e->loc, ev, e); + e->type = ((TypeFunction *)fd_aaLen->type)->next; } else e = Type::dotExp(sc, e, ident, flag); @@ -5007,18 +4918,6 @@ void TypeAArray::toDecoBuffer(OutBuffer *buf, int flag) next->toDecoBuffer(buf, (flag & 0x100) ? 0 : mod); } -void TypeAArray::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - if (mod != this->mod) - { toCBuffer3(buf, hgs, mod); - return; - } - next->toCBuffer2(buf, hgs, this->mod); - buf->writeByte('['); - index->toCBuffer2(buf, hgs, 0); - buf->writeByte(']'); -} - Expression *TypeAArray::defaultInit(Loc loc) { #if LOGDEFAULTINIT @@ -5027,12 +4926,12 @@ Expression *TypeAArray::defaultInit(Loc loc) return new NullExp(loc, this); } -int TypeAArray::isZeroInit(Loc loc) +bool TypeAArray::isZeroInit(Loc loc) { return true; } -int TypeAArray::checkBoolean() +bool TypeAArray::checkBoolean() { return true; } @@ -5080,16 +4979,6 @@ MATCH TypeAArray::implicitConvTo(Type *to) return MODimplicitConv(mod, to->mod) ? MATCHconst : MATCHnomatch; } } - else if (to->ty == Tstruct && ((TypeStruct *)to)->sym->ident == Id::AssociativeArray) - { - int errs = global.startGagging(); - Type *from = getImpl()->type; - if (global.endGagging(errs)) - { - return MATCHnomatch; - } - return from->implicitConvTo(to); - } return Type::implicitConvTo(to); } @@ -5106,15 +4995,6 @@ MATCH TypeAArray::constConv(Type *to) return Type::constConv(to); } -Type *TypeAArray::reliesOnTident(TemplateParameters *tparams) -{ - Type *t = TypeNext::reliesOnTident(tparams); - if (!t) - t = index->reliesOnTident(tparams); - return t; -} - - /***************************** TypePointer *****************************/ TypePointer::TypePointer(Type *t) @@ -5181,18 +5061,6 @@ d_uns64 TypePointer::size(Loc loc) return Target::ptrsize; } -void TypePointer::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - //printf("TypePointer::toCBuffer2() next = %d\n", next->ty); - if (mod != this->mod) - { toCBuffer3(buf, hgs, mod); - return; - } - next->toCBuffer2(buf, hgs, this->mod); - if (next->ty != Tfunction) - buf->writeByte('*'); -} - MATCH TypePointer::implicitConvTo(Type *to) { //printf("TypePointer::implicitConvTo(to = %s) %s\n", to->toChars(), toChars()); @@ -5210,7 +5078,22 @@ MATCH TypePointer::implicitConvTo(Type *to) return MATCHconst; if (next->covariant(tp->next) == 1) + { + Type *tret = this->next->nextOf(); + Type *toret = tp->next->nextOf(); + if (tret->ty == Tclass && toret->ty == Tclass) + { + /* Bugzilla 10219: Check covariant interface return with offset tweaking. + * interface I {} + * class C : Object, I {} + * I function() dg = function C() {} // should be error + */ + int offset = 0; + if (toret->isBaseOf(tret, &offset) && offset != 0) + return MATCHnomatch; + } return MATCHconvert; + } } else if (tp->next->ty == Tvoid) { @@ -5271,9 +5154,9 @@ Expression *TypePointer::defaultInit(Loc loc) return new NullExp(loc, this); } -int TypePointer::isZeroInit(Loc loc) +bool TypePointer::isZeroInit(Loc loc) { - return 1; + return true; } int TypePointer::hasPointers() @@ -5324,16 +5207,6 @@ d_uns64 TypeReference::size(Loc loc) return Target::ptrsize; } -void TypeReference::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - if (mod != this->mod) - { toCBuffer3(buf, hgs, mod); - return; - } - next->toCBuffer2(buf, hgs, this->mod); - buf->writeByte('&'); -} - Expression *TypeReference::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag) { #if LOGDOTEXP @@ -5352,9 +5225,9 @@ Expression *TypeReference::defaultInit(Loc loc) return new NullExp(loc, this); } -int TypeReference::isZeroInit(Loc loc) +bool TypeReference::isZeroInit(Loc loc) { - return 1; + return true; } @@ -5371,6 +5244,7 @@ TypeFunction::TypeFunction(Parameters *parameters, Type *treturn, int varargs, L this->linkage = linkage; this->inuse = 0; this->isnothrow = false; + this->isnogc = false; this->purity = PUREimpure; this->isproperty = false; this->isref = false; @@ -5381,6 +5255,8 @@ TypeFunction::TypeFunction(Parameters *parameters, Type *treturn, int varargs, L this->purity = PUREfwdref; if (stc & STCnothrow) this->isnothrow = true; + if (stc & STCnogc) + this->isnogc = true; if (stc & STCproperty) this->isproperty = true; @@ -5413,6 +5289,7 @@ Type *TypeFunction::syntaxCopy() TypeFunction *t = new TypeFunction(params, treturn, varargs, linkage); t->mod = mod; t->isnothrow = isnothrow; + t->isnogc = isnogc; t->purity = purity; t->isproperty = isproperty; t->isref = isref; @@ -5562,7 +5439,7 @@ int Type::covariant(Type *t, StorageClass *pstc) #endif } - /* Can convert pure to impure, and nothrow to throw + /* Can convert pure to impure, nothrow to throw, and nogc to gc */ if (!t1->purity && t2->purity) stc |= STCpure; @@ -5570,6 +5447,9 @@ int Type::covariant(Type *t, StorageClass *pstc) if (!t1->isnothrow && t2->isnothrow) stc |= STCnothrow; + if (!t1->isnogc && t2->isnogc) + stc |= STCnogc; + /* Can convert safe/trusted to system */ if (t1->trust <= TRUSTsystem && t2->trust >= TRUSTtrusted) @@ -5620,7 +5500,7 @@ void TypeFunction::toDecoBuffer(OutBuffer *buf, int flag) } buf->writeByte(mc); - if (purity || isnothrow || isproperty || isref || trust) + if (purity || isnothrow || isnogc || isproperty || isref || trust) { if (purity) buf->writestring("Na"); @@ -5630,6 +5510,8 @@ void TypeFunction::toDecoBuffer(OutBuffer *buf, int flag) buf->writestring("Nc"); if (isproperty) buf->writestring("Nd"); + if (isnogc) + buf->writestring("Ni"); switch (trust) { case TRUSTtrusted: @@ -5651,193 +5533,15 @@ void TypeFunction::toDecoBuffer(OutBuffer *buf, int flag) inuse--; } -void TypeFunction::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs) -{ - toCBufferWithAttributes(buf, ident, hgs, this, NULL); -} - -void TypeFunction::toCBufferWithAttributes(OutBuffer *buf, Identifier *ident, HdrGenState* hgs, TypeFunction *attrs, TemplateDeclaration *td) +Type *TypeFunction::semantic(Loc loc, Scope *sc) { - //printf("TypeFunction::toCBuffer() this = %p\n", this); - if (inuse) - { inuse = 2; // flag error to caller - return; - } - inuse++; - - /* Use 'storage class' style for attributes - */ - if (attrs->mod) + if (deco) // if semantic() already run { - MODtoBuffer(buf, attrs->mod); - buf->writeByte(' '); + //printf("already done\n"); + return this; } - - if (attrs->purity) - buf->writestring("pure "); - if (attrs->isnothrow) - buf->writestring("nothrow "); - if (attrs->isproperty) - buf->writestring("@property "); - if (attrs->isref) - buf->writestring("ref "); - - switch (attrs->trust) - { - case TRUSTsystem: - buf->writestring("@system "); - break; - - case TRUSTtrusted: - buf->writestring("@trusted "); - break; - - case TRUSTsafe: - buf->writestring("@safe "); - break; - default: break; - } - - if (hgs->ddoc != 1) - { - const char *p = NULL; - switch (attrs->linkage) - { - case LINKd: p = NULL; break; - case LINKc: p = "C"; break; - case LINKwindows: p = "Windows"; break; - case LINKpascal: p = "Pascal"; break; - case LINKcpp: p = "C++"; break; - default: - assert(0); - } - if (!hgs->hdrgen && p) - { - buf->writestring("extern ("); - buf->writestring(p); - buf->writestring(") "); - } - } - - if (!ident || ident->toHChars2() == ident->toChars()) - { if (next) - next->toCBuffer2(buf, hgs, 0); - else if (hgs->ddoc) - buf->writestring("auto"); - } - - if (ident) - { - if (next || hgs->ddoc) - buf->writeByte(' '); - buf->writestring(ident->toHChars2()); - } - - if (td) - { buf->writeByte('('); - for (size_t i = 0; i < td->origParameters->dim; i++) - { - TemplateParameter *tp = (*td->origParameters)[i]; - if (i) - buf->writestring(", "); - tp->toCBuffer(buf, hgs); - } - buf->writeByte(')'); - } - Parameter::argsToCBuffer(buf, hgs, parameters, varargs); - inuse--; -} - -// kind is inserted before the argument list and will usually be "function" or "delegate". -void functionToCBuffer2(TypeFunction *t, OutBuffer *buf, HdrGenState *hgs, int mod, const char *kind) -{ - if (hgs->ddoc != 1) - { - const char *p = NULL; - switch (t->linkage) - { - case LINKd: p = NULL; break; - case LINKc: p = "C"; break; - case LINKwindows: p = "Windows"; break; - case LINKpascal: p = "Pascal"; break; - case LINKcpp: p = "C++"; break; - default: - assert(0); - } - if (!hgs->hdrgen && p) - { - buf->writestring("extern ("); - buf->writestring(p); - buf->writestring(") "); - } - } - if (t->next) - { - t->next->toCBuffer2(buf, hgs, 0); - buf->writeByte(' '); - } - buf->writestring(kind); - Parameter::argsToCBuffer(buf, hgs, t->parameters, t->varargs); - t->attributesToCBuffer(buf, mod); -} - -void TypeFunction::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - //printf("TypeFunction::toCBuffer2() this = %p, ref = %d\n", this, isref); - if (inuse) - { inuse = 2; // flag error to caller - return; - } - inuse++; - - functionToCBuffer2(this, buf, hgs, mod, "function"); - - inuse--; -} - -void TypeFunction::attributesToCBuffer(OutBuffer *buf, int mod) -{ - /* Use postfix style for attributes - */ - if (mod != this->mod) - { - modToBuffer(buf); - } - if (purity) - buf->writestring(" pure"); - if (isnothrow) - buf->writestring(" nothrow"); - if (isproperty) - buf->writestring(" @property"); - if (isref) - buf->writestring(" ref"); - - switch (trust) - { - case TRUSTsystem: - buf->writestring(" @system"); - break; - - case TRUSTtrusted: - buf->writestring(" @trusted"); - break; - - case TRUSTsafe: - buf->writestring(" @safe"); - break; - default: break; - } -} - -Type *TypeFunction::semantic(Loc loc, Scope *sc) -{ - if (deco) // if semantic() already run - { - //printf("already done\n"); - return this; - } - //printf("TypeFunction::semantic() this = %p\n", this); - //printf("TypeFunction::semantic() %s, sc->stc = %llx, fargs = %p\n", toChars(), sc->stc, fargs); + //printf("TypeFunction::semantic() this = %p\n", this); + //printf("TypeFunction::semantic() %s, sc->stc = %llx, fargs = %p\n", toChars(), sc->stc, fargs); bool errors = false; @@ -5862,6 +5566,8 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc) tf->purity = PUREfwdref; if (sc->stc & STCnothrow) tf->isnothrow = true; + if (sc->stc & STCnogc) + tf->isnogc = true; if (sc->stc & STCref) tf->isref = true; @@ -5911,12 +5617,12 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc) error(loc, "functions cannot return a tuple"); errors = true; } - else if (tb->ty == Tstruct) + else if (!tf->isref && (tb->ty == Tstruct || tb->ty == Tsarray)) { - StructDeclaration *sd = ((TypeStruct *)tb)->sym; - if (sd->isforwardRef()) + Type *tb2 = tb->baseElemOf(); + if (tb2->ty == Tstruct && !((TypeStruct *)tb2)->sym->members) { - error(loc, "cannot return opaque struct %s by value", tb->toChars()); + error(loc, "functions cannot return opaque type %s by value", tb->toChars()); errors = true; } } @@ -5929,8 +5635,7 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc) error(loc, "functions cannot return scope %s", tf->next->toChars()); errors = true; } - if (tf->next->hasWild() && - !(tf->next->ty == Tpointer && tf->next->nextOf()->ty == Tfunction || tf->next->ty == Tdelegate)) + if (tf->next->hasWild()) wildreturn = true; } @@ -5968,11 +5673,27 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc) Type *t = fparam->type->toBasetype(); - if (!(fparam->storageClass & STClazy) && t->ty == Tvoid) + if (t->ty == Tfunction) + { + error(loc, "cannot have parameter of function type %s", fparam->type->toChars()); + errors = true; + } + else if (!(fparam->storageClass & (STCref | STCout)) && + (t->ty == Tstruct || t->ty == Tsarray)) + { + Type *tb2 = t->baseElemOf(); + if (tb2->ty == Tstruct && !((TypeStruct *)tb2)->sym->members) + { + error(loc, "cannot have parameter of opaque type %s by value", fparam->type->toChars()); + errors = true; + } + } + else if (!(fparam->storageClass & STClazy) && t->ty == Tvoid) { error(loc, "cannot have parameter of type %s", fparam->type->toChars()); errors = true; } + if (fparam->storageClass & (STCref | STClazy)) { } @@ -5990,15 +5711,14 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc) tv = tv->nextOf()->toBasetype(); if (tv->ty == Tstruct && ((TypeStruct *)tv)->sym->noDefaultCtor) { - error(loc, "cannot have out parameter of type %s because the default construction is disbaled", + error(loc, "cannot have out parameter of type %s because the default construction is disabled", fparam->type->toChars()); errors = true; } } } - if (t->hasWild() && - !(t->ty == Tpointer && t->nextOf()->ty == Tfunction || t->ty == Tdelegate)) + if (t->hasWild()) { wildparams |= 1; //if (tf->next && !wildreturn) @@ -6144,71 +5864,76 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc) void TypeFunction::purityLevel() { TypeFunction *tf = this; - if (tf->purity == PUREfwdref) - { /* Evaluate what kind of purity based on the modifiers for the parameters + if (tf->purity != PUREfwdref) + return; + + /* Evaluate what kind of purity based on the modifiers for the parameters + */ + tf->purity = PUREstrong; // assume strong until something weakens it + + size_t dim = Parameter::dim(tf->parameters); + if (!dim) + return; + for (size_t i = 0; i < dim; i++) + { + Parameter *fparam = Parameter::getNth(tf->parameters, i); + Type *t = fparam->type; + if (!t) + continue; + + if (fparam->storageClass & (STClazy | STCout)) + { + tf->purity = PUREweak; + break; + } + if (fparam->storageClass & STCref) + { + if (t->mod & MODimmutable) + continue; + if (t->mod & (MODconst | MODwild)) + { + tf->purity = PUREconst; + continue; + } + tf->purity = PUREweak; + break; + } + + t = t->baseElemOf(); + if (!t->hasPointers()) + continue; + if (t->mod & MODimmutable) + continue; + + /* Accept immutable(T)[] and immutable(T)* as being strongly pure */ - tf->purity = PUREstrong; // assume strong until something weakens it - if (tf->parameters) + if (t->ty == Tarray || t->ty == Tpointer) { - size_t dim = Parameter::dim(tf->parameters); - for (size_t i = 0; i < dim; i++) - { Parameter *fparam = Parameter::getNth(tf->parameters, i); - if (fparam->storageClass & STClazy) - { - tf->purity = PUREweak; - break; - } - if (fparam->storageClass & STCout) - { - tf->purity = PUREweak; - break; - } - if (!fparam->type) - continue; - if (fparam->storageClass & STCref) - { - if (!(fparam->type->mod & (MODconst | MODimmutable | MODwild))) - { tf->purity = PUREweak; - break; - } - if (fparam->type->mod & MODconst) - { tf->purity = PUREconst; - continue; - } - } - Type *t = fparam->type->toBasetype(); - if (!t->hasPointers()) - continue; - if (t->mod & MODimmutable) - continue; - /* The rest of this is too strict; fix later. - * For example, the only pointer members of a struct may be immutable, - * which would maintain strong purity. - */ - if (t->mod & (MODconst | MODwild)) - { tf->purity = PUREconst; - continue; - } - Type *tn = t->nextOf(); - if (tn) - { tn = tn->toBasetype(); - if (tn->ty == Tpointer || tn->ty == Tarray) - { /* Accept immutable(T)* and immutable(T)[] as being strongly pure - */ - if (tn->mod & MODimmutable) - continue; - if (tn->mod & (MODconst | MODwild)) - { tf->purity = PUREconst; - continue; - } - } - } - /* Should catch delegates and function pointers, and fold in their purity - */ - tf->purity = PUREweak; // err on the side of too strict - break; + Type *tn = t->nextOf()->toBasetype(); + if (tn->mod & MODimmutable) + continue; + if (tn->mod & (MODconst | MODwild)) + { + tf->purity = PUREconst; + continue; } } + + /* The rest of this is too strict; fix later. + * For example, the only pointer members of a struct may be immutable, + * which would maintain strong purity. + */ + if (t->mod & (MODconst | MODwild)) + { + tf->purity = PUREconst; + continue; + } + + /* Should catch delegates and function pointers, and fold in their purity + */ + + tf->purity = PUREweak; // err on the side of too strict + break; } } @@ -6226,7 +5951,7 @@ MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag) { //printf("TypeFunction::callMatch() %s\n", toChars()); MATCH match = MATCHexact; // assume exact match - unsigned wildmatch = 0; + unsigned char wildmatch = 0; if (tthis) { Type *t = tthis; @@ -6315,14 +6040,6 @@ MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag) { Expression *arg = (*args)[u]; assert(arg); - - if (arg->op == TOKfunction) - { - arg = ((FuncExp *)arg)->inferType(p->type, 1); - if (!arg) - goto L1; // try typesafe variadics - } - //printf("arg: %s, type: %s\n", arg->toChars(), arg->type->toChars()); Type *targ = arg->type; @@ -6334,8 +6051,10 @@ MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag) { //printf("%s of type %s implicitConvTo %s\n", arg->toChars(), targ->toChars(), tprm->toChars()); if (flag) + { // for partial ordering, value is an irrelevant mockup, just look at the type m = targ->implicitConvTo(tprm); + } else m = arg->implicitConvTo(tprm); //printf("match %d\n", m); @@ -6424,17 +6143,12 @@ MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag) if (sz != nargs - u) goto Nomatch; case Tarray: - { TypeArray *ta = (TypeArray *)tb; + { + TypeArray *ta = (TypeArray *)tb; for (; u < nargs; u++) { Expression *arg = (*args)[u]; assert(arg); - if (arg->op == TOKfunction) - { - arg = ((FuncExp *)arg)->inferType(tb->nextOf(), 1); - if (!arg) - goto Nomatch; - } /* If lazy array of delegates, * convert arg(s) to delegate(s) @@ -6487,18 +6201,6 @@ MATCH TypeFunction::callMatch(Type *tthis, Expressions *args, int flag) return MATCHnomatch; } -Type *TypeFunction::reliesOnTident(TemplateParameters *tparams) -{ - size_t dim = Parameter::dim(parameters); - for (size_t i = 0; i < dim; i++) - { Parameter *fparam = Parameter::getNth(parameters, i); - Type *t = fparam->type->reliesOnTident(tparams); - if (t) - return t; - } - return next ? next->reliesOnTident(tparams) : NULL; -} - /******************************************** * Return true if there are lazy parameters. */ @@ -6564,6 +6266,7 @@ Type *TypeFunction::addStorageClass(StorageClass stc) TypeFunction *t = (TypeFunction *)Type::addStorageClass(stc); if ((stc & STCpure && !t->purity) || (stc & STCnothrow && !t->isnothrow) || + (stc & STCnogc && !t->isnogc) || (stc & STCsafe && t->trust < TRUSTtrusted)) { // Klunky to change these @@ -6572,6 +6275,7 @@ Type *TypeFunction::addStorageClass(StorageClass stc) tf->fargs = fargs; tf->purity = t->purity; tf->isnothrow = t->isnothrow; + tf->isnogc = t->isnogc; tf->isproperty = t->isproperty; tf->isref = t->isref; tf->trust = t->trust; @@ -6581,6 +6285,8 @@ Type *TypeFunction::addStorageClass(StorageClass stc) tf->purity = PUREfwdref; if (stc & STCnothrow) tf->isnothrow = true; + if (stc & STCnogc) + tf->isnogc = true; if (stc & STCsafe) tf->trust = TRUSTsafe; @@ -6590,6 +6296,41 @@ Type *TypeFunction::addStorageClass(StorageClass stc) return t; } +/** For each active attribute (ref/const/nogc/etc) call fp with a void* for the +work param and a string representation of the attribute. */ +int TypeFunction::attributesApply(void *param, int (*fp)(void *, const char *), TRUSTformat trustFormat) +{ + int res = 0; + + if (purity) res = fp(param, "pure"); + if (res) return res; + + if (isnothrow) res = fp(param, "nothrow"); + if (res) return res; + + if (isnogc) res = fp(param, "@nogc"); + if (res) return res; + + if (isproperty) res = fp(param, "@property"); + if (res) return res; + + if (isref) res = fp(param, "ref"); + if (res) return res; + + TRUST trustAttrib = trust; + + if (trustAttrib == TRUSTdefault) + { + // Print out "@system" when trust equals TRUSTdefault (if desired). + if (trustFormat == TRUSTformatSystem) + trustAttrib = TRUSTsystem; + else + return res; // avoid calling with an empty string + } + + return fp(param, trustToChars(trustAttrib)); +} + /***************************** TypeDelegate *****************************/ TypeDelegate::TypeDelegate(Type *t) @@ -6662,21 +6403,26 @@ MATCH TypeDelegate::implicitConvTo(Type *to) return MATCHexact; #if 1 // not allowing covariant conversions because it interferes with overriding if (to->ty == Tdelegate && this->nextOf()->covariant(to->nextOf()) == 1) + { + Type *tret = this->next->nextOf(); + Type *toret = ((TypeDelegate *)to)->next->nextOf(); + if (tret->ty == Tclass && toret->ty == Tclass) + { + /* Bugzilla 10219: Check covariant interface return with offset tweaking. + * interface I {} + * class C : Object, I {} + * I delegate() dg = delegate C() {} // should be error + */ + int offset = 0; + if (toret->isBaseOf(tret, &offset) && offset != 0) + return MATCHnomatch; + } return MATCHconvert; + } #endif return MATCHnomatch; } -void TypeDelegate::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - if (mod != this->mod) - { toCBuffer3(buf, hgs, mod); - return; - } - - functionToCBuffer2((TypeFunction *)next, buf, hgs, mod, "delegate"); -} - Expression *TypeDelegate::defaultInit(Loc loc) { #if LOGDEFAULTINIT @@ -6685,12 +6431,12 @@ Expression *TypeDelegate::defaultInit(Loc loc) return new NullExp(loc, this); } -int TypeDelegate::isZeroInit(Loc loc) +bool TypeDelegate::isZeroInit(Loc loc) { - return 1; + return true; } -int TypeDelegate::checkBoolean() +bool TypeDelegate::checkBoolean() { return true; } @@ -6702,39 +6448,13 @@ Expression *TypeDelegate::dotExp(Scope *sc, Expression *e, Identifier *ident, in #endif if (ident == Id::ptr) { -#ifndef IN_GCC - e->type = tvoidptr; -#else - if (!e->isLvalue()) - e = e->castTo(sc, tvoidptr); - else - { - e = e->addressOf(sc); - e->type = tvoidptr; - e = new PtrExp(e->loc, e); - e->type = tvoidptr; - } -#endif - return e; + e = new DelegatePtrExp(e->loc, e); + e = e->semantic(sc); } else if (ident == Id::funcptr) { - if (!e->isLvalue()) - { - Identifier *idtmp = Lexer::uniqueId("__dgtmp"); - VarDeclaration *tmp = new VarDeclaration(e->loc, this, idtmp, new ExpInitializer(Loc(), e)); - tmp->storage_class |= STCtemp | STCctfe; - e = new DeclarationExp(e->loc, tmp); - e = new CommaExp(e->loc, e, new VarExp(e->loc, tmp)); - e = e->semantic(sc); - } - e = e->addressOf(sc); - e->type = tvoidptr; - e = new AddExp(e->loc, e, new IntegerExp(Target::ptrsize)); - e->type = tvoidptr; - e = new PtrExp(e->loc, e); - e->type = next->pointerTo(); - return e; + e = new DelegateFuncptrExp(e->loc, e); + e = e->semantic(sc); } else { @@ -6787,23 +6507,6 @@ void TypeQualified::addInst(TemplateInstance *inst) idents.push(inst); } -void TypeQualified::toCBuffer2Helper(OutBuffer *buf, HdrGenState *hgs) -{ - for (size_t i = 0; i < idents.dim; i++) - { RootObject *id = idents[i]; - - buf->writeByte('.'); - - if (id->dyncast() == DYNCAST_DSYMBOL) - { - TemplateInstance *ti = (TemplateInstance *)id; - ti->toCBuffer(buf, hgs); - } - else - buf->writestring(id->toChars()); - } -} - d_uns64 TypeQualified::size(Loc loc) { error(this->loc, "size of type %s is not known", toChars()); @@ -6840,10 +6543,26 @@ void TypeQualified::resolveHelper(Loc loc, Scope *sc, { RootObject *id = idents[i]; Type *t = s->getType(); // type symbol, type alias, or type tuple? + unsigned errorsave = global.errors; Dsymbol *sm = s->searchX(loc, sc, id); + if (global.errors != errorsave) + { + *pt = Type::terror; + return; + } //printf("\t3: s = %p %s %s, sm = %p\n", s, s->kind(), s->toChars(), sm); if (intypeid && !t && sm && sm->needThis()) goto L3; + if (VarDeclaration *v = s->isVarDeclaration()) + { + if (v->storage_class & (STCconst | STCimmutable | STCmanifest) || + v->type->isConst() || v->type->isImmutable()) + { + // Bugzilla 13087: this.field is not constant always + if (!v->isThisDeclaration()) + goto L3; + } + } if (!sm) { if (!t) @@ -6966,14 +6685,16 @@ void TypeQualified::resolveHelper(Loc loc, Scope *sc, return; } if (t->ty == Tinstance && t != this && !t->deco) - { error(loc, "forward reference to '%s'", t->toChars()); + { + if (!((TypeInstance *)t)->tempinst->errors) + error(loc, "forward reference to '%s'", t->toChars()); *pt = Type::terror; return; } if (t != this) { - if (t->reliesOnTident()) + if (reliesOnTident(t)) { if (s->scope) t = t->semantic(loc, s->scope); @@ -7055,16 +6776,6 @@ void TypeIdentifier::toDecoBuffer(OutBuffer *buf, int flag) buf->printf("%u%s", (unsigned)len, name); } -void TypeIdentifier::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - if (mod != this->mod) - { toCBuffer3(buf, hgs, mod); - return; - } - buf->writestring(this->ident->toChars()); - toCBuffer2Helper(buf, hgs); -} - /************************************* * Takes an array of Identifiers and figures out if * it represents a Type or an Expression. @@ -7102,18 +6813,6 @@ void TypeIdentifier::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsy } Dsymbol *s = sc->search(loc, ident, &scopesym); - if (s) - { - Declaration *d = s->isDeclaration(); - if (d && d->inuse) - { - error(loc, "circular reference to '%s'", d->toPrettyChars()); - *pe = NULL; - *ps = NULL; - *pt = Type::terror; - return; - } - } resolveHelper(loc, sc, s, scopesym, pe, pt, ps, intypeid); if (*pt) (*pt) = (*pt)->addMod(mod); @@ -7140,7 +6839,8 @@ Dsymbol *TypeIdentifier::toDsymbol(Scope *sc) RootObject *id = idents[i]; s = s->searchX(loc, sc, id); if (!s) // failed to find a symbol - { //printf("\tdidn't find a symbol\n"); + { + //printf("\tdidn't find a symbol\n"); break; } } @@ -7159,15 +6859,6 @@ Type *TypeIdentifier::semantic(Loc loc, Scope *sc) if (t) { //printf("\tit's a type %d, %s, %s\n", t->ty, t->toChars(), t->deco); - - if (t->ty == Ttypedef) - { - TypeTypedef *tt = (TypeTypedef *)t; - if (tt->sym->sem == SemanticIn) - { error(loc, "circular reference of typedef %s", tt->toChars()); - return terror; - } - } t = t->addMod(mod); } else @@ -7185,31 +6876,15 @@ Type *TypeIdentifier::semantic(Loc loc, Scope *sc) return t; } -Type *TypeIdentifier::reliesOnTident(TemplateParameters *tparams) +Expression *TypeIdentifier::toExpression() { - if (tparams) + Expression *e = new IdentifierExp(loc, ident); + for (size_t i = 0; i < idents.dim; i++) { - for (size_t i = 0; i < tparams->dim; i++) + RootObject *id = idents[i]; + if (id->dyncast() == DYNCAST_IDENTIFIER) { - TemplateParameter *tp = (*tparams)[i]; - if (tp->ident->equals(ident)) - return this; - } - return NULL; - } - else - return this; -} - -Expression *TypeIdentifier::toExpression() -{ - Expression *e = new IdentifierExp(loc, ident); - for (size_t i = 0; i < idents.dim; i++) - { - RootObject *id = idents[i]; - if (id->dyncast() == DYNCAST_IDENTIFIER) - { - e = new DotIdExp(loc, e, (Identifier *)id); + e = new DotIdExp(loc, e, (Identifier *)id); } else { @@ -7247,17 +6922,6 @@ Type *TypeInstance::syntaxCopy() return t; } - -void TypeInstance::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - if (mod != this->mod) - { toCBuffer3(buf, hgs, mod); - return; - } - tempinst->toCBuffer(buf, hgs); - toCBuffer2Helper(buf, hgs); -} - void TypeInstance::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid) { // Note close similarity to TypeIdentifier::resolve() @@ -7326,37 +6990,12 @@ Dsymbol *TypeInstance::toDsymbol(Scope *sc) //printf("TypeInstance::semantic(%s)\n", toChars()); resolve(loc, sc, &e, &t, &s); + if (t && t->ty != Tinstance) + s = t->toDsymbol(sc); return s; } -Type *TypeInstance::reliesOnTident(TemplateParameters *tparams) -{ - if (tparams) - { - for (size_t i = 0; i < tparams->dim; i++) - { - TemplateParameter *tp = (*tparams)[i]; - if (tempinst->name == tp->ident) - return this; - } - if (!tempinst->tiargs) - return NULL; - for (size_t i = 0; i < tempinst->tiargs->dim; i++) - { - Type *t = isType((*tempinst->tiargs)[i]); - t = t ? t->reliesOnTident(tparams) : NULL; - if (t) - return t; - } - return NULL; - } - else - { - return Type::reliesOnTident(tparams); - } -} - Expression *TypeInstance::toExpression() { Expression *e = new ScopeExp(loc, tempinst); @@ -7407,26 +7046,12 @@ Type *TypeTypeof::syntaxCopy() Dsymbol *TypeTypeof::toDsymbol(Scope *sc) { - Type *t; - - t = semantic(loc, sc); + Type *t = semantic(loc, sc); if (t == this) return NULL; return t->toDsymbol(sc); } -void TypeTypeof::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - if (mod != this->mod) - { toCBuffer3(buf, hgs, mod); - return; - } - buf->writestring("typeof("); - exp->toCBuffer(buf, hgs); - buf->writeByte(')'); - toCBuffer2Helper(buf, hgs); -} - void TypeTypeof::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid) { *pe = NULL; @@ -7448,16 +7073,22 @@ void TypeTypeof::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol Type *t; { + /* Currently we cannot evalute 'exp' in speculative context, because + * the type implementation may leak to the final execution. Consider: + * + * struct S(T) { + * string toString() const { return "x"; } + * } + * void main() { + * alias X = typeof(S!int()); + * assert(typeid(X).xtoString(null) == "x"); + * } + */ Scope *sc2 = sc->push(); sc2->intypeof = 1; - sc2->speculative = true; sc2->flags |= sc->flags & SCOPEstaticif; - unsigned oldspecgag = global.speculativeGag; - if (global.gag) - global.speculativeGag = global.gag; exp = exp->semantic(sc2); exp = resolvePropertiesOnly(sc2, exp); - global.speculativeGag = oldspecgag; sc2->pop(); if (exp->op == TOKtype) { @@ -7466,8 +7097,8 @@ void TypeTypeof::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol } else if (exp->op == TOKimport) { - ScopeDsymbol *s = ((ScopeExp *)exp)->sds; - if (s->isPackage()) + ScopeDsymbol *sds = ((ScopeExp *)exp)->sds; + if (sds->isPackage()) { error(loc, "%s has no type", exp->toChars()); goto Lerr; @@ -7673,17 +7304,6 @@ Type *TypeReturn::semantic(Loc loc, Scope *sc) return t; } -void TypeReturn::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - if (mod != this->mod) - { toCBuffer3(buf, hgs, mod); - return; - } - buf->writestring("typeof(return)"); - toCBuffer2Helper(buf, hgs); -} - - /***************************** TypeEnum *****************************/ TypeEnum::TypeEnum(EnumDeclaration *sym) @@ -7704,512 +7324,214 @@ char *TypeEnum::toChars() return sym->toChars(); } -Type *TypeEnum::syntaxCopy() -{ - return this; -} - -Type *TypeEnum::semantic(Loc loc, Scope *sc) -{ - //printf("TypeEnum::semantic() %s\n", toChars()); - if (deco) - return this; - return merge(); -} - -d_uns64 TypeEnum::size(Loc loc) -{ - return sym->getMemtype(loc)->size(loc); -} - -unsigned TypeEnum::alignsize() -{ - Type *t = sym->getMemtype(Loc()); - if (t->ty == Terror) - return 4; - return t->alignsize(); -} - -Dsymbol *TypeEnum::toDsymbol(Scope *sc) -{ - return sym; -} - -Type *TypeEnum::toBasetype() -{ - if (!sym->members && !sym->memtype) - return this; - return sym->getMemtype(Loc())->toBasetype(); -} - -void TypeEnum::toDecoBuffer(OutBuffer *buf, int flag) -{ - const char *name = sym->mangle(); - Type::toDecoBuffer(buf, flag); - buf->writestring(name); -} - -void TypeEnum::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - if (mod != this->mod) - { toCBuffer3(buf, hgs, mod); - return; - } - buf->writestring(sym->toChars()); -} - -Expression *TypeEnum::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag) -{ -#if LOGDOTEXP - printf("TypeEnum::dotExp(e = '%s', ident = '%s') '%s'\n", e->toChars(), ident->toChars(), toChars()); -#endif - Dsymbol *s = sym->search(e->loc, ident); - if (!s) - { - if (ident == Id::max || - ident == Id::min || - ident == Id::init || - ident == Id::mangleof || - !sym->memtype - ) - { - return getProperty(e->loc, ident, flag); - } - return sym->getMemtype(Loc())->dotExp(sc, e, ident, flag); - } - EnumMember *m = s->isEnumMember(); - return m->getVarExp(e->loc, sc); -} - -Expression *TypeEnum::getProperty(Loc loc, Identifier *ident, int flag) -{ Expression *e; - - if (ident == Id::max || ident == Id::min) - { - return sym->getMaxMinValue(loc, ident); - } - else if (ident == Id::init) - { - e = defaultInitLiteral(loc); - } - else if (ident == Id::stringof) - { char *s = toChars(); - e = new StringExp(loc, s, strlen(s), 'c'); - Scope sc; - e = e->semantic(&sc); - } - else if (ident == Id::mangleof) - { - e = Type::getProperty(loc, ident, flag); - } - else - { - e = toBasetype()->getProperty(loc, ident, flag); - } - return e; -} - -bool TypeEnum::isintegral() -{ - return sym->getMemtype(Loc())->isintegral(); -} - -bool TypeEnum::isfloating() -{ - return sym->getMemtype(Loc())->isfloating(); -} - -bool TypeEnum::isreal() -{ - return sym->getMemtype(Loc())->isreal(); -} - -bool TypeEnum::isimaginary() -{ - return sym->getMemtype(Loc())->isimaginary(); -} - -bool TypeEnum::iscomplex() -{ - return sym->getMemtype(Loc())->iscomplex(); -} - -bool TypeEnum::isunsigned() -{ - return sym->getMemtype(Loc())->isunsigned(); -} - -bool TypeEnum::isscalar() -{ - return sym->getMemtype(Loc())->isscalar(); -} - -int TypeEnum::isString() -{ - return sym->getMemtype(Loc())->isString(); -} - -int TypeEnum::isAssignable() -{ - return sym->getMemtype(Loc())->isAssignable(); -} - -int TypeEnum::checkBoolean() -{ - return sym->getMemtype(Loc())->checkBoolean(); -} - -int TypeEnum::needsDestruction() -{ - return sym->getMemtype(Loc())->needsDestruction(); -} - -bool TypeEnum::needsNested() -{ - return sym->getMemtype(Loc())->needsNested(); -} - -MATCH TypeEnum::implicitConvTo(Type *to) -{ - MATCH m; - - //printf("TypeEnum::implicitConvTo()\n"); - if (ty == to->ty && sym == ((TypeEnum *)to)->sym) - m = (mod == to->mod) ? MATCHexact : MATCHconst; - else if (sym->getMemtype(Loc())->implicitConvTo(to)) - m = MATCHconvert; // match with conversions - else - m = MATCHnomatch; // no match - return m; -} - -MATCH TypeEnum::constConv(Type *to) -{ - if (equals(to)) - return MATCHexact; - if (ty == to->ty && sym == ((TypeEnum *)to)->sym && - MODimplicitConv(mod, to->mod)) - return MATCHconst; - return MATCHnomatch; -} - - -Expression *TypeEnum::defaultInit(Loc loc) -{ -#if LOGDEFAULTINIT - printf("TypeEnum::defaultInit() '%s'\n", toChars()); -#endif - // Initialize to first member of enum - Expression *e = sym->getDefaultValue(loc); - e = e->copy(); - e->type = this; // to deal with const, immutable, etc., variants - return e; -} - -int TypeEnum::isZeroInit(Loc loc) -{ - return sym->getDefaultValue(loc)->isBool(false); -} - -int TypeEnum::hasPointers() -{ - return toBasetype()->hasPointers(); -} - -Type *TypeEnum::nextOf() -{ - return sym->getMemtype(Loc())->nextOf(); -} - -/***************************** TypeTypedef *****************************/ - -TypeTypedef::TypeTypedef(TypedefDeclaration *sym) - : Type(Ttypedef) -{ - this->sym = sym; -} - -const char *TypeTypedef::kind() -{ - return "typedef"; -} - -Type *TypeTypedef::syntaxCopy() -{ - return this; -} - -char *TypeTypedef::toChars() +Type *TypeEnum::syntaxCopy() { - return Type::toChars(); + return this; } -Type *TypeTypedef::semantic(Loc loc, Scope *sc) +Type *TypeEnum::semantic(Loc loc, Scope *sc) { - //printf("TypeTypedef::semantic(%s), sem = %d\n", toChars(), sym->sem); - int errors = global.errors; - sym->semantic(sc); - if (errors != global.errors || sym->errors || sym->basetype->ty == Terror) - return terror; + //printf("TypeEnum::semantic() %s\n", toChars()); + if (deco) + return this; return merge(); } -d_uns64 TypeTypedef::size(Loc loc) +d_uns64 TypeEnum::size(Loc loc) { - return sym->basetype->size(loc); + return sym->getMemtype(loc)->size(loc); } -unsigned TypeTypedef::alignsize() +unsigned TypeEnum::alignsize() { - return sym->basetype->alignsize(); + Type *t = sym->getMemtype(Loc()); + if (t->ty == Terror) + return 4; + return t->alignsize(); } -Dsymbol *TypeTypedef::toDsymbol(Scope *sc) +Dsymbol *TypeEnum::toDsymbol(Scope *sc) { return sym; } -void TypeTypedef::toDecoBuffer(OutBuffer *buf, int flag) +Type *TypeEnum::toBasetype() { - Type::toDecoBuffer(buf, flag); - const char *name = sym->mangle(); - buf->writestring(name); + if (!sym->members && !sym->memtype) + return this; + return sym->getMemtype(Loc())->toBasetype(); } -void TypeTypedef::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) +void TypeEnum::toDecoBuffer(OutBuffer *buf, int flag) { - //printf("TypeTypedef::toCBuffer2() '%s'\n", sym->toChars()); - if (mod != this->mod) - { toCBuffer3(buf, hgs, mod); - return; - } - buf->writestring(sym->toChars()); + const char *name = mangle(sym); + Type::toDecoBuffer(buf, flag); + buf->writestring(name); } -Expression *TypeTypedef::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag) +Expression *TypeEnum::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag) { #if LOGDOTEXP - printf("TypeTypedef::dotExp(e = '%s', ident = '%s') '%s'\n", e->toChars(), ident->toChars(), toChars()); + printf("TypeEnum::dotExp(e = '%s', ident = '%s') '%s'\n", e->toChars(), ident->toChars(), toChars()); #endif - if (ident == Id::init) + Dsymbol *s = sym->search(e->loc, ident); + if (!s) { - return Type::dotExp(sc, e, ident, flag); + if (ident == Id::max || + ident == Id::min || + ident == Id::init || + ident == Id::mangleof || + !sym->memtype + ) + { + return getProperty(e->loc, ident, flag); + } + return sym->getMemtype(Loc())->dotExp(sc, e, ident, flag); } - return sym->basetype->dotExp(sc, e, ident, flag); + EnumMember *m = s->isEnumMember(); + return m->getVarExp(e->loc, sc); } -structalign_t TypeTypedef::alignment() +Expression *TypeEnum::getProperty(Loc loc, Identifier *ident, int flag) { - if (sym->inuse) + Expression *e; + if (ident == Id::max || ident == Id::min) + { + return sym->getMaxMinValue(loc, ident); + } + else if (ident == Id::init) { - sym->error("circular definition"); - sym->basetype = Type::terror; - return STRUCTALIGN_DEFAULT; + e = defaultInitLiteral(loc); } - sym->inuse = 1; - structalign_t a = sym->basetype->alignment(); - sym->inuse = 0; - return a; -} - -Expression *TypeTypedef::getProperty(Loc loc, Identifier *ident, int flag) -{ -#if LOGDOTEXP - printf("TypeTypedef::getProperty(ident = '%s') '%s'\n", ident->toChars(), toChars()); -#endif - if (ident == Id::init) + else if (ident == Id::stringof) + { + char *s = toChars(); + e = new StringExp(loc, s, strlen(s), 'c'); + Scope sc; + e = e->semantic(&sc); + } + else if (ident == Id::mangleof) + { + e = Type::getProperty(loc, ident, flag); + } + else { - return Type::getProperty(loc, ident, flag); + e = toBasetype()->getProperty(loc, ident, flag); } - return sym->basetype->getProperty(loc, ident, flag); + return e; } -bool TypeTypedef::isintegral() +bool TypeEnum::isintegral() { - //printf("TypeTypedef::isintegral()\n"); - //printf("sym = '%s'\n", sym->toChars()); - //printf("basetype = '%s'\n", sym->basetype->toChars()); - return sym->basetype->isintegral(); + return sym->getMemtype(Loc())->isintegral(); } -bool TypeTypedef::isfloating() +bool TypeEnum::isfloating() { - return sym->basetype->isfloating(); + return sym->getMemtype(Loc())->isfloating(); } -bool TypeTypedef::isreal() +bool TypeEnum::isreal() { - return sym->basetype->isreal(); + return sym->getMemtype(Loc())->isreal(); } -bool TypeTypedef::isimaginary() +bool TypeEnum::isimaginary() { - return sym->basetype->isimaginary(); + return sym->getMemtype(Loc())->isimaginary(); } -bool TypeTypedef::iscomplex() +bool TypeEnum::iscomplex() { - return sym->basetype->iscomplex(); + return sym->getMemtype(Loc())->iscomplex(); } -bool TypeTypedef::isunsigned() +bool TypeEnum::isunsigned() { - return sym->basetype->isunsigned(); + return sym->getMemtype(Loc())->isunsigned(); } -bool TypeTypedef::isscalar() +bool TypeEnum::isscalar() { - return sym->basetype->isscalar(); + return sym->getMemtype(Loc())->isscalar(); } -int TypeTypedef::isAssignable() +bool TypeEnum::isString() { - return sym->basetype->isAssignable(); + return sym->getMemtype(Loc())->isString(); } -int TypeTypedef::checkBoolean() +bool TypeEnum::isAssignable() { - return sym->basetype->checkBoolean(); + return sym->getMemtype(Loc())->isAssignable(); } -int TypeTypedef::needsDestruction() +bool TypeEnum::checkBoolean() { - return sym->basetype->needsDestruction(); + return sym->getMemtype(Loc())->checkBoolean(); } -bool TypeTypedef::needsNested() +bool TypeEnum::needsDestruction() { - return sym->basetype->needsNested(); + return sym->getMemtype(Loc())->needsDestruction(); } -Type *TypeTypedef::toBasetype() +bool TypeEnum::needsNested() { - if (sym->inuse) - { - sym->error("circular definition"); - sym->basetype = Type::terror; - return Type::terror; - } - sym->inuse = 1; - Type *t = sym->basetype->toBasetype(); - sym->inuse = 0; - t = t->addMod(mod); - return t; + return sym->getMemtype(Loc())->needsNested(); } -MATCH TypeTypedef::implicitConvTo(Type *to) -{ MATCH m; +MATCH TypeEnum::implicitConvTo(Type *to) +{ + MATCH m; - //printf("TypeTypedef::implicitConvTo(to = %s) %s\n", to->toChars(), toChars()); - if (equals(to)) - m = MATCHexact; // exact match - else if (sym->basetype->implicitConvTo(to)) + //printf("TypeEnum::implicitConvTo()\n"); + if (ty == to->ty && sym == ((TypeEnum *)to)->sym) + m = (mod == to->mod) ? MATCHexact : MATCHconst; + else if (sym->getMemtype(Loc())->implicitConvTo(to)) m = MATCHconvert; // match with conversions - else if (ty == to->ty && sym == ((TypeTypedef *)to)->sym) - { - m = constConv(to); - } else m = MATCHnomatch; // no match return m; } -MATCH TypeTypedef::constConv(Type *to) +MATCH TypeEnum::constConv(Type *to) { if (equals(to)) return MATCHexact; - if (ty == to->ty && sym == ((TypeTypedef *)to)->sym) - return sym->basetype->implicitConvTo(((TypeTypedef *)to)->sym->basetype); + if (ty == to->ty && sym == ((TypeEnum *)to)->sym && + MODimplicitConv(mod, to->mod)) + return MATCHconst; return MATCHnomatch; } -Type *TypeTypedef::toHeadMutable() -{ - if (!mod) - return this; - - Type *tb = toBasetype(); - Type *t = tb->toHeadMutable(); - if (t->equals(tb)) - return this; - else - return mutableOf(); -} - -Expression *TypeTypedef::defaultInit(Loc loc) -{ -#if LOGDEFAULTINIT - printf("TypeTypedef::defaultInit() '%s'\n", toChars()); -#endif - if (sym->init) - { - //sym->init->toExpression()->print(); - return sym->init->toExpression(); - } - Type *bt = sym->basetype; - Expression *e = bt->defaultInit(loc); - e->type = this; - while (bt->ty == Tsarray) - { TypeSArray *tsa = (TypeSArray *)bt; - e->type = tsa->next; - bt = tsa->next->toBasetype(); - } - return e; -} -Expression *TypeTypedef::defaultInitLiteral(Loc loc) +Expression *TypeEnum::defaultInit(Loc loc) { #if LOGDEFAULTINIT - printf("TypeTypedef::defaultInitLiteral() '%s'\n", toChars()); + printf("TypeEnum::defaultInit() '%s'\n", toChars()); #endif - if (sym->init) - { - //sym->init->toExpression()->print(); - Expression *e = sym->init->toExpression(); - if (!e) - { - error(loc, "void initializer has no value"); - e = new ErrorExp(); - } - return e; - } - Type *bt = sym->basetype; - Expression *e = bt->defaultInitLiteral(loc); - e->type = this; + // Initialize to first member of enum + Expression *e = sym->getDefaultValue(loc); + e = e->copy(); + e->loc = loc; + e->type = this; // to deal with const, immutable, etc., variants return e; } -int TypeTypedef::isZeroInit(Loc loc) +bool TypeEnum::isZeroInit(Loc loc) { - if (sym->init) - { - if (sym->init->isVoidInitializer()) - return 1; // initialize voids to 0 - Expression *e = sym->init->toExpression(); - if (e && e->isBool(false)) - return 1; - return 0; // assume not - } - if (sym->inuse) - { - sym->error("circular definition"); - sym->basetype = Type::terror; - } - sym->inuse = 1; - int result = sym->basetype->isZeroInit(loc); - sym->inuse = 0; - return result; + return sym->getDefaultValue(loc)->isBool(false) != 0; } -int TypeTypedef::hasPointers() +int TypeEnum::hasPointers() { - return toBasetype()->hasPointers(); + return sym->getMemtype(Loc())->hasPointers(); } -int TypeTypedef::hasWild() +Type *TypeEnum::nextOf() { - assert(toBasetype()); - return mod & MODwild || toBasetype()->hasWild(); + return sym->getMemtype(Loc())->nextOf(); } /***************************** TypeStruct *****************************/ @@ -8248,10 +7570,11 @@ Type *TypeStruct::semantic(Loc loc, Scope *sc) { //printf("TypeStruct::semantic('%s')\n", sym->toChars()); - /* Cannot do semantic for sym because scope chain may not - * be right. + /* Don't semantic for sym because it should be deferred until + * sizeof needed or its members accessed. */ - //sym->semantic(sc); + // instead, parent should be set correctly + assert(sym->parent); return merge(); } @@ -8274,26 +7597,12 @@ Dsymbol *TypeStruct::toDsymbol(Scope *sc) void TypeStruct::toDecoBuffer(OutBuffer *buf, int flag) { - const char *name = sym->mangle(); + const char *name = mangle(sym); //printf("TypeStruct::toDecoBuffer('%s') = '%s'\n", toChars(), name); Type::toDecoBuffer(buf, flag); buf->writestring(name); } -void TypeStruct::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - if (mod != this->mod) - { - toCBuffer3(buf, hgs, mod); - return; - } - TemplateInstance *ti = sym->parent->isTemplateInstance(); - if (ti && ti->toAlias() == sym) - buf->writestring(ti->toChars()); - else - buf->writestring(sym->toChars()); -} - Expression *TypeStruct::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag) { Dsymbol *s; @@ -8321,13 +7630,13 @@ Expression *TypeStruct::dotExp(Scope *sc, Expression *e, Identifier *ident, int Expression *e0 = NULL; Expression *ev = e->op == TOKtype ? NULL : e; - if (sc->func && ev && ev->hasSideEffect()) + if (sc->func && ev && !isTrivialExp(ev)) { Identifier *id = Lexer::uniqueId("__tup"); ExpInitializer *ei = new ExpInitializer(e->loc, ev); VarDeclaration *vd = new VarDeclaration(e->loc, NULL, id, ei); - vd->storage_class |= STCtemp | STCctfe | STCref | STCforeach; - + vd->storage_class |= STCtemp | STCctfe + | (ev->isLvalue() ? STCref | STCforeach : STCrvalue); e0 = new DeclarationExp(e->loc, vd); ev = new VarExp(e->loc, vd); } @@ -8353,14 +7662,13 @@ Expression *TypeStruct::dotExp(Scope *sc, Expression *e, Identifier *ident, int } if (e->op == TOKdotexp) - { DotExp *de = (DotExp *)e; - + { + DotExp *de = (DotExp *)e; if (de->e1->op == TOKimport) { assert(0); // cannot find a case where this happens; leave // assert in until we do ScopeExp *se = (ScopeExp *)de->e1; - s = se->sds->search(e->loc, ident); e = de->e1; goto L1; @@ -8428,18 +7736,21 @@ Expression *TypeStruct::dotExp(Scope *sc, Expression *e, Identifier *ident, int TemplateInstance *ti = s->isTemplateInstance(); if (ti) - { if (!ti->semanticRun) + { + if (!ti->semanticRun) { - if (global.errors) - return new ErrorExp(); // TemplateInstance::semantic() will fail anyway ti->semantic(sc); + if (!ti->inst || ti->errors) // if template failed to expand + return new ErrorExp(); } s = ti->inst->toAlias(); if (!s->isTemplateInstance()) goto L1; - Expression *de = new DotExp(e->loc, e, new ScopeExp(e->loc, ti)); - de->type = e->type; - return de; + if (e->op == TOKtype) + e = new ScopeExp(e->loc, ti); + else + e = new DotExp(e->loc, e, new ScopeExp(e->loc, ti)); + return e->semantic(sc); } if (s->isImport() || s->isModule() || s->isPackage()) @@ -8511,9 +7822,9 @@ Expression *TypeStruct::dotExp(Scope *sc, Expression *e, Identifier *ident, int if (v->toParent() != sym) sym->error(e->loc, "'%s' is not a member", v->toChars()); +#if 0 // *(&e + offset) accessCheck(e->loc, sc, e, d); -#if 0 Expression *b = new AddrExp(e->loc, e); b->type = e->type->pointerTo(); b = new AddExp(e->loc, b, new IntegerExp(e->loc, v->offset, Type::tint32)); @@ -8555,8 +7866,9 @@ Expression *TypeStruct::defaultInitLiteral(Loc loc) #if LOGDEFAULTINIT printf("TypeStruct::defaultInitLiteral() '%s'\n", toChars()); #endif - //if (sym->isNested()) - // return defaultInit(loc); + sym->size(loc); + if (sym->sizeok != SIZEOKdone) + return new ErrorExp(); Expressions *structelems = new Expressions(); structelems->setDim(sym->fields.dim - sym->isNested()); unsigned offset = 0; @@ -8599,17 +7911,17 @@ Expression *TypeStruct::defaultInitLiteral(Loc loc) } -int TypeStruct::isZeroInit(Loc loc) +bool TypeStruct::isZeroInit(Loc loc) { - return sym->zeroInit; + return sym->zeroInit != 0; } -int TypeStruct::checkBoolean() +bool TypeStruct::checkBoolean() { return false; } -int TypeStruct::needsDestruction() +bool TypeStruct::needsDestruction() { return sym->dtor != NULL; } @@ -8628,16 +7940,17 @@ bool TypeStruct::needsNested() return false; } -int TypeStruct::isAssignable() +bool TypeStruct::isAssignable() { - int assignable = true; + bool assignable = true; unsigned offset; - /* If any of the fields are const or invariant, + /* If any of the fields are const or immutable, * then one cannot assign this struct. */ for (size_t i = 0; i < sym->fields.dim; i++) - { VarDeclaration *v = sym->fields[i]; + { + VarDeclaration *v = sym->fields[i]; //printf("%s [%d] v = (%s) %s, v->offset = %d, v->parent = %s", sym->toChars(), i, v->kind(), v->toChars(), v->offset, v->parent->kind()); if (i == 0) ; @@ -8682,18 +7995,6 @@ MATCH TypeStruct::implicitConvTo(Type *to) { MATCH m; //printf("TypeStruct::implicitConvTo(%s => %s)\n", toChars(), to->toChars()); - if (to->ty == Taarray && sym->ident == Id::AssociativeArray) - { - /* If there is an error instantiating AssociativeArray!(), it shouldn't - * be reported -- it just means implicit conversion is impossible. - */ - int errs = global.startGagging(); - to = ((TypeAArray*)to)->getImpl()->type; - if (global.endGagging(errs)) - { - return MATCHnomatch; - } - } if (ty == to->ty && sym == ((TypeStruct *)to)->sym) { @@ -8765,12 +8066,12 @@ MATCH TypeStruct::constConv(Type *to) return MATCHnomatch; } -unsigned TypeStruct::deduceWild(Type *t, bool isRef) +unsigned char TypeStruct::deduceWild(Type *t, bool isRef) { if (ty == t->ty && sym == ((TypeStruct *)t)->sym) return Type::deduceWild(t, isRef); - unsigned wm = 0; + unsigned char wm = 0; if (t->hasWild() && sym->aliasthis && !(att & RECtracing)) { @@ -8817,9 +8118,13 @@ Type *TypeClass::syntaxCopy() Type *TypeClass::semantic(Loc loc, Scope *sc) { //printf("TypeClass::semantic(%s)\n", sym->toChars()); - if (deco) - return this; - //printf("\t%s\n", merge()->deco); + + /* Don't semantic for sym because it should be deferred until + * sizeof needed or its members accessed. + */ + // instead, parent should be set correctly + assert(sym->parent); + return merge(); } @@ -8835,26 +8140,12 @@ Dsymbol *TypeClass::toDsymbol(Scope *sc) void TypeClass::toDecoBuffer(OutBuffer *buf, int flag) { - const char *name = sym->mangle(); + const char *name = mangle(sym); //printf("TypeClass::toDecoBuffer('%s' flag=%d mod=%x) = '%s'\n", toChars(), flag, mod, name); Type::toDecoBuffer(buf, flag); buf->writestring(name); } -void TypeClass::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - if (mod != this->mod) - { - toCBuffer3(buf, hgs, mod); - return; - } - TemplateInstance *ti = sym->parent->isTemplateInstance(); - if (ti && ti->toAlias() == sym) - buf->writestring(ti->toChars()); - else - buf->writestring(sym->toChars()); -} - Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident, int flag) { Dsymbol *s; @@ -8864,12 +8155,11 @@ Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident, int f #endif if (e->op == TOKdotexp) - { DotExp *de = (DotExp *)e; - + { + DotExp *de = (DotExp *)e; if (de->e1->op == TOKimport) { ScopeExp *se = (ScopeExp *)de->e1; - s = se->sds->search(e->loc, ident); e = de->e1; goto L1; @@ -8898,13 +8188,13 @@ Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident, int f Expression *e0 = NULL; Expression *ev = e->op == TOKtype ? NULL : e; - if (sc->func && ev && ev->hasSideEffect()) + if (sc->func && ev && !isTrivialExp(ev)) { Identifier *id = Lexer::uniqueId("__tup"); ExpInitializer *ei = new ExpInitializer(e->loc, ev); VarDeclaration *vd = new VarDeclaration(e->loc, NULL, id, ei); - vd->storage_class |= STCtemp | STCctfe | STCref | STCforeach; - + vd->storage_class |= STCtemp | STCctfe + | (ev->isLvalue() ? STCref | STCforeach : STCrvalue); e0 = new DeclarationExp(e->loc, vd); ev = new VarExp(e->loc, vd); } @@ -8932,6 +8222,12 @@ Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident, int f return e; } + // Bugzilla 12543 + if (ident == Id::__sizeof || ident == Id::__xalignof || ident == Id::mangleof) + { + return Type::getProperty(e->loc, ident, 0); + } + s = sym->search(e->loc, ident); L1: if (!s) @@ -8966,11 +8262,12 @@ Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident, int f if (!sym->vclassinfo) sym->vclassinfo = new TypeInfoClassDeclaration(sym->type); e = new VarExp(e->loc, sym->vclassinfo); - e = e->addressOf(sc); + e = e->addressOf(); e->type = t; // do this so we don't get redundant dereference } else - { /* For class objects, the classinfo reference is the first + { + /* For class objects, the classinfo reference is the first * entry in the vtbl[] */ e = new PtrExp(e->loc, e); @@ -8978,7 +8275,8 @@ Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident, int f if (sym->isInterfaceDeclaration()) { if (sym->isCPPinterface()) - { /* C++ interface vtbl[]s are different in that the + { + /* C++ interface vtbl[]s are different in that the * first entry is always pointer to the first virtual * function, not classinfo. * We can't get a .classinfo for it. @@ -9000,8 +8298,9 @@ Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident, int f } if (ident == Id::__vptr) - { /* The pointer to the vtbl[] - * *cast(invariant(void*)**)e + { + /* The pointer to the vtbl[] + * *cast(immutable(void*)**)e */ e = e->castTo(sc, tvoidptr->immutableOf()->pointerTo()->pointerTo()); e = new PtrExp(e->loc, e); @@ -9010,7 +8309,8 @@ Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident, int f } if (ident == Id::__monitor) - { /* The handle to the monitor (call it a void*) + { + /* The handle to the monitor (call it a void*) * *(cast(void**)e + 1) */ e = e->castTo(sc, tvoidptr->pointerTo()); @@ -9027,6 +8327,8 @@ Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident, int f } if (ident == Id::outer && sym->vthis) { + if (sym->vthis->scope) + sym->vthis->semantic(NULL); s = sym->vthis; } else @@ -9083,18 +8385,21 @@ Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident, int f TemplateInstance *ti = s->isTemplateInstance(); if (ti) - { if (!ti->semanticRun) + { + if (!ti->semanticRun) { - if (global.errors) - return new ErrorExp(); // TemplateInstance::semantic() will fail anyway ti->semantic(sc); + if (!ti->inst || ti->errors) // if template failed to expand + return new ErrorExp(); } s = ti->inst->toAlias(); if (!s->isTemplateInstance()) goto L1; - Expression *de = new DotExp(e->loc, e, new ScopeExp(e->loc, ti)); - de->type = e->type; - return de; + if (e->op == TOKtype) + e = new ScopeExp(e->loc, ti); + else + e = new DotExp(e->loc, e, new ScopeExp(e->loc, ti)); + return e->semantic(sc); } if (s->isImport() || s->isModule() || s->isPackage()) @@ -9237,7 +8542,7 @@ ClassDeclaration *TypeClass::isClassHandle() return sym; } -int TypeClass::isscope() +bool TypeClass::isscope() { return sym->isscope; } @@ -9263,12 +8568,14 @@ MATCH TypeClass::implicitConvTo(Type *to) ClassDeclaration *cdto = to->isClassHandle(); if (cdto) { - if (cdto->scope) + //printf("TypeClass::implicitConvTo(to = '%s') %s, isbase = %d %d\n", to->toChars(), toChars(), cdto->isBaseInfoComplete(), sym->isBaseInfoComplete()); + if (cdto->scope && !cdto->isBaseInfoComplete()) cdto->semantic(NULL); - if (sym->scope) + if (sym->scope && !sym->isBaseInfoComplete()) sym->semantic(NULL); if (cdto->isBaseOf(sym, NULL) && MODimplicitConv(mod, to->mod)) - { //printf("'to' is base\n"); + { + //printf("'to' is base\n"); return MATCHconvert; } } @@ -9308,13 +8615,13 @@ MATCH TypeClass::constConv(Type *to) return MATCHnomatch; } -unsigned TypeClass::deduceWild(Type *t, bool isRef) +unsigned char TypeClass::deduceWild(Type *t, bool isRef) { ClassDeclaration *cd = t->isClassHandle(); if (cd && (sym == cd || cd->isBaseOf(sym, NULL))) return Type::deduceWild(t, isRef); - unsigned wm = 0; + unsigned char wm = 0; if (t->hasWild() && sym->aliasthis && !(att & RECtracing)) { @@ -9339,12 +8646,12 @@ Expression *TypeClass::defaultInit(Loc loc) return new NullExp(loc, this); } -int TypeClass::isZeroInit(Loc loc) +bool TypeClass::isZeroInit(Loc loc) { - return 1; + return true; } -int TypeClass::checkBoolean() +bool TypeClass::checkBoolean() { return true; } @@ -9477,21 +8784,6 @@ bool TypeTuple::equals(RootObject *o) return false; } -Type *TypeTuple::reliesOnTident(TemplateParameters *tparams) -{ - if (arguments) - { - for (size_t i = 0; i < arguments->dim; i++) - { - Parameter *arg = (*arguments)[i]; - Type *t = arg->type->reliesOnTident(tparams); - if (t) - return t; - } - } - return NULL; -} - #if 0 Type *TypeTuple::makeConst() { @@ -9510,11 +8802,6 @@ Type *TypeTuple::makeConst() } #endif -void TypeTuple::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - Parameter::argsToCBuffer(buf, hgs, arguments, 0); -} - void TypeTuple::toDecoBuffer(OutBuffer *buf, int flag) { //printf("TypeTuple::toDecoBuffer() this = %p, %s\n", this, toChars()); @@ -9595,13 +8882,13 @@ Type *TypeSlice::syntaxCopy() Type *TypeSlice::semantic(Loc loc, Scope *sc) { //printf("TypeSlice::semantic() %s\n", toChars()); - next = next->semantic(loc, sc); - transitive(); - //printf("next: %s\n", next->toChars()); + Type *tn = next->semantic(loc, sc); + //printf("next: %s\n", tn->toChars()); - Type *tbn = next->toBasetype(); + Type *tbn = tn->toBasetype(); if (tbn->ty != Ttuple) - { error(loc, "can only slice tuple types, not %s", tbn->toChars()); + { + error(loc, "can only slice tuple types, not %s", tbn->toChars()); return Type::terror; } TypeTuple *tt = (TypeTuple *)tbn; @@ -9615,19 +8902,23 @@ Type *TypeSlice::semantic(Loc loc, Scope *sc) uinteger_t i2 = upr->toUInteger(); if (!(i1 <= i2 && i2 <= tt->arguments->dim)) - { error(loc, "slice [%llu..%llu] is out of range of [0..%u]", i1, i2, tt->arguments->dim); + { + error(loc, "slice [%llu..%llu] is out of range of [0..%u]", i1, i2, tt->arguments->dim); return Type::terror; } + next = tn; + transitive(); + Parameters *args = new Parameters; args->reserve((size_t)(i2 - i1)); for (size_t i = (size_t)i1; i < (size_t)i2; i++) - { Parameter *arg = (*tt->arguments)[i]; + { + Parameter *arg = (*tt->arguments)[i]; args->push(arg); } - - Type *t = (new TypeTuple(args))->semantic(loc, sc); - return t; + Type *t = new TypeTuple(args); + return t->semantic(loc, sc); } void TypeSlice::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid) @@ -9697,21 +8988,6 @@ void TypeSlice::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol } } -void TypeSlice::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - if (mod != this->mod) - { toCBuffer3(buf, hgs, mod); - return; - } - next->toCBuffer2(buf, hgs, this->mod); - - buf->writeByte('['); - sizeToCBuffer(buf, hgs, lwr); - buf->writestring(" .. "); - sizeToCBuffer(buf, hgs, upr); - buf->writeByte(']'); -} - /***************************** TypeNull *****************************/ TypeNull::TypeNull() @@ -9753,7 +9029,7 @@ MATCH TypeNull::implicitConvTo(Type *to) return MATCHnomatch; } -int TypeNull::checkBoolean() +bool TypeNull::checkBoolean() { return true; } @@ -9764,16 +9040,6 @@ void TypeNull::toDecoBuffer(OutBuffer *buf, int flag) Type::toDecoBuffer(buf, flag); } -void TypeNull::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) -{ - if (mod != this->mod) - { - toCBuffer3(buf, hgs, mod); - return; - } - buf->writestring("typeof(null)"); -} - d_uns64 TypeNull::size(Loc loc) { return tvoidptr->size(loc); } Expression *TypeNull::defaultInit(Loc loc) { return new NullExp(Loc(), Type::tnull); } @@ -9826,7 +9092,7 @@ char *Parameter::argsTypesToChars(Parameters *args, int varargs) HdrGenState hgs; argsToCBuffer(&buf, &hgs, args, varargs); - buf.writebyte(0); + buf.writeByte(0); return buf.extractData(); } @@ -10057,11 +9323,13 @@ int Parameter::foreach(Parameters *args, Parameter::ForeachDg dg, void *ctx, siz size_t n = pn ? *pn : 0; // take over index int result = 0; for (size_t i = 0; i < args->dim; i++) - { Parameter *arg = (*args)[i]; + { + Parameter *arg = (*args)[i]; Type *t = arg->type->toBasetype(); if (t->ty == Ttuple) - { TypeTuple *tu = (TypeTuple *)t; + { + TypeTuple *tu = (TypeTuple *)t; result = foreach(tu->arguments, dg, ctx, &n); } else diff --git a/gcc/d/dfrontend/mtype.h b/gcc/d/dfrontend/mtype.h index 4b009e997..72d68eaef 100644 --- a/gcc/d/dfrontend/mtype.h +++ b/gcc/d/dfrontend/mtype.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/mtype.h + */ #ifndef DMD_MTYPE_H #define DMD_MTYPE_H @@ -29,7 +30,6 @@ class StructDeclaration; class ClassDeclaration; class VarDeclaration; class EnumDeclaration; -class TypedefDeclaration; class TypeInfoDeclaration; class Dsymbol; class TemplateInstance; @@ -51,6 +51,9 @@ struct Symbol; class TypeTuple; void semanticTypeInfo(Scope *sc, Type *t); +MATCH deduceType(RootObject *o, Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm = NULL, size_t inferStart = 0); +Type *reliesOnTident(Type *t, TemplateParameters *tparams = NULL, size_t iStart = 0); +StorageClass ModToStc(unsigned mod); enum ENUMTY { @@ -65,7 +68,6 @@ enum ENUMTY Tstruct, Tenum, - Ttypedef, Tdelegate, Tnone, Tvoid, @@ -111,20 +113,26 @@ extern int Tsize_t; extern int Tptrdiff_t; -/* pick this order of numbers so switch statements work better +/** + * type modifiers + * pick this order of numbers so switch statements work better */ -#define MODconst 1 // type is const -#define MODimmutable 4 // type is immutable -#define MODshared 2 // type is shared -#define MODwild 8 // type is wild -#define MODwildconst (MODwild | MODconst) // type is wild const -#define MODmutable 0x10 // type is mutable (only used in wildcard matching) +enum MODFlags +{ + MODconst = 1, // type is const + MODimmutable = 4, // type is immutable + MODshared = 2, // type is shared + MODwild = 8, // type is wild + MODwildconst = (MODwild | MODconst), // type is wild const + MODmutable = 0x10 // type is mutable (only used in wildcard matching) +}; +typedef unsigned char MOD; class Type : public RootObject { public: TY ty; - unsigned char mod; // modifiers MODxxxx + MOD mod; // modifiers MODxxxx char *deco; /* These are cached values that are lazily evaluated by constOf(), immutableOf(), etc. @@ -196,7 +204,6 @@ class Type : public RootObject static ClassDeclaration *typeinfoclass; static ClassDeclaration *typeinfointerface; static ClassDeclaration *typeinfostruct; - static ClassDeclaration *typeinfotypedef; static ClassDeclaration *typeinfopointer; static ClassDeclaration *typeinfoarray; static ClassDeclaration *typeinfostaticarray; @@ -211,7 +218,6 @@ class Type : public RootObject static ClassDeclaration *typeinfoshared; static ClassDeclaration *typeinfowild; - static TemplateDeclaration *associativearray; static TemplateDeclaration *rtinfo; static Type *basic[TMAX]; @@ -233,9 +239,11 @@ class Type : public RootObject Type *copy(); virtual Type *syntaxCopy(); bool equals(RootObject *o); - int dyncast() { return DYNCAST_TYPE; } // kludge for template.isType() + // kludge for template.isType() + int dyncast() { return DYNCAST_TYPE; } int covariant(Type *t, StorageClass *pstc = NULL); char *toChars(); + char *toPrettyChars(bool QualifyTypes = false); static char needThisPrefix(); static void init(); @@ -248,11 +256,14 @@ class Type : public RootObject virtual void toDecoBuffer(OutBuffer *buf, int flag = 0); Type *merge(); Type *merge2(); - virtual void toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs); - virtual void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); - void toCBuffer3(OutBuffer *buf, HdrGenState *hgs, int mod); + void toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs); void modToBuffer(OutBuffer *buf); char *modToChars(); + + /** For each active modifier (MODconst, MODimmutable, etc) call fp with a + void* for the work param and a string representation of the attribute. */ + int modifiersApply(void *param, int (*fp)(void *, const char *)); + virtual bool isintegral(); virtual bool isfloating(); // real, imaginary, or complex virtual bool isreal(); @@ -260,10 +271,10 @@ class Type : public RootObject virtual bool iscomplex(); virtual bool isscalar(); virtual bool isunsigned(); - virtual int isscope(); - virtual int isString(); - virtual int isAssignable(); - virtual int checkBoolean(); // if can be converted to boolean value + virtual bool isscope(); + virtual bool isString(); + virtual bool isAssignable(); + virtual bool checkBoolean(); // if can be converted to boolean value virtual void checkDeprecated(Loc loc, Scope *sc); bool isConst() { return (mod & MODconst) != 0; } bool isImmutable() { return (mod & MODimmutable) != 0; } @@ -288,15 +299,15 @@ class Type : public RootObject void fixTo(Type *t); void check(); Type *addSTC(StorageClass stc); - Type *castMod(unsigned mod); - Type *addMod(unsigned mod); + Type *castMod(MOD mod); + Type *addMod(MOD mod); virtual Type *addStorageClass(StorageClass stc); Type *pointerTo(); Type *referenceTo(); Type *arrayOf(); Type *sarrayOf(dinteger_t dim); Type *aliasthisOf(); - int checkAliasThisRec(); + bool checkAliasThisRec(); virtual Type *makeConst(); virtual Type *makeImmutable(); virtual Type *makeShared(); @@ -311,7 +322,7 @@ class Type : public RootObject virtual int isBaseOf(Type *t, int *poffset); virtual MATCH implicitConvTo(Type *to); virtual MATCH constConv(Type *to); - virtual unsigned deduceWild(Type *t, bool isRef); + virtual unsigned char deduceWild(Type *t, bool isRef); virtual Type *substWildTo(unsigned mod); Type *unqualify(unsigned m); @@ -324,39 +335,29 @@ class Type : public RootObject Expression *noMember(Scope *sc, Expression *e, Identifier *ident, int flag); virtual Expression *defaultInit(Loc loc = Loc()); virtual Expression *defaultInitLiteral(Loc loc); - virtual Expression *voidInitLiteral(VarDeclaration *var); - virtual int isZeroInit(Loc loc = Loc()); // if initializer is 0 + virtual bool isZeroInit(Loc loc = Loc()); // if initializer is 0 virtual dt_t **toDt(dt_t **pdt); Identifier *getTypeInfoIdent(int internal); - virtual MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm = NULL); virtual void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false); Expression *getInternalTypeInfo(Scope *sc); Expression *getTypeInfo(Scope *sc); void genTypeInfo(Scope *sc); virtual TypeInfoDeclaration *getTypeInfoDeclaration(); virtual int builtinTypeInfo(); - virtual Type *reliesOnTident(TemplateParameters *tparams = NULL); virtual int hasWild(); virtual Expression *toExpression(); virtual int hasPointers(); - virtual TypeTuple *toArgTypes(); virtual Type *nextOf(); Type *baseElemOf(); uinteger_t sizemask(); - virtual int needsDestruction(); + virtual bool needsDestruction(); virtual bool needsNested(); - unsigned deduceWildHelper(Type **at, Type *tparam); - MATCH deduceTypeHelper(Type **at, Type *tparam); - static void error(Loc loc, const char *format, ...); static void warning(Loc loc, const char *format, ...); // For backend - virtual unsigned totym(); virtual type *toCtype(); - virtual type *toCParamtype(); - virtual Symbol *toSymbol(); // For eliminating dynamic_cast virtual TypeBasic *isTypeBasic(); @@ -369,14 +370,11 @@ class TypeError : public Type TypeError(); Type *syntaxCopy(); - void toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs); - d_uns64 size(Loc loc); Expression *getProperty(Loc loc, Identifier *ident, int flag); Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag); Expression *defaultInit(Loc loc); Expression *defaultInitLiteral(Loc loc); - TypeTuple *toArgTypes(); void accept(Visitor *v) { v->visit(this); } }; @@ -388,7 +386,6 @@ class TypeNext : public Type TypeNext(TY ty, Type *next); void toDecoBuffer(OutBuffer *buf, int flag); void checkDeprecated(Loc loc, Scope *sc); - Type *reliesOnTident(TemplateParameters *tparams = NULL); int hasWild(); Type *nextOf(); Type *makeConst(); @@ -401,7 +398,7 @@ class TypeNext : public Type Type *makeSharedWildConst(); Type *makeMutable(); MATCH constConv(Type *to); - unsigned deduceWild(Type *t, bool isRef); + unsigned char deduceWild(Type *t, bool isRef); void transitive(); void accept(Visitor *v) { v->visit(this); } }; @@ -420,7 +417,6 @@ class TypeBasic : public Type Expression *getProperty(Loc loc, Identifier *ident, int flag); Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag); char *toChars(); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); bool isintegral(); bool isfloating(); bool isreal(); @@ -430,9 +426,8 @@ class TypeBasic : public Type bool isunsigned(); MATCH implicitConvTo(Type *to); Expression *defaultInit(Loc loc); - int isZeroInit(Loc loc); + bool isZeroInit(Loc loc); int builtinTypeInfo(); - TypeTuple *toArgTypes(); // For eliminating dynamic_cast TypeBasic *isTypeBasic(); @@ -453,23 +448,19 @@ class TypeVector : public Type Expression *getProperty(Loc loc, Identifier *ident, int flag); Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag); char *toChars(); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); void toDecoBuffer(OutBuffer *buf, int flag); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm = NULL); - Type *reliesOnTident(TemplateParameters *tparams); bool isintegral(); bool isfloating(); bool isscalar(); bool isunsigned(); - int checkBoolean(); + bool checkBoolean(); MATCH implicitConvTo(Type *to); Expression *defaultInit(Loc loc); Expression *defaultInitLiteral(Loc loc); TypeBasic *elementType(); - int isZeroInit(Loc loc); + bool isZeroInit(Loc loc); dt_t **toDt(dt_t **pdt); TypeInfoDeclaration *getTypeInfoDeclaration(); - TypeTuple *toArgTypes(); type *toCtype(); void accept(Visitor *v) { v->visit(this); } @@ -497,28 +488,23 @@ class TypeSArray : public TypeArray Type *semantic(Loc loc, Scope *sc); void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false); void toDecoBuffer(OutBuffer *buf, int flag); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag); - int isString(); - int isZeroInit(Loc loc); + bool isString(); + bool isZeroInit(Loc loc); structalign_t alignment(); MATCH constConv(Type *to); MATCH implicitConvTo(Type *to); Expression *defaultInit(Loc loc); Expression *defaultInitLiteral(Loc loc); - Expression *voidInitLiteral(VarDeclaration *var); dt_t **toDt(dt_t **pdt); dt_t **toDtElem(dt_t **pdt, Expression *e); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm = NULL); TypeInfoDeclaration *getTypeInfoDeclaration(); Expression *toExpression(); int hasPointers(); - int needsDestruction(); + bool needsDestruction(); bool needsNested(); - TypeTuple *toArgTypes(); type *toCtype(); - type *toCParamtype(); void accept(Visitor *v) { v->visit(this); } }; @@ -534,18 +520,15 @@ class TypeDArray : public TypeArray Type *semantic(Loc loc, Scope *sc); void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false); void toDecoBuffer(OutBuffer *buf, int flag); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag); - int isString(); - int isZeroInit(Loc loc); - int checkBoolean(); + bool isString(); + bool isZeroInit(Loc loc); + bool checkBoolean(); MATCH implicitConvTo(Type *to); Expression *defaultInit(Loc loc); int builtinTypeInfo(); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm = NULL); TypeInfoDeclaration *getTypeInfoDeclaration(); int hasPointers(); - TypeTuple *toArgTypes(); type *toCtype(); void accept(Visitor *v) { v->visit(this); } @@ -558,28 +541,21 @@ class TypeAArray : public TypeArray Loc loc; Scope *sc; - StructDeclaration *impl; // implementation - TypeAArray(Type *t, Type *index); static TypeAArray *create(Type *t, Type *index); const char *kind(); Type *syntaxCopy(); d_uns64 size(Loc loc); Type *semantic(Loc loc, Scope *sc); - StructDeclaration *getImpl(); void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false); void toDecoBuffer(OutBuffer *buf, int flag); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag); Expression *defaultInit(Loc loc); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm = NULL); - int isZeroInit(Loc loc); - int checkBoolean(); + bool isZeroInit(Loc loc); + bool checkBoolean(); TypeInfoDeclaration *getTypeInfoDeclaration(); - Type *reliesOnTident(TemplateParameters *tparams); Expression *toExpression(); int hasPointers(); - TypeTuple *toArgTypes(); MATCH implicitConvTo(Type *to); MATCH constConv(Type *to); @@ -598,15 +574,13 @@ class TypePointer : public TypeNext Type *syntaxCopy(); Type *semantic(Loc loc, Scope *sc); d_uns64 size(Loc loc); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); MATCH implicitConvTo(Type *to); MATCH constConv(Type *to); bool isscalar(); Expression *defaultInit(Loc loc); - int isZeroInit(Loc loc); + bool isZeroInit(Loc loc); TypeInfoDeclaration *getTypeInfoDeclaration(); int hasPointers(); - TypeTuple *toArgTypes(); type *toCtype(); void accept(Visitor *v) { v->visit(this); } @@ -620,10 +594,9 @@ class TypeReference : public TypeNext Type *syntaxCopy(); Type *semantic(Loc loc, Scope *sc); d_uns64 size(Loc loc); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag); Expression *defaultInit(Loc loc); - int isZeroInit(Loc loc); + bool isZeroInit(Loc loc); void accept(Visitor *v) { v->visit(this); } }; @@ -641,6 +614,16 @@ enum TRUST TRUSTsafe = 3, // @safe }; +// in hdrgen.c +void trustToBuffer(OutBuffer *buf, TRUST trust); +const char *trustToChars(TRUST trust); + +enum TRUSTformat +{ + TRUSTformatDefault, // do not emit @system when trust == TRUSTdefault + TRUSTformatSystem, // emit @system when trust == TRUSTdefault +}; + enum PURE { PUREimpure = 0, // not pure at all @@ -650,6 +633,8 @@ enum PURE PUREfwdref = 4, // it's pure, but not known which level yet }; +RET retStyle(TypeFunction *tf); + class TypeFunction : public TypeNext { public: @@ -659,6 +644,7 @@ class TypeFunction : public TypeNext int varargs; // 1: T t, ...) style for variable number of arguments // 2: T t ...) style for variable number of arguments bool isnothrow; // true: nothrow + bool isnogc; // true: is @nogc bool isproperty; // can be called without parentheses bool isref; // true: returns a reference LINK linkage; // calling convention @@ -676,23 +662,18 @@ class TypeFunction : public TypeNext Type *semantic(Loc loc, Scope *sc); void purityLevel(); void toDecoBuffer(OutBuffer *buf, int flag); - void toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs); - void toCBufferWithAttributes(OutBuffer *buf, Identifier *ident, HdrGenState* hgs, TypeFunction *attrs, TemplateDeclaration *td); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); - void attributesToCBuffer(OutBuffer *buf, int mod); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm = NULL); TypeInfoDeclaration *getTypeInfoDeclaration(); - Type *reliesOnTident(TemplateParameters *tparams = NULL); bool hasLazyParameters(); bool parameterEscapes(Parameter *p); Type *addStorageClass(StorageClass stc); + /** For each active attribute (ref/const/nogc/etc) call fp with a void* for the + work param and a string representation of the attribute. */ + int attributesApply(void *param, int (*fp)(void *, const char *), TRUSTformat trustFormat = TRUSTformatDefault); + Type *substWildTo(unsigned mod); MATCH callMatch(Type *tthis, Expressions *toargs, int flag = 0); type *toCtype(); - RET retStyle(); - - unsigned totym(); Expression *defaultInit(Loc loc); void accept(Visitor *v) { v->visit(this); } @@ -710,14 +691,12 @@ class TypeDelegate : public TypeNext d_uns64 size(Loc loc); unsigned alignsize(); MATCH implicitConvTo(Type *to); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); Expression *defaultInit(Loc loc); - int isZeroInit(Loc loc); - int checkBoolean(); + bool isZeroInit(Loc loc); + bool checkBoolean(); TypeInfoDeclaration *getTypeInfoDeclaration(); Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag); int hasPointers(); - TypeTuple *toArgTypes(); type *toCtype(); void accept(Visitor *v) { v->visit(this); } @@ -727,14 +706,14 @@ class TypeQualified : public Type { public: Loc loc; - Objects idents; // array of Identifier and TypeInstance, - // representing ident.ident!tiargs.ident. ... etc. + // array of Identifier and TypeInstance, + // representing ident.ident!tiargs.ident. ... etc. + Objects idents; TypeQualified(TY ty, Loc loc); void syntaxCopyHelper(TypeQualified *t); void addIdent(Identifier *ident); void addInst(TemplateInstance *inst); - void toCBuffer2Helper(OutBuffer *buf, HdrGenState *hgs); d_uns64 size(Loc loc); void resolveHelper(Loc loc, Scope *sc, Dsymbol *s, Dsymbol *scopesym, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false); @@ -752,12 +731,9 @@ class TypeIdentifier : public TypeQualified Type *syntaxCopy(); //char *toChars(); void toDecoBuffer(OutBuffer *buf, int flag); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false); Dsymbol *toDsymbol(Scope *sc); Type *semantic(Loc loc, Scope *sc); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm = NULL); - Type *reliesOnTident(TemplateParameters *tparams = NULL); Expression *toExpression(); void accept(Visitor *v) { v->visit(this); } }; @@ -774,12 +750,9 @@ class TypeInstance : public TypeQualified Type *syntaxCopy(); //char *toChars(); //void toDecoBuffer(OutBuffer *buf, int flag); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false); Type *semantic(Loc loc, Scope *sc); Dsymbol *toDsymbol(Scope *sc); - Type *reliesOnTident(TemplateParameters *tparams = NULL); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm = NULL); Expression *toExpression(); void accept(Visitor *v) { v->visit(this); } }; @@ -794,7 +767,6 @@ class TypeTypeof : public TypeQualified const char *kind(); Type *syntaxCopy(); Dsymbol *toDsymbol(Scope *sc); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false); Type *semantic(Loc loc, Scope *sc); d_uns64 size(Loc loc); @@ -810,7 +782,6 @@ class TypeReturn : public TypeQualified Dsymbol *toDsymbol(Scope *sc); void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false); Type *semantic(Loc loc, Scope *sc); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); void accept(Visitor *v) { v->visit(this); } }; @@ -841,25 +812,21 @@ class TypeStruct : public Type Type *semantic(Loc loc, Scope *sc); Dsymbol *toDsymbol(Scope *sc); void toDecoBuffer(OutBuffer *buf, int flag); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag); structalign_t alignment(); Expression *defaultInit(Loc loc); Expression *defaultInitLiteral(Loc loc); - Expression *voidInitLiteral(VarDeclaration *var); - int isZeroInit(Loc loc); - int isAssignable(); - int checkBoolean(); - int needsDestruction(); + bool isZeroInit(Loc loc); + bool isAssignable(); + bool checkBoolean(); + bool needsDestruction(); bool needsNested(); dt_t **toDt(dt_t **pdt); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm = NULL); TypeInfoDeclaration *getTypeInfoDeclaration(); int hasPointers(); - TypeTuple *toArgTypes(); MATCH implicitConvTo(Type *to); MATCH constConv(Type *to); - unsigned deduceWild(Type *t, bool isRef); + unsigned char deduceWild(Type *t, bool isRef); Type *toHeadMutable(); type *toCtype(); @@ -880,7 +847,6 @@ class TypeEnum : public Type Type *semantic(Loc loc, Scope *sc); Dsymbol *toDsymbol(Scope *sc); void toDecoBuffer(OutBuffer *buf, int flag); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag); Expression *getProperty(Loc loc, Identifier *ident, int flag); bool isintegral(); @@ -890,74 +856,24 @@ class TypeEnum : public Type bool iscomplex(); bool isscalar(); bool isunsigned(); - int checkBoolean(); - int isString(); - int isAssignable(); - int needsDestruction(); + bool checkBoolean(); + bool isString(); + bool isAssignable(); + bool needsDestruction(); bool needsNested(); MATCH implicitConvTo(Type *to); MATCH constConv(Type *to); Type *toBasetype(); Expression *defaultInit(Loc loc); - int isZeroInit(Loc loc); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm = NULL); + bool isZeroInit(Loc loc); TypeInfoDeclaration *getTypeInfoDeclaration(); int hasPointers(); - TypeTuple *toArgTypes(); Type *nextOf(); type *toCtype(); void accept(Visitor *v) { v->visit(this); } }; -class TypeTypedef : public Type -{ -public: - TypedefDeclaration *sym; - - TypeTypedef(TypedefDeclaration *sym); - const char *kind(); - Type *syntaxCopy(); - d_uns64 size(Loc loc); - unsigned alignsize(); - char *toChars(); - Type *semantic(Loc loc, Scope *sc); - Dsymbol *toDsymbol(Scope *sc); - void toDecoBuffer(OutBuffer *buf, int flag); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); - Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag); - structalign_t alignment(); - Expression *getProperty(Loc loc, Identifier *ident, int flag); - bool isintegral(); - bool isfloating(); - bool isreal(); - bool isimaginary(); - bool iscomplex(); - bool isscalar(); - bool isunsigned(); - int checkBoolean(); - int isAssignable(); - int needsDestruction(); - bool needsNested(); - Type *toBasetype(); - MATCH implicitConvTo(Type *to); - MATCH constConv(Type *to); - Type *toHeadMutable(); - Expression *defaultInit(Loc loc); - Expression *defaultInitLiteral(Loc loc); - int isZeroInit(Loc loc); - dt_t **toDt(dt_t **pdt); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm = NULL); - TypeInfoDeclaration *getTypeInfoDeclaration(); - int hasPointers(); - TypeTuple *toArgTypes(); - int hasWild(); - - type *toCtype(); - type *toCParamtype(); - void accept(Visitor *v) { v->visit(this); } -}; - class TypeClass : public Type { public: @@ -972,27 +888,23 @@ class TypeClass : public Type Type *semantic(Loc loc, Scope *sc); Dsymbol *toDsymbol(Scope *sc); void toDecoBuffer(OutBuffer *buf, int flag); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); Expression *dotExp(Scope *sc, Expression *e, Identifier *ident, int flag); ClassDeclaration *isClassHandle(); int isBaseOf(Type *t, int *poffset); MATCH implicitConvTo(Type *to); MATCH constConv(Type *to); - unsigned deduceWild(Type *t, bool isRef); + unsigned char deduceWild(Type *t, bool isRef); Type *toHeadMutable(); Expression *defaultInit(Loc loc); - int isZeroInit(Loc loc); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm = NULL); - int isscope(); - int checkBoolean(); + bool isZeroInit(Loc loc); + bool isscope(); + bool checkBoolean(); TypeInfoDeclaration *getTypeInfoDeclaration(); int hasPointers(); - TypeTuple *toArgTypes(); int builtinTypeInfo(); type *toCtype(); - Symbol *toSymbol(); void accept(Visitor *v) { v->visit(this); } }; @@ -1011,8 +923,6 @@ class TypeTuple : public Type Type *syntaxCopy(); Type *semantic(Loc loc, Scope *sc); bool equals(RootObject *o); - Type *reliesOnTident(TemplateParameters *tparams = NULL); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); void toDecoBuffer(OutBuffer *buf, int flag); Expression *getProperty(Loc loc, Identifier *ident, int flag); Expression *defaultInit(Loc loc); @@ -1031,7 +941,6 @@ class TypeSlice : public TypeNext Type *syntaxCopy(); Type *semantic(Loc loc, Scope *sc); void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps, bool intypeid = false); - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); void accept(Visitor *v) { v->visit(this); } }; @@ -1044,9 +953,7 @@ class TypeNull : public Type Type *syntaxCopy(); void toDecoBuffer(OutBuffer *buf, int flag); MATCH implicitConvTo(Type *to); - int checkBoolean(); - - void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); + bool checkBoolean(); d_uns64 size(Loc loc); Expression *defaultInit(Loc loc); @@ -1071,7 +978,8 @@ class Parameter : public RootObject Parameter *syntaxCopy(); Type *isLazyArray(); void toDecoBuffer(OutBuffer *buf); - int dyncast() { return DYNCAST_PARAMETER; } // kludge for template.isType() + // kludge for template.isType() + int dyncast() { return DYNCAST_PARAMETER; } static Parameters *arraySyntaxCopy(Parameters *args); static char *argsTypesToChars(Parameters *args, int varargs); static void argsToCBuffer(OutBuffer *buf, HdrGenState *hgs, Parameters *arguments, int varargs); @@ -1086,11 +994,11 @@ class Parameter : public RootObject int arrayTypeCompatible(Loc loc, Type *t1, Type *t2); int arrayTypeCompatibleWithoutCasting(Loc loc, Type *t1, Type *t2); -void MODtoBuffer(OutBuffer *buf, unsigned char mod); -char *MODtoChars(unsigned char mod); -int MODimplicitConv(unsigned char modfrom, unsigned char modto); -int MODmethodConv(unsigned char modfrom, unsigned char modto); -unsigned char MODmerge(unsigned char mod1, unsigned char mod2); +void MODtoBuffer(OutBuffer *buf, MOD mod); +char *MODtoChars(MOD mod); +bool MODimplicitConv(MOD modfrom, MOD modto); +bool MODmethodConv(MOD modfrom, MOD modto); +MOD MODmerge(MOD mod1, MOD mod2); void identifierToDocBuffer(Identifier* ident, OutBuffer *buf, HdrGenState *hgs); #endif /* DMD_MTYPE_H */ diff --git a/gcc/d/dfrontend/nogc.c b/gcc/d/dfrontend/nogc.c new file mode 100644 index 000000000..b80f6ecf4 --- /dev/null +++ b/gcc/d/dfrontend/nogc.c @@ -0,0 +1,228 @@ + +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/nogc.c + */ + +#include "mars.h" +#include "init.h" +#include "visitor.h" +#include "expression.h" +#include "statement.h" +#include "declaration.h" +#include "id.h" +#include "module.h" +#include "scope.h" + +bool walkPostorder(Expression *e, StoppableVisitor *v); + +void FuncDeclaration::printGCUsage(Loc loc, const char* warn) +{ + if (!global.params.vgc) + return; + + Module *m = getModule(); + if (m && m->isRoot() && !inUnittest()) + { + fprintf(global.stdmsg, "%s: vgc: %s\n", loc.toChars(), warn); + } +} + +/************************************** + * Look for GC-allocations + */ +class NOGCVisitor : public StoppableVisitor +{ +public: + FuncDeclaration *f; + bool err; + + NOGCVisitor(FuncDeclaration *f) + { + this->f = f; + this->err = false; + } + + void doCond(Expression *exp) + { + if (exp) + walkPostorder(exp, this); + } + + void visit(Expression *e) + { + } + + void visit(DeclarationExp *e) + { + // Note that, walkPostorder does not support DeclarationExp today. + VarDeclaration *v = e->declaration->isVarDeclaration(); + if (v && !(v->storage_class & STCmanifest) && !v->isDataseg() && v->init) + { + if (v->init->isVoidInitializer()) + { + } + else + { + ExpInitializer *ei = v->init->isExpInitializer(); + assert(ei); + doCond(ei->exp); + } + } + } + + void visit(CallExp *e) + { + } + + void visit(ArrayLiteralExp *e) + { + if (e->type->ty != Tarray || !e->elements || !e->elements->dim) + return; + + if (f->setGC()) + { + e->error("array literal in @nogc function %s may cause GC allocation", + f->toChars()); + err = true; + return; + } + f->printGCUsage(e->loc, "array literal may cause GC allocation"); + } + + void visit(AssocArrayLiteralExp *e) + { + if (!e->keys->dim) + return; + + if (f->setGC()) + { + e->error("associative array literal in @nogc function %s may cause GC allocation", f->toChars()); + err = true; + return; + } + f->printGCUsage(e->loc, "associative array literal may cause GC allocation"); + } + + void visit(NewExp *e) + { + bool needGC = false; + if (e->member && !e->member->isNogc() && f->setGC()) + { + // @nogc-ness is already checked in NewExp::semantic + return; + } + if (e->onstack) + return; + + if (e->allocator) + { + if (!e->allocator->isNogc() && f->setGC()) + { + e->error("operator new in @nogc function %s may allocate", f->toChars()); + err = true; + return; + } + return; + } + + if (f->setGC()) + { + e->error("cannot use 'new' in @nogc function %s", f->toChars()); + err = true; + return; + } + f->printGCUsage(e->loc, "'new' causes GC allocation"); + } + + void visit(DeleteExp *e) + { + if (e->e1->op == TOKvar) + { + VarDeclaration *v = ((VarExp *)e->e1)->var->isVarDeclaration(); + if (v && v->onstack) + return; // delete for scope allocated class object + } + + if (f->setGC()) + { + e->error("cannot use 'delete' in @nogc function %s", f->toChars()); + err = true; + return; + } + f->printGCUsage(e->loc, "'delete' requires GC"); + } + + void visit(IndexExp* e) + { + Type *t1b = e->e1->type->toBasetype(); + if (t1b->ty == Taarray) + { + if (f->setGC()) + { + e->error("indexing an associative array in @nogc function %s may cause GC allocation", f->toChars()); + err = true; + return; + } + f->printGCUsage(e->loc, "indexing an associative array may cause GC allocation"); + } + } + + void visit(AssignExp *e) + { + if (e->e1->op == TOKarraylength) + { + if (f->setGC()) + { + e->error("setting 'length' in @nogc function %s may cause GC allocation", f->toChars()); + err = true; + return; + } + f->printGCUsage(e->loc, "setting 'length' may cause GC allocation"); + } + } + + void visit(CatAssignExp *e) + { + if (f->setGC()) + { + e->error("cannot use operator ~= in @nogc function %s", f->toChars()); + err = true; + return; + } + f->printGCUsage(e->loc, "operator ~= may cause GC allocation"); + } + + void visit(CatExp *e) + { + if (f->setGC()) + { + e->error("cannot use operator ~ in @nogc function %s", f->toChars()); + err = true; + return; + } + f->printGCUsage(e->loc, "operator ~ may cause GC allocation"); + } +}; + +Expression *checkGC(Scope *sc, Expression *e) +{ + FuncDeclaration *f = sc->func; + if (e && e->op != TOKerror && + f && sc->intypeof != 1 && !(sc->flags & SCOPEctfe) && + (f->type->ty == Tfunction && ((TypeFunction *)f->type)->isnogc || + (f->flags & FUNCFLAGnogcInprocess) || + global.params.vgc)) + { + NOGCVisitor gcv(f); + walkPostorder(e, &gcv); + if (gcv.err) + return new ErrorExp(); + } + return e; +} diff --git a/gcc/d/dfrontend/nspace.c b/gcc/d/dfrontend/nspace.c new file mode 100644 index 000000000..5b5e33408 --- /dev/null +++ b/gcc/d/dfrontend/nspace.c @@ -0,0 +1,236 @@ + +// Compiler implementation of the D programming language +// Copyright: Copyright (c) 2014 by Digital Mars, All Rights Reserved +// Authors: Walter Bright, http://www.digitalmars.com +// License: http://boost.org/LICENSE_1_0.txt +// Source: https://github.com/D-Programming-Language/dmd/blob/master/src/nspace.c + + +#include +#include +#include + +#include "mars.h" +#include "dsymbol.h" +#include "nspace.h" +#include "identifier.h" +#include "hdrgen.h" +#include "scope.h" + +/* This implements namespaces. + */ + +Nspace::Nspace(Loc loc, Identifier *ident, Dsymbols *members) + : ScopeDsymbol(ident) +{ + //printf("Nspace::Nspace(ident = %s)\n", ident->toChars()); + this->loc = loc; + this->members = members; +} + +Dsymbol *Nspace::syntaxCopy(Dsymbol *s) +{ + Nspace *ns = new Nspace(loc, ident, NULL); + ScopeDsymbol::syntaxCopy(ns); + return ns; +} + +void Nspace::semantic(Scope *sc) +{ + if (semanticRun >= PASSsemantic) + return; + semanticRun = PASSsemantic; +#if LOG + printf("+Nspace::semantic('%s')\n", toChars()); +#endif + if (scope) + { + sc = scope; + scope = NULL; + } + parent = sc->parent; + if (members) + { + if (!symtab) + symtab = new DsymbolTable(); + + // The namespace becomes 'imported' into the enclosing scope + for (Scope *sce = sc; 1; sce = sce->enclosing) + { + ScopeDsymbol *sds = (ScopeDsymbol *)sce->scopesym; + if (sds) + { + sds->importScope(this, PROTpublic); + break; + } + } + + assert(sc); + sc = sc->push(this); + sc->linkage = LINKcpp; // note that namespaces imply C++ linkage + sc->parent = this; + + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + //printf("add %s to scope %s\n", s->toChars(), toChars()); + s->addMember(sc, this, 1); + } + + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + s->setScope(sc); + } + + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + s->importAll(sc); + } + + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; +#if LOG + printf("\tmember '%s', kind = '%s'\n", s->toChars(), s->kind()); +#endif + s->semantic(sc); + } + sc->pop(); + } +#if LOG + printf("-Nspace::semantic('%s')\n", toChars()); +#endif +} + +void Nspace::semantic2(Scope *sc) +{ + if (semanticRun >= PASSsemantic2) + return; + semanticRun = PASSsemantic2; +#if LOG + printf("+Nspace::semantic2('%s')\n", toChars()); +#endif + if (members) + { + assert(sc); + sc = sc->push(this); + sc->linkage = LINKcpp; + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; +#if LOG + printf("\tmember '%s', kind = '%s'\n", s->toChars(), s->kind()); +#endif + s->semantic2(sc); + } + sc->pop(); + } +#if LOG + printf("-Nspace::semantic2('%s')\n", toChars()); +#endif +} + +void Nspace::semantic3(Scope *sc) +{ + if (semanticRun >= PASSsemantic3) + return; + semanticRun = PASSsemantic3; +#if LOG + printf("Nspace::semantic3('%s')\n", toChars()); +#endif + if (members) + { + sc = sc->push(this); + sc->linkage = LINKcpp; + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + s->semantic3(sc); + } + sc->pop(); + } +} + +const char *Nspace::kind() +{ + return "namespace"; +} + +bool Nspace::oneMember(Dsymbol **ps, Identifier *ident) +{ + return Dsymbol::oneMember(ps, ident); +} + +int Nspace::apply(Dsymbol_apply_ft_t fp, void *param) +{ + if (members) + { + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + if (s) + { + if (s->apply(fp, param)) + return 1; + } + } + } + return 0; +} + +bool Nspace::hasPointers() +{ + //printf("Nspace::hasPointers() %s\n", toChars()); + + if (members) + { + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + //printf(" s = %s %s\n", s->kind(), s->toChars()); + if (s->hasPointers()) + { + return true; + } + } + } + return false; +} + +void Nspace::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion) +{ + //printf("Nspace::setFieldOffset() %s\n", toChars()); + if (scope) // if fwd reference + semantic(NULL); // try to resolve it + if (members) + { + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + //printf("\t%s\n", s->toChars()); + s->setFieldOffset(ad, poffset, isunion); + } + } +} + + +void Nspace::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("extern (C++, "); + buf->writestring(ident->string); + buf->writeByte(')'); + buf->writenl(); + buf->writeByte('{'); + buf->writenl(); + buf->level++; + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + s->toCBuffer(buf, hgs); + } + buf->level--; + buf->writeByte('}'); + buf->writenl(); +} diff --git a/gcc/d/dfrontend/nspace.h b/gcc/d/dfrontend/nspace.h new file mode 100644 index 000000000..7e5cdccda --- /dev/null +++ b/gcc/d/dfrontend/nspace.h @@ -0,0 +1,39 @@ + +// Compiler implementation of the D programming language +// Copyright: Copyright (c) 2014 by Digital Mars, All Rights Reserved +// Authors: Walter Bright, http://www.digitalmars.com +// License: http://boost.org/LICENSE_1_0.txt +// Source: https://github.com/D-Programming-Language/dmd/blob/master/src/nspace.h + + +#ifndef DMD_NSPACE_H +#define DMD_NSPACE_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + +/* A namespace corresponding to a C++ namespace. + * Implies extern(C++). + */ + +class Nspace : public ScopeDsymbol +{ + public: + Nspace(Loc loc, Identifier *ident, Dsymbols *members); + + Dsymbol *syntaxCopy(Dsymbol *s); + void semantic(Scope *sc); + void semantic2(Scope *sc); + void semantic3(Scope *sc); + bool oneMember(Dsymbol **ps, Identifier *ident); + int apply(Dsymbol_apply_ft_t fp, void *param); + bool hasPointers(); + void setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, bool isunion); + const char *kind(); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void toObjFile(bool multiobj); + Nspace *isNspace() { return this; } +}; + +#endif /* DMD_NSPACE_H */ diff --git a/gcc/d/dfrontend/object.c b/gcc/d/dfrontend/object.c index e0733a99f..84455c9c0 100644 --- a/gcc/d/dfrontend/object.c +++ b/gcc/d/dfrontend/object.c @@ -1,11 +1,10 @@ - -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/object.c + */ #include @@ -21,7 +20,13 @@ bool RootObject::equals(RootObject *o) int RootObject::compare(RootObject *obj) { - return this - obj; + size_t lhs = (size_t)this; + size_t rhs = (size_t)obj; + if (lhs < rhs) + return -1; + else if (lhs > rhs) + return 1; + return 0; } void RootObject::print() diff --git a/gcc/d/dfrontend/object.h b/gcc/d/dfrontend/object.h index 6495f23bf..24742231c 100644 --- a/gcc/d/dfrontend/object.h +++ b/gcc/d/dfrontend/object.h @@ -1,11 +1,11 @@ -// Copyright (c) 1999-2011 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/object.h + */ #ifndef OBJECT_H #define OBJECT_H diff --git a/gcc/d/dfrontend/opover.c b/gcc/d/dfrontend/opover.c index 89f7a6270..2e2c55a92 100644 --- a/gcc/d/dfrontend/opover.c +++ b/gcc/d/dfrontend/opover.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/opover.c + */ #include #include @@ -16,7 +17,6 @@ #include "rmem.h" -//#include "port.h" #include "mtype.h" #include "init.h" #include "expression.h" @@ -30,6 +30,7 @@ static Dsymbol *inferApplyArgTypesX(Expression *ethis, FuncDeclaration *fstart, Parameters *arguments); static void inferApplyArgTypesZ(TemplateDeclaration *tstart, Parameters *arguments); static int inferApplyArgTypesY(TypeFunction *tf, Parameters *arguments, int flags = 0); +Expression *compare_overload(BinExp *e, Scope *sc, Identifier *id); /******************************** Expression **************************/ @@ -39,19 +40,78 @@ static int inferApplyArgTypesY(TypeFunction *tf, Parameters *arguments, int flag * to fit operator overload. */ -int Expression::isCommutative() +bool isCommutative(Expression *e) { - return false; // default is no reverse + class CommutativeVisitor : public Visitor + { + public: + bool result; + void visit(Expression *e) { result = false; } + void visit(AddExp *e) { result = true; } + void visit(MulExp *e) { result = true; } + void visit(AndExp *e) { result = true; } + void visit(OrExp *e) { result = true; } + void visit(XorExp *e) { result = true; } + void visit(EqualExp *e) { result = true; } + void visit(CmpExp *e) { result = true; } + }; + CommutativeVisitor v; + e->accept(&v); + return v.result; } /*********************************** * Get Identifier for operator overload. */ -Identifier *Expression::opId() +static Identifier *opId(Expression *e) { - assert(0); - return NULL; + class OpIdVisitor : public Visitor + { + public: + Identifier *id; + void visit(Expression *e) { assert(0); } + void visit(UAddExp *e) { id = Id::uadd; } + void visit(NegExp *e) { id = Id::neg; } + void visit(ComExp *e) { id = Id::com; } + void visit(CastExp *e) { id = Id::cast; } + void visit(InExp *e) { id = Id::opIn; } + void visit(PostExp *e) { id = (e->op == TOKplusplus) ? Id::postinc : Id::postdec; } + void visit(AddExp *e) { id = Id::add; } + void visit(MinExp *e) { id = Id::sub; } + void visit(MulExp *e) { id = Id::mul; } + void visit(DivExp *e) { id = Id::div; } + void visit(ModExp *e) { id = Id::mod; } + void visit(PowExp *e) { id = Id::pow; } + void visit(ShlExp *e) { id = Id::shl; } + void visit(ShrExp *e) { id = Id::shr; } + void visit(UshrExp *e) { id = Id::ushr; } + void visit(AndExp *e) { id = Id::iand; } + void visit(OrExp *e) { id = Id::ior; } + void visit(XorExp *e) { id = Id::ixor; } + void visit(CatExp *e) { id = Id::cat; } + void visit(AssignExp *e) { id = Id::assign; } + void visit(AddAssignExp *e) { id = Id::addass; } + void visit(MinAssignExp *e) { id = Id::subass; } + void visit(MulAssignExp *e) { id = Id::mulass; } + void visit(DivAssignExp *e) { id = Id::divass; } + void visit(ModAssignExp *e) { id = Id::modass; } + void visit(AndAssignExp *e) { id = Id::andass; } + void visit(OrAssignExp *e) { id = Id::orass; } + void visit(XorAssignExp *e) { id = Id::xorass; } + void visit(ShlAssignExp *e) { id = Id::shlass; } + void visit(ShrAssignExp *e) { id = Id::shrass; } + void visit(UshrAssignExp *e) { id = Id::ushrass; } + void visit(CatAssignExp *e) { id = Id::catass; } + void visit(PowAssignExp *e) { id = Id::powass; } + void visit(EqualExp *e) { id = Id::eq; } + void visit(CmpExp *e) { id = Id::cmp; } + void visit(ArrayExp *e) { id = Id::index; } + void visit(PtrExp *e) { id = Id::opStar; } + }; + OpIdVisitor v; + e->accept(&v); + return v.id; } /*********************************** @@ -59,96 +119,33 @@ Identifier *Expression::opId() * NULL if not supported for this operator. */ -Identifier *Expression::opId_r() +static Identifier *opId_r(Expression *e) { - return NULL; + class OpIdRVisitor : public Visitor + { + public: + Identifier *id; + void visit(Expression *e) { id = NULL; } + void visit(InExp *e) { id = Id::opIn_r; } + void visit(AddExp *e) { id = Id::add_r; } + void visit(MinExp *e) { id = Id::sub_r; } + void visit(MulExp *e) { id = Id::mul_r; } + void visit(DivExp *e) { id = Id::div_r; } + void visit(ModExp *e) { id = Id::mod_r; } + void visit(PowExp *e) { id = Id::pow_r; } + void visit(ShlExp *e) { id = Id::shl_r; } + void visit(ShrExp *e) { id = Id::shr_r; } + void visit(UshrExp *e) { id = Id::ushr_r; } + void visit(AndExp *e) { id = Id::iand_r; } + void visit(OrExp *e) { id = Id::ior_r; } + void visit(XorExp *e) { id = Id::ixor_r; } + void visit(CatExp *e) { id = Id::cat_r; } + }; + OpIdRVisitor v; + e->accept(&v); + return v.id; } -/************************* Operators *****************************/ - -Identifier *UAddExp::opId() { return Id::uadd; } - -Identifier *NegExp::opId() { return Id::neg; } - -Identifier *ComExp::opId() { return Id::com; } - -Identifier *CastExp::opId() { return Id::cast; } - -Identifier *InExp::opId() { return Id::opIn; } -Identifier *InExp::opId_r() { return Id::opIn_r; } - -Identifier *PostExp::opId() { return (op == TOKplusplus) - ? Id::postinc - : Id::postdec; } - -int AddExp::isCommutative() { return true; } -Identifier *AddExp::opId() { return Id::add; } -Identifier *AddExp::opId_r() { return Id::add_r; } - -Identifier *MinExp::opId() { return Id::sub; } -Identifier *MinExp::opId_r() { return Id::sub_r; } - -int MulExp::isCommutative() { return true; } -Identifier *MulExp::opId() { return Id::mul; } -Identifier *MulExp::opId_r() { return Id::mul_r; } - -Identifier *DivExp::opId() { return Id::div; } -Identifier *DivExp::opId_r() { return Id::div_r; } - -Identifier *ModExp::opId() { return Id::mod; } -Identifier *ModExp::opId_r() { return Id::mod_r; } - -Identifier *PowExp::opId() { return Id::pow; } -Identifier *PowExp::opId_r() { return Id::pow_r; } - -Identifier *ShlExp::opId() { return Id::shl; } -Identifier *ShlExp::opId_r() { return Id::shl_r; } - -Identifier *ShrExp::opId() { return Id::shr; } -Identifier *ShrExp::opId_r() { return Id::shr_r; } - -Identifier *UshrExp::opId() { return Id::ushr; } -Identifier *UshrExp::opId_r() { return Id::ushr_r; } - -int AndExp::isCommutative() { return true; } -Identifier *AndExp::opId() { return Id::iand; } -Identifier *AndExp::opId_r() { return Id::iand_r; } - -int OrExp::isCommutative() { return true; } -Identifier *OrExp::opId() { return Id::ior; } -Identifier *OrExp::opId_r() { return Id::ior_r; } - -int XorExp::isCommutative() { return true; } -Identifier *XorExp::opId() { return Id::ixor; } -Identifier *XorExp::opId_r() { return Id::ixor_r; } - -Identifier *CatExp::opId() { return Id::cat; } -Identifier *CatExp::opId_r() { return Id::cat_r; } - -Identifier * AssignExp::opId() { return Id::assign; } -Identifier * AddAssignExp::opId() { return Id::addass; } -Identifier * MinAssignExp::opId() { return Id::subass; } -Identifier * MulAssignExp::opId() { return Id::mulass; } -Identifier * DivAssignExp::opId() { return Id::divass; } -Identifier * ModAssignExp::opId() { return Id::modass; } -Identifier * AndAssignExp::opId() { return Id::andass; } -Identifier * OrAssignExp::opId() { return Id::orass; } -Identifier * XorAssignExp::opId() { return Id::xorass; } -Identifier * ShlAssignExp::opId() { return Id::shlass; } -Identifier * ShrAssignExp::opId() { return Id::shrass; } -Identifier *UshrAssignExp::opId() { return Id::ushrass; } -Identifier * CatAssignExp::opId() { return Id::catass; } -Identifier * PowAssignExp::opId() { return Id::powass; } - -int EqualExp::isCommutative() { return true; } -Identifier *EqualExp::opId() { return Id::eq; } - -int CmpExp::isCommutative() { return true; } -Identifier *CmpExp::opId() { return Id::cmp; } - -Identifier *ArrayExp::opId() { return Id::index; } -Identifier *PtrExp::opId() { return Id::opStar; } - /************************************ * If type is a class or struct, return the symbol for it, * else NULL @@ -204,527 +201,1035 @@ Objects *opToArg(Scope *sc, TOK op) * Return NULL if not an operator overload. */ -Expression *UnaExp::op_overload(Scope *sc) +Expression *op_overload(Expression *e, Scope *sc) { - //printf("UnaExp::op_overload() (%s)\n", toChars()); - - if (e1->op == TOKarray) + class OpOverload : public Visitor { - ArrayExp *ae = (ArrayExp *)e1; - ae->e1 = ae->e1->semantic(sc); - ae->e1 = resolveProperties(sc, ae->e1); + public: + Scope *sc; + Expression *result; - AggregateDeclaration *ad = isAggregate(ae->e1->type); - if (ad) + OpOverload(Scope *sc) + : sc(sc) { - /* Rewrite as: - * a.opIndexUnary!("+")(args); - */ - Dsymbol *fd = search_function(ad, Id::opIndexUnary); - if (fd) - { - Expression *e0 = resolveOpDollar(sc, ae); - Objects *tiargs = opToArg(sc, op); - Expression *e = new DotTemplateInstanceExp(loc, ae->e1, fd->ident, tiargs); - e = new CallExp(loc, e, ae->arguments); - e = combine(e0, e); - e = e->semantic(sc); - return e; - } + result = NULL; + } - // Didn't find it. Forward to aliasthis - if (ad->aliasthis && ae->e1->type != att1) - { - /* Rewrite op(a[arguments]) as: - * op(a.aliasthis[arguments]) - */ - Expression *e1 = ae->copy(); - ((ArrayExp *)e1)->e1 = new DotIdExp(loc, ae->e1, ad->aliasthis->ident); - UnaExp *ue = (UnaExp *)copy(); - if (!ue->att1 && ae->e1->type->checkAliasThisRec()) - ue->att1 = ae->e1->type; - ue->e1 = e1; - if (Expression *e = ue->trySemantic(sc)) - return e; - } - att1 = NULL; + void visit(Expression *e) + { + assert(0); } - } - else if (e1->op == TOKslice) - { - SliceExp *se = (SliceExp *)e1; - se->e1 = se->e1->semantic(sc); - se->e1 = resolveProperties(sc, se->e1); - AggregateDeclaration *ad = isAggregate(se->e1->type); - if (ad) + void visit(UnaExp *e) { - /* Rewrite as: - * a.opSliceUnary!("+")(lwr, upr); - */ - Dsymbol *fd = search_function(ad, Id::opSliceUnary); - if (fd) + //printf("UnaExp::op_overload() (%s)\n", e->toChars()); + + if (e->e1->op == TOKarray) { - Expression *e0 = resolveOpDollar(sc, se); - Expressions *a = new Expressions(); - assert(!se->lwr || se->upr); - if (se->lwr) + ArrayExp *ae = (ArrayExp *)e->e1; + ae->e1 = ae->e1->semantic(sc); + ae->e1 = resolveProperties(sc, ae->e1); + + AggregateDeclaration *ad = isAggregate(ae->e1->type); + if (ad) { - a->push(se->lwr); - a->push(se->upr); + Expression *e0 = NULL; + + /* Rewrite as: + * a.opIndexUnary!("+")(args); + */ + Dsymbol *fd = search_function(ad, Id::opIndexUnary); + if (fd) + { + // Deal with $ + Expression *ex = resolveOpDollar(sc, ae, &e0); + if (!ex) + goto Lfallback; + if (ex->op == TOKerror) + { + result = ex; + return; + } + Objects *tiargs = opToArg(sc, e->op); + result = new DotTemplateInstanceExp(e->loc, ae->e1, fd->ident, tiargs); + result = new CallExp(e->loc, result, ae->arguments); + if (ae->arguments->dim == 0) + result = result->trySemantic(sc); + else + result = result->semantic(sc); + if (!result) + goto Lfallback; + result = Expression::combine(e0, result); + return; + } + + // Didn't find it. Forward to aliasthis + if (ad->aliasthis && ae->e1->type != e->att1) + { + /* Rewrite op(a[arguments]) as: + * op(a.aliasthis[arguments]) + */ + Expression *e1 = ae->copy(); + ((ArrayExp *)e1)->e1 = new DotIdExp(e->loc, ae->e1, ad->aliasthis->ident); + UnaExp *ue = (UnaExp *)e->copy(); + if (!ue->att1 && ae->e1->type->checkAliasThisRec()) + ue->att1 = ae->e1->type; + ue->e1 = e1; + result = ue->trySemantic(sc); + if (result) + return; + } + e->att1 = NULL; + + Lfallback: + if (ae->arguments->dim == 0) + { + // op(a[]) + SliceExp *se = new SliceExp(ae->loc, ae->e1, NULL, NULL); + se->att1 = ae->att1; + e->e1 = se; + e->accept(this); + result = Expression::combine(e0, result); + return; + } + if (ae->arguments->dim == 1 && (*ae->arguments)[0]->op == TOKinterval) + { + // op(a[lwr..upr]) + IntervalExp *ie = (IntervalExp *)(*ae->arguments)[0]; + SliceExp *se = new SliceExp(ae->loc, ae->e1, ie->lwr, ie->upr); + se->att1 = ae->att1; + e->e1 = se; + e->accept(this); + result = Expression::combine(e0, result); + return; + } } - Objects *tiargs = opToArg(sc, op); - Expression *e = new DotTemplateInstanceExp(loc, se->e1, fd->ident, tiargs); - e = new CallExp(loc, e, a); - e = combine(e0, e); - e = e->semantic(sc); - return e; } + else if (e->e1->op == TOKslice) + { + SliceExp *se = (SliceExp *)e->e1; + se->e1 = se->e1->semantic(sc); + se->e1 = resolveProperties(sc, se->e1); - // Didn't find it. Forward to aliasthis - if (ad->aliasthis && se->e1->type != att1) + AggregateDeclaration *ad = isAggregate(se->e1->type); + if (ad) + { + /* Rewrite as: + * a.opSliceUnary!("+")(lwr, upr); + */ + Dsymbol *fd = search_function(ad, Id::opSliceUnary); + if (fd) + { + // Deal with $ + Expression *e0 = NULL; + Expression *ex = resolveOpDollar(sc, se, &e0); + if (ex->op == TOKerror) + { + result = ex; + return; + } + Expressions *a = new Expressions(); + assert(!se->lwr || se->upr); + if (se->lwr) + { + a->push(se->lwr); + a->push(se->upr); + } + Objects *tiargs = opToArg(sc, e->op); + result = new DotTemplateInstanceExp(e->loc, se->e1, fd->ident, tiargs); + result = new CallExp(e->loc, result, a); + result = result->semantic(sc); + result = Expression::combine(e0, result); + return; + } + + // Didn't find it. Forward to aliasthis + if (ad->aliasthis && se->e1->type != e->att1) + { + /* Rewrite op(a[lwr..upr]) as: + * op(a.aliasthis[lwr..upr]) + */ + Expression *e1 = se->copy(); + ((SliceExp *)e1)->e1 = new DotIdExp(e->loc, se->e1, ad->aliasthis->ident); + UnaExp *ue = (UnaExp *)e->copy(); + if (!ue->att1 && se->e1->type->checkAliasThisRec()) + ue->att1 = se->e1->type; + ue->e1 = e1; + result = ue->trySemantic(sc); + if (result) + return; + } + e->att1 = NULL; + } + } + + e->e1 = e->e1->semantic(sc); + e->e1 = resolveProperties(sc, e->e1); + + AggregateDeclaration *ad = isAggregate(e->e1->type); + if (ad) { - /* Rewrite op(a[lwr..upr]) as: - * op(a.aliasthis[lwr..upr]) + Dsymbol *fd = NULL; + #if 1 // Old way, kept for compatibility with D1 + if (e->op != TOKpreplusplus && e->op != TOKpreminusminus) + { + fd = search_function(ad, opId(e)); + if (fd) + { + if (e->op == TOKarray) + { + /* Rewrite op e1[arguments] as: + * e1.fd(arguments) + */ + result = new DotIdExp(e->loc, e->e1, fd->ident); + ArrayExp *ae = (ArrayExp *)e; + result = new CallExp(e->loc, result, ae->arguments); + result = result->semantic(sc); + return; + } + else + { + // Rewrite +e1 as e1.add() + result = build_overload(e->loc, sc, e->e1, NULL, fd); + return; + } + } + } + #endif + + /* Rewrite as: + * e1.opUnary!("+")(); */ - Expression *e1 = se->copy(); - ((SliceExp *)e1)->e1 = new DotIdExp(loc, se->e1, ad->aliasthis->ident); - UnaExp *ue = (UnaExp *)copy(); - if (!ue->att1 && se->e1->type->checkAliasThisRec()) - ue->att1 = se->e1->type; - ue->e1 = e1; - if (Expression *e = ue->trySemantic(sc)) - return e; + fd = search_function(ad, Id::opUnary); + if (fd) + { + Objects *tiargs = opToArg(sc, e->op); + result = new DotTemplateInstanceExp(e->loc, e->e1, fd->ident, tiargs); + result = new CallExp(e->loc, result); + result = result->semantic(sc); + return; + } + + // Didn't find it. Forward to aliasthis + if (ad->aliasthis && e->e1->type != e->att1) + { + /* Rewrite op(e1) as: + * op(e1.aliasthis) + */ + //printf("att una %s e1 = %s\n", Token::toChars(op), this->e1->type->toChars()); + Expression *e1 = new DotIdExp(e->loc, e->e1, ad->aliasthis->ident); + UnaExp *ue = (UnaExp *)e->copy(); + if (!ue->att1 && e->e1->type->checkAliasThisRec()) + ue->att1 = e->e1->type; + ue->e1 = e1; + result = ue->trySemantic(sc); + return; + } } - att1 = NULL; } - } - - e1 = e1->semantic(sc); - e1 = resolveProperties(sc, e1); - AggregateDeclaration *ad = isAggregate(e1->type); - if (ad) - { - Dsymbol *fd = NULL; -#if 1 // Old way, kept for compatibility with D1 - if (op != TOKpreplusplus && op != TOKpreminusminus) - { fd = search_function(ad, opId()); - if (fd) + void visit(ArrayExp *ae) + { + //printf("ArrayExp::op_overload() (%s)\n", ae->toChars()); + AggregateDeclaration *ad = isAggregate(ae->e1->type); + if (ad) { - if (op == TOKarray) + Expression *e0 = NULL; + + Dsymbol *fd = search_function(ad, opId(ae)); + if (fd) { + // Deal with $ + Expression *ex = resolveOpDollar(sc, ae, &e0); + if (!ex) + goto Lfallback; + if (ex->op == TOKerror) + { + result = ex; + return; + } + /* Rewrite op e1[arguments] as: - * e1.fd(arguments) + * e1.opIndex(arguments) */ - Expression *e = new DotIdExp(loc, e1, fd->ident); - ArrayExp *ae = (ArrayExp *)this; - e = new CallExp(loc, e, ae->arguments); - e = e->semantic(sc); - return e; + result = new DotIdExp(ae->loc, ae->e1, fd->ident); + result = new CallExp(ae->loc, result, ae->arguments); + if (ae->arguments->dim == 0) + result = result->trySemantic(sc); + else + result = result->semantic(sc); + if (!result) + goto Lfallback; + result = Expression::combine(e0, result); + return; } - else + + if (ae->e1->op == TOKtype && ae->arguments->dim < 2) + goto Lfallback; + + // Didn't find it. Forward to aliasthis + if (ad->aliasthis && ae->e1->type != ae->att1) { - // Rewrite +e1 as e1.add() - return build_overload(loc, sc, e1, NULL, fd); + /* Rewrite op(e1) as: + * op(e1.aliasthis) + */ + //printf("att arr e1 = %s\n", this->e1->type->toChars()); + Expression *e1 = new DotIdExp(ae->loc, ae->e1, ad->aliasthis->ident); + UnaExp *ue = (UnaExp *)ae->copy(); + if (!ue->att1 && ae->e1->type->checkAliasThisRec()) + ue->att1 = ae->e1->type; + ue->e1 = e1; + result = ue->trySemantic(sc); + if (result) + return; + } + + Lfallback: + if (ae->arguments->dim == 0) + { + // a[] + SliceExp *se = new SliceExp(ae->loc, ae->e1, NULL, NULL); + result = se->semantic(sc); + result = Expression::combine(e0, result); + return; + } + if (ae->arguments->dim == 1 && (*ae->arguments)[0]->op == TOKinterval) + { + // a[lwr..upr] + IntervalExp *ie = (IntervalExp *)(*ae->arguments)[0]; + SliceExp *se = new SliceExp(ae->loc, ae->e1, ie->lwr, ie->upr); + result = se->semantic(sc); + result = Expression::combine(e0, result); + return; } } } -#endif - /* Rewrite as: - * e1.opUnary!("+")(); + /*********************************************** + * This is mostly the same as UnaryExp::op_overload(), but has + * a different rewrite. */ - fd = search_function(ad, Id::opUnary); - if (fd) + void visit(CastExp *e) { - Objects *tiargs = opToArg(sc, op); - Expression *e = new DotTemplateInstanceExp(loc, e1, fd->ident, tiargs); - e = new CallExp(loc, e); - e = e->semantic(sc); - return e; - } + //printf("CastExp::op_overload() (%s)\n", e->toChars()); + AggregateDeclaration *ad = isAggregate(e->e1->type); + if (ad) + { + Dsymbol *fd = NULL; + /* Rewrite as: + * e1.opCast!(T)(); + */ + fd = search_function(ad, Id::cast); + if (fd) + { + #if 1 // Backwards compatibility with D1 if opCast is a function, not a template + if (fd->isFuncDeclaration()) + { + // Rewrite as: e1.opCast() + result = build_overload(e->loc, sc, e->e1, NULL, fd); + return; + } + #endif + Objects *tiargs = new Objects(); + tiargs->push(e->to); + result = new DotTemplateInstanceExp(e->loc, e->e1, fd->ident, tiargs); + result = new CallExp(e->loc, result); + result = result->semantic(sc); + return; + } - // Didn't find it. Forward to aliasthis - if (ad->aliasthis && this->e1->type != att1) - { - /* Rewrite op(e1) as: - * op(e1.aliasthis) - */ - //printf("att una %s e1 = %s\n", Token::toChars(op), this->e1->type->toChars()); - Expression *e1 = new DotIdExp(loc, this->e1, ad->aliasthis->ident); - UnaExp *ue = (UnaExp *)copy(); - if (!ue->att1 && this->e1->type->checkAliasThisRec()) - ue->att1 = this->e1->type; - ue->e1 = e1; - return ue->trySemantic(sc); + // Didn't find it. Forward to aliasthis + if (ad->aliasthis) + { + /* Rewrite op(e1) as: + * op(e1.aliasthis) + */ + Expression *e1 = new DotIdExp(e->loc, e->e1, ad->aliasthis->ident); + result = e->copy(); + ((UnaExp *)result)->e1 = e1; + result = result->trySemantic(sc); + return; + } + } } - } - return NULL; -} -Expression *ArrayExp::op_overload(Scope *sc) -{ - //printf("ArrayExp::op_overload() (%s)\n", toChars()); - AggregateDeclaration *ad = isAggregate(e1->type); - if (ad) - { - Dsymbol *fd = search_function(ad, opId()); - if (fd) + void visit(BinExp *e) { - /* Rewrite op e1[arguments] as: - * e1.opIndex(arguments) - */ - Expression *e0 = resolveOpDollar(sc, this); - Expression *e = new DotIdExp(loc, e1, fd->ident); - e = new CallExp(loc, e, arguments); - e = combine(e0, e); - e = e->semantic(sc); - return e; - } + //printf("BinExp::op_overload() (%s)\n", e->toChars()); - // Didn't find it. Forward to aliasthis - if (ad->aliasthis && this->e1->type != att1) - { - /* Rewrite op(e1) as: - * op(e1.aliasthis) - */ - //printf("att arr e1 = %s\n", this->e1->type->toChars()); - Expression *e1 = new DotIdExp(loc, this->e1, ad->aliasthis->ident); - UnaExp *ue = (UnaExp *)copy(); - if (!ue->att1 && this->e1->type->checkAliasThisRec()) - ue->att1 = this->e1->type; - ue->e1 = e1; - return ue->trySemantic(sc); - } - } - return NULL; -} + Identifier *id = opId(e); + Identifier *id_r = opId_r(e); -/*********************************************** - * This is mostly the same as UnaryExp::op_overload(), but has - * a different rewrite. - */ -Expression *CastExp::op_overload(Scope *sc) -{ - //printf("CastExp::op_overload() (%s)\n", toChars()); - AggregateDeclaration *ad = isAggregate(e1->type); - if (ad) - { - Dsymbol *fd = NULL; - /* Rewrite as: - * e1.opCast!(T)(); - */ - fd = search_function(ad, Id::cast); - if (fd) - { -#if 1 // Backwards compatibility with D1 if opCast is a function, not a template - if (fd->isFuncDeclaration()) - { // Rewrite as: e1.opCast() - return build_overload(loc, sc, e1, NULL, fd); + Expressions args1; + Expressions args2; + int argsset = 0; + + AggregateDeclaration *ad1 = isAggregate(e->e1->type); + AggregateDeclaration *ad2 = isAggregate(e->e2->type); + + if (e->op == TOKassign && ad1 == ad2) + { + StructDeclaration *sd = ad1->isStructDeclaration(); + if (sd && !sd->hasIdentityAssign) + { + /* This is bitwise struct assignment. */ + return; + } } -#endif - Objects *tiargs = new Objects(); - tiargs->push(to); - Expression *e = new DotTemplateInstanceExp(loc, e1, fd->ident, tiargs); - e = new CallExp(loc, e); - e = e->semantic(sc); - return e; - } - // Didn't find it. Forward to aliasthis - if (ad->aliasthis) - { - /* Rewrite op(e1) as: - * op(e1.aliasthis) - */ - Expression *e1 = new DotIdExp(loc, this->e1, ad->aliasthis->ident); - Expression *e = copy(); - ((UnaExp *)e)->e1 = e1; - e = e->trySemantic(sc); - return e; - } - } - return NULL; -} + Dsymbol *s = NULL; + Dsymbol *s_r = NULL; -Expression *BinExp::op_overload(Scope *sc) -{ - //printf("BinExp::op_overload() (%s)\n", toChars()); + #if 1 // the old D1 scheme + if (ad1 && id) + { + s = search_function(ad1, id); + } + if (ad2 && id_r) + { + s_r = search_function(ad2, id_r); - Identifier *id = opId(); - Identifier *id_r = opId_r(); + // Bugzilla 12778: If both x.opBinary(y) and y.opBinaryRight(x) found, + // and they are exactly same symbol, x.opBinary(y) should be preferred. + if (s_r && s_r == s) + s_r = NULL; + } + #endif - Expressions args1; - Expressions args2; - int argsset = 0; + Objects *tiargs = NULL; + if (e->op == TOKplusplus || e->op == TOKminusminus) + { + // Bug4099 fix + if (ad1 && search_function(ad1, Id::opUnary)) + return; + } + if (!s && !s_r && e->op != TOKequal && e->op != TOKnotequal && e->op != TOKassign && + e->op != TOKplusplus && e->op != TOKminusminus) + { + /* Try the new D2 scheme, opBinary and opBinaryRight + */ + if (ad1) + { + s = search_function(ad1, Id::opBinary); + if (s && !s->isTemplateDeclaration()) + { + e->e1->error("%s.opBinary isn't a template", e->e1->toChars()); + result = new ErrorExp(); + return; + } + } + if (ad2) + { + s_r = search_function(ad2, Id::opBinaryRight); + if (s_r && !s_r->isTemplateDeclaration()) + { + e->e2->error("%s.opBinaryRight isn't a template", e->e2->toChars()); + result = new ErrorExp(); + return; + } + if (s_r && s_r == s) // Bugzilla 12778 + s_r = NULL; + } - AggregateDeclaration *ad1 = isAggregate(e1->type); - AggregateDeclaration *ad2 = isAggregate(e2->type); + // Set tiargs, the template argument list, which will be the operator string + if (s || s_r) + { + id = Id::opBinary; + id_r = Id::opBinaryRight; + tiargs = opToArg(sc, e->op); + } + } - if (op == TOKassign && ad1 == ad2) - { - StructDeclaration *sd = ad1->isStructDeclaration(); - if (sd && !sd->hasIdentityAssign) - { /* This is bitwise struct assignment. */ - return NULL; - } - } + if (s || s_r) + { + /* Try: + * a.opfunc(b) + * b.opfunc_r(a) + * and see which is better. + */ - Dsymbol *s = NULL; - Dsymbol *s_r = NULL; + args1.setDim(1); + args1[0] = e->e1; + expandTuples(&args1); + args2.setDim(1); + args2[0] = e->e2; + expandTuples(&args2); + argsset = 1; -#if 1 // the old D1 scheme - if (ad1 && id) - { - s = search_function(ad1, id); - } - if (ad2 && id_r) - { - s_r = search_function(ad2, id_r); - } -#endif + Match m; + memset(&m, 0, sizeof(m)); + m.last = MATCHnomatch; - Objects *tiargs = NULL; - if (op == TOKplusplus || op == TOKminusminus) - { // Bug4099 fix - if (ad1 && search_function(ad1, Id::opUnary)) - return NULL; - } - if (!s && !s_r && op != TOKequal && op != TOKnotequal && op != TOKassign && - op != TOKplusplus && op != TOKminusminus) - { - /* Try the new D2 scheme, opBinary and opBinaryRight - */ - if (ad1) - { - s = search_function(ad1, Id::opBinary); - if (s && !s->isTemplateDeclaration()) - { e1->error("%s.opBinary isn't a template", e1->toChars()); - return new ErrorExp(); + if (s) + { + functionResolve(&m, s, e->loc, sc, tiargs, e->e1->type, &args2); + if (m.lastf && m.lastf->errors) + { + result = new ErrorExp(); + return; + } + } + + FuncDeclaration *lastf = m.lastf; + + if (s_r) + { + functionResolve(&m, s_r, e->loc, sc, tiargs, e->e2->type, &args1); + if (m.lastf && m.lastf->errors) + { + result = new ErrorExp(); + return; + } + } + + if (m.count > 1) + { + // Error, ambiguous + e->error("overloads %s and %s both match argument list for %s", + m.lastf->type->toChars(), + m.nextf->type->toChars(), + m.lastf->toChars()); + } + else if (m.last <= MATCHnomatch) + { + m.lastf = m.anyf; + if (tiargs) + goto L1; + } + + if (e->op == TOKplusplus || e->op == TOKminusminus) + { + // Kludge because operator overloading regards e++ and e-- + // as unary, but it's implemented as a binary. + // Rewrite (e1 ++ e2) as e1.postinc() + // Rewrite (e1 -- e2) as e1.postdec() + result = build_overload(e->loc, sc, e->e1, NULL, m.lastf ? m.lastf : s); + } + else if (lastf && m.lastf == lastf || !s_r && m.last <= MATCHnomatch) + { + // Rewrite (e1 op e2) as e1.opfunc(e2) + result = build_overload(e->loc, sc, e->e1, e->e2, m.lastf ? m.lastf : s); + } + else + { + // Rewrite (e1 op e2) as e2.opfunc_r(e1) + result = build_overload(e->loc, sc, e->e2, e->e1, m.lastf ? m.lastf : s_r); + } + return; } + + L1: + #if 1 // Retained for D1 compatibility + if (isCommutative(e) && !tiargs) + { + s = NULL; + s_r = NULL; + if (ad1 && id_r) + { + s_r = search_function(ad1, id_r); + } + if (ad2 && id) + { + s = search_function(ad2, id); + if (s && s == s_r) // Bugzilla 12778 + s = NULL; + } + + if (s || s_r) + { + /* Try: + * a.opfunc_r(b) + * b.opfunc(a) + * and see which is better. + */ + + if (!argsset) + { + args1.setDim(1); + args1[0] = e->e1; + expandTuples(&args1); + args2.setDim(1); + args2[0] = e->e2; + expandTuples(&args2); + } + + Match m; + memset(&m, 0, sizeof(m)); + m.last = MATCHnomatch; + + if (s_r) + { + functionResolve(&m, s_r, e->loc, sc, tiargs, e->e1->type, &args2); + if (m.lastf && m.lastf->errors) + { + result = new ErrorExp(); + return; + } + } + + FuncDeclaration *lastf = m.lastf; + + if (s) + { + functionResolve(&m, s, e->loc, sc, tiargs, e->e2->type, &args1); + if (m.lastf && m.lastf->errors) + { + result = new ErrorExp(); + return; + } + } + + if (m.count > 1) + { + // Error, ambiguous + e->error("overloads %s and %s both match argument list for %s", + m.lastf->type->toChars(), + m.nextf->type->toChars(), + m.lastf->toChars()); + } + else if (m.last <= MATCHnomatch) + { + m.lastf = m.anyf; + } + + if (lastf && m.lastf == lastf || !s && m.last <= MATCHnomatch) + { + // Rewrite (e1 op e2) as e1.opfunc_r(e2) + result = build_overload(e->loc, sc, e->e1, e->e2, m.lastf ? m.lastf : s_r); + } + else + { + // Rewrite (e1 op e2) as e2.opfunc(e1) + result = build_overload(e->loc, sc, e->e2, e->e1, m.lastf ? m.lastf : s); + } + + // When reversing operands of comparison operators, + // need to reverse the sense of the op + switch (e->op) + { + case TOKlt: e->op = TOKgt; break; + case TOKgt: e->op = TOKlt; break; + case TOKle: e->op = TOKge; break; + case TOKge: e->op = TOKle; break; + + // Floating point compares + case TOKule: e->op = TOKuge; break; + case TOKul: e->op = TOKug; break; + case TOKuge: e->op = TOKule; break; + case TOKug: e->op = TOKul; break; + + // These are symmetric + case TOKunord: + case TOKlg: + case TOKleg: + case TOKue: + break; + } + + return; + } + } + #endif + + // Try alias this on first operand + if (ad1 && ad1->aliasthis && + !(e->op == TOKassign && ad2 && ad1 == ad2)) // See Bugzilla 2943 + { + /* Rewrite (e1 op e2) as: + * (e1.aliasthis op e2) + */ + if (e->att1 && e->e1->type == e->att1) + return; + //printf("att bin e1 = %s\n", this->e1->type->toChars()); + Expression *e1 = new DotIdExp(e->loc, e->e1, ad1->aliasthis->ident); + BinExp *be = (BinExp *)e->copy(); + if (!be->att1 && e->e1->type->checkAliasThisRec()) + be->att1 = e->e1->type; + be->e1 = e1; + result = be->trySemantic(sc); + return; + } + + // Try alias this on second operand + /* Bugzilla 2943: make sure that when we're copying the struct, we don't + * just copy the alias this member + */ + if (ad2 && ad2->aliasthis && + !(e->op == TOKassign && ad1 && ad1 == ad2)) + { + /* Rewrite (e1 op e2) as: + * (e1 op e2.aliasthis) + */ + if (e->att2 && e->e2->type == e->att2) + return; + //printf("att bin e2 = %s\n", e->e2->type->toChars()); + Expression *e2 = new DotIdExp(e->loc, e->e2, ad2->aliasthis->ident); + BinExp *be = (BinExp *)e->copy(); + if (!be->att2 && e->e2->type->checkAliasThisRec()) + be->att2 = e->e2->type; + be->e2 = e2; + result = be->trySemantic(sc); + return; + } + return; } - if (ad2) + + void visit(EqualExp *e) { - s_r = search_function(ad2, Id::opBinaryRight); - if (s_r && !s_r->isTemplateDeclaration()) - { e2->error("%s.opBinaryRight isn't a template", e2->toChars()); - return new ErrorExp(); + //printf("EqualExp::op_overload() (%s)\n", e->toChars()); + + Type *t1 = e->e1->type->toBasetype(); + Type *t2 = e->e2->type->toBasetype(); + if (t1->ty == Tclass && t2->ty == Tclass) + { + ClassDeclaration *cd1 = t1->isClassHandle(); + ClassDeclaration *cd2 = t2->isClassHandle(); + + if (!(cd1->cpp || cd2->cpp)) + { + /* Rewrite as: + * .object.opEquals(e1, e2) + */ + Expression *e1x = e->e1; + Expression *e2x = e->e2; + + /* + * The explicit cast is necessary for interfaces, + * see http://d.puremagic.com/issues/show_bug.cgi?id=4088 + */ + Type *to = ClassDeclaration::object->getType(); + if (cd1->isInterfaceDeclaration()) + e1x = new CastExp(e->loc, e->e1, t1->isMutable() ? to : to->constOf()); + if (cd2->isInterfaceDeclaration()) + e2x = new CastExp(e->loc, e->e2, t2->isMutable() ? to : to->constOf()); + + result = new IdentifierExp(e->loc, Id::empty); + result = new DotIdExp(e->loc, result, Id::object); + result = new DotIdExp(e->loc, result, Id::eq); + result = new CallExp(e->loc, result, e1x, e2x); + result = result->semantic(sc); + return; + } } + + result = compare_overload(e, sc, Id::eq); } - // Set tiargs, the template argument list, which will be the operator string - if (s || s_r) + void visit(CmpExp *e) { - id = Id::opBinary; - id_r = Id::opBinaryRight; - tiargs = opToArg(sc, op); + //printf("CmpExp::op_overload() (%s)\n", e->toChars()); + + result = compare_overload(e, sc, Id::cmp); } - } - if (s || s_r) - { - /* Try: - * a.opfunc(b) - * b.opfunc_r(a) - * and see which is better. - */ + /********************************* + * Operator overloading for op= + */ + void visit(BinAssignExp *e) + { + //printf("BinAssignExp::op_overload() (%s)\n", e->toChars()); + + if (e->e1->op == TOKarray) + { + ArrayExp *ae = (ArrayExp *)e->e1; + ae->e1 = ae->e1->semantic(sc); + ae->e1 = resolveProperties(sc, ae->e1); + + AggregateDeclaration *ad = isAggregate(ae->e1->type); + if (ad) + { + Expression *e0 = NULL; + + /* Rewrite a[args]+=e2 as: + * a.opIndexOpAssign!("+")(e2, args); + */ + Dsymbol *fd = search_function(ad, Id::opIndexOpAssign); + if (fd) + { + // Deal with $ + Expression *ex = resolveOpDollar(sc, ae, &e0); + if (!ex) + goto Lfallback; + if (ex->op == TOKerror) + { + result = ex; + return; + } + + e->e2 = e->e2->semantic(sc); + if (e->e2->op == TOKerror) + { + result = e->e2; + return; + } + + Expressions *a = (Expressions *)ae->arguments->copy(); + a->insert(0, e->e2); + + Objects *tiargs = opToArg(sc, e->op); + result = new DotTemplateInstanceExp(e->loc, ae->e1, fd->ident, tiargs); + result = new CallExp(e->loc, result, a); + if (ae->arguments->dim == 0) + result = result->trySemantic(sc); + else + result = result->semantic(sc); + if (!result) + goto Lfallback; + result = Expression::combine(e0, result); + return; + } + + // Didn't find it. Forward to aliasthis + if (ad->aliasthis && ae->e1->type != e->att1) + { + /* Rewrite a[arguments] op= e2 as: + * a.aliasthis[arguments] op= e2 + */ + Expression *e1 = ae->copy(); + ((ArrayExp *)e1)->e1 = new DotIdExp(e->loc, ae->e1, ad->aliasthis->ident); + BinExp *be = (BinExp *)e->copy(); + if (!be->att1 && ae->e1->type->checkAliasThisRec()) + be->att1 = ae->e1->type; + be->e1 = e1; + result = be->trySemantic(sc); + if (result) + return; + } + e->att1 = NULL; + + Lfallback: + if (ae->arguments->dim == 0) + { + // a[] op= e2 + SliceExp *se = new SliceExp(ae->loc, ae->e1, NULL, NULL); + se->att1 = ae->att1; + e->e1 = se; + e->accept(this); + result = Expression::combine(e0, result); + return; + } + if (ae->arguments->dim == 1 && (*ae->arguments)[0]->op == TOKinterval) + { + // a[lwr..upr] op= e2 + IntervalExp *ie = (IntervalExp *)(*ae->arguments)[0]; + SliceExp *se = new SliceExp(ae->loc, ae->e1, ie->lwr, ie->upr); + se->att1 = ae->att1; + e->e1 = se; + e->accept(this); + result = Expression::combine(e0, result); + return; + } + } + } + else if (e->e1->op == TOKslice) + { + SliceExp *se = (SliceExp *)e->e1; + se->e1 = se->e1->semantic(sc); + se->e1 = resolveProperties(sc, se->e1); + + AggregateDeclaration *ad = isAggregate(se->e1->type); + if (ad) + { + /* Rewrite a[lwr..upr]+=e2 as: + * a.opSliceOpAssign!("+")(e2, lwr, upr); + */ + Dsymbol *fd = search_function(ad, Id::opSliceOpAssign); + if (fd) + { + // Deal with $ + Expression *e0 = NULL; + Expression *ex = resolveOpDollar(sc, se, &e0); + if (ex->op == TOKerror) + { + result = ex; + return; + } + Expressions *a = new Expressions(); + a->push(e->e2); + assert(!se->lwr || se->upr); + if (se->lwr) + { + a->push(se->lwr); + a->push(se->upr); + } + + Objects *tiargs = opToArg(sc, e->op); + result = new DotTemplateInstanceExp(e->loc, se->e1, fd->ident, tiargs); + result = new CallExp(e->loc, result, a); + result = result->semantic(sc); + result = Expression::combine(e0, result); + return; + } - args1.setDim(1); - args1[0] = e1; - expandTuples(&args1); - args2.setDim(1); - args2[0] = e2; - expandTuples(&args2); - argsset = 1; + // Didn't find it. Forward to aliasthis + if (ad->aliasthis && se->e1->type != e->att1) + { + /* Rewrite a[lwr..upr] op= e2 as: + * a.aliasthis[lwr..upr] op= e2 + */ + Expression *e1 = se->copy(); + ((SliceExp *)e1)->e1 = new DotIdExp(e->loc, se->e1, ad->aliasthis->ident); + BinExp *be = (BinExp *)e->copy(); + if (!be->att1 && se->e1->type->checkAliasThisRec()) + be->att1 = se->e1->type; + be->e1 = e1; + result = be->trySemantic(sc); + if (result) + return; + } + e->att1 = NULL; + } + } - Match m; - memset(&m, 0, sizeof(m)); - m.last = MATCHnomatch; + result = e->binSemanticProp(sc); + if (result) + return; - if (s) - functionResolve(&m, s, loc, sc, tiargs, e1->type, &args2); + // Don't attempt 'alias this' if an error occured + if (e->e1->type->ty == Terror || e->e2->type->ty == Terror) + { + result = new ErrorExp(); + return; + } - FuncDeclaration *lastf = m.lastf; + Identifier *id = opId(e); - if (s_r) - functionResolve(&m, s_r, loc, sc, tiargs, e2->type, &args1); + Expressions args2; - if (m.count > 1) - { - // Error, ambiguous - error("overloads %s and %s both match argument list for %s", - m.lastf->type->toChars(), - m.nextf->type->toChars(), - m.lastf->toChars()); - } - else if (m.last <= MATCHnomatch) - { - m.lastf = m.anyf; - if (tiargs) - goto L1; - } + AggregateDeclaration *ad1 = isAggregate(e->e1->type); - Expression *e; - if (op == TOKplusplus || op == TOKminusminus) - // Kludge because operator overloading regards e++ and e-- - // as unary, but it's implemented as a binary. - // Rewrite (e1 ++ e2) as e1.postinc() - // Rewrite (e1 -- e2) as e1.postdec() - e = build_overload(loc, sc, e1, NULL, m.lastf ? m.lastf : s); - else if (lastf && m.lastf == lastf || !s_r && m.last <= MATCHnomatch) - // Rewrite (e1 op e2) as e1.opfunc(e2) - e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); - else - // Rewrite (e1 op e2) as e2.opfunc_r(e1) - e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s_r); - return e; - } + Dsymbol *s = NULL; -L1: -#if 1 // Retained for D1 compatibility - if (isCommutative() && !tiargs) - { - s = NULL; - s_r = NULL; - if (ad1 && id_r) - { - s_r = search_function(ad1, id_r); - } - if (ad2 && id) - { - s = search_function(ad2, id); - } + #if 1 // the old D1 scheme + if (ad1 && id) + { + s = search_function(ad1, id); + } + #endif - if (s || s_r) - { - /* Try: - * a.opfunc_r(b) - * b.opfunc(a) - * and see which is better. - */ + Objects *tiargs = NULL; + if (!s) + { + /* Try the new D2 scheme, opOpAssign + */ + if (ad1) + { + s = search_function(ad1, Id::opOpAssign); + if (s && !s->isTemplateDeclaration()) + { + e->error("%s.opOpAssign isn't a template", e->e1->toChars()); + result = new ErrorExp(); + return; + } + } + + // Set tiargs, the template argument list, which will be the operator string + if (s) + { + id = Id::opOpAssign; + tiargs = opToArg(sc, e->op); + } + } - if (!argsset) + if (s) { - args1.setDim(1); - args1[0] = e1; - expandTuples(&args1); + /* Try: + * a.opOpAssign(b) + */ + args2.setDim(1); - args2[0] = e2; + args2[0] = e->e2; expandTuples(&args2); - } - - Match m; - memset(&m, 0, sizeof(m)); - m.last = MATCHnomatch; - if (s_r) - functionResolve(&m, s_r, loc, sc, tiargs, e1->type, &args2); + Match m; + memset(&m, 0, sizeof(m)); + m.last = MATCHnomatch; - FuncDeclaration *lastf = m.lastf; + if (s) + { + functionResolve(&m, s, e->loc, sc, tiargs, e->e1->type, &args2); + if (m.lastf && m.lastf->errors) + { + result = new ErrorExp(); + return; + } + } - if (s) - functionResolve(&m, s, loc, sc, tiargs, e2->type, &args1); + if (m.count > 1) + { + // Error, ambiguous + e->error("overloads %s and %s both match argument list for %s", + m.lastf->type->toChars(), + m.nextf->type->toChars(), + m.lastf->toChars()); + } + else if (m.last <= MATCHnomatch) + { + m.lastf = m.anyf; + if (tiargs) + goto L1; + } - if (m.count > 1) - { - // Error, ambiguous - error("overloads %s and %s both match argument list for %s", - m.lastf->type->toChars(), - m.nextf->type->toChars(), - m.lastf->toChars()); - } - else if (m.last <= MATCHnomatch) - { - m.lastf = m.anyf; + // Rewrite (e1 op e2) as e1.opOpAssign(e2) + result = build_overload(e->loc, sc, e->e1, e->e2, m.lastf ? m.lastf : s); + return; } - Expression *e; - if (lastf && m.lastf == lastf || !s && m.last <= MATCHnomatch) - // Rewrite (e1 op e2) as e1.opfunc_r(e2) - e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s_r); - else - // Rewrite (e1 op e2) as e2.opfunc(e1) - e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s); + L1: - // When reversing operands of comparison operators, - // need to reverse the sense of the op - switch (op) + // Try alias this on first operand + if (ad1 && ad1->aliasthis) { - case TOKlt: op = TOKgt; break; - case TOKgt: op = TOKlt; break; - case TOKle: op = TOKge; break; - case TOKge: op = TOKle; break; - - // Floating point compares - case TOKule: op = TOKuge; break; - case TOKul: op = TOKug; break; - case TOKuge: op = TOKule; break; - case TOKug: op = TOKul; break; - - // These are symmetric - case TOKunord: - case TOKlg: - case TOKleg: - case TOKue: - break; + /* Rewrite (e1 op e2) as: + * (e1.aliasthis op e2) + */ + if (e->att1 && e->e1->type == e->att1) + return; + //printf("att %s e1 = %s\n", Token::toChars(e->op), e->e1->type->toChars()); + Expression *e1 = new DotIdExp(e->loc, e->e1, ad1->aliasthis->ident); + BinExp *be = (BinExp *)e->copy(); + if (!be->att1 && e->e1->type->checkAliasThisRec()) + be->att1 = e->e1->type; + be->e1 = e1; + result = be->trySemantic(sc); + return; } - return e; + // Try alias this on second operand + AggregateDeclaration *ad2 = isAggregate(e->e2->type); + if (ad2 && ad2->aliasthis) + { + /* Rewrite (e1 op e2) as: + * (e1 op e2.aliasthis) + */ + if (e->att2 && e->e2->type == e->att2) + return; + //printf("att %s e2 = %s\n", Token::toChars(e->op), e->e2->type->toChars()); + Expression *e2 = new DotIdExp(e->loc, e->e2, ad2->aliasthis->ident); + BinExp *be = (BinExp *)e->copy(); + if (!be->att2 && e->e2->type->checkAliasThisRec()) + be->att2 = e->e2->type; + be->e2 = e2; + result = be->trySemantic(sc); + return; + } } - } -#endif - - // Try alias this on first operand - if (ad1 && ad1->aliasthis && - !(op == TOKassign && ad2 && ad1 == ad2)) // See Bugzilla 2943 - { - /* Rewrite (e1 op e2) as: - * (e1.aliasthis op e2) - */ - if (att1 && this->e1->type == att1) - return NULL; - //printf("att bin e1 = %s\n", this->e1->type->toChars()); - Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident); - BinExp *be = (BinExp *)copy(); - if (!be->att1 && this->e1->type->checkAliasThisRec()) - be->att1 = this->e1->type; - be->e1 = e1; - return be->trySemantic(sc); - } + }; - // Try alias this on second operand - if (ad2 && ad2->aliasthis && - /* Bugzilla 2943: make sure that when we're copying the struct, we don't - * just copy the alias this member - */ - !(op == TOKassign && ad1 && ad1 == ad2)) - { - /* Rewrite (e1 op e2) as: - * (e1 op e2.aliasthis) - */ - if (att2 && this->e2->type == att2) - return NULL; - //printf("att bin e2 = %s\n", this->e2->type->toChars()); - Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident); - BinExp *be = (BinExp *)copy(); - if (!be->att2 && this->e2->type->checkAliasThisRec()) - be->att2 = this->e2->type; - be->e2 = e2; - return be->trySemantic(sc); - } - return NULL; + OpOverload v(sc); + e->accept(&v); + return v.result; } /****************************************** * Common code for overloading of EqualExp and CmpExp */ -Expression *BinExp::compare_overload(Scope *sc, Identifier *id) +Expression *compare_overload(BinExp *e, Scope *sc, Identifier *id) { - //printf("BinExp::compare_overload(id = %s) %s\n", id->toChars(), toChars()); + //printf("BinExp::compare_overload(id = %s) %s\n", id->toChars(), e->toChars()); - AggregateDeclaration *ad1 = isAggregate(e1->type); - AggregateDeclaration *ad2 = isAggregate(e2->type); + AggregateDeclaration *ad1 = isAggregate(e->e1->type); + AggregateDeclaration *ad2 = isAggregate(e->e2->type); Dsymbol *s = NULL; Dsymbol *s_r = NULL; @@ -754,10 +1259,10 @@ Expression *BinExp::compare_overload(Scope *sc, Identifier *id) Expressions args2; args1.setDim(1); - args1[0] = e1; + args1[0] = e->e1; expandTuples(&args1); args2.setDim(1); - args2[0] = e2; + args2[0] = e->e2; expandTuples(&args2); Match m; @@ -771,13 +1276,21 @@ Expression *BinExp::compare_overload(Scope *sc, Identifier *id) } if (s) - functionResolve(&m, s, loc, sc, tiargs, e1->type, &args2); + { + functionResolve(&m, s, e->loc, sc, tiargs, e->e1->type, &args2); + if (m.lastf && m.lastf->errors) + return new ErrorExp(); + } FuncDeclaration *lastf = m.lastf; int count = m.count; if (s_r) - functionResolve(&m, s_r, loc, sc, tiargs, e2->type, &args1); + { + functionResolve(&m, s_r, e->loc, sc, tiargs, e->e2->type, &args1); + if (m.lastf && m.lastf->errors) + return new ErrorExp(); + } if (m.count > 1) { @@ -795,7 +1308,7 @@ Expression *BinExp::compare_overload(Scope *sc, Identifier *id) if (!(m.lastf == lastf && m.count == 2 && count == 1)) { // Error, ambiguous - error("overloads %s and %s both match argument list for %s", + e->error("overloads %s and %s both match argument list for %s", m.lastf->type->toChars(), m.nextf->type->toChars(), m.lastf->toChars()); @@ -806,28 +1319,31 @@ Expression *BinExp::compare_overload(Scope *sc, Identifier *id) m.lastf = m.anyf; } - Expression *e; + Expression *result; if (lastf && m.lastf == lastf || !s_r && m.last <= MATCHnomatch) + { // Rewrite (e1 op e2) as e1.opfunc(e2) - e = build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); + result = build_overload(e->loc, sc, e->e1, e->e2, m.lastf ? m.lastf : s); + } else - { // Rewrite (e1 op e2) as e2.opfunc_r(e1) - e = build_overload(loc, sc, e2, e1, m.lastf ? m.lastf : s_r); + { + // Rewrite (e1 op e2) as e2.opfunc_r(e1) + result = build_overload(e->loc, sc, e->e2, e->e1, m.lastf ? m.lastf : s_r); // When reversing operands of comparison operators, // need to reverse the sense of the op - switch (op) + switch (e->op) { - case TOKlt: op = TOKgt; break; - case TOKgt: op = TOKlt; break; - case TOKle: op = TOKge; break; - case TOKge: op = TOKle; break; + case TOKlt: e->op = TOKgt; break; + case TOKgt: e->op = TOKlt; break; + case TOKle: e->op = TOKge; break; + case TOKge: e->op = TOKle; break; // Floating point compares - case TOKule: op = TOKuge; break; - case TOKul: op = TOKug; break; - case TOKuge: op = TOKule; break; - case TOKug: op = TOKul; break; + case TOKule: e->op = TOKuge; break; + case TOKul: e->op = TOKug; break; + case TOKuge: e->op = TOKule; break; + case TOKug: e->op = TOKul; break; // The rest are symmetric default: @@ -835,7 +1351,7 @@ Expression *BinExp::compare_overload(Scope *sc, Identifier *id) } } - return e; + return result; } // Try alias this on first operand @@ -844,13 +1360,13 @@ Expression *BinExp::compare_overload(Scope *sc, Identifier *id) /* Rewrite (e1 op e2) as: * (e1.aliasthis op e2) */ - if (att1 && this->e1->type == att1) + if (e->att1 && e->e1->type == e->att1) return NULL; - //printf("att cmp_bin e1 = %s\n", this->e1->type->toChars()); - Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident); - BinExp *be = (BinExp *)copy(); - if (!be->att1 && this->e1->type->checkAliasThisRec()) - be->att1 = this->e1->type; + //printf("att cmp_bin e1 = %s\n", e->e1->type->toChars()); + Expression *e1 = new DotIdExp(e->loc, e->e1, ad1->aliasthis->ident); + BinExp *be = (BinExp *)e->copy(); + if (!be->att1 && e->e1->type->checkAliasThisRec()) + be->att1 = e->e1->type; be->e1 = e1; return be->trySemantic(sc); } @@ -861,13 +1377,13 @@ Expression *BinExp::compare_overload(Scope *sc, Identifier *id) /* Rewrite (e1 op e2) as: * (e1 op e2.aliasthis) */ - if (att2 && this->e2->type == att2) + if (e->att2 && e->e2->type == e->att2) return NULL; - //printf("att cmp_bin e2 = %s\n", this->e2->type->toChars()); - Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident); - BinExp *be = (BinExp *)copy(); - if (!be->att2 && this->e2->type->checkAliasThisRec()) - be->att2 = this->e2->type; + //printf("att cmp_bin e2 = %s\n", e->e2->type->toChars()); + Expression *e2 = new DotIdExp(e->loc, e->e2, ad2->aliasthis->ident); + BinExp *be = (BinExp *)e->copy(); + if (!be->att2 && e->e2->type->checkAliasThisRec()) + be->att2 = e->e2->type; be->e2 = e2; return be->trySemantic(sc); } @@ -875,276 +1391,6 @@ Expression *BinExp::compare_overload(Scope *sc, Identifier *id) return NULL; } -Expression *EqualExp::op_overload(Scope *sc) -{ - //printf("EqualExp::op_overload() (%s)\n", toChars()); - - Type *t1 = e1->type->toBasetype(); - Type *t2 = e2->type->toBasetype(); - if (t1->ty == Tclass && t2->ty == Tclass) - { ClassDeclaration *cd1 = t1->isClassHandle(); - ClassDeclaration *cd2 = t2->isClassHandle(); - - if (!(cd1->cpp || cd2->cpp)) - { - /* Rewrite as: - * .object.opEquals(e1, e2) - */ - Expression *e1x = e1; - Expression *e2x = e2; - - /* - * The explicit cast is necessary for interfaces, - * see http://d.puremagic.com/issues/show_bug.cgi?id=4088 - */ - Type *to = ClassDeclaration::object->getType(); - if (cd1->isInterfaceDeclaration()) - e1x = new CastExp(loc, e1, t1->isMutable() ? to : to->constOf()); - if (cd2->isInterfaceDeclaration()) - e2x = new CastExp(loc, e2, t2->isMutable() ? to : to->constOf()); - - Expression *e = new IdentifierExp(loc, Id::empty); - e = new DotIdExp(loc, e, Id::object); - e = new DotIdExp(loc, e, Id::eq); - e = new CallExp(loc, e, e1x, e2x); - e = e->semantic(sc); - return e; - } - } - - return compare_overload(sc, Id::eq); -} - -Expression *CmpExp::op_overload(Scope *sc) -{ - //printf("CmpExp::op_overload() (%s)\n", toChars()); - - return compare_overload(sc, Id::cmp); -} - -/********************************* - * Operator overloading for op= - */ -Expression *BinAssignExp::op_overload(Scope *sc) -{ - //printf("BinAssignExp::op_overload() (%s)\n", toChars()); - - if (e1->op == TOKarray) - { - ArrayExp *ae = (ArrayExp *)e1; - ae->e1 = ae->e1->semantic(sc); - ae->e1 = resolveProperties(sc, ae->e1); - - AggregateDeclaration *ad = isAggregate(ae->e1->type); - if (ad) - { - /* Rewrite a[args]+=e2 as: - * a.opIndexOpAssign!("+")(e2, args); - */ - Dsymbol *fd = search_function(ad, Id::opIndexOpAssign); - if (fd) - { - Expression *e0 = resolveOpDollar(sc, ae); - Expressions *a = (Expressions *)ae->arguments->copy(); - a->insert(0, e2); - - Objects *tiargs = opToArg(sc, op); - Expression *e = new DotTemplateInstanceExp(loc, ae->e1, fd->ident, tiargs); - e = new CallExp(loc, e, a); - e = combine(e0, e); - e = e->semantic(sc); - return e; - } - - // Didn't find it. Forward to aliasthis - if (ad->aliasthis && ae->e1->type != att1) - { - /* Rewrite a[arguments] op= e2 as: - * a.aliasthis[arguments] op= e2 - */ - Expression *e1 = ae->copy(); - ((ArrayExp *)e1)->e1 = new DotIdExp(loc, ae->e1, ad->aliasthis->ident); - BinExp *be = (BinExp *)copy(); - if (!be->att1 && ae->e1->type->checkAliasThisRec()) - be->att1 = ae->e1->type; - be->e1 = e1; - if (Expression *e = be->trySemantic(sc)) - return e; - } - att1 = NULL; - } - } - else if (e1->op == TOKslice) - { - SliceExp *se = (SliceExp *)e1; - se->e1 = se->e1->semantic(sc); - se->e1 = resolveProperties(sc, se->e1); - - AggregateDeclaration *ad = isAggregate(se->e1->type); - if (ad) - { - /* Rewrite a[lwr..upr]+=e2 as: - * a.opSliceOpAssign!("+")(e2, lwr, upr); - */ - Dsymbol *fd = search_function(ad, Id::opSliceOpAssign); - if (fd) - { - Expression *e0 = resolveOpDollar(sc, se); - Expressions *a = new Expressions(); - a->push(e2); - assert(!se->lwr || se->upr); - if (se->lwr) - { - a->push(se->lwr); - a->push(se->upr); - } - - Objects *tiargs = opToArg(sc, op); - Expression *e = new DotTemplateInstanceExp(loc, se->e1, fd->ident, tiargs); - e = new CallExp(loc, e, a); - e = combine(e0, e); - e = e->semantic(sc); - return e; - } - - // Didn't find it. Forward to aliasthis - if (ad->aliasthis && se->e1->type != att1) - { - /* Rewrite a[lwr..upr] op= e2 as: - * a.aliasthis[lwr..upr] op= e2 - */ - Expression *e1 = se->copy(); - ((SliceExp *)e1)->e1 = new DotIdExp(loc, se->e1, ad->aliasthis->ident); - BinExp *be = (BinExp *)copy(); - if (!be->att1 && se->e1->type->checkAliasThisRec()) - be->att1 = se->e1->type; - be->e1 = e1; - if (Expression *e = be->trySemantic(sc)) - return e; - } - att1 = NULL; - } - } - - BinExp::semantic(sc); - e1 = resolveProperties(sc, e1); - e2 = resolveProperties(sc, e2); - - // Don't attempt 'alias this' if an error occured - if (e1->type->ty == Terror || e2->type->ty == Terror) - return new ErrorExp(); - - Identifier *id = opId(); - - Expressions args2; - - AggregateDeclaration *ad1 = isAggregate(e1->type); - - Dsymbol *s = NULL; - -#if 1 // the old D1 scheme - if (ad1 && id) - { - s = search_function(ad1, id); - } -#endif - - Objects *tiargs = NULL; - if (!s) - { /* Try the new D2 scheme, opOpAssign - */ - if (ad1) - { - s = search_function(ad1, Id::opOpAssign); - if (s && !s->isTemplateDeclaration()) - { error("%s.opOpAssign isn't a template", e1->toChars()); - return new ErrorExp(); - } - } - - // Set tiargs, the template argument list, which will be the operator string - if (s) - { - id = Id::opOpAssign; - tiargs = opToArg(sc, op); - } - } - - if (s) - { - /* Try: - * a.opOpAssign(b) - */ - - args2.setDim(1); - args2[0] = e2; - expandTuples(&args2); - - Match m; - memset(&m, 0, sizeof(m)); - m.last = MATCHnomatch; - - if (s) - functionResolve(&m, s, loc, sc, tiargs, e1->type, &args2); - - if (m.count > 1) - { - // Error, ambiguous - error("overloads %s and %s both match argument list for %s", - m.lastf->type->toChars(), - m.nextf->type->toChars(), - m.lastf->toChars()); - } - else if (m.last <= MATCHnomatch) - { - m.lastf = m.anyf; - if (tiargs) - goto L1; - } - - // Rewrite (e1 op e2) as e1.opOpAssign(e2) - return build_overload(loc, sc, e1, e2, m.lastf ? m.lastf : s); - } - -L1: - - // Try alias this on first operand - if (ad1 && ad1->aliasthis) - { - /* Rewrite (e1 op e2) as: - * (e1.aliasthis op e2) - */ - if (att1 && this->e1->type == att1) - return NULL; - //printf("att %s e1 = %s\n", Token::toChars(op), this->e1->type->toChars()); - Expression *e1 = new DotIdExp(loc, this->e1, ad1->aliasthis->ident); - BinExp *be = (BinExp *)copy(); - if (!be->att1 && this->e1->type->checkAliasThisRec()) - be->att1 = this->e1->type; - be->e1 = e1; - return be->trySemantic(sc); - } - - // Try alias this on second operand - AggregateDeclaration *ad2 = isAggregate(e2->type); - if (ad2 && ad2->aliasthis) - { - /* Rewrite (e1 op e2) as: - * (e1 op e2.aliasthis) - */ - if (att2 && this->e2->type == att2) - return NULL; - //printf("att %s e2 = %s\n", Token::toChars(op), this->e2->type->toChars()); - Expression *e2 = new DotIdExp(loc, this->e2, ad2->aliasthis->ident); - BinExp *be = (BinExp *)copy(); - if (!be->att2 && this->e2->type->checkAliasThisRec()) - be->att2 = this->e2->type; - be->e2 = e2; - return be->trySemantic(sc); - } - return NULL; -} - /*********************************** * Utility to build a function call out of this reference and argument. */ @@ -1193,27 +1439,28 @@ Dsymbol *search_function(ScopeDsymbol *ad, Identifier *funcid) } -int ForeachStatement::inferAggregate(Scope *sc, Dsymbol *&sapply) +bool inferAggregate(ForeachStatement *fes, Scope *sc, Dsymbol *&sapply) { - Identifier *idapply = (op == TOKforeach) ? Id::apply : Id::applyReverse; - Identifier *idfront = (op == TOKforeach) ? Id::Ffront : Id::Fback; + Identifier *idapply = (fes->op == TOKforeach) ? Id::apply : Id::applyReverse; + Identifier *idfront = (fes->op == TOKforeach) ? Id::Ffront : Id::Fback; int sliced = 0; Type *tab; Type *att = NULL; - Expression *org_aggr = aggr; + Expression *org_aggr = fes->aggr; AggregateDeclaration *ad; while (1) { - aggr = aggr->semantic(sc); - aggr = resolveProperties(sc, aggr); - aggr = aggr->optimize(WANTvalue); - if (!aggr->type) + fes->aggr = fes->aggr->semantic(sc); + fes->aggr = resolveProperties(sc, fes->aggr); + fes->aggr = fes->aggr->optimize(WANTvalue); + if (!fes->aggr->type) goto Lerr; - tab = aggr->type->toBasetype(); + tab = fes->aggr->type->toBasetype(); if (att == tab) - { aggr = org_aggr; + { + fes->aggr = org_aggr; goto Lerr; } switch (tab->ty) @@ -1243,17 +1490,19 @@ int ForeachStatement::inferAggregate(Scope *sc, Dsymbol *&sapply) Dsymbol *s = search_function(ad, Id::slice); if (s) - { Expression *rinit = new SliceExp(aggr->loc, aggr, NULL, NULL); + { + Expression *rinit = new SliceExp(fes->aggr->loc, fes->aggr, NULL, NULL); rinit = rinit->trySemantic(sc); if (rinit) // if application of [] succeeded - { aggr = rinit; + { + fes->aggr = rinit; sliced = 1; continue; } } } - if (Dsymbol *shead = ad->search(Loc(), idfront)) + if (ad->search(Loc(), idfront)) { // range aggregate break; @@ -1263,14 +1512,15 @@ int ForeachStatement::inferAggregate(Scope *sc, Dsymbol *&sapply) { if (!att && tab->checkAliasThisRec()) att = tab; - aggr = new DotIdExp(aggr->loc, aggr, ad->aliasthis->ident); + fes->aggr = new DotIdExp(fes->aggr->loc, fes->aggr, ad->aliasthis->ident); continue; } goto Lerr; case Tdelegate: - if (aggr->op == TOKdelegate) - { DelegateExp *de = (DelegateExp *)aggr; + if (fes->aggr->op == TOKdelegate) + { + DelegateExp *de = (DelegateExp *)fes->aggr; sapply = de->func->isFuncDeclaration(); } break; @@ -1283,10 +1533,10 @@ int ForeachStatement::inferAggregate(Scope *sc, Dsymbol *&sapply) } break; } - return 1; + return true; Lerr: - return 0; + return false; } /***************************************** @@ -1295,29 +1545,30 @@ int ForeachStatement::inferAggregate(Scope *sc, Dsymbol *&sapply) * them from the aggregate type. */ -int ForeachStatement::inferApplyArgTypes(Scope *sc, Dsymbol *&sapply) +bool inferApplyArgTypes(ForeachStatement *fes, Scope *sc, Dsymbol *&sapply) { - if (!arguments || !arguments->dim) - return 0; + if (!fes->arguments || !fes->arguments->dim) + return false; if (sapply) // prefer opApply { - for (size_t u = 0; u < arguments->dim; u++) - { Parameter *arg = (*arguments)[u]; + for (size_t u = 0; u < fes->arguments->dim; u++) + { + Parameter *arg = (*fes->arguments)[u]; if (arg->type) { - arg->type = arg->type->semantic(loc, sc); + arg->type = arg->type->semantic(fes->loc, sc); arg->type = arg->type->addStorageClass(arg->storageClass); } } Expression *ethis; - Type *tab = aggr->type->toBasetype(); + Type *tab = fes->aggr->type->toBasetype(); if (tab->ty == Tclass || tab->ty == Tstruct) - ethis = aggr; + ethis = fes->aggr; else - { assert(tab->ty == Tdelegate && aggr->op == TOKdelegate); - ethis = ((DelegateExp *)aggr)->e1; + { assert(tab->ty == Tdelegate && fes->aggr->op == TOKdelegate); + ethis = ((DelegateExp *)fes->aggr)->e1; } /* Look for like an @@ -1326,29 +1577,32 @@ int ForeachStatement::inferApplyArgTypes(Scope *sc, Dsymbol *&sapply) */ FuncDeclaration *fd = sapply->isFuncDeclaration(); if (fd) - { sapply = inferApplyArgTypesX(ethis, fd, arguments); + { + sapply = inferApplyArgTypesX(ethis, fd, fes->arguments); } #if 0 TemplateDeclaration *td = sapply->isTemplateDeclaration(); if (td) - { inferApplyArgTypesZ(td, arguments); + { + inferApplyArgTypesZ(td, fes->arguments); } #endif - return sapply ? 1 : 0; + return sapply != NULL; } /* Return if no arguments need types. */ - for (size_t u = 0; u < arguments->dim; u++) - { Parameter *arg = (*arguments)[u]; + for (size_t u = 0; u < fes->arguments->dim; u++) + { + Parameter *arg = (*fes->arguments)[u]; if (!arg->type) break; } AggregateDeclaration *ad; - Parameter *arg = (*arguments)[0]; - Type *taggr = aggr->type; + Parameter *arg = (*fes->arguments)[0]; + Type *taggr = fes->aggr->type; assert(taggr); Type *tab = taggr->toBasetype(); switch (tab->ty) @@ -1356,14 +1610,14 @@ int ForeachStatement::inferApplyArgTypes(Scope *sc, Dsymbol *&sapply) case Tarray: case Tsarray: case Ttuple: - if (arguments->dim == 2) + if (fes->arguments->dim == 2) { if (!arg->type) { arg->type = Type::tsize_t; // key type arg->type = arg->type->addStorageClass(arg->storageClass); } - arg = (*arguments)[1]; + arg = (*fes->arguments)[1]; } if (!arg->type && tab->ty != Ttuple) { @@ -1373,16 +1627,17 @@ int ForeachStatement::inferApplyArgTypes(Scope *sc, Dsymbol *&sapply) break; case Taarray: - { TypeAArray *taa = (TypeAArray *)tab; + { + TypeAArray *taa = (TypeAArray *)tab; - if (arguments->dim == 2) + if (fes->arguments->dim == 2) { if (!arg->type) { arg->type = taa->index; // key type arg->type = arg->type->addStorageClass(arg->storageClass); } - arg = (*arguments)[1]; + arg = (*fes->arguments)[1]; } if (!arg->type) { @@ -1401,13 +1656,13 @@ int ForeachStatement::inferApplyArgTypes(Scope *sc, Dsymbol *&sapply) goto Laggr; Laggr: - if (arguments->dim == 1) + if (fes->arguments->dim == 1) { if (!arg->type) { /* Look for a front() or back() overload */ - Identifier *id = (op == TOKforeach) ? Id::Ffront : Id::Fback; + Identifier *id = (fes->op == TOKforeach) ? Id::Ffront : Id::Fback; Dsymbol *s = ad->search(Loc(), id); FuncDeclaration *fd = s ? s->isFuncDeclaration() : NULL; if (fd) @@ -1433,15 +1688,15 @@ int ForeachStatement::inferApplyArgTypes(Scope *sc, Dsymbol *&sapply) case Tdelegate: { - if (!inferApplyArgTypesY((TypeFunction *)tab->nextOf(), arguments)) - return 0; + if (!inferApplyArgTypesY((TypeFunction *)tab->nextOf(), fes->arguments)) + return false; break; } default: break; // ignore error, caught later } - return 1; + return true; } static Dsymbol *inferApplyArgTypesX(Expression *ethis, FuncDeclaration *fstart, Parameters *arguments) @@ -1449,7 +1704,7 @@ static Dsymbol *inferApplyArgTypesX(Expression *ethis, FuncDeclaration *fstart, struct ParamOpOver { Parameters *arguments; - unsigned char mod; + MOD mod; MATCH match; FuncDeclaration *fd_best; FuncDeclaration *fd_ambig; @@ -1495,10 +1750,10 @@ static Dsymbol *inferApplyArgTypesX(Expression *ethis, FuncDeclaration *fstart, { inferApplyArgTypesY((TypeFunction *)p.fd_best->type, arguments); if (p.fd_ambig) - { ::error(ethis->loc, "%s.%s matches more than one declaration:\n\t%s(%d):%s\nand:\n\t%s(%d):%s", + { ::error(ethis->loc, "%s.%s matches more than one declaration:\n%s: %s\nand:\n%s: %s", ethis->toChars(), fstart->ident->toChars(), - p.fd_best ->loc.filename, p.fd_best ->loc.linnum, p.fd_best ->type->toChars(), - p.fd_ambig->loc.filename, p.fd_ambig->loc.linnum, p.fd_ambig->type->toChars()); + p.fd_best ->loc.toChars(), p.fd_best ->type->toChars(), + p.fd_ambig->loc.toChars(), p.fd_ambig->type->toChars()); p.fd_best = NULL; } } @@ -1547,7 +1802,6 @@ static int inferApplyArgTypesY(TypeFunction *tf, Parameters *arguments, int flag arg->type = arg->type->addStorageClass(arg->storageClass); } } -Lmatch: return 1; Lnomatch: diff --git a/gcc/d/dfrontend/optimize.c b/gcc/d/dfrontend/optimize.c index 63002dcde..3a0bc5c02 100644 --- a/gcc/d/dfrontend/optimize.c +++ b/gcc/d/dfrontend/optimize.c @@ -1,12 +1,12 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars, All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0 + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/optimize.c + */ #include #include @@ -160,1068 +160,1102 @@ Expression *fromConstInitializer(int result, Expression *e1) return e; } - -Expression *Expression::optimize(int result, bool keepLvalue) -{ - //printf("Expression::optimize(result = x%x) %s\n", result, toChars()); - return this; -} - -Expression *VarExp::optimize(int result, bool keepLvalue) -{ - if (keepLvalue) - { - VarDeclaration *v = var->isVarDeclaration(); - if (v && !(v->storage_class & STCmanifest)) - return this; - } - return fromConstInitializer(result, this); -} - -Expression *TupleExp::optimize(int result, bool keepLvalue) -{ - if (e0) - e0 = e0->optimize(WANTvalue); - for (size_t i = 0; i < exps->dim; i++) - { - Expression *e = (*exps)[i]; - e = e->optimize(WANTvalue); - (*exps)[i] = e; - } - return this; -} - -Expression *ArrayLiteralExp::optimize(int result, bool keepLvalue) +Expression *Expression_optimize(Expression *e, int result, bool keepLvalue) { - if (elements) + class OptimizeVisitor : public Visitor { - for (size_t i = 0; i < elements->dim; i++) - { Expression *e = (*elements)[i]; + public: + int result; + bool keepLvalue; + Expression *ret; - e = e->optimize(WANTvalue | (result & WANTexpand)); - (*elements)[i] = e; + OptimizeVisitor(int result, bool keepLvalue) + : result(result), keepLvalue(keepLvalue) + { } - } - return this; -} - -Expression *AssocArrayLiteralExp::optimize(int result, bool keepLvalue) -{ - assert(keys->dim == values->dim); - for (size_t i = 0; i < keys->dim; i++) - { Expression *e = (*keys)[i]; - - e = e->optimize(WANTvalue | (result & WANTexpand)); - (*keys)[i] = e; - - e = (*values)[i]; - e = e->optimize(WANTvalue | (result & WANTexpand)); - (*values)[i] = e; - } - return this; -} -Expression *StructLiteralExp::optimize(int result, bool keepLvalue) -{ - if(stageflags & stageOptimize) return this; - int old = stageflags; - stageflags |= stageOptimize; - if (elements) - { - for (size_t i = 0; i < elements->dim; i++) - { Expression *e = (*elements)[i]; - if (!e) - continue; - e = e->optimize(WANTvalue | (result & WANTexpand)); - (*elements)[i] = e; + void visit(Expression *e) + { + //printf("Expression::optimize(result = x%x) %s\n", result, e->toChars()); } - } - stageflags = old; - return this; -} - -Expression *TypeExp::optimize(int result, bool keepLvalue) -{ - return this; -} - -Expression *UnaExp::optimize(int result, bool keepLvalue) -{ - //printf("UnaExp::optimize() %s\n", toChars()); - e1 = e1->optimize(result); - return this; -} - -Expression *NegExp::optimize(int result, bool keepLvalue) -{ Expression *e; - - e1 = e1->optimize(result); - if (e1->isConst() == 1) - { - e = Neg(type, e1); - } - else - e = this; - return e; -} - -Expression *ComExp::optimize(int result, bool keepLvalue) -{ Expression *e; - - e1 = e1->optimize(result); - if (e1->isConst() == 1) - { - e = Com(type, e1); - } - else - e = this; - return e; -} - -Expression *NotExp::optimize(int result, bool keepLvalue) -{ Expression *e; - - e1 = e1->optimize(result); - if (e1->isConst() == 1) - { - e = Not(type, e1); - } - else - e = this; - return e; -} - -Expression *BoolExp::optimize(int result, bool keepLvalue) -{ Expression *e; - - e1 = e1->optimize(result); - if (e1->isConst() == 1) - { - e = Bool(type, e1); - } - else - e = this; - return e; -} - -Expression *SymOffExp::optimize(int result, bool keepLvalue) -{ - assert(var); - return this; -} - -Expression *AddrExp::optimize(int result, bool keepLvalue) -{ Expression *e; - - //printf("AddrExp::optimize(result = %d) %s\n", result, toChars()); - /* Rewrite &(a,b) as (a,&b) - */ - if (e1->op == TOKcomma) - { CommaExp *ce = (CommaExp *)e1; - AddrExp *ae = new AddrExp(loc, ce->e2); - ae->type = type; - e = new CommaExp(ce->loc, ce->e1, ae); - e->type = type; - return e->optimize(result); - } - - // Keep lvalue-ness - e1 = e1->optimize(result, true); - - // Convert &*ex to ex - if (e1->op == TOKstar) - { Expression *ex; - - ex = ((PtrExp *)e1)->e1; - if (type->equals(ex->type)) - e = ex; - else + void visit(VarExp *e) { - e = ex->copy(); - e->type = type; + if (keepLvalue) + { + VarDeclaration *v = e->var->isVarDeclaration(); + if (v && !(v->storage_class & STCmanifest)) + return; + } + ret = fromConstInitializer(result, e); } - return e; - } - if (e1->op == TOKvar) - { VarExp *ve = (VarExp *)e1; - if (!ve->var->isOut() && !ve->var->isRef() && - !ve->var->isImportedSymbol()) + + void visit(TupleExp *e) { - SymOffExp *se = new SymOffExp(loc, ve->var, 0, ve->hasOverloads); - se->type = type; - return se; + if (e->e0) + e->e0 = e->e0->optimize(WANTvalue); + for (size_t i = 0; i < e->exps->dim; i++) + { + Expression *el = (*e->exps)[i]; + el = el->optimize(WANTvalue); + (*e->exps)[i] = el; + } } - } - if (e1->op == TOKindex) - { // Convert &array[n] to &array+n - IndexExp *ae = (IndexExp *)e1; - if (ae->e2->op == TOKint64 && ae->e1->op == TOKvar) + void visit(ArrayLiteralExp *e) { - sinteger_t index = ae->e2->toInteger(); - VarExp *ve = (VarExp *)ae->e1; - if (ve->type->ty == Tsarray - && !ve->var->isImportedSymbol()) - { - TypeSArray *ts = (TypeSArray *)ve->type; - sinteger_t dim = ts->dim->toInteger(); - if (index < 0 || index >= dim) + if (e->elements) + { + for (size_t i = 0; i < e->elements->dim; i++) { - error("array index %lld is out of bounds [0..%lld]", index, dim); - return new ErrorExp(); + Expression *el = (*e->elements)[i]; + el = el->optimize(WANTvalue | (result & WANTexpand)); + (*e->elements)[i] = el; } - e = new SymOffExp(loc, ve->var, index * ts->nextOf()->size()); - e->type = type; - return e; } } - } - return this; -} -Expression *PtrExp::optimize(int result, bool keepLvalue) -{ - //printf("PtrExp::optimize(result = x%x) %s\n", result, toChars()); - e1 = e1->optimize(result); - // Convert *&ex to ex - // But only if there is no type punning involved - if (e1->op == TOKaddress) - { Expression *e; - Expression *ex; - - ex = ((AddrExp *)e1)->e1; - if (type->equals(ex->type)) - e = ex; - else if (ex->type->implicitConvTo(type) >= MATCHconst) + void visit(AssocArrayLiteralExp *e) { - e = ex->copy(); - e->type = type; + assert(e->keys->dim == e->values->dim); + for (size_t i = 0; i < e->keys->dim; i++) + { + Expression *key = (*e->keys)[i]; + key = key->optimize(WANTvalue | (result & WANTexpand)); + (*e->keys)[i] = key; + + Expression *value = (*e->values)[i]; + value = value->optimize(WANTvalue | (result & WANTexpand)); + (*e->values)[i] = value; + } } - else + + void visit(StructLiteralExp *e) { - return this; + if (e->stageflags & stageOptimize) return; + int old = e->stageflags; + e->stageflags |= stageOptimize; + if (e->elements) + { + for (size_t i = 0; i < e->elements->dim; i++) + { + Expression *el = (*e->elements)[i]; + if (!el) + continue; + el = el->optimize(WANTvalue | (result & WANTexpand)); + (*e->elements)[i] = el; + } + } + e->stageflags = old; } - return e; - } - if (keepLvalue) - return this; - - // Constant fold *(&structliteral + offset) - if (e1->op == TOKadd) - { - Expression *e; - e = Ptr(type, e1); - if (e != EXP_CANT_INTERPRET) - return e; - } - if (e1->op == TOKsymoff) - { SymOffExp *se = (SymOffExp *)e1; - VarDeclaration *v = se->var->isVarDeclaration(); - Expression *e = expandVar(result, v); - if (e && e->op == TOKstructliteral) - { StructLiteralExp *sle = (StructLiteralExp *)e; - e = sle->getField(type, (unsigned)se->offset); - if (e && e != EXP_CANT_INTERPRET) - return e; + void visit(UnaExp *e) + { + //printf("UnaExp::optimize() %s\n", e->toChars()); + e->e1 = e->e1->optimize(result); } - } - return this; -} -Expression *DotVarExp::optimize(int result, bool keepLvalue) -{ - //printf("DotVarExp::optimize(result = x%x) %s\n", result, toChars()); - e1 = e1->optimize(result); - if (keepLvalue) - return this; + void visit(NegExp *e) + { + e->e1 = e->e1->optimize(result); + if (e->e1->isConst() == 1) + { + ret = Neg(e->type, e->e1); + } + } - Expression *e = e1; + void visit(ComExp *e) + { + e->e1 = e->e1->optimize(result); + if (e->e1->isConst() == 1) + { + ret = Com(e->type, e->e1); + } + } - if (e1->op == TOKvar) - { VarExp *ve = (VarExp *)e1; - VarDeclaration *v = ve->var->isVarDeclaration(); - e = expandVar(result, v); - } + void visit(NotExp *e) + { + e->e1 = e->e1->optimize(result); + if (e->e1->isConst() == 1) + { + ret = Not(e->type, e->e1); + } + } - if (e && e->op == TOKstructliteral) - { StructLiteralExp *sle = (StructLiteralExp *)e; - VarDeclaration *vf = var->isVarDeclaration(); - if (vf) + void visit(BoolExp *e) { - e = sle->getField(type, vf->offset); - if (e && e != EXP_CANT_INTERPRET) - return e; + e->e1 = e->e1->optimize(result); + if (e->e1->isConst() == 1) + { + ret = Bool(e->type, e->e1); + } } - } - return this; -} + void visit(SymOffExp *e) + { + assert(e->var); + } -Expression *NewExp::optimize(int result, bool keepLvalue) -{ - if (thisexp) - thisexp = thisexp->optimize(WANTvalue); + void visit(AddrExp *e) + { + //printf("AddrExp::optimize(result = %d) %s\n", result, e->toChars()); - // Optimize parameters - if (newargs) - { - for (size_t i = 0; i < newargs->dim; i++) - { Expression *e = (*newargs)[i]; + /* Rewrite &(a,b) as (a,&b) + */ + if (e->e1->op == TOKcomma) + { + CommaExp *ce = (CommaExp *)e->e1; + AddrExp *ae = new AddrExp(e->loc, ce->e2); + ae->type = e->type; + ret = new CommaExp(ce->loc, ce->e1, ae); + ret->type = e->type; + ret = ret->optimize(result); + return; + } - e = e->optimize(WANTvalue); - (*newargs)[i] = e; - } - } + // Keep lvalue-ness + e->e1 = e->e1->optimize(result, true); - if (arguments) - { - for (size_t i = 0; i < arguments->dim; i++) - { Expression *e = (*arguments)[i]; + // Convert &*ex to ex + if (e->e1->op == TOKstar) + { + Expression *ex = ((PtrExp *)e->e1)->e1; + if (e->type->equals(ex->type)) + ret = ex; + else if (e->type->toBasetype()->immutableOf()->equals(ex->type->toBasetype()->immutableOf())) + { + ret = ex->copy(); + ret->type = e->type; + } + return; + } + if (e->e1->op == TOKvar) + { + VarExp *ve = (VarExp *)e->e1; + if (!ve->var->isOut() && !ve->var->isRef() && + !ve->var->isImportedSymbol()) + { + ret = new SymOffExp(e->loc, ve->var, 0, ve->hasOverloads); + ret->type = e->type; + return; + } + } + if (e->e1->op == TOKindex) + { + // Convert &array[n] to &array+n + IndexExp *ae = (IndexExp *)e->e1; - e = e->optimize(WANTvalue); - (*arguments)[i] = e; + if (ae->e2->op == TOKint64 && ae->e1->op == TOKvar) + { + sinteger_t index = ae->e2->toInteger(); + VarExp *ve = (VarExp *)ae->e1; + if (ve->type->ty == Tsarray + && !ve->var->isImportedSymbol()) + { + TypeSArray *ts = (TypeSArray *)ve->type; + sinteger_t dim = ts->dim->toInteger(); + if (index < 0 || index >= dim) + { + e->error("array index %lld is out of bounds [0..%lld]", index, dim); + ret = new ErrorExp(); + return; + } + ret = new SymOffExp(e->loc, ve->var, index * ts->nextOf()->size()); + ret->type = e->type; + return; + } + } + } } - } - return this; -} - -Expression *CallExp::optimize(int result, bool keepLvalue) -{ - //printf("CallExp::optimize(result = %d) %s\n", result, toChars()); - // Optimize parameters with keeping lvalue-ness - if (arguments) - { - Type *t1 = e1->type->toBasetype(); - if (t1->ty == Tdelegate) t1 = t1->nextOf(); - assert(t1->ty == Tfunction); - TypeFunction *tf = (TypeFunction *)t1; - size_t pdim = Parameter::dim(tf->parameters) - (tf->varargs == 2 ? 1 : 0); - for (size_t i = 0; i < arguments->dim; i++) + void visit(PtrExp *e) { - Parameter *p = Parameter::getNth(tf->parameters, i); - bool keep = (p ? (p->storageClass & (STCref | STCout)) != 0 : false); - Expression *e = (*arguments)[i]; - e = e->optimize(WANTvalue, keep); - (*arguments)[i] = e; - } - } + //printf("PtrExp::optimize(result = x%x) %s\n", result, e->toChars()); + e->e1 = e->e1->optimize(result); + // Convert *&ex to ex + // But only if there is no type punning involved + if (e->e1->op == TOKaddress) + { + Expression *ex = ((AddrExp *)e->e1)->e1; + if (e->type->equals(ex->type)) + ret = ex; + else if (e->type->toBasetype()->immutableOf()->equals(ex->type->toBasetype()->immutableOf())) + { + ret = ex->copy(); + ret->type = e->type; + } + } + if (keepLvalue) + return; - e1 = e1->optimize(result); - if (keepLvalue) - return this; + // Constant fold *(&structliteral + offset) + if (e->e1->op == TOKadd) + { + Expression *ex = Ptr(e->type, e->e1); + if (ex != EXP_CANT_INTERPRET) + { + ret = ex; + return; + } + } - Expression *e = this; -#if 1 -#else - if (e1->op == TOKvar) - { - FuncDeclaration *fd = ((VarExp *)e1)->var->isFuncDeclaration(); - if (fd) - { - BUILTIN b = fd->isBuiltin(); - if (b) + if (e->e1->op == TOKsymoff) { - e = eval_builtin(b, arguments); - if (!e) // failed - e = this; // evaluate at runtime + SymOffExp *se = (SymOffExp *)e->e1; + VarDeclaration *v = se->var->isVarDeclaration(); + Expression *ex = expandVar(result, v); + if (ex && ex->op == TOKstructliteral) + { + StructLiteralExp *sle = (StructLiteralExp *)ex; + ex = sle->getField(e->type, (unsigned)se->offset); + if (ex && ex != EXP_CANT_INTERPRET) + { + ret = ex; + return; + } + } } } - } -#endif - return e; -} + void visit(DotVarExp *e) + { + //printf("DotVarExp::optimize(result = x%x) %s\n", result, e->toChars()); + e->e1 = e->e1->optimize(result); + if (keepLvalue) + return; -Expression *CastExp::optimize(int result, bool keepLvalue) -{ - //printf("CastExp::optimize(result = %d) %s\n", result, toChars()); - //printf("from %s to %s\n", type->toChars(), to->toChars()); - //printf("from %s\n", type->toChars()); - //printf("e1->type %s\n", e1->type->toChars()); - //printf("type = %p\n", type); - assert(type); - TOK op1 = e1->op; -#define X 0 - - Expression *e1old = e1; - e1 = e1->optimize(result); - e1 = fromConstInitializer(result, e1); - - if (e1 == e1old && - e1->op == TOKarrayliteral && - type->toBasetype()->ty == Tpointer && - e1->type->toBasetype()->ty != Tsarray) - { - // Casting this will result in the same expression, and - // infinite loop because of Expression::implicitCastTo() - return this; // no change - } - - if ((e1->op == TOKstring || e1->op == TOKarrayliteral) && - (type->ty == Tpointer || type->ty == Tarray) && - e1->type->toBasetype()->nextOf()->size() == type->nextOf()->size() - ) - { - Expression *e = e1->castTo(NULL, type); - if (X) printf(" returning1 %s\n", e->toChars()); - return e; - } + Expression *ex = e->e1; - if (e1->op == TOKstructliteral && - e1->type->implicitConvTo(type) >= MATCHconst) - { - if (X) printf(" returning2 %s\n", e1->toChars()); - L1: // Returning e1 with changing its type - Expression *e = (e1old == e1 ? e1->copy() : e1); - e->type = type; - return e; - } + if (ex->op == TOKvar) + { + VarExp *ve = (VarExp *)ex; + VarDeclaration *v = ve->var->isVarDeclaration(); + ex = expandVar(result, v); + } - /* The first test here is to prevent infinite loops - */ - if (op1 != TOKarrayliteral && e1->op == TOKarrayliteral) - return e1->castTo(NULL, to); - if (e1->op == TOKnull && - (type->ty == Tpointer || type->ty == Tclass || type->ty == Tarray)) - { - if (X) printf(" returning3 %s\n", e1->toChars()); - goto L1; - } + if (ex && ex->op == TOKstructliteral) + { + StructLiteralExp *sle = (StructLiteralExp *)ex; + VarDeclaration *vf = e->var->isVarDeclaration(); + if (vf && !vf->overlapped) + { + /* Bugzilla 13021: Prevent optimization if vf has overlapped fields. + */ + ex = sle->getField(e->type, vf->offset); + if (ex && ex != EXP_CANT_INTERPRET) + { + ret = ex; + return; + } + } + } + } - if (result & WANTflags && type->ty == Tclass && e1->type->ty == Tclass) - { - // See if we can remove an unnecessary cast - ClassDeclaration *cdfrom; - ClassDeclaration *cdto; - int offset; - - cdfrom = e1->type->isClassHandle(); - cdto = type->isClassHandle(); - if (cdto->isBaseOf(cdfrom, &offset) && offset == 0) + void visit(NewExp *e) { - if (X) printf(" returning4 %s\n", e1->toChars()); - goto L1; - } - } + if (e->thisexp) + e->thisexp = e->thisexp->optimize(WANTvalue); - // We can convert 'head const' to mutable - if (to->mutableOf()->constOf()->equals(e1->type->mutableOf()->constOf())) - { - if (X) printf(" returning5 %s\n", e1->toChars()); - goto L1; - } + // Optimize parameters + if (e->newargs) + { + for (size_t i = 0; i < e->newargs->dim; i++) + { + Expression *arg = (*e->newargs)[i]; + arg = arg->optimize(WANTvalue); + (*e->newargs)[i] = arg; + } + } - Expression *e; + if (e->arguments) + { + for (size_t i = 0; i < e->arguments->dim; i++) + { + Expression *arg = (*e->arguments)[i]; + if (!arg) + continue; + arg = arg->optimize(WANTvalue); + (*e->arguments)[i] = arg; + } + } + } - if (e1->isConst()) - { - if (e1->op == TOKsymoff) + void visit(CallExp *e) { - if (type->size() == e1->type->size() && - type->toBasetype()->ty != Tsarray) + //printf("CallExp::optimize(result = %d) %s\n", result, e->toChars()); + + // Optimize parameters with keeping lvalue-ness + if (e->arguments) { - goto L1; + Type *t1 = e->e1->type->toBasetype(); + if (t1->ty == Tdelegate) t1 = t1->nextOf(); + assert(t1->ty == Tfunction); + TypeFunction *tf = (TypeFunction *)t1; + for (size_t i = 0; i < e->arguments->dim; i++) + { + Parameter *p = Parameter::getNth(tf->parameters, i); + bool keep = p && (p->storageClass & (STCref | STCout)) != 0; + Expression *arg = (*e->arguments)[i]; + arg = arg->optimize(WANTvalue, keep); + (*e->arguments)[i] = arg; + } } - return this; + + e->e1 = e->e1->optimize(result); } - if (to->toBasetype()->ty == Tvoid) - e = this; - else - e = Cast(type, to, e1); - } - else - e = this; - if (X) printf(" returning6 %s\n", e->toChars()); - return e; -#undef X -} -Expression *BinExp::optimize(int result, bool keepLvalue) -{ - //printf("BinExp::optimize(result = %d) %s\n", result, toChars()); - if (op != TOKconstruct && op != TOKblit) // don't replace const variable with its initializer - e1 = e1->optimize(result); - e2 = e2->optimize(result); - if (op == TOKshlass || op == TOKshrass || op == TOKushrass) - { - if (e2->isConst() == 1) + void visit(CastExp *e) { - sinteger_t i2 = e2->toInteger(); - d_uns64 sz = e1->type->size() * 8; - if (i2 < 0 || i2 >= sz) - { error("shift assign by %lld is outside the range 0..%llu", i2, (ulonglong)sz - 1); - return new ErrorExp(); + //printf("CastExp::optimize(result = %d) %s\n", result, e->toChars()); + //printf("from %s to %s\n", e->type->toChars(), e->to->toChars()); + //printf("from %s\n", e->type->toChars()); + //printf("e1->type %s\n", e->e1->type->toChars()); + //printf("type = %p\n", e->type); + assert(e->type); + TOK op1 = e->e1->op; + + Expression *e1old = e->e1; + e->e1 = e->e1->optimize(result); + e->e1 = fromConstInitializer(result, e->e1); + + if (e->e1 == e1old && + e->e1->op == TOKarrayliteral && + e->type->toBasetype()->ty == Tpointer && + e->e1->type->toBasetype()->ty != Tsarray) + { + // Casting this will result in the same expression, and + // infinite loop because of Expression::implicitCastTo() + return; // no change } - } - } - return this; -} -Expression *AddExp::optimize(int result, bool keepLvalue) -{ Expression *e; - - //printf("AddExp::optimize(%s)\n", toChars()); - e1 = e1->optimize(result); - e2 = e2->optimize(result); - if (e1->op == TOKerror) - return e1; - if (e2->op == TOKerror) - return e2; - if (e1->isConst() && e2->isConst()) - { - if (e1->op == TOKsymoff && e2->op == TOKsymoff) - return this; - e = Add(type, e1, e2); - } - else - e = this; - return e; -} + if ((e->e1->op == TOKstring || e->e1->op == TOKarrayliteral) && + (e->type->ty == Tpointer || e->type->ty == Tarray) && + e->e1->type->toBasetype()->nextOf()->size() == e->type->nextOf()->size()) + { + // Bugzilla 12937: If target type is void array, trying to paint + // e->e1 with that type will cause infinite recursive optimization. + if (e->type->nextOf()->ty == Tvoid) + return; + + ret = e->e1->castTo(NULL, e->type); + //printf(" returning1 %s\n", ret->toChars()); + return; + } -Expression *MinExp::optimize(int result, bool keepLvalue) -{ Expression *e; + if (e->e1->op == TOKstructliteral && + e->e1->type->implicitConvTo(e->type) >= MATCHconst) + { + //printf(" returning2 %s\n", e->e1->toChars()); + L1: // Returning e1 with changing its type + ret = (e1old == e->e1 ? e->e1->copy() : e->e1); + ret->type = e->type; + return; + } - e1 = e1->optimize(result); - e2 = e2->optimize(result); - if (e1->op == TOKerror) - return e1; - if (e2->op == TOKerror) - return e2; - if (e1->isConst() && e2->isConst()) - { - if (e2->op == TOKsymoff) - return this; - e = Min(type, e1, e2); - } - else - e = this; - return e; -} + /* The first test here is to prevent infinite loops + */ + if (op1 != TOKarrayliteral && e->e1->op == TOKarrayliteral) + { + ret = e->e1->castTo(NULL, e->to); + return; + } + if (e->e1->op == TOKnull && + (e->type->ty == Tpointer || e->type->ty == Tclass || e->type->ty == Tarray)) + { + //printf(" returning3 %s\n", e->e1->toChars()); + goto L1; + } -Expression *MulExp::optimize(int result, bool keepLvalue) -{ Expression *e; - - //printf("MulExp::optimize(result = %d) %s\n", result, toChars()); - e1 = e1->optimize(result); - e2 = e2->optimize(result); - if (e1->op == TOKerror) - return e1; - if (e2->op == TOKerror) - return e2; - if (e1->isConst() == 1 && e2->isConst() == 1) - { - e = Mul(type, e1, e2); - } - else - e = this; - return e; -} + if (result & WANTflags && e->type->ty == Tclass && e->e1->type->ty == Tclass) + { + // See if we can remove an unnecessary cast + ClassDeclaration *cdfrom = e->e1->type->isClassHandle(); + ClassDeclaration *cdto = e->type->isClassHandle(); + int offset; + if (cdto->isBaseOf(cdfrom, &offset) && offset == 0) + { + //printf(" returning4 %s\n", e->e1->toChars()); + goto L1; + } + } -Expression *DivExp::optimize(int result, bool keepLvalue) -{ Expression *e; - - //printf("DivExp::optimize(%s)\n", toChars()); - e1 = e1->optimize(result); - e2 = e2->optimize(result); - if (e1->op == TOKerror) - return e1; - if (e2->op == TOKerror) - return e2; - if (e1->isConst() == 1 && e2->isConst() == 1) - { - e = Div(type, e1, e2); - } - else - e = this; - return e; -} - -Expression *ModExp::optimize(int result, bool keepLvalue) -{ Expression *e; - - e1 = e1->optimize(result); - e2 = e2->optimize(result); - if (e1->op == TOKerror) - return e1; - if (e2->op == TOKerror) - return e2; - if (e1->isConst() == 1 && e2->isConst() == 1) - { - e = Mod(type, e1, e2); - } - else - e = this; - return e; -} + // We can convert 'head const' to mutable + if (e->to->mutableOf()->constOf()->equals(e->e1->type->mutableOf()->constOf())) + { + //printf(" returning5 %s\n", e->e1->toChars()); + goto L1; + } -Expression *shift_optimize(int result, BinExp *e, Expression *(*shift)(Type *, Expression *, Expression *)) -{ Expression *ex = e; + if (e->e1->isConst()) + { + if (e->e1->op == TOKsymoff) + { + if (e->type->size() == e->e1->type->size() && + e->type->toBasetype()->ty != Tsarray) + { + goto L1; + } + return; + } + if (e->to->toBasetype()->ty != Tvoid) + ret = Cast(e->type, e->to, e->e1); + } + //printf(" returning6 %s\n", ret->toChars()); + } - e->e1 = e->e1->optimize(result); - e->e2 = e->e2->optimize(result); - if (e->e1->op == TOKerror) - return e->e1; - if (e->e2->op == TOKerror) - return e->e2; - if (e->e2->isConst() == 1) - { - sinteger_t i2 = e->e2->toInteger(); - d_uns64 sz = e->e1->type->size() * 8; - if (i2 < 0 || i2 >= sz) - { e->error("shift by %lld is outside the range 0..%llu", i2, (ulonglong)sz - 1); - return new ErrorExp(); + void visit(BinExp *e) + { + //printf("BinExp::optimize(result = %d) %s\n", result, e->toChars()); + if (e->op != TOKconstruct && e->op != TOKblit) // don't replace const variable with its initializer + e->e1 = e->e1->optimize(result); + e->e2 = e->e2->optimize(result); + if (e->op == TOKshlass || e->op == TOKshrass || e->op == TOKushrass) + { + if (e->e2->isConst() == 1) + { + sinteger_t i2 = e->e2->toInteger(); + d_uns64 sz = e->e1->type->size() * 8; + if (i2 < 0 || i2 >= sz) + { + e->error("shift assign by %lld is outside the range 0..%llu", i2, (ulonglong)sz - 1); + ret = new ErrorExp(); + return; + } + } + } } - if (e->e1->isConst() == 1) - ex = (*shift)(e->type, e->e1, e->e2); - } - return ex; -} -Expression *ShlExp::optimize(int result, bool keepLvalue) -{ - //printf("ShlExp::optimize(result = %d) %s\n", result, toChars()); - return shift_optimize(result, this, &Shl); -} + void visit(AddExp *e) + { + //printf("AddExp::optimize(%s)\n", e->toChars()); + e->e1 = e->e1->optimize(result); + e->e2 = e->e2->optimize(result); + if (e->e1->op == TOKerror) + { + ret = e->e1; + return; + } + if (e->e2->op == TOKerror) + { + ret = e->e2; + return; + } + if (e->e1->isConst() && e->e2->isConst()) + { + if (e->e1->op == TOKsymoff && e->e2->op == TOKsymoff) + return; + ret = Add(e->type, e->e1, e->e2); + } + } -Expression *ShrExp::optimize(int result, bool keepLvalue) -{ - //printf("ShrExp::optimize(result = %d) %s\n", result, toChars()); - return shift_optimize(result, this, &Shr); -} + void visit(MinExp *e) + { + e->e1 = e->e1->optimize(result); + e->e2 = e->e2->optimize(result); + if (e->e1->op == TOKerror) + { + ret = e->e1; + return; + } + if (e->e2->op == TOKerror) + { + ret = e->e2; + return; + } + if (e->e1->isConst() && e->e2->isConst()) + { + if (e->e2->op == TOKsymoff) + return; + ret = Min(e->type, e->e1, e->e2); + } + } -Expression *UshrExp::optimize(int result, bool keepLvalue) -{ - //printf("UshrExp::optimize(result = %d) %s\n", result, toChars()); - return shift_optimize(result, this, &Ushr); -} + void visit(MulExp *e) + { + //printf("MulExp::optimize(result = %d) %s\n", result, e->toChars()); + e->e1 = e->e1->optimize(result); + e->e2 = e->e2->optimize(result); + if (e->e1->op == TOKerror) + { + ret = e->e1; + return; + } + if (e->e2->op == TOKerror) + { + ret = e->e2; + return; + } + if (e->e1->isConst() == 1 && e->e2->isConst() == 1) + { + ret = Mul(e->type, e->e1, e->e2); + } + } -Expression *AndExp::optimize(int result, bool keepLvalue) -{ Expression *e; - - e1 = e1->optimize(result); - e2 = e2->optimize(result); - if (e1->op == TOKerror) - return e1; - if (e2->op == TOKerror) - return e2; - if (e1->isConst() == 1 && e2->isConst() == 1) - e = And(type, e1, e2); - else - e = this; - return e; -} + void visit(DivExp *e) + { + //printf("DivExp::optimize(%s)\n", e->toChars()); + e->e1 = e->e1->optimize(result); + e->e2 = e->e2->optimize(result); + if (e->e1->op == TOKerror) + { + ret = e->e1; + return; + } + if (e->e2->op == TOKerror) + { + ret = e->e2; + return; + } + if (e->e1->isConst() == 1 && e->e2->isConst() == 1) + { + ret = Div(e->type, e->e1, e->e2); + } + } -Expression *OrExp::optimize(int result, bool keepLvalue) -{ Expression *e; - - e1 = e1->optimize(result); - e2 = e2->optimize(result); - if (e1->op == TOKerror) - return e1; - if (e2->op == TOKerror) - return e2; - if (e1->isConst() == 1 && e2->isConst() == 1) - e = Or(type, e1, e2); - else - e = this; - return e; -} + void visit(ModExp *e) + { + e->e1 = e->e1->optimize(result); + e->e2 = e->e2->optimize(result); + if (e->e1->op == TOKerror) + { + ret = e->e1; + return; + } + if (e->e2->op == TOKerror) + { + ret = e->e2; + return; + } + if (e->e1->isConst() == 1 && e->e2->isConst() == 1) + { + ret = Mod(e->type, e->e1, e->e2); + } + } -Expression *XorExp::optimize(int result, bool keepLvalue) -{ Expression *e; - - e1 = e1->optimize(result); - e2 = e2->optimize(result); - if (e1->op == TOKerror) - return e1; - if (e2->op == TOKerror) - return e2; - if (e1->isConst() == 1 && e2->isConst() == 1) - e = Xor(type, e1, e2); - else - e = this; - return e; -} + void shift_optimize(BinExp *e, Expression *(*shift)(Type *, Expression *, Expression *)) + { + e->e1 = e->e1->optimize(result); + e->e2 = e->e2->optimize(result); + if (e->e1->op == TOKerror) + { + ret = e->e1; + return; + } + if (e->e2->op == TOKerror) + { + ret = e->e2; + return; + } + if (e->e2->isConst() == 1) + { + sinteger_t i2 = e->e2->toInteger(); + d_uns64 sz = e->e1->type->size() * 8; + if (i2 < 0 || i2 >= sz) + { + e->error("shift by %lld is outside the range 0..%llu", i2, (ulonglong)sz - 1); + ret = new ErrorExp(); + return; + } + if (e->e1->isConst() == 1) + ret = (*shift)(e->type, e->e1, e->e2); + } + } -Expression *PowExp::optimize(int result, bool keepLvalue) -{ Expression *e; + void visit(ShlExp *e) + { + //printf("ShlExp::optimize(result = %d) %s\n", result, e->toChars()); + shift_optimize(e, &Shl); + } - e1 = e1->optimize(result); - e2 = e2->optimize(result); - if (e1->op == TOKerror) - return e1; - if (e2->op == TOKerror) - return e2; + void visit(ShrExp *e) + { + //printf("ShrExp::optimize(result = %d) %s\n", result, e->toChars()); + shift_optimize(e, &Shr); + } - // Replace 1 ^^ x or 1.0^^x by (x, 1) - if ((e1->op == TOKint64 && e1->toInteger() == 1) || - (e1->op == TOKfloat64 && e1->toReal() == 1.0)) - { - e = new CommaExp(loc, e2, e1); - } - // Replace -1 ^^ x by (x&1) ? -1 : 1, where x is integral - else if (e2->type->isintegral() && e1->op == TOKint64 && (sinteger_t)e1->toInteger() == -1L) - { - Type* resultType = type; - e = new AndExp(loc, e2, new IntegerExp(loc, 1, e2->type)); - e = new CondExp(loc, e, new IntegerExp(loc, -1L, resultType), new IntegerExp(loc, 1L, resultType)); - } - // Replace x ^^ 0 or x^^0.0 by (x, 1) - else if ((e2->op == TOKint64 && e2->toInteger() == 0) || - (e2->op == TOKfloat64 && e2->toReal() == 0.0)) - { - if (e1->type->isintegral()) - e = new IntegerExp(loc, 1, e1->type); - else - e = new RealExp(loc, ldouble(1.0), e1->type); + void visit(UshrExp *e) + { + //printf("UshrExp::optimize(result = %d) %s\n", result, toChars()); + shift_optimize(e, &Ushr); + } - e = new CommaExp(loc, e1, e); - } - // Replace x ^^ 1 or x^^1.0 by (x) - else if ((e2->op == TOKint64 && e2->toInteger() == 1) || - (e2->op == TOKfloat64 && e2->toReal() == 1.0)) - { - e = e1; - } - // Replace x ^^ -1.0 by (1.0 / x) - else if ((e2->op == TOKfloat64 && e2->toReal() == -1.0)) - { - e = new DivExp(loc, new RealExp(loc, ldouble(1.0), e2->type), e1); - } - // All other negative integral powers are illegal - else if ((e1->type->isintegral()) && (e2->op == TOKint64) && (sinteger_t)e2->toInteger() < 0) - { - error("cannot raise %s to a negative integer power. Did you mean (cast(real)%s)^^%s ?", - e1->type->toBasetype()->toChars(), e1->toChars(), e2->toChars()); - e = new ErrorExp(); - } - else - { - // If e2 *could* have been an integer, make it one. - if (e2->op == TOKfloat64 && (e2->toReal() == (sinteger_t)(e2->toReal()))) - e2 = new IntegerExp(loc, e2->toInteger(), Type::tint64); + void visit(AndExp *e) + { + e->e1 = e->e1->optimize(result); + e->e2 = e->e2->optimize(result); + if (e->e1->op == TOKerror) + { + ret = e->e1; + return; + } + if (e->e2->op == TOKerror) + { + ret = e->e2; + return; + } + if (e->e1->isConst() == 1 && e->e2->isConst() == 1) + ret = And(e->type, e->e1, e->e2); + } - if (e1->isConst() == 1 && e2->isConst() == 1) + void visit(OrExp *e) { - e = Pow(type, e1, e2); - if (e != EXP_CANT_INTERPRET) - return e; + e->e1 = e->e1->optimize(result); + e->e2 = e->e2->optimize(result); + if (e->e1->op == TOKerror) + { + ret = e->e1; + return; + } + if (e->e2->op == TOKerror) + { + ret = e->e2; + return; + } + if (e->e1->isConst() == 1 && e->e2->isConst() == 1) + ret = Or(e->type, e->e1, e->e2); } - e = this; - } - if (e1->op == TOKint64 && e1->toInteger() > 0 && - !((e1->toInteger() - 1) & e1->toInteger()) && // is power of two - e2->type->isintegral() && e2->type->isunsigned()) - { - dinteger_t i = e1->toInteger(); - dinteger_t mul = 1; - while ((i >>= 1) > 1) - mul++; - Expression *shift = new MulExp(loc, e2, new IntegerExp(loc, mul, e2->type)); - shift->type = e2->type; - shift = shift->castTo(NULL, Type::tshiftcnt); - e = new ShlExp(loc, new IntegerExp(loc, 1, e1->type), shift); - e->type = type; - } + void visit(XorExp *e) + { + e->e1 = e->e1->optimize(result); + e->e2 = e->e2->optimize(result); + if (e->e1->op == TOKerror) + { + ret = e->e1; + return; + } + if (e->e2->op == TOKerror) + { + ret = e->e2; + return; + } + if (e->e1->isConst() == 1 && e->e2->isConst() == 1) + ret = Xor(e->type, e->e1, e->e2); + } - return e; -} + void visit(PowExp *e) + { + e->e1 = e->e1->optimize(result); + e->e2 = e->e2->optimize(result); + if (e->e1->op == TOKerror) + { + ret = e->e1; + return; + } + if (e->e2->op == TOKerror) + { + ret = e->e2; + return; + } -Expression *CommaExp::optimize(int result, bool keepLvalue) -{ Expression *e; - - //printf("CommaExp::optimize(result = %d) %s\n", result, toChars()); - // Comma needs special treatment, because it may - // contain compiler-generated declarations. We can interpret them, but - // otherwise we must NOT attempt to constant-fold them. - // In particular, if the comma returns a temporary variable, it needs - // to be an lvalue (this is particularly important for struct constructors) - - e1 = e1->optimize(0); - e2 = e2->optimize(result, keepLvalue); - if (e1->op == TOKerror) - return e1; - if (!e1 || e1->op == TOKint64 || e1->op == TOKfloat64 || !e1->hasSideEffect()) - { - e = e2; - if (e) - e->type = type; - } - else - e = this; - //printf("-CommaExp::optimize(result = %d) %s\n", result, e->toChars()); - return e; -} + // Replace 1 ^^ x or 1.0^^x by (x, 1) + if ((e->e1->op == TOKint64 && e->e1->toInteger() == 1) || + (e->e1->op == TOKfloat64 && e->e1->toReal() == 1.0)) + { + ret = new CommaExp(e->loc, e->e2, e->e1); + return; + } -Expression *ArrayLengthExp::optimize(int result, bool keepLvalue) -{ Expression *e; + // Replace -1 ^^ x by (x&1) ? -1 : 1, where x is integral + if (e->e2->type->isintegral() && e->e1->op == TOKint64 && (sinteger_t)e->e1->toInteger() == -1L) + { + Type* resultType = e->type; + ret = new AndExp(e->loc, e->e2, new IntegerExp(e->loc, 1, e->e2->type)); + ret = new CondExp(e->loc, ret, new IntegerExp(e->loc, -1L, resultType), new IntegerExp(e->loc, 1L, resultType)); + return; + } - //printf("ArrayLengthExp::optimize(result = %d) %s\n", result, toChars()); - e1 = e1->optimize(WANTvalue | WANTexpand); - if (e1->op == TOKerror) - return e1; - e = this; - if (e1->op == TOKstring || e1->op == TOKarrayliteral || e1->op == TOKassocarrayliteral || - e1->type->toBasetype()->ty == Tsarray) - { - e = ArrayLength(type, e1); - } - return e; -} + // Replace x ^^ 0 or x^^0.0 by (x, 1) + if ((e->e2->op == TOKint64 && e->e2->toInteger() == 0) || + (e->e2->op == TOKfloat64 && e->e2->toReal() == 0.0)) + { + if (e->e1->type->isintegral()) + ret = new IntegerExp(e->loc, 1, e->e1->type); + else + ret = new RealExp(e->loc, ldouble(1.0), e->e1->type); -Expression *EqualExp::optimize(int result, bool keepLvalue) -{ - //printf("EqualExp::optimize(result = %x) %s\n", result, toChars()); - e1 = e1->optimize(WANTvalue); - e2 = e2->optimize(WANTvalue); - - Expression *e1 = fromConstInitializer(result, this->e1); - Expression *e2 = fromConstInitializer(result, this->e2); - if (e1->op == TOKerror) - return e1; - if (e2->op == TOKerror) - return e2; - - Expression *e = Equal(op, type, e1, e2); - if (e == EXP_CANT_INTERPRET) - e = this; - return e; -} + ret = new CommaExp(e->loc, e->e1, ret); + return; + } -Expression *IdentityExp::optimize(int result, bool keepLvalue) -{ - //printf("IdentityExp::optimize(result = %d) %s\n", result, toChars()); - e1 = e1->optimize(WANTvalue); - e2 = e2->optimize(WANTvalue); + // Replace x ^^ 1 or x^^1.0 by (x) + if ((e->e2->op == TOKint64 && e->e2->toInteger() == 1) || + (e->e2->op == TOKfloat64 && e->e2->toReal() == 1.0)) + { + ret = e->e1; + return; + } - if (e1->op == TOKerror) - return e1; - if (e2->op == TOKerror) - return e2; + // Replace x ^^ -1.0 by (1.0 / x) + if ((e->e2->op == TOKfloat64 && e->e2->toReal() == -1.0)) + { + ret = new DivExp(e->loc, new RealExp(e->loc, ldouble(1.0), e->e2->type), e->e1); + return; + } - Expression *e = this; + // All other negative integral powers are illegal + if ((e->e1->type->isintegral()) && (e->e2->op == TOKint64) && (sinteger_t)e->e2->toInteger() < 0) + { + e->error("cannot raise %s to a negative integer power. Did you mean (cast(real)%s)^^%s ?", + e->e1->type->toBasetype()->toChars(), e->e1->toChars(), e->e2->toChars()); + ret = new ErrorExp(); + return; + } - if ((this->e1->isConst() && this->e2->isConst()) || - (this->e1->op == TOKnull && this->e2->op == TOKnull) - ) - { - e = Identity(op, type, this->e1, this->e2); - if (e == EXP_CANT_INTERPRET) - e = this; - } - return e; -} + // If e2 *could* have been an integer, make it one. + if (e->e2->op == TOKfloat64 && (e->e2->toReal() == (sinteger_t)(e->e2->toReal()))) + e->e2 = new IntegerExp(e->loc, e->e2->toInteger(), Type::tint64); -/* It is possible for constant folding to change an array expression of - * unknown length, into one where the length is known. - * If the expression 'arr' is a literal, set lengthVar to be its length. - */ -void setLengthVarIfKnown(VarDeclaration *lengthVar, Expression *arr) -{ - if (!lengthVar) - return; - if (lengthVar->init && !lengthVar->init->isVoidInitializer()) - return; // we have previously calculated the length - size_t len; - if (arr->op == TOKstring) - len = ((StringExp *)arr)->len; - else if (arr->op == TOKarrayliteral) - len = ((ArrayLiteralExp *)arr)->elements->dim; - else - { - Type *t = arr->type->toBasetype(); - if (t->ty == Tsarray) - len = (size_t)((TypeSArray *)t)->dim->toInteger(); - else - return; // we don't know the length yet - } + if (e->e1->isConst() == 1 && e->e2->isConst() == 1) + { + Expression *ex = Pow(e->type, e->e1, e->e2); + if (ex != EXP_CANT_INTERPRET) + { + ret = ex; + return; + } + } - Expression *dollar = new IntegerExp(Loc(), len, Type::tsize_t); - lengthVar->init = new ExpInitializer(Loc(), dollar); - lengthVar->storage_class |= STCstatic | STCconst; -} + // (2 ^^ n) ^^ p -> 1 << n * p + if (e->e1->op == TOKint64 && e->e1->toInteger() > 0 && + !((e->e1->toInteger() - 1) & e->e1->toInteger()) && + e->e2->type->isintegral() && e->e2->type->isunsigned()) + { + dinteger_t i = e->e1->toInteger(); + dinteger_t mul = 1; + while ((i >>= 1) > 1) + mul++; + Expression *shift = new MulExp(e->loc, e->e2, new IntegerExp(e->loc, mul, e->e2->type)); + shift->type = e->e2->type; + shift = shift->castTo(NULL, Type::tshiftcnt); + ret = new ShlExp(e->loc, new IntegerExp(e->loc, 1, e->e1->type), shift); + ret->type = e->type; + return; + } + } + void visit(CommaExp *e) + { + //printf("CommaExp::optimize(result = %d) %s\n", result, e->toChars()); + // Comma needs special treatment, because it may + // contain compiler-generated declarations. We can interpret them, but + // otherwise we must NOT attempt to constant-fold them. + // In particular, if the comma returns a temporary variable, it needs + // to be an lvalue (this is particularly important for struct constructors) + + e->e1 = e->e1->optimize(0); + e->e2 = e->e2->optimize(result, keepLvalue); + if (e->e1->op == TOKerror) + { + ret = e->e1; + return; + } + if (!e->e1 || e->e1->op == TOKint64 || e->e1->op == TOKfloat64 || !hasSideEffect(e->e1)) + { + ret = e->e2; + if (ret) + ret->type = e->type; + } -Expression *IndexExp::optimize(int result, bool keepLvalue) -{ Expression *e; + //printf("-CommaExp::optimize(result = %d) %s\n", result, e->e->toChars()); + } - //printf("IndexExp::optimize(result = %d) %s\n", result, toChars()); - e1 = e1->optimize(WANTvalue | (result & WANTexpand)); + void visit(ArrayLengthExp *e) + { + //printf("ArrayLengthExp::optimize(result = %d) %s\n", result, e->toChars()); + e->e1 = e->e1->optimize(WANTvalue | WANTexpand); + if (e->e1->op == TOKerror) + { + ret = e->e1; + return; + } - Expression *ex = fromConstInitializer(result, e1); + // CTFE interpret static immutable arrays (to get better diagnostics) + if (e->e1->op == TOKvar) + { + VarDeclaration *v = ((VarExp *)e->e1)->var->isVarDeclaration(); + if (v && (v->storage_class & STCstatic) && (v->storage_class & STCimmutable) && v->init) + { + if (Expression *ci = v->getConstInitializer()) + e->e1 = ci; + } + } - // We might know $ now - setLengthVarIfKnown(lengthVar, ex); - e2 = e2->optimize(WANTvalue); - if (keepLvalue) - return this; - e = Index(type, ex, e2); - if (e == EXP_CANT_INTERPRET) - e = this; - return e; -} + if (e->e1->op == TOKstring || e->e1->op == TOKarrayliteral || e->e1->op == TOKassocarrayliteral || + e->e1->type->toBasetype()->ty == Tsarray) + { + ret = ArrayLength(e->type, e->e1); + } + } + void visit(EqualExp *e) + { + //printf("EqualExp::optimize(result = %x) %s\n", result, e->toChars()); + e->e1 = e->e1->optimize(WANTvalue); + e->e2 = e->e2->optimize(WANTvalue); -Expression *SliceExp::optimize(int result, bool keepLvalue) -{ Expression *e; + Expression *e1 = fromConstInitializer(result, e->e1); + Expression *e2 = fromConstInitializer(result, e->e2); + if (e1->op == TOKerror) + { + ret = e1; + return; + } + if (e2->op == TOKerror) + { + ret = e2; + return; + } - //printf("SliceExp::optimize(result = %d) %s\n", result, toChars()); - e = this; - e1 = e1->optimize(WANTvalue | (result & WANTexpand)); - if (!lwr) - { if (e1->op == TOKstring) - { // Convert slice of string literal into dynamic array - Type *t = e1->type->toBasetype(); - if (t->nextOf()) - e = e1->castTo(NULL, t->nextOf()->arrayOf()); + ret = Equal(e->op, e->type, e->e1, e->e2); + if (ret == EXP_CANT_INTERPRET) + ret = e; } - return e; - } - e1 = fromConstInitializer(result, e1); - // We might know $ now - setLengthVarIfKnown(lengthVar, e1); - lwr = lwr->optimize(WANTvalue); - upr = upr->optimize(WANTvalue); - e = Slice(type, e1, lwr, upr); - if (e == EXP_CANT_INTERPRET) - e = this; - //printf("-SliceExp::optimize() %s\n", e->toChars()); - return e; -} - -Expression *AndAndExp::optimize(int result, bool keepLvalue) -{ Expression *e; - //printf("AndAndExp::optimize(%d) %s\n", result, toChars()); - e1 = e1->optimize(WANTflags); - if (e1->op == TOKerror) - return e1; - e = this; - if (e1->isBool(false)) - { - if (type->toBasetype()->ty == Tvoid) - e = e2; - else - { e = new CommaExp(loc, e1, new IntegerExp(loc, 0, type)); - e->type = type; - } - e = e->optimize(result); - } - else - { - e2 = e2->optimize(WANTflags); - if (result && e2->type->toBasetype()->ty == Tvoid && !global.errors) - { - error("void has no value"); - return new ErrorExp(); - } - if (e1->isConst()) + void visit(IdentityExp *e) { - if (e2->isConst()) - { int n1 = e1->isBool(1); - int n2 = e2->isBool(1); + //printf("IdentityExp::optimize(result = %d) %s\n", result, e->toChars()); + e->e1 = e->e1->optimize(WANTvalue); + e->e2 = e->e2->optimize(WANTvalue); + + if (e->e1->op == TOKerror) + { + ret = e->e1; + return; + } + if (e->e2->op == TOKerror) + { + ret = e->e2; + return; + } - e = new IntegerExp(loc, n1 && n2, type); + if ((e->e1->isConst() && e->e2->isConst()) || + (e->e1->op == TOKnull && e->e2->op == TOKnull) + ) + { + ret = Identity(e->op, e->type, e->e1, e->e2); + if (ret == EXP_CANT_INTERPRET) + ret = e; } - else if (e1->isBool(true)) + } + + /* It is possible for constant folding to change an array expression of + * unknown length, into one where the length is known. + * If the expression 'arr' is a literal, set lengthVar to be its length. + */ + static void setLengthVarIfKnown(VarDeclaration *lengthVar, Expression *arr) + { + if (!lengthVar) + return; + if (lengthVar->init && !lengthVar->init->isVoidInitializer()) + return; // we have previously calculated the length + size_t len; + if (arr->op == TOKstring) + len = ((StringExp *)arr)->len; + else if (arr->op == TOKarrayliteral) + len = ((ArrayLiteralExp *)arr)->elements->dim; + else { - if (type->toBasetype()->ty == Tvoid) - e = e2; - else e = new BoolExp(loc, e2, type); + Type *t = arr->type->toBasetype(); + if (t->ty == Tsarray) + len = (size_t)((TypeSArray *)t)->dim->toInteger(); + else + return; // we don't know the length yet } + + Expression *dollar = new IntegerExp(Loc(), len, Type::tsize_t); + lengthVar->init = new ExpInitializer(Loc(), dollar); + lengthVar->storage_class |= STCstatic | STCconst; } - } - return e; -} -Expression *OrOrExp::optimize(int result, bool keepLvalue) -{ Expression *e; - - e1 = e1->optimize(WANTflags); - if (e1->op == TOKerror) - return e1; - e = this; - if (e1->isBool(true)) - { // Replace with (e1, 1) - e = new CommaExp(loc, e1, new IntegerExp(loc, 1, type)); - e->type = type; - e = e->optimize(result); - } - else - { - e2 = e2->optimize(WANTflags); - if (result && e2->type->toBasetype()->ty == Tvoid && !global.errors) + void visit(IndexExp *e) { - error("void has no value"); - return new ErrorExp(); + //printf("IndexExp::optimize(result = %d) %s\n", result, e->toChars()); + e->e1 = e->e1->optimize(WANTvalue | (result & WANTexpand)); + + Expression *ex = fromConstInitializer(result, e->e1); + + // We might know $ now + setLengthVarIfKnown(e->lengthVar, ex); + e->e2 = e->e2->optimize(WANTvalue); + if (keepLvalue) + return; + ret = Index(e->type, ex, e->e2); + if (ret == EXP_CANT_INTERPRET) + ret = e; } - if (e1->isConst()) + + void visit(SliceExp *e) { - if (e2->isConst()) - { int n1 = e1->isBool(1); - int n2 = e2->isBool(1); + //printf("SliceExp::optimize(result = %d) %s\n", result, e->toChars()); + e->e1 = e->e1->optimize(WANTvalue | (result & WANTexpand)); + if (!e->lwr) + { + if (e->e1->op == TOKstring) + { + // Convert slice of string literal into dynamic array + Type *t = e->e1->type->toBasetype(); + if (t->nextOf()) + ret = e->e1->castTo(NULL, t->nextOf()->arrayOf()); + } + return; + } + e->e1 = fromConstInitializer(result, e->e1); + // We might know $ now + setLengthVarIfKnown(e->lengthVar, e->e1); + e->lwr = e->lwr->optimize(WANTvalue); + e->upr = e->upr->optimize(WANTvalue); + ret = Slice(e->type, e->e1, e->lwr, e->upr); + if (ret == EXP_CANT_INTERPRET) + ret = e; + //printf("-SliceExp::optimize() %s\n", ret->toChars()); + } - e = new IntegerExp(loc, n1 || n2, type); + void visit(AndAndExp *e) + { + //printf("AndAndExp::optimize(%d) %s\n", result, e->toChars()); + e->e1 = e->e1->optimize(WANTflags); + if (e->e1->op == TOKerror) + { + ret = e->e1; + return; } - else if (e1->isBool(false)) + if (e->e1->isBool(false)) { - if (type->toBasetype()->ty == Tvoid) - e = e2; + if (e->type->toBasetype()->ty == Tvoid) + ret = e->e2; else - e = new BoolExp(loc, e2, type); + { + ret = new CommaExp(e->loc, e->e1, new IntegerExp(e->loc, 0, e->type)); + ret->type = e->type; + } + ret = ret->optimize(result); + return; } - } - } - return e; -} -Expression *CmpExp::optimize(int result, bool keepLvalue) -{ Expression *e; + e->e2 = e->e2->optimize(WANTflags); + if (result && e->e2->type->toBasetype()->ty == Tvoid && !global.errors) + { + e->error("void has no value"); + ret = new ErrorExp(); + return; + } - //printf("CmpExp::optimize() %s\n", toChars()); - e1 = e1->optimize(WANTvalue); - e2 = e2->optimize(WANTvalue); + if (e->e1->isConst()) + { + if (e->e2->isConst()) + { + int n1 = e->e1->isBool(1); + int n2 = e->e2->isBool(1); + ret = new IntegerExp(e->loc, n1 && n2, e->type); + } + else if (e->e1->isBool(true)) + { + if (e->type->toBasetype()->ty == Tvoid) + ret = e->e2; + else + ret = new BoolExp(e->loc, e->e2, e->type); + } + } + } - Expression *e1 = fromConstInitializer(result, this->e1); - Expression *e2 = fromConstInitializer(result, this->e2); + void visit(OrOrExp *e) + { + e->e1 = e->e1->optimize(WANTflags); + if (e->e1->op == TOKerror) + { + ret = e->e1; + return; + } + if (e->e1->isBool(true)) + { + // Replace with (e1, 1) + ret = new CommaExp(e->loc, e->e1, new IntegerExp(e->loc, 1, e->type)); + ret->type = e->type; + ret = ret->optimize(result); + return; + } + e->e2 = e->e2->optimize(WANTflags); + if (result && e->e2->type->toBasetype()->ty == Tvoid && !global.errors) + { + e->error("void has no value"); + ret = new ErrorExp(); + return; + } + if (e->e1->isConst()) + { + if (e->e2->isConst()) + { + int n1 = e->e1->isBool(1); + int n2 = e->e2->isBool(1); + ret = new IntegerExp(e->loc, n1 || n2, e->type); + } + else if (e->e1->isBool(false)) + { + if (e->type->toBasetype()->ty == Tvoid) + ret = e->e2; + else + ret = new BoolExp(e->loc, e->e2, e->type); + } + } + } - e = Cmp(op, type, e1, e2); - if (e == EXP_CANT_INTERPRET) - e = this; - return e; -} + void visit(CmpExp *e) + { + //printf("CmpExp::optimize() %s\n", e->toChars()); + e->e1 = e->e1->optimize(WANTvalue); + e->e2 = e->e2->optimize(WANTvalue); -Expression *CatExp::optimize(int result, bool keepLvalue) -{ Expression *e; + Expression *e1 = fromConstInitializer(result, e->e1); + Expression *e2 = fromConstInitializer(result, e->e2); - //printf("CatExp::optimize(%d) %s\n", result, toChars()); - e1 = e1->optimize(result); - e2 = e2->optimize(result); - e = Cat(type, e1, e2); - if (e == EXP_CANT_INTERPRET) - e = this; - return e; -} + ret = Cmp(e->op, e->type, e1, e2); + if (ret == EXP_CANT_INTERPRET) + ret = e; + } + void visit(CatExp *e) + { + //printf("CatExp::optimize(%d) %s\n", result, e->toChars()); + e->e1 = e->e1->optimize(result); + e->e2 = e->e2->optimize(result); -Expression *CondExp::optimize(int result, bool keepLvalue) -{ Expression *e; + if (e->e1->op == TOKcat) + { + // Bugzilla 12798: optimize ((expr ~ str1) ~ str2) + CatExp *ce1 = (CatExp *)e->e1; + CatExp cex(e->loc, ce1->e2, e->e2); + cex.type = e->type; + Expression *ex = cex.optimize(result); + if (ex != &cex) + { + e->e1 = ce1->e1; + e->e2 = ex; + } + } - econd = econd->optimize(WANTflags); - if (econd->isBool(true)) - e = e1->optimize(result, keepLvalue); - else if (econd->isBool(false)) - e = e2->optimize(result, keepLvalue); - else - { e1 = e1->optimize(result, keepLvalue); - e2 = e2->optimize(result, keepLvalue); - e = this; - } - return e; -} + ret = Cat(e->type, e->e1, e->e2); + if (ret == EXP_CANT_INTERPRET) + ret = e; + } + void visit(CondExp *e) + { + e->econd = e->econd->optimize(WANTflags); + if (e->econd->isBool(true)) + ret = e->e1->optimize(result, keepLvalue); + else if (e->econd->isBool(false)) + ret = e->e2->optimize(result, keepLvalue); + else + { + e->e1 = e->e1->optimize(result, keepLvalue); + e->e2 = e->e2->optimize(result, keepLvalue); + } + } + }; + OptimizeVisitor v(result, keepLvalue); + v.ret = e; + e->accept(&v); + return v.ret; +} diff --git a/gcc/d/dfrontend/outbuffer.c b/gcc/d/dfrontend/outbuffer.c index 279a77cd0..1086304d6 100644 --- a/gcc/d/dfrontend/outbuffer.c +++ b/gcc/d/dfrontend/outbuffer.c @@ -1,11 +1,11 @@ -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/outbuffer.c + */ #include #include @@ -13,6 +13,10 @@ #include #include +#if __sun +#include +#endif + #include "outbuffer.h" #include "object.h" #include "rmem.h" @@ -306,17 +310,16 @@ void OutBuffer::align(size_t size) void OutBuffer::vprintf(const char *format, va_list args) { - char buffer[128]; - char *p; - unsigned psize; int count; - p = buffer; - psize = sizeof(buffer); + if (doindent) + write(NULL, 0); // perform indent + unsigned psize = 128; for (;;) { + reserve(psize); #if _WIN32 - count = _vsnprintf(p,psize,format,args); + count = _vsnprintf((char *)data + offset,psize,format,args); if (count != -1) break; psize *= 2; @@ -332,7 +335,7 @@ void OutBuffer::vprintf(const char *format, va_list args) of ap is undefined after the call. The application should call va_end(ap) itself afterwards. */ - count = vsnprintf(p,psize,format,va); + count = vsnprintf((char *)data + offset,psize,format,va); va_end(va); if (count == -1) psize *= 2; @@ -341,11 +344,10 @@ void OutBuffer::vprintf(const char *format, va_list args) else break; #else - assert(0); + assert(0); #endif - p = (char *) alloca(psize); // buffer too small, try again with larger size } - write(p,count); + offset += count; } void OutBuffer::printf(const char *format, ...) @@ -405,8 +407,16 @@ void OutBuffer::remove(size_t offset, size_t nbytes) this->offset -= nbytes; } -char *OutBuffer::toChars() +char *OutBuffer::peekString() { - writeByte(0); + if (!offset || data[offset-1] != '\0') + writeByte(0); return (char *)data; } + +char *OutBuffer::extractString() +{ + if (!offset || data[offset-1] != '\0') + writeByte(0); + return extractData(); +} diff --git a/gcc/d/dfrontend/outbuffer.h b/gcc/d/dfrontend/outbuffer.h index fddc04242..9a8405646 100644 --- a/gcc/d/dfrontend/outbuffer.h +++ b/gcc/d/dfrontend/outbuffer.h @@ -1,11 +1,11 @@ -// Copyright (c) 1999-2011 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/outbuffer.h + */ #ifndef OUTBUFFER_H #define OUTBUFFER_H @@ -45,7 +45,6 @@ struct OutBuffer void prependstring(const char *string); void writenl(); // write newline void writeByte(unsigned b); - void writebyte(unsigned b) { writeByte(b); } void writeUTF8(unsigned b); void prependbyte(unsigned b); void writewchar(unsigned w); @@ -63,7 +62,9 @@ struct OutBuffer void spread(size_t offset, size_t nbytes); size_t insert(size_t offset, const void *data, size_t nbytes); void remove(size_t offset, size_t nbytes); - char *toChars(); + // Append terminating null if necessary and get view of internal buffer + char *peekString(); + // Append terminating null if necessary and take ownership of data char *extractString(); }; diff --git a/gcc/d/dfrontend/parse.c b/gcc/d/dfrontend/parse.c index 23a7d8e0e..c1d9deb38 100644 --- a/gcc/d/dfrontend/parse.c +++ b/gcc/d/dfrontend/parse.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/parse.c + */ // This is the D parser @@ -34,6 +35,7 @@ #include "id.h" #include "version.h" #include "aliasthis.h" +#include "nspace.h" // How multiple declarations are parsed. // If 1, treat as C. @@ -52,9 +54,6 @@ // int a[3][4]; #define CARRAYDECL 1 -// Support D1 inout -#define D1INOUT 0 - Parser::Parser(Module *module, const utf8_t *base, size_t length, int doDocComment) : Lexer(module, base, 0, length, doDocComment, 0) { @@ -78,6 +77,7 @@ Parser::Parser(Loc loc, Module *module, const utf8_t *base, size_t length, int d //printf("Parser::Parser()\n"); scanloc = loc; +#ifndef IN_GCC if (loc.filename) { /* Create a pseudo-filename for the mixin string, as it may not even exist @@ -87,6 +87,7 @@ Parser::Parser(Loc loc, Module *module, const utf8_t *base, size_t length, int d sprintf(filename, "%s-mixin-%d", loc.filename, (int)loc.linnum); scanloc.filename = filename; } +#endif md = NULL; linkage = LINKd; @@ -99,6 +100,25 @@ Parser::Parser(Loc loc, Module *module, const utf8_t *base, size_t length, int d Dsymbols *Parser::parseModule() { Dsymbols *decldefs; + bool isdeprecated = false; + Expression *msg = NULL; + + if (token.value == TOKdeprecated) + { + Token *tk; + if (skipParensIf(peek(&token), &tk) && tk->value == TOKmodule) + { + // deprecated (...) module ... + isdeprecated = true; + nextToken(); + if (token.value == TOKlparen) + { + check(TOKlparen); + msg = parseAssignExp(); + check(TOKrparen); + } + } + } // ModuleDeclation leads off if (token.value == TOKmodule) @@ -113,7 +133,8 @@ Dsymbols *Parser::parseModule() { nextToken(); if (token.value != TOKidentifier) - { error("module (system) identifier expected"); + { + error("module (system) identifier expected"); goto Lerr; } Identifier *id = token.ident; @@ -128,7 +149,8 @@ Dsymbols *Parser::parseModule() #endif if (token.value != TOKidentifier) - { error("Identifier expected following module"); + { + error("Identifier expected following module"); goto Lerr; } else @@ -144,13 +166,16 @@ Dsymbols *Parser::parseModule() a->push(id); nextToken(); if (token.value != TOKidentifier) - { error("Identifier expected following package"); + { + error("Identifier expected following package"); goto Lerr; } id = token.ident; } md = new ModuleDeclaration(loc, a, id, safe); + md->isdeprecated = isdeprecated; + md->msg = msg; if (token.value != TOKsemicolon) error("';' expected following module declaration instead of %s", token.toChars()); @@ -174,30 +199,61 @@ Dsymbols *Parser::parseModule() return new Dsymbols(); } -Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) -{ Dsymbol *s; - Dsymbols *decldefs; - Dsymbols *a; - Dsymbols *aelse; - PROT prot; - StorageClass stc; +struct PrefixAttributes +{ StorageClass storageClass; - Condition *condition; + Expression *depmsg; + LINK link; + PROT protection; + unsigned alignment; + Expressions *udas; const utf8_t *comment; + + PrefixAttributes() + : storageClass(STCundefined), + depmsg(NULL), + link(LINKdefault), + protection(PROTundefined), + alignment(0), + udas(NULL), + comment(NULL) + { + } +}; + +Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl, PrefixAttributes *pAttrs) +{ Dsymbol *lastDecl = NULL; // used to link unittest to its previous declaration if (!pLastDecl) pLastDecl = &lastDecl; + LINK linksave = linkage; // save global state + //printf("Parser::parseDeclDefs()\n"); - decldefs = new Dsymbols(); + Dsymbols *decldefs = new Dsymbols(); do { - comment = token.blockComment; - storageClass = STCundefined; + // parse result + Dsymbol *s = NULL; + Dsymbols *a = NULL; + + PrefixAttributes attrs; + if (!once || !pAttrs) + { + pAttrs = &attrs; + pAttrs->comment = token.blockComment; + } + PROT prot; + StorageClass stc; + Condition *condition; + + linkage = linksave; + switch (token.value) { case TOKenum: - { /* Determine if this is a manifest constant declaration, + { + /* Determine if this is a manifest constant declaration, * or a conventional enum. */ Token *t = peek(&token); @@ -218,7 +274,8 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) } case TOKimport: - s = parseImport(decldefs, 0); + a = parseImport(); + // keep pLastDecl break; case TOKtemplate: @@ -231,7 +288,8 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) switch (peekNext()) { case TOKlparen: - { // mixin(string) + { + // mixin(string) nextToken(); check(TOKlparen, "mixin"); Expression *e = parseAssignExp(); @@ -276,10 +334,10 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) case TOKclass: case TOKinterface: Ldeclaration: - a = parseDeclarations(STCundefined, NULL); - if (a->dim) *pLastDecl = (*a)[a->dim-1]; - decldefs->append(a); - continue; + a = parseDeclarations(false, pAttrs, pAttrs->comment); + if (a && a->dim) + *pLastDecl = (*a)[a->dim-1]; + break; case TOKthis: if (peekNext() == TOKdot) @@ -293,29 +351,27 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) break; case TOKinvariant: - { Token *t; - t = peek(&token); - if (t->value == TOKlparen) + { + Token *t = peek(&token); + if (t->value == TOKlparen && peek(t)->value == TOKrparen || + t->value == TOKlcurly) { - if (peek(t)->value == TOKrparen) - // invariant() forms start of class invariant - s = parseInvariant(); - else - // invariant(type) - goto Ldeclaration; + // invariant {} + // invariant() {} + s = parseInvariant(); } else { - error("use 'immutable' instead of 'invariant'"); - stc = STCimmutable; - goto Lstc; + error("invariant body expected, not '%s'", token.toChars()); + goto Lerror; } break; } case TOKunittest: s = parseUnitTest(); - if (*pLastDecl) (*pLastDecl)->ddocUnittest = (UnitTestDeclaration *)s; + if (*pLastDecl) + (*pLastDecl)->ddocUnittest = (UnitTestDeclaration *)s; break; case TOKnew: @@ -326,33 +382,40 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) s = parseDelete(); break; - case TOKeof: + case TOKcolon: + case TOKlcurly: + error("Declaration expected, not '%s'",token.toChars()); + goto Lerror; + case TOKrcurly: + case TOKeof: if (once) error("Declaration expected, not '%s'", token.toChars()); return decldefs; case TOKstatic: - nextToken(); - if (token.value == TOKthis) + { + TOK next = peekNext(); + if (next == TOKthis) s = parseStaticCtor(); - else if (token.value == TOKtilde) + else if (next == TOKtilde) s = parseStaticDtor(); - else if (token.value == TOKassert) + else if (next == TOKassert) s = parseStaticAssert(); - else if (token.value == TOKif) + else if (next == TOKif) { condition = parseStaticIfCondition(); + Dsymbols *athen; if (token.value == TOKcolon) - a = parseBlock(pLastDecl); + athen = parseBlock(pLastDecl); else { Loc lookingForElseSave = lookingForElse; lookingForElse = token.loc; - a = parseBlock(pLastDecl); + athen = parseBlock(pLastDecl); lookingForElse = lookingForElseSave; } - aelse = NULL; + Dsymbols *aelse = NULL; if (token.value == TOKelse) { Loc elseloc = token.loc; @@ -360,18 +423,20 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) aelse = parseBlock(pLastDecl); checkDanglingElse(elseloc); } - s = new StaticIfDeclaration(condition, a, aelse); - break; + s = new StaticIfDeclaration(condition, athen, aelse); } - else if (token.value == TOKimport) + else if (next == TOKimport) { - s = parseImport(decldefs, 1); + a = parseImport(); + // keep pLastDecl } else - { stc = STCstatic; - goto Lstc2; + { + stc = STCstatic; + goto Lstc; } break; + } case TOKconst: if (peekNext() == TOKlparen) @@ -386,17 +451,21 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) goto Lstc; case TOKshared: - { TOK next = peekNext(); + { + TOK next = peekNext(); if (next == TOKlparen) goto Ldeclaration; if (next == TOKstatic) - { TOK next2 = peekNext2(); + { + TOK next2 = peekNext2(); if (next2 == TOKthis) - { s = parseSharedStaticCtor(); + { + s = parseSharedStaticCtor(); break; } if (next2 == TOKtilde) - { s = parseSharedStaticDtor(); + { + s = parseSharedStaticDtor(); break; } } @@ -427,75 +496,23 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) stc = parseAttribute(&exps); if (stc) goto Lstc; // it's a predefined attribute - a = parseBlock(pLastDecl); - s = new UserAttributeDeclaration(exps, a); - break; + // no redundant/conflicting check for UDAs + pAttrs->udas = UserAttributeDeclaration::concat(pAttrs->udas, exps); + goto Lautodecl; } - Lstc: - if (storageClass & stc) - error("redundant storage class '%s'", Token::toChars(token.value)); - composeStorageClass(storageClass | stc); - nextToken(); - Lstc2: - storageClass |= stc; - switch (token.value) + if (pAttrs->storageClass & stc) { - case TOKshared: - // Look for "shared static this" or "shared static ~this" - if (peekNext() == TOKstatic) - { TOK next2 = peekNext2(); - if (next2 == TOKthis || next2 == TOKtilde) - break; - } - case TOKconst: - case TOKinvariant: - case TOKimmutable: - case TOKwild: - // If followed by a (, it is not a storage class - if (peek(&token)->value == TOKlparen) - break; - if (token.value == TOKconst) - stc = STCconst; - else if (token.value == TOKshared) - stc = STCshared; - else if (token.value == TOKwild) - stc = STCwild; - else - { - if (token.value == TOKinvariant) - error("use 'immutable' instead of 'invariant'"); - stc = STCimmutable; - } - goto Lstc; - case TOKdeprecated: - if (peek(&token)->value == TOKlparen) - break; - stc = STCdeprecated; - goto Lstc; - case TOKfinal: stc = STCfinal; goto Lstc; - case TOKauto: stc = STCauto; goto Lstc; - case TOKscope: stc = STCscope; goto Lstc; - case TOKoverride: stc = STCoverride; goto Lstc; - case TOKabstract: stc = STCabstract; goto Lstc; - case TOKsynchronized: stc = STCsynchronized; goto Lstc; - case TOKnothrow: stc = STCnothrow; goto Lstc; - case TOKpure: stc = STCpure; goto Lstc; - case TOKref: stc = STCref; goto Lstc; - case TOKgshared: stc = STCgshared; goto Lstc; - //case TOKmanifest: stc = STCmanifest; goto Lstc; - case TOKat: - { Expressions *udas = NULL; - stc = parseAttribute(&udas); - if (udas) - // BUG: Should fix this - error("user defined attributes must be first"); - goto Lstc; - } - default: - break; + if (token.value == TOKidentifier) + error("redundant storage class '@%s'", token.ident->toChars()); + else + error("redundant storage class '%s'", Token::toChars(token.value)); } + composeStorageClass(pAttrs->storageClass | stc); + pAttrs->storageClass |= stc; + nextToken(); + Lautodecl: Token *tk; /* Look for auto initializers: @@ -506,10 +523,16 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) skipParensIf(peek(&token), &tk) && tk->value == TOKassign) { - a = parseAutoDeclarations(storageClass, comment); - if (a->dim) *pLastDecl = (*a)[a->dim-1]; - decldefs->append(a); - continue; + a = parseAutoDeclarations(pAttrs->storageClass, pAttrs->comment); + pAttrs->storageClass = STCundefined; + if (a && a->dim) + *pLastDecl = (*a)[a->dim-1]; + if (pAttrs->udas) + { + s = new UserAttributeDeclaration(pAttrs->udas, a); + pAttrs->udas = NULL; + } + break; } /* Look for return type inference for template functions. @@ -524,28 +547,55 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) tk->value == TOKbody) ) { - a = parseDeclarations(storageClass, comment); - if (a->dim) *pLastDecl = (*a)[a->dim-1]; - decldefs->append(a); - continue; + a = parseDeclarations(true, pAttrs, pAttrs->comment); + if (a && a->dim) + *pLastDecl = (*a)[a->dim-1]; + if (pAttrs->udas) + { + s = new UserAttributeDeclaration(pAttrs->udas, a); + pAttrs->udas = NULL; + } + break; + } + + a = parseBlock(pLastDecl, pAttrs); + if (pAttrs->storageClass != STCundefined) + { + s = new StorageClassDeclaration(pAttrs->storageClass, a); + pAttrs->storageClass = STCundefined; + } + if (pAttrs->udas) + { + if (s) + a = new Dsymbols(), a->push(s); + s = new UserAttributeDeclaration(pAttrs->udas, a); + pAttrs->udas = NULL; } - a = parseBlock(pLastDecl); - s = new StorageClassDeclaration(storageClass, a); break; case TOKdeprecated: + { if (peek(&token)->value != TOKlparen) { stc = STCdeprecated; goto Lstc; } - { nextToken(); check(TOKlparen); Expression *e = parseAssignExp(); check(TOKrparen); - a = parseBlock(pLastDecl); - s = new DeprecatedDeclaration(e, a); + if (pAttrs->depmsg) + { + error("conflicting storage class 'deprecated(%s)' and 'deprecated(%s)'", + pAttrs->depmsg->toChars(), e->toChars()); + } + pAttrs->depmsg = e; + a = parseBlock(pLastDecl, pAttrs); + if (pAttrs->depmsg) + { + s = new DeprecatedDeclaration(pAttrs->depmsg, a); + pAttrs->depmsg = NULL; + } break; } @@ -553,52 +603,100 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) { warning(token.loc, "use @(attributes) instead of [attributes]"); Expressions *exps = parseArguments(); - a = parseBlock(pLastDecl); - s = new UserAttributeDeclaration(exps, a); + // no redundant/conflicting check for UDAs + + pAttrs->udas = UserAttributeDeclaration::concat(pAttrs->udas, exps); + a = parseBlock(pLastDecl, pAttrs); + if (pAttrs->udas) + { + s = new UserAttributeDeclaration(pAttrs->udas, a); + pAttrs->udas = NULL; + } break; } case TOKextern: + { if (peek(&token)->value != TOKlparen) - { stc = STCextern; + { + stc = STCextern; goto Lstc; } - { - LINK linksave = linkage; - linkage = parseLinkage(); - a = parseBlock(pLastDecl); - s = new LinkDeclaration(linkage, a); - linkage = linksave; + + Loc linkLoc = token.loc; + Identifiers *idents = NULL; + LINK link = parseLinkage(&idents); + if (pAttrs->link != LINKdefault) + { + if (pAttrs->link != link) + { + error("conflicting linkage extern (%s) and extern (%s)", + linkageToChars(pAttrs->link), linkageToChars(link)); + } + else if (idents) + { + // Allow: + // extern(C++, foo) extern(C++, bar) void foo(); + // to be equivalent with: + // extern(C++, foo.bar) void foo(); + } + else + error("redundant linkage extern (%s)", linkageToChars(pAttrs->link)); + } + pAttrs->link = link; + this->linkage = link; + a = parseBlock(pLastDecl, pAttrs); + if (idents) + { + assert(link == LINKcpp); + assert(idents->dim); + for (size_t i = idents->dim; i;) + { + Identifier *id = (*idents)[--i]; + if (s) + a = new Dsymbols(), a->push(s); + s = new Nspace(linkLoc, id, a); + } + delete idents; + pAttrs->link = LINKdefault; + } + else if (pAttrs->link != LINKdefault) + { + s = new LinkDeclaration(pAttrs->link, a); + pAttrs->link = LINKdefault; + } break; } + case TOKprivate: prot = PROTprivate; goto Lprot; case TOKpackage: prot = PROTpackage; goto Lprot; case TOKprotected: prot = PROTprotected; goto Lprot; case TOKpublic: prot = PROTpublic; goto Lprot; case TOKexport: prot = PROTexport; goto Lprot; - Lprot: nextToken(); - switch (token.value) + if (pAttrs->protection != PROTundefined) { - case TOKprivate: - case TOKpackage: - case TOKprotected: - case TOKpublic: - case TOKexport: - error("redundant protection attribute"); - break; - default: break; + if (pAttrs->protection != prot) + error("conflicting protection attribute '%s' and '%s'", + protectionToChars(pAttrs->protection), protectionToChars(prot)); + else + error("redundant protection attribute '%s'", protectionToChars(prot)); + } + pAttrs->protection = prot; + a = parseBlock(pLastDecl, pAttrs); + if (pAttrs->protection != PROTundefined) + { + s = new ProtDeclaration(pAttrs->protection, a); + pAttrs->protection = PROTundefined; } - a = parseBlock(pLastDecl); - s = new ProtDeclaration(prot, a); break; case TOKalign: - { unsigned n; - - s = NULL; + { nextToken(); + + unsigned n; if (token.value == TOKlparen) { nextToken(); @@ -609,7 +707,8 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) n = (unsigned)token.uns64value; } else - { error("positive integer expected, not %s", token.toChars()); + { + error("positive integer expected, not %s", token.toChars()); n = 1; } nextToken(); @@ -618,8 +717,37 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) else n = STRUCTALIGN_DEFAULT; // default - a = parseBlock(pLastDecl); - s = new AlignDeclaration(n, a); + if (pAttrs->alignment != 0) + { + const char *s1 = ""; + OutBuffer buf1; + if (n != STRUCTALIGN_DEFAULT) + { + buf1.printf("(%d)", n); + s1 = buf1.peekString(); + } + if (pAttrs->alignment != n) + { + OutBuffer buf2; + const char *s2 = ""; + if (pAttrs->alignment != STRUCTALIGN_DEFAULT) + { + buf2.printf("(%d)", pAttrs->alignment); + s2 = buf2.peekString(); + } + error("conflicting alignment attribute align%s and align%s", s2, s1); + } + else + error("redundant alignment attribute align%s", s1); + } + + pAttrs->alignment = n; + a = parseBlock(pLastDecl, pAttrs); + if (pAttrs->alignment != 0) + { + s = new AlignDeclaration(pAttrs->alignment, a); + pAttrs->alignment = 0; + } break; } @@ -631,7 +759,8 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) nextToken(); check(TOKlparen); if (token.value != TOKidentifier) - { error("pragma(identifier) expected"); + { + error("pragma(identifier) expected"); goto Lerror; } Identifier *ident = token.ident; @@ -641,11 +770,10 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) else check(TOKrparen); // pragma(identifier) - if (token.value == TOKsemicolon) - a = NULL; - else - a = parseBlock(pLastDecl); - s = new PragmaDeclaration(loc, ident, args, a); + Dsymbols *a2 = NULL; + if (token.value != TOKsemicolon) + a2 = parseBlock(pLastDecl); + s = new PragmaDeclaration(loc, ident, args, a2); break; } @@ -659,7 +787,8 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) else if (token.value == TOKint32v || token.value == TOKint64v) s = new DebugSymbol(token.loc, (unsigned)token.uns64value); else - { error("identifier or integer expected, not %s", token.toChars()); + { + error("identifier or integer expected, not %s", token.toChars()); s = NULL; } nextToken(); @@ -682,7 +811,8 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) else if (token.value == TOKint32v || token.value == TOKint64v) s = new VersionSymbol(token.loc, (unsigned)token.uns64value); else - { error("identifier or integer expected, not %s", token.toChars()); + { + error("identifier or integer expected, not %s", token.toChars()); s = NULL; } nextToken(); @@ -695,18 +825,18 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) goto Lcondition; Lcondition: + { + Dsymbols *athen; + if (token.value == TOKcolon) + athen = parseBlock(pLastDecl); + else { - if (token.value == TOKcolon) - a = parseBlock(pLastDecl); - else - { - Loc lookingForElseSave = lookingForElse; - lookingForElse = token.loc; - a = parseBlock(pLastDecl); - lookingForElse = lookingForElseSave; - } + Loc lookingForElseSave = lookingForElse; + lookingForElse = token.loc; + athen = parseBlock(pLastDecl); + lookingForElse = lookingForElseSave; } - aelse = NULL; + Dsymbols *aelse = NULL; if (token.value == TOKelse) { Loc elseloc = token.loc; @@ -714,8 +844,9 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) aelse = parseBlock(pLastDecl); checkDanglingElse(elseloc); } - s = new ConditionalDeclaration(condition, a, aelse); + s = new ConditionalDeclaration(condition, athen, aelse); break; + } case TOKsemicolon: // empty declaration //error("empty declaration"); @@ -731,13 +862,22 @@ Dsymbols *Parser::parseDeclDefs(int once, Dsymbol **pLastDecl) s = NULL; continue; } + if (s) - { decldefs->push(s); - addComment(s, comment); + { if (!s->isAttribDeclaration()) *pLastDecl = s; + decldefs->push(s); + addComment(s, pAttrs->comment); + } + else if (a && a->dim) + { + decldefs->append(a); } } while (!once); + + linkage = linksave; + return decldefs; } @@ -781,6 +921,8 @@ StorageClass Parser::parseAttribute(Expressions **pudas) { if (token.ident == Id::property) stc = STCproperty; + else if (token.ident == Id::nogc) + stc = STCnogc; else if (token.ident == Id::safe) stc = STCsafe; else if (token.ident == Id::trusted) @@ -790,7 +932,8 @@ StorageClass Parser::parseAttribute(Expressions **pudas) else if (token.ident == Id::disable) stc = STCdisable; else - { // Allow identifier, template instantiation, or function call + { + // Allow identifier, template instantiation, or function call Expression *exp = parsePrimaryExp(); if (token.value == TOKlparen) { @@ -803,7 +946,8 @@ StorageClass Parser::parseAttribute(Expressions **pudas) } } else if (token.value == TOKlparen) - { // @( ArgumentList ) + { + // @( ArgumentList ) // Concatenate with existing udas = parseArguments(); } @@ -828,28 +972,37 @@ StorageClass Parser::parseAttribute(Expressions **pudas) * Parse const/immutable/shared/inout/nothrow/pure postfix */ -StorageClass Parser::parsePostfix() +StorageClass Parser::parsePostfix(Expressions **pudas) { - StorageClass stc = 0; + StorageClass stc = STCundefined; while (1) { switch (token.value) { case TOKconst: stc |= STCconst; break; - case TOKinvariant: - error("use 'immutable' instead of 'invariant'"); case TOKimmutable: stc |= STCimmutable; break; case TOKshared: stc |= STCshared; break; case TOKwild: stc |= STCwild; break; case TOKnothrow: stc |= STCnothrow; break; case TOKpure: stc |= STCpure; break; case TOKat: - { Expressions *udas = NULL; + { + Expressions *udas = NULL; stc |= parseAttribute(&udas); if (udas) - // BUG: Should fix this - error("user defined attributes cannot appear as postfixes"); + { + if (pudas) + *pudas = UserAttributeDeclaration::concat(*pudas, udas); + else + { + // Disallow: + // void function() @uda fp; + // () @uda { return 1; } + error("user defined attributes cannot appear as postfixes"); + } + continue; + } break; } @@ -871,8 +1024,6 @@ StorageClass Parser::parseTypeCtor() switch (token.value) { case TOKconst: stc |= STCconst; break; - case TOKinvariant: - error("use 'immutable' instead of 'invariant'"); case TOKimmutable: stc |= STCimmutable; break; case TOKshared: stc |= STCshared; break; case TOKwild: stc |= STCwild; break; @@ -888,7 +1039,7 @@ StorageClass Parser::parseTypeCtor() * Parse declarations after an align, protection, or extern decl. */ -Dsymbols *Parser::parseBlock(Dsymbol **pLastDecl) +Dsymbols *Parser::parseBlock(Dsymbol **pLastDecl, PrefixAttributes *pAttrs) { Dsymbols *a = NULL; @@ -912,7 +1063,8 @@ Dsymbols *Parser::parseBlock(Dsymbol **pLastDecl) nextToken(); a = parseDeclDefs(0, pLastDecl); if (token.value != TOKrcurly) - { /* { */ + { + /* { */ error("matching '}' expected, not %s", token.toChars()); } else @@ -923,15 +1075,11 @@ Dsymbols *Parser::parseBlock(Dsymbol **pLastDecl) case TOKcolon: nextToken(); -#if 0 - a = NULL; -#else a = parseDeclDefs(0, pLastDecl); // grab declarations up to closing curly bracket -#endif break; default: - a = parseDeclDefs(1, pLastDecl); + a = parseDeclDefs(1, pLastDecl, pAttrs); break; } return a; @@ -939,6 +1087,7 @@ Dsymbols *Parser::parseBlock(Dsymbol **pLastDecl) /********************************** * Parse a static assertion. + * Current token is 'static'. */ StaticAssert *Parser::parseStaticAssert() @@ -949,6 +1098,7 @@ StaticAssert *Parser::parseStaticAssert() //printf("parseStaticAssert()\n"); nextToken(); + nextToken(); check(TOKlparen); exp = parseAssignExp(); if (token.value == TOKcomma) @@ -1002,12 +1152,15 @@ Type *Parser::parseVector() } /*********************************** - * Parse extern (linkage) + * Parse: + * extern (linkage) + * extern (C++, namespaces) * The parser is on the 'extern' token. */ -LINK Parser::parseLinkage() +LINK Parser::parseLinkage(Identifiers **pidents) { + Identifiers *idents = NULL; LINK link = LINKdefault; nextToken(); assert(token.value == TOKlparen); @@ -1026,8 +1179,31 @@ LINK Parser::parseLinkage() { link = LINKc; if (token.value == TOKplusplus) - { link = LINKcpp; + { + link = LINKcpp; nextToken(); + if (token.value == TOKcomma) // , namespaces + { + idents = new Identifiers(); + nextToken(); + while (1) + { + if (token.value == TOKidentifier) + { + Identifier *idn = token.ident; + idents->push(idn); + nextToken(); + if (token.value == TOKdot) + { + nextToken(); + continue; + } + } + else + error("identifier expected for C++ namespace"); + break; + } + } } } else if (id == Id::System) @@ -1045,6 +1221,7 @@ LINK Parser::parseLinkage() link = LINKd; // default } check(TOKrparen); + *pidents = idents; return link; } @@ -1091,15 +1268,15 @@ Condition *Parser::parseVersionCondition() if (token.value == TOKlparen) { nextToken(); - if (token.value == TOKidentifier) - id = token.ident; - else if (token.value == TOKint32v || token.value == TOKint64v) - level = (unsigned)token.uns64value; /* Allow: * version (unittest) * version (assert) * even though they are keywords */ + if (token.value == TOKidentifier) + id = token.ident; + else if (token.value == TOKint32v || token.value == TOKint64v) + level = (unsigned)token.uns64value; else if (token.value == TOKunittest) id = Lexer::idPool(Token::toChars(TOKunittest)); else if (token.value == TOKassert) @@ -1122,6 +1299,7 @@ Condition *Parser::parseVersionCondition() * body * else * body + * Current token is 'static'. */ Condition *Parser::parseStaticIfCondition() @@ -1130,6 +1308,7 @@ Condition *Parser::parseStaticIfCondition() Condition *condition; Loc loc = token.loc; + nextToken(); nextToken(); if (token.value == TOKlparen) { @@ -1159,6 +1338,7 @@ Condition *Parser::parseStaticIfCondition() Dsymbol *Parser::parseCtor() { + Expressions *udas = NULL; Loc loc = token.loc; nextToken(); @@ -1168,10 +1348,17 @@ Dsymbol *Parser::parseCtor() nextToken(); nextToken(); check(TOKrparen); - StorageClass stc = parsePostfix(); + + StorageClass stc = parsePostfix(&udas); PostBlitDeclaration *f = new PostBlitDeclaration(loc, Loc(), stc, Id::_postblit); - parseContracts(f); - return f; + Dsymbol *s = parseContracts(f); + if (udas) + { + Dsymbols *a = new Dsymbols(); + a->push(f); + s = new UserAttributeDeclaration(udas, a); + } + return s; } /* Look ahead to see if: @@ -1185,7 +1372,7 @@ Dsymbol *Parser::parseCtor() int varargs; Parameters *parameters = parseParameters(&varargs); - StorageClass stc = parsePostfix(); + StorageClass stc = parsePostfix(&udas); Expression *constraint = tpl ? parseConstraint() : NULL; @@ -1193,11 +1380,17 @@ Dsymbol *Parser::parseCtor() tf = tf->addSTC(stc); CtorDeclaration *f = new CtorDeclaration(loc, Loc(), stc, tf); - parseContracts(f); + Dsymbol *s = parseContracts(f); + if (udas) + { + Dsymbols *a = new Dsymbols(); + a->push(f); + s = new UserAttributeDeclaration(udas, a); + } // Wrap a template around it Dsymbols *decldefs = new Dsymbols(); - decldefs->push(f); + decldefs->push(s); TemplateDeclaration *tempdecl = new TemplateDeclaration(loc, f->ident, tpl, constraint, decldefs); return tempdecl; @@ -1207,13 +1400,19 @@ Dsymbol *Parser::parseCtor() */ int varargs; Parameters *parameters = parseParameters(&varargs); - StorageClass stc = parsePostfix(); + StorageClass stc = parsePostfix(&udas); Type *tf = new TypeFunction(parameters, NULL, varargs, linkage, stc); // RetrunType -> auto tf = tf->addSTC(stc); CtorDeclaration *f = new CtorDeclaration(loc, Loc(), stc, tf); - parseContracts(f); - return f; + Dsymbol *s = parseContracts(f); + if (udas) + { + Dsymbols *a = new Dsymbols(); + a->push(f); + s = new UserAttributeDeclaration(udas, a); + } + return s; } /***************************************** @@ -1222,9 +1421,9 @@ Dsymbol *Parser::parseCtor() * Current token is '~'. */ -DtorDeclaration *Parser::parseDtor() +Dsymbol *Parser::parseDtor() { - DtorDeclaration *f; + Expressions *udas = NULL; Loc loc = token.loc; nextToken(); @@ -1232,29 +1431,38 @@ DtorDeclaration *Parser::parseDtor() check(TOKlparen); check(TOKrparen); - StorageClass stc = parsePostfix(); - f = new DtorDeclaration(loc, Loc(), stc, Id::dtor); - parseContracts(f); - return f; + StorageClass stc = parsePostfix(&udas); + DtorDeclaration *f = new DtorDeclaration(loc, Loc(), stc, Id::dtor); + Dsymbol *s = parseContracts(f); + if (udas) + { + Dsymbols *a = new Dsymbols(); + a->push(f); + s = new UserAttributeDeclaration(udas, a); + } + return s; } /***************************************** * Parse a static constructor definition: * static this() { body } - * Current token is 'this'. + * Current token is 'static'. */ -StaticCtorDeclaration *Parser::parseStaticCtor() +Dsymbol *Parser::parseStaticCtor() { + //Expressions *udas = NULL; Loc loc = token.loc; + nextToken(); nextToken(); check(TOKlparen); check(TOKrparen); + StorageClass stc = parsePostfix(NULL); - StaticCtorDeclaration *f = new StaticCtorDeclaration(loc, Loc()); - parseContracts(f); - return f; + StaticCtorDeclaration *f = new StaticCtorDeclaration(loc, Loc(), stc); + Dsymbol *s = parseContracts(f); + return s; } /***************************************** @@ -1263,8 +1471,9 @@ StaticCtorDeclaration *Parser::parseStaticCtor() * Current token is 'shared'. */ -SharedStaticCtorDeclaration *Parser::parseSharedStaticCtor() +Dsymbol *Parser::parseSharedStaticCtor() { + //Expressions *udas = NULL; Loc loc = token.loc; nextToken(); @@ -1272,33 +1481,43 @@ SharedStaticCtorDeclaration *Parser::parseSharedStaticCtor() nextToken(); check(TOKlparen); check(TOKrparen); + StorageClass stc = parsePostfix(NULL); - SharedStaticCtorDeclaration *f = new SharedStaticCtorDeclaration(loc, Loc()); - parseContracts(f); - return f; + SharedStaticCtorDeclaration *f = new SharedStaticCtorDeclaration(loc, Loc(), stc); + Dsymbol *s = parseContracts(f); + return s; } /***************************************** * Parse a static destructor definition: * static ~this() { body } - * Current token is '~'. + * Current token is 'static'. */ -StaticDtorDeclaration *Parser::parseStaticDtor() +Dsymbol *Parser::parseStaticDtor() { + Expressions *udas = NULL; Loc loc = token.loc; + nextToken(); nextToken(); check(TOKthis); check(TOKlparen); check(TOKrparen); - StorageClass stc = parsePostfix(); + + StorageClass stc = parsePostfix(&udas); if (stc & STCshared) error("to create a 'shared' static destructor, move 'shared' in front of the declaration"); StaticDtorDeclaration *f = new StaticDtorDeclaration(loc, Loc(), stc); - parseContracts(f); - return f; + Dsymbol *s = parseContracts(f); + if (udas) + { + Dsymbols *a = new Dsymbols(); + a->push(f); + s = new UserAttributeDeclaration(udas, a); + } + return s; } /***************************************** @@ -1307,8 +1526,9 @@ StaticDtorDeclaration *Parser::parseStaticDtor() * Current token is 'shared'. */ -SharedStaticDtorDeclaration *Parser::parseSharedStaticDtor() +Dsymbol *Parser::parseSharedStaticDtor() { + Expressions *udas = NULL; Loc loc = token.loc; nextToken(); @@ -1317,13 +1537,20 @@ SharedStaticDtorDeclaration *Parser::parseSharedStaticDtor() check(TOKthis); check(TOKlparen); check(TOKrparen); - StorageClass stc = parsePostfix(); + + StorageClass stc = parsePostfix(&udas); if (stc & STCshared) error("static destructor is 'shared' already"); SharedStaticDtorDeclaration *f = new SharedStaticDtorDeclaration(loc, Loc(), stc); - parseContracts(f); - return f; + Dsymbol *s = parseContracts(f); + if (udas) + { + Dsymbols *a = new Dsymbols(); + a->push(f); + s = new UserAttributeDeclaration(udas, a); + } + return s; } /***************************************** @@ -1473,12 +1700,9 @@ Parameters *Parser::parseParameters(int *pvarargs, TemplateParameters **tpl) stc = STCconst; goto L2; - case TOKinvariant: case TOKimmutable: if (peek(&token)->value == TOKlparen) goto Ldefault; - if (token.value == TOKinvariant) - error("use 'immutable' instead of 'invariant'"); stc = STCimmutable; goto L2; @@ -1496,9 +1720,6 @@ Parameters *Parser::parseParameters(int *pvarargs, TemplateParameters **tpl) case TOKin: stc = STCin; goto L2; case TOKout: stc = STCout; goto L2; -#if D1INOUT - case TOKinout: -#endif case TOKref: stc = STCref; goto L2; case TOKlazy: stc = STClazy; goto L2; case TOKscope: stc = STCscope; goto L2; @@ -1544,7 +1765,8 @@ Parameters *Parser::parseParameters(int *pvarargs, TemplateParameters **tpl) default: Ldefault: { stc = storageClass & (STCin | STCout | STCref | STClazy); - if (stc & (stc - 1) && // if stc is not a power of 2 + // if stc is not a power of 2 + if (stc & (stc - 1) && !(stc == (STCin | STCref))) error("incompatible parameter storage classes"); if ((storageClass & STCscope) && (storageClass & (STCref | STCout))) @@ -1676,6 +1898,8 @@ EnumDeclaration *Parser::parseEnum() else { type = parseType(&ident, NULL); + if (!ident) + error("no identifier for declarator %s", type->toChars()); if (id || memtype) error("type only allowed if anonymous enum and no enum type"); } @@ -2041,7 +2265,7 @@ TemplateParameters *Parser::parseTemplateParameterList(int flag) else def = parseCondExp(); } - tp = new TemplateAliasParameter(loc/*todo*/, tp_ident, spectype, spec, def); + tp = new TemplateAliasParameter(loc, tp_ident, spectype, spec, def); } else if (t->value == TOKcolon || t->value == TOKassign || t->value == TOKcomma || t->value == TOKrparen) @@ -2123,7 +2347,7 @@ TemplateParameters *Parser::parseTemplateParameterList(int flag) nextToken(); tp_defaultvalue = parseDefaultInitExp(); } - tp = new TemplateValueParameter(loc/*todo*/, tp_ident, tp_valtype, tp_specvalue, tp_defaultvalue); + tp = new TemplateValueParameter(loc, tp_ident, tp_valtype, tp_specvalue, tp_defaultvalue); } tpl->push(tp); if (token.value != TOKcomma) @@ -2183,11 +2407,7 @@ Dsymbol *Parser::parseMixin() tiargs = NULL; if (token.value == TOKnot) { - nextToken(); - if (token.value == TOKlparen) - tiargs = parseTemplateArgumentList(); - else - tiargs = parseTemplateArgument(); + tiargs = parseTemplateArguments(); } if (tiargs && token.value == TOKdot) @@ -2239,28 +2459,62 @@ Dsymbol *Parser::parseMixin() } /****************************************** - * Parse template argument list. + * Parse template arguments. * Input: - * current token is opening '(' + * current token is opening '!' * Output: * current token is one after closing ')' */ -Objects *Parser::parseTemplateArgumentList() +Objects *Parser::parseTemplateArguments() { - //printf("Parser::parseTemplateArgumentList()\n"); - if (token.value != TOKlparen && token.value != TOKlcurly) - { error("!(TemplateArgumentList) expected following TemplateIdentifier"); - return new Objects(); + Objects *tiargs; + + nextToken(); + if (token.value == TOKlparen) + { + // ident!(template_arguments) + tiargs = parseTemplateArgumentList(); } - return parseTemplateArgumentList2(); + else + { + // ident!template_argument + tiargs = parseTemplateSingleArgument(); + } + if (token.value == TOKnot) + { + TOK tok = peekNext(); + if (tok != TOKis && tok != TOKin) + { + error("multiple ! arguments are not allowed"); + Lagain: + nextToken(); + if (token.value == TOKlparen) + parseTemplateArgumentList(); + else + parseTemplateSingleArgument(); + if (token.value == TOKnot && (tok = peekNext()) != TOKis && tok != TOKin) + goto Lagain; + } + } + return tiargs; } -Objects *Parser::parseTemplateArgumentList2() +/****************************************** + * Parse template argument list. + * Input: + * current token is opening '(', + * or ',' for __traits + * Output: + * current token is one after closing ')' + */ + +Objects *Parser::parseTemplateArgumentList() { - //printf("Parser::parseTemplateArgumentList2()\n"); + //printf("Parser::parseTemplateArgumentList()\n"); Objects *tiargs = new Objects(); TOK endtok = TOKrparen; + assert(token.value == TOKlparen || token.value == TOKcomma); nextToken(); // Get TemplateArgumentList @@ -2292,9 +2546,9 @@ Objects *Parser::parseTemplateArgumentList2() * current token is the arg */ -Objects *Parser::parseTemplateArgument() +Objects *Parser::parseTemplateSingleArgument() { - //printf("parseTemplateArgument()\n"); + //printf("parseTemplateSingleArgument()\n"); Objects *tiargs = new Objects(); Type *ta; switch (token.value) @@ -2353,6 +2607,7 @@ Objects *Parser::parseTemplateArgument() case TOKwcharv: case TOKdcharv: case TOKstring: + case TOKxstring: case TOKfile: case TOKline: case TOKmodulestring: @@ -2369,19 +2624,18 @@ Objects *Parser::parseTemplateArgument() error("template argument expected following !"); break; } - if (token.value == TOKnot) - { - TOK tok = peekNext(); - if (tok != TOKis && tok != TOKin) - error("multiple ! arguments are not allowed"); - } return tiargs; } -Import *Parser::parseImport(Dsymbols *decldefs, int isstatic) +Dsymbols *Parser::parseImport() { + Dsymbols *decldefs = new Dsymbols(); Identifier *aliasid = NULL; + int isstatic = token.value == TOKstatic; + if (isstatic) + nextToken(); + //printf("Parser::parseImport()\n"); do { @@ -2464,7 +2718,7 @@ Import *Parser::parseImport(Dsymbols *decldefs, int isstatic) nextToken(); } - return NULL; + return decldefs; } Type *Parser::parseType(Identifier **pident, TemplateParameters **tpl) @@ -2492,12 +2746,9 @@ Type *Parser::parseType(Identifier **pident, TemplateParameters **tpl) nextToken(); continue; - case TOKinvariant: case TOKimmutable: if (peekNext() == TOKlparen) break; - if (token.value == TOKinvariant) - error("use 'immutable' instead of 'invariant'"); stc |= STCimmutable; nextToken(); continue; @@ -2577,13 +2828,7 @@ Type *Parser::parseBasicType() { // ident!(template_arguments) TemplateInstance *tempinst = new TemplateInstance(loc, id); - nextToken(); - if (token.value == TOKlparen) - // ident!(template_arguments) - tempinst->tiargs = parseTemplateArgumentList(); - else - // ident!template_argument - tempinst->tiargs = parseTemplateArgument(); + tempinst->tiargs = parseTemplateArguments(); tid = new TypeInstance(loc, tempinst); goto Lident2; } @@ -2604,13 +2849,7 @@ Type *Parser::parseBasicType() if (token.value == TOKnot) { TemplateInstance *tempinst = new TemplateInstance(loc, id); - nextToken(); - if (token.value == TOKlparen) - // ident!(template_arguments) - tempinst->tiargs = parseTemplateArgumentList(); - else - // ident!template_argument - tempinst->tiargs = parseTemplateArgument(); + tempinst->tiargs = parseTemplateArguments(); tid->addInst(tempinst); } else @@ -2642,10 +2881,8 @@ Type *Parser::parseBasicType() check(TOKrparen); break; - case TOKinvariant: - error("use 'immutable' instead of 'invariant'"); case TOKimmutable: - // invariant(type) + // immutable(type) nextToken(); check(TOKlparen); t = parseType()->addSTC(STCimmutable); @@ -2737,7 +2974,8 @@ Type *Parser::parseBasicType2(Type *t) case TOKdelegate: case TOKfunction: - { // Handle delegate declaration: + { + // Handle delegate declaration: // t delegate(parameter list) nothrow pure // t function(parameter list) nothrow pure Parameters *arguments; @@ -2747,7 +2985,7 @@ Type *Parser::parseBasicType2(Type *t) nextToken(); arguments = parseParameters(&varargs); - StorageClass stc = parsePostfix(); + StorageClass stc = parsePostfix(NULL); TypeFunction *tf = new TypeFunction(arguments, t, varargs, linkage, stc); if (stc & (STCconst | STCimmutable | STCshared | STCwild)) { @@ -2773,15 +3011,15 @@ Type *Parser::parseBasicType2(Type *t) return NULL; } -Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters **tpl, StorageClass storage_class, int* pdisable) -{ Type *ts; - +Type *Parser::parseDeclarator(Type *t, Identifier **pident, + TemplateParameters **tpl, StorageClass storage_class, int* pdisable, Expressions **pudas) +{ //printf("parseDeclarator(tpl = %p)\n", tpl); t = parseBasicType2(t); + Type *ts; switch (token.value) { - case TOKidentifier: if (pident) *pident = token.ident; @@ -2792,9 +3030,10 @@ Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters * break; case TOKlparen: - if (peekNext() == TOKmul || // like: T (*fp)(); - peekNext() == TOKlparen // like: T ((*fp))(); - /* || peekNext() == TOKlbracket*/) // like: T ([] a) + // like: T (*fp)(); + // like: T ((*fp))(); + if (peekNext() == TOKmul || + peekNext() == TOKlparen) { /* Parse things with parentheses around the identifier, like: * int (*ident[3])[] @@ -2882,22 +3121,22 @@ Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters * { if (tpl) { - /* Look ahead to see if this is (...)(...), - * i.e. a function template declaration - */ Token *tk = peekPastParen(&token); if (tk->value == TOKlparen) { + /* Look ahead to see if this is (...)(...), + * i.e. a function template declaration + */ //printf("function template declaration\n"); // Gather template parameter list *tpl = parseTemplateParameterList(); } - /* or (...) =, - * i.e. a variable template declaration - */ else if (tk->value == TOKassign) { + /* or (...) =, + * i.e. a variable template declaration + */ //printf("variable template declaration\n"); *tpl = parseTemplateParameterList(); break; @@ -2909,7 +3148,7 @@ Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters * /* Parse const/immutable/shared/inout/nothrow/pure postfix */ - StorageClass stc = parsePostfix(); + StorageClass stc = parsePostfix(pudas); stc |= storage_class; // merge prefix storage classes Type *tf = new TypeFunction(arguments, t, varargs, linkage, stc); tf = tf->addSTC(stc); @@ -2935,6 +3174,115 @@ Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters * return ts; } +void Parser::parseStorageClasses(StorageClass &storage_class, LINK &link, unsigned &structalign, Expressions *&udas) +{ + StorageClass stc; + bool sawLinkage = false; // seen a linkage declaration + + while (1) + { + switch (token.value) + { + case TOKconst: + if (peek(&token)->value == TOKlparen) + break; // const as type constructor + stc = STCconst; // const as storage class + goto L1; + + case TOKimmutable: + if (peek(&token)->value == TOKlparen) + break; + stc = STCimmutable; + goto L1; + + case TOKshared: + if (peek(&token)->value == TOKlparen) + break; + stc = STCshared; + goto L1; + + case TOKwild: + if (peek(&token)->value == TOKlparen) + break; + stc = STCwild; + goto L1; + + case TOKstatic: stc = STCstatic; goto L1; + case TOKfinal: stc = STCfinal; goto L1; + case TOKauto: stc = STCauto; goto L1; + case TOKscope: stc = STCscope; goto L1; + case TOKoverride: stc = STCoverride; goto L1; + case TOKabstract: stc = STCabstract; goto L1; + case TOKsynchronized: stc = STCsynchronized; goto L1; + case TOKdeprecated: stc = STCdeprecated; goto L1; + case TOKnothrow: stc = STCnothrow; goto L1; + case TOKpure: stc = STCpure; goto L1; + case TOKref: stc = STCref; goto L1; + case TOKgshared: stc = STCgshared; goto L1; + case TOKenum: stc = STCmanifest; goto L1; + case TOKat: + { + stc = parseAttribute(&udas); + if (stc) + goto L1; + continue; + } + L1: + if (storage_class & stc) + error("redundant storage class '%s'", token.toChars()); + storage_class = storage_class | stc; + composeStorageClass(storage_class); + nextToken(); + continue; + + case TOKextern: + { + if (peek(&token)->value != TOKlparen) + { + stc = STCextern; + goto L1; + } + + if (sawLinkage) + error("redundant linkage declaration"); + sawLinkage = true; + Identifiers *idents = NULL; + link = parseLinkage(&idents); + if (idents) + { + error("C++ name spaces not allowed here"); + delete idents; + } + continue; + } + + case TOKalign: + { + nextToken(); + if (token.value == TOKlparen) + { + nextToken(); + if (token.value == TOKint32v && token.uns64value > 0) + structalign = (unsigned)token.uns64value; + else + { + error("positive integer expected, not %s", token.toChars()); + structalign = 1; + } + nextToken(); + check(TOKrparen); + } + else + structalign = STRUCTALIGN_DEFAULT; // default + continue; + } + default: + break; + } + break; + } +} + /********************************** * Parse Declarations. * These can be: @@ -2943,10 +3291,9 @@ Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters * * Return array of Declaration *'s. */ -Dsymbols *Parser::parseDeclarations(StorageClass storage_class, const utf8_t *comment) +Dsymbols *Parser::parseDeclarations(bool autodecl, PrefixAttributes *pAttrs, const utf8_t *comment) { - StorageClass stc; - int disable; + StorageClass storage_class = STCundefined; Type *ts; Type *t; Type *tfirst; @@ -2962,8 +3309,9 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, const utf8_t *co if (!comment) comment = token.blockComment; - if (storage_class) - { ts = NULL; // infer type + if (autodecl) + { + ts = NULL; // infer type goto L2; } @@ -3021,14 +3369,31 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, const utf8_t *co if (token.value == TOKlparen) tpl = parseTemplateParameterList(); check(TOKassign); + + storage_class = STCundefined; + link = linkage; + structalign = 0; + udas = NULL; + parseStorageClasses(storage_class, link, structalign, udas); + + if (udas) + error("user defined attributes not allowed for %s declarations", Token::toChars(tok)); + t = parseType(); Dsymbol *s = new AliasDeclaration(loc, ident, t); + ((Declaration *)s)->storage_class = storage_class; + if (link != linkage) + { + Dsymbols *a2 = new Dsymbols(); + a2->push(s); + s = new LinkDeclaration(link, a2); + } if (tpl) { Dsymbols *a2 = new Dsymbols(); a2->push(s); TemplateDeclaration *tempdecl = - new TemplateDeclaration(loc, ident, tpl, NULL/*constraint*/, a2); + new TemplateDeclaration(loc, ident, tpl, NULL, a2); s = tempdecl; } a->push(s); @@ -3042,129 +3407,37 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, const utf8_t *co nextToken(); addComment(s, comment); if (token.value != TOKidentifier) - { - error("Identifier expected following comma, not %s", token.toChars()); - break; - } - if (peekNext() != TOKassign && peekNext() != TOKlparen) - { - error("= expected following identifier"); - nextToken(); - break; - } - continue; - default: - error("semicolon expected to close %s declaration", Token::toChars(tok)); - break; - } - break; - } - return a; - } - break; - } - case TOKtypedef: - deprecation("use of typedef is deprecated; use alias instead"); - tok = token.value; - nextToken(); - break; - default: break; - } - - storage_class = STCundefined; - while (1) - { - switch (token.value) - { - case TOKconst: - if (peek(&token)->value == TOKlparen) - break; // const as type constructor - stc = STCconst; // const as storage class - goto L1; - - case TOKinvariant: - case TOKimmutable: - if (peek(&token)->value == TOKlparen) - break; - if (token.value == TOKinvariant) - error("use 'immutable' instead of 'invariant'"); - stc = STCimmutable; - goto L1; - - case TOKshared: - if (peek(&token)->value == TOKlparen) - break; - stc = STCshared; - goto L1; - - case TOKwild: - if (peek(&token)->value == TOKlparen) - break; - stc = STCwild; - goto L1; - - case TOKstatic: stc = STCstatic; goto L1; - case TOKfinal: stc = STCfinal; goto L1; - case TOKauto: stc = STCauto; goto L1; - case TOKscope: stc = STCscope; goto L1; - case TOKoverride: stc = STCoverride; goto L1; - case TOKabstract: stc = STCabstract; goto L1; - case TOKsynchronized: stc = STCsynchronized; goto L1; - case TOKdeprecated: stc = STCdeprecated; goto L1; - case TOKnothrow: stc = STCnothrow; goto L1; - case TOKpure: stc = STCpure; goto L1; - case TOKref: stc = STCref; goto L1; - case TOKgshared: stc = STCgshared; goto L1; - case TOKenum: stc = STCmanifest; goto L1; - case TOKat: - { - stc = parseAttribute(&udas); - if (stc) - goto L1; - continue; - } - L1: - if (storage_class & stc) - error("redundant storage class '%s'", token.toChars()); - storage_class = storage_class | stc; - composeStorageClass(storage_class); - nextToken(); - continue; - - case TOKextern: - if (peek(&token)->value != TOKlparen) - { stc = STCextern; - goto L1; - } - - link = parseLinkage(); - continue; - - case TOKalign: - { - nextToken(); - if (token.value == TOKlparen) - { - nextToken(); - if (token.value == TOKint32v && token.uns64value > 0) - structalign = (unsigned)token.uns64value; - else - { error("positive integer expected, not %s", token.toChars()); - structalign = 1; + { + error("Identifier expected following comma, not %s", token.toChars()); + break; + } + if (peekNext() != TOKassign && peekNext() != TOKlparen) + { + error("= expected following identifier"); + nextToken(); + break; + } + continue; + default: + error("semicolon expected to close %s declaration", Token::toChars(tok)); + break; } - nextToken(); - check(TOKrparen); + break; } - else - structalign = STRUCTALIGN_DEFAULT; // default - continue; + return a; } - default: - break; + break; } - break; + case TOKtypedef: + error("use alias instead of typedef"); + tok = token.value; + nextToken(); + break; + default: break; } + parseStorageClasses(storage_class, link, structalign, udas); + if (token.value == TOKstruct || token.value == TOKunion || token.value == TOKclass || @@ -3186,6 +3459,12 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, const utf8_t *co a = new Dsymbols(); a->push(s); } + if (link != linkage) + { + s = new LinkDeclaration(link, a); + a = new Dsymbols(); + a->push(s); + } if (udas) { s = new UserAttributeDeclaration(udas, a); @@ -3197,32 +3476,28 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, const utf8_t *co return a; } - if (udas) - { - // Need to improve this -// error("user defined attributes not allowed for local declarations"); - } - /* Look for auto initializers: * storage_class identifier = initializer; * storage_class identifier(...) = initializer; */ - if (storage_class && + if ((storage_class || udas) && token.value == TOKidentifier && skipParensIf(peek(&token), &tk) && tk->value == TOKassign) { + Dsymbols *a = parseAutoDeclarations(storage_class, comment); if (udas) { - // Need to improve this - error("user defined attributes not allowed for auto declarations"); + Dsymbol *s = new UserAttributeDeclaration(udas, a); + a = new Dsymbols(); + a->push(s); } - return parseAutoDeclarations(storage_class, comment); + return a; } /* Look for return type inference for template functions. */ - if (storage_class && + if ((storage_class || udas) && token.value == TOKidentifier && skipParens(peek(&token), &tk) && skipAttributes(tk, &tk) && @@ -3249,9 +3524,10 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, const utf8_t *co { loc = token.loc; TemplateParameters *tpl = NULL; + int disable; ident = NULL; - t = parseDeclarator(ts, &ident, &tpl, storage_class, &disable); + t = parseDeclarator(ts, &ident, &tpl, storage_class, &disable, &udas); assert(t); if (!tfirst) tfirst = t; @@ -3284,7 +3560,7 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, const utf8_t *co } if (tok == TOKtypedef) { - v = new TypedefDeclaration(loc, ident, t, init); + v = new AliasDeclaration(loc, ident, t); // dummy } else { @@ -3310,7 +3586,8 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, const utf8_t *co a->push(s); } switch (token.value) - { case TOKsemicolon: + { + case TOKsemicolon: nextToken(); addComment(v, comment); break; @@ -3327,9 +3604,9 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, const utf8_t *co } else if (t->ty == Tfunction) { - TypeFunction *tf = (TypeFunction *)t; Expression *constraint = NULL; #if 0 + TypeFunction *tf = (TypeFunction *)t; if (Parameter::isTPL(tf->parameters)) { if (!tpl) @@ -3338,15 +3615,79 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, const utf8_t *co #endif //printf("%s funcdecl t = %s, storage_class = x%lx\n", loc.toChars(), t->toChars(), storage_class); + if (pAttrs) + { + StorageClass prefixStc = pAttrs->storageClass; + t = t->addSTC(prefixStc); + + TypeFunction *tf = (TypeFunction *)t; + if (prefixStc & STCpure) + { + if (tf->purity == PUREfwdref) + error("redundant storage class 'pure'"); + tf->purity = PUREfwdref; + } + if (prefixStc & STCnothrow) + { + if (tf->isnothrow) + error("redundant storage class 'nothrow'"); + tf->isnothrow = true; + } + if (prefixStc & STCnogc) + { + if (tf->isnogc) + error("redundant storage class '@nogc'"); + tf->isnogc = true; + } + if (prefixStc & STCproperty) + { + if (tf->isproperty) + error("redundant storage class '@property'"); + tf->isproperty = true; + } + + if (prefixStc & STCref) + { + if (tf->isref) + error("redundant storage class 'ref'"); + tf->isref = true; + } + + StorageClass postfixTrustStc; + switch (tf->trust) + { + case TRUSTdefault: + if (prefixStc & STCsafe) tf->trust = TRUSTsafe; + if (prefixStc & STCsystem) tf->trust = TRUSTsystem; + if (prefixStc & STCtrusted) tf->trust = TRUSTtrusted; + break; + case TRUSTsafe: postfixTrustStc = STCsafe; goto Ltrust; + case TRUSTsystem: postfixTrustStc = STCsystem; goto Ltrust; + case TRUSTtrusted: postfixTrustStc = STCtrusted; goto Ltrust; + Ltrust: + { + if (prefixStc & postfixTrustStc) + error("redundant storage class '%s'", trustToChars(tf->trust)); + else if (prefixStc & (STCsafe | STCsystem | STCtrusted)) + error("conflicting storage class '%s'", trustToChars(tf->trust)); + break; + } + default: + assert(0); + } + + storage_class |= prefixStc & ~(STC_TYPECTOR | STC_FUNCATTR); + pAttrs->storageClass = STCundefined; + } FuncDeclaration *f = new FuncDeclaration(loc, Loc(), ident, storage_class | (disable ? STCdisable : 0), t); addComment(f, comment); if (tpl) constraint = parseConstraint(); - parseContracts(f); - addComment(f, NULL); - Dsymbol *s = f; + Dsymbol *s = parseContracts(f); + addComment(s, NULL); + /* A template parameter list means it's a function template */ if (tpl) @@ -3401,7 +3742,7 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, const utf8_t *co Dsymbols *a2 = new Dsymbols(); a2->push(s); TemplateDeclaration *tempdecl = - new TemplateDeclaration(loc, ident, tpl, NULL/*constraint*/, a2, 0); + new TemplateDeclaration(loc, ident, tpl, NULL, a2, 0); s = tempdecl; } @@ -3475,7 +3816,7 @@ Dsymbols *Parser::parseAutoDeclarations(StorageClass storageClass, const utf8_t Dsymbols *a2 = new Dsymbols(); a2->push(v); TemplateDeclaration *tempdecl = - new TemplateDeclaration(loc, ident, tpl, NULL/*constraint*/, a2, 0); + new TemplateDeclaration(loc, ident, tpl, NULL, a2, 0); s = tempdecl; } @@ -3509,11 +3850,11 @@ Dsymbols *Parser::parseAutoDeclarations(StorageClass storageClass, const utf8_t * Parse contracts following function declaration. */ -void Parser::parseContracts(FuncDeclaration *f) +FuncDeclaration *Parser::parseContracts(FuncDeclaration *f) { LINK linksave = linkage; - bool literal = f->isFuncLiteralDeclaration(); + bool literal = f->isFuncLiteralDeclaration() != NULL; // The following is irrelevant, as it is overridden by sc->linkage in // TypeFunction::semantic @@ -3598,8 +3939,8 @@ void Parser::parseContracts(FuncDeclaration *f) default: if (literal) { - error("missing %s{ ... } for function literal", - (f->frequire || f->fensure) ? "body " : ""); + const char *sbody = (f->frequire || f->fensure) ? "body " : ""; + error("missing %s{ ... } for function literal", sbody); } else if (!f->frequire && !f->fensure) // allow these even with no body { @@ -3614,6 +3955,8 @@ void Parser::parseContracts(FuncDeclaration *f) } linkage = linksave; + + return f; } /***************************************** @@ -3856,7 +4199,7 @@ Expression *Parser::parseDefaultInitExp() Token *t = peek(&token); if (t->value == TOKcomma || t->value == TOKrparen) { - Expression *e; + Expression *e = NULL; if (token.value == TOKfile) e = new FileInitExp(token.loc); else if (token.value == TOKline) @@ -3867,6 +4210,8 @@ Expression *Parser::parseDefaultInitExp() e = new FuncInitExp(token.loc); else if (token.value == TOKprettyfunc) e = new PrettyFuncInitExp(token.loc); + else + assert(0); nextToken(); return e; } @@ -3962,6 +4307,7 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr) case TOKtrue: case TOKfalse: case TOKstring: + case TOKxstring: case TOKlparen: case TOKcast: case TOKmul: @@ -3998,20 +4344,17 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr) Token *t = peek(&token); if (t->value == TOKassert) { - nextToken(); s = new StaticAssertStatement(parseStaticAssert()); break; } if (t->value == TOKif) { - nextToken(); cond = parseStaticIfCondition(); goto Lcondition; } if (t->value == TOKimport) - { nextToken(); - Dsymbols *imports = new Dsymbols(); - parseImport(imports, 1); // static import ... + { + Dsymbols *imports = parseImport(); s = new ImportStatement(loc, imports); if (flags & PSscope) s = new ScopeStatement(loc, s); @@ -4043,6 +4386,8 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr) // bug 7773: int.max is always a part of expression if (peekNext() == TOKdot) goto Lexp; + if (peekNext() == TOKlparen) + goto Lexp; case TOKtypedef: case TOKalias: case TOKconst: @@ -4050,7 +4395,6 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr) case TOKabstract: case TOKextern: case TOKalign: - case TOKinvariant: case TOKimmutable: case TOKshared: case TOKwild: @@ -4066,7 +4410,7 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr) case TOKinterface: Ldeclaration: { - Dsymbols *a = parseDeclarations(STCundefined, NULL); + Dsymbols *a = parseDeclarations(false, NULL, NULL); if (a->dim > 1) { Statements *as = new Statements(); @@ -4281,9 +4625,6 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr) switch (token.value) { case TOKref: -#if D1INOUT - case TOKinout: -#endif stc = STCref; goto Lagain; @@ -4294,13 +4635,10 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr) goto Lagain; } break; - case TOKinvariant: case TOKimmutable: if (peekNext() != TOKlparen) { stc = STCimmutable; - if (token.value == TOKinvariant) - error("use 'immutable' instead of 'invariant'"); goto Lagain; } break; @@ -4399,13 +4737,10 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr) goto LagainStc; } break; - case TOKinvariant: case TOKimmutable: if (peekNext() != TOKlparen) { stc = STCimmutable; - if (token.value == TOKinvariant) - error("use 'immutable' instead of 'invariant'"); goto LagainStc; } break; @@ -4437,11 +4772,11 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr) check(TOKassign); arg = new Parameter(storageClass, at, ai, NULL); } - // Check for " ident;" else if (storageClass == 0 && token.value == TOKidentifier && peek(&token)->value == TOKsemicolon) { + // Check for " ident;" arg = new Parameter(0, NULL, token.ident, NULL); nextToken(); nextToken(); @@ -4539,7 +4874,7 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr) { Loc lookingForElseSave = lookingForElse; lookingForElse = loc; - ifbody = parseStatement(0 /*PSsemi*/); + ifbody = parseStatement(0); lookingForElse = lookingForElseSave; } elsebody = NULL; @@ -4547,7 +4882,7 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr) { Loc elseloc = token.loc; nextToken(); - elsebody = parseStatement(0 /*PSsemi*/); + elsebody = parseStatement(0); checkDanglingElse(elseloc); } s = new ConditionalStatement(loc, cond, ifbody, elsebody); @@ -4874,6 +5209,10 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr) Loc labelloc; nextToken(); + StorageClass stc = parsePostfix(NULL); + if (stc & (STCconst | STCimmutable | STCshared | STCwild)) + error("const/immutable/shared/inout attributes are not allowed on asm blocks"); + check(TOKlcurly); Token *toklist = NULL; Token **ptoklist = &toklist; @@ -4943,9 +5282,8 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr) goto Lerror; #ifdef IN_GCC - case TOKlparen: case TOKstring: - // If the first token is a string or '(', parse as extended asm. + // If the first token is a string, parse as extended asm. if (! toklist) { s = parseExtAsm(); @@ -4957,7 +5295,7 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr) default: Ldefault: - *ptoklist = new Token(); + *ptoklist = Token::alloc(); memcpy(*ptoklist, &token, sizeof(Token)); ptoklist = &(*ptoklist)->next; *ptoklist = NULL; @@ -4967,15 +5305,14 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr) } break; } - s = new CompoundStatement(loc, statements); + s = new CompoundAsmStatement(loc, statements, stc); nextToken(); break; } case TOKimport: { - Dsymbols *imports = new Dsymbols(); - parseImport(imports, 0); + Dsymbols *imports = parseImport(); s = new ImportStatement(loc, imports); if (flags & PSscope) s = new ScopeStatement(loc, s); @@ -5008,21 +5345,14 @@ Statement *Parser::parseStatement(int flags, const utf8_t** endPtr) } #ifdef IN_GCC -Statement *Parser::parseExtAsm() +/*********************************** + * Parse list of extended asm input or output operands. + * ExtAsmOperand: + * [Identifier] StringLiteral (Identifier), ... + */ +int Parser::parseExtAsmOperands(Expressions *args, Identifiers *names, Expressions *constraints) { - Expression *insn; - Expressions *args = NULL; - Identifiers *names = NULL; - Expressions *constraints = NULL; - int outputargs = 0; - Expressions *clobbers = NULL; - bool isInputPhase = false; // Output operands first, then input. - - insn = parseExpression(); - if (token.value == TOKrparen || token.value == TOKsemicolon) - goto Ldone; - - check(TOKcolon); + int numargs = 0; while (1) { @@ -5033,16 +5363,9 @@ Statement *Parser::parseExtAsm() switch (token.value) { case TOKsemicolon: - case TOKrparen: - goto Ldone; - case TOKcolon: - nextToken(); - goto LnextPhase; - case TOKeof: - error("unterminated statement"); - goto Ldone; + return numargs; case TOKlbracket: nextToken(); @@ -5052,39 +5375,49 @@ Statement *Parser::parseExtAsm() nextToken(); } else + { error("expected identifier after '['"); + return numargs; + } check(TOKrbracket); - // drop through + // fall through default: constraint = parsePrimaryExp(); if (constraint->op != TOKstring) - error("expected constant string constraint for operand"); - arg = parseAssignExp(); - if (! args) { - args = new Expressions; - constraints = new Expressions; - names = new Identifiers; + error(constraint->loc, "expected constant string constraint for operand, not '%s'", constraint->toChars()); + goto Lerror; } + arg = parseAssignExp(); + args->push(arg); names->push(name); constraints->push(constraint); - if (! isInputPhase) - outputargs++; + numargs++; if (token.value == TOKcomma) nextToken(); break; } - continue; - - LnextPhase: - if (! isInputPhase) - isInputPhase = true; - else - break; } +Lerror: + while (token.value != TOKrcurly && + token.value != TOKsemicolon && + token.value != TOKeof) + nextToken(); + + return numargs; +} + +/*********************************** + * Parse list of extended asm clobbers. + * ExtAsmClobbers: + * StringLiteral, ... + */ +Expressions *Parser::parseExtAsmClobbers() +{ + Expressions *clobbers = NULL; while (1) { @@ -5093,31 +5426,144 @@ Statement *Parser::parseExtAsm() switch (token.value) { case TOKsemicolon: - case TOKrparen: - goto Ldone; - + case TOKcolon: case TOKeof: - error("unterminated statement"); - goto Ldone; + return clobbers; - default: + case TOKstring: clobber = parseAssignExp(); - if (clobber->op != TOKstring) - error("expected constant string constraint for clobber name"); - if (! clobbers) + if (!clobbers) clobbers = new Expressions; clobbers->push(clobber); if (token.value == TOKcomma) nextToken(); break; + + default: + error("expected constant string constraint for clobber name, not '%s'", token.toChars()); + goto Lerror; + } + } +Lerror: + while (token.value != TOKrcurly && + token.value != TOKsemicolon && + token.value != TOKeof) + nextToken(); + + return clobbers; +} + +/*********************************** + * Parse list of extended asm goto labels. + * ExtAsmGotoLabels: + * Identifier, ... + */ +Identifiers *Parser::parseExtAsmGotoLabels() +{ + Identifiers *labels = NULL; + + while (1) + { + switch (token.value) + { + case TOKsemicolon: + case TOKeof: + return labels; + + case TOKidentifier: + if (!labels) + labels = new Identifiers(); + labels->push(token.ident); + + if (nextToken() == TOKcomma) + nextToken(); + break; + + default: + error("expected identifier for goto label name, not '%s'", token.toChars()); + goto Lerror; + } + } +Lerror: + while (token.value != TOKrcurly && + token.value != TOKsemicolon && + token.value != TOKeof) + nextToken(); + + return labels; +} + +/*********************************** + * Parse an extended asm statement. + * ExtAsmStatement: + * asm { StringLiterals [ : InputOperands [ : OutputOperands [ : Clobbers [ : GotoLabels ] ] ] ] } + */ + +Statement *Parser::parseExtAsm() +{ + Expressions *args = NULL; + Identifiers *names = NULL; + Expressions *constraints = NULL; + int outputargs = 0; + Expressions *clobbers = NULL; + Identifiers *labels = NULL; + Loc loc = token.loc; + + Expression *insn = parseExpression(); + if (token.value == TOKsemicolon) + goto Ldone; + + for (int section = 0; section < 4; ++section) + { + check(TOKcolon); + + switch (section) + { + case 0: + if (!args) + { + args = new Expressions; + constraints = new Expressions; + names = new Identifiers; + } + outputargs = parseExtAsmOperands(args, names, constraints); + break; + + case 1: + parseExtAsmOperands(args, names, constraints); + break; + + case 2: + clobbers = parseExtAsmClobbers(); + break; + + case 3: + labels = parseExtAsmGotoLabels(); + break; + + default: + assert(0); + } + + switch (token.value) + { + case TOKsemicolon: + goto Ldone; + + case TOKeof: + error("matching '}' expected, not end of file"); + goto Ldone; + + default: + continue; } } Ldone: check(TOKsemicolon); - return new ExtAsmStatement(token.loc, insn, args, names, - constraints, outputargs, clobbers); + return new ExtAsmStatement(loc, insn, args, names, + constraints, outputargs, clobbers, labels); } #endif @@ -5166,12 +5612,12 @@ int Parser::isDeclaration(Token *t, int needId, TOK endtok, Token **pt) while (1) { if ((t->value == TOKconst || - t->value == TOKinvariant || t->value == TOKimmutable || t->value == TOKwild || t->value == TOKshared) && peek(t)->value != TOKlparen) - { /* const type + { + /* const type * immutable type * shared type * wild type @@ -5191,7 +5637,8 @@ int Parser::isDeclaration(Token *t, int needId, TOK endtok, Token **pt) if ( needId == 1 || (needId == 0 && !haveId) || (needId == 2 && haveId)) - { if (pt) + { + if (pt) *pt = t; goto Lis; } @@ -5292,6 +5739,7 @@ int Parser::isBasicType(Token **pt) case TOKwcharv: case TOKdcharv: case TOKstring: + case TOKxstring: case TOKfile: case TOKline: case TOKmodulestring: @@ -5320,7 +5768,6 @@ int Parser::isBasicType(Token **pt) goto L3; case TOKconst: - case TOKinvariant: case TOKimmutable: case TOKshared: case TOKwild: @@ -5492,7 +5939,6 @@ int Parser::isDeclarator(Token **pt, int *haveId, int *haveTpl, TOK endtok) switch (t->value) { case TOKconst: - case TOKinvariant: case TOKimmutable: case TOKshared: case TOKwild: @@ -5559,19 +6005,16 @@ int Parser::isParameters(Token **pt) t = peek(t); break; -#if D1INOUT - case TOKinout: -#endif case TOKin: case TOKout: case TOKref: case TOKlazy: + case TOKscope: case TOKfinal: case TOKauto: continue; case TOKconst: - case TOKinvariant: case TOKimmutable: case TOKshared: case TOKwild: @@ -5787,7 +6230,6 @@ int Parser::skipAttributes(Token *t, Token **pt) switch (t->value) { case TOKconst: - case TOKinvariant: case TOKimmutable: case TOKshared: case TOKwild: @@ -5807,13 +6249,15 @@ int Parser::skipAttributes(Token *t, Token **pt) case TOKat: t = peek(t); if (t->value == TOKidentifier) - { /* @identifier + { + /* @identifier * @identifier!arg * @identifier!(arglist) * any of the above followed by (arglist) * @predefined_attribute */ if (t->ident == Id::property || + t->ident == Id::nogc || t->ident == Id::safe || t->ident == Id::trusted || t->ident == Id::system || @@ -5824,7 +6268,8 @@ int Parser::skipAttributes(Token *t, Token **pt) { t = peek(t); if (t->value == TOKlparen) - { // @identifier!(arglist) + { + // @identifier!(arglist) if (!skipParens(t, &t)) goto Lerror; // t is on the next of closing parenthesis @@ -5834,7 +6279,8 @@ int Parser::skipAttributes(Token *t, Token **pt) // @identifier!arg // Do low rent skipTemplateArgument if (t->value == TOKvector) - { // identifier!__vector(type) + { + // identifier!__vector(type) t = peek(t); if (!skipParens(t, &t)) goto Lerror; @@ -5853,7 +6299,8 @@ int Parser::skipAttributes(Token *t, Token **pt) continue; } if (t->value == TOKlparen) - { // @( ArgumentList ) + { + // @( ArgumentList ) if (!skipParens(t, &t)) goto Lerror; // t is on the next of closing parenthesis @@ -5899,13 +6346,7 @@ Expression *Parser::parsePrimaryExp() TemplateInstance *tempinst; tempinst = new TemplateInstance(loc, id); - nextToken(); - if (token.value == TOKlparen) - // ident!(template_arguments) - tempinst->tiargs = parseTemplateArgumentList(); - else - // ident!template_argument - tempinst->tiargs = parseTemplateArgument(); + tempinst->tiargs = parseTemplateArguments(); e = new ScopeExp(loc, tempinst); } else @@ -6045,6 +6486,7 @@ Expression *Parser::parsePrimaryExp() break; case TOKstring: + case TOKxstring: { // cat adjacent strings utf8_t *s = token.ustring; @@ -6053,7 +6495,8 @@ Expression *Parser::parsePrimaryExp() while (1) { nextToken(); - if (token.value == TOKstring) + if (token.value == TOKstring || + token.value == TOKxstring) { if (token.postfix) { if (token.postfix != postfix) @@ -6102,6 +6545,12 @@ Expression *Parser::parsePrimaryExp() case TOKdchar: t = Type::tdchar; goto LabelX; LabelX: nextToken(); + if (token.value == TOKlparen) + { + e = new TypeExp(loc, t); + e = new CallExp(loc, e, parseArguments()); + break; + } check(TOKdot, t->toChars()); if (token.value != TOKidentifier) { error("found '%s' when expecting identifier following '%s.'", token.toChars(), t->toChars()); @@ -6158,7 +6607,7 @@ Expression *Parser::parsePrimaryExp() ident = token.ident; nextToken(); if (token.value == TOKcomma) - args = parseTemplateArgumentList2(); // __traits(identifier, args...) + args = parseTemplateArgumentList(); // __traits(identifier, args...) else check(TOKrparen); // __traits(identifier) @@ -6195,7 +6644,6 @@ Expression *Parser::parsePrimaryExp() token.value == TOKargTypes || token.value == TOKparameters || token.value == TOKconst && peek(&token)->value == TOKrparen || - token.value == TOKinvariant && peek(&token)->value == TOKrparen || token.value == TOKimmutable && peek(&token)->value == TOKrparen || token.value == TOKshared && peek(&token)->value == TOKrparen || token.value == TOKwild && peek(&token)->value == TOKrparen || @@ -6204,12 +6652,10 @@ Expression *Parser::parsePrimaryExp() token.value == TOKreturn)) { tok2 = token.value; - if (token.value == TOKinvariant) - { - error("use 'immutable' instead of 'invariant'"); - tok2 = TOKimmutable; - } nextToken(); + + if (tok2 == TOKtypedef) + deprecation("typedef is removed"); } else { @@ -6221,7 +6667,8 @@ Expression *Parser::parsePrimaryExp() if (token.value == TOKcomma) tpl = parseTemplateParameterList(1); else - { tpl = new TemplateParameters(); + { + tpl = new TemplateParameters(); check(TOKrparen); } } @@ -6229,7 +6676,8 @@ Expression *Parser::parsePrimaryExp() check(TOKrparen); } else - { error("(type identifier : specialization) expected following is"); + { + error("(type identifier : specialization) expected following is"); goto Lerr; } e = new IsExp(loc, targ, ident, tok, tspec, tok2, tpl); @@ -6354,28 +6802,32 @@ Expression *Parser::parsePrimaryExp() save = token.value; nextToken(); if (token.value != TOKlparen && token.value != TOKlcurly) - { // function type (parameters) { statements... } + { + // function type (parameters) { statements... } // delegate type (parameters) { statements... } tret = parseBasicType(); tret = parseBasicType2(tret); // function return type } if (token.value == TOKlparen) - { // function (parameters) { statements... } + { + // function (parameters) { statements... } // delegate (parameters) { statements... } } else - { // function { statements... } + { + // function { statements... } // delegate { statements... } break; } /* fall through to TOKlparen */ case TOKlparen: - { // (parameters) => expression + { + // (parameters) => expression // (parameters) { statements... } parameters = parseParameters(&varargs, &tpl); - stc = parsePostfix(); + stc = parsePostfix(NULL); if (stc & (STCconst | STCimmutable | STCshared | STCwild)) error("const/immutable/shared/inout attributes are only valid for non-static member functions"); break; @@ -6385,7 +6837,8 @@ Expression *Parser::parsePrimaryExp() break; case TOKidentifier: - { // identifier => expression + { + // identifier => expression parameters = new Parameters(); id = Lexer::uniqueId("__T"); t = new TypeIdentifier(loc, id); @@ -6460,15 +6913,8 @@ Expression *Parser::parsePostExp(Expression *e) nextToken(); if (token.value == TOKnot && peekNext() != TOKis && peekNext() != TOKin) - { // identifier!(template-argument-list) - Objects *tiargs; - nextToken(); - if (token.value == TOKlparen) - // ident!(template_arguments) - tiargs = parseTemplateArgumentList(); - else - // ident!template_argument - tiargs = parseTemplateArgument(); + { + Objects *tiargs = parseTemplateArguments(); e = new DotTemplateInstanceExp(loc, e, id, tiargs); } else @@ -6503,45 +6949,29 @@ Expression *Parser::parsePostExp(Expression *e) // array[lwr .. upr] Expression *index; Expression *upr; + Expressions *arguments = new Expressions(); inBrackets++; nextToken(); - if (token.value == TOKrbracket) - { // array[] - inBrackets--; - e = new SliceExp(loc, e, NULL, NULL); - nextToken(); - } - else + while (token.value != TOKrbracket && token.value != TOKeof) { index = parseAssignExp(); if (token.value == TOKslice) - { // array[lwr .. upr] + { + // array[..., lwr..upr, ...] nextToken(); upr = parseAssignExp(); - e = new SliceExp(loc, e, index, upr); + arguments->push(new IntervalExp(loc, index, upr)); } else - { // array[index, i2, i3, i4, ...] - Expressions *arguments = new Expressions(); arguments->push(index); - if (token.value == TOKcomma) - { - nextToken(); - while (token.value != TOKrbracket && token.value != TOKeof) - { - Expression *arg = parseAssignExp(); - arguments->push(arg); - if (token.value == TOKrbracket) - break; - check(TOKcomma); - } - } - e = new ArrayExp(loc, e, arguments); - } - check(TOKrbracket); - inBrackets--; + if (token.value == TOKrbracket) + break; + check(TOKcomma); } + check(TOKrbracket); + inBrackets--; + e = new ArrayExp(loc, e, arguments); continue; } @@ -6634,12 +7064,9 @@ Expression *Parser::parseUnaryExp() nextToken(); continue; - case TOKinvariant: case TOKimmutable: if (peekNext() == TOKlparen) break; - if (token.value == TOKinvariant) - error("use 'immutable' instead of 'invariant'"); m |= MODimmutable; nextToken(); continue; @@ -6683,7 +7110,6 @@ Expression *Parser::parseUnaryExp() case TOKwild: case TOKshared: case TOKconst: - case TOKinvariant: case TOKimmutable: // immutable(type)(arguments) / immutable(type).init { StorageClass stc = parseTypeCtor(); @@ -6694,7 +7120,8 @@ Expression *Parser::parseUnaryExp() { nextToken(); if (token.value != TOKidentifier) - { error("Identifier expected following (type)."); + { + error("Identifier expected following (type)."); return NULL; } e = typeDotIdExp(loc, t, token.ident); @@ -7307,6 +7734,8 @@ void initPrecedence() precedence[TOKsymoff] = PREC_primary; precedence[TOKstructliteral] = PREC_primary; precedence[TOKarraylength] = PREC_primary; + precedence[TOKdelegateptr] = PREC_primary; + precedence[TOKdelegatefuncptr] = PREC_primary; precedence[TOKremove] = PREC_primary; precedence[TOKtuple] = PREC_primary; precedence[TOKtraits] = PREC_primary; @@ -7418,5 +7847,6 @@ void initPrecedence() precedence[TOKcomma] = PREC_expr; precedence[TOKdeclaration] = PREC_expr; -} + precedence[TOKinterval] = PREC_assign; +} diff --git a/gcc/d/dfrontend/parse.h b/gcc/d/dfrontend/parse.h index 922263074..912154077 100644 --- a/gcc/d/dfrontend/parse.h +++ b/gcc/d/dfrontend/parse.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2011 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/parse.h + */ #ifndef DMD_PARSE_H #define DMD_PARSE_H @@ -45,6 +46,7 @@ struct ModuleDeclaration; class TemplateDeclaration; class TemplateInstance; class StaticAssert; +struct PrefixAttributes; /************************************ * These control how parseStatement() works. @@ -73,33 +75,33 @@ class Parser : public Lexer Parser(Module *module, const utf8_t *base, size_t length, int doDocComment); Dsymbols *parseModule(); - Dsymbols *parseDeclDefs(int once, Dsymbol **pLastDecl = NULL); + Dsymbols *parseDeclDefs(int once, Dsymbol **pLastDecl = NULL, PrefixAttributes *pAttrs = NULL); Dsymbols *parseAutoDeclarations(StorageClass storageClass, const utf8_t *comment); - Dsymbols *parseBlock(Dsymbol **pLastDecl); + Dsymbols *parseBlock(Dsymbol **pLastDecl, PrefixAttributes *pAttrs = NULL); void composeStorageClass(StorageClass stc); StorageClass parseAttribute(Expressions **pexps); - StorageClass parsePostfix(); + StorageClass parsePostfix(Expressions **pudas); StorageClass parseTypeCtor(); Expression *parseConstraint(); TemplateDeclaration *parseTemplateDeclaration(bool ismixin = false); TemplateParameters *parseTemplateParameterList(int flag = 0); Dsymbol *parseMixin(); + Objects *parseTemplateArguments(); Objects *parseTemplateArgumentList(); - Objects *parseTemplateArgumentList2(); - Objects *parseTemplateArgument(); + Objects *parseTemplateSingleArgument(); StaticAssert *parseStaticAssert(); TypeQualified *parseTypeof(); Type *parseVector(); - LINK parseLinkage(); + LINK parseLinkage(Identifiers **); Condition *parseDebugCondition(); Condition *parseVersionCondition(); Condition *parseStaticIfCondition(); Dsymbol *parseCtor(); - DtorDeclaration *parseDtor(); - StaticCtorDeclaration *parseStaticCtor(); - StaticDtorDeclaration *parseStaticDtor(); - SharedStaticCtorDeclaration *parseSharedStaticCtor(); - SharedStaticDtorDeclaration *parseSharedStaticDtor(); + Dsymbol *parseDtor(); + Dsymbol *parseStaticCtor(); + Dsymbol *parseStaticDtor(); + Dsymbol *parseSharedStaticCtor(); + Dsymbol *parseSharedStaticDtor(); InvariantDeclaration *parseInvariant(); UnitTestDeclaration *parseUnitTest(); NewDeclaration *parseNew(); @@ -108,18 +110,23 @@ class Parser : public Lexer EnumDeclaration *parseEnum(); Dsymbol *parseAggregate(); BaseClasses *parseBaseClasses(); - Import *parseImport(Dsymbols *decldefs, int isstatic); + Dsymbols *parseImport(); Type *parseType(Identifier **pident = NULL, TemplateParameters **tpl = NULL); Type *parseBasicType(); Type *parseBasicType2(Type *t); - Type *parseDeclarator(Type *t, Identifier **pident, TemplateParameters **tpl = NULL, StorageClass storage_class = 0, int* pdisable = NULL); - Dsymbols *parseDeclarations(StorageClass storage_class, const utf8_t *comment); - void parseContracts(FuncDeclaration *f); + Type *parseDeclarator(Type *t, Identifier **pident, + TemplateParameters **tpl = NULL, StorageClass storage_class = 0, int* pdisable = NULL, Expressions **pudas = NULL); + void parseStorageClasses(StorageClass &storage_class, LINK &link, unsigned &structalign, Expressions *&udas); + Dsymbols *parseDeclarations(bool autodecl, PrefixAttributes *pAttrs, const utf8_t *comment); + FuncDeclaration *parseContracts(FuncDeclaration *f); void checkDanglingElse(Loc elseloc); /** endPtr used for documented unittests */ Statement *parseStatement(int flags, const utf8_t** endPtr = NULL); #ifdef IN_GCC Statement *parseExtAsm(); + int parseExtAsmOperands(Expressions *args, Identifiers *names, Expressions *constraints); + Expressions *parseExtAsmClobbers(); + Identifiers *parseExtAsmGotoLabels(); #endif Initializer *parseInitializer(); Expression *parseDefaultInitExp(); diff --git a/gcc/d/dfrontend/port.h b/gcc/d/dfrontend/port.h index 4339378a0..6663c1f27 100644 --- a/gcc/d/dfrontend/port.h +++ b/gcc/d/dfrontend/port.h @@ -1,8 +1,11 @@ -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com +/* Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/port.h + */ #ifndef PORT_H #define PORT_H diff --git a/gcc/d/dfrontend/readme.txt b/gcc/d/dfrontend/readme.txt index 2b79949dd..87229c86b 100644 --- a/gcc/d/dfrontend/readme.txt +++ b/gcc/d/dfrontend/readme.txt @@ -1,7 +1,7 @@ The D Programming Language Compiler Front End Source - Copyright (c) 1999-2013, by Digital Mars + Copyright (c) 1999-2014, by Digital Mars http://www.digitalmars.com/ All Rights Reserved @@ -12,10 +12,9 @@ of the D Programming Language defined in the documents at http://dlang.org/ These sources are free, they are redistributable and modifiable -under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 1, or (at your option) -any later version (attached as gpl.txt), -or the Artistic License (attached as artistic.txt). +under the terms of the Boost Software License, Version 1.0. +The terms of this license are in the file boostlicense.txt, +or see http://www.boost.org/LICENSE_1_0.txt. The optimizer and code generator sources are covered under a separate license, backendlicense.txt. diff --git a/gcc/d/dfrontend/rmem.c b/gcc/d/dfrontend/rmem.c index deb7586ce..a8628aaaf 100644 --- a/gcc/d/dfrontend/rmem.c +++ b/gcc/d/dfrontend/rmem.c @@ -1,11 +1,11 @@ -// Copyright (c) 2000-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Copyright (c) 2000-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/rmem.c + */ #include #include @@ -133,16 +133,22 @@ void Mem::error() /* =================================================== */ +#if defined(__has_feature) +#if __has_feature(address_sanitizer) +#define USE_ASAN_NEW_DELETE +#endif +#endif + +#if !defined(USE_ASAN_NEW_DELETE) + #if 1 /* Allocate, but never release */ -// Allocate a little less than 64kB because the C runtime adds some overhead that -// causes the actual memory block to be larger than 64kB otherwise. E.g. the dmc -// runtime rounds the size up to 128kB, but the remaining space in the chunk is less -// than 64kB, so it cannot be used by another chunk. -#define CHUNK_SIZE (4096 * 16 - 64) +// Allocate a little less than 1Mb because the C runtime adds some overhead that +// causes the actual memory block to be larger than 1Mb otherwise. +#define CHUNK_SIZE (256 * 4096 - 64) static size_t heapleft = 0; static void *heapp; @@ -204,3 +210,5 @@ void operator delete(void *p) } #endif + +#endif diff --git a/gcc/d/dfrontend/rmem.h b/gcc/d/dfrontend/rmem.h index c4f784a04..5f5b83f8b 100644 --- a/gcc/d/dfrontend/rmem.h +++ b/gcc/d/dfrontend/rmem.h @@ -1,11 +1,11 @@ - +// Compiler implementation of the D programming language // Copyright (c) 2000-2012 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt +// https://github.com/D-Programming-Language/dmd/blob/master/src/root/rmem.h #ifndef ROOT_MEM_H #define ROOT_MEM_H diff --git a/gcc/d/dfrontend/root.h b/gcc/d/dfrontend/root.h index 06e7a673e..bf4f1e1e0 100644 --- a/gcc/d/dfrontend/root.h +++ b/gcc/d/dfrontend/root.h @@ -1,11 +1,11 @@ -// Copyright (c) 1999-2011 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/root.h + */ #ifndef ROOT_H #define ROOT_H diff --git a/gcc/d/dfrontend/sapply.c b/gcc/d/dfrontend/sapply.c index 5a267e342..3d7f2b2a5 100644 --- a/gcc/d/dfrontend/sapply.c +++ b/gcc/d/dfrontend/sapply.c @@ -1,18 +1,20 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/sapply.c + */ #include #include #include "mars.h" #include "statement.h" +#include "visitor.h" /************************************** @@ -26,181 +28,130 @@ * Creating an iterator for this would be much more complex. */ -typedef bool (*sapply_fp_t)(Statement *, void *); - -bool Statement::apply(sapply_fp_t fp, void *param) +class PostorderStatementVisitor : public StoppableVisitor { - return (*fp)(this, param); -} - -/****************************** - * Perform apply() on an t if not null - */ -#define scondApply(t, fp, param) (t ? t->apply(fp, param) : false) - - +public: + StoppableVisitor *v; + PostorderStatementVisitor(StoppableVisitor *v) : v(v) {} -bool PeelStatement::apply(sapply_fp_t fp, void *param) -{ - return s->apply(fp, param) || - (*fp)(this, param); -} - -bool CompoundStatement::apply(sapply_fp_t fp, void *param) -{ - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; - - bool r = scondApply(s, fp, param); - if (r) - return r; + bool doCond(Statement *s) + { + if (!stop && s) + s->accept(this); + return stop; } - return (*fp)(this, param); -} - -bool UnrolledLoopStatement::apply(sapply_fp_t fp, void *param) -{ - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; - - bool r = scondApply(s, fp, param); - if (r) - return r; + bool applyTo(Statement *s) + { + s->accept(v); + stop = v->stop; + return true; } - return (*fp)(this, param); -} - -bool ScopeStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(statement, fp, param) || - (*fp)(this, param); -} - -bool WhileStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(body, fp, param) || - (*fp)(this, param); -} -bool DoStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(body, fp, param) || - (*fp)(this, param); -} - -bool ForStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(init, fp, param) || - scondApply(body, fp, param) || - (*fp)(this, param); -} - -bool ForeachStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(body, fp, param) || - (*fp)(this, param); -} - -bool ForeachRangeStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(body, fp, param) || - (*fp)(this, param); -} - -bool IfStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(ifbody, fp, param) || - scondApply(elsebody, fp, param) || - (*fp)(this, param); -} - -bool ConditionalStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(ifbody, fp, param) || - scondApply(elsebody, fp, param) || - (*fp)(this, param); -} - -bool PragmaStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(body, fp, param) || - (*fp)(this, param); -} - -bool SwitchStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(body, fp, param) || - (*fp)(this, param); -} - -bool CaseStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(statement, fp, param) || - (*fp)(this, param); -} - -bool CaseRangeStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(statement, fp, param) || - (*fp)(this, param); -} - -bool DefaultStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(statement, fp, param) || - (*fp)(this, param); -} - -bool SynchronizedStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(body, fp, param) || - (*fp)(this, param); -} - -bool WithStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(body, fp, param) || - (*fp)(this, param); -} - -bool TryCatchStatement::apply(sapply_fp_t fp, void *param) -{ - bool r = scondApply(body, fp, param); - if (r) - return r; - - for (size_t i = 0; i < catches->dim; i++) - { Catch *c = (*catches)[i]; - - r = scondApply(c->handler, fp, param); - if (r) - return r; + void visit(Statement *s) + { + applyTo(s); } - return (*fp)(this, param); -} - -bool TryFinallyStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(body, fp, param) || - scondApply(finalbody, fp, param) || - (*fp)(this, param); -} - -bool OnScopeStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(statement, fp, param) || - (*fp)(this, param); -} - -bool DebugStatement::apply(sapply_fp_t fp, void *param) -{ - return scondApply(statement, fp, param) || - (*fp)(this, param); -} + void visit(PeelStatement *s) + { + doCond(s->s) || applyTo(s); + } + void visit(CompoundStatement *s) + { + for (size_t i = 0; i < s->statements->dim; i++) + if (doCond((*s->statements)[i])) + return; + applyTo(s); + } + void visit(UnrolledLoopStatement *s) + { + for (size_t i = 0; i < s->statements->dim; i++) + if (doCond((*s->statements)[i])) + return; + applyTo(s); + } + void visit(ScopeStatement *s) + { + doCond(s->statement) || applyTo(s); + } + void visit(WhileStatement *s) + { + doCond(s->body) || applyTo(s); + } + void visit(DoStatement *s) + { + doCond(s->body) || applyTo(s); + } + void visit(ForStatement *s) + { + doCond(s->init) || doCond(s->body) || applyTo(s); + } + void visit(ForeachStatement *s) + { + doCond(s->body) || applyTo(s); + } + void visit(ForeachRangeStatement *s) + { + doCond(s->body) || applyTo(s); + } + void visit(IfStatement *s) + { + doCond(s->ifbody) || doCond(s->elsebody) || applyTo(s); + } + void visit(PragmaStatement *s) + { + doCond(s->body) || applyTo(s); + } + void visit(SwitchStatement *s) + { + doCond(s->body) || applyTo(s); + } + void visit(CaseStatement *s) + { + doCond(s->statement) || applyTo(s); + } + void visit(DefaultStatement *s) + { + doCond(s->statement) || applyTo(s); + } + void visit(SynchronizedStatement *s) + { + doCond(s->body) || applyTo(s); + } + void visit(WithStatement *s) + { + doCond(s->body) || applyTo(s); + } + void visit(TryCatchStatement *s) + { + if (doCond(s->body)) + return; + + for (size_t i = 0; i < s->catches->dim; i++) + if (doCond((*s->catches)[i]->handler)) + return; + applyTo(s); + } + void visit(TryFinallyStatement *s) + { + doCond(s->body) || doCond(s->finalbody) || applyTo(s); + } + void visit(OnScopeStatement *s) + { + doCond(s->statement) || applyTo(s); + } + void visit(DebugStatement *s) + { + doCond(s->statement) || applyTo(s); + } + void visit(LabelStatement *s) + { + doCond(s->statement) || applyTo(s); + } +}; -bool LabelStatement::apply(sapply_fp_t fp, void *param) +bool walkPostorder(Statement *s, StoppableVisitor *v) { - return scondApply(statement, fp, param) || - (*fp)(this, param); + PostorderStatementVisitor pv(v); + s->accept(&pv); + return v->stop; } - diff --git a/gcc/d/dfrontend/scope.c b/gcc/d/dfrontend/scope.c index 5b2ce7d7c..9f3433cb4 100644 --- a/gcc/d/dfrontend/scope.c +++ b/gcc/d/dfrontend/scope.c @@ -1,11 +1,13 @@ -// Copyright (c) 1999-2010 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/scope.c + */ #include #include @@ -31,7 +33,7 @@ Scope *Scope::freelist = NULL; -void *Scope::operator new(size_t size) +Scope *Scope::alloc() { if (freelist) { @@ -43,9 +45,7 @@ void *Scope::operator new(size_t size) return s; } - void *p = ::operator new(size); - //printf("new %p\n", p); - return p; + return new Scope(); } Scope::Scope() @@ -55,11 +55,12 @@ Scope::Scope() //printf("Scope::Scope() %p\n", this); this->module = NULL; this->scopesym = NULL; - this->sd = NULL; + this->sds = NULL; this->enclosing = NULL; this->parent = NULL; this->sw = NULL; this->tf = NULL; + this->os = NULL; this->tinst = NULL; this->sbreak = NULL; this->scontinue = NULL; @@ -73,12 +74,11 @@ Scope::Scope() this->explicitProtection = 0; this->stc = 0; this->depmsg = NULL; - this->offset = 0; this->inunion = 0; this->nofree = 0; this->noctor = 0; this->intypeof = 0; - this->speculative = 0; + this->speculative = false; this->lastVar = NULL; this->callSuper = 0; this->fieldinit = NULL; @@ -90,62 +90,28 @@ Scope::Scope() this->userAttribDecl = NULL; } -Scope::Scope(Scope *enclosing) +Scope *Scope::copy() { - //printf("Scope::Scope(enclosing = %p) %p\n", enclosing, this); - assert(!(enclosing->flags & SCOPEfree)); - this->module = enclosing->module; - this->func = enclosing->func; - this->parent = enclosing->parent; - this->scopesym = NULL; - this->sd = NULL; - this->sw = enclosing->sw; - this->tf = enclosing->tf; - this->tinst = enclosing->tinst; - this->sbreak = enclosing->sbreak; - this->scontinue = enclosing->scontinue; - this->fes = enclosing->fes; - this->callsc = enclosing->callsc; - this->structalign = enclosing->structalign; - this->enclosing = enclosing; -#ifdef DEBUG - if (enclosing->enclosing) - assert(!(enclosing->enclosing->flags & SCOPEfree)); - if (this == enclosing->enclosing) - { - printf("this = %p, enclosing = %p, enclosing->enclosing = %p\n", this, enclosing, enclosing->enclosing); - } - assert(this != enclosing->enclosing); -#endif - this->slabel = NULL; - this->linkage = enclosing->linkage; - this->protection = enclosing->protection; - this->explicitProtection = enclosing->explicitProtection; - this->depmsg = enclosing->depmsg; - this->stc = enclosing->stc; - this->offset = 0; - this->inunion = enclosing->inunion; - this->nofree = 0; - this->noctor = enclosing->noctor; - this->intypeof = enclosing->intypeof; - this->speculative = enclosing->speculative; - this->lastVar = enclosing->lastVar; - this->callSuper = enclosing->callSuper; - this->fieldinit = enclosing->saveFieldInit(); - this->fieldinit_dim = enclosing->fieldinit_dim; - this->flags = (enclosing->flags & (SCOPEcontract | SCOPEdebug | SCOPEctfe | SCOPEcompile)); - this->lastdc = NULL; - this->lastoffset = 0; - this->docbuf = enclosing->docbuf; - this->userAttribDecl = enclosing->userAttribDecl; - assert(this != enclosing); + Scope *sc = Scope::alloc(); + memcpy(sc, this, sizeof(Scope)); + + /* Bugzilla 11777: The copied scope should not inherit fieldinit. + */ + sc->fieldinit = NULL; + + return sc; } Scope *Scope::createGlobal(Module *module) { Scope *sc; - sc = new Scope(); + sc = Scope::alloc(); + memset(sc, 0, sizeof(Scope)); + sc->structalign = STRUCTALIGN_DEFAULT; + sc->linkage = LINKd; + sc->protection = PROTpublic; + sc->module = module; sc->scopesym = new ScopeDsymbol(); sc->scopesym->symtab = new DsymbolTable(); @@ -165,8 +131,29 @@ Scope *Scope::createGlobal(Module *module) Scope *Scope::push() { - //printf("Scope::push()\n"); - Scope *s = new Scope(this); + Scope *s = copy(); + + //printf("Scope::push(this = %p) new = %p\n", this, s); + assert(!(flags & SCOPEfree)); + s->scopesym = NULL; + s->sds = NULL; + s->enclosing = this; +#ifdef DEBUG + if (enclosing) + assert(!(enclosing->flags & SCOPEfree)); + if (s == enclosing) + { + printf("this = %p, enclosing = %p, enclosing->enclosing = %p\n", s, this, enclosing); + } + assert(s != enclosing); +#endif + s->slabel = NULL; + s->nofree = 0; + s->fieldinit = saveFieldInit(); + s->flags = (flags & (SCOPEcontract | SCOPEdebug | SCOPEctfe | SCOPEcompile)); + s->lastdc = NULL; + s->lastoffset = 0; + assert(this != s); return s; } @@ -189,13 +176,12 @@ Scope *Scope::pop() enclosing->callSuper |= callSuper; if (enclosing->fieldinit && fieldinit) { + assert(fieldinit != enclosing->fieldinit); + size_t dim = fieldinit_dim; for (size_t i = 0; i < dim; i++) enclosing->fieldinit[i] |= fieldinit[i]; - /* Workaround regression @@@BUG11777@@@. - Probably this memory is used in future. mem.free(fieldinit); - */ fieldinit = NULL; } } @@ -213,6 +199,25 @@ Scope *Scope::startCTFE() { Scope *sc = this->push(); sc->flags = this->flags | SCOPEctfe; +#if 0 + /* TODO: Currently this is not possible, because we need to + * unspeculative some types and symbols if they are necessary for the + * final executable. Consider: + * + * struct S(T) { + * string toString() const { return "instantiated"; } + * } + * enum x = S!int(); + * void main() { + * // To call x.toString in runtime, compiler should unspeculative S!int. + * assert(x.toString() == "instantiated"); + * } + */ + + // If a template is instantiated from CT evaluated expression, + // compiler can elide its code generation. + sc->speculative = true; +#endif return sc; } @@ -244,17 +249,18 @@ void Scope::mergeCallSuper(Loc loc, unsigned cs) bool ok = true; - // If one has returned without a constructor call, there must be never - // have been ctor calls in the other. if ( (aRet && !aAny && bAny) || (bRet && !bAny && aAny)) - { ok = false; + { + // If one has returned without a constructor call, there must be never + // have been ctor calls in the other. + ok = false; } - // If one branch has called a ctor and then exited, anything the - // other branch has done is OK (except returning without a - // ctor call, but we already checked that). else if (aRet && aAll) { + // If one branch has called a ctor and then exited, anything the + // other branch has done is OK (except returning without a + // ctor call, but we already checked that). callSuper |= cs & (CSXany_ctor | CSXlabel); } else if (bRet && bAll) @@ -317,62 +323,6 @@ bool mergeFieldInit(Loc loc, unsigned &fieldInit, unsigned fi, bool mustInit) return ok; } -#if 0 - // This does a primitive flow analysis to support the restrictions - // regarding when and how constructors can appear. - // It merges the results of two paths. - // The two paths are fieldInit and fi; the result is merged into fieldInit. - - if (fi != fieldInit) - { // Have ALL branches called a constructor? - int aAll = (fi & CSXthis_ctor) != 0; - int bAll = (fieldInit & CSXthis_ctor) != 0; - - // Have ANY branches called a constructor? - bool aAny = (fi & CSXany_ctor) != 0; - bool bAny = (fieldInit & CSXany_ctor) != 0; - - // Have any branches returned? - bool aRet = (fi & CSXreturn) != 0; - bool bRet = (fieldInit & CSXreturn) != 0; - - bool ok = true; - -printf("L%d fieldInit = x%x, fi = x%x\n", __LINE__, fieldInit, fi); - - // If one has returned without a constructor call, there must be never - // have been ctor calls in the other. - if ( (aRet && !aAny && bAny) || - (bRet && !bAny && aAny)) - { ok = false; -printf("L%d\n", __LINE__); - } - // If one branch has called a ctor and then exited, anything the - // other branch has done is OK (except returning without a - // ctor call, but we already checked that). - else if (aRet && aAll) - { - //fieldInit |= fi & (CSXany_ctor | CSXlabel); -printf("L%d -> fieldInit = x%x\n", __LINE__, fieldInit); - } - else if (bRet && bAll) - { - fieldInit = fi;// | (fieldInit & (CSXany_ctor | CSXlabel)); -printf("L%d -> fieldInit = x%x\n", __LINE__, fieldInit); - } - else - { // Both branches must have called ctors, or both not. - ok = (aAll == bAll); - // If one returned without a ctor, we must remember that - // (Don't bother if we've already found an error) - if (ok && aRet && !aAny) - fieldInit |= CSXreturn; - fieldInit |= fi & (CSXany_ctor | CSXlabel); -printf("L%d ok = %d, fieldInit = x%x, fi = x%x\n", __LINE__, ok, fieldInit, fi); - } - return ok; - } -#endif return true; } @@ -587,6 +537,7 @@ void *scope_search_fp(void *arg, const char *seed) assert(id); Scope *sc = (Scope *)arg; + Module::clearCache(); Dsymbol *s = sc->search(Loc(), id, NULL); return (void*)s; } diff --git a/gcc/d/dfrontend/scope.h b/gcc/d/dfrontend/scope.h index 5fd364b4a..9000b1495 100644 --- a/gcc/d/dfrontend/scope.h +++ b/gcc/d/dfrontend/scope.h @@ -1,11 +1,13 @@ -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/scope.h + */ #ifndef DMD_SCOPE_H #define DMD_SCOPE_H @@ -68,28 +70,24 @@ struct Scope Module *module; // Root module ScopeDsymbol *scopesym; // current symbol - ScopeDsymbol *sd; // if in static if, and declaring new symbols, - // sd gets the addMember() + ScopeDsymbol *sds; // if in static if, and declaring new symbols, + // sds gets the addMember() FuncDeclaration *func; // function we are in Dsymbol *parent; // parent to use LabelStatement *slabel; // enclosing labelled statement SwitchStatement *sw; // enclosing switch statement TryFinallyStatement *tf; // enclosing try finally statement + OnScopeStatement *os; // enclosing scope(xxx) statement TemplateInstance *tinst; // enclosing template instance Statement *sbreak; // enclosing statement that supports "break" Statement *scontinue; // enclosing statement that supports "continue" ForeachStatement *fes; // if nested function for ForeachStatement, this is it Scope *callsc; // used for __FUNCTION__, __PRETTY_FUNCTION__ and __MODULE__ - unsigned offset; // next offset to use in aggregate - // This really shouldn't be a part of Scope, because it requires - // semantic() to be done in the lexical field order. It should be - // set in a pass after semantic() on all fields so they can be - // semantic'd in any order. int inunion; // we're processing members of a union int nofree; // set if shouldn't free it int noctor; // set if constructor calls aren't allowed int intypeof; // in typeof(exp) - bool speculative; // in __traits(compiles) or typeof(exp) + bool speculative; // in __traits(compiles) and so on VarDeclaration *lastVar; // Previous symbol used to prevent goto-skips-init unsigned callSuper; // primitive flow analysis for constructors @@ -110,15 +108,17 @@ struct Scope UserAttributeDeclaration *userAttribDecl; // user defined attributes DocComment *lastdc; // documentation comment for last symbol at this scope - size_t lastoffset; // offset in docbuf of where to insert next dec + size_t lastoffset; // offset in docbuf of where to insert next dec (for ditto) + size_t lastoffset2; // offset in docbuf of where to insert next dec (for unittest) OutBuffer *docbuf; // buffer for documentation output static Scope *freelist; - static void *operator new(size_t sz); + static Scope *alloc(); static Scope *createGlobal(Module *module); Scope(); - Scope(Scope *enclosing); + + Scope *copy(); Scope *push(); Scope *push(ScopeDsymbol *ss); diff --git a/gcc/d/dfrontend/sideeffect.c b/gcc/d/dfrontend/sideeffect.c index 03f7aa837..ccec73aeb 100644 --- a/gcc/d/dfrontend/sideeffect.c +++ b/gcc/d/dfrontend/sideeffect.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/sideeffect.c + */ #include #include @@ -23,22 +24,127 @@ #include "scope.h" #include "attrib.h" -int lambdaHasSideEffect(Expression *e, void *param); +bool walkPostorder(Expression *e, StoppableVisitor *v); +bool lambdaHasSideEffect(Expression *e); + +/************************************************** + * Front-end expression rewriting should create temporary variables for + * non trivial sub-expressions in order to: + * 1. save evaluation order + * 2. prevent sharing of sub-expression in AST + */ +bool isTrivialExp(Expression *e) +{ + class IsTrivialExp : public StoppableVisitor + { + public: + IsTrivialExp() {} + + void visit(Expression *e) + { + /* Bugzilla 11201: CallExp is always non trivial expression, + * especially for inlining. + */ + if (e->op == TOKcall) + { + stop = true; + return; + } + + // stop walking if we determine this expression has side effects + stop = lambdaHasSideEffect(e); + } + }; + + IsTrivialExp v; + return walkPostorder(e, &v) == false; +} /******************************************** * Determine if Expression has any side effects. */ -bool Expression::hasSideEffect() +bool hasSideEffect(Expression *e) { - bool has = false; - apply(&lambdaHasSideEffect, &has); - return has; + class LambdaHasSideEffect : public StoppableVisitor + { + public: + LambdaHasSideEffect() {} + + void visit(Expression *e) + { + // stop walking if we determine this expression has side effects + stop = lambdaHasSideEffect(e); + } + }; + + LambdaHasSideEffect v; + return walkPostorder(e, &v); } -int lambdaHasSideEffect(Expression *e, void *param) +/******************************************** + * Determine if the call of f, or function type or delegate type t1, has any side effects. + * Returns: + * 0 has any side effects + * 1 nothrow + constant purity + * 2 nothrow + strong purity + */ + +int callSideEffectLevel(FuncDeclaration *f) +{ + /* Bugzilla 12760: ctor call always has side effects. + */ + if (f->isCtorDeclaration()) + return 0; + + assert(f->type->ty == Tfunction); + TypeFunction *tf = (TypeFunction *)f->type; + if (tf->isnothrow) + { + PURE purity = f->isPure(); + if (purity == PUREstrong) + return 2; + if (purity == PUREconst) + return 1; + } + return 0; +} + +int callSideEffectLevel(Type *t) +{ + t = t->toBasetype(); + + TypeFunction *tf; + if (t->ty == Tdelegate) + tf = (TypeFunction *)((TypeDelegate *)t)->next; + else + { + assert(t->ty == Tfunction); + tf = (TypeFunction *)t; + } + + tf->purityLevel(); + PURE purity = tf->purity; + if (t->ty == Tdelegate && purity > PUREweak) + { + if (tf->isMutable()) + purity = PUREweak; + else if (!tf->isImmutable()) + purity = PUREconst; + } + + if (tf->isnothrow) + { + if (purity == PUREstrong) + return 2; + if (purity == PUREconst) + return 1; + } + return 0; +} + +bool lambdaHasSideEffect(Expression *e) { - bool *phas = (bool *)param; switch (e->op) { // Sort the cases by most frequently used first @@ -68,47 +174,45 @@ int lambdaHasSideEffect(Expression *e, void *param) case TOKdelete: case TOKnew: case TOKnewanonclass: - *phas = true; - break; + return true; case TOKcall: - { CallExp *ce = (CallExp *)e; - + { + CallExp *ce = (CallExp *)e; /* Calling a function or delegate that is pure nothrow * has no side effects. */ if (ce->e1->type) { Type *t = ce->e1->type->toBasetype(); - if ((t->ty == Tfunction && ((TypeFunction *)t)->purity > PUREweak && - ((TypeFunction *)t)->isnothrow) - || - (t->ty == Tdelegate && ((TypeFunction *)((TypeDelegate *)t)->next)->purity > PUREweak && - ((TypeFunction *)((TypeDelegate *)t)->next)->isnothrow) - ) + if (t->ty == Tdelegate) + t = ((TypeDelegate *)t)->next; + if (t->ty == Tfunction && + (ce->f ? callSideEffectLevel(ce->f) + : callSideEffectLevel(ce->e1->type)) > 0) { } else - *phas = true; + return true; } break; } case TOKcast: - { CastExp *ce = (CastExp *)e; - + { + CastExp *ce = (CastExp *)e; /* if: * cast(classtype)func() // because it may throw */ if (ce->to->ty == Tclass && ce->e1->op == TOKcall && ce->e1->type->ty == Tclass) - *phas = true; + return true; break; } default: break; } - return *phas; // stop walking if we determine this expression has side effects + return false; } @@ -116,135 +220,140 @@ int lambdaHasSideEffect(Expression *e, void *param) * The result of this expression will be discarded. * Complain if the operation has no side effects (and hence is meaningless). */ -void Expression::discardValue() +void discardValue(Expression *e) { - bool has = false; - lambdaHasSideEffect(this, &has); - if (!has) + if (lambdaHasSideEffect(e)) // check side-effect shallowly + return; + switch (e->op) { - switch (op) + case TOKcast: { - case TOKcast: - { CastExp *ce = (CastExp *)this; - if (ce->to->equals(Type::tvoid)) - { /* - * Don't complain about an expression with no effect if it was cast to void - */ - ce->e1->useValue(); - break; - } - goto Ldefault; // complain - } - - case TOKerror: - break; - - case TOKcall: - /* Don't complain about calling functions with no effect, - * because purity and nothrow are inferred, and because some of the - * runtime library depends on it. Needs more investigation. + CastExp *ce = (CastExp *)e; + if (ce->to->equals(Type::tvoid)) + { + /* + * Don't complain about an expression with no effect if it was cast to void */ - break; - - case TOKimport: - error("%s has no effect", toChars()); - break; - - case TOKandand: - { AndAndExp *aae = (AndAndExp *)this; - aae->e1->useValue(); - aae->e2->discardValue(); - break; + return; } + break; // complain + } - case TOKoror: - { OrOrExp *ooe = (OrOrExp *)this; - ooe->e1->useValue(); - ooe->e2->discardValue(); - break; - } + case TOKerror: + return; - case TOKquestion: - { CondExp *ce = (CondExp *)this; - ce->econd->useValue(); - ce->e1->discardValue(); - ce->e2->discardValue(); - break; + case TOKvar: + { + VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); + if (v && (v->storage_class & STCtemp)) + { + // Bugzilla 5810: Don't complain about an internal generated variable. + return; } - - case TOKcomma: - { CommaExp *ce = (CommaExp *)this; - - /* Check for compiler-generated code of the form auto __tmp, e, __tmp; - * In such cases, only check e for side effect (it's OK for __tmp to have - * no side effect). - * See Bugzilla 4231 for discussion - */ - CommaExp* firstComma = ce; - while (firstComma->e1->op == TOKcomma) - firstComma = (CommaExp *)firstComma->e1; - if (firstComma->e1->op == TOKdeclaration && - ce->e2->op == TOKvar && - ((DeclarationExp *)firstComma->e1)->declaration == ((VarExp*)ce->e2)->var) + break; + } + case TOKcall: + /* Issue 3882: */ + if (global.params.warnings && !global.gag) + { + CallExp *ce = (CallExp *)e; + if (e->type->ty == Tvoid) { - ce->e1->useValue(); - break; + /* Don't complain about calling void-returning functions with no side-effect, + * because purity and nothrow are inferred, and because some of the + * runtime library depends on it. Needs more investigation. + * + * One possible solution is to restrict this message to only be called in hierarchies that + * never call assert (and or not called from inside unittest blocks) + */ + } + else if (ce->e1->type) + { + Type *t = ce->e1->type->toBasetype(); + if (t->ty == Tdelegate) + t = ((TypeDelegate *)t)->next; + if (t->ty == Tfunction && + (ce->f ? callSideEffectLevel(ce->f) + : callSideEffectLevel(ce->e1->type)) > 0) + { + const char *s; + if (ce->f) + s = ce->f->toPrettyChars(); + else if (ce->e1->op == TOKstar) + { + // print 'fp' if ce->e1 is (*fp) + s = ((PtrExp *)ce->e1)->e1->toChars(); + } + else + s = ce->e1->toChars(); + + e->warning("calling %s without side effects discards return value of type %s, prepend a cast(void) if intentional", + s, e->type->toChars()); + } } - // Don't check e1 until we cast(void) the a,b code generation - //ce->e1->discardValue(); - ce->e2->discardValue(); - break; } + return; - case TOKtuple: - /* Pass without complaint if any of the tuple elements have side effects. - * Ideally any tuple elements with no side effects should raise an error, - * this needs more investigation as to what is the right thing to do. - */ - if (!hasSideEffect()) - goto Ldefault; - break; + case TOKimport: + e->error("%s has no effect", e->toChars()); + return; - default: - Ldefault: - error("%s has no effect in expression (%s)", - Token::toChars(op), toChars()); - break; + case TOKandand: + { + AndAndExp *aae = (AndAndExp *)e; + discardValue(aae->e2); + return; } - } - else - { - useValue(); - } -} -/* This isn't used yet because the only way an expression has an unused sub-expression - * is with the CommaExp, and that currently generates messages from rewrites into comma - * expressions. Needs more investigation. - */ -void Expression::useValue() -{ -#if 0 - // Disabled because need to cast(void) the a,b code generation - void *p; - apply(&lambdaUseValue, &p); -#endif -} + case TOKoror: + { + OrOrExp *ooe = (OrOrExp *)e; + discardValue(ooe->e2); + return; + } + + case TOKquestion: + { + CondExp *ce = (CondExp *)e; + discardValue(ce->e1); + discardValue(ce->e2); + return; + } -#if 0 -int lambdaUseValue(Expression *e, void *param) -{ - switch (e->op) - { case TOKcomma: - { CommaExp *ce = (CommaExp *)e; - discardValue(ce->E1); - break; + { + CommaExp *ce = (CommaExp *)e; + /* Check for compiler-generated code of the form auto __tmp, e, __tmp; + * In such cases, only check e for side effect (it's OK for __tmp to have + * no side effect). + * See Bugzilla 4231 for discussion + */ + CommaExp* firstComma = ce; + while (firstComma->e1->op == TOKcomma) + firstComma = (CommaExp *)firstComma->e1; + if (firstComma->e1->op == TOKdeclaration && + ce->e2->op == TOKvar && + ((DeclarationExp *)firstComma->e1)->declaration == ((VarExp*)ce->e2)->var) + { + return; + } + // Don't check e1 until we cast(void) the a,b code generation + //discardValue(ce->e1); + discardValue(ce->e2); + return; } + case TOKtuple: + /* Pass without complaint if any of the tuple elements have side effects. + * Ideally any tuple elements with no side effects should raise an error, + * this needs more investigation as to what is the right thing to do. + */ + if (!hasSideEffect(e)) + break; + return; + default: break; } - return 0; + e->error("%s has no effect in expression (%s)", Token::toChars(e->op), e->toChars()); } -#endif diff --git a/gcc/d/dfrontend/speller.c b/gcc/d/dfrontend/speller.c index 29b52d550..6cc6cccc0 100644 --- a/gcc/d/dfrontend/speller.c +++ b/gcc/d/dfrontend/speller.c @@ -1,11 +1,11 @@ -// Copyright (c) 2010-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Copyright (c) 2010-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/speller.c + */ #include #include @@ -201,7 +201,7 @@ void *spellerX(const char *seed, size_t seedlen, fp_speller_t fp, void *fparg, void *speller(const char *seed, fp_speller_t fp, void *fparg, const char *charset) { size_t seedlen = strlen(seed); - size_t maxdist = seedlen < 3 ? seedlen - 1 : 2; + size_t maxdist = seedlen < 4 ? seedlen / 2 : 2; for (int distance = 0; distance < maxdist; distance++) { void *p = spellerX(seed, seedlen, fp, fparg, charset, distance); if (p) @@ -242,7 +242,6 @@ void unittest_speller() { "hello", "ehllo", "y" }, { "hello", "helol", "y" }, { "hello", "abcd", "n" }, - //{ "ehllo", "helol", "y" }, { "hello", "helxxlo", "y" }, { "hello", "ehlxxlo", "n" }, { "hello", "heaao", "y" }, diff --git a/gcc/d/dfrontend/speller.h b/gcc/d/dfrontend/speller.h index 5f4e28f49..b01c5a81f 100644 --- a/gcc/d/dfrontend/speller.h +++ b/gcc/d/dfrontend/speller.h @@ -1,11 +1,11 @@ -// Copyright (c) 2010-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Copyright (c) 2010-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/speller.h + */ typedef void *(fp_speller_t)(void *, const char *); diff --git a/gcc/d/dfrontend/statement.c b/gcc/d/dfrontend/statement.c index c284ee0bd..f69b37b83 100644 --- a/gcc/d/dfrontend/statement.c +++ b/gcc/d/dfrontend/statement.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/statement.c + */ #include #include @@ -31,6 +32,9 @@ #include "attrib.h" #include "import.h" +bool walkPostorder(Statement *s, StoppableVisitor *v); +bool isNonAssignmentArrayOp(Expression *e); + Identifier *fixupLabelName(Scope *sc, Identifier *ident) { unsigned flags = (sc->flags & SCOPEcontract); @@ -44,9 +48,8 @@ Identifier *fixupLabelName(Scope *sc, Identifier *ident) const char *prefix = flags == SCOPErequire ? "__in_" : "__out_"; OutBuffer buf; buf.printf("%s%s", prefix, ident->toChars()); - buf.writeByte(0); - const char *name = (const char *)buf.extractData(); + const char *name = buf.extractString(); ident = Lexer::idPool(name); } return ident; @@ -87,17 +90,10 @@ char *Statement::toChars() HdrGenState hgs; OutBuffer buf; - toCBuffer(&buf, &hgs); - buf.writebyte(0); - return buf.extractData(); + ::toCBuffer(this, &buf, &hgs); + return buf.extractString(); } -void Statement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->printf("Statement::toCBuffer()"); - buf->writenl(); - assert(0); -} Statement *Statement::semantic(Scope *sc) { @@ -170,82 +166,552 @@ bool Statement::hasContinue() bool Statement::usesEH() { - struct UsesEH + class UsesEH : public StoppableVisitor { - static bool lambdaUsesEH(Statement *s, void *param) - { - return s->usesEHimpl(); - } + public: + void visit(Statement *s) {} + void visit(TryCatchStatement *s) { stop = true; } + void visit(TryFinallyStatement *s) { stop = true; } + void visit(OnScopeStatement *s) { stop = true; } + void visit(SynchronizedStatement *s) { stop = true; } }; UsesEH ueh; - return apply(&UsesEH::lambdaUsesEH, &ueh); + return walkPostorder(this, &ueh); } -bool Statement::usesEHimpl() { return false; } -bool TryCatchStatement::usesEHimpl() { return true; } -bool TryFinallyStatement::usesEHimpl() { return true; } -bool OnScopeStatement::usesEHimpl() { return true; } -bool SynchronizedStatement::usesEHimpl() { return true; } - /* ============================================== */ // true if statement 'comes from' somewhere else, like a goto bool Statement::comeFrom() { - struct ComeFrom + class ComeFrom : public StoppableVisitor { - static bool lambdaComeFrom(Statement *s, void *param) - { - return s->comeFromImpl(); - } + public: + void visit(Statement *s) {} + void visit(CaseStatement *s) { stop = true; } + void visit(DefaultStatement *s) { stop = true; } + void visit(LabelStatement *s) { stop = true; } + void visit(AsmStatement *s) { stop = true; } +#ifdef IN_GCC + void visit(ExtAsmStatement *s) { stop = true; } +#endif }; ComeFrom cf; - return apply(&ComeFrom::lambdaComeFrom, &cf); + return walkPostorder(this, &cf); } -bool Statement::comeFromImpl() { return false; } -bool CaseStatement::comeFromImpl() { return true; } -bool DefaultStatement::comeFromImpl() { return true; } -bool LabelStatement::comeFromImpl() { return true; } -bool AsmStatement::comeFromImpl() { return true; } - /* ============================================== */ // Return true if statement has executable code. bool Statement::hasCode() { - struct HasCode + class HasCode : public StoppableVisitor { - static bool lambdaHasCode(Statement *s, void *param) - { - return s->hasCodeImpl(); - } + public: + void visit(Statement *s) { stop = true; } + void visit(ExpStatement *s) { stop = s->exp != NULL; } + void visit(CompoundStatement *s) {} + void visit(ScopeStatement *s) {} + void visit(ImportStatement *s) {} }; HasCode hc; - return apply(&HasCode::lambdaHasCode, &hc); + return walkPostorder(this, &hc); } -bool Statement::hasCodeImpl() { return true; } -bool ExpStatement::hasCodeImpl() { return exp != NULL; } -bool CompoundStatement::hasCodeImpl() { return false; } -bool ScopeStatement::hasCodeImpl() { return false; } -bool ImportStatement::hasCodeImpl() { return false; } - - /* ============================================== */ /* Only valid after semantic analysis * If 'mustNotThrow' is true, generate an error if it throws */ -int Statement::blockExit(bool mustNotThrow) +int Statement::blockExit(FuncDeclaration *func, bool mustNotThrow) { - printf("Statement::blockExit(%p)\n", this); - printf("%s\n", toChars()); - assert(0); - return BEany; + class BlockExit : public Visitor + { + public: + FuncDeclaration *func; + bool mustNotThrow; + int result; + + BlockExit(FuncDeclaration *func, bool mustNotThrow) + : func(func), mustNotThrow(mustNotThrow) + { + result = BEnone; + } + + void visit(Statement *s) + { + printf("Statement::blockExit(%p)\n", s); + printf("%s\n", s->toChars()); + assert(0); + result = BEany; + } + + void visit(ErrorStatement *s) + { + result = BEany; + } + + void visit(ExpStatement *s) + { + result = BEfallthru; + if (s->exp) + { + if (s->exp->op == TOKhalt) + { + result = BEhalt; + return; + } + if (s->exp->op == TOKassert) + { + AssertExp *a = (AssertExp *)s->exp; + if (a->e1->isBool(false)) // if it's an assert(0) + { + result = BEhalt; + return; + } + } + if (canThrow(s->exp, func, mustNotThrow)) + result |= BEthrow; + } + } + + void visit(CompileStatement *s) + { + assert(global.errors); + result = BEfallthru; + } + + void visit(CompoundStatement *cs) + { + //printf("CompoundStatement::blockExit(%p) %d\n", cs, cs->statements->dim); + result = BEfallthru; + Statement *slast = NULL; + for (size_t i = 0; i < cs->statements->dim; i++) + { + Statement *s = (*cs->statements)[i]; + if (s) + { + //printf("result = x%x\n", result); + //printf("s: %s\n", s->toChars()); + if (global.params.warnings && result & BEfallthru && slast) + { + slast = slast->last(); + if (slast && (slast->isCaseStatement() || slast->isDefaultStatement()) && + (s->isCaseStatement() || s->isDefaultStatement())) + { + // Allow if last case/default was empty + CaseStatement *sc = slast->isCaseStatement(); + DefaultStatement *sd = slast->isDefaultStatement(); + if (sc && (!sc->statement->hasCode() || sc->statement->isCaseStatement() || sc->statement->isErrorStatement())) + ; + else if (sd && (!sd->statement->hasCode() || sd->statement->isCaseStatement() || sd->statement->isErrorStatement())) + ; + else + { + const char *gototype = s->isCaseStatement() ? "case" : "default"; + s->warning("switch case fallthrough - use 'goto %s;' if intended", gototype); + } + } + } + + if (!(result & BEfallthru) && !s->comeFrom()) + { + if (s->blockExit(func, mustNotThrow) != BEhalt && s->hasCode()) + s->warning("statement is not reachable"); + } + else + { + result &= ~BEfallthru; + result |= s->blockExit(func, mustNotThrow); + } + slast = s; + } + } + } + + void visit(UnrolledLoopStatement *uls) + { + result = BEfallthru; + for (size_t i = 0; i < uls->statements->dim; i++) + { + Statement *s = (*uls->statements)[i]; + if (s) + { + int r = s->blockExit(func, mustNotThrow); + result |= r & ~(BEbreak | BEcontinue); + } + } + } + + void visit(ScopeStatement *s) + { + //printf("ScopeStatement::blockExit(%p)\n", s->statement); + result = s->statement ? s->statement->blockExit(func, mustNotThrow) : BEfallthru; + } + + void visit(WhileStatement *s) + { + assert(global.errors); + result = BEfallthru; + } + + void visit(DoStatement *s) + { + if (s->body) + { + result = s->body->blockExit(func, mustNotThrow); + if (result == BEbreak) + { + result = BEfallthru; + return; + } + if (result & BEcontinue) + result |= BEfallthru; + } + else + result = BEfallthru; + if (result & BEfallthru) + { + if (canThrow(s->condition, func, mustNotThrow)) + result |= BEthrow; + if (!(result & BEbreak) && s->condition->isBool(true)) + result &= ~BEfallthru; + } + result &= ~(BEbreak | BEcontinue); + } + + void visit(ForStatement *s) + { + result = BEfallthru; + if (s->init) + { + result = s->init->blockExit(func, mustNotThrow); + if (!(result & BEfallthru)) + return; + } + if (s->condition) + { + if (canThrow(s->condition, func, mustNotThrow)) + result |= BEthrow; + if (s->condition->isBool(true)) + result &= ~BEfallthru; + else if (s->condition->isBool(false)) + return; + } + else + result &= ~BEfallthru; // the body must do the exiting + if (s->body) + { + int r = s->body->blockExit(func, mustNotThrow); + if (r & (BEbreak | BEgoto)) + result |= BEfallthru; + result |= r & ~(BEfallthru | BEbreak | BEcontinue); + } + if (s->increment && canThrow(s->increment, func, mustNotThrow)) + result |= BEthrow; + } + + void visit(ForeachStatement *s) + { + result = BEfallthru; + if (canThrow(s->aggr, func, mustNotThrow)) + result |= BEthrow; + if (s->body) + result |= s->body->blockExit(func, mustNotThrow) & ~(BEbreak | BEcontinue); + } + + void visit(ForeachRangeStatement *s) + { + assert(global.errors); + result = BEfallthru; + } + + void visit(IfStatement *s) + { + //printf("IfStatement::blockExit(%p)\n", s); + + result = BEnone; + if (canThrow(s->condition, func, mustNotThrow)) + result |= BEthrow; + if (s->condition->isBool(true)) + { + if (s->ifbody) + result |= s->ifbody->blockExit(func, mustNotThrow); + else + result |= BEfallthru; + } + else if (s->condition->isBool(false)) + { + if (s->elsebody) + result |= s->elsebody->blockExit(func, mustNotThrow); + else + result |= BEfallthru; + } + else + { + if (s->ifbody) + result |= s->ifbody->blockExit(func, mustNotThrow); + else + result |= BEfallthru; + if (s->elsebody) + result |= s->elsebody->blockExit(func, mustNotThrow); + else + result |= BEfallthru; + } + //printf("IfStatement::blockExit(%p) = x%x\n", s, result); + } + + void visit(ConditionalStatement *s) + { + result = s->ifbody->blockExit(func, mustNotThrow); + if (s->elsebody) + result |= s->elsebody->blockExit(func, mustNotThrow); + } + + void visit(PragmaStatement *s) + { + result = BEfallthru; + #if 0 // currently, no code is generated for Pragma's, so it's just fallthru + if (arrayExpressionCanThrow(s->args, func, mustNotThrow)) + result |= BEthrow; + if (s->body) + result |= s->body->blockExit(func, mustNotThrow); + #endif + } + + void visit(StaticAssertStatement *s) + { + result = BEfallthru; + } + + void visit(SwitchStatement *s) + { + result = BEnone; + if (canThrow(s->condition, func, mustNotThrow)) + result |= BEthrow; + if (s->body) + { + result |= s->body->blockExit(func, mustNotThrow); + if (result & BEbreak) + { + result |= BEfallthru; + result &= ~BEbreak; + } + } + else + result |= BEfallthru; + } + + void visit(CaseStatement *s) + { + result = s->statement->blockExit(func, mustNotThrow); + } + + void visit(DefaultStatement *s) + { + result = s->statement->blockExit(func, mustNotThrow); + } + + void visit(GotoDefaultStatement *s) + { + result = BEgoto; + } + + void visit(GotoCaseStatement *s) + { + result = BEgoto; + } + + void visit(SwitchErrorStatement *s) + { + // Switch errors are non-recoverable + result = BEhalt; + } + + void visit(ReturnStatement *s) + { + result = BEreturn; + if (s->exp && canThrow(s->exp, func, mustNotThrow)) + result |= BEthrow; + } + + void visit(BreakStatement *s) + { + //printf("BreakStatement::blockExit(%p) = x%x\n", s, s->ident ? BEgoto : BEbreak); + result = s->ident ? BEgoto : BEbreak; + } + + void visit(ContinueStatement *s) + { + result = s->ident ? BEgoto : BEcontinue; + } + + void visit(SynchronizedStatement *s) + { + result = s->body ? s->body->blockExit(func, mustNotThrow) : BEfallthru; + } + + void visit(WithStatement *s) + { + result = BEnone; + if (canThrow(s->exp, func, mustNotThrow)) + result = BEthrow; + if (s->body) + result |= s->body->blockExit(func, mustNotThrow); + else + result |= BEfallthru; + } + + void visit(TryCatchStatement *s) + { + assert(s->body); + result = s->body->blockExit(func, false); + + int catchresult = 0; + for (size_t i = 0; i < s->catches->dim; i++) + { + Catch *c = (*s->catches)[i]; + if (c->type == Type::terror) + continue; + + int cresult; + if (c->handler) + cresult = c->handler->blockExit(func, mustNotThrow); + else + cresult = BEfallthru; + + /* If we're catching Object, then there is no throwing + */ + Identifier *id = c->type->toBasetype()->isClassHandle()->ident; + if (c->internalCatch && (cresult & BEfallthru)) + { + // Bugzilla 11542: leave blockExit flags of the body + cresult &= ~BEfallthru; + } + else if (id == Id::Object || id == Id::Throwable) + { + result &= ~(BEthrow | BEerrthrow); + } + else if (id == Id::Exception) + { + result &= ~BEthrow; + } + catchresult |= cresult; + } + if (mustNotThrow && (result & BEthrow)) + { + // now explain why this is nothrow + s->body->blockExit(func, mustNotThrow); + } + result |= catchresult; + } + + void visit(TryFinallyStatement *s) + { + result = BEfallthru; + if (s->body) + result = s->body->blockExit(func, false); + + // check finally body as well, it may throw (bug #4082) + int finalresult = BEfallthru; + if (s->finalbody) + finalresult = s->finalbody->blockExit(func, false); + + // If either body or finalbody halts + if (result == BEhalt) + finalresult = BEnone; + if (finalresult == BEhalt) + result = BEnone; + + if (mustNotThrow) + { + // now explain why this is nothrow + if (s->body && (result & BEthrow)) + s->body->blockExit(func, mustNotThrow); + if (s->finalbody && (finalresult & BEthrow)) + s->finalbody->blockExit(func, mustNotThrow); + } + + #if 0 + // Bugzilla 13201: Mask to prevent spurious warnings for + // destructor call, exit of synchronized statement, etc. + if (result == BEhalt && finalresult != BEhalt && s->finalbody && + s->finalbody->hasCode()) + { + s->finalbody->warning("statement is not reachable"); + } + #endif + + if (!(finalresult & BEfallthru)) + result &= ~BEfallthru; + result |= finalresult & ~BEfallthru; + } + + void visit(OnScopeStatement *s) + { + // At this point, this statement is just an empty placeholder + result = BEfallthru; + } + + void visit(ThrowStatement *s) + { + if (s->internalThrow) + { + // Bugzilla 8675: Allow throwing 'Throwable' object even if mustNotThrow. + result = BEfallthru; + return; + } + + Type *t = s->exp->type->toBasetype(); + ClassDeclaration *cd = t->isClassHandle(); + assert(cd); + + if (cd == ClassDeclaration::errorException || + ClassDeclaration::errorException->isBaseOf(cd, NULL)) + { + result = BEerrthrow; + return; + } + if (mustNotThrow) + s->error("%s is thrown but not caught", s->exp->type->toChars()); + + result = BEthrow; + } + + void visit(GotoStatement *s) + { + //printf("GotoStatement::blockExit(%p)\n", s); + result = BEgoto; + } + + void visit(LabelStatement *s) + { + //printf("LabelStatement::blockExit(%p)\n", s); + result = s->statement ? s->statement->blockExit(func, mustNotThrow) : BEfallthru; + } + + void visit(CompoundAsmStatement *s) + { + if (mustNotThrow && !(s->stc & STCnothrow)) + s->deprecation("asm statement is assumed to throw - mark it with 'nothrow' if it does not"); + + // Assume the worst + result = BEfallthru | BEreturn | BEgoto | BEhalt; + if (!(s->stc & STCnothrow)) result |= BEthrow; + } + + void visit(ImportStatement *s) + { + result = BEfallthru; + } + }; + + BlockExit be(func, mustNotThrow); + accept(&be); + return be.result; } Statement *Statement::last() @@ -303,11 +769,6 @@ Statement *ErrorStatement::semantic(Scope *sc) return this; } -int ErrorStatement::blockExit(bool mustNotThrow) -{ - return BEany; -} - /******************************** PeelStatement ***************************/ PeelStatement::PeelStatement(Statement *s) @@ -350,24 +811,6 @@ Statement *ExpStatement::syntaxCopy() return es; } -void ExpStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - if (exp) - { exp->toCBuffer(buf, hgs); - if (exp->op != TOKdeclaration) - { buf->writeByte(';'); - if (!hgs->FLinit.init) - buf->writenl(); - } - } - else - { - buf->writeByte(';'); - if (!hgs->FLinit.init) - buf->writenl(); - } -} - Statement *ExpStatement::semantic(Scope *sc) { if (exp) @@ -392,33 +835,15 @@ Statement *ExpStatement::semantic(Scope *sc) exp = exp->semantic(sc); exp = exp->addDtorHook(sc); exp = resolveProperties(sc, exp); - exp->discardValue(); + discardValue(exp); exp = exp->optimize(0); + exp = checkGC(sc, exp); if (exp->op == TOKerror) return new ErrorStatement(); } return this; } -int ExpStatement::blockExit(bool mustNotThrow) -{ int result = BEfallthru; - - if (exp) - { - if (exp->op == TOKhalt) - return BEhalt; - if (exp->op == TOKassert) - { AssertExp *a = (AssertExp *)exp; - - if (a->e1->isBool(false)) // if it's an assert(0) - return BEhalt; - } - if (exp->canThrow(mustNotThrow)) - result |= BEthrow; - } - return result; -} - Statement *ExpStatement::scopeCode(Scope *sc, Statement **sentry, Statement **sexception, Statement **sfinally) { //printf("ExpStatement::scopeCode()\n"); @@ -500,15 +925,6 @@ Statement *CompileStatement::syntaxCopy() return es; } -void CompileStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("mixin("); - exp->toCBuffer(buf, hgs); - buf->writestring(");"); - if (!hgs->FLinit.init) - buf->writenl(); -} - Statements *CompileStatement::flatten(Scope *sc) { //printf("CompileStatement::flatten() %s\n", exp->toChars()); @@ -516,14 +932,14 @@ Statements *CompileStatement::flatten(Scope *sc) exp = exp->semantic(sc); exp = resolveProperties(sc, exp); sc = sc->endCTFE(); - exp = exp->ctfeInterpret(); Statements *a = new Statements(); if (exp->op != TOKerror) { - StringExp *se = exp->toString(); + Expression *e = exp->ctfeInterpret(); + StringExp *se = e->toStringExp(); if (!se) - error("argument to mixin must be a string, not (%s)", exp->toChars()); + error("argument to mixin must be a string, not (%s) of type %s", exp->toChars(), exp->type->toChars()); else { se = se->toUTF8(sc); @@ -556,13 +972,6 @@ Statement *CompileStatement::semantic(Scope *sc) return s->semantic(sc); } -int CompileStatement::blockExit(bool mustNotThrow) -{ - assert(global.errors); - return BEfallthru; -} - - /******************************** CompoundStatement ***************************/ CompoundStatement::CompoundStatement(Loc loc, Statements *s) @@ -674,22 +1083,24 @@ Statement *CompoundStatement::semantic(Scope *sc) Identifier *id = Lexer::uniqueId("__o"); - Statement *handler = sexception; - if (sexception->blockExit(false) & BEfallthru) - { handler = new ThrowStatement(Loc(), new IdentifierExp(Loc(), id)); - ((ThrowStatement *)handler)->internalThrow = true; - handler = new CompoundStatement(Loc(), sexception, handler); + Statement *handler = new PeelStatement(sexception); + if (sexception->blockExit(sc->func, false) & BEfallthru) + { + ThrowStatement *ts = new ThrowStatement(Loc(), new IdentifierExp(Loc(), id)); + ts->internalThrow = true; + handler = new CompoundStatement(Loc(), handler, ts); } Catches *catches = new Catches(); Catch *ctch = new Catch(Loc(), NULL, id, handler); ctch->internalCatch = true; catches->push(ctch); - s = new TryCatchStatement(Loc(), body, catches); + s = new TryCatchStatement(Loc(), body, catches); if (sfinally) s = new TryFinallyStatement(Loc(), s, sfinally); s = s->semantic(sc); + statements->setDim(i + 1); statements->push(s); break; @@ -801,64 +1212,6 @@ Statement *CompoundStatement::last() return s; } -void CompoundStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; - if (s) - s->toCBuffer(buf, hgs); - } -} - -int CompoundStatement::blockExit(bool mustNotThrow) -{ - //printf("CompoundStatement::blockExit(%p) %d\n", this, statements->dim); - int result = BEfallthru; - Statement *slast = NULL; - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; - if (s) - { - //printf("result = x%x\n", result); - //printf("s: %s\n", s->toChars()); - if (global.params.warnings && result & BEfallthru && slast) - { - slast = slast->last(); - if (slast && (slast->isCaseStatement() || slast->isDefaultStatement()) && - (s->isCaseStatement() || s->isDefaultStatement())) - { - // Allow if last case/default was empty - CaseStatement *sc = slast->isCaseStatement(); - DefaultStatement *sd = slast->isDefaultStatement(); - if (sc && (!sc->statement->hasCode() || sc->statement->isCaseStatement())) - ; - else if (sd && (!sd->statement->hasCode() || sd->statement->isCaseStatement())) - ; - else - { - const char *gototype = s->isCaseStatement() ? "case" : "default"; - s->warning("switch case fallthrough - use 'goto %s;' if intended", gototype); - } - } - } - - if (!(result & BEfallthru) && !s->comeFrom()) - { - if (s->blockExit(mustNotThrow) != BEhalt && s->hasCode()) - s->warning("statement is not reachable"); - } - else - { - result &= ~BEfallthru; - result |= s->blockExit(mustNotThrow); - } - slast = s; - } - } - return result; -} - - /******************************** CompoundDeclarationStatement ***************************/ CompoundDeclarationStatement::CompoundDeclarationStatement(Loc loc, Statements *s) @@ -881,59 +1234,6 @@ Statement *CompoundDeclarationStatement::syntaxCopy() return cs; } -void CompoundDeclarationStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - bool anywritten = false; - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; - ExpStatement *ds; - if (s && - (ds = s->isExpStatement()) != NULL && - ds->exp->op == TOKdeclaration) - { - DeclarationExp *de = (DeclarationExp *)ds->exp; - Declaration *d = de->declaration->isDeclaration(); - assert(d); - VarDeclaration *v = d->isVarDeclaration(); - if (v) - { - /* This essentially copies the part of VarDeclaration::toCBuffer() - * that does not print the type. - * Should refactor this. - */ - if (anywritten) - { - buf->writestring(", "); - buf->writestring(v->ident->toChars()); - } - else - { - StorageClassDeclaration::stcToCBuffer(buf, v->storage_class); - if (v->type) - v->type->toCBuffer(buf, v->ident, hgs); - else - buf->writestring(v->ident->toChars()); - } - - if (v->init) - { buf->writestring(" = "); - ExpInitializer *ie = v->init->isExpInitializer(); - if (ie && (ie->exp->op == TOKconstruct || ie->exp->op == TOKblit)) - ((AssignExp *)ie->exp)->e2->toCBuffer(buf, hgs); - else - v->init->toCBuffer(buf, hgs); - } - } - else - d->toCBuffer(buf, hgs); - anywritten = true; - } - } - buf->writeByte(';'); - if (!hgs->FLinit.init) - buf->writenl(); -} - /**************************** UnrolledLoopStatement ***************************/ UnrolledLoopStatement::UnrolledLoopStatement(Loc loc, Statements *s) @@ -983,26 +1283,6 @@ Statement *UnrolledLoopStatement::semantic(Scope *sc) return serror ? serror : this; } -void UnrolledLoopStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("unrolled {"); - buf->writenl(); - buf->level++; - - for (size_t i = 0; i < statements->dim; i++) - { - Statement *s; - - s = (*statements)[i]; - if (s) - s->toCBuffer(buf, hgs); - } - - buf->level--; - buf->writeByte('}'); - buf->writenl(); -} - bool UnrolledLoopStatement::hasBreak() { return true; @@ -1013,21 +1293,6 @@ bool UnrolledLoopStatement::hasContinue() return true; } -int UnrolledLoopStatement::blockExit(bool mustNotThrow) -{ - int result = BEfallthru; - for (size_t i = 0; i < statements->dim; i++) - { Statement *s = (*statements)[i]; - if (s) - { - int r = s->blockExit(mustNotThrow); - result |= r & ~(BEbreak | BEcontinue); - } - } - return result; -} - - /******************************** ScopeStatement ***************************/ ScopeStatement::ScopeStatement(Loc loc, Statement *s) @@ -1108,27 +1373,6 @@ bool ScopeStatement::hasContinue() return statement ? statement->hasContinue() : false; } -int ScopeStatement::blockExit(bool mustNotThrow) -{ - //printf("ScopeStatement::blockExit(%p)\n", statement); - return statement ? statement->blockExit(mustNotThrow) : BEfallthru; -} - - -void ScopeStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writeByte('{'); - buf->writenl(); - buf->level++; - - if (statement) - statement->toCBuffer(buf, hgs); - - buf->level--; - buf->writeByte('}'); - buf->writenl(); -} - /******************************** WhileStatement ***************************/ WhileStatement::WhileStatement(Loc loc, Expression *c, Statement *b) @@ -1165,23 +1409,6 @@ bool WhileStatement::hasContinue() return true; } -int WhileStatement::blockExit(bool mustNotThrow) -{ - assert(global.errors); - return BEfallthru; -} - - -void WhileStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("while ("); - condition->toCBuffer(buf, hgs); - buf->writebyte(')'); - buf->writenl(); - if (body) - body->toCBuffer(buf, hgs); -} - /******************************** DoStatement ***************************/ DoStatement::DoStatement(Loc loc, Statement *b, Expression *c) @@ -1207,6 +1434,7 @@ Statement *DoStatement::semantic(Scope *sc) condition = condition->semantic(sc); condition = resolveProperties(sc, condition); condition = condition->optimize(WANTvalue); + condition = checkGC(sc, condition); condition = condition->checkToBoolean(sc); @@ -1229,42 +1457,6 @@ bool DoStatement::hasContinue() return true; } -int DoStatement::blockExit(bool mustNotThrow) -{ int result; - - if (body) - { result = body->blockExit(mustNotThrow); - if (result == BEbreak) - return BEfallthru; - if (result & BEcontinue) - result |= BEfallthru; - } - else - result = BEfallthru; - if (result & BEfallthru) - { - if (condition->canThrow(mustNotThrow)) - result |= BEthrow; - if (!(result & BEbreak) && condition->isBool(true)) - result &= ~BEfallthru; - } - result &= ~(BEbreak | BEcontinue); - return result; -} - - -void DoStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("do"); - buf->writenl(); - if (body) - body->toCBuffer(buf, hgs); - buf->writestring("while ("); - condition->toCBuffer(buf, hgs); - buf->writestring(");"); - buf->writenl(); -} - /******************************** ForStatement ***************************/ ForStatement::ForStatement(Loc loc, Statement *init, Expression *condition, Expression *increment, Statement *body) @@ -1333,15 +1525,19 @@ Statement *ForStatement::semantic(Scope *sc) sc->noctor++; if (condition) - { condition = condition->semantic(sc); + { + condition = condition->semantic(sc); condition = resolveProperties(sc, condition); condition = condition->optimize(WANTvalue); + condition = checkGC(sc, condition); condition = condition->checkToBoolean(sc); } if (increment) - { increment = increment->semantic(sc); + { + increment = increment->semantic(sc); increment = resolveProperties(sc, increment); increment = increment->optimize(0); + increment = checkGC(sc, increment); } sc->sbreak = this; @@ -1378,67 +1574,6 @@ bool ForStatement::hasContinue() return true; } -int ForStatement::blockExit(bool mustNotThrow) -{ int result = BEfallthru; - - if (init) - { result = init->blockExit(mustNotThrow); - if (!(result & BEfallthru)) - return result; - } - if (condition) - { if (condition->canThrow(mustNotThrow)) - result |= BEthrow; - if (condition->isBool(true)) - result &= ~BEfallthru; - else if (condition->isBool(false)) - return result; - } - else - result &= ~BEfallthru; // the body must do the exiting - if (body) - { int r = body->blockExit(mustNotThrow); - if (r & (BEbreak | BEgoto)) - result |= BEfallthru; - result |= r & ~(BEfallthru | BEbreak | BEcontinue); - } - if (increment && increment->canThrow(mustNotThrow)) - result |= BEthrow; - return result; -} - - -void ForStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("for ("); - if (init) - { - hgs->FLinit.init++; - init->toCBuffer(buf, hgs); - hgs->FLinit.init--; - } - else - buf->writebyte(';'); - if (condition) - { buf->writebyte(' '); - condition->toCBuffer(buf, hgs); - } - buf->writebyte(';'); - if (increment) - { buf->writebyte(' '); - increment->toCBuffer(buf, hgs); - } - buf->writebyte(')'); - buf->writenl(); - buf->writebyte('{'); - buf->writenl(); - buf->level++; - body->toCBuffer(buf, hgs); - buf->level--; - buf->writebyte('}'); - buf->writenl(); -} - /******************************** ForeachStatement ***************************/ ForeachStatement::ForeachStatement(Loc loc, TOK op, Parameters *arguments, @@ -1484,19 +1619,57 @@ Statement *ForeachStatement::semantic(Scope *sc) if (func->fes) func = func->fes->func; - if (!inferAggregate(sc, sapply)) + if (!inferAggregate(this, sc, sapply)) { error("invalid foreach aggregate %s", aggr->toChars()); Lerror: return new ErrorStatement(); } + Dsymbol* sapplyOld = sapply; // 'sapply' will be NULL if and after 'inferApplyArgTypes' errors + /* Check for inference errors */ - if (!inferApplyArgTypes(sc, sapply)) + if (!inferApplyArgTypes(this, sc, sapply)) { + /** + Try and extract the parameter count of the opApply callback function, e.g.: + int opApply(int delegate(int, float)) => 2 args + */ + bool foundMismatch = false; + size_t foreachParamCount = 0; + if (sapplyOld) + { + if (FuncDeclaration *fd = sapplyOld->isFuncDeclaration()) + { + int fvarargs; // ignored (opApply shouldn't take variadics) + Parameters *fparameters = fd->getParameters(&fvarargs); + + if (Parameter::dim(fparameters) == 1) + { + // first param should be the callback function + Parameter *fparam = Parameter::getNth(fparameters, 0); + if ((fparam->type->ty == Tpointer || fparam->type->ty == Tdelegate) && + fparam->type->nextOf()->ty == Tfunction) + { + TypeFunction *tf = (TypeFunction *)fparam->type->nextOf(); + foreachParamCount = Parameter::dim(tf->parameters); + foundMismatch = true; + } + } + } + } + //printf("dim = %d, arguments->dim = %d\n", dim, arguments->dim); - error("cannot uniquely infer foreach argument types"); + if (foundMismatch && dim != foreachParamCount) + { + const char *plural = foreachParamCount > 1 ? "s" : ""; + error("cannot infer argument types, expected %d argument%s, not %d", + foreachParamCount, plural, dim); + } + else + error("cannot uniquely infer foreach argument types"); + goto Lerror; } @@ -1512,7 +1685,8 @@ Statement *ForeachStatement::semantic(Scope *sc) Type *argtype = (*arguments)[dim-1]->type; if (argtype) - { argtype = argtype->semantic(loc, sc); + { + argtype = argtype->semantic(loc, sc); if (argtype->ty == Terror) goto Lerror; } @@ -1523,7 +1697,8 @@ Statement *ForeachStatement::semantic(Scope *sc) size_t n; TupleExp *te = NULL; if (aggr->op == TOKtuple) // expression tuple - { te = (TupleExp *)aggr; + { + te = (TupleExp *)aggr; n = te->exps->dim; } else if (aggr->op == TOKtype) // type tuple @@ -1533,7 +1708,8 @@ Statement *ForeachStatement::semantic(Scope *sc) else assert(0); for (size_t j = 0; j < n; j++) - { size_t k = (op == TOKforeach) ? j : n - 1 - j; + { + size_t k = (op == TOKforeach) ? j : n - 1 - j; Expression *e = NULL; Type *t = NULL; if (te) @@ -1544,9 +1720,11 @@ Statement *ForeachStatement::semantic(Scope *sc) Statements *st = new Statements(); if (dim == 2) - { // Declare key + { + // Declare key if (arg->storageClass & (STCout | STCref | STClazy)) - { error("no storage class for key %s", arg->ident->toChars()); + { + error("no storage class for key %s", arg->ident->toChars()); goto Lerror; } arg->type = arg->type->semantic(loc, sc); @@ -1556,12 +1734,14 @@ Statement *ForeachStatement::semantic(Scope *sc) if (global.params.is64bit) { if (keyty != Tint64 && keyty != Tuns64) - { error("foreach: key type must be int or uint, long or ulong, not %s", arg->type->toChars()); + { + error("foreach: key type must be int or uint, long or ulong, not %s", arg->type->toChars()); goto Lerror; } } else - { error("foreach: key type must be int or uint, not %s", arg->type->toChars()); + { + error("foreach: key type must be int or uint, not %s", arg->type->toChars()); goto Lerror; } } @@ -1581,24 +1761,32 @@ Statement *ForeachStatement::semantic(Scope *sc) } Dsymbol *var; if (te) - { Type *tb = e->type->toBasetype(); + { + Type *tb = e->type->toBasetype(); Dsymbol *ds = NULL; if ((tb->ty == Tfunction || tb->ty == Tsarray) && e->op == TOKvar) ds = ((VarExp *)e)->var; else if (e->op == TOKtemplate) - ds =((TemplateExp *)e)->td; + ds = ((TemplateExp *)e)->td; else if (e->op == TOKimport) - ds =((ScopeExp *)e)->sds; + ds = ((ScopeExp *)e)->sds; + else if (e->op == TOKfunction) + { + FuncExp *fe = (FuncExp *)e; + ds = fe->td ? (Dsymbol *)fe->td : fe->fd; + } if (ds) { var = new AliasDeclaration(loc, arg->ident, ds); if (arg->storageClass & STCref) - { error("symbol %s cannot be ref", s->toChars()); + { + error("symbol %s cannot be ref", s->toChars()); goto Lerror; } if (argtype) - { error("cannot specify element type for symbol %s", ds->toChars()); + { + error("cannot specify element type for symbol %s", ds->toChars()); goto Lerror; } } @@ -1606,7 +1794,8 @@ Statement *ForeachStatement::semantic(Scope *sc) { var = new AliasDeclaration(loc, arg->ident, e->type); if (argtype) - { error("cannot specify element type for type %s", e->type->toChars()); + { + error("cannot specify element type for type %s", e->type->toChars()); goto Lerror; } } @@ -1621,8 +1810,10 @@ Statement *ForeachStatement::semantic(Scope *sc) v->storage_class |= STCref | STCforeach; if (e->isConst() || e->op == TOKstring || e->op == TOKstructliteral || e->op == TOKarrayliteral) - { if (v->storage_class & STCref) - { error("constant value %s cannot be ref", ie->toChars()); + { + if (v->storage_class & STCref) + { + error("constant value %s cannot be ref", ie->toChars()); goto Lerror; } else @@ -1635,7 +1826,8 @@ Statement *ForeachStatement::semantic(Scope *sc) { var = new AliasDeclaration(loc, arg->ident, t); if (argtype) - { error("cannot specify element type for symbol %s", s->toChars()); + { + error("cannot specify element type for symbol %s", s->toChars()); goto Lerror; } } @@ -1664,11 +1856,11 @@ Statement *ForeachStatement::semantic(Scope *sc) sc->noctor++; -Lagain: switch (tab->ty) { case Tarray: case Tsarray: + { if (!checkForArgTypes()) return this; @@ -1693,13 +1885,16 @@ Statement *ForeachStatement::semantic(Scope *sc) (tnv->ty == Tchar || tnv->ty == Twchar || tnv->ty == Tdchar)) { if (arg->storageClass & STCref) - { error("foreach: value of UTF conversion cannot be ref"); + { + error("foreach: value of UTF conversion cannot be ref"); goto Lerror2; } if (dim == 2) - { arg = (*arguments)[0]; + { + arg = (*arguments)[0]; if (arg->storageClass & STCref) - { error("foreach: key cannot be ref"); + { + error("foreach: key cannot be ref"); goto Lerror2; } } @@ -1708,7 +1903,8 @@ Statement *ForeachStatement::semantic(Scope *sc) } for (size_t i = 0; i < dim; i++) - { // Declare args + { + // Declare args Parameter *arg = (*arguments)[i]; arg->type = arg->type->semantic(loc, sc); arg->type = arg->type->addStorageClass(arg->storageClass); @@ -1732,10 +1928,15 @@ Statement *ForeachStatement::semantic(Scope *sc) goto Lerror2; } } - TypeSArray *ta = tab->ty == Tsarray ? (TypeSArray *)tab : NULL; - if (ta && !IntRange::fromType(var->type).contains(ta->dim->getIntRange())) + if (tab->ty == Tsarray) { - error("index type '%s' cannot cover index range 0..%llu", arg->type->toChars(), ta->dim->toInteger()); + TypeSArray *ta = (TypeSArray *)tab; + IntRange dimrange = getIntRange(ta->dim); + if (!IntRange::fromType(var->type).contains(dimrange)) + { + error("index type '%s' cannot cover index range 0..%llu", arg->type->toChars(), ta->dim->toInteger()); + } + key->range = new IntRange(SignExtendedNumber(0), dimrange.imax); } } else @@ -1749,12 +1950,8 @@ Statement *ForeachStatement::semantic(Scope *sc) value = var; if (var->storage_class & STCref) { - /* Reference to immutable data should be marked as const - */ if (aggr->checkModifiable(sc, 1) == 2) var->storage_class |= STCctorinit; - else if (!tn->isMutable()) - var->storage_class |= STCconst; Type *t = tab->nextOf(); if (!t->immutableOf()->equals(arg->type->immutableOf()) || @@ -1766,14 +1963,8 @@ Statement *ForeachStatement::semantic(Scope *sc) } } } -#if 0 - DeclarationExp *de = new DeclarationExp(loc, var); - de->semantic(sc); -#endif } -#if 1 - { /* Convert to a ForStatement * foreach (key, value; a) body => * for (T[] tmp = a[], size_t key; key < tmp.length; ++key) @@ -1785,7 +1976,19 @@ Statement *ForeachStatement::semantic(Scope *sc) */ Identifier *id = Lexer::uniqueId("__aggr"); ExpInitializer *ie = new ExpInitializer(loc, new SliceExp(loc, aggr, NULL, NULL)); - VarDeclaration *tmp = new VarDeclaration(loc, tab->nextOf()->arrayOf(), id, ie); + VarDeclaration *tmp; + if (aggr->op == TOKarrayliteral && + !((*arguments)[dim - 1]->storageClass & STCref)) + { + ArrayLiteralExp *ale = (ArrayLiteralExp *)aggr; + size_t edim = ale->elements ? ale->elements->dim : 0; + aggr->type = tab->nextOf()->sarrayOf(edim); + + // for (T[edim] tmp = a, ...) + tmp = new VarDeclaration(loc, aggr->type, id, ie); + } + else + tmp = new VarDeclaration(loc, tab->nextOf()->arrayOf(), id, ie); tmp->storage_class |= STCtemp; Expression *tmp_length = new DotIdExp(loc, new VarExp(loc, tmp), Id::length); @@ -1799,7 +2002,7 @@ Statement *ForeachStatement::semantic(Scope *sc) if (op == TOKforeach_reverse) key->init = new ExpInitializer(loc, tmp_length); else - key->init = new ExpInitializer(loc, new IntegerExp(loc, 0, NULL)); + key->init = new ExpInitializer(loc, new IntegerExp(loc, 0, key->type)); Statements *cs = new Statements(); cs->push(new ExpStatement(loc, tmp)); @@ -1808,25 +2011,33 @@ Statement *ForeachStatement::semantic(Scope *sc) Expression *cond; if (op == TOKforeach_reverse) + { // key-- cond = new PostExp(TOKminusminus, loc, new VarExp(loc, key)); + } else + { // key < tmp.length cond = new CmpExp(TOKlt, loc, new VarExp(loc, key), tmp_length); + } Expression *increment = NULL; if (op == TOKforeach) + { // key += 1 - increment = new AddAssignExp(loc, new VarExp(loc, key), new IntegerExp(loc, 1, NULL)); + increment = new AddAssignExp(loc, new VarExp(loc, key), new IntegerExp(loc, 1, key->type)); + } // T value = tmp[key]; value->init = new ExpInitializer(loc, new IndexExp(loc, new VarExp(loc, tmp), new VarExp(loc, key))); Statement *ds = new ExpStatement(loc, value); if (dim == 2) - { Parameter *arg = (*arguments)[0]; + { + Parameter *arg = (*arguments)[0]; if ((arg->storageClass & STCref) && arg->type->equals(key->type)) { + key->range = NULL; AliasDeclaration *v = new AliasDeclaration(loc, arg->ident, key); body = new CompoundStatement(loc, new ExpStatement(loc, v), body); } @@ -1836,6 +2047,12 @@ Statement *ForeachStatement::semantic(Scope *sc) VarDeclaration *v = new VarDeclaration(loc, arg->type, arg->ident, ei); v->storage_class |= STCforeach | (arg->storageClass & STCref); body = new CompoundStatement(loc, new ExpStatement(loc, v), body); + if (key->range && !arg->type->isMutable()) + { + /* Limit the range of the key to the specified range + */ + v->range = new IntRange(key->range->imin, key->range->imax - SignExtendedNumber(1)); + } } } body = new CompoundStatement(loc, ds, body); @@ -1845,39 +2062,7 @@ Statement *ForeachStatement::semantic(Scope *sc) ls->gotoTarget = s; s = s->semantic(sc); break; - } -#else - if (tab->nextOf()->implicitConvTo(value->type) < MATCHconst) - { - if (aggr->op == TOKstring) - aggr = aggr->implicitCastTo(sc, value->type->arrayOf()); - else - error("foreach: %s is not an array of %s", - tab->toChars(), value->type->toChars()); - } - - if (key) - { - if (key->type->ty != Tint32 && key->type->ty != Tuns32) - { - if (global.params.is64bit) - { - if (key->type->ty != Tint64 && key->type->ty != Tuns64) - error("foreach: key type must be int or uint, long or ulong, not %s", key->type->toChars()); - } - else - error("foreach: key type must be int or uint, not %s", key->type->toChars()); - } - - if (key->storage_class & (STCout | STCref)) - error("foreach: key cannot be out or ref"); - } - - sc->sbreak = this; - sc->scontinue = this; - body = body->semantic(sc); - break; -#endif + } case Taarray: if (!checkForArgTypes()) @@ -1889,11 +2074,7 @@ Statement *ForeachStatement::semantic(Scope *sc) error("only one or two arguments for associative array foreach"); goto Lerror2; } - - /* This only works if Key or Value is a static array. - */ - tab = taa->getImpl()->type; - goto Lagain; + goto Lapply; case Tclass: case Tstruct: @@ -1902,12 +2083,13 @@ Statement *ForeachStatement::semantic(Scope *sc) if (sapply) goto Lapply; - { /* Look for range iteration, i.e. the properties + { + /* Look for range iteration, i.e. the properties * .empty, .popFront, .popBack, .front and .back * foreach (e; aggr) { ... } * translates to: - * for (auto __r = aggr[]; !__r.empty; __r.popFront) - * { auto e = __r.front; + * for (auto __r = aggr[]; !__r.empty; __r.popFront) { + * auto e = __r.front; * ... * } */ @@ -1917,11 +2099,13 @@ Statement *ForeachStatement::semantic(Scope *sc) Identifier *idfront; Identifier *idpopFront; if (op == TOKforeach) - { idfront = Id::Ffront; + { + idfront = Id::Ffront; idpopFront = Id::FpopFront; } else - { idfront = Id::Fback; + { + idfront = Id::Fback; idpopFront = Id::FpopBack; } Dsymbol *sfront = ad->search(Loc(), idfront); @@ -1995,7 +2179,12 @@ Statement *ForeachStatement::semantic(Scope *sc) break; } if (exps->dim != dim) - goto Lrangeerr; + { + const char *plural = exps->dim > 1 ? "s" : ""; + error("cannot infer argument types, expected %d argument%s, not %d", + exps->dim, plural, dim); + goto Lerror2; + } for (size_t i = 0; i < dim; i++) { @@ -2008,7 +2197,7 @@ Statement *ForeachStatement::semantic(Scope *sc) #endif if (!arg->type) arg->type = exp->type; - arg->type = arg->type->addStorageClass(arg->storageClass); + arg->type = arg->type->addStorageClass(arg->storageClass)->semantic(loc, sc); if (!exp->implicitConvTo(arg->type)) goto Lrangeerr; @@ -2040,21 +2229,26 @@ Statement *ForeachStatement::semantic(Scope *sc) goto Lerror2; } case Tdelegate: + if (op == TOKforeach_reverse) + warning("cannot use foreach_reverse with a delegate"); Lapply: { Expression *ec; Expression *e; if (!checkForArgTypes()) - { body = body->semanticNoScope(sc); + { + body = body->semanticNoScope(sc); return this; } TypeFunction *tfld = NULL; if (sapply) - { FuncDeclaration *fdapply = sapply->isFuncDeclaration(); + { + FuncDeclaration *fdapply = sapply->isFuncDeclaration(); if (fdapply) - { assert(fdapply->type && fdapply->type->ty == Tfunction); + { + assert(fdapply->type && fdapply->type->ty == Tfunction); tfld = (TypeFunction *)fdapply->type->semantic(loc, sc); goto Lget; } @@ -2067,7 +2261,8 @@ Statement *ForeachStatement::semantic(Scope *sc) { Parameter *p = Parameter::getNth(tfld->parameters, 0); if (p->type && p->type->ty == Tdelegate) - { Type *t = p->type->semantic(loc, sc); + { + Type *t = p->type->semantic(loc, sc); assert(t->ty == Tdelegate); tfld = (TypeFunction *)t->nextOf(); } @@ -2080,32 +2275,38 @@ Statement *ForeachStatement::semantic(Scope *sc) */ Parameters *args = new Parameters(); for (size_t i = 0; i < dim; i++) - { Parameter *arg = (*arguments)[i]; + { + Parameter *arg = (*arguments)[i]; StorageClass stc = STCref; Identifier *id; arg->type = arg->type->semantic(loc, sc); arg->type = arg->type->addStorageClass(arg->storageClass); if (tfld) - { Parameter *prm = Parameter::getNth(tfld->parameters, i); + { + Parameter *prm = Parameter::getNth(tfld->parameters, i); //printf("\tprm = %s%s\n", (prm->storageClass&STCref?"ref ":""), prm->ident->toChars()); stc = prm->storageClass & STCref; id = arg->ident; // argument copy is not need. if ((arg->storageClass & STCref) != stc) - { if (!stc) - { error("foreach: cannot make %s ref", arg->ident->toChars()); + { + if (!stc) + { + error("foreach: cannot make %s ref", arg->ident->toChars()); goto Lerror2; } goto LcopyArg; } } else if (arg->storageClass & STCref) - { // default delegate parameters are marked as ref, then + { + // default delegate parameters are marked as ref, then // argument copy is not need. id = arg->ident; } else - { // Make a copy of the ref argument so it isn't + { + // Make a copy of the ref argument so it isn't // a reference. LcopyArg: id = Lexer::uniqueId("__applyArg", (int)i); @@ -2147,18 +2348,21 @@ Statement *ForeachStatement::semantic(Scope *sc) if (dim == 2) { if (arg->storageClass & STCref) - { error("foreach: index cannot be ref"); + { + error("foreach: index cannot be ref"); goto Lerror2; } if (!taa->index->implicitConvTo(arg->type)) - { error("foreach: index must be type %s, not %s", taa->index->toChars(), arg->type->toChars()); + { + error("foreach: index must be type %s, not %s", taa->index->toChars(), arg->type->toChars()); goto Lerror2; } arg = (*arguments)[1]; } if ((!arg->type->equals(taa->nextOf()) && (arg->storageClass & STCref)) || !taa->nextOf()->implicitConvTo(arg->type)) - { error("foreach: value must be type %s, not %s", taa->nextOf()->toChars(), arg->type->toChars()); + { + error("foreach: value must be type %s, not %s", taa->nextOf()->toChars(), arg->type->toChars()); goto Lerror2; } /* Call: @@ -2171,14 +2375,14 @@ Statement *ForeachStatement::semantic(Scope *sc) unsigned char i = dim == 2; if (!fdapply[i]) { args = new Parameters; - args->push(new Parameter(STCin, Type::tvoid->pointerTo(), NULL, NULL)); + args->push(new Parameter(0, Type::tvoid->pointerTo(), NULL, NULL)); args->push(new Parameter(STCin, Type::tsize_t, NULL, NULL)); Parameters* dgargs = new Parameters; - dgargs->push(new Parameter(STCin, Type::tvoidptr, NULL, NULL)); + dgargs->push(new Parameter(0, Type::tvoidptr, NULL, NULL)); if (dim == 2) - dgargs->push(new Parameter(STCin, Type::tvoidptr, NULL, NULL)); + dgargs->push(new Parameter(0, Type::tvoidptr, NULL, NULL)); fldeTy[i] = new TypeDelegate(new TypeFunction(dgargs, Type::tint32, 0, LINKd)); - args->push(new Parameter(STCin, fldeTy[i], NULL, NULL)); + args->push(new Parameter(0, fldeTy[i], NULL, NULL)); fdapply[i] = FuncDeclaration::genCfunc(args, Type::tint32, name[i]); } @@ -2234,11 +2438,11 @@ Statement *ForeachStatement::semantic(Scope *sc) args = new Parameters; args->push(new Parameter(STCin, tn->arrayOf(), NULL, NULL)); Parameters* dgargs = new Parameters; - dgargs->push(new Parameter(STCin, Type::tvoidptr, NULL, NULL)); + dgargs->push(new Parameter(0, Type::tvoidptr, NULL, NULL)); if (dim == 2) - dgargs->push(new Parameter(STCin, Type::tvoidptr, NULL, NULL)); + dgargs->push(new Parameter(0, Type::tvoidptr, NULL, NULL)); dgty = new TypeDelegate(new TypeFunction(dgargs, Type::tint32, 0, LINKd)); - args->push(new Parameter(STCin, dgty, NULL, NULL)); + args->push(new Parameter(0, dgty, NULL, NULL)); fdapply = FuncDeclaration::genCfunc(args, Type::tint32, fdname); ec = new VarExp(Loc(), fdapply); @@ -2264,13 +2468,18 @@ Statement *ForeachStatement::semantic(Scope *sc) exps->push(flde); if (aggr->op == TOKdelegate && ((DelegateExp *)aggr)->func->isNested()) + { // See Bugzilla 3560 e = new CallExp(loc, ((DelegateExp *)aggr)->e1, exps); + } else e = new CallExp(loc, aggr, exps); e = e->semantic(sc); + if (e->op == TOKerror) + goto Lerror2; if (e->type != Type::tint32) - { error("opApply() function for %s must return an int", tab->toChars()); + { + error("opApply() function for %s must return an int", tab->toChars()); goto Lerror2; } } @@ -2286,17 +2495,23 @@ Statement *ForeachStatement::semantic(Scope *sc) exps->push(flde); e = new CallExp(loc, ec, exps); e = e->semantic(sc); + if (e->op == TOKerror) + goto Lerror2; if (e->type != Type::tint32) - { error("opApply() function for %s must return an int", tab->toChars()); + { + error("opApply() function for %s must return an int", tab->toChars()); goto Lerror2; } } if (!cases->dim) + { // Easy case, a clean exit from the loop s = new ExpStatement(loc, e); + } else - { // Construct a switch statement around the return value + { + // Construct a switch statement around the return value // of the apply function. Statements *a = new Statements(); @@ -2334,10 +2549,12 @@ Statement *ForeachStatement::semantic(Scope *sc) } bool ForeachStatement::checkForArgTypes() -{ bool result = true; +{ + bool result = true; for (size_t i = 0; i < arguments->dim; i++) - { Parameter *arg = (*arguments)[i]; + { + Parameter *arg = (*arguments)[i]; if (!arg->type) { error("cannot infer type for %s", arg->ident->toChars()); @@ -2358,50 +2575,6 @@ bool ForeachStatement::hasContinue() return true; } -int ForeachStatement::blockExit(bool mustNotThrow) -{ int result = BEfallthru; - - if (aggr->canThrow(mustNotThrow)) - result |= BEthrow; - - if (body) - { - result |= body->blockExit(mustNotThrow) & ~(BEbreak | BEcontinue); - } - return result; -} - - -void ForeachStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring(Token::toChars(op)); - buf->writestring(" ("); - for (size_t i = 0; i < arguments->dim; i++) - { - Parameter *a = (*arguments)[i]; - if (i) - buf->writestring(", "); - if (a->storageClass & STCref) - buf->writestring((char*)"ref "); - if (a->type) - a->type->toCBuffer(buf, a->ident, hgs); - else - buf->writestring(a->ident->toChars()); - } - buf->writestring("; "); - aggr->toCBuffer(buf, hgs); - buf->writebyte(')'); - buf->writenl(); - buf->writebyte('{'); - buf->writenl(); - buf->level++; - if (body) - body->toCBuffer(buf, hgs); - buf->level--; - buf->writebyte('}'); - buf->writenl(); -} - /**************************** ForeachRangeStatement ***************************/ @@ -2455,7 +2628,22 @@ Statement *ForeachRangeStatement::semantic(Scope *sc) arg->type = arg->type->semantic(loc, sc); arg->type = arg->type->addStorageClass(arg->storageClass); lwr = lwr->implicitCastTo(sc, arg->type); - upr = upr->implicitCastTo(sc, arg->type); + + if (upr->implicitConvTo(arg->type) || (arg->storageClass & STCref)) + { + upr = upr->implicitCastTo(sc, arg->type); + } + else + { + // See if upr-1 fits in arg->type + Expression *limit = new MinExp(loc, upr, new IntegerExp(1)); + limit = limit->semantic(sc); + limit = limit->optimize(WANTvalue); + if (!limit->implicitConvTo(arg->type)) + { + upr = upr->implicitCastTo(sc, arg->type); + } + } } else { @@ -2477,13 +2665,18 @@ Statement *ForeachRangeStatement::semantic(Scope *sc) else { AddExp ea(loc, lwr, upr); - Expression *e = ea.typeCombine(sc); + if (typeCombine(&ea, sc)) + return new ErrorStatement(); arg->type = ea.type; lwr = ea.e1; upr = ea.e2; } arg->type = arg->type->addStorageClass(arg->storageClass); } + if (arg->type->ty == Terror || + lwr->op == TOKerror || + upr->op == TOKerror) + return new ErrorStatement(); /* Convert to a for loop: * foreach (key; lwr .. upr) => @@ -2492,14 +2685,19 @@ Statement *ForeachRangeStatement::semantic(Scope *sc) * foreach_reverse (key; lwr .. upr) => * for (auto tmp = lwr, auto key = upr; key-- > tmp;) */ - ExpInitializer *ie = new ExpInitializer(loc, (op == TOKforeach) ? lwr : upr); - key = new VarDeclaration(loc, arg->type->mutableOf(), Lexer::uniqueId("__key"), ie); + key = new VarDeclaration(loc, upr->type->mutableOf(), Lexer::uniqueId("__key"), ie); key->storage_class |= STCtemp; + SignExtendedNumber lower = getIntRange(lwr).imin; + SignExtendedNumber upper = getIntRange(upr).imax; + if (lower <= upper) + { + key->range = new IntRange(lower, upper); + } Identifier *id = Lexer::uniqueId("__limit"); ie = new ExpInitializer(loc, (op == TOKforeach) ? upr : lwr); - VarDeclaration *tmp = new VarDeclaration(loc, arg->type, id, ie); + VarDeclaration *tmp = new VarDeclaration(loc, upr->type, id, ie); tmp->storage_class |= STCtemp; Statements *cs = new Statements(); @@ -2521,20 +2719,28 @@ Statement *ForeachRangeStatement::semantic(Scope *sc) { cond = new PostExp(TOKminusminus, loc, new VarExp(loc, key)); if (arg->type->isscalar()) + { // key-- > tmp cond = new CmpExp(TOKgt, loc, cond, new VarExp(loc, tmp)); + } else + { // key-- != tmp cond = new EqualExp(TOKnotequal, loc, cond, new VarExp(loc, tmp)); + } } else { if (arg->type->isscalar()) + { // key < tmp cond = new CmpExp(TOKlt, loc, new VarExp(loc, key), new VarExp(loc, tmp)); + } else + { // key != tmp cond = new EqualExp(TOKnotequal, loc, new VarExp(loc, key), new VarExp(loc, tmp)); + } } Expression *increment = NULL; @@ -2545,15 +2751,22 @@ Statement *ForeachRangeStatement::semantic(Scope *sc) if ((arg->storageClass & STCref) && arg->type->equals(key->type)) { + key->range = NULL; AliasDeclaration *v = new AliasDeclaration(loc, arg->ident, key); body = new CompoundStatement(loc, new ExpStatement(loc, v), body); } else { - ie = new ExpInitializer(loc, new IdentifierExp(loc, key->ident)); + ie = new ExpInitializer(loc, new CastExp(loc, new VarExp(loc, key), arg->type)); VarDeclaration *v = new VarDeclaration(loc, arg->type, arg->ident, ie); v->storage_class |= STCtemp | STCforeach | (arg->storageClass & STCref); body = new CompoundStatement(loc, new ExpStatement(loc, v), body); + if (key->range && !arg->type->isMutable()) + { + /* Limit the range of the key to the specified range + */ + v->range = new IntRange(key->range->imin, key->range->imax - SignExtendedNumber(1)); + } } if (arg->storageClass & STCref) { @@ -2581,40 +2794,6 @@ bool ForeachRangeStatement::hasContinue() return true; } -int ForeachRangeStatement::blockExit(bool mustNotThrow) -{ - assert(global.errors); - return BEfallthru; -} - - -void ForeachRangeStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring(Token::toChars(op)); - buf->writestring(" ("); - - if (arg->type) - arg->type->toCBuffer(buf, arg->ident, hgs); - else - buf->writestring(arg->ident->toChars()); - - buf->writestring("; "); - lwr->toCBuffer(buf, hgs); - buf->writestring(" .. "); - upr->toCBuffer(buf, hgs); - buf->writebyte(')'); - buf->writenl(); - buf->writebyte('{'); - buf->writenl(); - buf->level++; - if (body) - body->toCBuffer(buf, hgs); - buf->level--; - buf->writebyte('}'); - buf->writenl(); -} - - /******************************** IfStatement ***************************/ IfStatement::IfStatement(Loc loc, Parameter *arg, Expression *condition, Statement *ifbody, Statement *elsebody) @@ -2654,7 +2833,8 @@ Statement *IfStatement::semantic(Scope *sc) sym->parent = sc->scopesym; Scope *scd = sc->push(sym); if (arg) - { /* Declare arg, which we will set to be the + { + /* Declare arg, which we will set to be the * result of condition. */ @@ -2681,6 +2861,7 @@ Statement *IfStatement::semantic(Scope *sc) condition = condition->addDtorHook(sc); condition = resolveProperties(sc, condition); } + condition = checkGC(sc, condition); // Convert to boolean after declaring arg so this works: // if (S arg = S()) {} @@ -2712,77 +2893,6 @@ Statement *IfStatement::semantic(Scope *sc) return this; } -int IfStatement::blockExit(bool mustNotThrow) -{ - //printf("IfStatement::blockExit(%p)\n", this); - - int result = BEnone; - if (condition->canThrow(mustNotThrow)) - result |= BEthrow; - if (condition->isBool(true)) - { - if (ifbody) - result |= ifbody->blockExit(mustNotThrow); - else - result |= BEfallthru; - } - else if (condition->isBool(false)) - { - if (elsebody) - result |= elsebody->blockExit(mustNotThrow); - else - result |= BEfallthru; - } - else - { - if (ifbody) - result |= ifbody->blockExit(mustNotThrow); - else - result |= BEfallthru; - if (elsebody) - result |= elsebody->blockExit(mustNotThrow); - else - result |= BEfallthru; - } - //printf("IfStatement::blockExit(%p) = x%x\n", this, result); - return result; -} - - -void IfStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("if ("); - if (arg) - { - if (arg->type) - arg->type->toCBuffer(buf, arg->ident, hgs); - else - { - buf->writestring("auto "); - buf->writestring(arg->ident->toChars()); - } - buf->writestring(" = "); - } - condition->toCBuffer(buf, hgs); - buf->writebyte(')'); - buf->writenl(); - if (!ifbody->isScopeStatement()) - buf->level++; - ifbody->toCBuffer(buf, hgs); - if (!ifbody->isScopeStatement()) - buf->level--; - if (elsebody) - { - buf->writestring("else"); - buf->writenl(); - if (!elsebody->isScopeStatement()) - buf->level++; - elsebody->toCBuffer(buf, hgs); - if (!elsebody->isScopeStatement()) - buf->level--; - } -} - /******************************** ConditionalStatement ***************************/ ConditionalStatement::ConditionalStatement(Loc loc, Condition *condition, Statement *ifbody, Statement *elsebody) @@ -2853,42 +2963,6 @@ Statements *ConditionalStatement::flatten(Scope *sc) return a; } -int ConditionalStatement::blockExit(bool mustNotThrow) -{ - int result = ifbody->blockExit(mustNotThrow); - if (elsebody) - result |= elsebody->blockExit(mustNotThrow); - return result; -} - -void ConditionalStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - condition->toCBuffer(buf, hgs); - buf->writenl(); - buf->writeByte('{'); - buf->writenl(); - buf->level++; - if (ifbody) - ifbody->toCBuffer(buf, hgs); - buf->level--; - buf->writeByte('}'); - buf->writenl(); - if (elsebody) - { - buf->writestring("else"); - buf->writenl(); - buf->writeByte('{'); - buf->level++; - buf->writenl(); - elsebody->toCBuffer(buf, hgs); - buf->level--; - buf->writeByte('}'); - buf->writenl(); - } - buf->writenl(); -} - - /******************************** PragmaStatement ***************************/ PragmaStatement::PragmaStatement(Loc loc, Identifier *ident, Expressions *args, Statement *body) @@ -2931,9 +3005,10 @@ Statement *PragmaStatement::semantic(Scope *sc) { errorSupplemental(loc, "while evaluating pragma(msg, %s)", (*args)[i]->toChars()); goto Lerror; } - StringExp *se = e->toString(); + StringExp *se = e->toStringExp(); if (se) { + se = se->toUTF8(sc); fprintf(stderr, "%.*s", (int)se->len, (char *)se->string); } else @@ -2962,7 +3037,7 @@ Statement *PragmaStatement::semantic(Scope *sc) e = e->ctfeInterpret(); (*args)[0] = e; - StringExp *se = e->toString(); + StringExp *se = e->toStringExp(); if (!se) error("string expected for library name, not '%s'", e->toChars()); else if (global.params.verbose) @@ -3011,50 +3086,6 @@ Statement *PragmaStatement::semantic(Scope *sc) return body; } -int PragmaStatement::blockExit(bool mustNotThrow) -{ - int result = BEfallthru; -#if 0 // currently, no code is generated for Pragma's, so it's just fallthru - if (arrayExpressionCanThrow(args)) - result |= BEthrow; - if (body) - result |= body->blockExit(mustNotThrow); -#endif - return result; -} - - -void PragmaStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("pragma ("); - buf->writestring(ident->toChars()); - if (args && args->dim) - { - buf->writestring(", "); - argsToCBuffer(buf, args, hgs); - } - buf->writeByte(')'); - if (body) - { - buf->writenl(); - buf->writeByte('{'); - buf->writenl(); - buf->level++; - - body->toCBuffer(buf, hgs); - - buf->level--; - buf->writeByte('}'); - buf->writenl(); - } - else - { - buf->writeByte(';'); - buf->writenl(); - } -} - - /******************************** StaticAssertStatement ***************************/ StaticAssertStatement::StaticAssertStatement(StaticAssert *sa) @@ -3075,17 +3106,6 @@ Statement *StaticAssertStatement::semantic(Scope *sc) return NULL; } -int StaticAssertStatement::blockExit(bool mustNotThrow) -{ - return BEfallthru; -} - -void StaticAssertStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - sa->toCBuffer(buf, hgs); -} - - /******************************** SwitchStatement ***************************/ SwitchStatement::SwitchStatement(Loc loc, Expression *c, Statement *b, bool isFinal) @@ -3132,11 +3152,12 @@ Statement *SwitchStatement::semantic(Scope *sc) } else { - condition = condition->integralPromotions(sc); + condition = integralPromotions(condition, sc); if (condition->op != TOKerror && !condition->type->isintegral()) error("'%s' must be of integral or string type, it is a %s", condition->toChars(), condition->type->toChars()); } condition = condition->optimize(WANTvalue); + condition = checkGC(sc, condition); sc = sc->push(); sc->sbreak = this; @@ -3181,11 +3202,8 @@ Statement *SwitchStatement::semantic(Scope *sc) bool needswitcherror = false; if (isFinal) - { Type *t = condition->type; - while (t && t->ty == Ttypedef) - { // Don't use toBasetype() because that will skip past enums - t = ((TypeTypedef *)t)->sym->basetype; - } + { + Type *t = condition->type; Dsymbol *ds; EnumDeclaration *ed = NULL; if (t && ((ds = t->toDsymbol(sc)) != NULL)) @@ -3222,7 +3240,7 @@ Statement *SwitchStatement::semantic(Scope *sc) { hasNoDefault = 1; if (!isFinal && !body->isErrorStatement()) - deprecation("non-final switch statement without a default is deprecated"); + deprecation("switch statement without a default is deprecated; use 'final switch' or add 'default: assert(0);' or add 'default: break;'"); // Generate runtime error if the default is hit Statements *a = new Statements(); @@ -3239,7 +3257,7 @@ Statement *SwitchStatement::semantic(Scope *sc) a->reserve(2); sc->sw->sdefault = new DefaultStatement(loc, s); a->push(body); - if (body->blockExit(false) & BEfallthru) + if (body->blockExit(sc->func, false) & BEfallthru) a->push(new BreakStatement(Loc(), NULL)); a->push(sc->sw->sdefault); cs = new CompoundStatement(loc, a); @@ -3255,50 +3273,6 @@ bool SwitchStatement::hasBreak() return true; } -int SwitchStatement::blockExit(bool mustNotThrow) -{ int result = BEnone; - if (condition->canThrow(mustNotThrow)) - result |= BEthrow; - - if (body) - { result |= body->blockExit(mustNotThrow); - if (result & BEbreak) - { result |= BEfallthru; - result &= ~BEbreak; - } - } - else - result |= BEfallthru; - - return result; -} - - -void SwitchStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring(isFinal ? "final switch (" : "switch ("); - condition->toCBuffer(buf, hgs); - buf->writebyte(')'); - buf->writenl(); - if (body) - { - if (!body->isScopeStatement()) - { - buf->writebyte('{'); - buf->writenl(); - buf->level++; - body->toCBuffer(buf, hgs); - buf->level--; - buf->writebyte('}'); - buf->writenl(); - } - else - { - body->toCBuffer(buf, hgs); - } - } -} - /******************************** CaseStatement ***************************/ CaseStatement::CaseStatement(Loc loc, Expression *exp, Statement *s) @@ -3349,7 +3323,9 @@ Statement *CaseStatement::semantic(Scope *sc) else exp = exp->ctfeInterpret(); - if (exp->op != TOKstring && exp->op != TOKint64 && exp->op != TOKerror) + if (StringExp *se = exp->toStringExp()) + exp = se; + else if (exp->op != TOKint64 && exp->op != TOKerror) { error("case must be a string or an integral constant, not %s", exp->toChars()); exp = new ErrorExp(); @@ -3398,21 +3374,6 @@ int CaseStatement::compare(RootObject *obj) return exp->compare(cs2->exp); } -int CaseStatement::blockExit(bool mustNotThrow) -{ - return statement->blockExit(mustNotThrow); -} - - -void CaseStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("case "); - exp->toCBuffer(buf, hgs); - buf->writebyte(':'); - buf->writenl(); - statement->toCBuffer(buf, hgs); -} - /******************************** CaseRangeStatement ***************************/ @@ -3504,18 +3465,6 @@ Statement *CaseRangeStatement::semantic(Scope *sc) return s; } -void CaseRangeStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("case "); - first->toCBuffer(buf, hgs); - buf->writestring(": .. case "); - last->toCBuffer(buf, hgs); - buf->writebyte(':'); - buf->writenl(); - statement->toCBuffer(buf, hgs); -} - - /******************************** DefaultStatement ***************************/ DefaultStatement::DefaultStatement(Loc loc, Statement *s) @@ -3556,19 +3505,6 @@ Statement *DefaultStatement::semantic(Scope *sc) return this; } -int DefaultStatement::blockExit(bool mustNotThrow) -{ - return statement->blockExit(mustNotThrow); -} - - -void DefaultStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("default:"); - buf->writenl(); - statement->toCBuffer(buf, hgs); -} - /******************************** GotoDefaultStatement ***************************/ GotoDefaultStatement::GotoDefaultStatement(Loc loc) @@ -3591,18 +3527,6 @@ Statement *GotoDefaultStatement::semantic(Scope *sc) return this; } -int GotoDefaultStatement::blockExit(bool mustNotThrow) -{ - return BEgoto; -} - - -void GotoDefaultStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("goto default;"); - buf->writenl(); -} - /******************************** GotoCaseStatement ***************************/ GotoCaseStatement::GotoCaseStatement(Loc loc, Expression *exp) @@ -3638,23 +3562,6 @@ Statement *GotoCaseStatement::semantic(Scope *sc) return this; } -int GotoCaseStatement::blockExit(bool mustNotThrow) -{ - return BEgoto; -} - - -void GotoCaseStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("goto case"); - if (exp) - { buf->writebyte(' '); - exp->toCBuffer(buf, hgs); - } - buf->writebyte(';'); - buf->writenl(); -} - /******************************** SwitchErrorStatement ***************************/ SwitchErrorStatement::SwitchErrorStatement(Loc loc) @@ -3662,19 +3569,6 @@ SwitchErrorStatement::SwitchErrorStatement(Loc loc) { } -int SwitchErrorStatement::blockExit(bool mustNotThrow) -{ - // Switch errors are non-recoverable - return BEhalt; -} - - -void SwitchErrorStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("SwitchErrorStatement::toCBuffer()"); - buf->writenl(); -} - /******************************** ReturnStatement ***************************/ ReturnStatement::ReturnStatement(Loc loc, Expression *exp) @@ -3719,14 +3613,17 @@ Statement *ReturnStatement::semantic(Scope *sc) // main() returns 0, even if it returns void if (!exp && (!tbret || tbret->ty == Tvoid) && fd->isMain()) - { implicit0 = 1; + { + implicit0 = 1; exp = new IntegerExp(0); } - if ((sc->flags & SCOPEcontract) || (scx->flags & SCOPEcontract)) + if (sc->flags & SCOPEcontract) error("return statements cannot be in contracts"); - if (sc->tf || scx->tf) - error("return statements cannot be in finally, scope(exit) or scope(success) bodies"); + if (sc->os && sc->os->tok != TOKon_scope_failure) + error("return statements cannot be in %s bodies", Token::toChars(sc->os->tok)); + if (sc->tf) + error("return statements cannot be in finally bodies"); if (fd->isCtorDeclaration()) { @@ -3747,13 +3644,22 @@ Statement *ReturnStatement::semantic(Scope *sc) FuncLiteralDeclaration *fld = fd->isFuncLiteralDeclaration(); if (tret) - exp = exp->inferType(tbret); + exp = inferType(exp, tbret); else if (fld && fld->treq) - exp = exp->inferType(fld->treq->nextOf()->nextOf()); + exp = inferType(exp, fld->treq->nextOf()->nextOf()); exp = exp->semantic(sc); exp = resolveProperties(sc, exp); - if (!exp->rvalue(true)) // don't make error for void expression + if (exp->type && exp->type->ty != Tvoid || + exp->op == TOKfunction || exp->op == TOKtype || exp->op == TOKtemplate) + { + if (!exp->rvalue()) // don't make error for void expression + exp = new ErrorExp(); + } + if (isNonAssignmentArrayOp(exp)) + { + exp->error("array operation %s without assignment not implemented", exp->toChars()); exp = new ErrorExp(); + } if (exp->op == TOKcall) exp = valueNoDtor(exp); @@ -3788,8 +3694,10 @@ Statement *ReturnStatement::semantic(Scope *sc) VarDeclaration *v = ve->var->isVarDeclaration(); if (tf->isref) + { // Function returns a reference fd->nrvo_can = 0; + } else if (!v || v->isOut() || v->isRef()) fd->nrvo_can = 0; else if (fd->nrvo_var == NULL) @@ -3871,10 +3779,6 @@ Statement *ReturnStatement::semantic(Scope *sc) if (!tf->isref) exp = exp->optimize(WANTvalue); - - if (!fd->returns) - fd->returns = new ReturnStatements(); - fd->returns->push(this); } } else if (fd->inferRetType) @@ -3947,7 +3851,10 @@ Statement *ReturnStatement::semantic(Scope *sc) sc->fes->cases->push(s); // Construct: { vresult = exp; return cases->dim + 1; } - exp = new ConstructExp(loc, new VarExp(Loc(), fd->vresult), exp); + if (tf->isref) + exp = new ConstructExp(loc, new VarExp(Loc(), fd->vresult), exp); + else + exp = new BlitExp(loc, new VarExp(Loc(), fd->vresult), exp); exp = exp->semantic(sc); Statement *s1 = new ExpStatement(loc, exp); Statement *s2 = new ReturnStatement(Loc(), new IntegerExp(sc->fes->cases->dim + 1)); @@ -3958,6 +3865,8 @@ Statement *ReturnStatement::semantic(Scope *sc) if (exp) { + exp = checkGC(sc, exp); + if (tf->isref && !fd->isCtorDeclaration()) { // Function returns a reference @@ -3966,7 +3875,6 @@ Statement *ReturnStatement::semantic(Scope *sc) } else { - //exp->dump(0); //exp->print(); exp->checkEscape(); } @@ -3977,7 +3885,10 @@ Statement *ReturnStatement::semantic(Scope *sc) VarExp *v = new VarExp(Loc(), fd->vresult); assert(eorg); - exp = new ConstructExp(loc, v, eorg); + if (tf->isref) + exp = new ConstructExp(loc, v, eorg); + else + exp = new BlitExp(loc, v, eorg); exp = exp->semantic(sc); } } @@ -4011,7 +3922,8 @@ Statement *ReturnStatement::semantic(Scope *sc) gs->label = fd->returnLabel; if (exp) - { /* Replace: return exp; + { + /* Replace: return exp; * with: exp; goto returnLabel; */ Statement *s = new ExpStatement(Loc(), exp); @@ -4033,32 +3945,14 @@ Statement *ReturnStatement::semantic(Scope *sc) * cast(void)exp; return; */ Expression *ce = new CastExp(loc, exp, Type::tvoid); - Statement *s = new ExpStatement(loc, ce); - s = s->semantic(sc); - - exp = NULL; - return new CompoundStatement(loc, s, this); - } - - return this; -} - -int ReturnStatement::blockExit(bool mustNotThrow) -{ int result = BEreturn; - - if (exp && exp->canThrow(mustNotThrow)) - result |= BEthrow; - return result; -} + Statement *s = new ExpStatement(loc, ce); + s = s->semantic(sc); + exp = NULL; + return new CompoundStatement(loc, s, this); + } -void ReturnStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->printf("return "); - if (exp) - exp->toCBuffer(buf, hgs); - buf->writeByte(';'); - buf->writenl(); + return this; } /******************************** BreakStatement ***************************/ @@ -4111,7 +4005,7 @@ Statement *BreakStatement::semantic(Scope *sc) { Statement *s = ls->statement; - if (!s->hasBreak()) + if (!s || !s->hasBreak()) error("label '%s' has no break", ident->toChars()); else if (ls->tf != sc->tf) error("cannot break out of finally block"); @@ -4125,36 +4019,23 @@ Statement *BreakStatement::semantic(Scope *sc) } else if (!sc->sbreak) { - if (sc->fes) + if (sc->os && sc->os->tok != TOKon_scope_failure) + { + error("break is not inside %s bodies", Token::toChars(sc->os->tok)); + } + else if (sc->fes) { // Replace break; with return 1; Statement *s = new ReturnStatement(Loc(), new IntegerExp(1)); return s; } - error("break is not inside a loop or switch"); + else + error("break is not inside a loop or switch"); return new ErrorStatement(); } return this; } -int BreakStatement::blockExit(bool mustNotThrow) -{ - //printf("BreakStatement::blockExit(%p) = x%x\n", this, ident ? BEgoto : BEbreak); - return ident ? BEgoto : BEbreak; -} - - -void BreakStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("break"); - if (ident) - { buf->writebyte(' '); - buf->writestring(ident->toChars()); - } - buf->writebyte(';'); - buf->writenl(); -} - /******************************** ContinueStatement ***************************/ ContinueStatement::ContinueStatement(Loc loc, Identifier *ident) @@ -4216,7 +4097,7 @@ Statement *ContinueStatement::semantic(Scope *sc) { Statement *s = ls->statement; - if (!s->hasContinue()) + if (!s || !s->hasContinue()) error("label '%s' has no continue", ident->toChars()); else if (ls->tf != sc->tf) error("cannot continue out of finally block"); @@ -4230,34 +4111,23 @@ Statement *ContinueStatement::semantic(Scope *sc) } else if (!sc->scontinue) { - if (sc->fes) + if (sc->os && sc->os->tok != TOKon_scope_failure) + { + error("continue is not inside %s bodies", Token::toChars(sc->os->tok)); + } + else if (sc->fes) { // Replace continue; with return 0; Statement *s = new ReturnStatement(Loc(), new IntegerExp(0)); return s; } - error("continue is not inside a loop"); + else + error("continue is not inside a loop"); + return new ErrorStatement(); } return this; } -int ContinueStatement::blockExit(bool mustNotThrow) -{ - return ident ? BEgoto : BEcontinue; -} - - -void ContinueStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("continue"); - if (ident) - { buf->writebyte(' '); - buf->writestring(ident->toChars()); - } - buf->writebyte(';'); - buf->writenl(); -} - /******************************** SynchronizedStatement ***************************/ SynchronizedStatement::SynchronizedStatement(Loc loc, Expression *exp, Statement *body) @@ -4265,15 +4135,6 @@ SynchronizedStatement::SynchronizedStatement(Loc loc, Expression *exp, Statement { this->exp = exp; this->body = body; - this->esync = NULL; -} - -SynchronizedStatement::SynchronizedStatement(Loc loc, elem *esync, Statement *body) - : Statement(loc) -{ - this->exp = NULL; - this->body = body; - this->esync = esync; } Statement *SynchronizedStatement::syntaxCopy() @@ -4289,6 +4150,8 @@ Statement *SynchronizedStatement::semantic(Scope *sc) { exp = exp->semantic(sc); exp = resolveProperties(sc, exp); + // exp = exp->optimize(0); //? + exp = checkGC(sc, exp); if (exp->op == TOKerror) goto Lbody; ClassDeclaration *cd = exp->type->isClassHandle(); @@ -4330,7 +4193,7 @@ Statement *SynchronizedStatement::semantic(Scope *sc) cs->push(new ExpStatement(loc, tmp)); Parameters* args = new Parameters; - args->push(new Parameter(STCin, ClassDeclaration::object->type, NULL, NULL)); + args->push(new Parameter(0, ClassDeclaration::object->type, NULL, NULL)); FuncDeclaration *fdenter = FuncDeclaration::genCfunc(args, Type::tvoid, Id::monitorenter); Expression *e = new CallExp(loc, new VarExp(loc, fdenter), new VarExp(loc, tmp)); @@ -4371,7 +4234,7 @@ Statement *SynchronizedStatement::semantic(Scope *sc) cs->push(new ExpStatement(loc, v)); Parameters* args = new Parameters; - args->push(new Parameter(STCin, t->pointerTo(), NULL, NULL)); + args->push(new Parameter(0, t->pointerTo(), NULL, NULL)); FuncDeclaration *fdenter = FuncDeclaration::genCfunc(args, Type::tvoid, Id::criticalenter); Expression *e = new DotIdExp(loc, new VarExp(loc, tmp), Id::ptr); @@ -4410,27 +4273,6 @@ bool SynchronizedStatement::hasContinue() return false; //true; } -int SynchronizedStatement::blockExit(bool mustNotThrow) -{ - return body ? body->blockExit(mustNotThrow) : BEfallthru; -} - - -void SynchronizedStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("synchronized"); - if (exp) - { buf->writebyte('('); - exp->toCBuffer(buf, hgs); - buf->writebyte(')'); - } - if (body) - { - buf->writebyte(' '); - body->toCBuffer(buf, hgs); - } -} - /******************************** WithStatement ***************************/ WithStatement::WithStatement(Loc loc, Expression *exp, Statement *body) @@ -4454,6 +4296,8 @@ Statement *WithStatement::semantic(Scope *sc) //printf("WithStatement::semantic()\n"); exp = exp->semantic(sc); exp = resolveProperties(sc, exp); + // exp = exp_>optimize(0); //? + exp = checkGC(sc, exp); if (exp->op == TOKerror) return new ErrorStatement(); if (exp->op == TOKimport) @@ -4474,8 +4318,7 @@ Statement *WithStatement::semantic(Scope *sc) } else { - Type *t = exp->type; - t = t->toBasetype(); + Type *t = exp->type->toBasetype(); Expression *olde = exp; if (t->ty == Tpointer) @@ -4506,7 +4349,7 @@ Statement *WithStatement::semantic(Scope *sc) exp = new CommaExp(loc, new DeclarationExp(loc, wthis), new VarExp(loc, wthis)); exp = exp->semantic(sc); } - Expression *e = exp->addressOf(sc); + Expression *e = exp->addressOf(); init = new ExpInitializer(loc, e); wthis = new VarDeclaration(loc, e->type, Id::withSym, init); wthis->semantic(sc); @@ -4536,29 +4379,6 @@ Statement *WithStatement::semantic(Scope *sc) return this; } -void WithStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("with ("); - exp->toCBuffer(buf, hgs); - buf->writestring(")"); - buf->writenl(); - if (body) - body->toCBuffer(buf, hgs); -} - -int WithStatement::blockExit(bool mustNotThrow) -{ - int result = BEnone; - if (exp->canThrow(mustNotThrow)) - result = BEthrow; - if (body) - result |= body->blockExit(mustNotThrow); - else - result |= BEfallthru; - return result; -} - - /******************************** TryCatchStatement ***************************/ TryCatchStatement::TryCatchStatement(Loc loc, Statement *body, Catches *catches) @@ -4584,7 +4404,7 @@ Statement *TryCatchStatement::syntaxCopy() Statement *TryCatchStatement::semantic(Scope *sc) { - body = body->semanticScope(sc, NULL /*this*/, NULL); + body = body->semanticScope(sc, NULL, NULL); assert(body); /* Even if body is empty, still do semantic analysis on catches @@ -4621,7 +4441,7 @@ Statement *TryCatchStatement::semantic(Scope *sc) * of recoverable exceptions. */ - if (!(body->blockExit(false) & BEthrow) && ClassDeclaration::exception) + if (!(body->blockExit(sc->func, false) & BEthrow) && ClassDeclaration::exception) { for (size_t i = 0; i < catches->dim; i++) { Catch *c = (*catches)[i]; @@ -4648,53 +4468,6 @@ bool TryCatchStatement::hasBreak() return false; } -int TryCatchStatement::blockExit(bool mustNotThrow) -{ - assert(body); - int result = body->blockExit(false); - - int catchresult = 0; - for (size_t i = 0; i < catches->dim; i++) - { - Catch *c = (*catches)[i]; - if (c->type == Type::terror) - continue; - - catchresult |= c->blockExit(mustNotThrow); - - /* If we're catching Object, then there is no throwing - */ - Identifier *id = c->type->toBasetype()->isClassHandle()->ident; - if (id == Id::Object || id == Id::Throwable) - { - result &= ~(BEthrow | BEerrthrow); - } - if (id == Id::Exception) - { - result &= ~BEthrow; - } - } - if (mustNotThrow && (result & BEthrow)) - { - body->blockExit(mustNotThrow); // now explain why this is nothrow - } - return result | catchresult; -} - - -void TryCatchStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("try"); - buf->writenl(); - if (body) - body->toCBuffer(buf, hgs); - for (size_t i = 0; i < catches->dim; i++) - { - Catch *c = (*catches)[i]; - c->toCBuffer(buf, hgs); - } -} - /******************************** Catch ***************************/ Catch::Catch(Loc loc, Type *t, Identifier *id, Statement *handler) @@ -4723,6 +4496,11 @@ void Catch::semantic(Scope *sc) //printf("Catch::semantic(%s)\n", ident->toChars()); #ifndef IN_GCC + if (sc->os && sc->os->tok != TOKon_scope_failure) + { + // If enclosing is scope(success) or scope(exit), this will be placed in finally block. + error(loc, "cannot put catch statement inside %s", Token::toChars(sc->os->tok)); + } if (sc->tf) { /* This is because the _d_local_unwind() gets the stack munged @@ -4740,13 +4518,20 @@ void Catch::semantic(Scope *sc) sc = sc->push(sym); if (!type) - type = new TypeIdentifier(Loc(), Id::Throwable); + { + // reference .object.Throwable + TypeIdentifier *tid = new TypeIdentifier(Loc(), Id::empty); + tid->addIdent(Id::object); + tid->addIdent(Id::Throwable); + type = tid; + } type = type->semantic(loc, sc); ClassDeclaration *cd = type->toBasetype()->isClassHandle(); if (!cd || ((cd != ClassDeclaration::throwable) && !ClassDeclaration::throwable->isBaseOf(cd, NULL))) { if (type != Type::terror) - { error(loc, "can only catch class objects derived from Throwable, not '%s'", type->toChars()); + { + error(loc, "can only catch class objects derived from Throwable, not '%s'", type->toChars()); type = Type::terror; } } @@ -4771,30 +4556,6 @@ void Catch::semantic(Scope *sc) sc->pop(); } -int Catch::blockExit(bool mustNotThrow) -{ - return handler ? handler->blockExit(mustNotThrow) : BEfallthru; -} - -void Catch::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("catch"); - if (type) - { buf->writebyte('('); - type->toCBuffer(buf, ident, hgs); - buf->writebyte(')'); - } - buf->writenl(); - buf->writebyte('{'); - buf->writenl(); - buf->level++; - if (handler) - handler->toCBuffer(buf, hgs); - buf->level--; - buf->writebyte('}'); - buf->writenl(); -} - /****************************** TryFinallyStatement ***************************/ TryFinallyStatement::TryFinallyStatement(Loc loc, Statement *body, Statement *finalbody) @@ -4830,35 +4591,14 @@ Statement *TryFinallyStatement::semantic(Scope *sc) return finalbody; if (!finalbody) return body; - if (body->blockExit(false) == BEfallthru) - { Statement *s = new CompoundStatement(loc, body, finalbody); + if (body->blockExit(sc->func, false) == BEfallthru) + { + Statement *s = new CompoundStatement(loc, body, finalbody); return s; } return this; } -void TryFinallyStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("try"); - buf->writenl(); - buf->writebyte('{'); - buf->writenl(); - buf->level++; - body->toCBuffer(buf, hgs); - buf->level--; - buf->writebyte('}'); - buf->writenl(); - buf->writestring("finally"); - buf->writenl(); - buf->writebyte('{'); - buf->writenl(); - buf->level++; - finalbody->toCBuffer(buf, hgs); - buf->level--; - buf->writeByte('}'); - buf->writenl(); -} - bool TryFinallyStatement::hasBreak() { return false; //true; @@ -4869,23 +4609,6 @@ bool TryFinallyStatement::hasContinue() return false; //true; } -int TryFinallyStatement::blockExit(bool mustNotThrow) -{ - int result = BEfallthru; - if (body) - result = body->blockExit(mustNotThrow); - // check finally body as well, it may throw (bug #4082) - if (finalbody) - { - int finalresult = finalbody->blockExit(mustNotThrow); - if (!(finalresult & BEfallthru)) - result &= ~BEfallthru; - result |= finalresult & ~BEfallthru; - } - return result; -} - - /****************************** OnScopeStatement ***************************/ OnScopeStatement::OnScopeStatement(Loc loc, TOK tok, Statement *statement) @@ -4904,20 +4627,41 @@ Statement *OnScopeStatement::syntaxCopy() Statement *OnScopeStatement::semantic(Scope *sc) { - /* semantic is called on results of scopeCode() */ - return this; -} +#ifndef IN_GCC + if (tok != TOKon_scope_exit) + { + // scope(success) and scope(failure) are rewritten to try-catch(-finally) statement, + // so the generated catch block cannot be placed in finally block. + // See also Catch::semantic. + if (sc->os && sc->os->tok != TOKon_scope_failure) + { + // If enclosing is scope(success) or scope(exit), this will be placed in finally block. + error("cannot put %s statement inside %s", Token::toChars(tok), Token::toChars(sc->os->tok)); + return new ErrorStatement(); + } + if (sc->tf) + { + error("cannot put %s statement inside finally block", Token::toChars(tok)); + return new ErrorStatement(); + } + } +#endif -int OnScopeStatement::blockExit(bool mustNotThrow) -{ // At this point, this statement is just an empty placeholder - return BEfallthru; -} + sc = sc->push(); + sc->tf = NULL; + sc->os = this; + if (tok != TOKon_scope_failure) + { + // Jump out from scope(failure) block is allowed. + sc->sbreak = NULL; + sc->scontinue = NULL; + } + statement = statement->semanticNoScope(sc); + sc->pop(); -void OnScopeStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring(Token::toChars(tok)); - buf->writebyte(' '); - statement->toCBuffer(buf, hgs); + if (!statement || statement->isErrorStatement()) + return statement; + return this; } Statement *OnScopeStatement::scopeCode(Scope *sc, Statement **sentry, Statement **sexception, Statement **sfinally) @@ -4927,14 +4671,17 @@ Statement *OnScopeStatement::scopeCode(Scope *sc, Statement **sentry, Statement *sentry = NULL; *sexception = NULL; *sfinally = NULL; + + Statement *s = new PeelStatement(statement); + switch (tok) { case TOKon_scope_exit: - *sfinally = statement; + *sfinally = s; break; case TOKon_scope_failure: - *sexception = statement; + *sexception = s; break; case TOKon_scope_success: @@ -4957,7 +4704,7 @@ Statement *OnScopeStatement::scopeCode(Scope *sc, Statement **sentry, Statement e = new VarExp(Loc(), v); e = new NotExp(Loc(), e); - *sfinally = new IfStatement(Loc(), NULL, e, statement, NULL); + *sfinally = new IfStatement(Loc(), NULL, e, s, NULL); break; } @@ -4993,6 +4740,7 @@ Statement *ThrowStatement::semantic(Scope *sc) exp = exp->semantic(sc); exp = resolveProperties(sc, exp); + exp = checkGC(sc, exp); if (exp->op == TOKerror) return new ErrorStatement(); ClassDeclaration *cd = exp->type->toBasetype()->isClassHandle(); @@ -5005,34 +4753,6 @@ Statement *ThrowStatement::semantic(Scope *sc) return this; } -int ThrowStatement::blockExit(bool mustNotThrow) -{ - Type *t = exp->type->toBasetype(); - ClassDeclaration *cd = t->isClassHandle(); - assert(cd); - - if (cd == ClassDeclaration::errorException || - ClassDeclaration::errorException->isBaseOf(cd, NULL)) - { - return BEerrthrow; - } - // Bugzilla 8675 - // Throwing Errors is allowed even if mustNotThrow - if (!internalThrow && mustNotThrow) - error("%s is thrown but not caught", exp->type->toChars()); - - return BEthrow; -} - - -void ThrowStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->printf("throw "); - exp->toCBuffer(buf, hgs); - buf->writeByte(';'); - buf->writenl(); -} - /******************************** DebugStatement **************************/ DebugStatement::DebugStatement(Loc loc, Statement *statement) @@ -5076,15 +4796,6 @@ Statements *DebugStatement::flatten(Scope *sc) return a; } -void DebugStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - if (statement) - { - statement->toCBuffer(buf, hgs); - } -} - - /******************************** GotoStatement ***************************/ GotoStatement::GotoStatement(Loc loc, Identifier *ident) @@ -5093,6 +4804,7 @@ GotoStatement::GotoStatement(Loc loc, Identifier *ident) this->ident = ident; this->label = NULL; this->tf = NULL; + this->os = NULL; this->lastVar = NULL; this->fd = NULL; } @@ -5112,6 +4824,7 @@ Statement *GotoStatement::semantic(Scope *sc) this->lastVar = sc->lastVar; this->fd = sc->func; tf = sc->tf; + os = sc->os; label = fd->searchLabel(ident); if (!label->statement && sc->fes) { @@ -5147,6 +4860,22 @@ bool GotoStatement::checkLabel() return true; } + if (label->statement->os != os) + { + if (os && os->tok == TOKon_scope_failure && !label->statement->os) + { + // Jump out from scope(failure) block is allowed. + } + else + { + if (label->statement->os) + error("cannot goto in to %s block", Token::toChars(label->statement->os->tok)); + else + error("cannot goto out of %s block", Token::toChars(os->tok)); + return true; + } + } + if (label->statement->tf != tf) { error("cannot goto in or out of finally block"); @@ -5169,7 +4898,7 @@ bool GotoStatement::checkLabel() error("goto skips declaration of with temporary at %s", vd->loc.toChars()); return true; } - else + else if (!(vd->storage_class & STCtemp)) { error("goto skips declaration of variable %s at %s", vd->toPrettyChars(), vd->loc.toChars()); return true; @@ -5178,21 +4907,6 @@ bool GotoStatement::checkLabel() return false; } -int GotoStatement::blockExit(bool mustNotThrow) -{ - //printf("GotoStatement::blockExit(%p)\n", this); - return BEgoto; -} - - -void GotoStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring("goto "); - buf->writestring(ident->toChars()); - buf->writebyte(';'); - buf->writenl(); -} - /******************************** LabelStatement ***************************/ LabelStatement::LabelStatement(Loc loc, Identifier *ident, Statement *statement) @@ -5201,6 +4915,7 @@ LabelStatement::LabelStatement(Loc loc, Identifier *ident, Statement *statement) this->ident = ident; this->statement = statement; this->tf = NULL; + this->os = NULL; this->gotoTarget = NULL; this->lastVar = NULL; this->lblock = NULL; @@ -5209,7 +4924,7 @@ LabelStatement::LabelStatement(Loc loc, Identifier *ident, Statement *statement) Statement *LabelStatement::syntaxCopy() { - LabelStatement *s = new LabelStatement(loc, ident, statement->syntaxCopy()); + LabelStatement *s = new LabelStatement(loc, ident, statement ? statement->syntaxCopy() : NULL); return s; } @@ -5230,6 +4945,7 @@ Statement *LabelStatement::semantic(Scope *sc) else ls->statement = this; tf = sc->tf; + os = sc->os; sc = sc->push(); sc->scopesym = sc->enclosing->scopesym; sc->callSuper |= CSXlabel; @@ -5241,11 +4957,25 @@ Statement *LabelStatement::semantic(Scope *sc) } sc->slabel = this; if (statement) - statement = statement->semanticNoScope(sc); + statement = statement->semantic(sc); sc->pop(); return this; } +Statement *LabelStatement::scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally) +{ + //printf("LabelStatement::scopeCode()\n"); + if (statement) + statement = statement->scopeCode(sc, sentry, sexit, sfinally); + else + { + *sentry = NULL; + *sexit = NULL; + *sfinally = NULL; + } + return this; +} + Statements *LabelStatement::flatten(Scope *sc) { Statements *a = NULL; @@ -5269,24 +4999,6 @@ Statements *LabelStatement::flatten(Scope *sc) return a; } - -int LabelStatement::blockExit(bool mustNotThrow) -{ - //printf("LabelStatement::blockExit(%p)\n", this); - return statement ? statement->blockExit(mustNotThrow) : BEfallthru; -} - - -void LabelStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - buf->writestring(ident->toChars()); - buf->writebyte(':'); - buf->writenl(); - if (statement) - statement->toCBuffer(buf, hgs); -} - - /******************************** LabelDsymbol ***************************/ LabelDsymbol::LabelDsymbol(Identifier *ident) @@ -5325,42 +5037,143 @@ Statement *AsmStatement::syntaxCopy() } -int AsmStatement::blockExit(bool mustNotThrow) +#ifdef IN_GCC +/************************ ExtAsmStatement ***************************************/ + +ExtAsmStatement::ExtAsmStatement(Loc loc, Expression *insn, + Expressions *args, Identifiers *names, + Expressions *constraints, int outputargs, + Expressions *clobbers, Identifiers *labels) + : Statement(loc) +{ + this->insn = insn; + this->args = args; + this->names = names; + this->constraints = constraints; + this->outputargs = outputargs; + this->clobbers = clobbers; + this->labels = labels; + this->gotos = NULL; +} + +Statement *ExtAsmStatement::syntaxCopy() { - if (mustNotThrow) - error("asm statements are assumed to throw", toChars()); - // Assume the worst - return BEfallthru | BEthrow | BEreturn | BEgoto | BEhalt; + Expressions *c_args = Expression::arraySyntaxCopy(args); + Expressions *c_constraints = Expression::arraySyntaxCopy(constraints); + Expressions *c_clobbers = Expression::arraySyntaxCopy(clobbers); + + return new ExtAsmStatement(loc, insn->syntaxCopy(), c_args, names, + c_constraints, outputargs, c_clobbers, labels); } -void AsmStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +Statement *ExtAsmStatement::semantic(Scope *sc) { - buf->writestring("asm { "); - Token *t = tokens; - buf->level++; - while (t) + // Fold the instruction template string. + insn = insn->semantic(sc); + insn->optimize(WANTvalue); + + if (insn->op != TOKstring || ((StringExp *) insn)->sz != 1) + error("instruction template must be a constant char string"); + + // Analyse all input and output operands. + if (args) + { + for (size_t i = 0; i < args->dim; i++) + { + Expression *e = (*args)[i]; + e = e->semantic(sc); + if (i < outputargs) + e = e->modifiableLvalue(sc, NULL); + else + e = e->optimize(WANTvalue); + (*args)[i] = e; + + e = (*constraints)[i]; + e = e->semantic(sc); + e = e->optimize(WANTvalue); + if (e->op != TOKstring || ((StringExp *) e)->sz != 1) + error ("constraint must be a constant char string"); + (*constraints)[i] = e; + } + } + + // Fold all clobbers. + if (clobbers) + { + for (size_t i = 0; i < clobbers->dim; i++) + { + Expression *e = (*clobbers)[i]; + e = e->semantic(sc); + e = e->optimize(WANTvalue); + if (e->op != TOKstring || ((StringExp *) e)->sz != 1) + error("clobber specification must be a constant char string"); + (*clobbers)[i] = e; + } + } + + // Analyse all goto labels. + if (labels) { - buf->writestring(t->toChars()); - if (t->next && - t->value != TOKmin && - t->value != TOKcomma && - t->next->value != TOKcomma && - t->value != TOKlbracket && - t->next->value != TOKlbracket && - t->next->value != TOKrbracket && - t->value != TOKlparen && - t->next->value != TOKlparen && - t->next->value != TOKrparen && - t->value != TOKdot && - t->next->value != TOKdot) + for (size_t i = 0; i < labels->dim; i++) { - buf->writebyte(' '); + Identifier *ident = (*labels)[i]; + GotoStatement *s = new GotoStatement(loc, ident); + if (!gotos) + gotos = new GotoStatements(); + gotos->push(s); + s->semantic(sc); } - t = t->next; } - buf->level--; - buf->writestring("; }"); - buf->writenl(); + + return this; +} + +#endif + +/************************ CompoundAsmStatement ***************************************/ + +CompoundAsmStatement::CompoundAsmStatement(Loc loc, Statements *s, StorageClass stc) + : CompoundStatement(loc, s) +{ + this->stc = stc; +} + +CompoundAsmStatement *CompoundAsmStatement::syntaxCopy() +{ + Statements *a = new Statements(); + a->setDim(statements->dim); + for (size_t i = 0; i < statements->dim; i++) + { + Statement *s = (*statements)[i]; + (*a)[i] = s ? s->syntaxCopy() : NULL; + } + return new CompoundAsmStatement(loc, a, stc); +} + +Statements *CompoundAsmStatement::flatten(Scope *sc) +{ + return NULL; +} + +CompoundAsmStatement *CompoundAsmStatement::semantic(Scope *sc) +{ + for (size_t i = 0; i < statements->dim; i++) + { + Statement *s = (*statements)[i]; + (*statements)[i] = s ? s->semantic(sc) : NULL; + } + + assert(sc->func); + // use setImpure/setGC when the deprecation cycle is over + PURE pure; + if (!(stc & STCpure) && (pure = sc->func->isPureBypassingInference()) != PUREimpure && pure != PUREfwdref) + deprecation("asm statement is assumed to be impure - mark it with 'pure' if it is not"); + if (!(stc & STCnogc) && sc->func->isNogcBypassingInference()) + deprecation("asm statement is assumed to use the GC - mark it with '@nogc' if it does not"); + if (!(stc & (STCtrusted|STCsafe)) && sc->func->setUnsafe()) + error("asm statement is assumed to be @system - mark it with '@trusted' if it is not"); + + return this; } /************************ ImportStatement ***************************************/ @@ -5388,7 +5201,7 @@ Statement *ImportStatement::semantic(Scope *sc) for (size_t i = 0; i < imports->dim; i++) { Import *s = (*imports)[i]->isImport(); - + assert(!s->aliasdecls.dim); for (size_t j = 0; j < s->names.dim; j++) { Identifier *name = s->names[j]; @@ -5415,17 +5228,3 @@ Statement *ImportStatement::semantic(Scope *sc) } return this; } - -int ImportStatement::blockExit(bool mustNotThrow) -{ - return BEfallthru; -} - -void ImportStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - for (size_t i = 0; i < imports->dim; i++) - { - Dsymbol *s = (*imports)[i]; - s->toCBuffer(buf, hgs); - } -} diff --git a/gcc/d/dfrontend/statement.h b/gcc/d/dfrontend/statement.h index e5ab08c5c..727b20a98 100644 --- a/gcc/d/dfrontend/statement.h +++ b/gcc/d/dfrontend/statement.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/statement.h + */ #ifndef DMD_STATEMENT_H #define DMD_STATEMENT_H @@ -34,9 +35,6 @@ class VarDeclaration; class Condition; class Module; struct Token; -struct InlineCostState; -struct InlineDoState; -struct InlineScanState; class ErrorStatement; class ReturnStatement; class CompoundStatement; @@ -56,20 +54,18 @@ struct CompiledCtfeFunction; enum TOK; -typedef bool (*sapply_fp_t)(Statement *, void *); - // Back end -struct IRState; -struct Blockx; #ifdef IN_GCC typedef union tree_node block; -typedef union tree_node elem; #else struct block; -struct elem; #endif struct code; +Expression *interpret(Statement *s, InterState *istate); +bool inferAggregate(ForeachStatement *fes, Scope *sc, Dsymbol *&sapply); +bool inferApplyArgTypes(ForeachStatement *fes, Scope *sc, Dsymbol *&sapply); + /* How a statement exits; this is returned by blockExit() */ enum BE @@ -86,6 +82,8 @@ enum BE BEany = (BEfallthru | BEthrow | BEreturn | BEgoto | BEhalt), }; +void toCBuffer(Statement *s, OutBuffer *buf, HdrGenState *hgs); + class Statement : public RootObject { public: @@ -100,7 +98,6 @@ class Statement : public RootObject void error(const char *format, ...); void warning(const char *format, ...); void deprecation(const char *format, ...); - virtual void toCBuffer(OutBuffer *buf, HdrGenState *hgs); virtual Statement *semantic(Scope *sc); Statement *semanticScope(Scope *sc, Statement *sbreak, Statement *scontinue); Statement *semanticNoScope(Scope *sc); @@ -108,24 +105,17 @@ class Statement : public RootObject virtual bool hasBreak(); virtual bool hasContinue(); bool usesEH(); - virtual bool usesEHimpl(); - virtual int blockExit(bool mustNotThrow); + int blockExit(FuncDeclaration *func, bool mustNotThrow); bool comeFrom(); - virtual bool comeFromImpl(); bool hasCode(); - virtual bool hasCodeImpl(); virtual Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally); virtual Statements *flatten(Scope *sc); - virtual Expression *interpret(InterState *istate); - virtual bool apply(sapply_fp_t fp, void *param); - virtual void ctfeCompile(CompiledCtfeFunction *ccf); + Expression *interpret(InterState *istate) + { + return ::interpret(this, istate); + } virtual Statement *last(); - virtual int inlineCost(InlineCostState *ics); - virtual Expression *doInline(InlineDoState *ids); - virtual Statement *doInlineStatement(InlineDoState *ids); - virtual Statement *inlineScan(InlineScanState *iss); - // Avoid dynamic_cast virtual ErrorStatement *isErrorStatement() { return NULL; } virtual ScopeStatement *isScopeStatement() { return NULL; } @@ -136,6 +126,7 @@ class Statement : public RootObject virtual CaseStatement *isCaseStatement() { return NULL; } virtual DefaultStatement *isDefaultStatement() { return NULL; } virtual LabelStatement *isLabelStatement() { return NULL; } + virtual DtorExpStatement *isDtorExpStatement() { return NULL; } virtual void accept(Visitor *v) { v->visit(this); } }; @@ -148,7 +139,6 @@ class ErrorStatement : public Statement ErrorStatement(); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - int blockExit(bool mustNotThrow); ErrorStatement *isErrorStatement() { return this; } void accept(Visitor *v) { v->visit(this); } @@ -161,7 +151,6 @@ class PeelStatement : public Statement PeelStatement(Statement *s); Statement *semantic(Scope *sc); - bool apply(sapply_fp_t fp, void *param); void accept(Visitor *v) { v->visit(this); } }; @@ -174,19 +163,9 @@ class ExpStatement : public Statement ExpStatement(Loc loc, Dsymbol *s); static ExpStatement *create(Loc loc, Expression *exp); Statement *syntaxCopy(); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); Statement *semantic(Scope *sc); - Expression *interpret(InterState *istate); - void ctfeCompile(CompiledCtfeFunction *ccf); - int blockExit(bool mustNotThrow); - bool hasCodeImpl(); Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally); - int inlineCost(InlineCostState *ics); - Expression *doInline(InlineDoState *ids); - Statement *doInlineStatement(InlineDoState *ids); - Statement *inlineScan(InlineScanState *iss); - ExpStatement *isExpStatement() { return this; } void accept(Visitor *v) { v->visit(this); } }; @@ -202,6 +181,8 @@ class DtorExpStatement : public ExpStatement DtorExpStatement(Loc loc, Expression *exp, VarDeclaration *v); Statement *syntaxCopy(); void accept(Visitor *v) { v->visit(this); } + + DtorExpStatement *isDtorExpStatement() { return this; } }; class CompileStatement : public Statement @@ -211,10 +192,8 @@ class CompileStatement : public Statement CompileStatement(Loc loc, Expression *exp); Statement *syntaxCopy(); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); Statements *flatten(Scope *sc); Statement *semantic(Scope *sc); - int blockExit(bool mustNotThrow); void accept(Visitor *v) { v->visit(this); } }; @@ -228,22 +207,11 @@ class CompoundStatement : public Statement CompoundStatement(Loc loc, Statement *s1, Statement *s2); static CompoundStatement *create(Loc loc, Statement *s1, Statement *s2); Statement *syntaxCopy(); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); Statement *semantic(Scope *sc); - int blockExit(bool mustNotThrow); - bool hasCodeImpl(); Statements *flatten(Scope *sc); ReturnStatement *isReturnStatement(); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); Statement *last(); - int inlineCost(InlineCostState *ics); - Expression *doInline(InlineDoState *ids); - Statement *doInlineStatement(InlineDoState *ids); - Statement *inlineScan(InlineScanState *iss); - CompoundStatement *isCompoundStatement() { return this; } void accept(Visitor *v) { v->visit(this); } }; @@ -253,7 +221,6 @@ class CompoundDeclarationStatement : public CompoundStatement public: CompoundDeclarationStatement(Loc loc, Statements *s); Statement *syntaxCopy(); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void accept(Visitor *v) { v->visit(this); } }; @@ -270,16 +237,6 @@ class UnrolledLoopStatement : public Statement Statement *semantic(Scope *sc); bool hasBreak(); bool hasContinue(); - int blockExit(bool mustNotThrow); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - - int inlineCost(InlineCostState *ics); - Expression *doInline(InlineDoState *ids); - Statement *doInlineStatement(InlineDoState *ids); - Statement *inlineScan(InlineScanState *iss); void accept(Visitor *v) { v->visit(this); } }; @@ -291,22 +248,11 @@ class ScopeStatement : public Statement ScopeStatement(Loc loc, Statement *s); Statement *syntaxCopy(); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); ScopeStatement *isScopeStatement() { return this; } ReturnStatement *isReturnStatement(); Statement *semantic(Scope *sc); bool hasBreak(); bool hasContinue(); - int blockExit(bool mustNotThrow); - bool hasCodeImpl(); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); - - int inlineCost(InlineCostState *ics); - Expression *doInline(InlineDoState *ids); - Statement *doInlineStatement(InlineDoState *ids); - Statement *inlineScan(InlineScanState *iss); void accept(Visitor *v) { v->visit(this); } }; @@ -322,13 +268,6 @@ class WhileStatement : public Statement Statement *semantic(Scope *sc); bool hasBreak(); bool hasContinue(); - int blockExit(bool mustNotThrow); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - - Statement *inlineScan(InlineScanState *iss); void accept(Visitor *v) { v->visit(this); } }; @@ -344,13 +283,6 @@ class DoStatement : public Statement Statement *semantic(Scope *sc); bool hasBreak(); bool hasContinue(); - int blockExit(bool mustNotThrow); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - - Statement *inlineScan(InlineScanState *iss); void accept(Visitor *v) { v->visit(this); } }; @@ -375,15 +307,6 @@ class ForStatement : public Statement Statement *getRelatedLabeled() { return relatedLabeled ? relatedLabeled : this; } bool hasBreak(); bool hasContinue(); - int blockExit(bool mustNotThrow); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - - int inlineCost(InlineCostState *ics); - Statement *inlineScan(InlineScanState *iss); - Statement *doInlineStatement(InlineDoState *ids); void accept(Visitor *v) { v->visit(this); } }; @@ -408,17 +331,8 @@ class ForeachStatement : public Statement Statement *syntaxCopy(); Statement *semantic(Scope *sc); bool checkForArgTypes(); - int inferAggregate(Scope *sc, Dsymbol *&sapply); - int inferApplyArgTypes(Scope *sc, Dsymbol *&sapply); bool hasBreak(); bool hasContinue(); - int blockExit(bool mustNotThrow); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - - Statement *inlineScan(InlineScanState *iss); void accept(Visitor *v) { v->visit(this); } }; @@ -440,13 +354,6 @@ class ForeachRangeStatement : public Statement Statement *semantic(Scope *sc); bool hasBreak(); bool hasContinue(); - int blockExit(bool mustNotThrow); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - - Statement *inlineScan(InlineScanState *iss); void accept(Visitor *v) { v->visit(this); } }; @@ -464,18 +371,8 @@ class IfStatement : public Statement IfStatement(Loc loc, Parameter *arg, Expression *condition, Statement *ifbody, Statement *elsebody); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - int blockExit(bool mustNotThrow); IfStatement *isIfStatement() { return this; } - int inlineCost(InlineCostState *ics); - Expression *doInline(InlineDoState *ids); - Statement *doInlineStatement(InlineDoState *ids); - Statement *inlineScan(InlineScanState *iss); - void accept(Visitor *v) { v->visit(this); } }; @@ -490,10 +387,7 @@ class ConditionalStatement : public Statement Statement *syntaxCopy(); Statement *semantic(Scope *sc); Statements *flatten(Scope *sc); - int blockExit(bool mustNotThrow); - bool apply(sapply_fp_t fp, void *param); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void accept(Visitor *v) { v->visit(this); } }; @@ -507,10 +401,6 @@ class PragmaStatement : public Statement PragmaStatement(Loc loc, Identifier *ident, Expressions *args, Statement *body); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - int blockExit(bool mustNotThrow); - bool apply(sapply_fp_t fp, void *param); - - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void accept(Visitor *v) { v->visit(this); } }; @@ -523,9 +413,7 @@ class StaticAssertStatement : public Statement StaticAssertStatement(StaticAssert *sa); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - int blockExit(bool mustNotThrow); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void accept(Visitor *v) { v->visit(this); } }; @@ -547,13 +435,6 @@ class SwitchStatement : public Statement Statement *syntaxCopy(); Statement *semantic(Scope *sc); bool hasBreak(); - int blockExit(bool mustNotThrow); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - - Statement *inlineScan(InlineScanState *iss); void accept(Visitor *v) { v->visit(this); } }; @@ -571,16 +452,8 @@ class CaseStatement : public Statement Statement *syntaxCopy(); Statement *semantic(Scope *sc); int compare(RootObject *obj); - int blockExit(bool mustNotThrow); - bool comeFromImpl(); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); CaseStatement *isCaseStatement() { return this; } - Statement *inlineScan(InlineScanState *iss); - void accept(Visitor *v) { v->visit(this); } }; @@ -595,8 +468,6 @@ class CaseRangeStatement : public Statement CaseRangeStatement(Loc loc, Expression *first, Expression *last, Statement *s); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - bool apply(sapply_fp_t fp, void *param); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void accept(Visitor *v) { v->visit(this); } }; @@ -612,16 +483,8 @@ class DefaultStatement : public Statement DefaultStatement(Loc loc, Statement *s); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - int blockExit(bool mustNotThrow); - bool comeFromImpl(); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); DefaultStatement *isDefaultStatement() { return this; } - Statement *inlineScan(InlineScanState *iss); - void accept(Visitor *v) { v->visit(this); } }; @@ -633,10 +496,6 @@ class GotoDefaultStatement : public Statement GotoDefaultStatement(Loc loc); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - Expression *interpret(InterState *istate); - void ctfeCompile(CompiledCtfeFunction *ccf); - int blockExit(bool mustNotThrow); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void accept(Visitor *v) { v->visit(this); } }; @@ -650,10 +509,6 @@ class GotoCaseStatement : public Statement GotoCaseStatement(Loc loc, Expression *exp); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - Expression *interpret(InterState *istate); - void ctfeCompile(CompiledCtfeFunction *ccf); - int blockExit(bool mustNotThrow); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void accept(Visitor *v) { v->visit(this); } }; @@ -662,9 +517,6 @@ class SwitchErrorStatement : public Statement { public: SwitchErrorStatement(Loc loc); - int blockExit(bool mustNotThrow); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - void ctfeCompile(CompiledCtfeFunction *ccf); void accept(Visitor *v) { v->visit(this); } }; @@ -677,16 +529,7 @@ class ReturnStatement : public Statement ReturnStatement(Loc loc, Expression *exp); Statement *syntaxCopy(); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); Statement *semantic(Scope *sc); - int blockExit(bool mustNotThrow); - Expression *interpret(InterState *istate); - void ctfeCompile(CompiledCtfeFunction *ccf); - - int inlineCost(InlineCostState *ics); - Expression *doInline(InlineDoState *ids); - Statement *doInlineStatement(InlineDoState *ids); - Statement *inlineScan(InlineScanState *iss); ReturnStatement *isReturnStatement() { return this; } void accept(Visitor *v) { v->visit(this); } @@ -700,10 +543,6 @@ class BreakStatement : public Statement BreakStatement(Loc loc, Identifier *ident); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - Expression *interpret(InterState *istate); - void ctfeCompile(CompiledCtfeFunction *ccf); - int blockExit(bool mustNotThrow); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void accept(Visitor *v) { v->visit(this); } }; @@ -716,10 +555,6 @@ class ContinueStatement : public Statement ContinueStatement(Loc loc, Identifier *ident); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - Expression *interpret(InterState *istate); - void ctfeCompile(CompiledCtfeFunction *ccf); - int blockExit(bool mustNotThrow); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void accept(Visitor *v) { v->visit(this); } }; @@ -735,16 +570,7 @@ class SynchronizedStatement : public Statement Statement *semantic(Scope *sc); bool hasBreak(); bool hasContinue(); - bool usesEHimpl(); - int blockExit(bool mustNotThrow); - bool apply(sapply_fp_t fp, void *param); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - - Statement *inlineScan(InlineScanState *iss); -// Back end - elem *esync; - SynchronizedStatement(Loc loc, elem *esync, Statement *body); void accept(Visitor *v) { v->visit(this); } }; @@ -758,13 +584,6 @@ class WithStatement : public Statement WithStatement(Loc loc, Expression *exp, Statement *body); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - int blockExit(bool mustNotThrow); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); - - Statement *inlineScan(InlineScanState *iss); void accept(Visitor *v) { v->visit(this); } }; @@ -779,15 +598,7 @@ class TryCatchStatement : public Statement Statement *syntaxCopy(); Statement *semantic(Scope *sc); bool hasBreak(); - bool usesEHimpl(); - int blockExit(bool mustNotThrow); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); - - Statement *inlineScan(InlineScanState *iss); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void accept(Visitor *v) { v->visit(this); } }; @@ -799,14 +610,13 @@ class Catch : public RootObject Identifier *ident; VarDeclaration *var; Statement *handler; - bool internalCatch; // was generated by the compiler, - // wasn't present in source code + // was generated by the compiler, + // wasn't present in source code + bool internalCatch; Catch(Loc loc, Type *t, Identifier *id, Statement *handler); Catch *syntaxCopy(); void semantic(Scope *sc); - int blockExit(bool mustNotThrow); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); }; class TryFinallyStatement : public Statement @@ -818,17 +628,9 @@ class TryFinallyStatement : public Statement TryFinallyStatement(Loc loc, Statement *body, Statement *finalbody); static TryFinallyStatement *create(Loc loc, Statement *body, Statement *finalbody); Statement *syntaxCopy(); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); Statement *semantic(Scope *sc); bool hasBreak(); bool hasContinue(); - bool usesEHimpl(); - int blockExit(bool mustNotThrow); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); - - Statement *inlineScan(InlineScanState *iss); void accept(Visitor *v) { v->visit(this); } }; @@ -841,14 +643,8 @@ class OnScopeStatement : public Statement OnScopeStatement(Loc loc, TOK tok, Statement *statement); Statement *syntaxCopy(); - int blockExit(bool mustNotThrow); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); Statement *semantic(Scope *sc); - bool usesEHimpl(); Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); void accept(Visitor *v) { v->visit(this); } }; @@ -857,18 +653,13 @@ class ThrowStatement : public Statement { public: Expression *exp; - bool internalThrow; // was generated by the compiler, - // wasn't present in source code + // was generated by the compiler, + // wasn't present in source code + bool internalThrow; ThrowStatement(Loc loc, Expression *exp); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - int blockExit(bool mustNotThrow); - Expression *interpret(InterState *istate); - void ctfeCompile(CompiledCtfeFunction *ccf); - - Statement *inlineScan(InlineScanState *iss); void accept(Visitor *v) { v->visit(this); } }; @@ -882,8 +673,6 @@ class DebugStatement : public Statement Statement *syntaxCopy(); Statement *semantic(Scope *sc); Statements *flatten(Scope *sc); - bool apply(sapply_fp_t fp, void *param); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void accept(Visitor *v) { v->visit(this); } }; @@ -893,6 +682,7 @@ class GotoStatement : public Statement Identifier *ident; LabelDsymbol *label; TryFinallyStatement *tf; + OnScopeStatement *os; VarDeclaration *lastVar; FuncDeclaration *fd; @@ -900,11 +690,7 @@ class GotoStatement : public Statement Statement *syntaxCopy(); Statement *semantic(Scope *sc); bool checkLabel(); - int blockExit(bool mustNotThrow); - Expression *interpret(InterState *istate); - void ctfeCompile(CompiledCtfeFunction *ccf); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void accept(Visitor *v) { v->visit(this); } }; @@ -914,6 +700,7 @@ class LabelStatement : public Statement Identifier *ident; Statement *statement; TryFinallyStatement *tf; + OnScopeStatement *os; Statement *gotoTarget; // interpret VarDeclaration *lastVar; block *lblock; // back end @@ -924,14 +711,8 @@ class LabelStatement : public Statement Statement *syntaxCopy(); Statement *semantic(Scope *sc); Statements *flatten(Scope *sc); - int blockExit(bool mustNotThrow); - bool comeFromImpl(); - Expression *interpret(InterState *istate); - bool apply(sapply_fp_t fp, void *param); - void ctfeCompile(CompiledCtfeFunction *ccf); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - - Statement *inlineScan(InlineScanState *iss); + Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally); + LabelStatement *isLabelStatement() { return this; } void accept(Visitor *v) { v->visit(this); } @@ -948,6 +729,8 @@ class LabelDsymbol : public Dsymbol void accept(Visitor *v) { v->visit(this); } }; +Statement* asmSemantic(AsmStatement *s, Scope *sc); + class AsmStatement : public Statement { public: @@ -955,22 +738,29 @@ class AsmStatement : public Statement code *asmcode; unsigned asmalign; // alignment of this statement unsigned regs; // mask of registers modified (must match regm_t in back end) - unsigned char refparam; // !=0 if function parameter is referenced - unsigned char naked; // !=0 if function is to be naked + bool refparam; // true if function parameter is referenced + bool naked; // true if function is to be naked AsmStatement(Loc loc, Token *tokens); Statement *syntaxCopy(); - Statement *semantic(Scope *sc); - int blockExit(bool mustNotThrow); - bool comeFromImpl(); - Expression *interpret(InterState *istate); - void ctfeCompile(CompiledCtfeFunction *ccf); + Statement *semantic(Scope *sc) + { + return asmSemantic(this, sc); + } - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + void accept(Visitor *v) { v->visit(this); } +}; + +// a complete asm {} block +class CompoundAsmStatement : public CompoundStatement +{ +public: + StorageClass stc; // postfix attributes like nothrow/pure/@trusted - //int inlineCost(InlineCostState *ics); - //Expression *doInline(InlineDoState *ids); - //Statement *inlineScan(InlineScanState *iss); + CompoundAsmStatement(Loc loc, Statements *s, StorageClass stc); + CompoundAsmStatement *syntaxCopy(); + CompoundAsmStatement *semantic(Scope *sc); + Statements *flatten(Scope *sc); void accept(Visitor *v) { v->visit(this); } }; @@ -983,16 +773,6 @@ class ImportStatement : public Statement ImportStatement(Loc loc, Dsymbols *imports); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - int blockExit(bool mustNotThrow); - bool hasCodeImpl(); - Expression *interpret(InterState *istate); - void ctfeCompile(CompiledCtfeFunction *ccf); - - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - - int inlineCost(InlineCostState *ics); - Expression *doInline(InlineDoState *ids); - Statement *doInlineStatement(InlineDoState *ids); void accept(Visitor *v) { v->visit(this); } }; @@ -1004,23 +784,19 @@ class ExtAsmStatement : public Statement { public: Expression *insn; - Expressions *args; - Identifiers *names; // of NULL or Identifier* - Expressions *constraints; // of StringExp* + Expressions *args; + Identifiers *names; + Expressions *constraints; // Array of StringExp's unsigned outputargs; - Expressions *clobbers; // of StringExp* + Expressions *clobbers; // Array of StringExp's + Identifiers *labels; + GotoStatements *gotos; ExtAsmStatement(Loc loc, Expression *insn, Expressions *args, - Identifiers *names, Expressions *constraints, - int outputargs, Expressions *clobbers); + Identifiers *names, Expressions *constraints, + int outputargs, Expressions *clobbers, Identifiers *labels); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - int blockExit(bool mustNotThrow); - bool comeFromImpl(); - Expression *interpret(InterState *istate); - void ctfeCompile(CompiledCtfeFunction *ccf); - - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void accept(Visitor *v) { v->visit(this); } }; diff --git a/gcc/d/dfrontend/staticassert.c b/gcc/d/dfrontend/staticassert.c index 26674baf2..c3542f1ff 100644 --- a/gcc/d/dfrontend/staticassert.c +++ b/gcc/d/dfrontend/staticassert.c @@ -1,11 +1,13 @@ -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/staticassert.c + */ #include #include @@ -40,7 +42,7 @@ Dsymbol *StaticAssert::syntaxCopy(Dsymbol *s) return sa; } -int StaticAssert::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) +int StaticAssert::addMember(Scope *sc, ScopeDsymbol *sds, int memnum) { return 0; // we didn't add anything } @@ -52,8 +54,9 @@ void StaticAssert::semantic(Scope *sc) void StaticAssert::semantic2(Scope *sc) { //printf("StaticAssert::semantic2() %s\n", toChars()); - ScopeDsymbol *sd = new ScopeDsymbol(); - sc = sc->push(sd); + ScopeDsymbol *sds = new ScopeDsymbol(); + sc = sc->push(sds); + sc->speculative = true; sc->flags |= SCOPEstaticassert; sc = sc->startCTFE(); @@ -90,13 +93,13 @@ void StaticAssert::semantic2(Scope *sc) sc = sc->endCTFE(); msg = msg->ctfeInterpret(); hgs.console = 1; - StringExp * s = msg->toString(); + StringExp * s = msg->toStringExp(); if (s) { s->postfix = 0; // Don't display a trailing 'c' msg = s; } msg->toCBuffer(&buf, &hgs); - error("%s", buf.toChars()); + error("%s", buf.peekString()); } else error("(%s) is false", exp->toChars()); @@ -118,11 +121,7 @@ bool StaticAssert::oneMember(Dsymbol **ps, Identifier *ident) return true; } -void StaticAssert::inlineScan() -{ -} - -void StaticAssert::toObjFile(int multiobj) +void StaticAssert::toObjFile(bool multiobj) { } diff --git a/gcc/d/dfrontend/staticassert.h b/gcc/d/dfrontend/staticassert.h index 03065899b..4762fb75f 100644 --- a/gcc/d/dfrontend/staticassert.h +++ b/gcc/d/dfrontend/staticassert.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2006 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/staticassert.h + */ #ifndef DMD_STATICASSERT_H #define DMD_STATICASSERT_H @@ -29,12 +30,11 @@ class StaticAssert : public Dsymbol StaticAssert(Loc loc, Expression *exp, Expression *msg); Dsymbol *syntaxCopy(Dsymbol *s); - int addMember(Scope *sc, ScopeDsymbol *sd, int memnum); + int addMember(Scope *sc, ScopeDsymbol *sds, int memnum); void semantic(Scope *sc); void semantic2(Scope *sc); - void inlineScan(); bool oneMember(Dsymbol **ps, Identifier *ident); - void toObjFile(int multiobj); + void toObjFile(bool multiobj); const char *kind(); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void accept(Visitor *v) { v->visit(this); } diff --git a/gcc/d/dfrontend/stringtable.c b/gcc/d/dfrontend/stringtable.c index a14857bd9..8777cd281 100644 --- a/gcc/d/dfrontend/stringtable.c +++ b/gcc/d/dfrontend/stringtable.c @@ -1,12 +1,11 @@ -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. - +/* Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/stringtable.c + */ #include #include // uint{8|16|32}_t @@ -70,6 +69,15 @@ hash_t calcHash(const char *str, size_t len) } } +static int hashCmp(hash_t lhs, hash_t rhs) +{ + if (lhs == rhs) + return 0; + else if (lhs < rhs) + return -1; + return 1; +} + void StringValue::ctor(const char *p, size_t length) { this->length = length; @@ -130,7 +138,7 @@ void **StringTable::search(const char *s, size_t len) //printf("\thash = %d, u = %d\n",hash,u); while (*se) { - cmp = (*se)->hash - hash; + cmp = hashCmp((*se)->hash, hash); if (cmp == 0) { cmp = (*se)->value.len() - len; diff --git a/gcc/d/dfrontend/stringtable.h b/gcc/d/dfrontend/stringtable.h index 2dd7a7be6..60fbda936 100644 --- a/gcc/d/dfrontend/stringtable.h +++ b/gcc/d/dfrontend/stringtable.h @@ -1,12 +1,11 @@ -// Copyright (c) 1999-2011 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. - +/* Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved, written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + * https://github.com/D-Programming-Language/dmd/blob/master/src/root/stringtable.h + */ #ifndef STRINGTABLE_H #define STRINGTABLE_H diff --git a/gcc/d/dfrontend/struct.c b/gcc/d/dfrontend/struct.c index bbf1bc725..27cee4d7d 100644 --- a/gcc/d/dfrontend/struct.c +++ b/gcc/d/dfrontend/struct.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/struct.c + */ #include #include @@ -22,32 +23,11 @@ #include "statement.h" #include "template.h" +TypeTuple *toArgTypes(Type *t); + FuncDeclaration *StructDeclaration::xerreq; // object.xopEquals FuncDeclaration *StructDeclaration::xerrcmp; // object.xopCmp -/*************************************** - * Search toHash member function for TypeInfo_Struct. - * const hash_t toHash(); - */ -FuncDeclaration *search_toHash(StructDeclaration *sd) -{ - Dsymbol *s = search_function(sd, Id::tohash); - FuncDeclaration *fd = s ? s->isFuncDeclaration() : NULL; - if (fd) - { - static TypeFunction *tftohash; - if (!tftohash) - { - tftohash = new TypeFunction(NULL, Type::thash_t, 0, LINKd); - tftohash->mod = MODconst; - tftohash = (TypeFunction *)tftohash->merge(); - } - - fd = fd->overloadExactMatch(tftohash); - } - return fd; -} - /*************************************** * Search toString member function for TypeInfo_Struct. * string toString(); @@ -108,14 +88,13 @@ void semanticTypeInfo(Scope *sc, Type *t) } void visit(TypeStruct *t) { - Dsymbol *s; StructDeclaration *sd = t->sym; if (sd->members && (sd->xeq && sd->xeq != sd->xerreq || sd->xcmp && sd->xcmp != sd->xerrcmp || (sd->postblit && !(sd->postblit->storage_class & STCdisable)) || sd->dtor || - search_toHash(sd) || + sd->xhash || search_toString(sd) ) && sd->inNonRoot()) @@ -153,7 +132,6 @@ AggregateDeclaration::AggregateDeclaration(Loc loc, Identifier *id) storage_class = 0; protection = PROTpublic; type = NULL; - handle = NULL; structsize = 0; // size of struct alignsize = 0; // size of struct for alignment purposes sizeok = SIZEOKnone; // size not determined yet @@ -190,134 +168,146 @@ void AggregateDeclaration::setScope(Scope *sc) void AggregateDeclaration::semantic2(Scope *sc) { - //printf("AggregateDeclaration::semantic2(%s)\n", toChars()); - if (scope && members) + //printf("AggregateDeclaration::semantic2(%s) type = %s, errors = %d\n", toChars(), type->toChars(), errors); + if (!members) + return; + + if (scope) { error("has forward references"); return; } - if (members) + + Scope *sc2 = sc->push(this); + sc2->stc &= STCsafe | STCtrusted | STCsystem; + sc2->parent = this; + //if (isUnionDeclaration()) // TODO + // sc2->inunion = 1; + sc2->protection = PROTpublic; + sc2->explicitProtection = 0; + sc2->structalign = STRUCTALIGN_DEFAULT; + sc2->userAttribDecl = NULL; + + for (size_t i = 0; i < members->dim; i++) { - sc = sc->push(this); - sc->parent = this; - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (*members)[i]; - //printf("\t[%d] %s\n", i, s->toChars()); - s->semantic2(sc); - } - sc->pop(); + Dsymbol *s = (*members)[i]; + //printf("\t[%d] %s\n", i, s->toChars()); + s->semantic2(sc2); } + + sc2->pop(); } void AggregateDeclaration::semantic3(Scope *sc) { - //printf("AggregateDeclaration::semantic3(%s)\n", toChars()); - if (members) + //printf("AggregateDeclaration::semantic3(%s) type = %s, errors = %d\n", toChars(), type->toChars(), errors); + if (!members) + return; + + StructDeclaration *sd = isStructDeclaration(); + if (!sc) // from runDeferredSemantic3 for TypeInfo generation { - StructDeclaration *sd = isStructDeclaration(); - if (!sc) // from runDeferredSemantic3 for TypeInfo generation - goto Lxop; + assert(sd); + sd->semanticTypeInfoMembers(); + return; + } - sc = sc->push(this); - sc->parent = this; - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (*members)[i]; - s->semantic3(sc); - } - sc = sc->pop(); + Scope *sc2 = sc->push(this); + sc2->stc &= STCsafe | STCtrusted | STCsystem; + sc2->parent = this; + if (isUnionDeclaration()) + sc2->inunion = 1; + sc2->protection = PROTpublic; + sc2->explicitProtection = 0; + sc2->structalign = STRUCTALIGN_DEFAULT; + sc2->userAttribDecl = NULL; - if (!getRTInfo && Type::rtinfo && - (!isDeprecated() || global.params.useDeprecated) && // don't do it for unused deprecated types - (type && type->ty != Terror)) // or error types - { - // Evaluate: RTinfo!type - Objects *tiargs = new Objects(); - tiargs->push(type); - TemplateInstance *ti = new TemplateInstance(loc, Type::rtinfo, tiargs); - ti->semantic(sc); - ti->semantic2(sc); - ti->semantic3(sc); - Dsymbol *s = ti->toAlias(); - Expression *e = new DsymbolExp(Loc(), s, 0); - - Scope *sc2 = ti->tempdecl->scope->startCTFE(); - sc2->tinst = sc->tinst; - e = e->semantic(sc2); - sc2->endCTFE(); - - e = e->ctfeInterpret(); - getRTInfo = e; - } + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + s->semantic3(sc2); + } - if (sd) - { - Lxop: - if (sd->xeq && - sd->xeq->scope && - sd->xeq->semanticRun < PASSsemantic3done) - { - unsigned errors = global.startGagging(); - sd->xeq->semantic3(sd->xeq->scope); - if (global.endGagging(errors)) - sd->xeq = sd->xerreq; - } + sc2->pop(); - if (sd->xcmp && - sd->xcmp->scope && - sd->xcmp->semanticRun < PASSsemantic3done) - { - unsigned errors = global.startGagging(); - sd->xcmp->semantic3(sd->xcmp->scope); - if (global.endGagging(errors)) - sd->xcmp = sd->xerrcmp; - } + // don't do it for unused deprecated types + // or error types + if (!getRTInfo && Type::rtinfo && + (!isDeprecated() || global.params.useDeprecated) && + (type && type->ty != Terror)) + { + // Evaluate: RTinfo!type + Objects *tiargs = new Objects(); + tiargs->push(type); + TemplateInstance *ti = new TemplateInstance(loc, Type::rtinfo, tiargs); + ti->semantic(sc); + ti->semantic2(sc); + ti->semantic3(sc); + Dsymbol *s = ti->toAlias(); + Expression *e = new DsymbolExp(Loc(), s, 0); + + Scope *sc3 = ti->tempdecl->scope->startCTFE(); + sc3->tinst = sc->tinst; + e = e->semantic(sc3); + sc3->endCTFE(); + + e = e->ctfeInterpret(); + getRTInfo = e; + } - FuncDeclaration *ftostr = search_toString(sd); - if (ftostr && - ftostr->scope && - ftostr->semanticRun < PASSsemantic3done) - { - ftostr->semantic3(ftostr->scope); - } + if (sd) + sd->semanticTypeInfoMembers(); +} - FuncDeclaration *ftohash = search_toHash(sd); - if (ftohash && - ftohash->scope && - ftohash->semanticRun < PASSsemantic3done) - { - ftohash->semantic3(ftohash->scope); - } +void StructDeclaration::semanticTypeInfoMembers() +{ + if (xeq && + xeq->scope && + xeq->semanticRun < PASSsemantic3done) + { + unsigned errors = global.startGagging(); + xeq->semantic3(xeq->scope); + if (global.endGagging(errors)) + xeq = xerreq; + } - if (sd->postblit && - sd->postblit->scope && - sd->postblit->semanticRun < PASSsemantic3done) - { - sd->postblit->semantic3(sd->postblit->scope); - } + if (xcmp && + xcmp->scope && + xcmp->semanticRun < PASSsemantic3done) + { + unsigned errors = global.startGagging(); + xcmp->semantic3(xcmp->scope); + if (global.endGagging(errors)) + xcmp = xerrcmp; + } - if (sd->dtor && - sd->dtor->scope && - sd->dtor->semanticRun < PASSsemantic3done) - { - sd->dtor->semantic3(sd->dtor->scope); - } - } + FuncDeclaration *ftostr = search_toString(this); + if (ftostr && + ftostr->scope && + ftostr->semanticRun < PASSsemantic3done) + { + ftostr->semantic3(ftostr->scope); } -} -void AggregateDeclaration::inlineScan() -{ - //printf("AggregateDeclaration::inlineScan(%s)\n", toChars()); - if (members) + if (xhash && + xhash->scope && + xhash->semanticRun < PASSsemantic3done) { - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (*members)[i]; - //printf("inline scan aggregate symbol '%s'\n", s->toChars()); - s->inlineScan(); - } + xhash->semantic3(xhash->scope); + } + + if (postblit && + postblit->scope && + postblit->semanticRun < PASSsemantic3done) + { + postblit->semantic3(postblit->scope); + } + + if (dtor && + dtor->scope && + dtor->semanticRun < PASSsemantic3done) + { + dtor->semantic3(dtor->scope); } } @@ -342,7 +332,7 @@ unsigned AggregateDeclaration::size(Loc loc) * 1 this member does */ static int func(Dsymbol *s, void *param) - { SV *psv = (SV *)param; + { VarDeclaration *v = s->isVarDeclaration(); if (v) { @@ -360,7 +350,8 @@ unsigned AggregateDeclaration::size(Loc loc) SV sv; for (size_t i = 0; i < members->dim; i++) - { Dsymbol *s = (*members)[i]; + { + Dsymbol *s = (*members)[i]; if (s->apply(&SV::func, &sv)) goto L1; } @@ -399,11 +390,14 @@ bool AggregateDeclaration::isExport() /**************************** * Do byte or word alignment as necessary. * Align sizes of 0, as we may not know array sizes yet. + * + * alignment: struct alignment that is in effect + * size: alignment requirement of field */ void AggregateDeclaration::alignmember( - structalign_t alignment, // struct alignment that is in effect - unsigned size, // alignment requirement of field + structalign_t alignment, + unsigned size, unsigned *poffset) { //printf("alignment = %d, size = %d, offset = %d\n",alignment,size,offset); @@ -414,14 +408,11 @@ void AggregateDeclaration::alignmember( break; case (structalign_t) STRUCTALIGN_DEFAULT: - { /* Must match what the corresponding C compiler's default - * alignment behavior is. - */ - assert(size != 3); - unsigned sa = (size == 0 || 8 < size) ? 8 : size; - *poffset = (*poffset + sa - 1) & ~(sa - 1); + // Alignment in Target::fieldalignsize must match what the + // corresponding C compiler's default alignment behavior is. + assert(size > 0 && !(size & (size - 1))); + *poffset = (*poffset + size - 1) & ~(size - 1); break; - } default: // Align on alignment boundary, which must be a positive power of 2 @@ -435,15 +426,23 @@ void AggregateDeclaration::alignmember( * Place a member (mem) into an aggregate (agg), which can be a struct, union or class * Returns: * offset to place field at + * + * nextoffset: next location in aggregate + * memsize: size of member + * memalignsize: size of member for alignment purposes + * alignment: alignment in effect for this member + * paggsize: size of aggregate (updated) + * paggalignsize: size of aggregate for alignment purposes (updated) + * isunion: the aggregate is a union */ unsigned AggregateDeclaration::placeField( - unsigned *nextoffset, // next location in aggregate - unsigned memsize, // size of member - unsigned memalignsize, // size of member for alignment purposes - structalign_t alignment, // alignment in effect for this member - unsigned *paggsize, // size of aggregate (updated) - unsigned *paggalignsize, // size of aggregate for alignment purposes (updated) - bool isunion // the aggregate is a union + unsigned *nextoffset, + unsigned memsize, + unsigned memalignsize, + structalign_t alignment, + unsigned *paggsize, + unsigned *paggalignsize, + bool isunion ) { unsigned ofs = *nextoffset; @@ -518,11 +517,12 @@ void AggregateDeclaration::makeNested() //printf("makeNested %s, enclosing = %s\n", toChars(), enclosing->toChars()); Type *t; if (ad) - t = ad->handle; + t = ad->handleType(); else if (fd) - { AggregateDeclaration *ad2 = fd->isMember2(); + { + AggregateDeclaration *ad2 = fd->isMember2(); if (ad2) - t = ad2->handle; + t = ad2->handleType(); else t = Type::tvoidptr; } @@ -595,20 +595,21 @@ int AggregateDeclaration::numFieldsInUnion(int firstIndex) /******************************************* * Look for constructor declaration. */ -void AggregateDeclaration::searchCtor() +Dsymbol *AggregateDeclaration::searchCtor() { - ctor = search(Loc(), Id::ctor); - if (ctor) + Dsymbol *s = search(Loc(), Id::ctor); + if (s) { - if (!(ctor->isCtorDeclaration() || - ctor->isTemplateDeclaration() || - ctor->isOverloadSet())) + if (!(s->isCtorDeclaration() || + s->isTemplateDeclaration() || + s->isOverloadSet())) { - error("%s %s is not a constructor; identifiers starting with __ are reserved for the implementation", ctor->kind(), ctor->toChars()); + error("%s %s is not a constructor; identifiers starting with __ are reserved for the implementation", s->kind(), s->toChars()); errors = true; - ctor = NULL; + s = NULL; } } + return s; } /********************************* StructDeclaration ****************************/ @@ -617,13 +618,14 @@ StructDeclaration::StructDeclaration(Loc loc, Identifier *id) : AggregateDeclaration(loc, id) { zeroInit = 0; // assume false until we do semantic processing - hasIdentityAssign = 0; - hasIdentityEquals = 0; + hasIdentityAssign = false; + hasIdentityEquals = false; cpctor = NULL; postblit = NULL; xeq = NULL; xcmp = NULL; + xhash = NULL; alignment = 0; ispod = ISPODfwd; arg1type = NULL; @@ -650,27 +652,14 @@ Dsymbol *StructDeclaration::syntaxCopy(Dsymbol *s) void StructDeclaration::semantic(Scope *sc) { - Scope *sc2; - //printf("+StructDeclaration::semantic(this=%p, %s '%s', sizeok = %d)\n", this, parent->toChars(), toChars(), sizeok); //static int count; if (++count == 20) halt(); - assert(type); - if (!members) // if opaque declaration - { + if (semanticRun >= PASSsemanticdone) return; - } - - if (symtab) - { if (sizeok == SIZEOKdone || !scope) - { //printf("already completed\n"); - scope = NULL; - return; // semantic() already completed - } - } - else - symtab = new DsymbolTable(); + unsigned dprogress_save = Module::dprogress; + int errors = global.errors; Scope *scx = NULL; if (scope) @@ -679,21 +668,55 @@ void StructDeclaration::semantic(Scope *sc) scx = scope; // save so we don't make redundant copies scope = NULL; } - unsigned dprogress_save = Module::dprogress; - int errors = global.errors; - parent = sc->parent; + if (!parent) + { + assert(sc->parent && sc->func); + parent = sc->parent; + } + assert(parent && !isAnonymous()); type = type->semantic(loc, sc); - handle = type; - protection = sc->protection; - alignment = sc->structalign; - storage_class |= sc->stc; - if (sc->stc & STCdeprecated) - isdeprecated = true; - assert(!isAnonymous()); - if (sc->stc & STCabstract) - error("structs, unions cannot be abstract"); - userAttribDecl = sc->userAttribDecl; + + if (type->ty == Tstruct && ((TypeStruct *)type)->sym != this) + { + TemplateInstance *ti = ((TypeStruct *)type)->sym->isInstantiated(); + if (ti && isError(ti)) + ((TypeStruct *)type)->sym = this; + } + + // Ungag errors when not speculative + Ungag ungag = ungagSpeculative(); + + if (semanticRun == PASSinit) + { + protection = sc->protection; + + alignment = sc->structalign; + + storage_class |= sc->stc; + if (storage_class & STCdeprecated) + isdeprecated = true; + if (storage_class & STCabstract) + error("structs, unions cannot be abstract"); + userAttribDecl = sc->userAttribDecl; + } + else if (symtab) + { + if (sizeok == SIZEOKdone || !scx) + { + semanticRun = PASSsemanticdone; + return; + } + } + semanticRun = PASSsemantic; + + if (!members) // if opaque declaration + { + semanticRun = PASSsemanticdone; + return; + } + if (!symtab) + symtab = new DsymbolTable(); if (sizeok == SIZEOKnone) // if not already done the addMember step { @@ -706,7 +729,7 @@ void StructDeclaration::semantic(Scope *sc) } sizeok = SIZEOKnone; - sc2 = sc->push(this); + Scope *sc2 = sc->push(this); sc2->stc &= STCsafe | STCtrusted | STCsystem; sc2->parent = this; if (isUnionDeclaration()) @@ -726,6 +749,12 @@ void StructDeclaration::semantic(Scope *sc) s->setScope(sc2); } + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + s->importAll(sc2); + } + for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; @@ -740,9 +769,6 @@ void StructDeclaration::semantic(Scope *sc) if (sizeok == SIZEOKnone && s->isAliasDeclaration()) finalizeSize(sc2); } - - // Ungag errors when not speculative - Ungag ungag = ungagSpeculative(); s->semantic(sc2); } finalizeSize(sc2); @@ -759,9 +785,8 @@ void StructDeclaration::semantic(Scope *sc) fields.setDim(0); structsize = 0; alignsize = 0; -// structalign = 0; - scope = scx ? scx : new Scope(*sc); + scope = scx ? scx : sc->copy(); scope->setNoFree(); scope->module->addDeferredSemantic(this); @@ -771,6 +796,7 @@ void StructDeclaration::semantic(Scope *sc) } Module::dprogress++; + semanticRun = PASSsemanticdone; //printf("-StructDeclaration::semantic(this=%p, '%s')\n", this, toChars()); @@ -798,15 +824,16 @@ void StructDeclaration::semantic(Scope *sc) } } - dtor = buildDtor(sc2); - postblit = buildPostBlit(sc2); - cpctor = buildCpCtor(sc2); + dtor = buildDtor(this, sc2); + postblit = buildPostBlit(this, sc2); + cpctor = buildCpCtor(this, sc2); - buildOpAssign(sc2); - buildOpEquals(sc2); + buildOpAssign(this, sc2); + buildOpEquals(this, sc2); - xeq = buildXopEquals(sc2); - xcmp = buildXopCmp(sc2); + xeq = buildXopEquals(this, sc2); + xcmp = buildXopCmp(this, sc2); + xhash = buildXtoHash(this, sc2); /* Even if the struct is merely imported and its semantic3 is not run, * the TypeInfo object would be speculatively stored in each object @@ -817,13 +844,13 @@ void StructDeclaration::semantic(Scope *sc) /* Defer requesting semantic3 until TypeInfo generation is actually invoked. * See semanticTypeInfo(). */ - inv = buildInv(sc2); + inv = buildInv(this, sc2); sc2->pop(); /* Look for special member functions. */ - searchCtor(); + ctor = searchCtor(); aggNew = (NewDeclaration *)search(Loc(), Id::classNew); aggDelete = (DeleteDeclaration *)search(Loc(), Id::classDelete); @@ -832,15 +859,12 @@ void StructDeclaration::semantic(Scope *sc) Dsymbol *scall = search(Loc(), Id::call); if (scall) { - unsigned errors = global.startGagging(); - unsigned oldspec = global.speculativeGag; - global.speculativeGag = global.gag; + unsigned xerrors = global.startGagging(); sc = sc->push(); sc->speculative = true; FuncDeclaration *fcall = resolveFuncCall(loc, sc, scall, NULL, NULL, NULL, 1); sc = sc->pop(); - global.speculativeGag = oldspec; - global.endGagging(errors); + global.endGagging(xerrors); if (fcall && fcall->isStatic()) { @@ -850,25 +874,26 @@ void StructDeclaration::semantic(Scope *sc) } } - TypeTuple *tup = type->toArgTypes(); + TypeTuple *tup = toArgTypes(type); size_t dim = tup->arguments->dim; if (dim >= 1) - { assert(dim <= 2); + { + assert(dim <= 2); arg1type = (*tup->arguments)[0]->type; if (dim == 2) arg2type = (*tup->arguments)[1]->type; } if (sc->func) - { semantic2(sc); - semantic3(sc); - } if (global.errors != errors) - { // The type is no good. + { + // The type is no good. type = Type::terror; this->errors = true; + if (deferred) + deferred->errors = true; } if (deferred && !global.gag) @@ -877,12 +902,14 @@ void StructDeclaration::semantic(Scope *sc) deferred->semantic3(sc); } +#if 0 if (type->ty == Tstruct && ((TypeStruct *)type)->sym != this) { - error("failed semantic analysis"); - this->errors = true; - type = Type::terror; + printf("this = %p %s\n", this, this->toChars()); + printf("type = %d sym = %p\n", type->ty, ((TypeStruct *)type)->sym); } +#endif + assert(type->ty != Tstruct || ((TypeStruct *)type)->sym == this); } Dsymbol *StructDeclaration::search(Loc loc, Identifier *ident, int flags) @@ -911,7 +938,8 @@ void StructDeclaration::finalizeSize(Scope *sc) unsigned offset = 0; bool isunion = isUnionDeclaration() != NULL; for (size_t i = 0; i < members->dim; i++) - { Dsymbol *s = (*members)[i]; + { + Dsymbol *s = (*members)[i]; s->setFieldOffset(this, &offset, isunion); } if (sizeok == SIZEOKfwd) @@ -938,6 +966,81 @@ void StructDeclaration::finalizeSize(Scope *sc) fill(loc, NULL, true); } +/*************************************** + * Fit elements[] to the corresponding type of field[]. + * Input: + * loc + * sc + * elements The explicit arguments that given to construct object. + * stype The constructed object type. + * Returns false if any errors occur. + * Otherwise, returns true and elements[] are rewritten for the output. + */ +bool StructDeclaration::fit(Loc loc, Scope *sc, Expressions *elements, Type *stype) +{ + if (!elements) + return true; + + size_t nfields = fields.dim - isNested(); + size_t offset = 0; + for (size_t i = 0; i < elements->dim; i++) + { + Expression *e = (*elements)[i]; + if (!e) + continue; + + e = resolveProperties(sc, e); + if (i >= nfields) + { + if (i == fields.dim - 1 && isNested() && e->op == TOKnull) + { + // CTFE sometimes creates null as hidden pointer; we'll allow this. + continue; + } + ::error(loc, "more initializers than fields (%d) of %s", nfields, toChars()); + return false; + } + VarDeclaration *v = fields[i]; + if (v->offset < offset) + { + ::error(loc, "overlapping initialization for %s", v->toChars()); + return false; + } + offset = (unsigned)(v->offset + v->type->size()); + + Type *telem = v->type; + if (stype) + telem = telem->addMod(stype->mod); + Type *origType = telem; + while (!e->implicitConvTo(telem) && telem->toBasetype()->ty == Tsarray) + { + /* Static array initialization, as in: + * T[3][5] = e; + */ + telem = telem->toBasetype()->nextOf(); + } + + if (!e->implicitConvTo(telem)) + telem = origType; // restore type for better diagnostic + + e = e->implicitCastTo(sc, telem); + if (e->op == TOKerror) + return false; + + (*elements)[i] = e->isLvalue() ? callCpCtor(sc, e) : valueNoDtor(e); + } + return true; +} + +/*************************************** + * Fill out remainder of elements[] with default initializers for fields[]. + * Input: + * loc + * elements explicit arguments which given to construct object. + * ctorinit true if the elements will be used for default initialization. + * Returns false if any errors occur. + * Otherwise, returns true and the missing arguments will be pushed in elements[]. + */ bool StructDeclaration::fill(Loc loc, Expressions *elements, bool ctorinit) { assert(sizeok == SIZEOKdone); @@ -1041,10 +1144,20 @@ bool StructDeclaration::fill(Loc loc, Expressions *elements, bool ctorinit) ::error(loc, "field %s.%s must be initialized because it has no default constructor", type->toChars(), vx->toChars()); } - if (vx->type->needsNested() && ctorinit) - e = vx->type->defaultInit(loc); + + /* Bugzilla 12509: Get the element of static array type. + */ + Type *telem = vx->type; + if (telem->ty == Tsarray) + { + telem = telem->baseElemOf(); + if (telem->ty == Tvoid) + telem = Type::tuns8->addMod(telem->mod); + } + if (telem->needsNested() && ctorinit) + e = telem->defaultInit(loc); else - e = vx->type->defaultInitLiteral(loc); + e = telem->defaultInitLiteral(loc); } (*elements)[fieldi] = e; } @@ -1067,7 +1180,7 @@ bool StructDeclaration::fill(Loc loc, Expressions *elements, bool ctorinit) * This is defined as: * not nested * no postblits, destructors, or assignment operators - * no fields that are themselves non-POD + * no 'ref' fields or fields that are themselves non-POD * The idea being these are compatible with C structs. */ bool StructDeclaration::isPOD() @@ -1086,7 +1199,11 @@ bool StructDeclaration::isPOD() { VarDeclaration *v = fields[i]; if (v->storage_class & STCref) - continue; + { + ispod = ISPODno; + break; + } + Type *tv = v->type->baseElemOf(); if (tv->ty == Tstruct) { diff --git a/gcc/d/dfrontend/target.h b/gcc/d/dfrontend/target.h index b069b4741..a662ec6b0 100644 --- a/gcc/d/dfrontend/target.h +++ b/gcc/d/dfrontend/target.h @@ -1,11 +1,13 @@ -// Copyright (c) 2013 by Digital Mars -// All Rights Reserved -// written by Iain Buclaw -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 2013-2014 by Digital Mars + * All Rights Reserved + * written by Iain Buclaw + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/target.h + */ #ifndef TARGET_H #define TARGET_H @@ -14,6 +16,7 @@ // At present it is incomplete, but in future it should grow to contain // most or all target machine and target O/S specific information. +class Expression; class Type; struct Target @@ -23,11 +26,14 @@ struct Target static int realpad; // 'padding' added to the CPU real size to bring it up to realsize static int realalignsize; // alignment for reals static bool reverseCppOverloads; // with dmc, overloaded functions are grouped and in reverse order + static int longsize; // size of a C 'long' or 'unsigned long' type static void init(); static unsigned alignsize(Type* type); static unsigned fieldalign(Type* type); static unsigned critsecsize(); + static Type *va_listType(); // get type of va_list + static Expression *paintAsType(Expression *e, Type *type); }; #endif diff --git a/gcc/d/dfrontend/template.c b/gcc/d/dfrontend/template.c index 2134180b9..e3407192c 100644 --- a/gcc/d/dfrontend/template.c +++ b/gcc/d/dfrontend/template.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/template.c + */ // Handle template implementation @@ -41,6 +42,8 @@ size_t templateParameterLookup(Type *tparam, TemplateParameters *parameters); int arrayObjectMatch(Objects *oa1, Objects *oa2); hash_t arrayObjectHash(Objects *oa1); +unsigned char deduceWildHelper(Type *t, Type **at, Type *tparam); +MATCH deduceTypeHelper(Type *t, Type **at, Type *tparam); /******************************************** * These functions substitute for dynamic_cast. dynamic_cast does not work @@ -130,7 +133,8 @@ Type *getType(RootObject *o) { Type *t = isType(o); if (!t) - { Expression *e = isExpression(o); + { + Expression *e = isExpression(o); if (e) t = e->type; } @@ -145,7 +149,8 @@ Dsymbol *getDsymbol(RootObject *oarg) Dsymbol *sa; Expression *ea = isExpression(oarg); if (ea) - { // Try to convert Expression to symbol + { + // Try to convert Expression to symbol if (ea->op == TOKvar) sa = ((VarExp *)ea)->var; else if (ea->op == TOKfunction) @@ -159,7 +164,8 @@ Dsymbol *getDsymbol(RootObject *oarg) sa = NULL; } else - { // Try to convert Type to symbol + { + // Try to convert Type to symbol Type *ta = isType(oarg); if (ta) sa = ta->toDsymbol(NULL); @@ -199,6 +205,62 @@ Expression *getValue(Dsymbol *&s) return e; } +/********************************** + * Return true if e could be valid only as a template value parameter. + * Return false if it might be an alias or tuple. + * (Note that even in this case, it could still turn out to be a value). + */ +bool definitelyValueParameter(Expression *e) +{ + // None of these can be value parameters + if (e->op == TOKtuple || e->op == TOKimport || + e->op == TOKtype || e->op == TOKdottype || + e->op == TOKtemplate || e->op == TOKdottd || + e->op == TOKfunction || e->op == TOKerror || + e->op == TOKthis || e->op == TOKsuper) + return false; + + if (e->op != TOKdotvar) + return true; + + /* Template instantiations involving a DotVar expression are difficult. + * In most cases, they should be treated as a value parameter, and interpreted. + * But they might also just be a fully qualified name, which should be treated + * as an alias. + */ + + // x.y.f cannot be a value + FuncDeclaration *f = ((DotVarExp *)e)->var->isFuncDeclaration(); + if (f) + return false; + + while (e->op == TOKdotvar) + { + e = ((DotVarExp *)e)->e1; + } + // this.x.y and super.x.y couldn't possibly be valid values. + if (e->op == TOKthis || e->op == TOKsuper) + return false; + + // e.type.x could be an alias + if (e->op == TOKdottype) + return false; + + // var.x.y is the only other possible form of alias + if (e->op != TOKvar) + return true; + + VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); + + // func.x.y is not an alias + if (!v) + return true; + + // TODO: Should we force CTFE if it is a global constant? + + return false; +} + /****************************** * If o1 matches o2, return 1. * Else, return 0. @@ -230,21 +292,20 @@ int match(RootObject *o1, RootObject *o2) { //printf("t1 = %s\n", t1->toChars()); //printf("t2 = %s\n", t2->toChars()); - if (!t2 || !t1->equals(t2)) + if (!t2) + goto Lnomatch; + if (!t1->equals(t2)) goto Lnomatch; } else if (e1) { -#if 0 - if (e1 && e2) - { - printf("match %d\n", e1->equals(e2)); - printf("\te1 = %p %s %s %s\n", e1, e1->type->toChars(), Token::toChars(e1->op), e1->toChars()); - printf("\te2 = %p %s %s %s\n", e2, e2->type->toChars(), Token::toChars(e2->op), e2->toChars()); - } -#endif if (!e2) goto Lnomatch; +#if 0 + printf("match %d\n", e1->equals(e2)); + printf("\te1 = %p %s %s %s\n", e1, e1->type->toChars(), Token::toChars(e1->op), e1->toChars()); + printf("\te2 = %p %s %s %s\n", e2, e2->type->toChars(), Token::toChars(e2->op), e2->toChars()); +#endif if (!e1->equals(e2)) goto Lnomatch; } @@ -290,7 +351,8 @@ int arrayObjectMatch(Objects *oa1, Objects *oa2) if (oa1->dim != oa2->dim) return 0; for (size_t j = 0; j < oa1->dim; j++) - { RootObject *o1 = (*oa1)[j]; + { + RootObject *o1 = (*oa1)[j]; RootObject *o2 = (*oa2)[j]; if (!match(o1, o2)) { @@ -308,7 +370,8 @@ hash_t arrayObjectHash(Objects *oa1) { hash_t hash = 0; for (size_t j = 0; j < oa1->dim; j++) - { /* Must follow the logic of match() + { + /* Must follow the logic of match() */ RootObject *o1 = (*oa1)[j]; if (Type *t1 = isType(o1)) @@ -322,7 +385,7 @@ hash_t arrayObjectHash(Objects *oa1) if (e1->op == TOKint64) { IntegerExp *ne = (IntegerExp *)e1; - hash += (size_t)ne->value; + hash += (size_t)ne->getInteger(); } } else if (s1) @@ -358,7 +421,8 @@ void ObjectToCBuffer(OutBuffer *buf, HdrGenState *hgs, RootObject *oarg) * Perhaps it would be better to demangle what genIdent() does. */ if (t) - { //printf("\tt: %s ty = %d\n", t->toChars(), t->ty); + { + //printf("\tt: %s ty = %d\n", t->toChars(), t->ty); t->toCBuffer(buf, NULL, hgs); } else if (e) @@ -422,7 +486,8 @@ TemplateDeclaration::TemplateDeclaration(Loc loc, Identifier *id, #if 0 if (parameters) for (int i = 0; i < parameters->dim; i++) - { TemplateParameter *tp = (*parameters)[i]; + { + TemplateParameter *tp = (*parameters)[i]; //printf("\tparameter[%d] = %p\n", i, tp); TemplateTypeParameter *ttp = tp->isTemplateTypeParameter(); @@ -473,7 +538,8 @@ Dsymbol *TemplateDeclaration::syntaxCopy(Dsymbol *) p = new TemplateParameters(); p->setDim(parameters->dim); for (size_t i = 0; i < p->dim; i++) - { TemplateParameter *tp = (*parameters)[i]; + { + TemplateParameter *tp = (*parameters)[i]; (*p)[i] = tp->syntaxCopy(); } } @@ -499,31 +565,18 @@ void TemplateDeclaration::semantic(Scope *sc) // Remember templates defined in module object that we need to know about if (sc->module && sc->module->ident == Id::object) { - if (ident == Id::AssociativeArray) - Type::associativearray = this; - else if (ident == Id::RTInfo) + if (ident == Id::RTInfo) Type::rtinfo = this; } - if (/*global.params.useArrayBounds &&*/ sc->module) - { - // Generate this function as it may be used - // when template is instantiated in other modules - sc->module->toModuleArray(); - } - - if (/*global.params.useAssert &&*/ sc->module) - { - // Generate this function as it may be used - // when template is instantiated in other modules - sc->module->toModuleAssert(); - } - - if (sc->module) + if (Module *m = sc->module) // should use getModule() instead? { - // Generate this function as it may be used + // Generate these functions as they may be used // when template is instantiated in other modules - sc->module->toModuleUnittest(); + // even if assertions or bounds checking are disabled in this module + m->toModuleArray(); + m->toModuleAssert(); + m->toModuleUnittest(); } /* Remember Scope for later instantiations, but make @@ -531,7 +584,7 @@ void TemplateDeclaration::semantic(Scope *sc) */ if (!this->scope) { - this->scope = new Scope(*sc); + this->scope = sc->copy(); this->scope->setNoFree(); } @@ -578,6 +631,37 @@ void TemplateDeclaration::semantic(Scope *sc) } } + /* Calculate TemplateParameter::dependent + */ + TemplateParameters tparams; + tparams.setDim(1); + for (size_t i = 0; i < parameters->dim; i++) + { + TemplateParameter *tp = (*parameters)[i]; + tparams[0] = tp; + + for (size_t j = 0; j < parameters->dim; j++) + { + // Skip cases like: X(T : T) + if (i == j) + continue; + + if (TemplateTypeParameter *ttp = (*parameters)[j]->isTemplateTypeParameter()) + { + if (reliesOnTident(ttp->specType, &tparams)) + tp->dependent = true; + } + else if (TemplateAliasParameter *tap = (*parameters)[j]->isTemplateAliasParameter()) + { + if (reliesOnTident(tap->specType, &tparams) || + reliesOnTident(isType(tap->specAlias), &tparams)) + { + tp->dependent = true; + } + } + } + } + paramscope->pop(); // Compute again @@ -641,7 +725,8 @@ bool TemplateDeclaration::overloadInsert(Dsymbol *s) goto Lcontinue; for (size_t i = 0; i < td->parameters->dim; i++) - { TemplateParameter *p1 = (*td->parameters)[i]; + { + TemplateParameter *p1 = (*td->parameters)[i]; TemplateParameter *p2 = (*f2->parameters)[i]; if (!p1->overloadMatch(p2)) @@ -671,7 +756,7 @@ bool TemplateDeclaration::overloadInsert(Dsymbol *s) */ bool TemplateDeclaration::evaluateConstraint( TemplateInstance *ti, Scope *sc, Scope *paramscope, - Objects *dedtypes, FuncDeclaration *fd) + Objects *dedargs, FuncDeclaration *fd) { /* Detect recursive attempts to instantiate this template declaration, * Bugzilla 4072 @@ -686,10 +771,9 @@ bool TemplateDeclaration::evaluateConstraint( * Workaround the problem by setting a flag to relax the checking on frame errors. */ - int nmatches = 0; for (TemplatePrevious *p = previous; p; p = p->prev) { - if (arrayObjectMatch(p->dedargs, dedtypes)) + if (arrayObjectMatch(p->dedargs, dedargs)) { //printf("recursive, no match p->sc=%p %p %s\n", p->sc, this, this->toChars()); /* It must be a subscope of p->sc, other scope chains are not recursive @@ -708,15 +792,17 @@ bool TemplateDeclaration::evaluateConstraint( TemplatePrevious pr; pr.prev = previous; pr.sc = paramscope; - pr.dedargs = dedtypes; + pr.dedargs = dedargs; previous = ≺ // add this to threaded list - int nerrors = global.errors; + unsigned int nerrors = global.errors; Scope *scx = paramscope->push(ti); scx->parent = ti; scx->tinst = ti; + scx->speculative = true; + assert(!ti->symtab); if (fd) { /* Declare all the function parameters as variables and add them to the scope @@ -747,6 +833,8 @@ bool TemplateDeclaration::evaluateConstraint( VarDeclaration *v = new VarDeclaration(loc, fparam->type, fparam->ident, NULL); v->storage_class = fparam->storageClass; v->semantic(scx); + if (!ti->symtab) + ti->symtab = new DsymbolTable(); if (!scx->insert(v)) error("parameter %s.%s is already defined", toChars(), v->toChars()); else @@ -762,11 +850,15 @@ bool TemplateDeclaration::evaluateConstraint( scx = scx->startCTFE(); scx->flags |= SCOPEstaticif; + assert(ti->inst == NULL); + ti->inst = ti; // temporary instantiation to enable genIdent() //printf("\tscx->parent = %s %s\n", scx->parent->kind(), scx->parent->toPrettyChars()); e = e->semantic(scx); e = resolveProperties(scx, e); + ti->inst = NULL; + ti->symtab = NULL; scx = scx->endCTFE(); scx = scx->pop(); @@ -902,9 +994,6 @@ MATCH TemplateDeclaration::matchWithInstance(Scope *sc, TemplateInstance *ti, if (m > MATCHnomatch && constraint && !flag) { - // minimum requred settings - ti->symtab = new DsymbolTable(); - //ti->parent = this->parent; if (ti->hasNestedArgs(ti->tiargs, this->isstatic)) // TODO: should gag error ti->parent = ti->enclosing; else @@ -929,10 +1018,12 @@ MATCH TemplateDeclaration::matchWithInstance(Scope *sc, TemplateInstance *ti, // Resolve parameter types and 'auto ref's. tf->fargs = fargs; fd->type = tf->semantic(loc, paramscope); + fd->originalType = fd->type; // for mangling if (fd->type->ty != Tfunction) goto Lnomatch; } + // TODO: dedtypes => ti->tiargs ? if (!evaluateConstraint(ti, sc, paramscope, dedtypes, fd)) goto Lnomatch; } @@ -1012,16 +1103,17 @@ MATCH TemplateDeclaration::leastAsSpecialized(Scope *sc, TemplateDeclaration *td // Set type arguments to dummy template instance to be types // generated from the parameters to this template declaration ti.tiargs = new Objects(); - ti.tiargs->setDim(parameters->dim); - for (size_t i = 0; i < ti.tiargs->dim; i++) + ti.tiargs->reserve(parameters->dim); + for (size_t i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (*parameters)[i]; - + if (tp->dependent) + break; RootObject *p = (RootObject *)tp->dummyArg(); - if (p) - (*ti.tiargs)[i] = p; - else - ti.tiargs->setDim(i); + if (!p) + break; + + ti.tiargs->push(p); } // Temporary Array to hold deduced types @@ -1035,7 +1127,8 @@ MATCH TemplateDeclaration::leastAsSpecialized(Scope *sc, TemplateDeclaration *td /* A non-variadic template is more specialized than a * variadic one. */ - if (isVariadic() && !td2->isVariadic()) + TemplateTupleParameter *tp = isVariadic(); + if (tp && !tp->dependent && !td2->isVariadic()) goto L1; #if LOG_LEASTAS @@ -1050,6 +1143,32 @@ MATCH TemplateDeclaration::leastAsSpecialized(Scope *sc, TemplateDeclaration *td return MATCHnomatch; } +class TypeDeduced : public Type +{ +public: + Type *tded; + Expressions argexps; // corresponding expressions + Types tparams; // tparams[i]->mod + + TypeDeduced(Type *tt, Expression *e, Type *tparam) + : Type(Tnone) + { + tded = tt; + argexps.push(e); + tparams.push(tparam); + } + void update(Expression *e, Type *tparam) + { + argexps.push(e); + tparams.push(tparam); + } + void update(Type *tt, Expression *e, Type *tparam) + { + tded = tt; + argexps.push(e); + tparams.push(tparam); + } +}; /************************************************* * Match function arguments against a specific template function. @@ -1081,13 +1200,13 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( MATCH matchTiargs = MATCHexact; Parameters *fparameters; // function parameter list int fvarargs; // function varargs - Objects dedtypes; // for T:T*, the dedargs is the T*, dedtypes is the T unsigned wildmatch = 0; - TemplateParameters *inferparams = parameters; + size_t inferStart = 0; Loc loc = ti->loc; Objects *tiargs = ti->tiargs; - Objects *dedargs = &ti->tdtypes; + Objects *dedargs = new Objects(); + Objects* dedtypes = &ti->tdtypes; // for T:T*, the dedargs is the T*, dedtypes is the T #if 0 printf("\nTemplateDeclaration::deduceFunctionTemplateMatch() %s\n", toChars()); @@ -1107,8 +1226,8 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( dedargs->setDim(parameters->dim); dedargs->zero(); - dedtypes.setDim(parameters->dim); - dedtypes.zero(); + dedtypes->setDim(parameters->dim); + dedtypes->zero(); if (errors || fd->errors) return MATCHnomatch; @@ -1173,7 +1292,7 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( { assert(i < parameters->dim); Declaration *sparam = NULL; - MATCH m = (*parameters)[i]->matchArg(loc, paramscope, dedargs, i, parameters, &dedtypes, &sparam); + MATCH m = (*parameters)[i]->matchArg(loc, paramscope, dedargs, i, parameters, dedtypes, &sparam); //printf("\tdeduceType m = %d\n", m); if (m <= MATCHnomatch) goto Lnomatch; @@ -1186,14 +1305,10 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( } if (n < parameters->dim && !tp_is_declared) { - inferparams = new TemplateParameters(); - inferparams->setDim(parameters->dim - n); - memcpy(inferparams->tdata(), - parameters->tdata() + n, - inferparams->dim * sizeof(*inferparams->tdata())); + inferStart = n; } else - inferparams = NULL; + inferStart = parameters->dim; //printf("tiargs matchTiargs = %d\n", matchTiargs); } #if 0 @@ -1259,6 +1374,13 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( } } +#if BUGZILLA_11946 + if (isstatic) + tthis = NULL; +#else + if (toParent()->isModule() || (scope->stc & STCstatic)) + tthis = NULL; +#endif if (tthis) { bool hasttp = false; @@ -1272,7 +1394,7 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( hasttp = true; Type *t = new TypeIdentifier(Loc(), ttp->ident); - MATCH m = tthis->deduceType(paramscope, t, parameters, &dedtypes); + MATCH m = deduceType(tthis, paramscope, t, parameters, dedtypes); if (m <= MATCHnomatch) goto Lnomatch; if (m < match) @@ -1352,7 +1474,7 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( for (size_t j = parami + 1; j < nfparams; j++) { Parameter *p = Parameter::getNth(fparameters, j); - if (!inferparams || !p->type->reliesOnTident(inferparams)) + if (!reliesOnTident(p->type, parameters, inferStart)) { Type *pt = p->type->syntaxCopy()->semantic(fd->loc, paramscope); rem += pt->ty == Ttuple ? ((TypeTuple *)pt)->arguments->dim : 1; @@ -1380,23 +1502,15 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( Type *tt; MATCH m; - - if (tid->mod & MODwild) + if (unsigned char wm = deduceWildHelper(farg->type, &tt, tid)) { - unsigned wm = farg->type->deduceWildHelper(&tt, tid); - if (wm) - { - wildmatch |= wm; - m = MATCHconst; - goto Lx; - } + wildmatch |= wm; + m = MATCHconst; + } + else + { + m = deduceTypeHelper(farg->type, &tt, tid); } - - m = farg->type->deduceTypeHelper(&tt, tid); - if (!m) - goto Lnomatch; - - Lx: if (m <= MATCHnomatch) goto Lnomatch; if (m < match) @@ -1421,7 +1535,7 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( // If parameter type doesn't depend on inferred template parameters, // semantic it to get actual type. - if (!inferparams || !prmtype->reliesOnTident(inferparams)) + if (!reliesOnTident(prmtype, parameters, inferStart)) { // should copy prmtype to avoid affecting semantic result prmtype = prmtype->syntaxCopy()->semantic(fd->loc, paramscope); @@ -1477,71 +1591,58 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( #endif Type *argtype = farg->type; - /* Allow expressions that have CT-known boundaries and type [] to match with [dim] - */ - Type *taai; - if ( argtype->ty == Tarray && - (prmtype->ty == Tsarray || - prmtype->ty == Taarray && (taai = ((TypeAArray *)prmtype)->index)->ty == Tident && - ((TypeIdentifier *)taai)->idents.dim == 0)) + if (!(fparam->storageClass & STClazy) && argtype->ty == Tvoid && farg->op != TOKfunction) + goto Lnomatch; + + // Bugzilla 12876: optimize arugument to allow CT-known length matching + farg = farg->optimize(WANTvalue, (fparam->storageClass & (STCref | STCout)) != 0); + //printf("farg = %s %s\n", farg->type->toChars(), farg->toChars()); + + RootObject *oarg = farg; + if ((fparam->storageClass & STCref) && + (!(fparam->storageClass & STCauto) || farg->isLvalue())) { - if (farg->op == TOKstring) - { - StringExp *se = (StringExp *)farg; - argtype = argtype->nextOf()->sarrayOf(se->len); - } - else if (farg->op == TOKslice) - { - SliceExp *se = (SliceExp *)farg; - Type *tsa = se->toStaticArrayType(); - if (tsa) - argtype = tsa; - } - else if (farg->op == TOKarrayliteral) + /* Allow expressions that have CT-known boundaries and type [] to match with [dim] + */ + Type *taai; + if ( argtype->ty == Tarray && + (prmtype->ty == Tsarray || + prmtype->ty == Taarray && (taai = ((TypeAArray *)prmtype)->index)->ty == Tident && + ((TypeIdentifier *)taai)->idents.dim == 0)) { - ArrayLiteralExp *ae = (ArrayLiteralExp *)farg; - argtype = argtype->nextOf()->sarrayOf(ae->elements->dim); + if (farg->op == TOKstring) + { + StringExp *se = (StringExp *)farg; + argtype = se->type->nextOf()->sarrayOf(se->len); + } + else if (farg->op == TOKarrayliteral) + { + ArrayLiteralExp *ae = (ArrayLiteralExp *)farg; + argtype = ae->type->nextOf()->sarrayOf(ae->elements->dim); + } + else if (farg->op == TOKslice) + { + SliceExp *se = (SliceExp *)farg; + if (Type *tsa = toStaticArrayType(se)) + argtype = tsa; + } } - } - /* Allow implicit function literals to delegate conversion - */ - if (farg->op == TOKfunction) - { - FuncExp *fe = (FuncExp *)farg; - Expression *e = fe->inferType(prmtype, 1, paramscope, inferparams); - if (!e) - goto Lvarargs; - farg = e; - argtype = farg->type; - } - - if (!(fparam->storageClass & STClazy) && argtype->ty == Tvoid) - goto Lnomatch; - - /* Remove top const for dynamic array types and pointer types - */ - if ((argtype->ty == Tarray || argtype->ty == Tpointer) && - !argtype->isMutable() && - (!(fparam->storageClass & STCref) || - (fparam->storageClass & STCauto) && !farg->isLvalue())) - { - argtype = argtype->mutableOf(); + oarg = argtype; } if (fvarargs == 2 && parami + 1 == nfparams && argi + 1 < nfargs) goto Lvarargs; unsigned wm = 0; - MATCH m = argtype->deduceType(paramscope, prmtype, parameters, &dedtypes, &wm); - //printf("\tdeduceType m = %d\n", m); - //printf("\twildmatch = x%x m = %d\n", wildmatch, m); + MATCH m = deduceType(oarg, paramscope, prmtype, parameters, dedtypes, &wm, inferStart); + //printf("\tL%d deduceType m = %d, wm = x%x, wildmatch = x%x\n", __LINE__, m, wm, wildmatch); wildmatch |= wm; /* If no match, see if the argument can be matched by using * implicit conversions. */ - if (m == MATCHnomatch) + if (m == MATCHnomatch && prmtype->deco) m = farg->implicitConvTo(prmtype); /* If no match, see if there's a conversion to a delegate @@ -1557,7 +1658,7 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( if (!tf->varargs && Parameter::dim(tf->parameters) == 0) { - m = farg->type->deduceType(paramscope, tf->next, parameters, &dedtypes); + m = deduceType(farg->type, paramscope, tf->next, parameters, dedtypes); if (m == MATCHnomatch && tf->next->toBasetype()->ty == Tvoid) m = MATCHconvert; } @@ -1585,10 +1686,8 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( { if (!farg->isLvalue()) { - if (farg->op == TOKstring && argtype->ty == Tsarray) - { - } - else if (farg->op == TOKslice && argtype->ty == Tsarray) + if ((farg->op == TOKstring || farg->op == TOKslice) && + (prmtype->ty == Tsarray || prmtype->ty == Taarray)) { // Allow conversion from T[lwr .. upr] to ref T[upr-lwr] } @@ -1665,7 +1764,7 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( TemplateValueParameter *tvp = tprm->isTemplateValueParameter(); if (!tvp) goto Lnomatch; - Expression *e = (Expression *)dedtypes[i]; + Expression *e = (Expression *)(*dedtypes)[i]; if (e) { if (!dim->equals(e)) @@ -1677,7 +1776,7 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( MATCH m = (MATCH)dim->implicitConvTo(vt); if (m <= MATCHnomatch) goto Lnomatch; - dedtypes[i] = dim; + (*dedtypes)[i] = dim; } } } @@ -1686,25 +1785,16 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( case Tarray: { TypeArray *ta = (TypeArray *)tb; + Type *tret = fparam->isLazyArray(); for (; argi < nfargs; argi++) { Expression *arg = (*fargs)[argi]; assert(arg); - if (arg->op == TOKfunction) - { - FuncExp *fe = (FuncExp *)arg; - Expression *e = fe->inferType(tb->nextOf(), 1, paramscope, inferparams); - if (!e) - goto Lnomatch; - arg = e; - } - MATCH m; /* If lazy array of delegates, * convert arg(s) to delegate(s) */ - Type *tret = fparam->isLazyArray(); if (tret) { if (ta->next->equals(arg->type)) @@ -1723,8 +1813,9 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( } else { - m = arg->type->deduceType(paramscope, ta->next, parameters, &dedtypes); - //m = arg->implicitConvTo(ta->next); + unsigned wm = 0; + m = deduceType(arg, paramscope, ta->next, parameters, dedtypes, &wm, inferStart); + wildmatch |= wm; } if (m == MATCHnomatch) goto Lnomatch; @@ -1749,6 +1840,28 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( Lmatch: + for (size_t i = 0; i < dedtypes->dim; i++) + { + Type *at = isType((*dedtypes)[i]); + if (at && at->ty == Tnone) + { + TypeDeduced *xt = (TypeDeduced *)at; + Type *tt = xt->tded; // 'unbox' + + bool iswild = true; + for (size_t j = 0; iswild && j < xt->tparams.dim; j++) + iswild = iswild && xt->tparams[j]->isWild(); + if (iswild) + tt = tt->unqualify(MODimmutable | MODconst); + + // Remove top-const + if (tt->ty == Tarray || tt->ty == Tpointer) + tt = tt->mutableOf(); + + (*dedtypes)[i] = tt; + delete xt; + } + } for (size_t i = ntargs; i < dedargs->dim; i++) { TemplateParameter *tparam = (*parameters)[i]; @@ -1757,7 +1870,7 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( * But for function templates, we really need them to match */ RootObject *oarg = (*dedargs)[i]; - RootObject *oded = dedtypes[i]; + RootObject *oded = (*dedtypes)[i]; //printf("1dedargs[%d] = %p, dedtypes[%d] = %p\n", i, oarg, i, oded); //if (oarg) printf("oarg: %s\n", oarg->toChars()); //if (oded) printf("oded: %s\n", oded->toChars()); @@ -1771,13 +1884,13 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( * the oded == oarg */ (*dedargs)[i] = oded; - MATCH m2 = tparam->matchArg(loc, paramscope, dedargs, i, parameters, &dedtypes, NULL); + MATCH m2 = tparam->matchArg(loc, paramscope, dedargs, i, parameters, dedtypes, NULL); //printf("m2 = %d\n", m2); if (m2 <= MATCHnomatch) goto Lnomatch; if (m2 < matchTiargs) matchTiargs = m2; // pick worst match - if (dedtypes[i] != oded) + if (!(*dedtypes)[i]->equals(oded)) error("specialization not allowed for deduced parameter %s", tparam->ident->toChars()); } else @@ -1791,9 +1904,12 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( oded = tparam->defaultArg(loc, paramscope); if (!oded) { - if (tp && // if tuple parameter and - fptupindex == IDX_NOTFOUND && // tuple parameter was not in function parameter list and - ntargs == dedargs->dim - 1) // we're one argument short (i.e. no tuple argument) + // if tuple parameter and + // tuple parameter was not in function parameter list and + // we're one argument short (i.e. no tuple argument) + if (tp && + fptupindex == IDX_NOTFOUND && + ntargs == dedargs->dim - 1) { // make tuple argument an empty tuple oded = (RootObject *)new Tuple(); @@ -1825,6 +1941,7 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( if (!fd) goto Lnomatch; } + ti->tiargs = dedargs; if (constraint) { if (!evaluateConstraint(ti, sc, paramscope, dedargs, fd)) @@ -1838,7 +1955,6 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch( printf("\tdedargs[%d] = %d, %s\n", i, t->dyncast(), t->toChars()); } #endif - ti->tiargs = &ti->tdtypes; // for better error message paramscope->pop(); //printf("\tmatch %d\n", match); @@ -1929,7 +2045,8 @@ RootObject *TemplateDeclaration::declareParameter(Scope *sc, TemplateParameter * */ TemplateTupleParameter *isVariadic(TemplateParameters *parameters) -{ size_t dim = parameters->dim; +{ + size_t dim = parameters->dim; TemplateTupleParameter *tp = NULL; if (dim) @@ -2049,8 +2166,9 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, // tf->mod, tthis_fd->mod, fd->isolateReturn()); if (MODimplicitConv(tf->mod, tthis_fd->mod) || tf->isWild() && tf->isShared() == tthis_fd->isShared() || - fd->isolateReturn()/* && tf->isShared() == tthis_fd->isShared()*/) + fd->isolateReturn()) { + /* && tf->isShared() == tthis_fd->isShared()*/ // Uniquely constructed object can ignore shared qualifier. // TODO: Is this appropriate? tthis_fd = NULL; @@ -2099,7 +2217,6 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, if (!fd->fbody && m->lastf->fbody) goto LlastIsBetter; } - Lambiguous: m->nextf = fd; m->count++; return 0; @@ -2147,6 +2264,8 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, m->last = MATCHnomatch; return 1; } + //printf("td = %s\n", td->toChars()); + FuncDeclaration *f; f = td->onemember ? td->onemember->isFuncDeclaration() : NULL; if (!f) @@ -2230,6 +2349,22 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, if (mfa < m->last) return 0; + if (mta < ta_last) goto Ltd_best2; + if (mta > ta_last) goto Ltd2; + + if (mfa < m->last) goto Ltd_best2; + if (mfa > m->last) goto Ltd2; + + Lambig2: // td_best and td are ambiguous + //printf("Lambig2\n"); + m->nextf = fd; + m->count++; + return 0; + + Ltd_best2: + return 0; + + Ltd2: // td is the new best match assert(td->scope); td_best = td; @@ -2251,6 +2386,8 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, if (f->type->ty != Tfunction || f->errors) goto Lerror; + /* This is a 'dummy' instance to evaluate constraint properly. + */ TemplateInstance *ti = new TemplateInstance(loc, td, tiargs); ti->tinst = td->getInstantiating(sc); if (ti->tinst) @@ -2258,8 +2395,6 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, else ti->instantiatingModule = sc->instantiatingModule(); ti->parent = td->parent; // Maybe calculating valid 'enclosing' is unnecessary. - ti->semantictiargsdone = true; - ti->symtab = new DsymbolTable(); FuncDeclaration *fd = f; int x = td->deduceFunctionTemplateMatch(ti, sc, fd, tthis, fargs); @@ -2329,8 +2464,6 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, if (c1 < c2) goto Ltd_best; } - Lambig: // td_best and td are ambiguous - //printf("Lambig\n"); m->nextf = fd; m->count++; continue; @@ -2374,11 +2507,27 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, p.ta_last = m->last != MATCHnomatch ? MATCHexact : MATCHnomatch; p.tthis_best = NULL; - FuncDeclaration *fd = dstart->isFuncDeclaration(); TemplateDeclaration *td = dstart->isTemplateDeclaration(); if (td && td->funcroot) dstart = td->funcroot; + + unsigned errors = global.errors; overloadApply(dstart, &p, &ParamDeduce::fp); + if (global.errors != errors) + { + Lerror: + static FuncDeclaration *errorFunc = NULL; + if (errorFunc == NULL) + { + errorFunc = new FuncDeclaration(Loc(), Loc(), Id::empty, STCundefined, NULL); + errorFunc->type = Type::terror; + errorFunc->errors = true; + } + m->count = 1; + m->lastf = errorFunc; + m->last = MATCHnomatch; + return; + } //printf("td_best = %p, m->lastf = %p\n", p.td_best, m->lastf); if (p.td_best && p.ti_best) @@ -2393,7 +2542,7 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, assert(p.td_best->scope); if (!sc) sc = p.td_best->scope; // workaround for Type::aliasthisOf - TemplateInstance *ti = new TemplateInstance(loc, p.td_best, &p.ti_best->tdtypes); // TODO? + TemplateInstance *ti = new TemplateInstance(loc, p.td_best, p.ti_best->tiargs); ti->semantic(sc, fargs); m->lastf = ti->toAlias()->isFuncDeclaration(); @@ -2417,9 +2566,10 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, assert(tf->ty == Tfunction); if (!tf->callMatch(p.tthis_best, fargs)) { - m->lastf = NULL; m->count = 0; - goto Lerror; + m->lastf = NULL; + m->last = MATCHnomatch; + return; } if (FuncLiteralDeclaration *fld = m->lastf->isFuncLiteralDeclaration()) @@ -2454,8 +2604,6 @@ void functionResolve(Match *m, Dsymbol *dstart, Loc loc, Scope *sc, } else { - Lerror: - // Keep m->lastf and m->count as-is. m->last = MATCHnomatch; } } @@ -2495,7 +2643,10 @@ FuncDeclaration *TemplateDeclaration::doHeaderInstantiation( hasttp = true; } if (hasttp) - tf = (TypeFunction *)tf->addMod(tthis->mod); + { + tf = (TypeFunction *)tf->addSTC(ModToStc(tthis->mod)); + assert(!tf->deco); + } } Scope *scx = sc2->push(); @@ -2518,7 +2669,7 @@ FuncDeclaration *TemplateDeclaration::doHeaderInstantiation( } else { - tret = ad->handle; + tret = ad->handleType(); assert(tret); tret = tret->addStorageClass(fd->storage_class | scx->stc); tret = tret->addMod(tf->mod); @@ -2533,6 +2684,7 @@ FuncDeclaration *TemplateDeclaration::doHeaderInstantiation( fd->type = tf; fd->type = fd->type->addSTC(scx->stc); fd->type = fd->type->semantic(fd->loc, scx); + fd->originalType = fd->type; // for mangling //printf("\t[%s] fd->type = %s, mod = %x, ", loc.toChars(), fd->type->toChars(), fd->type->mod); //printf("fd->needThis() = %d\n", fd->needThis()); @@ -2557,8 +2709,8 @@ void TemplateDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) FuncDeclaration *fd = (*members)[0]->isFuncDeclaration(); if (fd && fd->type && fd->type->ty == Tfunction && fd->ident == ident) { - TypeFunction *tf = (TypeFunction *)fd->type; - tf->toCBufferWithAttributes(buf, ident, hgs, tf, this); + StorageClassDeclaration::stcToCBuffer(buf, fd->storage_class); + functionToBufferFull((TypeFunction *)fd->type, buf, ident, hgs, this); if (constraint) { @@ -2662,7 +2814,7 @@ void TemplateDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { hgs->tpltMember++; buf->writenl(); - buf->writebyte('{'); + buf->writeByte('{'); buf->writenl(); buf->level++; for (size_t i = 0; i < members->dim; i++) @@ -2671,7 +2823,7 @@ void TemplateDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) s->toCBuffer(buf, hgs); } buf->level--; - buf->writebyte('}'); + buf->writeByte('}'); buf->writenl(); hgs->tpltMember--; } @@ -2715,8 +2867,7 @@ char *TemplateDeclaration::toChars() constraint->toCBuffer(&buf, &hgs); buf.writeByte(')'); } - buf.writeByte(0); - return (char *)buf.extractData(); + return buf.extractString(); } PROT TemplateDeclaration::prot() @@ -2772,7 +2923,8 @@ TemplateInstance *TemplateDeclaration::addInstance(TemplateInstance *ti) /* See if we need to rehash */ if (numinstances > buckets.dim * 4) - { // rehash + { + // rehash //printf("rehash\n"); size_t newdim = buckets.dim * 2 + 1; TemplateInstances **newp = (TemplateInstances **)::calloc(newdim, sizeof(TemplateInstances *)); @@ -2823,7 +2975,8 @@ void TemplateDeclaration::removeInstance(TemplateInstance *handle) { TemplateInstance *ti = (*instances)[i]; if (handle == ti) - { instances->remove(i); + { + instances->remove(i); break; } } @@ -2855,8 +3008,8 @@ TemplateInstance *TemplateDeclaration::getInstantiating(Scope *sc) size_t templateIdentifierLookup(Identifier *id, TemplateParameters *parameters) { for (size_t i = 0; i < parameters->dim; i++) - { TemplateParameter *tp = (*parameters)[i]; - + { + TemplateParameter *tp = (*parameters)[i]; if (tp->ident->equals(id)) return i; } @@ -2874,13 +3027,15 @@ size_t templateParameterLookup(Type *tparam, TemplateParameters *parameters) return IDX_NOTFOUND; } -unsigned Type::deduceWildHelper(Type **at, Type *tparam) +unsigned char deduceWildHelper(Type *t, Type **at, Type *tparam) { - assert(tparam->mod & MODwild); + if ((tparam->mod & MODwild) == 0) + return 0; + *at = NULL; #define X(U,T) ((U) << 4) | (T) - switch (X(tparam->mod, mod)) + switch (X(tparam->mod, t->mod)) { case X(MODwild, 0): case X(MODwild, MODconst): @@ -2899,11 +3054,11 @@ unsigned Type::deduceWildHelper(Type **at, Type *tparam) case X(MODshared | MODwildconst, MODshared | MODconst): case X(MODshared | MODwildconst, MODimmutable): { - unsigned wm = (mod & ~MODshared); + unsigned char wm = (t->mod & ~MODshared); if (wm == 0) wm = MODmutable; - unsigned m = (mod & (MODconst | MODimmutable)) | (tparam->mod & mod & MODshared); - *at = unqualify(m); + unsigned char m = (t->mod & (MODconst | MODimmutable)) | (tparam->mod & t->mod & MODshared); + *at = t->unqualify(m); return wm; } @@ -2920,7 +3075,7 @@ unsigned Type::deduceWildHelper(Type **at, Type *tparam) case X(MODshared | MODwildconst, MODshared | MODwild): case X(MODshared | MODwildconst, MODshared | MODwildconst): { - *at = unqualify(tparam->mod & mod); + *at = t->unqualify(tparam->mod & t->mod); return MODwild; } @@ -2930,12 +3085,12 @@ unsigned Type::deduceWildHelper(Type **at, Type *tparam) #undef X } -MATCH Type::deduceTypeHelper(Type **at, Type *tparam) +MATCH deduceTypeHelper(Type *t, Type **at, Type *tparam) { // 9*9 == 81 cases #define X(U,T) ((U) << 4) | (T) - switch (X(tparam->mod, mod)) + switch (X(tparam->mod, t->mod)) { case X(0, 0): case X(0, MODconst): @@ -2956,7 +3111,7 @@ MATCH Type::deduceTypeHelper(Type **at, Type *tparam) // foo(U) shared(inout(const(T))) => shared(inout(const(T))) // foo(U) immutable(T) => immutable(T) { - *at = this; + *at = t; return MATCHexact; } @@ -2977,7 +3132,7 @@ MATCH Type::deduceTypeHelper(Type **at, Type *tparam) // foo(shared(inout(const(U)))) shared(inout(const(T))) => T // foo(immutable(U)) immutable(T) => T { - *at = mutableOf()->unSharedOf(); + *at = t->mutableOf()->unSharedOf(); return MATCHexact; } @@ -3002,14 +3157,14 @@ MATCH Type::deduceTypeHelper(Type **at, Type *tparam) // foo(inout(const(U))) shared(inout(const(T))) => shared(T) // foo(shared(const(U))) immutable(T) => T { - *at = mutableOf(); + *at = t->mutableOf(); return MATCHconst; } case X(MODconst, MODshared): // foo(const(U)) shared(T) => shared(T) { - *at = this; + *at = t; return MATCHconst; } @@ -3022,7 +3177,7 @@ MATCH Type::deduceTypeHelper(Type **at, Type *tparam) // foo(shared(U)) shared(inout(const(T))) => inout(const(T)) // foo(shared(const(U))) shared(T) => T { - *at = unSharedOf(); + *at = t->unSharedOf(); return MATCHconst; } @@ -3035,14 +3190,14 @@ MATCH Type::deduceTypeHelper(Type **at, Type *tparam) // foo(shared(inout(const(U)))) immutable(T) => T // foo(shared(inout(const(U)))) shared(inout(T)) => T { - *at = unSharedOf()->mutableOf(); + *at = t->unSharedOf()->mutableOf(); return MATCHconst; } case X(MODshared | MODconst, MODshared | MODwild): // foo(shared(const(U))) shared(inout(T)) => T { - *at = unSharedOf()->mutableOf(); + *at = t->unSharedOf()->mutableOf(); return MATCHconst; } @@ -3158,943 +3313,1549 @@ MATCH Type::deduceTypeHelper(Type **at, Type *tparam) * dedtypes = [ int ] // Array of Expression/Type's */ -MATCH Type::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, - Objects *dedtypes, unsigned *wm) +static Expression *emptyArrayElement = NULL; + +MATCH deduceType(RootObject *o, Scope *sc, Type *tparam, TemplateParameters *parameters, + Objects *dedtypes, unsigned *wm, size_t inferStart) { -#if 0 - printf("Type::deduceType()\n"); - printf("\tthis = %d, ", ty); print(); - printf("\ttparam = %d, ", tparam->ty); tparam->print(); -#endif - if (!tparam) - goto Lnomatch; + class DeduceType : public Visitor + { + public: + Scope *sc; + Type *tparam; + TemplateParameters *parameters; + Objects *dedtypes; + unsigned *wm; + size_t inferStart; + MATCH result; - if (this == tparam) - goto Lexact; + DeduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm, size_t inferStart) + : sc(sc), tparam(tparam), parameters(parameters), dedtypes(dedtypes), wm(wm), inferStart(inferStart) + { + result = MATCHnomatch; + } - if (tparam->ty == Tident) - { - // Determine which parameter tparam is - size_t i = templateParameterLookup(tparam, parameters); - if (i == IDX_NOTFOUND) + void visit(Type *t) { - if (!sc) + #if 0 + printf("Type::deduceType()\n"); + printf("\tthis = %d, ", t->ty); t->print(); + printf("\ttparam = %d, ", tparam->ty); tparam->print(); + #endif + if (!tparam) goto Lnomatch; - /* Need a loc to go with the semantic routine. - */ - Loc loc; - if (parameters->dim) + if (t == tparam) + goto Lexact; + + if (tparam->ty == Tident) { - TemplateParameter *tp = (*parameters)[0]; - loc = tp->loc; - } + // Determine which parameter tparam is + size_t i = templateParameterLookup(tparam, parameters); + if (i == IDX_NOTFOUND) + { + if (!sc) + goto Lnomatch; - /* BUG: what if tparam is a template instance, that - * has as an argument another Tident? - */ - tparam = tparam->semantic(loc, sc); - assert(tparam->ty != Tident); - return deduceType(sc, tparam, parameters, dedtypes, wm); - } + /* Need a loc to go with the semantic routine. + */ + Loc loc; + if (parameters->dim) + { + TemplateParameter *tp = (*parameters)[0]; + loc = tp->loc; + } - TemplateParameter *tp = (*parameters)[i]; + /* BUG: what if tparam is a template instance, that + * has as an argument another Tident? + */ + tparam = tparam->semantic(loc, sc); + assert(tparam->ty != Tident); + result = deduceType(t, sc, tparam, parameters, dedtypes, wm); + return; + } - TypeIdentifier *tident = (TypeIdentifier *)tparam; - if (tident->idents.dim > 0) - { - //printf("matching %s to %s\n", tparam->toChars(), toChars()); - Dsymbol *s = this->toDsymbol(sc); - for (size_t j = tident->idents.dim; j-- > 0; ) - { - RootObject *id = tident->idents[j]; - if (id->dyncast() == DYNCAST_IDENTIFIER) + TemplateParameter *tp = (*parameters)[i]; + + TypeIdentifier *tident = (TypeIdentifier *)tparam; + if (tident->idents.dim > 0) { - if (!s || !s->parent) - goto Lnomatch; - Dsymbol *s2 = s->parent->searchX(Loc(), sc, id); - if (!s2) - goto Lnomatch; - s2 = s2->toAlias(); - //printf("[%d] s = %s %s, s2 = %s %s\n", j, s->kind(), s->toChars(), s2->kind(), s2->toChars()); - if (s != s2) + //printf("matching %s to %s\n", tparam->toChars(), t->toChars()); + Dsymbol *s = t->toDsymbol(sc); + for (size_t j = tident->idents.dim; j-- > 0; ) { - if (Type *t = s2->getType()) + RootObject *id = tident->idents[j]; + if (id->dyncast() == DYNCAST_IDENTIFIER) { - if (s != t->toDsymbol(sc)) + if (!s || !s->parent) goto Lnomatch; + Dsymbol *s2 = s->parent->searchX(Loc(), sc, id); + if (!s2) + goto Lnomatch; + s2 = s2->toAlias(); + //printf("[%d] s = %s %s, s2 = %s %s\n", j, s->kind(), s->toChars(), s2->kind(), s2->toChars()); + if (s != s2) + { + if (Type *tx = s2->getType()) + { + if (s != tx->toDsymbol(sc)) + goto Lnomatch; + } + else + goto Lnomatch; + } + s = s->parent; } else goto Lnomatch; } - s = s->parent; - } - else + //printf("[e] s = %s\n", s?s->toChars():"(null)"); + if (tp->isTemplateTypeParameter()) + { + Type *tt = s->getType(); + if (!tt) + goto Lnomatch; + Type *at = (Type *)(*dedtypes)[i]; + if (at && at->ty == Tnone) + at = ((TypeDeduced *)at)->tded; + if (!at || tt->equals(at)) + { + (*dedtypes)[i] = tt; + goto Lexact; + } + } + if (tp->isTemplateAliasParameter()) + { + Dsymbol *s2 = (Dsymbol *)(*dedtypes)[i]; + if (!s2 || s == s2) + { + (*dedtypes)[i] = s; + goto Lexact; + } + } goto Lnomatch; - } - //printf("[e] s = %s\n", s?s->toChars():"(null)"); - if (TemplateTypeParameter *ttp = tp->isTemplateTypeParameter()) - { - Type *tt = s->getType(); - if (!tt) + } + + // Found the corresponding parameter tp + if (!tp->isTemplateTypeParameter()) goto Lnomatch; + Type *at = (Type *)(*dedtypes)[i]; - if (!at || tt->equals(at)) + Type *tt; + if (unsigned char wx = wm ? deduceWildHelper(t, &tt, tparam) : 0) { - (*dedtypes)[i] = tt; - goto Lexact; + if (!at) + { + (*dedtypes)[i] = tt; + *wm |= wx; + goto Lconst; + } + + if (at && at->ty == Tnone) // type vs expressions + { + TypeDeduced *xt = (TypeDeduced *)at; + at = xt->tded; + delete xt; + } + + if (tt->equals(at)) + { + (*dedtypes)[i] = tt; // Prefer current type match + goto Lconst; + } + if (tt->implicitConvTo(at->constOf())) + { + (*dedtypes)[i] = at->constOf()->mutableOf(); + *wm |= MODconst; + goto Lconst; + } + if (at->implicitConvTo(tt->constOf())) + { + (*dedtypes)[i] = tt->constOf()->mutableOf(); + *wm |= MODconst; + goto Lconst; + } + goto Lnomatch; + } + else if (MATCH m = deduceTypeHelper(t, &tt, tparam)) + { + // type vs (none) + if (!at) + { + (*dedtypes)[i] = tt; + if (m == MATCHexact) + goto Lexact; + else + goto Lconst; + } + + // type vs expressions + if (at->ty == Tnone) + { + TypeDeduced *xt = (TypeDeduced *)at; + result = MATCHexact; + for (size_t j = 0; j < xt->argexps.dim; j++) + { + Expression *e = xt->argexps[j]; + if (e == emptyArrayElement) + continue; + m = e->implicitConvTo(tt->addMod(xt->tparams[j]->mod)); + if (result > m) + result = m; + if (result <= MATCHnomatch) + break; + } + if (result > MATCHnomatch) + { + (*dedtypes)[i] = tt; + return; + } + + at = xt->tded; + } + + // type vs type + if (tt->equals(at)) + { + goto Lexact; + } + if (tt->ty == Tclass && at->ty == Tclass) + { + result = tt->implicitConvTo(at); + return; + } + if (tt->ty == Tsarray && at->ty == Tarray && + tt->nextOf()->implicitConvTo(at->nextOf()) >= MATCHconst) + { + goto Lexact; + } } + goto Lnomatch; } - if (TemplateAliasParameter *tap = tp->isTemplateAliasParameter()) + else if (tparam->ty == Ttypeof) { - Dsymbol *s2 = (Dsymbol *)(*dedtypes)[i]; - if (!s2 || s == s2) + /* Need a loc to go with the semantic routine. + */ + Loc loc; + if (parameters->dim) { - (*dedtypes)[i] = s; - goto Lexact; + TemplateParameter *tp = (*parameters)[0]; + loc = tp->loc; } - } - goto Lnomatch; - } - // Found the corresponding parameter tp - if (!tp->isTemplateTypeParameter()) - goto Lnomatch; - Type *tt; - Type *at = (Type *)(*dedtypes)[i]; + tparam = tparam->semantic(loc, sc); + } - if (wm && (tparam->mod & MODwild)) - { - unsigned wx = deduceWildHelper(&tt, tparam); - if (wx) + if (t->ty != tparam->ty) { - if (!at) + if (Dsymbol *sym = t->toDsymbol(sc)) { - (*dedtypes)[i] = tt; - *wm |= wx; - goto Lconst; + if (sym->isforwardRef() && !tparam->deco) + goto Lnomatch; } - if (tt->equals(at)) + MATCH m = t->implicitConvTo(tparam); + if (m == MATCHnomatch) { - goto Lconst; + if (t->ty == Tclass) + { + TypeClass *tc = (TypeClass *)t; + if (tc->sym->aliasthis && !(tc->att & RECtracingDT)) + { + tc->att = (AliasThisRec)(tc->att | RECtracingDT); + m = deduceType(t->aliasthisOf(), sc, tparam, parameters, dedtypes, wm); + tc->att = (AliasThisRec)(tc->att & ~RECtracingDT); + } + } + else if (t->ty == Tstruct) + { + TypeStruct *ts = (TypeStruct *)t; + if (ts->sym->aliasthis && !(ts->att & RECtracingDT)) + { + ts->att = (AliasThisRec)(ts->att | RECtracingDT); + m = deduceType(t->aliasthisOf(), sc, tparam, parameters, dedtypes, wm); + ts->att = (AliasThisRec)(ts->att & ~RECtracingDT); + } + } } - if (tt->implicitConvTo(at->constOf())) + result = m; + return; + } + + if (t->nextOf()) + { + if (tparam->deco && !tparam->hasWild()) { - (*dedtypes)[i] = at->constOf()->mutableOf(); - *wm |= MODconst; - goto Lconst; + result = t->implicitConvTo(tparam); + return; } - if (at->implicitConvTo(tt->constOf())) + + Type *tpn = tparam->nextOf(); + if (wm && t->ty == Taarray && tparam->isWild()) { - (*dedtypes)[i] = tt->constOf()->mutableOf(); - *wm |= MODconst; - goto Lconst; + // Bugzilla 12403: In IFTI, stop inout matching on transitive part of AA types. + tpn = tpn->substWildTo(MODmutable); } - goto Lnomatch; + + result = deduceType(t->nextOf(), sc, tpn, parameters, dedtypes, wm); + return; } + + Lexact: + result = MATCHexact; + return; + + Lnomatch: + result = MATCHnomatch; + return; + + Lconst: + result = MATCHconst; } - MATCH m = deduceTypeHelper(&tt, tparam); - if (m) + void visit(TypeVector *t) { - if (!at) - { - (*dedtypes)[i] = tt; - if (m == MATCHexact) - goto Lexact; - else - goto Lconst; - } - - if (tt->equals(at)) - { - goto Lexact; - } - if (tt->ty == Tclass && at->ty == Tclass) + #if 0 + printf("TypeVector::deduceType()\n"); + printf("\tthis = %d, ", t->ty); t->print(); + printf("\ttparam = %d, ", tparam->ty); tparam->print(); + #endif + if (tparam->ty == Tvector) { - return tt->implicitConvTo(at); - } - if (tt->ty == Tsarray && at->ty == Tarray && - tt->nextOf()->implicitConvTo(at->nextOf()) >= MATCHconst) - { - goto Lexact; + TypeVector *tp = (TypeVector *)tparam; + result = deduceType(t->basetype, sc, tp->basetype, parameters, dedtypes, wm); + return; } + visit((Type *)t); } - goto Lnomatch; - } - else if (tparam->ty == Ttypeof) - { - /* Need a loc to go with the semantic routine. - */ - Loc loc; - if (parameters->dim) - { - TemplateParameter *tp = (*parameters)[0]; - loc = tp->loc; - } - - tparam = tparam->semantic(loc, sc); - } - if (ty != tparam->ty) - { - if (Dsymbol *sym = toDsymbol(sc)) + void visit(TypeDArray *t) { - if (sym->isforwardRef() && !tparam->deco) - goto Lnomatch; + #if 0 + printf("TypeDArray::deduceType()\n"); + printf("\tthis = %d, ", t->ty); t->print(); + printf("\ttparam = %d, ", tparam->ty); tparam->print(); + #endif + visit((Type *)t); } - // Can't instantiate AssociativeArray!() without a scope - if (tparam->ty == Taarray && !((TypeAArray*)tparam)->sc) - ((TypeAArray*)tparam)->sc = sc; - - MATCH m = implicitConvTo(tparam); - if (m == MATCHnomatch) + void visit(TypeSArray *t) { - if (ty == Tclass) + #if 0 + printf("TypeSArray::deduceType()\n"); + printf("\tthis = %d, ", t->ty); t->print(); + printf("\ttparam = %d, ", tparam->ty); tparam->print(); + #endif + + // Extra check that array dimensions must match + if (tparam) { - TypeClass *tc = (TypeClass *)this; - if (tc->sym->aliasthis && !(tc->att & RECtracingDT)) + if (tparam->ty == Tarray) { - tc->att = (AliasThisRec)(tc->att | RECtracingDT); - m = aliasthisOf()->deduceType(sc, tparam, parameters, dedtypes, wm); - tc->att = (AliasThisRec)(tc->att & ~RECtracingDT); + MATCH m = deduceType(t->next, sc, tparam->nextOf(), parameters, dedtypes, wm); + result = (m >= MATCHconst) ? MATCHconvert : MATCHnomatch; + return; + } + + TemplateParameter *tp = NULL; + Expression *edim = NULL; + size_t i; + if (tparam->ty == Tsarray) + { + TypeSArray *tsa = (TypeSArray *)tparam; + if (tsa->dim->op == TOKvar && + ((VarExp *)tsa->dim)->var->storage_class & STCtemplateparameter) + { + Identifier *id = ((VarExp *)tsa->dim)->var->ident; + i = templateIdentifierLookup(id, parameters); + assert(i != IDX_NOTFOUND); + tp = (*parameters)[i]; + } + else + edim = tsa->dim; + } + else if (tparam->ty == Taarray) + { + TypeAArray *taa = (TypeAArray *)tparam; + i = templateParameterLookup(taa->index, parameters); + if (i != IDX_NOTFOUND) + tp = (*parameters)[i]; + else + { + Expression *e; + Type *tx; + Dsymbol *s; + taa->index->resolve(Loc(), sc, &e, &tx, &s); + edim = s ? getValue(s) : getValue(e); + } + } + if (tp && tp->matchArg(sc, t->dim, i, parameters, dedtypes, NULL) || + edim && edim->toInteger() == t->dim->toInteger()) + { + result = deduceType(t->next, sc, tparam->nextOf(), parameters, dedtypes, wm); + return; } } - else if (ty == Tstruct) + visit((Type *)t); + return; + + result = MATCHnomatch; + } + + void visit(TypeAArray *t) + { + #if 0 + printf("TypeAArray::deduceType()\n"); + printf("\tthis = %d, ", t->ty); t->print(); + printf("\ttparam = %d, ", tparam->ty); tparam->print(); + #endif + + // Extra check that index type must match + if (tparam && tparam->ty == Taarray) { - TypeStruct *ts = (TypeStruct *)this; - if (ts->sym->aliasthis && !(ts->att & RECtracingDT)) + TypeAArray *tp = (TypeAArray *)tparam; + if (!deduceType(t->index, sc, tp->index, parameters, dedtypes)) { - ts->att = (AliasThisRec)(ts->att | RECtracingDT); - m = aliasthisOf()->deduceType(sc, tparam, parameters, dedtypes, wm); - ts->att = (AliasThisRec)(ts->att & ~RECtracingDT); + result = MATCHnomatch; + return; } } + visit((Type *)t); } - return m; - } - if (nextOf()) - { - if (tparam->deco && !tparam->hasWild()) - return implicitConvTo(tparam); + void visit(TypeFunction *t) + { + //printf("TypeFunction::deduceType()\n"); + //printf("\tthis = %d, ", t->ty); t->print(); + //printf("\ttparam = %d, ", tparam->ty); tparam->print(); - return nextOf()->deduceType(sc, tparam->nextOf(), parameters, dedtypes, wm); - } + // Extra check that function characteristics must match + if (tparam && tparam->ty == Tfunction) + { + TypeFunction *tp = (TypeFunction *)tparam; + if (t->varargs != tp->varargs || + t->linkage != tp->linkage) + { + result = MATCHnomatch; + return; + } -Lexact: - return MATCHexact; + size_t nfargs = Parameter::dim(t->parameters); + size_t nfparams = Parameter::dim(tp->parameters); -Lnomatch: - return MATCHnomatch; + // bug 2579 fix: Apply function parameter storage classes to parameter types + for (size_t i = 0; i < nfparams; i++) + { + Parameter *fparam = Parameter::getNth(tp->parameters, i); + fparam->type = fparam->type->addStorageClass(fparam->storageClass); + fparam->storageClass &= ~(STC_TYPECTOR | STCin); + } + //printf("\t-> this = %d, ", ty); print(); + //printf("\t-> tparam = %d, ", tparam->ty); tparam->print(); -Lconst: - return MATCHconst; -} + /* See if tuple match + */ + if (nfparams > 0 && nfargs >= nfparams - 1) + { + /* See if 'A' of the template parameter matches 'A' + * of the type of the last function parameter. + */ + Parameter *fparam = Parameter::getNth(tp->parameters, nfparams - 1); + assert(fparam); + assert(fparam->type); + if (fparam->type->ty != Tident) + goto L1; + TypeIdentifier *tid = (TypeIdentifier *)fparam->type; + if (tid->idents.dim) + goto L1; -MATCH TypeVector::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, - Objects *dedtypes, unsigned *wm) -{ -#if 0 - printf("TypeVector::deduceType()\n"); - printf("\tthis = %d, ", ty); print(); - printf("\ttparam = %d, ", tparam->ty); tparam->print(); -#endif - if (tparam->ty == Tvector) - { - TypeVector *tp = (TypeVector *)tparam; - return basetype->deduceType(sc, tp->basetype, parameters, dedtypes, wm); - } - return Type::deduceType(sc, tparam, parameters, dedtypes, wm); -} + /* Look through parameters to find tuple matching tid->ident + */ + size_t tupi = 0; + for (; 1; tupi++) + { + if (tupi == parameters->dim) + goto L1; + TemplateParameter *tx = (*parameters)[tupi]; + TemplateTupleParameter *tup = tx->isTemplateTupleParameter(); + if (tup && tup->ident->equals(tid->ident)) + break; + } -MATCH TypeDArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, - Objects *dedtypes, unsigned *wm) -{ -#if 0 - printf("TypeDArray::deduceType()\n"); - printf("\tthis = %d, ", ty); print(); - printf("\ttparam = %d, ", tparam->ty); tparam->print(); -#endif - return Type::deduceType(sc, tparam, parameters, dedtypes, wm); -} + /* The types of the function arguments [nfparams - 1 .. nfargs] + * now form the tuple argument. + */ + size_t tuple_dim = nfargs - (nfparams - 1); -MATCH TypeSArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, - Objects *dedtypes, unsigned *wm) -{ -#if 0 - printf("TypeSArray::deduceType()\n"); - printf("\tthis = %d, ", ty); print(); - printf("\ttparam = %d, ", tparam->ty); tparam->print(); -#endif + /* See if existing tuple, and whether it matches or not + */ + RootObject *o = (*dedtypes)[tupi]; + if (o) + { + // Existing deduced argument must be a tuple, and must match + Tuple *tup = isTuple(o); + if (!tup || tup->objects.dim != tuple_dim) + { + result = MATCHnomatch; + return; + } + for (size_t i = 0; i < tuple_dim; i++) + { + Parameter *arg = Parameter::getNth(t->parameters, nfparams - 1 + i); + if (!arg->type->equals(tup->objects[i])) + { + result = MATCHnomatch; + return; + } + } + } + else + { + // Create new tuple + Tuple *tup = new Tuple(); + tup->objects.setDim(tuple_dim); + for (size_t i = 0; i < tuple_dim; i++) + { + Parameter *arg = Parameter::getNth(t->parameters, nfparams - 1 + i); + tup->objects[i] = arg->type; + } + (*dedtypes)[tupi] = tup; + } + nfparams--; // don't consider the last parameter for type deduction + goto L2; + } - // Extra check that array dimensions must match - if (tparam) - { - if (tparam->ty == Tarray) - { - MATCH m = next->deduceType(sc, tparam->nextOf(), parameters, dedtypes, wm); - if (m == MATCHexact) - m = MATCHconvert; - return m; + L1: + if (nfargs != nfparams) + { + result = MATCHnomatch; + return; + } + L2: + for (size_t i = 0; i < nfparams; i++) + { + Parameter *a = Parameter::getNth(t->parameters, i); + Parameter *ap = Parameter::getNth(tp->parameters, i); + if (a->storageClass != ap->storageClass || + !deduceType(a->type, sc, ap->type, parameters, dedtypes)) + { + result = MATCHnomatch; + return; + } + } + } + visit((Type *)t); } - Identifier *id = NULL; - if (tparam->ty == Tsarray) + void visit(TypeIdentifier *t) { - TypeSArray *tp = (TypeSArray *)tparam; - if (tp->dim->op == TOKvar && - ((VarExp *)tp->dim)->var->storage_class & STCtemplateparameter) + // Extra check + if (tparam && tparam->ty == Tident) { - id = ((VarExp *)tp->dim)->var->ident; + TypeIdentifier *tp = (TypeIdentifier *)tparam; + + for (size_t i = 0; i < t->idents.dim; i++) + { + RootObject *id1 = t->idents[i]; + RootObject *id2 = tp->idents[i]; + + if (!id1->equals(id2)) + { + result = MATCHnomatch; + return; + } + } } - else if (dim->toInteger() != tp->dim->toInteger()) - return MATCHnomatch; + visit((Type *)t); } - else if (tparam->ty == Taarray) + + void visit(TypeInstance *t) { - TypeAArray *tp = (TypeAArray *)tparam; - if (tp->index->ty == Tident && - ((TypeIdentifier *)tp->index)->idents.dim == 0) + #if 0 + printf("TypeInstance::deduceType()\n"); + printf("\tthis = %d, ", t->ty); t->print(); + printf("\ttparam = %d, ", tparam->ty); tparam->print(); + #endif + // Extra check + if (tparam && tparam->ty == Tinstance && t->tempinst->tempdecl) { - id = ((TypeIdentifier *)tp->index)->ident; - } - } - if (id) - { - // This code matches code in TypeInstance::deduceType() - size_t i = templateIdentifierLookup(id, parameters); - if (i == IDX_NOTFOUND) - goto Lnomatch; - TemplateParameter *tp = (*parameters)[i]; - if (!tp->matchArg(sc, dim, i, parameters, dedtypes, NULL)) - goto Lnomatch; - return next->deduceType(sc, tparam->nextOf(), parameters, dedtypes, wm); - } - } - return Type::deduceType(sc, tparam, parameters, dedtypes, wm); + TemplateDeclaration *tempdecl = t->tempinst->tempdecl->isTemplateDeclaration(); + assert(tempdecl); - Lnomatch: - return MATCHnomatch; -} + TypeInstance *tp = (TypeInstance *)tparam; -MATCH TypeAArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm) -{ -#if 0 - printf("TypeAArray::deduceType()\n"); - printf("\tthis = %d, ", ty); print(); - printf("\ttparam = %d, ", tparam->ty); tparam->print(); -#endif + //printf("tempinst->tempdecl = %p\n", tempdecl); + //printf("tp->tempinst->tempdecl = %p\n", tp->tempinst->tempdecl); + if (!tp->tempinst->tempdecl) + { + //printf("tp->tempinst->name = '%s'\n", tp->tempinst->name->toChars()); - // Extra check that index type must match - if (tparam && tparam->ty == Taarray) - { - TypeAArray *tp = (TypeAArray *)tparam; - if (!index->deduceType(sc, tp->index, parameters, dedtypes)) - { - return MATCHnomatch; - } - } - return Type::deduceType(sc, tparam, parameters, dedtypes, wm); -} + /* Handle case of: + * template Foo(T : sa!(T), alias sa) + */ + size_t i = templateIdentifierLookup(tp->tempinst->name, parameters); + if (i == IDX_NOTFOUND) + { + /* Didn't find it as a parameter identifier. Try looking + * it up and seeing if is an alias. See Bugzilla 1454 + */ + TypeIdentifier *tid = new TypeIdentifier(tp->loc, tp->tempinst->name); + Type *tx; + Expression *e; + Dsymbol *s; + tid->resolve(tp->loc, sc, &e, &tx, &s); + if (tx) + { + s = tx->toDsymbol(sc); + if (s) + { + TemplateInstance *ti = s->parent->isTemplateInstance(); + s = ti ? ti->tempdecl : NULL; + } + } + if (s) + { + s = s->toAlias(); + TemplateDeclaration *td = s->isTemplateDeclaration(); + if (td) + { + if (td->overroot) + td = td->overroot; + for (; td; td = td->overnext) + { + if (td == tempdecl) + goto L2; + } + } + } + goto Lnomatch; + } + TemplateParameter *tpx = (*parameters)[i]; + if (!tpx->matchArg(sc, tempdecl, i, parameters, dedtypes, NULL)) + goto Lnomatch; + } + else if (tempdecl != tp->tempinst->tempdecl) + goto Lnomatch; -MATCH TypeFunction::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm) -{ - //printf("TypeFunction::deduceType()\n"); - //printf("\tthis = %d, ", ty); print(); - //printf("\ttparam = %d, ", tparam->ty); tparam->print(); + L2: - // Extra check that function characteristics must match - if (tparam && tparam->ty == Tfunction) - { - TypeFunction *tp = (TypeFunction *)tparam; - if (varargs != tp->varargs || - linkage != tp->linkage) - return MATCHnomatch; + for (size_t i = 0; 1; i++) + { + //printf("\ttest: tempinst->tiargs[%d]\n", i); + RootObject *o1 = NULL; + if (i < t->tempinst->tiargs->dim) + o1 = (*t->tempinst->tiargs)[i]; + else if (i < t->tempinst->tdtypes.dim && i < tp->tempinst->tiargs->dim) + { + // Pick up default arg + o1 = t->tempinst->tdtypes[i]; + } + else if (i >= tp->tempinst->tiargs->dim) + break; + + if (i >= tp->tempinst->tiargs->dim) + { + size_t dim = tempdecl->parameters->dim - (tempdecl->isVariadic() ? 1 : 0); + while (i < dim && ((*tempdecl->parameters)[i]->dependent || + (*tempdecl->parameters)[i]->hasDefaultArg())) + { + i++; + } + if (i >= dim) + break; // match if all remained parameters are dependent + goto Lnomatch; + } + + RootObject *o2 = (*tp->tempinst->tiargs)[i]; + Type *t2 = isType(o2); + + size_t j; + if (t2 && + t2->ty == Tident && + i == tp->tempinst->tiargs->dim - 1 && + (j = templateParameterLookup(t2, parameters), j != IDX_NOTFOUND) && + j == parameters->dim - 1 && + (*parameters)[j]->isTemplateTupleParameter()) + { + /* Given: + * struct A(B...) {} + * alias A!(int, float) X; + * static if (is(X Y == A!(Z), Z...)) {} + * deduce that Z is a tuple(int, float) + */ - size_t nfargs = Parameter::dim(this->parameters); - size_t nfparams = Parameter::dim(tp->parameters); + /* Create tuple from remaining args + */ + Tuple *vt = new Tuple(); + size_t vtdim = (tempdecl->isVariadic() + ? t->tempinst->tiargs->dim : t->tempinst->tdtypes.dim) - i; + vt->objects.setDim(vtdim); + for (size_t k = 0; k < vtdim; k++) + { + RootObject *o; + if (k < t->tempinst->tiargs->dim) + o = (*t->tempinst->tiargs)[i + k]; + else // Pick up default arg + o = t->tempinst->tdtypes[i + k]; + vt->objects[k] = o; + } + + Tuple *v = (Tuple *)(*dedtypes)[j]; + if (v) + { + if (!match(v, vt)) + goto Lnomatch; + } + else + (*dedtypes)[j] = vt; + break; + } + else if (!o1) + break; + + Type *t1 = isType(o1); + Dsymbol *s1 = isDsymbol(o1); + Dsymbol *s2 = isDsymbol(o2); + Expression *e1 = s1 ? getValue(s1) : getValue(isExpression(o1)); + Expression *e2 = isExpression(o2); + #if 0 + Tuple *v1 = isTuple(o1); + Tuple *v2 = isTuple(o2); + if (t1) printf("t1 = %s\n", t1->toChars()); + if (t2) printf("t2 = %s\n", t2->toChars()); + if (e1) printf("e1 = %s\n", e1->toChars()); + if (e2) printf("e2 = %s\n", e2->toChars()); + if (s1) printf("s1 = %s\n", s1->toChars()); + if (s2) printf("s2 = %s\n", s2->toChars()); + if (v1) printf("v1 = %s\n", v1->toChars()); + if (v2) printf("v2 = %s\n", v2->toChars()); + #endif + + if (t1 && t2) + { + if (!deduceType(t1, sc, t2, parameters, dedtypes)) + goto Lnomatch; + } + else if (e1 && e2) + { + Le: + e1 = e1->ctfeInterpret(); + + /* If it is one of the template parameters for this template, + * we should not attempt to interpret it. It already has a value. + */ + if (e2->op == TOKvar && + (((VarExp *)e2)->var->storage_class & STCtemplateparameter)) + { + /* + * (T:Number!(e2), int e2) + */ + j = templateIdentifierLookup(((VarExp *)e2)->var->ident, parameters); + if (j != IDX_NOTFOUND) + goto L1; + // The template parameter was not from this template + // (it may be from a parent template, for example) + } + + e2 = e2->semantic(sc); // Bugzilla 13417 + e2 = e2->ctfeInterpret(); + + //printf("e1 = %s, type = %s %d\n", e1->toChars(), e1->type->toChars(), e1->type->ty); + //printf("e2 = %s, type = %s %d\n", e2->toChars(), e2->type->toChars(), e2->type->ty); + if (!e1->equals(e2)) + { + if (!e2->implicitConvTo(e1->type)) + goto Lnomatch; + + e2 = e2->implicitCastTo(sc, e1->type); + e2 = e2->ctfeInterpret(); + if (!e1->equals(e2)) + goto Lnomatch; + } + } + else if (e1 && t2 && t2->ty == Tident) + { + j = templateParameterLookup(t2, parameters); + L1: + if (j == IDX_NOTFOUND) + { + t2->resolve(((TypeIdentifier *)t2)->loc, sc, &e2, &t2, &s2); + if (e2) + goto Le; + goto Lnomatch; + } + if (!(*parameters)[j]->matchArg(sc, e1, j, parameters, dedtypes, NULL)) + goto Lnomatch; + } + else if (s1 && s2) + { + Ls: + if (!s1->equals(s2)) + goto Lnomatch; + } + else if (s1 && t2 && t2->ty == Tident) + { + j = templateParameterLookup(t2, parameters); + if (j == IDX_NOTFOUND) + { + t2->resolve(((TypeIdentifier *)t2)->loc, sc, &e2, &t2, &s2); + if (s2) + goto Ls; + goto Lnomatch; + } + if (!(*parameters)[j]->matchArg(sc, s1, j, parameters, dedtypes, NULL)) + goto Lnomatch; + } + else + goto Lnomatch; + } + } + visit((Type *)t); + return; - // bug 2579 fix: Apply function parameter storage classes to parameter types - for (size_t i = 0; i < nfparams; i++) - { - Parameter *fparam = Parameter::getNth(tp->parameters, i); - fparam->type = fparam->type->addStorageClass(fparam->storageClass); - fparam->storageClass &= ~(STC_TYPECTOR | STCin); + Lnomatch: + //printf("no match\n"); + result = MATCHnomatch; } - //printf("\t-> this = %d, ", ty); print(); - //printf("\t-> tparam = %d, ", tparam->ty); tparam->print(); - /* See if tuple match - */ - if (nfparams > 0 && nfargs >= nfparams - 1) + void visit(TypeStruct *t) { - /* See if 'A' of the template parameter matches 'A' - * of the type of the last function parameter. - */ - Parameter *fparam = Parameter::getNth(tp->parameters, nfparams - 1); - assert(fparam); - assert(fparam->type); - if (fparam->type->ty != Tident) - goto L1; - TypeIdentifier *tid = (TypeIdentifier *)fparam->type; - if (tid->idents.dim) - goto L1; + //printf("TypeStruct::deduceType()\n"); + //printf("\tthis->parent = %s, ", sym->parent->toChars()); t->print(); + //printf("\ttparam = %d, ", tparam->ty); tparam->print(); - /* Look through parameters to find tuple matching tid->ident + /* If this struct is a template struct, and we're matching + * it against a template instance, convert the struct type + * to a template instance, too, and try again. */ - size_t tupi = 0; - for (; 1; tupi++) - { if (tupi == parameters->dim) - goto L1; - TemplateParameter *t = (*parameters)[tupi]; - TemplateTupleParameter *tup = t->isTemplateTupleParameter(); - if (tup && tup->ident->equals(tid->ident)) - break; - } + TemplateInstance *ti = t->sym->parent->isTemplateInstance(); - /* The types of the function arguments [nfparams - 1 .. nfargs] - * now form the tuple argument. - */ - size_t tuple_dim = nfargs - (nfparams - 1); + if (tparam && tparam->ty == Tinstance) + { + if (ti && ti->toAlias() == t->sym) + { + TypeInstance *tx = new TypeInstance(Loc(), ti); + result = deduceType(tx, sc, tparam, parameters, dedtypes, wm); + return; + } - /* See if existing tuple, and whether it matches or not - */ - RootObject *o = (*dedtypes)[tupi]; - if (o) - { // Existing deduced argument must be a tuple, and must match - Tuple *t = isTuple(o); - if (!t || t->objects.dim != tuple_dim) - return MATCHnomatch; - for (size_t i = 0; i < tuple_dim; i++) - { Parameter *arg = Parameter::getNth(this->parameters, nfparams - 1 + i); - if (!arg->type->equals(t->objects[i])) - return MATCHnomatch; + /* Match things like: + * S!(T).foo + */ + TypeInstance *tpi = (TypeInstance *)tparam; + if (tpi->idents.dim) + { + RootObject *id = tpi->idents[tpi->idents.dim - 1]; + if (id->dyncast() == DYNCAST_IDENTIFIER && t->sym->ident->equals((Identifier *)id)) + { + Type *tparent = t->sym->parent->getType(); + if (tparent) + { + /* Slice off the .foo in S!(T).foo + */ + tpi->idents.dim--; + result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm); + tpi->idents.dim++; + return; + } + } } } - else - { // Create new tuple - Tuple *t = new Tuple(); - t->objects.setDim(tuple_dim); - for (size_t i = 0; i < tuple_dim; i++) - { Parameter *arg = Parameter::getNth(this->parameters, nfparams - 1 + i); - t->objects[i] = arg->type; + + // Extra check + if (tparam && tparam->ty == Tstruct) + { + TypeStruct *tp = (TypeStruct *)tparam; + + //printf("\t%d\n", (MATCH) t->implicitConvTo(tp)); + if (wm && t->deduceWild(tparam, false)) + { + result = MATCHconst; + return; } - (*dedtypes)[tupi] = t; + result = t->implicitConvTo(tp); + return; } - nfparams--; // don't consider the last parameter for type deduction - goto L2; + visit((Type *)t); } - L1: - if (nfargs != nfparams) - return MATCHnomatch; - L2: - for (size_t i = 0; i < nfparams; i++) + void visit(TypeEnum *t) { - Parameter *a = Parameter::getNth(this->parameters, i); - Parameter *ap = Parameter::getNth(tp->parameters, i); - if (a->storageClass != ap->storageClass || - !a->type->deduceType(sc, ap->type, parameters, dedtypes)) - return MATCHnomatch; - } - } - return Type::deduceType(sc, tparam, parameters, dedtypes, wm); -} + // Extra check + if (tparam && tparam->ty == Tenum) + { + TypeEnum *tp = (TypeEnum *)tparam; -MATCH TypeIdentifier::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm) -{ - // Extra check - if (tparam && tparam->ty == Tident) - { - TypeIdentifier *tp = (TypeIdentifier *)tparam; + if (t->sym != tp->sym) + { + result = MATCHnomatch; + return; + } + } + Type *tb = t->toBasetype(); + if (tb->ty == tparam->ty || + tb->ty == Tsarray && tparam->ty == Taarray) + { + result = deduceType(tb, sc, tparam, parameters, dedtypes, wm); + return; + } + visit((Type *)t); + } - for (size_t i = 0; i < idents.dim; i++) + /* Helper for TypeClass::deduceType(). + * Classes can match with implicit conversion to a base class or interface. + * This is complicated, because there may be more than one base class which + * matches. In such cases, one or more parameters remain ambiguous. + * For example, + * + * interface I(X, Y) {} + * class C : I(uint, double), I(char, double) {} + * C x; + * foo(T, U)( I!(T, U) x) + * + * deduces that U is double, but T remains ambiguous (could be char or uint). + * + * Given a baseclass b, and initial deduced types 'dedtypes', this function + * tries to match tparam with b, and also tries all base interfaces of b. + * If a match occurs, numBaseClassMatches is incremented, and the new deduced + * types are ANDed with the current 'best' estimate for dedtypes. + */ + static void deduceBaseClassParameters(BaseClass *b, + Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, + Objects *best, int &numBaseClassMatches) { - RootObject *id1 = idents[i]; - RootObject *id2 = tp->idents[i]; + TemplateInstance *parti = b->base ? b->base->parent->isTemplateInstance() : NULL; + if (parti) + { + // Make a temporary copy of dedtypes so we don't destroy it + Objects *tmpdedtypes = new Objects(); + tmpdedtypes->setDim(dedtypes->dim); + memcpy(tmpdedtypes->tdata(), dedtypes->tdata(), dedtypes->dim * sizeof(void *)); + + TypeInstance *t = new TypeInstance(Loc(), parti); + MATCH m = deduceType(t, sc, tparam, parameters, tmpdedtypes); + if (m > MATCHnomatch) + { + // If this is the first ever match, it becomes our best estimate + if (numBaseClassMatches==0) + memcpy(best->tdata(), tmpdedtypes->tdata(), tmpdedtypes->dim * sizeof(void *)); + else for (size_t k = 0; k < tmpdedtypes->dim; ++k) + { + // If we've found more than one possible type for a parameter, + // mark it as unknown. + if ((*tmpdedtypes)[k] != (*best)[k]) + (*best)[k] = (*dedtypes)[k]; + } + ++numBaseClassMatches; + } + } + // Now recursively test the inherited interfaces + for (size_t j = 0; j < b->baseInterfaces_dim; ++j) + { + deduceBaseClassParameters( &(b->baseInterfaces)[j], + sc, tparam, parameters, dedtypes, + best, numBaseClassMatches); + } - if (!id1->equals(id2)) - return MATCHnomatch; } - } - return Type::deduceType(sc, tparam, parameters, dedtypes, wm); -} -MATCH TypeInstance::deduceType(Scope *sc, - Type *tparam, TemplateParameters *parameters, - Objects *dedtypes, unsigned *wm) -{ -#if 0 - printf("TypeInstance::deduceType()\n"); - printf("\tthis = %d, ", ty); print(); - printf("\ttparam = %d, ", tparam->ty); tparam->print(); -#endif - // Extra check - if (tparam && tparam->ty == Tinstance && tempinst->tempdecl) - { - TemplateDeclaration *tempdecl = tempinst->tempdecl->isTemplateDeclaration(); - assert(tempdecl); + void visit(TypeClass *t) + { + //printf("TypeClass::deduceType(this = %s)\n", t->toChars()); - TypeInstance *tp = (TypeInstance *)tparam; + /* If this class is a template class, and we're matching + * it against a template instance, convert the class type + * to a template instance, too, and try again. + */ + TemplateInstance *ti = t->sym->parent->isTemplateInstance(); - //printf("tempinst->tempdecl = %p\n", tempdecl); - //printf("tp->tempinst->tempdecl = %p\n", tp->tempinst->tempdecl); - if (!tp->tempinst->tempdecl) - { - //printf("tp->tempinst->name = '%s'\n", tp->tempinst->name->toChars()); - if (!tp->tempinst->name->equals(tempinst->name)) + if (tparam && tparam->ty == Tinstance) { - /* Handle case of: - * template Foo(T : sa!(T), alias sa) + if (ti && ti->toAlias() == t->sym) + { + TypeInstance *tx = new TypeInstance(Loc(), ti); + MATCH m = deduceType(tx, sc, tparam, parameters, dedtypes, wm); + // Even if the match fails, there is still a chance it could match + // a base class. + if (m != MATCHnomatch) + { + result = m; + return; + } + } + + /* Match things like: + * S!(T).foo */ - size_t i = templateIdentifierLookup(tp->tempinst->name, parameters); - if (i == IDX_NOTFOUND) + TypeInstance *tpi = (TypeInstance *)tparam; + if (tpi->idents.dim) { - /* Didn't find it as a parameter identifier. Try looking - * it up and seeing if is an alias. See Bugzilla 1454 - */ - TypeIdentifier *tid = new TypeIdentifier(tp->loc, tp->tempinst->name); - Type *t; - Expression *e; - Dsymbol *s; - tid->resolve(tp->loc, sc, &e, &t, &s); - if (t) + RootObject *id = tpi->idents[tpi->idents.dim - 1]; + if (id->dyncast() == DYNCAST_IDENTIFIER && t->sym->ident->equals((Identifier *)id)) { - s = t->toDsymbol(sc); - if (s) + Type *tparent = t->sym->parent->getType(); + if (tparent) { - TemplateInstance *ti = s->parent->isTemplateInstance(); - s = ti ? ti->tempdecl : NULL; + /* Slice off the .foo in S!(T).foo + */ + tpi->idents.dim--; + result = deduceType(tparent, sc, tpi, parameters, dedtypes, wm); + tpi->idents.dim++; + return; } } - if (s) + } + + // If it matches exactly or via implicit conversion, we're done + visit((Type *)t); + if (result != MATCHnomatch) + return; + + /* There is still a chance to match via implicit conversion to + * a base class or interface. Because there could be more than one such + * match, we need to check them all. + */ + + int numBaseClassMatches = 0; // Have we found an interface match? + + // Our best guess at dedtypes + Objects *best = new Objects(); + best->setDim(dedtypes->dim); + + ClassDeclaration *s = t->sym; + while (s && s->baseclasses->dim > 0) + { + // Test the base class + deduceBaseClassParameters((*s->baseclasses)[0], + sc, tparam, parameters, dedtypes, + best, numBaseClassMatches); + + // Test the interfaces inherited by the base class + for (size_t i = 0; i < s->interfaces_dim; ++i) { - s = s->toAlias(); - TemplateDeclaration *td = s->isTemplateDeclaration(); - if (td && td == tempdecl) - goto L2; + BaseClass *b = s->interfaces[i]; + deduceBaseClassParameters(b, sc, tparam, parameters, dedtypes, + best, numBaseClassMatches); } - goto Lnomatch; + s = (*s->baseclasses)[0]->base; } - TemplateParameter *tpx = (*parameters)[i]; - if (!tpx->matchArg(sc, tempdecl, i, parameters, dedtypes, NULL)) - goto Lnomatch; - } - } - else if (tempdecl != tp->tempinst->tempdecl) - goto Lnomatch; - L2: + if (numBaseClassMatches == 0) + { + result = MATCHnomatch; + return; + } - for (size_t i = 0; 1; i++) - { - //printf("\ttest: tempinst->tiargs[%d]\n", i); - RootObject *o1 = NULL; - if (i < tempinst->tiargs->dim) - o1 = (*tempinst->tiargs)[i]; - else if (i < tempinst->tdtypes.dim && i < tp->tempinst->tiargs->dim) - // Pick up default arg - o1 = tempinst->tdtypes[i]; - else if (i >= tp->tempinst->tiargs->dim) - break; + // If we got at least one match, copy the known types into dedtypes + memcpy(dedtypes->tdata(), best->tdata(), best->dim * sizeof(void *)); + result = MATCHconvert; + return; + } - if (i >= tp->tempinst->tiargs->dim) - goto Lnomatch; + // Extra check + if (tparam && tparam->ty == Tclass) + { + TypeClass *tp = (TypeClass *)tparam; - RootObject *o2 = (*tp->tempinst->tiargs)[i]; - Type *t2 = isType(o2); - - size_t j; - if (t2 && - t2->ty == Tident && - i == tp->tempinst->tiargs->dim - 1 && - (j = templateParameterLookup(t2, parameters), j != IDX_NOTFOUND) && - j == parameters->dim - 1 && - (*parameters)[j]->isTemplateTupleParameter()) - { - /* Given: - * struct A(B...) {} - * alias A!(int, float) X; - * static if (is(X Y == A!(Z), Z...)) {} - * deduce that Z is a tuple(int, float) - */ + //printf("\t%d\n", (MATCH) t->implicitConvTo(tp)); + if (wm && t->deduceWild(tparam, false)) + { + result = MATCHconst; + return; + } + result = t->implicitConvTo(tp); + return; + } + visit((Type *)t); + } - /* Create tuple from remaining args - */ - Tuple *vt = new Tuple(); - size_t vtdim = (tempdecl->isVariadic() - ? tempinst->tiargs->dim : tempinst->tdtypes.dim) - i; - vt->objects.setDim(vtdim); - for (size_t k = 0; k < vtdim; k++) + void visit(Expression *e) + { + //printf("Expression::deduceType(e = %s)\n", e->toChars()); + size_t i = templateParameterLookup(tparam, parameters); + if (i == IDX_NOTFOUND || ((TypeIdentifier *)tparam)->idents.dim > 0) + { + if (e == emptyArrayElement && tparam->ty == Tarray) { - RootObject *o; - if (k < tempinst->tiargs->dim) - o = (*tempinst->tiargs)[i + k]; - else // Pick up default arg - o = tempinst->tdtypes[i + k]; - vt->objects[k] = o; + Type *tn = ((TypeNext *)tparam)->next; + result = deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm); + return; } + e->type->accept(this); + return; + } - Tuple *v = (Tuple *)(*dedtypes)[j]; - if (v) + TemplateTypeParameter *tp = (*parameters)[i]->isTemplateTypeParameter(); + if (!tp) + return; // nomatch + + if (e == emptyArrayElement) + { + if ((*dedtypes)[i]) { - if (!match(v, vt)) - goto Lnomatch; + result = MATCHexact; + return; + } + if (tp->defaultType) + { + tp->defaultType->accept(this); + return; } - else - (*dedtypes)[j] = vt; - break; //return MATCHexact; } - else if (!o1) - break; - Type *t1 = isType(o1); - Dsymbol *s1 = isDsymbol(o1); - Dsymbol *s2 = isDsymbol(o2); - Expression *e1 = s1 ? getValue(s1) : getValue(isExpression(o1)); - Expression *e2 = isExpression(o2); - Tuple *v1 = isTuple(o1); - Tuple *v2 = isTuple(o2); -#if 0 - if (t1) printf("t1 = %s\n", t1->toChars()); - if (t2) printf("t2 = %s\n", t2->toChars()); - if (e1) printf("e1 = %s\n", e1->toChars()); - if (e2) printf("e2 = %s\n", e2->toChars()); - if (s1) printf("s1 = %s\n", s1->toChars()); - if (s2) printf("s2 = %s\n", s2->toChars()); - if (v1) printf("v1 = %s\n", v1->toChars()); - if (v2) printf("v2 = %s\n", v2->toChars()); -#endif + Type *at = (Type *)(*dedtypes)[i]; + Type *tt; + if (unsigned char wx = wm ? deduceWildHelper(e->type, &tt, tparam) : 0) + { + result = MATCHconst; + } + else if (MATCH m = deduceTypeHelper(e->type, &tt, tparam)) + { + result = m; + } + else + return; // nomatch - if (t1 && t2) + // expression vs (none) + if (!at) { - if (!t1->deduceType(sc, t2, parameters, dedtypes)) - goto Lnomatch; + (*dedtypes)[i] = new TypeDeduced(tt, e, tparam); + return; } - else if (e1 && e2) + + TypeDeduced *xt = NULL; + if (at->ty == Tnone) { - Le: - e1 = e1->ctfeInterpret(); + xt = (TypeDeduced *)at; + at = xt->tded; + } - /* If it is one of the template parameters for this template, - * we should not attempt to interpret it. It already has a value. - */ - if (e2->op == TOKvar && - (((VarExp *)e2)->var->storage_class & STCtemplateparameter)) + // From previous matched expressions to current deduced type + MATCH match1 = MATCHnomatch; + if (xt) + { + match1 = MATCHexact; + for (size_t j = 0; j < xt->argexps.dim; j++) { - /* - * (T:Number!(e2), int e2) - */ - j = templateIdentifierLookup(((VarExp *)e2)->var->ident, parameters); - if (j != IDX_NOTFOUND) - goto L1; - // The template parameter was not from this template - // (it may be from a parent template, for example) + Expression *e = xt->argexps[j]; + if (e == emptyArrayElement) + continue; + Type *pt = tt->addMod(xt->tparams[j]->mod); + if (wm && *wm) + pt = pt->substWildTo(*wm); + MATCH m = e->implicitConvTo(pt); + if (match1 > m) + match1 = m; + if (match1 <= MATCHnomatch) + break; } + } + // From current expresssion to previous deduced type + Type *pt = at->addMod(tparam->mod); + if (wm && *wm) + pt = pt->substWildTo(*wm); + MATCH match2 = e->implicitConvTo(pt); - e2 = e2->ctfeInterpret(); - - //printf("e1 = %s, type = %s %d\n", e1->toChars(), e1->type->toChars(), e1->type->ty); - //printf("e2 = %s, type = %s %d\n", e2->toChars(), e2->type->toChars(), e2->type->ty); - if (!e1->equals(e2)) + if (match1 > MATCHnomatch && match2 > MATCHnomatch) + { + if (at->implicitConvTo(tt) <= MATCHnomatch) + match1 = MATCHnomatch; // Prefer at + else if (tt->implicitConvTo(at) <= MATCHnomatch) + match2 = MATCHnomatch; // Prefer tt + else if (tt->isTypeBasic() && tt->ty == at->ty && tt->mod != at->mod) { - if (!e2->implicitConvTo(e1->type)) - goto Lnomatch; - - e2 = e2->implicitCastTo(sc, e1->type); - e2 = e2->ctfeInterpret(); - if (!e1->equals(e2)) - goto Lnomatch; + if (!tt->isMutable() && !at->isMutable()) + tt = tt->mutableOf()->addMod(MODmerge(tt->mod, at->mod)); + else if (tt->isMutable()) + { + if (at->mod == 0) // Prefer unshared + match1 = MATCHnomatch; + else + match2 = MATCHnomatch; + } + else if (at->isMutable()) + { + if (tt->mod == 0) // Prefer unshared + match2 = MATCHnomatch; + else + match1 = MATCHnomatch; + } + //printf("tt = %s, at = %s\n", tt->toChars(), at->toChars()); } } - else if (e1 && t2 && t2->ty == Tident) + if (match1 > MATCHnomatch) { - j = templateParameterLookup(t2, parameters); - L1: - if (j == IDX_NOTFOUND) + // Prefer current match: tt + if (xt) + xt->update(tt, e, tparam); + else + (*dedtypes)[i] = tt; + result = match1; + return; + } + else if (match2 > MATCHnomatch) + { + // Prefer previous match: (*dedtypes)[i] + if (xt) + xt->update(e, tparam); + result = match2; + return; + } + + // todo? + } + + MATCH deduceEmptyArrayElement() + { + if (!emptyArrayElement) + { + emptyArrayElement = new IdentifierExp(Loc(), Id::p); // dummy + emptyArrayElement->type = Type::tvoid; + } + assert(tparam->ty == Tarray); + + Type *tn = ((TypeNext *)tparam)->next; + return deduceType(emptyArrayElement, sc, tn, parameters, dedtypes, wm); + } + + void visit(NullExp *e) + { + if (tparam->ty == Tarray && e->type->ty == Tnull) + { + // tparam:T[] <- e:null (void[]) + result = deduceEmptyArrayElement(); + return; + } + visit((Expression *)e); + } + + void visit(StringExp *e) + { + Type *taai; + if (e->type->ty == Tarray && + (tparam->ty == Tsarray || + tparam->ty == Taarray && (taai = ((TypeAArray *)tparam)->index)->ty == Tident && + ((TypeIdentifier *)taai)->idents.dim == 0)) + { + // Consider compile-time known boundaries + e->type->nextOf()->sarrayOf(e->len)->accept(this); + return; + } + visit((Expression *)e); + } + + void visit(ArrayLiteralExp *e) + { + if ((!e->elements || !e->elements->dim) && + e->type->toBasetype()->nextOf()->ty == Tvoid && + (tparam->ty == Tarray/* || tparam->ty == Tsarray || tparam->ty == Taarray*/)) + { + // tparam:T[] <- e:[] (void[]) + result = deduceEmptyArrayElement(); + return; + } + + if (tparam->ty == Tarray && e->elements && e->elements->dim) + { + Type *tn = ((TypeDArray *)tparam)->next; + result = MATCHexact; + for (size_t i = 0; i < e->elements->dim; i++) { - t2->resolve(((TypeIdentifier *)t2)->loc, sc, &e2, &t2, &s2); - if (e2) - goto Le; - goto Lnomatch; + MATCH m = deduceType((*e->elements)[i], sc, tn, parameters, dedtypes, wm); + if (m < result) + result = m; + if (result <= MATCHnomatch) + break; } - if (!(*parameters)[j]->matchArg(sc, e1, j, parameters, dedtypes, NULL)) - goto Lnomatch; + return; } - else if (s1 && s2) + + Type *taai; + if (e->type->ty == Tarray && + (tparam->ty == Tsarray || + tparam->ty == Taarray && (taai = ((TypeAArray *)tparam)->index)->ty == Tident && + ((TypeIdentifier *)taai)->idents.dim == 0)) { - Ls: - if (!s1->equals(s2)) - goto Lnomatch; + // Consider compile-time known boundaries + e->type->nextOf()->sarrayOf(e->elements->dim)->accept(this); + return; } - else if (s1 && t2 && t2->ty == Tident) + visit((Expression *)e); + } + + void visit(AssocArrayLiteralExp *e) + { + if (tparam->ty == Taarray && e->keys && e->keys->dim) { - j = templateParameterLookup(t2, parameters); - if (j == IDX_NOTFOUND) + TypeAArray *taa = (TypeAArray *)tparam; + result = MATCHexact; + for (size_t i = 0; i < e->keys->dim; i++) { - t2->resolve(((TypeIdentifier *)t2)->loc, sc, &e2, &t2, &s2); - if (s2) - goto Ls; - goto Lnomatch; + MATCH m1 = deduceType((*e->keys)[i], sc, taa->index, parameters, dedtypes, wm); + if (m1 < result) + result = m1; + if (result <= MATCHnomatch) + break; + MATCH m2 = deduceType((*e->values)[i], sc, taa->next, parameters, dedtypes, wm); + if (m2 < result) + result = m2; + if (result <= MATCHnomatch) + break; } - if (!(*parameters)[j]->matchArg(sc, s1, j, parameters, dedtypes, NULL)) - goto Lnomatch; + return; } - else - goto Lnomatch; + visit((Expression *)e); } - } - return Type::deduceType(sc, tparam, parameters, dedtypes, wm); -Lnomatch: - //printf("no match\n"); - return MATCHnomatch; -} + void visit(FuncExp *e) + { + //printf("e->type = %s, tparam = %s\n", e->type->toChars(), tparam->toChars()); + if (e->td) + { + Type *to = tparam; + if (!to->nextOf() || to->nextOf()->ty != Tfunction) + return; + TypeFunction *tof = (TypeFunction *)to->nextOf(); -MATCH TypeStruct::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm) -{ - //printf("TypeStruct::deduceType()\n"); - //printf("\tthis->parent = %s, ", sym->parent->toChars()); print(); - //printf("\ttparam = %d, ", tparam->ty); tparam->print(); + // Parameter types inference from 'tof' + assert(e->td->scope); + TypeFunction *tf = (TypeFunction *)e->fd->type; + //printf("\ttof = %s\n", tof->toChars()); + //printf("\ttf = %s\n", tf->toChars()); + size_t dim = Parameter::dim(tf->parameters); - /* If this struct is a template struct, and we're matching - * it against a template instance, convert the struct type - * to a template instance, too, and try again. - */ - TemplateInstance *ti = sym->parent->isTemplateInstance(); + if (Parameter::dim(tof->parameters) != dim || + tof->varargs != tf->varargs) + return; - if (tparam && tparam->ty == Tinstance) - { - if (ti && ti->toAlias() == sym) - { - TypeInstance *t = new TypeInstance(Loc(), ti); - return t->deduceType(sc, tparam, parameters, dedtypes, wm); + Objects *tiargs = new Objects(); + tiargs->reserve(e->td->parameters->dim); + + for (size_t i = 0; i < e->td->parameters->dim; i++) + { + TemplateParameter *tp = (*e->td->parameters)[i]; + size_t u = 0; + for (; u < dim; u++) + { + Parameter *p = Parameter::getNth(tf->parameters, u); + if (p->type->ty == Tident && + ((TypeIdentifier *)p->type)->ident == tp->ident) + { + break; + } + } + assert(u < dim); + Parameter *pto = Parameter::getNth(tof->parameters, u); + if (!pto) + break; + Type *t = pto->type->syntaxCopy(); // Bugzilla 11774 + if (reliesOnTident(t, parameters, inferStart)) + return; + t = t->semantic(e->loc, sc); + if (t->ty == Terror) + return; + tiargs->push(t); + } + + // Set target of return type inference + if (!tf->next && tof->next) + e->fd->treq = tparam; + + TemplateInstance *ti = new TemplateInstance(e->loc, e->td, tiargs); + Expression *ex = (new ScopeExp(e->loc, ti))->semantic(e->td->scope); + + // Reset inference target for the later re-semantic + e->fd->treq = NULL; + + if (ex->op == TOKerror) + return; + if (ex->op != TOKfunction) + return; + visit(ex->type); + return; + } + + Type *t = e->type; + + if (t->ty == Tdelegate && tparam->ty == Tpointer) + return; + + // Allow conversion from implicit function pointer to delegate + if (e->tok == TOKreserved && + t->ty == Tpointer && tparam->ty == Tdelegate) + { + TypeFunction *tf = (TypeFunction *)t->nextOf(); + t = (new TypeDelegate(tf))->merge(); + } + //printf("tparam = %s <= e->type = %s, t = %s\n", tparam->toChars(), e->type->toChars(), t->toChars()); + visit(t); } - /* Match things like: - * S!(T).foo - */ - TypeInstance *tpi = (TypeInstance *)tparam; - if (tpi->idents.dim) + void visit(SliceExp *e) { - RootObject *id = tpi->idents[tpi->idents.dim - 1]; - if (id->dyncast() == DYNCAST_IDENTIFIER && sym->ident->equals((Identifier *)id)) + Type *taai; + if (e->type->ty == Tarray && + (tparam->ty == Tsarray || + tparam->ty == Taarray && (taai = ((TypeAArray *)tparam)->index)->ty == Tident && + ((TypeIdentifier *)taai)->idents.dim == 0)) { - Type *tparent = sym->parent->getType(); - if (tparent) + // Consider compile-time known boundaries + if (Type *tsa = toStaticArrayType(e)) { - /* Slice off the .foo in S!(T).foo - */ - tpi->idents.dim--; - MATCH m = tparent->deduceType(sc, tpi, parameters, dedtypes, wm); - tpi->idents.dim++; - return m; + tsa->accept(this); + return; } } + visit((Expression *)e); } - } - - // Extra check - if (tparam && tparam->ty == Tstruct) - { - TypeStruct *tp = (TypeStruct *)tparam; - - //printf("\t%d\n", (MATCH) implicitConvTo(tp)); - if (wm && deduceWild(tparam, false)) - return MATCHconst; - return implicitConvTo(tp); - } - return Type::deduceType(sc, tparam, parameters, dedtypes, wm); -} -MATCH TypeEnum::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm) -{ - // Extra check - if (tparam && tparam->ty == Tenum) - { - TypeEnum *tp = (TypeEnum *)tparam; - - if (sym != tp->sym) - return MATCHnomatch; - } - Type *tb = toBasetype(); - if (tb->ty == tparam->ty || - tb->ty == Tsarray && tparam->ty == Taarray) - { - return tb->deduceType(sc, tparam, parameters, dedtypes, wm); - } - return Type::deduceType(sc, tparam, parameters, dedtypes, wm); -} + void visit(CommaExp *e) + { + ((CommaExp *)e)->e2->accept(this); + } + }; -MATCH TypeTypedef::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm) -{ - // Extra check - if (tparam && tparam->ty == Ttypedef) + DeduceType v(sc, tparam, parameters, dedtypes, wm, inferStart); + if (Type *t = isType(o)) + t->accept(&v); + else { - TypeTypedef *tp = (TypeTypedef *)tparam; - - if (sym != tp->sym) - return MATCHnomatch; + assert(isExpression(o)); + ((Expression *)o)->accept(&v); } - return Type::deduceType(sc, tparam, parameters, dedtypes, wm); + return v.result; } -/* Helper for TypeClass::deduceType(). - * Classes can match with implicit conversion to a base class or interface. - * This is complicated, because there may be more than one base class which - * matches. In such cases, one or more parameters remain ambiguous. - * For example, - * - * interface I(X, Y) {} - * class C : I(uint, double), I(char, double) {} - * C x; - * foo(T, U)( I!(T, U) x) - * - * deduces that U is double, but T remains ambiguous (could be char or uint). - * - * Given a baseclass b, and initial deduced types 'dedtypes', this function - * tries to match tparam with b, and also tries all base interfaces of b. - * If a match occurs, numBaseClassMatches is incremented, and the new deduced - * types are ANDed with the current 'best' estimate for dedtypes. +/******************************* + * Input: + * t Tested type, if NULL, returns NULL. + * tparams Optional template parameters. + * == NULL: + * If one of the subtypes of this type is a TypeIdentifier, + * i.e. it's an unresolved type, return that type. + * != NULL: + * Only when the TypeIdentifier is one of template parameters, + * return that type. */ -void deduceBaseClassParameters(BaseClass *b, - Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, - Objects *best, int &numBaseClassMatches) + +Type *reliesOnTident(Type *t, TemplateParameters *tparams, size_t iStart) { - TemplateInstance *parti = b->base ? b->base->parent->isTemplateInstance() : NULL; - if (parti) + class ReliesOnTident : public Visitor { - // Make a temporary copy of dedtypes so we don't destroy it - Objects *tmpdedtypes = new Objects(); - tmpdedtypes->setDim(dedtypes->dim); - memcpy(tmpdedtypes->tdata(), dedtypes->tdata(), dedtypes->dim * sizeof(void *)); + public: + TemplateParameters *tparams; + size_t iStart; + Type *result; - TypeInstance *t = new TypeInstance(Loc(), parti); - MATCH m = t->deduceType(sc, tparam, parameters, tmpdedtypes); - if (m > MATCHnomatch) + ReliesOnTident(TemplateParameters *tparams, size_t iStart) + : tparams(tparams), iStart(iStart) { - // If this is the first ever match, it becomes our best estimate - if (numBaseClassMatches==0) - memcpy(best->tdata(), tmpdedtypes->tdata(), tmpdedtypes->dim * sizeof(void *)); - else for (size_t k = 0; k < tmpdedtypes->dim; ++k) - { - // If we've found more than one possible type for a parameter, - // mark it as unknown. - if ((*tmpdedtypes)[k] != (*best)[k]) - (*best)[k] = (*dedtypes)[k]; - } - ++numBaseClassMatches; + result = NULL; } - } - // Now recursively test the inherited interfaces - for (size_t j = 0; j < b->baseInterfaces_dim; ++j) - { - deduceBaseClassParameters( &(b->baseInterfaces)[j], - sc, tparam, parameters, dedtypes, - best, numBaseClassMatches); - } -} + void visit(Type *t) + { + } -MATCH TypeClass::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wm) -{ - //printf("TypeClass::deduceType(this = %s)\n", toChars()); + void visit(TypeNext *t) + { + t->next->accept(this); + } - /* If this class is a template class, and we're matching - * it against a template instance, convert the class type - * to a template instance, too, and try again. - */ - TemplateInstance *ti = sym->parent->isTemplateInstance(); + void visit(TypeVector *t) + { + t->basetype->accept(this); + } - if (tparam && tparam->ty == Tinstance) - { - if (ti && ti->toAlias() == sym) + void visit(TypeAArray *t) { - TypeInstance *t = new TypeInstance(Loc(), ti); - MATCH m = t->deduceType(sc, tparam, parameters, dedtypes, wm); - // Even if the match fails, there is still a chance it could match - // a base class. - if (m != MATCHnomatch) - return m; + visit((TypeNext *)t); + if (!result) + t->index->accept(this); } - /* Match things like: - * S!(T).foo - */ - TypeInstance *tpi = (TypeInstance *)tparam; - if (tpi->idents.dim) - { RootObject *id = tpi->idents[tpi->idents.dim - 1]; - if (id->dyncast() == DYNCAST_IDENTIFIER && sym->ident->equals((Identifier *)id)) + void visit(TypeFunction *t) + { + size_t dim = Parameter::dim(t->parameters); + for (size_t i = 0; i < dim; i++) { - Type *tparent = sym->parent->getType(); - if (tparent) - { - /* Slice off the .foo in S!(T).foo - */ - tpi->idents.dim--; - MATCH m = tparent->deduceType(sc, tpi, parameters, dedtypes, wm); - tpi->idents.dim++; - return m; - } + Parameter *fparam = Parameter::getNth(t->parameters, i); + fparam->type->accept(this); + if (result) + return; } + if (t->next) + t->next->accept(this); } - // If it matches exactly or via implicit conversion, we're done - MATCH m = Type::deduceType(sc, tparam, parameters, dedtypes, wm); - if (m != MATCHnomatch) - return m; - - /* There is still a chance to match via implicit conversion to - * a base class or interface. Because there could be more than one such - * match, we need to check them all. - */ - - int numBaseClassMatches = 0; // Have we found an interface match? + void visit(TypeIdentifier *t) + { + if (!tparams) + { + result = t; + return; + } - // Our best guess at dedtypes - Objects *best = new Objects(); - best->setDim(dedtypes->dim); + for (size_t i = iStart; i < tparams->dim; i++) + { + TemplateParameter *tp = (*tparams)[i]; + if (tp->ident->equals(t->ident)) + { + result = t; + return; + } + } + } - ClassDeclaration *s = sym; - while (s && s->baseclasses->dim > 0) + void visit(TypeInstance *t) { - // Test the base class - deduceBaseClassParameters((*s->baseclasses)[0], - sc, tparam, parameters, dedtypes, - best, numBaseClassMatches); + if (!tparams) + return; - // Test the interfaces inherited by the base class - for (size_t i = 0; i < s->interfaces_dim; ++i) + for (size_t i = iStart; i < tparams->dim; i++) { - BaseClass *b = s->interfaces[i]; - deduceBaseClassParameters(b, sc, tparam, parameters, dedtypes, - best, numBaseClassMatches); + TemplateParameter *tp = (*tparams)[i]; + if (t->tempinst->name == tp->ident) + { + result = t; + return; + } + } + if (!t->tempinst->tiargs) + return; + for (size_t i = 0; i < t->tempinst->tiargs->dim; i++) + { + Type *ta = isType((*t->tempinst->tiargs)[i]); + if (ta) + { + ta->accept(this); + if (result) + return; + } } - s = (*s->baseclasses)[0]->base; } - if (numBaseClassMatches == 0) - return MATCHnomatch; - - // If we got at least one match, copy the known types into dedtypes - memcpy(dedtypes->tdata(), best->tdata(), best->dim * sizeof(void *)); - return MATCHconvert; - } + void visit(TypeTuple *t) + { + if (t->arguments) + { + for (size_t i = 0; i < t->arguments->dim; i++) + { + Parameter *arg = (*t->arguments)[i]; + arg->type->accept(this); + if (result) + return; + } + } + } + }; - // Extra check - if (tparam && tparam->ty == Tclass) - { - TypeClass *tp = (TypeClass *)tparam; + if (!t) + return NULL; - //printf("\t%d\n", (MATCH) implicitConvTo(tp)); - if (wm && deduceWild(tparam, false)) - return MATCHconst; - return implicitConvTo(tp); - } - return Type::deduceType(sc, tparam, parameters, dedtypes, wm); + ReliesOnTident v(tparams, iStart); + t->accept(&v); + return v.result; } /* ======================== TemplateParameter =============================== */ @@ -4103,6 +4864,7 @@ TemplateParameter::TemplateParameter(Loc loc, Identifier *ident) { this->loc = loc; this->ident = ident; + this->dependent = false; this->sparam = NULL; } @@ -4212,7 +4974,7 @@ void TemplateTypeParameter::declareParameter(Scope *sc) void TemplateTypeParameter::semantic(Scope *sc, TemplateParameters *parameters) { //printf("TemplateTypeParameter::semantic('%s')\n", ident->toChars()); - if (specType && !specType->reliesOnTident(parameters)) + if (specType && !reliesOnTident(specType, parameters)) { specType = specType->semantic(loc, sc); } @@ -4271,9 +5033,10 @@ MATCH TemplateTypeParameter::matchArg(Scope *sc, RootObject *oarg, goto Lnomatch; //printf("\tcalling deduceType(): ta is %s, specType is %s\n", ta->toChars(), specType->toChars()); - MATCH m2 = ta->deduceType(sc, specType, parameters, dedtypes); + MATCH m2 = deduceType(ta, sc, specType, parameters, dedtypes); if (m2 <= MATCHnomatch) - { //printf("\tfailed deduceType\n"); + { + //printf("\tfailed deduceType\n"); goto Lnomatch; } @@ -4285,11 +5048,13 @@ MATCH TemplateTypeParameter::matchArg(Scope *sc, RootObject *oarg, else { if ((*dedtypes)[i]) - { // Must match already deduced type + { + // Must match already deduced type Type *t = (Type *)(*dedtypes)[i]; if (!t->equals(ta)) - { //printf("t = %s ta = %s\n", t->toChars(), ta->toChars()); + { + //printf("t = %s ta = %s\n", t->toChars(), ta->toChars()); goto Lnomatch; } } @@ -4304,7 +5069,7 @@ MATCH TemplateTypeParameter::matchArg(Scope *sc, RootObject *oarg, if (psparam) *psparam = new AliasDeclaration(loc, ident, ta); //printf("\tm = %d\n", m); - return m; + return dependent ? MATCHexact : m; Lnomatch: if (psparam) @@ -4350,10 +5115,8 @@ void TemplateTypeParameter::toCBuffer(OutBuffer *buf, HdrGenState *hgs) void *TemplateTypeParameter::dummyArg() { - Type *t; - if (specType) - t = specType; - else + Type *t = specType; + if (!t) { // Use this for alias-parameter's too (?) if (!tdummy) @@ -4372,9 +5135,7 @@ RootObject *TemplateTypeParameter::specialization() RootObject *TemplateTypeParameter::defaultArg(Loc loc, Scope *sc) { - Type *t; - - t = defaultType; + Type *t = defaultType; if (t) { t = t->syntaxCopy(); @@ -4383,6 +5144,11 @@ RootObject *TemplateTypeParameter::defaultArg(Loc loc, Scope *sc) return t; } +bool TemplateTypeParameter::hasDefaultArg() +{ + return defaultType != NULL; +} + /* ======================== TemplateThisParameter =========================== */ // this-parameter @@ -4460,8 +5226,9 @@ RootObject *aliasParameterSemantic(Loc loc, Scope *sc, RootObject *o, TemplatePa { Expression *ea = isExpression(o); Type *ta = isType(o); - if (ta && (!parameters || !ta->reliesOnTident(parameters))) - { Dsymbol *s = ta->toDsymbol(sc); + if (ta && (!parameters || !reliesOnTident(ta, parameters))) + { + Dsymbol *s = ta->toDsymbol(sc); if (s) o = s; else @@ -4480,7 +5247,7 @@ RootObject *aliasParameterSemantic(Loc loc, Scope *sc, RootObject *o, TemplatePa void TemplateAliasParameter::semantic(Scope *sc, TemplateParameters *parameters) { - if (specType && !specType->reliesOnTident(parameters)) + if (specType && !reliesOnTident(specType, parameters)) { specType = specType->semantic(loc, sc); } @@ -4512,7 +5279,9 @@ MATCH TemplateAliasParameter::matchArg(Scope *sc, RootObject *oarg, Declaration **psparam) { //printf("TemplateAliasParameter::matchArg()\n"); - RootObject *sa = getDsymbol(oarg); + MATCH m = MATCHexact; + Type *ta = isType(oarg); + RootObject *sa = ta && !ta->deco ? NULL : getDsymbol(oarg); Expression *ea = isExpression(oarg); if (ea && (ea->op == TOKthis || ea->op == TOKsuper)) sa = ((ThisExp *)ea)->var; @@ -4520,11 +5289,15 @@ MATCH TemplateAliasParameter::matchArg(Scope *sc, RootObject *oarg, sa = ((ScopeExp *)ea)->sds; if (sa) { + if (((Dsymbol *)sa)->isAggregateDeclaration()) + m = MATCHconvert; + /* specType means the alias must be a declaration with a type * that matches specType. */ if (specType) - { Declaration *d = ((Dsymbol *)sa)->isDeclaration(); + { + Declaration *d = ((Dsymbol *)sa)->isDeclaration(); if (!d) goto Lnomatch; if (!d->type->equals(specType)) @@ -4535,12 +5308,29 @@ MATCH TemplateAliasParameter::matchArg(Scope *sc, RootObject *oarg, { sa = oarg; if (ea) - { if (specType) + { + if (specType) { if (!ea->type->equals(specType)) goto Lnomatch; } } + else if (ta && ta->ty == Tinstance && !specAlias) + { + /* Bugzilla xxxxx: Specialized parameter should be prefeerd + * match to the template type parameter. + * template X(alias a) {} // a == this + * template X(alias a : B!A, alias B, A...) {} // B!A => ta + */ + } + else if (sa && sa == TemplateTypeParameter::tdummy) + { + /* Bugzilla 2025: Aggregate Types should preferentially + * match to the template type parameter. + * template X(alias a) {} // a == this + * template X(T) {} // T => sa + */ + } else goto Lnomatch; } @@ -4549,22 +5339,33 @@ MATCH TemplateAliasParameter::matchArg(Scope *sc, RootObject *oarg, { if (sa == sdummy) goto Lnomatch; - if (sa != specAlias && isDsymbol(sa)) + Dsymbol *sx = isDsymbol(sa); + if (sa != specAlias && sx) { - TemplateInstance *ti = isDsymbol(sa)->isTemplateInstance(); - Type *ta = isType(specAlias); - if (!ti || !ta) + Type *talias = isType(specAlias); + if (!talias) + goto Lnomatch; + + TemplateInstance *ti = sx->isTemplateInstance(); + if (!ti && sx->parent) + { + ti = sx->parent->isTemplateInstance(); + if (ti && ti->name != sx->ident) + goto Lnomatch; + } + if (!ti) goto Lnomatch; + Type *t = new TypeInstance(Loc(), ti); - MATCH m = t->deduceType(sc, ta, parameters, dedtypes); - if (m <= MATCHnomatch) + MATCH m2 = deduceType(t, sc, talias, parameters, dedtypes); + if (m2 <= MATCHnomatch) goto Lnomatch; } } else if ((*dedtypes)[i]) - { // Must match already deduced symbol + { + // Must match already deduced symbol RootObject *si = (*dedtypes)[i]; - if (!sa || si != sa) goto Lnomatch; } @@ -4576,6 +5377,10 @@ MATCH TemplateAliasParameter::matchArg(Scope *sc, RootObject *oarg, { *psparam = new AliasDeclaration(loc, ident, s); } + else if (Type *t = isType(sa)) + { + *psparam = new AliasDeclaration(loc, ident, t); + } else { assert(ea); @@ -4588,7 +5393,7 @@ MATCH TemplateAliasParameter::matchArg(Scope *sc, RootObject *oarg, *psparam = v; } } - return MATCHexact; + return dependent ? MATCHexact : m; Lnomatch: if (psparam) @@ -4612,7 +5417,8 @@ void TemplateAliasParameter::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring("alias "); if (specType) - { HdrGenState hgs1; + { + HdrGenState hgs1; specType->toCBuffer(buf, ident, &hgs1); } else @@ -4631,9 +5437,8 @@ void TemplateAliasParameter::toCBuffer(OutBuffer *buf, HdrGenState *hgs) void *TemplateAliasParameter::dummyArg() -{ RootObject *s; - - s = specAlias; +{ + RootObject *s = specAlias; if (!s) { if (!sdummy) @@ -4667,6 +5472,11 @@ RootObject *TemplateAliasParameter::defaultArg(Loc loc, Scope *sc) return o; } +bool TemplateAliasParameter::hasDefaultArg() +{ + return defaultAlias != NULL; +} + /* ======================== TemplateValueParameter ========================== */ // value-parameter @@ -4714,7 +5524,8 @@ void TemplateValueParameter::semantic(Scope *sc, TemplateParameters *parameters) bool wasSame = (sparam->type == valType); sparam->semantic(sc); if (sparam->type == Type::terror && wasSame) - { /* If sparam has a type error, avoid duplicate errors + { + /* If sparam has a type error, avoid duplicate errors * The simple solution of leaving that function if sparam->type == Type::terror * doesn't quite work because it causes failures in xtest46 for bug 6295 */ @@ -4722,12 +5533,6 @@ void TemplateValueParameter::semantic(Scope *sc, TemplateParameters *parameters) return; } valType = valType->semantic(loc, sc); - if (!(valType->isintegral() || valType->isfloating() || valType->isString()) && - valType->ty != Tident) - { - if (valType != Type::terror) - error(loc, "arithmetic/string type expected for value-parameter, not %s", valType->toChars()); - } #if 0 // defer semantic analysis to arg match if (specValue) @@ -4801,7 +5606,7 @@ MATCH TemplateValueParameter::matchArg(Scope *sc, RootObject *oarg, ei = new VarExp(loc, f); ei = ei->semantic(sc); if (!f->needThis()) - ei = resolveProperties(sc, ei); + ei = resolvePropertiesOnly(sc, ei); /* If it was really a property, it will become a CallExp. * If it stayed as a var, it cannot be interpreted. */ @@ -4814,7 +5619,8 @@ MATCH TemplateValueParameter::matchArg(Scope *sc, RootObject *oarg, } if (ei && ei->op == TOKvar) - { // Resolve const variables that we had skipped earlier + { + // Resolve const variables that we had skipped earlier ei = ei->ctfeInterpret(); } @@ -4825,20 +5631,17 @@ MATCH TemplateValueParameter::matchArg(Scope *sc, RootObject *oarg, if (ei->type) { - m = (MATCH)ei->implicitConvTo(vt); + m = ei->implicitConvTo(vt); //printf("m: %d\n", m); if (m <= MATCHnomatch) goto Lnomatch; - if (m != MATCHexact) - { - ei = ei->implicitCastTo(sc, vt); - ei = ei->ctfeInterpret(); - } + ei = ei->implicitCastTo(sc, vt); + ei = ei->ctfeInterpret(); } if (specValue) { - if (!ei || _aaGetRvalue(edummies, ei->type) == ei) + if (!ei || (Expression *)dmd_aaGetRvalue(edummies, (void *)ei->type) == ei) goto Lnomatch; Expression *e = specValue; @@ -4864,7 +5667,8 @@ MATCH TemplateValueParameter::matchArg(Scope *sc, RootObject *oarg, else { if ((*dedtypes)[i]) - { // Must match already deduced value + { + // Must match already deduced value Expression *e = (Expression *)(*dedtypes)[i]; if (!ei || !ei->equals(e)) @@ -4880,7 +5684,7 @@ MATCH TemplateValueParameter::matchArg(Scope *sc, RootObject *oarg, sparam->storage_class = STCmanifest; *psparam = sparam; } - return m; + return dependent ? MATCHexact : m; Lnomatch: //printf("\tno match\n"); @@ -4919,13 +5723,12 @@ void TemplateValueParameter::toCBuffer(OutBuffer *buf, HdrGenState *hgs) void *TemplateValueParameter::dummyArg() -{ Expression *e; - - e = specValue; +{ + Expression *e = specValue; if (!e) { // Create a dummy value - Expression **pe = (Expression **)_aaGet(&edummies, valType); + Expression **pe = (Expression **)dmd_aaGet(&edummies, (void *)valType); if (!*pe) *pe = valType->defaultInit(); e = *pe; @@ -4954,6 +5757,11 @@ RootObject *TemplateValueParameter::defaultArg(Loc loc, Scope *sc) return e; } +bool TemplateValueParameter::hasDefaultArg() +{ + return defaultValue != NULL; +} + /* ======================== TemplateTupleParameter ========================== */ // variadic-parameter @@ -5009,9 +5817,11 @@ MATCH TemplateTupleParameter::matchArg(Loc loc, Scope *sc, Objects *tiargs, assert(i + 1 == dedtypes->dim); // must be the last one Tuple *ovar; - if ((*dedtypes)[i] && isTuple((*dedtypes)[i])) - // It was already been deduced - ovar = isTuple((*dedtypes)[i]); + if (Tuple *u = isTuple((*dedtypes)[i])) + { + // It has already been deduced + ovar = u; + } else if (i + 1 == tiargs->dim && isTuple((*tiargs)[i])) ovar = isTuple((*tiargs)[i]); else @@ -5037,11 +5847,19 @@ MATCH TemplateTupleParameter::matchArg(Scope *sc, RootObject *oarg, Tuple *ovar = isTuple(oarg); if (!ovar) return MATCHnomatch; + if ((*dedtypes)[i]) + { + Tuple *tup = isTuple((*dedtypes)[i]); + if (!tup) + return MATCHnomatch; + if (!match(tup, ovar)) + return MATCHnomatch; + } (*dedtypes)[i] = ovar; if (psparam) *psparam = new TupleDeclaration(loc, ident, &ovar->objects); - return MATCHexact; + return dependent ? MATCHexact : MATCHconvert; } @@ -5101,6 +5919,11 @@ RootObject *TemplateTupleParameter::defaultArg(Loc loc, Scope *sc) return NULL; } +bool TemplateTupleParameter::hasDefaultArg() +{ + return false; +} + /* ======================== TemplateInstance ================================ */ TemplateInstance::TemplateInstance(Loc loc, Identifier *ident) @@ -5120,10 +5943,10 @@ TemplateInstance::TemplateInstance(Loc loc, Identifier *ident) this->argsym = NULL; this->aliasdecl = NULL; this->semantictiargsdone = false; - this->withsym = NULL; this->nest = 0; this->havetempdecl = false; this->enclosing = NULL; + this->gagged = false; this->speculative = false; this->hash = 0; this->fargs = NULL; @@ -5151,10 +5974,10 @@ TemplateInstance::TemplateInstance(Loc loc, TemplateDeclaration *td, Objects *ti this->argsym = NULL; this->aliasdecl = NULL; this->semantictiargsdone = true; - this->withsym = NULL; this->nest = 0; this->havetempdecl = true; this->enclosing = NULL; + this->gagged = false; this->speculative = false; this->hash = 0; this->fargs = NULL; @@ -5167,7 +5990,8 @@ Objects *TemplateInstance::arraySyntaxCopy(Objects *objs) { Objects *a = NULL; if (objs) - { a = new Objects(); + { + a = new Objects(); a->setDim(objs->dim); for (size_t i = 0; i < objs->dim; i++) { @@ -5210,6 +6034,12 @@ void TemplateInstance::expandMembers(Scope *sc2) s->setScope(sc2); } + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + s->importAll(sc2); + } + for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; @@ -5237,6 +6067,7 @@ void TemplateInstance::tryExpandMembers(Scope *sc2) } expandMembers(sc2); + nest--; } @@ -5244,6 +6075,7 @@ void TemplateInstance::trySemantic3(Scope *sc2) { // extracted to a function to allow windows SEH to work without destructors in the same function static int nest; + //printf("%d\n", nest); if (++nest > 300) { global.gag = 0; // ensure error message gets printed @@ -5280,29 +6112,47 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) #endif return; } - - // get the enclosing template instance from the scope tinst - tinst = sc->tinst; - - Module *mi = sc->instantiatingModule(); + if (semanticRun != PASSinit) + { +#if LOG + printf("Recursive template expansion\n"); +#endif + error(loc, "recursive template expansion"); + if (global.gag) + semanticRun = PASSinit; + else + inst = this; + errors = true; + return; + } /* If a TemplateInstance is ever instantiated by non-root modules, * we do not have to generate code for it, * because it will be generated when the non-root module is compiled. */ + Module *mi = sc->instantiatingModule(); if (!instantiatingModule || instantiatingModule->isRoot()) instantiatingModule = mi; //printf("mi = %s\n", mi->toChars()); - if (semanticRun != PASSinit) + /* Get the enclosing template instance from the scope tinst + */ + tinst = sc->tinst; + + if (global.gag) + gagged = true; + if (sc->speculative || (tinst && tinst->speculative)) { -#if LOG - printf("Recursive template expansion\n"); -#endif - error(loc, "recursive template expansion"); -// inst = this; - return; + //printf("\tspeculative ti %s '%s' gag = %d, spec = %d\n", tempdecl->parent->toChars(), toChars(), global.gag, sc->speculative); + speculative = true; + } + if (sc->flags & (SCOPEstaticif | SCOPEstaticassert | SCOPEcompile)) + { + // Disconnect the chain if this instantiation is in definitely speculative context. + // It should be done after sc->instantiatingModule(). + tinst = NULL; } + semanticRun = PASSsemantic; #if LOG @@ -5312,13 +6162,19 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) * then run semantic on each argument (place results in tiargs[]), * last find most specialized template from overload list/set. */ - if (!findTemplateDeclaration(sc) || + if (!findTempDecl(sc, NULL) || !semanticTiargs(sc) || !findBestMatch(sc, fargs)) { - inst = this; - inst->errors = true; - return; // error recovery + if (gagged) + { + // Bugzilla 13220: Rollback status for later semantic re-running. + semanticRun = PASSinit; + } + else + inst = this; + errors = true; + return; } TemplateDeclaration *tempdecl = this->tempdecl->isTemplateDeclaration(); assert(tempdecl); @@ -5340,31 +6196,61 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) inst = ti; parent = ti->parent; - // If both this and the previous instantiation were speculative, + // If both this and the previous instantiation were gagged, // use the number of errors that happened last time. - if (inst->speculative && global.gag) + if (inst->gagged && gagged) { global.errors += inst->errors; global.gaggedErrors += inst->errors; } - // If the first instantiation was speculative, but this is not: - if (inst->speculative && !global.gag) + // If the first instantiation was gagged, but this is not: + if (inst->gagged && !gagged) { // If the first instantiation had failed, re-run semantic, // so that error messages are shown. if (inst->errors) goto L1; - // It had succeeded, mark it is a non-speculative instantiation, + // It had succeeded, mark it is a non-gagged instantiation, // and reuse it. - inst->speculative = 0; + inst->gagged = false; + } + + // If the first instantiation was speculative, but this is not: + if (inst->speculative && !sc->speculative) + { + // Mark it is a non-speculative instantiation. + inst->speculative = false; + + // Bugzilla 13400: When an instance is changed to non-speculative, + // its instantiatingModule should also be updated. + // See test/runnable/link13400.d + inst->instantiatingModule = mi; + } + + // If the first instantiation was in speculative context, but this is not: + if (!inst->tinst && inst->speculative && + tinst && !(sc->flags & (SCOPEstaticif | SCOPEstaticassert | SCOPEcompile))) + { + // Reconnect the chain if this instantiation is not in speculative context. + TemplateInstance *tix = tinst; + while (tix && tix != inst) + tix = tix->tinst; + if (tix != inst) // Bugzilla 13379: Prevent circular chain + inst->tinst = tinst; } #if LOG printf("\tit's a match with instance %p, %d\n", inst, inst->semanticRun); #endif - if (!inst->instantiatingModule || inst->instantiatingModule->isRoot()) + // If this is not a speculative instantiation, it might allow us to + // elide codegen for the template instance. + if (!speculative && + (!inst->instantiatingModule || inst->instantiatingModule->isRoot())) + { inst->instantiatingModule = mi; + } + errors = inst->errors; return; } L1: ; @@ -5378,9 +6264,6 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) #endif unsigned errorsave = global.errors; inst = this; - // Mark as speculative if we are instantiated from inside is(typeof()) - if (global.gag && sc->speculative) - speculative = 1; TemplateInstance *tempdecl_instance_idx = tempdecl->addInstance(this); @@ -5395,10 +6278,10 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) // an error. #if 1 Dsymbols *target_symbol_list = NULL; - size_t target_symbol_list_idx; - - { Dsymbols *a; + size_t target_symbol_list_idx = 0; + { + Dsymbols *a; Scope *scx = sc; #if 0 for (scx = sc; scx; scx = scx->enclosing) @@ -5469,8 +6352,10 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) #endif // Copy the syntax trees from the TemplateDeclaration - if (members && speculative && !errors) - {} // Don't copy again so they were previously created. + if (members && speculative && !errors) // todo + { + // Don't copy again so they were previously created. + } else members = Dsymbol::arraySyntaxCopy(tempdecl->members); @@ -5558,7 +6443,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) //s->print(); //printf("'%s', '%s'\n", s->ident->toChars(), tempdecl->ident->toChars()); //printf("setting aliasdecl\n"); - aliasdecl = new AliasDeclaration(loc, s->ident, s); + aliasdecl = s; } } @@ -5566,7 +6451,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) */ if (fargs && aliasdecl) { - FuncDeclaration *fd = aliasdecl->toAlias()->isFuncDeclaration(); + FuncDeclaration *fd = aliasdecl->isFuncDeclaration(); if (fd) { /* Transmit fargs to type so that TypeFunction::semantic() can @@ -5588,17 +6473,42 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) sc2->parent = this; sc2->tinst = this; sc2->speculative = speculative; +#if BUGZILLA_11946 if (enclosing && tempdecl->isstatic) sc2->stc &= ~STCstatic; +#endif tryExpandMembers(sc2); semanticRun = PASSsemanticdone; + /* ConditionalDeclaration may introduce eponymous declaration, + * so we should find it once again after semantic. + */ + if (members->dim) + { + Dsymbol *s; + if (Dsymbol::oneMembers(members, &s, tempdecl->ident) && s) + { + if (!aliasdecl || aliasdecl != s) + { + //printf("s->kind = '%s'\n", s->kind()); + //s->print(); + //printf("'%s', '%s'\n", s->ident->toChars(), tempdecl->ident->toChars()); + //printf("setting aliasdecl 2\n"); + aliasdecl = s; + } + } + } + + if (global.errors != errorsave) + goto Laftersemantic; + /* If any of the instantiation members didn't get semantic() run * on them due to forward references, we cannot run semantic2() * or semantic3() yet. */ + { bool found_deferred_ad = false; for (size_t i = 0; i < Module::deferred.dim; i++) { @@ -5608,43 +6518,23 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) { //printf("deferred template aggregate: %s %s\n", // sd->parent->toChars(), sd->toChars()); - found_deferred_ad = true; - if (ad->parent == this) - { - ad->deferred = this; - break; - } - } - } - if (found_deferred_ad || Module::deferred.dim) - goto Laftersemantic; - - /* ConditionalDeclaration may introduce eponymous declaration, - * so we should find it once again after semantic. - */ - if (members->dim) - { - Dsymbol *s; - if (Dsymbol::oneMembers(members, &s, tempdecl->ident) && s) - { - if (!aliasdecl || aliasdecl->toAlias() != s) + found_deferred_ad = true; + if (ad->parent == this) { - //printf("s->kind = '%s'\n", s->kind()); - //s->print(); - //printf("'%s', '%s'\n", s->ident->toChars(), tempdecl->ident->toChars()); - //printf("setting aliasdecl 2\n"); - aliasdecl = new AliasDeclaration(loc, s->ident, s); + ad->deferred = this; + break; } } - else if (aliasdecl) - aliasdecl = NULL; + } + if (found_deferred_ad || Module::deferred.dim) + goto Laftersemantic; } /* The problem is when to parse the initializer for a variable. * Perhaps VarDeclaration::semantic() should do it like it does * for initializers inside a function. */ -// if (sc->parent->isFuncDeclaration()) + //if (sc->parent->isFuncDeclaration()) { /* BUG 782: this has problems if the classes this depends on * are forward referenced. Find a way to defer semantic() @@ -5652,17 +6542,14 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) */ semantic2(sc2); } + if (global.errors != errorsave) + goto Laftersemantic; - if (sc->func && aliasdecl && aliasdecl->toAlias()->isFuncDeclaration()) + if (sc->func && (aliasdecl && aliasdecl->toAlias()->isFuncDeclaration() || !tinst)) { /* Template function instantiation should run semantic3 immediately * for attribute inference. */ - //printf("function semantic3 %s inside %s\n", toChars(), sc->func->toChars()); - trySemantic3(sc2); - } - else if (sc->func && !tinst) - { /* If a template is instantiated inside function, the whole instantiation * should be done at that position. But, immediate running semantic3 of * dependent templates may cause unresolved forward reference (Bugzilla 9050). @@ -5720,13 +6607,15 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) // Give additional context info if error occurred during instantiation if (global.errors != errorsave) { - if (!tempdecl->literal) - error(loc, "error instantiating"); - if (tinst) - { tinst->printInstantiationTrace(); + if (!errors) + { + if (!tempdecl->literal) + error(loc, "error instantiating"); + if (tinst) + tinst->printInstantiationTrace(); } errors = true; - if (global.gag) + if (gagged) { // Errors are gagged, so remove the template instance from the // instance/symbol lists we added it to and reset our state to @@ -5742,6 +6631,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) } semanticRun = PASSinit; inst = NULL; + symtab = NULL; } } @@ -5753,14 +6643,24 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) /********************************************** * Find template declaration corresponding to template instance. + * + * Returns: + * false if finding fails. + * Note: + * This function is reentrant against error occurrence. If returns false, + * any members of this object won't be modified, and repetition call will + * reproduce same error. */ -bool TemplateInstance::findTemplateDeclaration(Scope *sc) +bool TemplateInstance::findTempDecl(Scope *sc, WithScopeSymbol **pwithsym) { + if (pwithsym) + *pwithsym = NULL; + if (havetempdecl) return true; - //printf("TemplateInstance::findTemplateDeclaration() %s\n", toChars()); + //printf("TemplateInstance::findTempDecl() %s\n", toChars()); if (!tempdecl) { /* Given: @@ -5785,7 +6685,8 @@ bool TemplateInstance::findTemplateDeclaration(Scope *sc) if (s->parent) printf("s->parent = '%s'\n", s->parent->toChars()); #endif - withsym = scopesym->isWithScopeSymbol(); + if (pwithsym) + *pwithsym = scopesym->isWithScopeSymbol(); /* We might have found an alias within a template when * we really want the template. @@ -5808,7 +6709,7 @@ bool TemplateInstance::findTemplateDeclaration(Scope *sc) } } - if (!updateTemplateDeclaration(sc, s)) + if (!updateTempDecl(sc, s)) { return false; } @@ -5854,9 +6755,17 @@ bool TemplateInstance::findTemplateDeclaration(Scope *sc) /********************************************** * Confirm s is a valid template, then store it. + * Input: + * sc + * s candidate symbol of template. It may be: + * TemplateDeclaration + * FuncDeclaration with findTemplateDeclRoot() != NULL + * OverloadSet which contains candidates + * Returns: + * true if updating succeeds. */ -bool TemplateInstance::updateTemplateDeclaration(Scope *sc, Dsymbol *s) +bool TemplateInstance::updateTempDecl(Scope *sc, Dsymbol *s) { if (s) { @@ -5893,6 +6802,13 @@ bool TemplateInstance::updateTemplateDeclaration(Scope *sc, Dsymbol *s) } } + OverDeclaration *od = s->isOverDeclaration(); + if (od) + { + tempdecl = od; // TODO: more strict check + return true; + } + /* It should be a TemplateDeclaration, not some other symbol */ if (FuncDeclaration *f = s->isFuncDeclaration()) @@ -5944,84 +6860,50 @@ bool TemplateInstance::updateTemplateDeclaration(Scope *sc, Dsymbol *s) return (tempdecl != NULL); } +/********************************** + * Run semantic on the elements of tiargs. + * Input: + * sc + * Returns: + * false if one or more arguments have errors. + * Note: + * This function is reentrant against error occurrence. If returns false, + * all elements of tiargs won't be modified. + */ + bool TemplateInstance::semanticTiargs(Scope *sc) { //printf("+TemplateInstance::semanticTiargs() %s\n", toChars()); if (semantictiargsdone) return true; - semantictiargsdone = 1; - semanticTiargs(loc, sc, tiargs, 0); - return arrayObjectIsError(tiargs) == 0; -} - -/********************************** - * Return true if e could be valid only as a template value parameter. - * Return false if it might be an alias or tuple. - * (Note that even in this case, it could still turn out to be a value). - */ -bool definitelyValueParameter(Expression *e) -{ - // None of these can be value parameters - if (e->op == TOKtuple || e->op == TOKimport || - e->op == TOKtype || e->op == TOKdottype || - e->op == TOKtemplate || e->op == TOKdottd || - e->op == TOKfunction || e->op == TOKerror || - e->op == TOKthis || e->op == TOKsuper) - return false; - - if (e->op != TOKdotvar) - return true; - - /* Template instantiations involving a DotVar expression are difficult. - * In most cases, they should be treated as a value parameter, and interpreted. - * But they might also just be a fully qualified name, which should be treated - * as an alias. - */ - - // x.y.f cannot be a value - FuncDeclaration *f = ((DotVarExp *)e)->var->isFuncDeclaration(); - if (f) - return false; - - while (e->op == TOKdotvar) + if (semanticTiargs(loc, sc, tiargs, 0)) { - e = ((DotVarExp *)e)->e1; - } - // this.x.y and super.x.y couldn't possibly be valid values. - if (e->op == TOKthis || e->op == TOKsuper) - return false; - - // e.type.x could be an alias - if (e->op == TOKdottype) - return false; - - // var.x.y is the only other possible form of alias - if (e->op != TOKvar) - return true; - - VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); - - // func.x.y is not an alias - if (!v) + // cache the result iff semantic analysis succeeded entirely + semantictiargsdone = 1; return true; - - // TODO: Should we force CTFE if it is a global constant? - + } return false; } /********************************** + * Run semantic of tiargs as arguments of template. * Input: + * loc + * sc + * tiargs array of template arguments * flags 1: replace const variables with their initializers * 2: don't devolve Parameter to Type + * Returns: + * false if one or more arguments have errors. */ -void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags) +bool TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags) { // Run semantic on each argument, place results in tiargs[] //printf("+TemplateInstance::semanticTiargs()\n"); if (!tiargs) - return; + return true; + bool err = false; for (size_t j = 0; j < tiargs->dim; j++) { RootObject *o = (*tiargs)[j]; @@ -6065,6 +6947,11 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f j--; continue; } + if (ta->ty == Terror) + { + err = true; + continue; + } (*tiargs)[j] = ta->merge2(); } else if (ea) @@ -6080,8 +6967,8 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f if (ea->op == TOKvar && (v = ((VarExp *)ea)->var->isVarDeclaration()) != NULL && !(v->storage_class & STCtemplateparameter)) { - if (v->sem < SemanticDone) - v->semantic(sc); + if (v->sem < SemanticDone && v->scope) + v->semantic(NULL); // skip optimization for variable symbols } else @@ -6099,7 +6986,7 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f } else if (definitelyValueParameter(ea)) { - int olderrs = global.errors; + unsigned int olderrs = global.errors; ea->rvalue(); // check void expression ea = ea->ctfeInterpret(); if (global.errors != olderrs) @@ -6121,6 +7008,11 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f j--; continue; } + if (ea->op == TOKerror) + { + err = true; + continue; + } (*tiargs)[j] = ea; if (ea->op == TOKtype) @@ -6176,11 +7068,16 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f { Ldsym: //printf("dsym %s %s\n", sa->kind(), sa->toChars()); + if (sa->errors) + { + err = true; + continue; + } + TupleDeclaration *d = sa->toAlias()->isTupleDeclaration(); if (d) { // Expand tuple - size_t dim = d->objects->dim; tiargs->remove(j); tiargs->insert(j, d->objects); j--; @@ -6229,6 +7126,7 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f printf("\ttiargs[%d] = ta %p, ea %p, sa %p, va %p\n", j, ta, ea, sa, va); } #endif + return !err; } bool TemplateInstance::findBestMatch(Scope *sc, Expressions *fargs) @@ -6307,7 +7205,6 @@ bool TemplateInstance::findBestMatch(Scope *sc, Expressions *fargs) if (c1 < c2) goto Ltd_best; } - Lambig: // td_best and td are ambiguous td_ambig = td; return 0; @@ -6346,10 +7243,10 @@ bool TemplateInstance::findBestMatch(Scope *sc, Expressions *fargs) if (p.td_ambig) { - ::error(loc, "%s %s.%s matches more than one template declaration:\n\t%s(%d):%s\nand\n\t%s(%d):%s", + ::error(loc, "%s %s.%s matches more than one template declaration:\n%s: %s\nand\n%s: %s", p.td_best->kind(), p.td_best->parent->toPrettyChars(), p.td_best->ident->toChars(), - p.td_best->loc.filename, p.td_best->loc.linnum, p.td_best->toChars(), - p.td_ambig->loc.filename, p.td_ambig->loc.linnum, p.td_ambig->toChars()); + p.td_best->loc.toChars() , p.td_best->toChars(), + p.td_ambig->loc.toChars(), p.td_ambig->toChars()); return false; } if (p.td_best) @@ -6364,7 +7261,43 @@ bool TemplateInstance::findBestMatch(Scope *sc, Expressions *fargs) } } - if (!td_last) + if (td_last) + { + /* Bugzilla 7469: Normalize template value arguments by using corresponding + * template value parameter types for correct mangling. + * + * By doing this before hasNestedArgs, CTFEable local variable will be + * accepted as a value parameter. For example: + * + * void foo() { + * struct S(int n) {} // non-global template + * const int num = 1; // CTFEable local variable + * S!num s; // S!1 is instantiated, not S!num + * } + */ + size_t dim = td_last->parameters->dim - (td_last->isVariadic() ? 1 : 0); + for (size_t i = 0; i < dim; i++) + { + if (tiargs->dim <= i) + tiargs->push(tdtypes[i]); + assert(i < tiargs->dim); + + TemplateValueParameter *tvp = (*td_last->parameters)[i]->isTemplateValueParameter(); + if (!tvp) + continue; + assert(tdtypes[i]); + // tdtypes[i] is already normalized to the required type in matchArg + + (*tiargs)[i] = tdtypes[i]; + } + } + else if (errors && inst) + { + // instantiation was failed with error reporting + assert(global.errors); + return false; + } + else { TemplateDeclaration *tdecl = tempdecl->isTemplateDeclaration(); @@ -6377,7 +7310,7 @@ bool TemplateInstance::findBestMatch(Scope *sc, Expressions *fargs) error("does not match template declaration %s", tdecl->toChars()); else ::error(loc, "%s %s.%s does not match any template declaration", - tdecl->kind(), tdecl->parent->toPrettyChars(), tdecl->ident->toChars()); + tempdecl->kind(), tempdecl->parent->toPrettyChars(), tempdecl->ident->toChars()); return false; } @@ -6403,6 +7336,8 @@ bool TemplateInstance::findBestMatch(Scope *sc, Expressions *fargs) bool TemplateInstance::needsTypeInference(Scope *sc, int flag) { //printf("TemplateInstance::needsTypeInference() %s\n", toChars()); + if (semanticRun != PASSinit) + return false; struct ParamNeedsInf { @@ -6423,7 +7358,6 @@ bool TemplateInstance::needsTypeInference(Scope *sc, int flag) TemplateDeclaration *td = s->isTemplateDeclaration(); if (!td) { - Lcontinue: return 0; } @@ -6464,27 +7398,13 @@ bool TemplateInstance::needsTypeInference(Scope *sc, int flag) if (tp && td->parameters->dim > 1) return 1; - if (ti->tiargs->dim < td->parameters->dim) + if (!tp && ti->tiargs->dim < td->parameters->dim) { // Can remain tiargs be filled by default arguments? for (size_t i = ti->tiargs->dim; i < td->parameters->dim; i++) { - tp = (*td->parameters)[i]; - if (TemplateTypeParameter *ttp = tp->isTemplateTypeParameter()) - { - if (!ttp->defaultType) - return 1; - } - else if (TemplateAliasParameter *tap = tp->isTemplateAliasParameter()) - { - if (!tap->defaultAlias) - return 1; - } - else if (TemplateValueParameter *tvp = tp->isTemplateValueParameter()) - { - if (!tvp->defaultValue) - return 1; - } + if (!(*td->parameters)[i]->hasDefaultArg()) + return 1; } } @@ -6534,11 +7454,22 @@ bool TemplateInstance::needsTypeInference(Scope *sc, int flag) OverloadSet *tovers = tempdecl->isOverloadSet(); size_t overs_dim = tovers ? tovers->a.dim : 1; + unsigned olderrs = global.errors; for (size_t oi = 0; oi < overs_dim; oi++) { - if (int r = overloadApply(tovers ? tovers->a[oi] : tempdecl, &p, &ParamNeedsInf::fp)) + if (overloadApply(tovers ? tovers->a[oi] : tempdecl, &p, &ParamNeedsInf::fp)) return true; } + if (olderrs != global.errors) + { + if (!global.gag) + { + errorSupplemental(loc, "while looking for match for %s", toChars()); + semanticRun = PASSsemanticdone; + inst = this; + } + errors = true; + } //printf("false\n"); return false; } @@ -6585,14 +7516,14 @@ bool TemplateInstance::hasNestedArgs(Objects *args, bool isstatic) goto Lsa; } // Emulate Expression::toMangleBuffer call that had exist in TemplateInstance::genIdent. - if (ea->op != TOKint64 && // IntegerExp - ea->op != TOKfloat64 && // RealExp - ea->op != TOKcomplex80 && // CompexExp - ea->op != TOKnull && // NullExp - ea->op != TOKstring && // StringExp - ea->op != TOKarrayliteral && // ArrayLiteralExp - ea->op != TOKassocarrayliteral && // AssocArrayLiteralExp - ea->op != TOKstructliteral) // StructLiteralExp + if (ea->op != TOKint64 && + ea->op != TOKfloat64 && + ea->op != TOKcomplex80 && + ea->op != TOKnull && + ea->op != TOKstring && + ea->op != TOKarrayliteral && + ea->op != TOKassocarrayliteral && + ea->op != TOKstructliteral) { ea->error("expression %s is not a valid template value argument", ea->toChars()); } @@ -6609,7 +7540,6 @@ bool TemplateInstance::hasNestedArgs(Objects *args, bool isstatic) sa = ti; } TemplateInstance *ti = sa->isTemplateInstance(); - AggregateDeclaration *ad = sa->isAggregateDeclaration(); Declaration *d = sa->isDeclaration(); if ((td && td->literal) || (ti && ti->enclosing) || @@ -6638,7 +7568,8 @@ bool TemplateInstance::hasNestedArgs(Objects *args, bool isstatic) for (Dsymbol *p = dparent; p; p = p->parent) { if (p == enclosing) - { enclosing = dparent; + { + enclosing = dparent; goto L1; // dparent is most nested } } @@ -6655,7 +7586,7 @@ bool TemplateInstance::hasNestedArgs(Objects *args, bool isstatic) } else if (va) { - nested |= hasNestedArgs(&va->objects, isstatic); + nested |= (int)hasNestedArgs(&va->objects, isstatic); } } //printf("-TemplateInstance::hasNestedArgs('%s') = %d\n", tempdecl->ident->toChars(), nested); @@ -6675,7 +7606,13 @@ Identifier *TemplateInstance::genIdent(Objects *args) //printf("TemplateInstance::genIdent('%s')\n", tempdecl->ident->toChars()); OutBuffer buf; char *id = tempdecl->ident->toChars(); - buf.printf("__T%llu%s", (ulonglong)strlen(id), id); + if (!members) + { + // Use "__U" for the symbols declared inside template constraint. + buf.printf("__U%llu%s", (ulonglong)strlen(id), id); + } + else + buf.printf("__T%llu%s", (ulonglong)strlen(id), id); for (size_t i = 0; i < args->dim; i++) { RootObject *o = (*args)[i]; @@ -6725,7 +7662,8 @@ Identifier *TemplateInstance::genIdent(Objects *args) } buf.writeByte('V'); if (ea->op == TOKtuple) - { ea->error("tuple is not a valid template value argument"); + { + ea->error("tuple is not a valid template value argument"); continue; } // Now that we know it is not an alias, we MUST obtain a value @@ -6733,18 +7671,10 @@ Identifier *TemplateInstance::genIdent(Objects *args) ea = ea->ctfeInterpret(); if (ea->op == TOKerror || olderr != global.errors) continue; -#if 1 + /* Use deco that matches what it would be for a function parameter */ buf.writestring(ea->type->deco); -#else - // Use type of parameter, not type of argument - TemplateParameter *tp = (*tempdecl->parameters)[i]; - assert(tp); - TemplateValueParameter *tvp = tp->isTemplateValueParameter(); - assert(tvp); - buf.writestring(tvp->valType->deco); -#endif ea->toMangleBuffer(&buf); } else if (sa) @@ -6758,18 +7688,7 @@ Identifier *TemplateInstance::genIdent(Objects *args) error("forward reference of %s %s", d->kind(), d->toChars()); continue; } -#if 0 - VarDeclaration *v = sa->isVarDeclaration(); - if (v && v->storage_class & STCmanifest) - { ExpInitializer *ei = v->init->isExpInitializer(); - if (ei) - { - ea = ei->exp; - goto Lea; - } - } -#endif - const char *p = sa->mangle(); + const char *p = mangle(sa); /* Bugzilla 3043: if the first character of p is a digit this * causes ambiguity issues because the digits of the two numbers are adjacent. @@ -6790,8 +7709,7 @@ Identifier *TemplateInstance::genIdent(Objects *args) assert(0); } buf.writeByte('Z'); - id = buf.toChars(); - //buf.data = NULL; // we can free the string after call to idPool() + id = buf.peekString(); //printf("\tgenIdent = %s\n", id); return Lexer::idPool(id); } @@ -6803,7 +7721,7 @@ Identifier *TemplateInstance::genIdent(Objects *args) Identifier *TemplateInstance::getIdent() { - if (!ident && inst) + if (!ident && inst && !errors) ident = genIdent(tiargs); // need an identifier for name mangling purposes. return ident; } @@ -6840,11 +7758,21 @@ void TemplateInstance::semantic2(Scope *sc) #endif if (!errors && members) { + TemplateDeclaration *tempdecl = this->tempdecl->isTemplateDeclaration(); + assert(tempdecl); + sc = tempdecl->scope; assert(sc); sc = sc->push(argsym); sc = sc->push(this); sc->tinst = this; + + int needGagging = (gagged && !global.gag); + unsigned int olderrors = global.errors; + int oldGaggedErrors; + if (needGagging) + oldGaggedErrors = global.startGagging(); + for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; @@ -6852,7 +7780,24 @@ void TemplateInstance::semantic2(Scope *sc) printf("\tmember '%s', kind = '%s'\n", s->toChars(), s->kind()); #endif s->semantic2(sc); + if (gagged && global.errors != olderrors) + break; + } + + if (global.errors != olderrors) + { + if (!errors) + { + if (!tempdecl->literal) + error(loc, "error instantiating"); + if (tinst) + tinst->printInstantiationTrace(); + } + errors = true; } + if (needGagging) + global.endGagging(oldGaggedErrors); + sc = sc->pop(); sc->pop(); } @@ -6872,32 +7817,47 @@ void TemplateInstance::semantic3(Scope *sc) semanticRun = PASSsemantic3; if (!errors && members) { + TemplateDeclaration *tempdecl = this->tempdecl->isTemplateDeclaration(); + assert(tempdecl); + sc = tempdecl->scope; sc = sc->push(argsym); sc = sc->push(this); sc->tinst = this; - int needGagging = (speculative && !global.gag); - int olderrors = global.errors; + + int needGagging = (gagged && !global.gag); + unsigned int olderrors = global.errors; int oldGaggedErrors; - /* If this is a speculative instantiation, gag errors. + /* If this is a gagged instantiation, gag errors. * Future optimisation: If the results are actually needed, errors * would already be gagged, so we don't really need to run semantic * on the members. */ if (needGagging) oldGaggedErrors = global.startGagging(); + for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->semantic3(sc); - if (speculative && global.errors != olderrors) + if (gagged && global.errors != olderrors) break; } - if (needGagging) - { // If errors occurred, this instantiation failed - if (global.endGagging(oldGaggedErrors)) - errors = true; + + if (global.errors != olderrors) + { + if (!errors) + { + if (!tempdecl->literal) + error(loc, "error instantiating"); + if (tinst) + tinst->printInstantiationTrace(); + } + errors = true; } + if (needGagging) + global.endGagging(oldGaggedErrors); + sc = sc->pop(); sc->pop(); } @@ -6937,6 +7897,7 @@ void TemplateInstance::printInstantiationTrace() { for (TemplateInstance *cur = this; cur; cur = cur->tinst) { + cur->errors = true; errorSupplemental(cur->loc, format, cur->toChars()); } } @@ -6947,6 +7908,7 @@ void TemplateInstance::printInstantiationTrace() int recursionDepth=0; for (TemplateInstance *cur = this; cur; cur = cur->tinst) { + cur->errors = true; if (cur->tinst && cur->tempdecl && cur->tinst->tempdecl && cur->tempdecl->loc.equals(cur->tinst->tempdecl->loc)) { @@ -6969,6 +7931,8 @@ void TemplateInstance::printInstantiationTrace() unsigned i = 0; for (TemplateInstance *cur = this; cur; cur = cur->tinst) { + cur->errors = true; + if (i == max_shown / 2) errorSupplemental(cur->loc, "... (%d instantiations, -v to show) ...", n_instantiations - max_shown); @@ -6980,21 +7944,6 @@ void TemplateInstance::printInstantiationTrace() } } -void TemplateInstance::inlineScan() -{ -#if LOG - printf("TemplateInstance::inlineScan('%s')\n", toChars()); -#endif - if (!errors && members) - { - for (size_t i = 0; i < members->dim; i++) - { - Dsymbol *s = (*members)[i]; - s->inlineScan(); - } - } -} - void TemplateInstance::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { Identifier *id = name; @@ -7027,10 +7976,10 @@ void TemplateInstance::toCBufferTiargs(OutBuffer *buf, HdrGenState *hgs) } else if (Expression *e = isExpression(oarg)) { - if (e->op == TOKint64 || // IntegerExp(10, true, false, 'c') - e->op == TOKfloat64 || // RealExp(3.14, 1.4i) - e->op == TOKnull || // NullExp - e->op == TOKstring || // StringExp + if (e->op == TOKint64 || + e->op == TOKfloat64 || + e->op == TOKnull || + e->op == TOKstring || e->op == TOKthis) { buf->writestring(e->toChars()); @@ -7063,14 +8012,7 @@ Dsymbol *TemplateInstance::toAlias() // Maybe we can resolve it if (scope) { - /* Anything that affects scope->offset must be - * done in lexical order. Fwd ref error if it is affected, otherwise allow. - */ - unsigned offset = scope->offset; - Scope *sc = scope; semantic(scope); -// if (offset != sc->offset) -// inst = NULL; // trigger fwd ref error } if (!inst) { @@ -7091,11 +8033,6 @@ Dsymbol *TemplateInstance::toAlias() return inst; } -AliasDeclaration *TemplateInstance::isAliasDeclaration() -{ - return aliasdecl; -} - const char *TemplateInstance::kind() { return "template instance"; @@ -7114,11 +8051,20 @@ char *TemplateInstance::toChars() char *s; toCBuffer(&buf, &hgs); - s = buf.toChars(); - buf.data = NULL; + s = buf.extractString(); return s; } +char *TemplateInstance::toPrettyCharsHelper() +{ + OutBuffer buf; + HdrGenState hgs; + hgs.fullQualification = 1; + toCBuffer(&buf, &hgs); + + return buf.extractString(); +} + int TemplateInstance::compare(RootObject *o) { TemplateInstance *ti = (TemplateInstance *)o; @@ -7183,7 +8129,81 @@ hash_t TemplateInstance::hashCode() return hash; } +/*********************************************** + * Returns true if this is not instantiated in non-root module, and + * is a part of non-speculative instantiatiation. + * + * Note: instantiatingModule does not stabilize until semantic analysis is completed, + * so don't call this function during semantic analysis to return precise result. + */ +bool TemplateInstance::needsCodegen() +{ + /* The issue is that if the importee is compiled with a different -debug + * setting than the importer, the importer may believe it exists + * in the compiled importee when it does not, when the instantiation + * is behind a conditional debug declaration. + */ + // workaround for Bugzilla 11239 + if (global.params.useUnitTests || + global.params.allInst || + global.params.debuglevel) + { + //printf("%s instantiatingModule = %s, speculative = %d, enclosing in nonRoot = %d\n", + // toPrettyChars(), instantiatingModule ? instantiatingModule->toChars() : NULL, speculative, + // enclosing && !enclosing->isInstantiated() && enclosing->inNonRoot()); + if (enclosing) + { + // Bugzilla 13415: If and only if the enclosing scope needs codegen, + // the nested templates would need code generation. + if (TemplateInstance *ti = enclosing->isInstantiated()) + return ti->needsCodegen(); + else + return !enclosing->inNonRoot(); + } + return true; + } + + if (instantiatingModule && !instantiatingModule->isRoot()) + { + Module *mi = instantiatingModule; + + // If mi imports any root modules, we still need to generate the code. + for (size_t i = 0; i < Module::amodules.dim; ++i) + { + Module *m = Module::amodules[i]; + m->insearch = 0; + } + bool importsRoot = false; + for (size_t i = 0; i < Module::amodules.dim; ++i) + { + Module *m = Module::amodules[i]; + if (m->isRoot() && mi->imports(m)) + { + importsRoot = true; + break; + } + } + for (size_t i = 0; i < Module::amodules.dim; ++i) + { + Module *m = Module::amodules[i]; + m->insearch = 0; + } + if (!importsRoot) + { + //printf("instantiated by %s %s\n", instantiatingModule->toChars(), toChars()); + return false; + } + } + + for (TemplateInstance *ti = this; ti; ti = ti->tinst) + { + //printf("\tti = %s spec = %d\n", ti->toChars(), ti->speculative); + if (!ti->speculative) + return true; + } + return false; +} /* ======================== TemplateMixin ================================ */ @@ -7205,7 +8225,7 @@ Dsymbol *TemplateMixin::syntaxCopy(Dsymbol *s) return tm; } -bool TemplateMixin::findTemplateDeclaration(Scope *sc) +bool TemplateMixin::findTempDecl(Scope *sc) { // Follow qualifications to find the TemplateDeclaration if (!tempdecl) @@ -7291,24 +8311,15 @@ void TemplateMixin::semantic(Scope *sc) #endif if (semanticRun != PASSinit) { - // This for when a class/struct contains mixin members, and - // is done over because of forward references - if (parent && toParent()->isAggregateDeclaration()) - { - if (sc->parent != parent) - return; - semanticRun = PASSsemantic; // do over - } - else - { + // When a class/struct contains mixin members, and is done over + // because of forward references, never reach here so semanticRun + // has been reset to PASSinit. #if LOG - printf("\tsemantic done\n"); + printf("\tsemantic done\n"); #endif - return; - } + return; } - if (semanticRun == PASSinit) - semanticRun = PASSsemantic; + semanticRun = PASSsemantic; #if LOG printf("\tdo semantic\n"); #endif @@ -7321,11 +8332,10 @@ void TemplateMixin::semantic(Scope *sc) scope = NULL; } - /* Run semantic on each argument, place results in tiargs[], * then find best match template with tiargs */ - if (!findTemplateDeclaration(sc) || + if (!findTempDecl(sc) || !semanticTiargs(sc) || !findBestMatch(sc, NULL)) { @@ -7344,7 +8354,7 @@ void TemplateMixin::semantic(Scope *sc) { // Forward reference //printf("forward reference - deferring\n"); - scope = scx ? scx : new Scope(*sc); + scope = scx ? scx : sc->copy(); scope->setNoFree(); scope->module->addDeferredSemantic(this); } @@ -7352,7 +8362,7 @@ void TemplateMixin::semantic(Scope *sc) } inst = this; - inst->errors = true; + errors = true; return; // error recovery } TemplateDeclaration *tempdecl = this->tempdecl->isTemplateDeclaration(); @@ -7379,7 +8389,7 @@ void TemplateMixin::semantic(Scope *sc) symtab = sc->parent->isScopeDsymbol()->symtab; L1: assert(symtab); - int num = (int)_aaLen(symtab->tab) + 1; + int num = (int)dmd_aaLen(symtab->tab) + 1; ident = Lexer::uniqueId(s, num); symtab->insert(this); } @@ -7441,7 +8451,9 @@ void TemplateMixin::semantic(Scope *sc) // Copy the syntax trees from the TemplateDeclaration if (scx && members && !errors) - {} // Don't copy again so they were previously created. + { + // Don't copy again so they were previously created. + } else members = Dsymbol::arraySyntaxCopy(tempdecl->members); if (!members) @@ -7487,10 +8499,7 @@ void TemplateMixin::semantic(Scope *sc) #if LOG printf("\tdo semantic() on template instance members '%s'\n", toChars()); #endif - Scope *sc2; - sc2 = argscope->push(this); - sc2->offset = sc->offset; - + Scope *sc2 = argscope->push(this); size_t deferred_dim = Module::deferred.dim; static int nest; @@ -7508,6 +8517,12 @@ void TemplateMixin::semantic(Scope *sc) s->setScope(sc2); } + for (size_t i = 0; i < members->dim; i++) + { + Dsymbol *s = (*members)[i]; + s->importAll(sc2); + } + for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; @@ -7516,8 +8531,6 @@ void TemplateMixin::semantic(Scope *sc) nest--; - sc->offset = sc2->offset; - if (!sc->func && Module::deferred.dim > deferred_dim) { sc2->pop(); @@ -7541,7 +8554,7 @@ void TemplateMixin::semantic(Scope *sc) { // Forward reference //printf("forward reference - deferring\n"); - scope = scx ? scx : new Scope(*sc); + scope = scx ? scx : sc->copy(); scope->setNoFree(); scope->module->addDeferredSemantic(this); } @@ -7622,11 +8635,6 @@ void TemplateMixin::semantic3(Scope *sc) } } -void TemplateMixin::inlineScan() -{ - TemplateInstance::inlineScan(); -} - const char *TemplateMixin::kind() { return "mixin"; @@ -7642,7 +8650,8 @@ int TemplateMixin::apply(Dsymbol_apply_ft_t fp, void *param) if (members) { for (size_t i = 0; i < members->dim; i++) - { Dsymbol *s = (*members)[i]; + { + Dsymbol *s = (*members)[i]; if (s) { if (s->apply(fp, param)) @@ -7680,7 +8689,8 @@ void TemplateMixin::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, if (members) { for (size_t i = 0; i < members->dim; i++) - { Dsymbol *s = (*members)[i]; + { + Dsymbol *s = (*members)[i]; //printf("\t%s\n", s->toChars()); s->setFieldOffset(ad, poffset, isunion); } @@ -7694,8 +8704,7 @@ char *TemplateMixin::toChars() char *s; TemplateInstance::toCBuffer(&buf, &hgs); - s = buf.toChars(); - buf.data = NULL; + s = buf.extractString(); return s; } @@ -7708,11 +8717,9 @@ void TemplateMixin::toCBuffer(OutBuffer *buf, HdrGenState *hgs) if (ident && memcmp(ident->string, "__mixin", 7) != 0) { - buf->writebyte(' '); + buf->writeByte(' '); buf->writestring(ident->toChars()); } - buf->writebyte(';'); + buf->writeByte(';'); buf->writenl(); } - - diff --git a/gcc/d/dfrontend/template.h b/gcc/d/dfrontend/template.h index b1cea6241..aa9fe7686 100644 --- a/gcc/d/dfrontend/template.h +++ b/gcc/d/dfrontend/template.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2013 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/template.h + */ #ifndef DMD_TEMPLATE_H #define DMD_TEMPLATE_H @@ -46,7 +47,8 @@ class Tuple : public RootObject public: Objects objects; - int dyncast() { return DYNCAST_TUPLE; } // kludge for template.isType() + // kludge for template.isType() + int dyncast() { return DYNCAST_TUPLE; } }; struct TemplatePrevious @@ -91,9 +93,7 @@ class TemplateDeclaration : public ScopeDsymbol const char *kind(); char *toChars(); - void emitComment(Scope *sc); PROT prot(); -// void toDocBuffer(OutBuffer *buf); bool evaluateConstraint(TemplateInstance *ti, Scope *sc, Scope *paramscope, Objects *dedtypes, FuncDeclaration *fd); @@ -117,24 +117,33 @@ class TemplateDeclaration : public ScopeDsymbol void accept(Visitor *v) { v->visit(this); } }; +/* For type-parameter: + * template Foo(ident) // specType is set to NULL + * template Foo(ident : specType) + * For value-parameter: + * template Foo(valType ident) // specValue is set to NULL + * template Foo(valType ident : specValue) + * For alias-parameter: + * template Foo(alias ident) + * For this-parameter: + * template Foo(this ident) + */ class TemplateParameter { public: - /* For type-parameter: - * template Foo(ident) // specType is set to NULL - * template Foo(ident : specType) - * For value-parameter: - * template Foo(valType ident) // specValue is set to NULL - * template Foo(valType ident : specValue) - * For alias-parameter: - * template Foo(alias ident) - * For this-parameter: - * template Foo(this ident) - */ - Loc loc; Identifier *ident; + /* True if this is a part of precedent parameter specialization pattern. + * + * template A(T : X!TL, alias X, TL...) {} + * // X and TL are dependent template parameter + * + * A dependent template parameter should return MATCHexact in matchArg() + * to respect the match level of the corresponding precedent parameter. + */ + bool dependent; + Declaration *sparam; TemplateParameter(Loc loc, Identifier *ident); @@ -152,6 +161,7 @@ class TemplateParameter virtual void toCBuffer(OutBuffer *buf, HdrGenState *hgs) = 0; virtual RootObject *specialization() = 0; virtual RootObject *defaultArg(Loc loc, Scope *sc) = 0; + virtual bool hasDefaultArg() = 0; /* If TemplateParameter's match as far as overloading goes. */ @@ -167,12 +177,12 @@ class TemplateParameter virtual void *dummyArg() = 0; }; +/* Syntax: + * ident : specType = defaultType + */ class TemplateTypeParameter : public TemplateParameter { public: - /* Syntax: - * ident : specType = defaultType - */ Type *specType; // type parameter: if !=NULL, this is the type specialization Type *defaultType; @@ -188,18 +198,18 @@ class TemplateTypeParameter : public TemplateParameter void toCBuffer(OutBuffer *buf, HdrGenState *hgs); RootObject *specialization(); RootObject *defaultArg(Loc loc, Scope *sc); + bool hasDefaultArg(); int overloadMatch(TemplateParameter *); MATCH matchArg(Scope *sc, RootObject *oarg, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam); void *dummyArg(); }; +/* Syntax: + * this ident : specType = defaultType + */ class TemplateThisParameter : public TemplateTypeParameter { public: - /* Syntax: - * this ident : specType = defaultType - */ - TemplateThisParameter(Loc loc, Identifier *ident, Type *specType, Type *defaultType); TemplateThisParameter *isTemplateThisParameter(); @@ -207,13 +217,12 @@ class TemplateThisParameter : public TemplateTypeParameter void toCBuffer(OutBuffer *buf, HdrGenState *hgs); }; +/* Syntax: + * valType ident : specValue = defaultValue + */ class TemplateValueParameter : public TemplateParameter { public: - /* Syntax: - * valType ident : specValue = defaultValue - */ - Type *valType; Expression *specValue; Expression *defaultValue; @@ -230,18 +239,18 @@ class TemplateValueParameter : public TemplateParameter void toCBuffer(OutBuffer *buf, HdrGenState *hgs); RootObject *specialization(); RootObject *defaultArg(Loc loc, Scope *sc); + bool hasDefaultArg(); int overloadMatch(TemplateParameter *); MATCH matchArg(Scope *sc, RootObject *oarg, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam); void *dummyArg(); }; +/* Syntax: + * specType ident : specAlias = defaultAlias + */ class TemplateAliasParameter : public TemplateParameter { public: - /* Syntax: - * specType ident : specAlias = defaultAlias - */ - Type *specType; RootObject *specAlias; RootObject *defaultAlias; @@ -258,18 +267,18 @@ class TemplateAliasParameter : public TemplateParameter void toCBuffer(OutBuffer *buf, HdrGenState *hgs); RootObject *specialization(); RootObject *defaultArg(Loc loc, Scope *sc); + bool hasDefaultArg(); int overloadMatch(TemplateParameter *); MATCH matchArg(Scope *sc, RootObject *oarg, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam); void *dummyArg(); }; +/* Syntax: + * ident ... + */ class TemplateTupleParameter : public TemplateParameter { public: - /* Syntax: - * ident ... - */ - TemplateTupleParameter(Loc loc, Identifier *ident); TemplateTupleParameter *isTemplateTupleParameter(); @@ -280,46 +289,52 @@ class TemplateTupleParameter : public TemplateParameter void toCBuffer(OutBuffer *buf, HdrGenState *hgs); RootObject *specialization(); RootObject *defaultArg(Loc loc, Scope *sc); + bool hasDefaultArg(); int overloadMatch(TemplateParameter *); MATCH matchArg(Loc loc, Scope *sc, Objects *tiargs, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam); MATCH matchArg(Scope *sc, RootObject *oarg, size_t i, TemplateParameters *parameters, Objects *dedtypes, Declaration **psparam); void *dummyArg(); }; +/* Given: + * foo!(args) => + * name = foo + * tiargs = args + */ class TemplateInstance : public ScopeDsymbol { public: - /* Given: - * foo!(args) => - * name = foo - * tiargs = args - */ Identifier *name; - Objects *tiargs; // Array of Types/Expressions of template - // instance arguments [int*, char, 10*10] - Objects tdtypes; // Array of Types/Expressions corresponding - // to TemplateDeclaration.parameters - // [int, char, 100] + // Array of Types/Expressions of template + // instance arguments [int*, char, 10*10] + Objects *tiargs; + + // Array of Types/Expressions corresponding + // to TemplateDeclaration.parameters + // [int, char, 100] + Objects tdtypes; Dsymbol *tempdecl; // referenced by foo.bar.abc + Dsymbol *enclosing; // if referencing local symbols, this is the context + Dsymbol *aliasdecl; // !=NULL if instance is an alias for its sole member TemplateInstance *inst; // refer to existing instance TemplateInstance *tinst; // enclosing template instance ScopeDsymbol *argsym; // argument symbol table - AliasDeclaration *aliasdecl; // !=NULL if instance is an alias for its - // sole member - WithScopeSymbol *withsym; // if a member of a with statement int nest; // for recursion detection bool semantictiargsdone; // has semanticTiargs() been done? bool havetempdecl; // if used second constructor - bool speculative; // if only instantiated with errors gagged - Dsymbol *enclosing; // if referencing local symbols, this is the context + bool gagged; // if the instantiation is done with error gagging hash_t hash; // cached result of hashCode() Expressions *fargs; // for function template, these are the function arguments - Module *instantiatingModule; // the top module that instantiated this instance TemplateInstances* deferred; + // Used to determine the instance needs code generation. + // Note that these are inaccurate until semantic analysis phase completed. + Module *instantiatingModule; // the top module that instantiated this instance + bool speculative; // if the instantiation is speculative + TemplateInstance(Loc loc, Identifier *temp_id); TemplateInstance(Loc loc, TemplateDeclaration *tempdecl, Objects *tiargs); static Objects *arraySyntaxCopy(Objects *objs); @@ -328,25 +343,25 @@ class TemplateInstance : public ScopeDsymbol void semantic(Scope *sc); void semantic2(Scope *sc); void semantic3(Scope *sc); - void inlineScan(); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void toCBufferTiargs(OutBuffer *buf, HdrGenState *hgs); Dsymbol *toAlias(); // resolve real symbol const char *kind(); bool oneMember(Dsymbol **ps, Identifier *ident); char *toChars(); - const char *mangle(bool isv = false); + char* toPrettyCharsHelper(); void printInstantiationTrace(); Identifier *getIdent(); int compare(RootObject *o); hash_t hashCode(); - void toObjFile(int multiobj); // compile to .obj file + bool needsCodegen(); + void toObjFile(bool multiobj); // compile to .obj file // Internal - bool findTemplateDeclaration(Scope *sc); - bool updateTemplateDeclaration(Scope *sc, Dsymbol *s); - static void semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags); + bool findTempDecl(Scope *sc, WithScopeSymbol **pwithsym); + bool updateTempDecl(Scope *sc, Dsymbol *s); + static bool semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags); bool semanticTiargs(Scope *sc); bool findBestMatch(Scope *sc, Expressions *fargs); bool needsTypeInference(Scope *sc, int flag = 0); @@ -358,7 +373,6 @@ class TemplateInstance : public ScopeDsymbol void trySemantic3(Scope *sc2); TemplateInstance *isTemplateInstance() { return this; } - AliasDeclaration *isAliasDeclaration(); void accept(Visitor *v) { v->visit(this); } }; @@ -372,7 +386,6 @@ class TemplateMixin : public TemplateInstance void semantic(Scope *sc); void semantic2(Scope *sc); void semantic3(Scope *sc); - void inlineScan(); const char *kind(); bool oneMember(Dsymbol **ps, Identifier *ident); int apply(Dsymbol_apply_ft_t fp, void *param); @@ -381,9 +394,9 @@ class TemplateMixin : public TemplateInstance char *toChars(); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - void toObjFile(int multiobj); // compile to .obj file + void toObjFile(bool multiobj); // compile to .obj file - bool findTemplateDeclaration(Scope *sc); + bool findTempDecl(Scope *sc); TemplateMixin *isTemplateMixin() { return this; } void accept(Visitor *v) { v->visit(this); } diff --git a/gcc/d/dfrontend/traits.c b/gcc/d/dfrontend/traits.c index 9870239de..04118d4ca 100644 --- a/gcc/d/dfrontend/traits.c +++ b/gcc/d/dfrontend/traits.c @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/traits.c + */ #include #include @@ -35,6 +36,7 @@ #include "attrib.h" #include "hdrgen.h" #include "parse.h" +#include "speller.h" #define LOGSEMANTIC 0 @@ -100,13 +102,13 @@ static void collectUnitTests(Dsymbols *symbols, AA *uniqueUnitTests, Expressions UnitTestDeclaration *unitTest = symbol->isUnitTestDeclaration(); if (unitTest) { - if (!_aaGetRvalue(uniqueUnitTests, unitTest)) + if (!dmd_aaGetRvalue(uniqueUnitTests, (void *)unitTest)) { FuncAliasDeclaration* alias = new FuncAliasDeclaration(unitTest, 0); alias->protection = unitTest->protection; Expression* e = new DsymbolExp(Loc(), alias); unitTests->push(e); - bool* value = (bool*) _aaGet(&uniqueUnitTests, unitTest); + bool* value = (bool*) dmd_aaGet(&uniqueUnitTests, (void *)unitTest); *value = true; } } @@ -135,20 +137,20 @@ bool isTypeStaticArray(Type *t) { return t->toBasetype()->ty == Tsarray; } bool isTypeAbstractClass(Type *t) { return t->toBasetype()->ty == Tclass && ((TypeClass *)t->toBasetype())->sym->isAbstract(); } bool isTypeFinalClass(Type *t) { return t->toBasetype()->ty == Tclass && (((TypeClass *)t->toBasetype())->sym->storage_class & STCfinal) != 0; } -Expression *TraitsExp::isTypeX(bool (*fp)(Type *t)) +Expression *isTypeX(TraitsExp *e, bool (*fp)(Type *t)) { int result = 0; - if (!args || !args->dim) + if (!e->args || !e->args->dim) goto Lfalse; - for (size_t i = 0; i < args->dim; i++) + for (size_t i = 0; i < e->args->dim; i++) { - Type *t = getType((*args)[i]); + Type *t = getType((*e->args)[i]); if (!t || !fp(t)) goto Lfalse; } result = 1; Lfalse: - return new IntegerExp(loc, result, Type::tbool); + return new IntegerExp(e->loc, result, Type::tbool); } bool isFuncAbstractFunction(FuncDeclaration *f) { return f->isAbstract(); } @@ -158,14 +160,14 @@ bool isFuncFinalFunction(FuncDeclaration *f) { return f->isFinalFunc(); } bool isFuncStaticFunction(FuncDeclaration *f) { return !f->needThis() && !f->isNested(); } bool isFuncOverrideFunction(FuncDeclaration *f) { return f->isOverride(); } -Expression *TraitsExp::isFuncX(bool (*fp)(FuncDeclaration *f)) +Expression *isFuncX(TraitsExp *e, bool (*fp)(FuncDeclaration *f)) { int result = 0; - if (!args || !args->dim) + if (!e->args || !e->args->dim) goto Lfalse; - for (size_t i = 0; i < args->dim; i++) + for (size_t i = 0; i < e->args->dim; i++) { - Dsymbol *s = getDsymbol((*args)[i]); + Dsymbol *s = getDsymbol((*e->args)[i]); if (!s) goto Lfalse; FuncDeclaration *f = s->isFuncDeclaration(); @@ -174,21 +176,21 @@ Expression *TraitsExp::isFuncX(bool (*fp)(FuncDeclaration *f)) } result = 1; Lfalse: - return new IntegerExp(loc, result, Type::tbool); + return new IntegerExp(e->loc, result, Type::tbool); } bool isDeclRef(Declaration *d) { return d->isRef(); } bool isDeclOut(Declaration *d) { return d->isOut(); } bool isDeclLazy(Declaration *d) { return (d->storage_class & STClazy) != 0; } -Expression *TraitsExp::isDeclX(bool (*fp)(Declaration *d)) +Expression *isDeclX(TraitsExp *e, bool (*fp)(Declaration *d)) { int result = 0; - if (!args || !args->dim) + if (!e->args || !e->args->dim) goto Lfalse; - for (size_t i = 0; i < args->dim; i++) + for (size_t i = 0; i < e->args->dim; i++) { - Dsymbol *s = getDsymbol((*args)[i]); + Dsymbol *s = getDsymbol((*e->args)[i]); if (!s) goto Lfalse; Declaration *d = s->isDeclaration(); @@ -197,72 +199,155 @@ Expression *TraitsExp::isDeclX(bool (*fp)(Declaration *d)) } result = 1; Lfalse: - return new IntegerExp(loc, result, Type::tbool); + return new IntegerExp(e->loc, result, Type::tbool); +} + +// callback for TypeFunction::attributesApply +struct PushAttributes +{ + Expressions *mods; + + static int fp(void *param, const char *str) + { + PushAttributes *p = (PushAttributes *)param; + p->mods->push(new StringExp(Loc(), (char *)str)); + return 0; + } +}; + +const char* traits[] = { + "isAbstractClass", + "isArithmetic", + "isAssociativeArray", + "isFinalClass", + "isPOD", + "isNested", + "isFloating", + "isIntegral", + "isScalar", + "isStaticArray", + "isUnsigned", + "isVirtualFunction", + "isVirtualMethod", + "isAbstractFunction", + "isFinalFunction", + "isOverrideFunction", + "isStaticFunction", + "isRef", + "isOut", + "isLazy", + "hasMember", + "identifier", + "getProtection", + "parent", + "getMember", + "getOverloads", + "getVirtualFunctions", + "getVirtualMethods", + "classInstanceSize", + "allMembers", + "derivedMembers", + "isSame", + "compiles", + "parameters", + "getAliasThis", + "getAttributes", + "getFunctionAttributes", + "getUnitTests", + "getVirtualIndex", + NULL +}; + +StringTable traitsStringTable; + +void initTraitsStringTable() +{ + traitsStringTable._init(); + + for (size_t idx = 0; ; idx++) + { + const char *s = traits[idx]; + if (!s) break; + StringValue *sv = traitsStringTable.insert(s, strlen(s)); + sv->ptrvalue = (void *)traits[idx]; + } +} + +void *trait_search_fp(void *arg, const char *seed) +{ + //printf("trait_search_fp('%s')\n", seed); + size_t len = strlen(seed); + if (!len) + return NULL; + + StringValue *sv = traitsStringTable.lookup(seed, len); + return sv ? (void*)sv->ptrvalue : NULL; } -Expression *TraitsExp::semantic(Scope *sc) +Expression *semanticTraits(TraitsExp *e, Scope *sc) { #if LOGSEMANTIC - printf("TraitsExp::semantic() %s\n", toChars()); + printf("TraitsExp::semantic() %s\n", e->toChars()); #endif - if (ident != Id::compiles && ident != Id::isSame && - ident != Id::identifier && ident != Id::getProtection) + if (e->ident != Id::compiles && e->ident != Id::isSame && + e->ident != Id::identifier && e->ident != Id::getProtection) { - TemplateInstance::semanticTiargs(loc, sc, args, 1); + if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 1)) + return new ErrorExp(); } - size_t dim = args ? args->dim : 0; - Declaration *d; + size_t dim = e->args ? e->args->dim : 0; - if (ident == Id::isArithmetic) + if (e->ident == Id::isArithmetic) { - return isTypeX(&isTypeArithmetic); + return isTypeX(e, &isTypeArithmetic); } - else if (ident == Id::isFloating) + else if (e->ident == Id::isFloating) { - return isTypeX(&isTypeFloating); + return isTypeX(e, &isTypeFloating); } - else if (ident == Id::isIntegral) + else if (e->ident == Id::isIntegral) { - return isTypeX(&isTypeIntegral); + return isTypeX(e, &isTypeIntegral); } - else if (ident == Id::isScalar) + else if (e->ident == Id::isScalar) { - return isTypeX(&isTypeScalar); + return isTypeX(e, &isTypeScalar); } - else if (ident == Id::isUnsigned) + else if (e->ident == Id::isUnsigned) { - return isTypeX(&isTypeUnsigned); + return isTypeX(e, &isTypeUnsigned); } - else if (ident == Id::isAssociativeArray) + else if (e->ident == Id::isAssociativeArray) { - return isTypeX(&isTypeAssociativeArray); + return isTypeX(e, &isTypeAssociativeArray); } - else if (ident == Id::isStaticArray) + else if (e->ident == Id::isStaticArray) { - return isTypeX(&isTypeStaticArray); + return isTypeX(e, &isTypeStaticArray); } - else if (ident == Id::isAbstractClass) + else if (e->ident == Id::isAbstractClass) { - return isTypeX(&isTypeAbstractClass); + return isTypeX(e, &isTypeAbstractClass); } - else if (ident == Id::isFinalClass) + else if (e->ident == Id::isFinalClass) { - return isTypeX(&isTypeFinalClass); + return isTypeX(e, &isTypeFinalClass); } - else if (ident == Id::isPOD) + else if (e->ident == Id::isPOD) { if (dim != 1) goto Ldimerror; - RootObject *o = (*args)[0]; + RootObject *o = (*e->args)[0]; Type *t = isType(o); StructDeclaration *sd; if (!t) { - error("type expected as second argument of __traits %s instead of %s", ident->toChars(), o->toChars()); + e->error("type expected as second argument of __traits %s instead of %s", e->ident->toChars(), o->toChars()); goto Lfalse; } - if (t->toBasetype()->ty == Tstruct - && ((sd = (StructDeclaration *)(((TypeStruct *)t->toBasetype())->sym)) != NULL)) + Type *tb = t->baseElemOf(); + if (tb->ty == Tstruct + && ((sd = (StructDeclaration *)(((TypeStruct *)tb)->sym)) != NULL)) { if (sd->isPOD()) goto Ltrue; @@ -271,11 +356,11 @@ Expression *TraitsExp::semantic(Scope *sc) } goto Ltrue; } - else if (ident == Id::isNested) + else if (e->ident == Id::isNested) { if (dim != 1) goto Ldimerror; - RootObject *o = (*args)[0]; + RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); AggregateDeclaration *a; FuncDeclaration *f; @@ -296,61 +381,63 @@ Expression *TraitsExp::semantic(Scope *sc) goto Lfalse; } - error("aggregate or function expected instead of '%s'", o->toChars()); + e->error("aggregate or function expected instead of '%s'", o->toChars()); goto Lfalse; } - else if (ident == Id::isAbstractFunction) + else if (e->ident == Id::isAbstractFunction) { - return isFuncX(&isFuncAbstractFunction); + return isFuncX(e, &isFuncAbstractFunction); } - else if (ident == Id::isVirtualFunction) + else if (e->ident == Id::isVirtualFunction) { - return isFuncX(&isFuncVirtualFunction); + return isFuncX(e, &isFuncVirtualFunction); } - else if (ident == Id::isVirtualMethod) + else if (e->ident == Id::isVirtualMethod) { - return isFuncX(&isFuncVirtualMethod); + return isFuncX(e, &isFuncVirtualMethod); } - else if (ident == Id::isFinalFunction) + else if (e->ident == Id::isFinalFunction) { - return isFuncX(&isFuncFinalFunction); + return isFuncX(e, &isFuncFinalFunction); } - else if (ident == Id::isOverrideFunction) + else if (e->ident == Id::isOverrideFunction) { - return isFuncX(&isFuncOverrideFunction); + return isFuncX(e, &isFuncOverrideFunction); } - else if (ident == Id::isStaticFunction) + else if (e->ident == Id::isStaticFunction) { - return isFuncX(&isFuncStaticFunction); + return isFuncX(e, &isFuncStaticFunction); } - else if (ident == Id::isRef) + else if (e->ident == Id::isRef) { - return isDeclX(&isDeclRef); + return isDeclX(e, &isDeclRef); } - else if (ident == Id::isOut) + else if (e->ident == Id::isOut) { - return isDeclX(&isDeclOut); + return isDeclX(e, &isDeclOut); } - else if (ident == Id::isLazy) + else if (e->ident == Id::isLazy) { - return isDeclX(&isDeclLazy); + return isDeclX(e, &isDeclLazy); } - else if (ident == Id::identifier) - { // Get identifier for symbol as a string literal - + else if (e->ident == Id::identifier) + { + // Get identifier for symbol as a string literal /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that * a symbol should not be folded to a constant. * Bit 1 means don't convert Parameter to Type if Parameter has an identifier */ - TemplateInstance::semanticTiargs(loc, sc, args, 2); + if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 2)) + return new ErrorExp(); if (dim != 1) goto Ldimerror; - RootObject *o = (*args)[0]; + RootObject *o = (*e->args)[0]; Parameter *po = isParameter(o); Identifier *id; if (po) - { id = po->ident; + { + id = po->ident; assert(id); } else @@ -358,47 +445,48 @@ Expression *TraitsExp::semantic(Scope *sc) Dsymbol *s = getDsymbol(o); if (!s || !s->ident) { - error("argument %s has no identifier", o->toChars()); + e->error("argument %s has no identifier", o->toChars()); goto Lfalse; } id = s->ident; } - StringExp *se = new StringExp(loc, id->toChars()); + StringExp *se = new StringExp(e->loc, id->toChars()); return se->semantic(sc); } - else if (ident == Id::getProtection) + else if (e->ident == Id::getProtection) { if (dim != 1) goto Ldimerror; Scope *sc2 = sc->push(); sc2->flags = sc->flags | SCOPEnoaccesscheck; - TemplateInstance::semanticTiargs(loc, sc2, args, 1); + bool ok = TemplateInstance::semanticTiargs(e->loc, sc2, e->args, 1); sc2->pop(); - RootObject *o = (*args)[0]; + if (!ok) + return new ErrorExp(); + + RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); if (!s) { if (!isError(o)) - error("argument %s has no protection", o->toChars()); + e->error("argument %s has no protection", o->toChars()); goto Lfalse; } if (s->scope) s->semantic(s->scope); - PROT protection = s->prot(); - - const char *protName = Pprotectionnames[protection]; + const char *protName = protectionToChars(s->prot()); assert(protName); - StringExp *se = new StringExp(loc, (char *) protName); + StringExp *se = new StringExp(e->loc, (char *) protName); return se->semantic(sc); } - else if (ident == Id::parent) + else if (e->ident == Id::parent) { if (dim != 1) goto Ldimerror; - RootObject *o = (*args)[0]; + RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); if (s) { @@ -409,34 +497,57 @@ Expression *TraitsExp::semantic(Scope *sc) } if (!s || s->isImport()) { - error("argument %s has no parent", o->toChars()); + e->error("argument %s has no parent", o->toChars()); goto Lfalse; } - return (new DsymbolExp(loc, s))->semantic(sc); + + if (FuncDeclaration *f = s->isFuncDeclaration()) + { + if (TemplateDeclaration *td = getFuncTemplateDecl(f)) + { + if (td->overroot) // if not start of overloaded list of TemplateDeclaration's + td = td->overroot; // then get the start + Expression *ex = new TemplateExp(e->loc, td, f); + ex = ex->semantic(sc); + return ex; + } + + if (FuncLiteralDeclaration *fld = f->isFuncLiteralDeclaration()) + { + // Directly translate to VarExp instead of FuncExp + Expression *ex = new VarExp(e->loc, fld, 1); + return ex->semantic(sc); + } + } + + return (new DsymbolExp(e->loc, s))->semantic(sc); } - else if (ident == Id::hasMember || - ident == Id::getMember || - ident == Id::getOverloads || - ident == Id::getVirtualMethods || - ident == Id::getVirtualFunctions) + else if (e->ident == Id::hasMember || + e->ident == Id::getMember || + e->ident == Id::getOverloads || + e->ident == Id::getVirtualMethods || + e->ident == Id::getVirtualFunctions) { if (dim != 2) goto Ldimerror; - RootObject *o = (*args)[0]; - Expression *e = isExpression((*args)[1]); - if (!e) - { error("expression expected as second argument of __traits %s", ident->toChars()); + RootObject *o = (*e->args)[0]; + Expression *ex = isExpression((*e->args)[1]); + if (!ex) + { + e->error("expression expected as second argument of __traits %s", e->ident->toChars()); goto Lfalse; } - e = e->ctfeInterpret(); - StringExp *se = e->toString(); + ex = ex->ctfeInterpret(); + StringExp *se = ex->toStringExp(); if (!se || se->length() == 0) - { error("string expected as second argument of __traits %s instead of %s", ident->toChars(), e->toChars()); + { + e->error("string expected as second argument of __traits %s instead of %s", e->ident->toChars(), ex->toChars()); goto Lfalse; } se = se->toUTF8(sc); if (se->sz != 1) - { error("string must be chars"); + { + e->error("string must be chars"); goto Lfalse; } Identifier *id = Lexer::idPool((char *)se->string); @@ -445,23 +556,25 @@ Expression *TraitsExp::semantic(Scope *sc) */ Dsymbol *sym = getDsymbol(o); if (sym) - { e = new DsymbolExp(loc, sym); - e = new DotIdExp(loc, e, id); + { + ex = new DsymbolExp(e->loc, sym); + ex = new DotIdExp(e->loc, ex, id); } else if (Type *t = isType(o)) - e = typeDotIdExp(loc, t, id); - else if (Expression *ex = isExpression(o)) - e = new DotIdExp(loc, ex, id); + ex = typeDotIdExp(e->loc, t, id); + else if (Expression *ex2 = isExpression(o)) + ex = new DotIdExp(e->loc, ex2, id); else - { error("invalid first argument"); + { + e->error("invalid first argument"); goto Lfalse; } - if (ident == Id::hasMember) + if (e->ident == Id::hasMember) { if (sym) { - Dsymbol *sm = sym->search(loc, id); + Dsymbol *sm = sym->search(e->loc, id); if (sm) goto Ltrue; } @@ -469,138 +582,194 @@ Expression *TraitsExp::semantic(Scope *sc) /* Take any errors as meaning it wasn't found */ Scope *sc2 = sc->push(); - e = e->trySemantic(sc2); + ex = ex->trySemantic(sc2); sc2->pop(); - if (!e) + if (!ex) goto Lfalse; else goto Ltrue; } - else if (ident == Id::getMember) + else if (e->ident == Id::getMember) { - e = e->semantic(sc); - return e; + ex = ex->semantic(sc); + return ex; } - else if (ident == Id::getVirtualFunctions || - ident == Id::getVirtualMethods || - ident == Id::getOverloads) + else if (e->ident == Id::getVirtualFunctions || + e->ident == Id::getVirtualMethods || + e->ident == Id::getOverloads) { unsigned errors = global.errors; - Expression *ex = e; - e = e->semantic(sc); + Expression *eorig = ex; + ex = ex->semantic(sc); if (errors < global.errors) - error("%s cannot be resolved", ex->toChars()); + e->error("%s cannot be resolved", eorig->toChars()); - /* Create tuple of functions of e + /* Create tuple of functions of ex */ - //e->dump(0); + //ex->print(); Expressions *exps = new Expressions(); FuncDeclaration *f; - if (e->op == TOKvar) - { VarExp *ve = (VarExp *)e; + if (ex->op == TOKvar) + { + VarExp *ve = (VarExp *)ex; f = ve->var->isFuncDeclaration(); - e = NULL; + ex = NULL; } - else if (e->op == TOKdotvar) - { DotVarExp *dve = (DotVarExp *)e; + else if (ex->op == TOKdotvar) + { + DotVarExp *dve = (DotVarExp *)ex; f = dve->var->isFuncDeclaration(); if (dve->e1->op == TOKdottype || dve->e1->op == TOKthis) - e = NULL; + ex = NULL; else - e = dve->e1; + ex = dve->e1; } else f = NULL; Ptrait p; p.exps = exps; - p.e1 = e; - p.ident = ident; + p.e1 = ex; + p.ident = e->ident; overloadApply(f, &p, &fptraits); - TupleExp *tup = new TupleExp(loc, exps); + TupleExp *tup = new TupleExp(e->loc, exps); return tup->semantic(sc); } else assert(0); } - else if (ident == Id::classInstanceSize) + else if (e->ident == Id::classInstanceSize) { if (dim != 1) goto Ldimerror; - RootObject *o = (*args)[0]; + RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); ClassDeclaration *cd; if (!s || (cd = s->isClassDeclaration()) == NULL) { - error("first argument is not a class"); + e->error("first argument is not a class"); + goto Lfalse; + } + if (cd->sizeok == SIZEOKnone) + { + if (cd->scope) + cd->semantic(cd->scope); + } + if (cd->sizeok != SIZEOKdone) + { + e->error("%s %s is forward referenced", cd->kind(), cd->toChars()); goto Lfalse; } - return new IntegerExp(loc, cd->structsize, Type::tsize_t); + return new IntegerExp(e->loc, cd->structsize, Type::tsize_t); } - else if (ident == Id::getAliasThis) + else if (e->ident == Id::getAliasThis) { if (dim != 1) goto Ldimerror; - RootObject *o = (*args)[0]; + RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); AggregateDeclaration *ad; if (!s || (ad = s->isAggregateDeclaration()) == NULL) { - error("argument is not an aggregate type"); + e->error("argument is not an aggregate type"); goto Lfalse; } Expressions *exps = new Expressions(); if (ad->aliasthis) - exps->push(new StringExp(loc, ad->aliasthis->ident->toChars())); + exps->push(new StringExp(e->loc, ad->aliasthis->ident->toChars())); - Expression *e = new TupleExp(loc, exps); - e = e->semantic(sc); - return e; + Expression *ex = new TupleExp(e->loc, exps); + ex = ex->semantic(sc); + return ex; } - else if (ident == Id::getAttributes) + else if (e->ident == Id::getAttributes) { if (dim != 1) goto Ldimerror; - RootObject *o = (*args)[0]; + RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); if (!s) { #if 0 - Expression *e = isExpression(o); + Expression *x = isExpression(o); Type *t = isType(o); - if (e) printf("e = %s %s\n", Token::toChars(e->op), e->toChars()); + if (x) printf("e = %s %s\n", Token::toChars(x->op), x->toChars()); if (t) printf("t = %d %s\n", t->ty, t->toChars()); #endif - error("first argument is not a symbol"); + e->error("first argument is not a symbol"); goto Lfalse; } - //printf("getAttributes %s, attrs = %p, scope = %p\n", s->toChars(), s->userAttributes, s->userAttributesScope); + //printf("getAttributes %s, attrs = %p, scope = %p\n", s->toChars(), s->userAttribDecl, s->scope); UserAttributeDeclaration *udad = s->userAttribDecl; - TupleExp *tup = new TupleExp(loc, udad ? udad->getAttributes() : new Expressions()); + TupleExp *tup = new TupleExp(e->loc, udad ? udad->getAttributes() : new Expressions()); return tup->semantic(sc); } - else if (ident == Id::allMembers || ident == Id::derivedMembers) + else if (e->ident == Id::getFunctionAttributes) { + /// extract all function attributes as a tuple (const/shared/inout/pure/nothrow/etc) except UDAs. + if (dim != 1) goto Ldimerror; - RootObject *o = (*args)[0]; + RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); - ScopeDsymbol *sd; + Type *t = isType(o); + TypeFunction *tf = NULL; + + if (s) + { + if (FuncDeclaration *f = s->isFuncDeclaration()) + t = f->type; + else if (VarDeclaration *v = s->isVarDeclaration()) + t = v->type; + } + if (t) + { + if (t->ty == Tfunction) + tf = (TypeFunction *)t; + else if (t->ty == Tdelegate) + tf = (TypeFunction *)t->nextOf(); + else if (t->ty == Tpointer && t->nextOf()->ty == Tfunction) + tf = (TypeFunction *)t->nextOf(); + } + if (!tf) + { + e->error("first argument is not a function"); + goto Lfalse; + } + + Expressions *mods = new Expressions(); + + PushAttributes pa; + pa.mods = mods; + + tf->modifiersApply(&pa, &PushAttributes::fp); + tf->attributesApply(&pa, &PushAttributes::fp, TRUSTformatSystem); + + TupleExp *tup = new TupleExp(e->loc, mods); + return tup->semantic(sc); + } + else if (e->ident == Id::allMembers || e->ident == Id::derivedMembers) + { + if (dim != 1) + goto Ldimerror; + RootObject *o = (*e->args)[0]; + Dsymbol *s = getDsymbol(o); + ScopeDsymbol *sds; if (!s) { - error("argument has no members"); + e->error("argument has no members"); goto Lfalse; } Import *import; if ((import = s->isImport()) != NULL) { // Bugzilla 9692 - sd = import->mod; + sds = import->mod; } - else if ((sd = s->isScopeDsymbol()) == NULL) + else if ((sds = s->isScopeDsymbol()) == NULL) { - error("%s %s has no members", s->kind(), s->toChars()); + e->error("%s %s has no members", s->kind(), s->toChars()); goto Lfalse; } @@ -614,9 +783,9 @@ Expression *TraitsExp::semantic(Scope *sc) //printf("\t[%i] %s %s\n", i, sm->kind(), sm->toChars()); if (sm->ident) { - if (sm->ident != Id::ctor && // backword compatibility - sm->ident != Id::dtor && // backword compatibility - sm->ident != Id::_postblit && // backword compatibility + if (sm->ident != Id::ctor && + sm->ident != Id::dtor && + sm->ident != Id::_postblit && memcmp(sm->ident->string, "__", 2) == 0) { return 0; @@ -653,17 +822,18 @@ Expression *TraitsExp::semantic(Scope *sc) Identifiers *idents = new Identifiers; - ScopeDsymbol::foreach(sc, sd->members, &PushIdentsDg::dg, idents); + ScopeDsymbol::foreach(sc, sds->members, &PushIdentsDg::dg, idents); - ClassDeclaration *cd = sd->isClassDeclaration(); - if (cd && ident == Id::allMembers) + ClassDeclaration *cd = sds->isClassDeclaration(); + if (cd && e->ident == Id::allMembers) { struct PushBaseMembers { static void dg(ClassDeclaration *cd, Identifiers *idents) { for (size_t i = 0; i < cd->baseclasses->dim; i++) - { ClassDeclaration *cb = (*cd->baseclasses)[i]->base; + { + ClassDeclaration *cb = (*cd->baseclasses)[i]->base; ScopeDsymbol::foreach(NULL, cb->members, &PushIdentsDg::dg, idents); if (cb->baseclasses->dim) dg(cb, idents); @@ -677,8 +847,9 @@ Expression *TraitsExp::semantic(Scope *sc) assert(sizeof(Expressions) == sizeof(Identifiers)); Expressions *exps = (Expressions *)idents; for (size_t i = 0; i < idents->dim; i++) - { Identifier *id = (*idents)[i]; - StringExp *se = new StringExp(loc, id->toChars()); + { + Identifier *id = (*idents)[i]; + StringExp *se = new StringExp(e->loc, id->toChars()); (*exps)[i] = se; } @@ -686,11 +857,11 @@ Expression *TraitsExp::semantic(Scope *sc) * To make an array literal, enclose __traits in [ ]: * [ __traits(allMembers, ...) ] */ - Expression *e = new TupleExp(loc, exps); - e = e->semantic(sc); - return e; + Expression *ex = new TupleExp(e->loc, exps); + ex = ex->semantic(sc); + return ex; } - else if (ident == Id::compiles) + else if (e->ident == Id::compiles) { /* Determine if all the objects - types, expressions, or symbols - * compile without error @@ -701,39 +872,43 @@ Expression *TraitsExp::semantic(Scope *sc) for (size_t i = 0; i < dim; i++) { unsigned errors = global.startGagging(); - unsigned oldspec = global.speculativeGag; - global.speculativeGag = global.gag; Scope *sc2 = sc->push(); sc2->speculative = true; - sc2->flags = sc->flags & ~SCOPEctfe | SCOPEcompile; + sc2->flags = (sc->flags & ~SCOPEctfe) | SCOPEcompile; bool err = false; - RootObject *o = (*args)[i]; + RootObject *o = (*e->args)[i]; Type *t = isType(o); - Expression *e = t ? t->toExpression() : isExpression(o); - if (!e && t) + Expression *ex = t ? t->toExpression() : isExpression(o); + if (!ex && t) { Dsymbol *s; - t->resolve(loc, sc2, &e, &t, &s); + t->resolve(e->loc, sc2, &ex, &t, &s); if (t) { - t->semantic(loc, sc2); + t->semantic(e->loc, sc2); if (t->ty == Terror) err = true; } else if (s && s->errors) err = true; } - if (e) + if (ex) { - e = e->semantic(sc2); - e = e->optimize(WANTvalue); - if (e->op == TOKerror) + ex = ex->semantic(sc2); + ex = resolvePropertiesOnly(sc2, ex); + ex = ex->optimize(WANTvalue); + if (sc2->func && sc2->func->type->ty == Tfunction) + { + TypeFunction *tf = (TypeFunction *)sc2->func->type; + canThrow(ex, sc2->func, tf->isnothrow); + } + ex = checkGC(sc2, ex); + if (ex->op == TOKerror) err = true; } sc2->pop(); - global.speculativeGag = oldspec; if (global.endGagging(errors) || err) { goto Lfalse; @@ -741,15 +916,16 @@ Expression *TraitsExp::semantic(Scope *sc) } goto Ltrue; } - else if (ident == Id::isSame) + else if (e->ident == Id::isSame) { /* Determine if two symbols are the same */ if (dim != 2) goto Ldimerror; - TemplateInstance::semanticTiargs(loc, sc, args, 0); - RootObject *o1 = (*args)[0]; - RootObject *o2 = (*args)[1]; + if (!TemplateInstance::semanticTiargs(e->loc, sc, e->args, 0)) + return new ErrorExp(); + RootObject *o1 = (*e->args)[0]; + RootObject *o2 = (*e->args)[1]; Dsymbol *s1 = getDsymbol(o1); Dsymbol *s2 = getDsymbol(o2); @@ -758,7 +934,8 @@ Expression *TraitsExp::semantic(Scope *sc) printf("o1: %p\n", o1); printf("o2: %p\n", o2); if (!s1) - { Expression *ea = isExpression(o1); + { + Expression *ea = isExpression(o1); if (ea) printf("%s\n", ea->toChars()); Type *ta = isType(o1); @@ -796,15 +973,15 @@ Expression *TraitsExp::semantic(Scope *sc) else goto Lfalse; } - else if (ident == Id::getUnitTests) + else if (e->ident == Id::getUnitTests) { if (dim != 1) goto Ldimerror; - RootObject *o = (*args)[0]; + RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); if (!s) { - error("argument %s to __traits(getUnitTests) must be a module or aggregate", o->toChars()); + e->error("argument %s to __traits(getUnitTests) must be a module or aggregate", o->toChars()); goto Lfalse; } @@ -816,7 +993,7 @@ Expression *TraitsExp::semantic(Scope *sc) if (!scope) { - error("argument %s to __traits(getUnitTests) must be a module or aggregate, not a %s", s->toChars(), s->kind()); + e->error("argument %s to __traits(getUnitTests) must be a module or aggregate, not a %s", s->toChars(), s->kind()); goto Lfalse; } @@ -830,41 +1007,44 @@ Expression *TraitsExp::semantic(Scope *sc) collectUnitTests(symbols, uniqueUnitTests, unitTests); } - TupleExp *tup = new TupleExp(loc, unitTests); + TupleExp *tup = new TupleExp(e->loc, unitTests); return tup->semantic(sc); } - else if(ident == Id::getVirtualIndex) + else if(e->ident == Id::getVirtualIndex) { if (dim != 1) goto Ldimerror; - RootObject *o = (*args)[0]; + RootObject *o = (*e->args)[0]; Dsymbol *s = getDsymbol(o); FuncDeclaration *fd; if (!s || (fd = s->isFuncDeclaration()) == NULL) { - error("first argument to __traits(getVirtualIndex) must be a function"); + e->error("first argument to __traits(getVirtualIndex) must be a function"); goto Lfalse; } fd = fd->toAliasFunc(); // Neccessary to support multiple overloads. - ptrdiff_t result = fd->isVirtual() ? fd->vtblIndex : -1; - return new IntegerExp(loc, fd->vtblIndex, Type::tptrdiff_t); + return new IntegerExp(e->loc, fd->vtblIndex, Type::tptrdiff_t); } else { - error("unrecognized trait %s", ident->toChars()); + if (const char *sub = (const char *)speller(e->ident->toChars(), &trait_search_fp, NULL, idchars)) + e->error("unrecognized trait '%s', did you mean '%s'?", e->ident->toChars(), sub); + else + e->error("unrecognized trait '%s'", e->ident->toChars()); + goto Lfalse; } return NULL; Ldimerror: - error("wrong number of arguments %d", (int)dim); + e->error("wrong number of arguments %d", (int)dim); goto Lfalse; Lfalse: - return new IntegerExp(loc, 0, Type::tbool); + return new IntegerExp(e->loc, 0, Type::tbool); Ltrue: - return new IntegerExp(loc, 1, Type::tbool); + return new IntegerExp(e->loc, 1, Type::tbool); } diff --git a/gcc/d/dfrontend/unittests.c b/gcc/d/dfrontend/unittests.c index 3cadebe55..47f7ca330 100644 --- a/gcc/d/dfrontend/unittests.c +++ b/gcc/d/dfrontend/unittests.c @@ -1,11 +1,13 @@ -// Copyright (c) 2010-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/unittests.c + */ #include diff --git a/gcc/d/dfrontend/utf.c b/gcc/d/dfrontend/utf.c index 874a978f9..fa47285c6 100644 --- a/gcc/d/dfrontend/utf.c +++ b/gcc/d/dfrontend/utf.c @@ -1,10 +1,13 @@ -// Copyright (c) 2003-2012 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. + +/* Compiler implementation of the D programming language + * Copyright (c) 2003-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/utf.c + */ /// Description of UTF-8 in [1]. Unicode non-characters and private-use /// code points described in [2],[4]. @@ -67,12 +70,16 @@ char const UTF16_DECODE_INVALID_CODE_POINT[]= "Invalid code point decoded"; bool utf_isValidDchar(dchar_t c) { // TODO: Whether non-char code points should be rejected is pending review - return c <= 0x10FFFF // largest character code point - && !(0xD800 <= c && c <= 0xDFFF) // surrogate pairs - && (c & 0xFFFFFE) != 0x00FFFE // non-characters -// && (c & 0xFFFE) != 0xFFFE // non-characters -// && !(0x00FDD0 <= c && c <= 0x00FDEF) // non-characters - ; + // largest character code point + if (c > 0x10FFFF) + return false; + // surrogate pairs + if (0xD800 <= c && c <= 0xDFFF) + return false; + // non-characters + if ((c & 0xFFFFFE) == 0x00FFFE) + return false; + return true; } /******************************* @@ -239,7 +246,8 @@ const char *utf_decodeChar(utf8_t const *s, size_t len, size_t *pidx, dchar_t *p * 11111100 100000xx (10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx) */ utf8_t u2 = s[++i]; - if ((u & 0xFE) == 0xC0 || // overlong combination + // overlong combination + if ((u & 0xFE) == 0xC0 || (u == 0xE0 && (u2 & 0xE0) == 0x80) || (u == 0xF0 && (u2 & 0xF0) == 0x80) || (u == 0xF8 && (u2 & 0xF8) == 0x80) || diff --git a/gcc/d/dfrontend/utf.h b/gcc/d/dfrontend/utf.h index 3f2d60ffe..b0ee25d5c 100644 --- a/gcc/d/dfrontend/utf.h +++ b/gcc/d/dfrontend/utf.h @@ -1,11 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 2003-2010 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. + +/* Compiler implementation of the D programming language + * Copyright (c) 2003-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/utf.h + */ #ifndef DMD_UTF_H #define DMD_UTF_H @@ -64,7 +66,7 @@ static utf16_t const ALPHA_TABLE[ALPHA_TABLE_LENGTH][2] = { 0x0CE0, 0x0CE1 }, { 0x0CE6, 0x0CEF }, { 0x0D02, 0x0D03 }, { 0x0D05, 0x0D0C }, { 0x0D0E, 0x0D10 }, { 0x0D12, 0x0D28 }, { 0x0D2A, 0x0D39 }, { 0x0D3E, 0x0D43 }, { 0x0D46, 0x0D48 }, { 0x0D4A, 0x0D4D }, { 0x0D60, 0x0D61 }, { 0x0D66, 0x0D6F }, - { 0x0E01, 0x0E3A }, { 0x0E40, 0x0E5B }, /* { 0x0E50, 0x0E59 }, */ { 0x0E81, 0x0E82 }, + { 0x0E01, 0x0E3A }, { 0x0E40, 0x0E5B }, { 0x0E81, 0x0E82 }, { 0x0E84, 0x0E84 }, { 0x0E87, 0x0E88 }, { 0x0E8A, 0x0E8A }, { 0x0E8D, 0x0E8D }, { 0x0E94, 0x0E97 }, { 0x0E99, 0x0E9F }, { 0x0EA1, 0x0EA3 }, { 0x0EA5, 0x0EA5 }, { 0x0EA7, 0x0EA7 }, { 0x0EAA, 0x0EAB }, { 0x0EAD, 0x0EAE }, { 0x0EB0, 0x0EB9 }, diff --git a/gcc/d/dfrontend/version.c b/gcc/d/dfrontend/version.c index bfe3eaafb..201fa4b84 100644 --- a/gcc/d/dfrontend/version.c +++ b/gcc/d/dfrontend/version.c @@ -1,11 +1,13 @@ -// Copyright (c) 1999-2005 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/version.c + */ #include #include @@ -46,14 +48,13 @@ Dsymbol *DebugSymbol::syntaxCopy(Dsymbol *s) return ds; } -int DebugSymbol::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) +int DebugSymbol::addMember(Scope *sc, ScopeDsymbol *sds, int memnum) { - //printf("DebugSymbol::addMember('%s') %s\n", sd->toChars(), toChars()); - Module *m; + //printf("DebugSymbol::addMember('%s') %s\n", sds->toChars(), toChars()); + Module *m = sds->isModule(); // Do not add the member to the symbol table, // just make sure subsequent debug declarations work. - m = sd->isModule(); if (ident) { if (!m) @@ -135,14 +136,13 @@ Dsymbol *VersionSymbol::syntaxCopy(Dsymbol *s) return ds; } -int VersionSymbol::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) +int VersionSymbol::addMember(Scope *sc, ScopeDsymbol *sds, int memnum) { - //printf("VersionSymbol::addMember('%s') %s\n", sd->toChars(), toChars()); - Module *m; + //printf("VersionSymbol::addMember('%s') %s\n", sds->toChars(), toChars()); + Module *m = sds->isModule(); // Do not add the member to the symbol table, // just make sure subsequent debug declarations work. - m = sd->isModule(); if (ident) { VersionCondition::checkPredefined(loc, ident->toChars()); diff --git a/gcc/d/dfrontend/version.h b/gcc/d/dfrontend/version.h index 652ea1ae1..2dec2765d 100644 --- a/gcc/d/dfrontend/version.h +++ b/gcc/d/dfrontend/version.h @@ -1,12 +1,13 @@ -// Compiler implementation of the D programming language -// Copyright (c) 1999-2006 by Digital Mars -// All Rights Reserved -// written by Walter Bright -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. +/* Compiler implementation of the D programming language + * Copyright (c) 1999-2014 by Digital Mars + * All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/version.h + */ #ifndef DMD_VERSION_H #define DMD_VERSION_H @@ -29,7 +30,7 @@ class DebugSymbol : public Dsymbol DebugSymbol(Loc loc, unsigned level); Dsymbol *syntaxCopy(Dsymbol *); - int addMember(Scope *sc, ScopeDsymbol *s, int memnum); + int addMember(Scope *sc, ScopeDsymbol *sds, int memnum); void semantic(Scope *sc); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); const char *kind(); @@ -45,7 +46,7 @@ class VersionSymbol : public Dsymbol VersionSymbol(Loc loc, unsigned level); Dsymbol *syntaxCopy(Dsymbol *); - int addMember(Scope *sc, ScopeDsymbol *s, int memnum); + int addMember(Scope *sc, ScopeDsymbol *sds, int memnum); void semantic(Scope *sc); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); const char *kind(); diff --git a/gcc/d/dfrontend/visitor.h b/gcc/d/dfrontend/visitor.h index acb5395f4..570c5559d 100644 --- a/gcc/d/dfrontend/visitor.h +++ b/gcc/d/dfrontend/visitor.h @@ -1,10 +1,12 @@ -// Compiler implementation of the D programming language -// Copyright (c) 2013-2014 by Digital Mars -// All Rights Reserved -// http://www.digitalmars.com -// License for redistribution is by either the Artistic License -// in artistic.txt, or the GNU General Public License in gnu.txt. -// See the included readme.txt for details. + +/* Compiler implementation of the D programming language + * Copyright (c) 2013-2014 by Digital Mars + * All Rights Reserved + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + * https://github.com/D-Programming-Language/dmd/blob/master/src/visitor.h + */ #ifndef DMD_VISITOR_H #define DMD_VISITOR_H @@ -50,6 +52,7 @@ class DebugStatement; class GotoStatement; class LabelStatement; class AsmStatement; +class CompoundAsmStatement; #ifdef IN_GCC class ExtAsmStatement; #endif @@ -75,7 +78,6 @@ class TypeTypeof; class TypeReturn; class TypeStruct; class TypeEnum; -class TypeTypedef; class TypeClass; class TypeTuple; class TypeSlice; @@ -123,8 +125,8 @@ class InterfaceDeclaration; class Declaration; class TupleDeclaration; -class TypedefDeclaration; class AliasDeclaration; +class OverDeclaration; class VarDeclaration; class SymbolDeclaration; class ClassInfoDeclaration; @@ -134,7 +136,6 @@ class TypeInfoDeclaration; class TypeInfoStructDeclaration; class TypeInfoClassDeclaration; class TypeInfoInterfaceDeclaration; -class TypeInfoTypedefDeclaration; class TypeInfoPointerDeclaration; class TypeInfoArrayDeclaration; class TypeInfoStaticArrayDeclaration; @@ -164,6 +165,128 @@ class UnitTestDeclaration; class NewDeclaration; class DeleteDeclaration; +class Initializer; +class VoidInitializer; +class ErrorInitializer; +class StructInitializer; +class ArrayInitializer; +class ExpInitializer; + +class Expression; +class IntegerExp; +class ErrorExp; +class RealExp; +class ComplexExp; +class IdentifierExp; +class DollarExp; +class DsymbolExp; +class ThisExp; +class SuperExp; +class NullExp; +class StringExp; +class TupleExp; +class ArrayLiteralExp; +class AssocArrayLiteralExp; +class StructLiteralExp; +class TypeExp; +class ScopeExp; +class TemplateExp; +class NewExp; +class NewAnonClassExp; +class SymbolExp; +class SymOffExp; +class VarExp; +class OverExp; +class FuncExp; +class DeclarationExp; +class TypeidExp; +class TraitsExp; +class HaltExp; +class IsExp; +class UnaExp; +class BinExp; +class BinAssignExp; +class CompileExp; +class FileExp; +class AssertExp; +class DotIdExp; +class DotTemplateExp; +class DotVarExp; +class DotTemplateInstanceExp; +class DelegateExp; +class DotTypeExp; +class CallExp; +class AddrExp; +class PtrExp; +class NegExp; +class UAddExp; +class ComExp; +class NotExp; +class BoolExp; +class DeleteExp; +class CastExp; +class VectorExp; +class SliceExp; +class ArrayLengthExp; +class IntervalExp; +class DelegatePtrExp; +class DelegateFuncptrExp; +class ArrayExp; +class DotExp; +class CommaExp; +class IndexExp; +class PostExp; +class PreExp; +class AssignExp; +class ConstructExp; +class BlitExp; +class AddAssignExp; +class MinAssignExp; +class MulAssignExp; +class DivAssignExp; +class ModAssignExp; +class AndAssignExp; +class OrAssignExp; +class XorAssignExp; +class PowAssignExp; +class ShlAssignExp; +class ShrAssignExp; +class UshrAssignExp; +class CatAssignExp; +class AddExp; +class MinExp; +class CatExp; +class MulExp; +class DivExp; +class ModExp; +class PowExp; +class ShlExp; +class ShrExp; +class UshrExp; +class AndExp; +class OrExp; +class XorExp; +class OrOrExp; +class AndAndExp; +class CmpExp; +class InExp; +class RemoveExp; +class EqualExp; +class IdentityExp; +class CondExp; +class DefaultInitExp; +class FileInitExp; +class LineInitExp; +class ModuleInitExp; +class FuncInitExp; +class PrettyFuncInitExp; +class ClassReferenceExp; +class VoidInitExp; +class ThrownExceptionExp; +#ifdef IN_GCC +class WrappedExp; +#endif + class Visitor { public: @@ -206,6 +329,7 @@ class Visitor virtual void visit(GotoStatement *s) { visit((Statement *)s); } virtual void visit(LabelStatement *s) { visit((Statement *)s); } virtual void visit(AsmStatement *s) { visit((Statement *)s); } + virtual void visit(CompoundAsmStatement *s) { visit((CompoundStatement *)s); } #ifdef IN_GCC virtual void visit(ExtAsmStatement *s) { visit((Statement *)s); } #endif @@ -231,7 +355,6 @@ class Visitor virtual void visit(TypeReturn *t) { visit((TypeQualified *)t); } virtual void visit(TypeStruct *t) { visit((Type *)t); } virtual void visit(TypeEnum *t) { visit((Type *)t); } - virtual void visit(TypeTypedef *t) { visit((Type *)t); } virtual void visit(TypeClass *t) { visit((Type *)t); } virtual void visit(TypeTuple *t) { visit((Type *)t); } virtual void visit(TypeSlice *t) { visit((TypeNext *)t); } @@ -279,8 +402,8 @@ class Visitor virtual void visit(Declaration *s) { visit((Dsymbol *)s); } virtual void visit(TupleDeclaration *s) { visit((Declaration *)s); } - virtual void visit(TypedefDeclaration *s) { visit((Declaration *)s); } virtual void visit(AliasDeclaration *s) { visit((Declaration *)s); } + virtual void visit(OverDeclaration *s) { visit((Declaration *)s); } virtual void visit(VarDeclaration *s) { visit((Declaration *)s); } virtual void visit(SymbolDeclaration *s) { visit((Declaration *)s); } virtual void visit(ClassInfoDeclaration *s) { visit((VarDeclaration *)s); } @@ -290,7 +413,6 @@ class Visitor virtual void visit(TypeInfoStructDeclaration *s) { visit((TypeInfoDeclaration *)s); } virtual void visit(TypeInfoClassDeclaration *s) { visit((TypeInfoDeclaration *)s); } virtual void visit(TypeInfoInterfaceDeclaration *s) { visit((TypeInfoDeclaration *)s); } - virtual void visit(TypeInfoTypedefDeclaration *s) { visit((TypeInfoDeclaration *)s); } virtual void visit(TypeInfoPointerDeclaration *s) { visit((TypeInfoDeclaration *)s); } virtual void visit(TypeInfoArrayDeclaration *s) { visit((TypeInfoDeclaration *)s); } virtual void visit(TypeInfoStaticArrayDeclaration *s) { visit((TypeInfoDeclaration *)s); } @@ -319,6 +441,135 @@ class Visitor virtual void visit(UnitTestDeclaration *s) { visit((FuncDeclaration *)s); } virtual void visit(NewDeclaration *s) { visit((FuncDeclaration *)s); } virtual void visit(DeleteDeclaration *s) { visit((FuncDeclaration *)s); } + + virtual void visit(Initializer *) { assert(0); } + virtual void visit(VoidInitializer *i) { visit((Initializer *)i); } + virtual void visit(ErrorInitializer *i) { visit((Initializer *)i); } + virtual void visit(StructInitializer *i) { visit((Initializer *)i); } + virtual void visit(ArrayInitializer *i) { visit((Initializer *)i); } + virtual void visit(ExpInitializer *i) { visit((Initializer *)i); } + + virtual void visit(Expression *) { assert(0); } + virtual void visit(IntegerExp *e) { visit((Expression *)e); } + virtual void visit(ErrorExp *e) { visit((Expression *)e); } + virtual void visit(RealExp *e) { visit((Expression *)e); } + virtual void visit(ComplexExp *e) { visit((Expression *)e); } + virtual void visit(IdentifierExp *e) { visit((Expression *)e); } + virtual void visit(DollarExp *e) { visit((IdentifierExp *)e); } + virtual void visit(DsymbolExp *e) { visit((Expression *)e); } + virtual void visit(ThisExp *e) { visit((Expression *)e); } + virtual void visit(SuperExp *e) { visit((ThisExp *)e); } + virtual void visit(NullExp *e) { visit((Expression *)e); } + virtual void visit(StringExp *e) { visit((Expression *)e); } + virtual void visit(TupleExp *e) { visit((Expression *)e); } + virtual void visit(ArrayLiteralExp *e) { visit((Expression *)e); } + virtual void visit(AssocArrayLiteralExp *e) { visit((Expression *)e); } + virtual void visit(StructLiteralExp *e) { visit((Expression *)e); } + virtual void visit(TypeExp *e) { visit((Expression *)e); } + virtual void visit(ScopeExp *e) { visit((Expression *)e); } + virtual void visit(TemplateExp *e) { visit((Expression *)e); } + virtual void visit(NewExp *e) { visit((Expression *)e); } + virtual void visit(NewAnonClassExp *e) { visit((Expression *)e); } + virtual void visit(SymbolExp *e) { visit((Expression *)e); } + virtual void visit(SymOffExp *e) { visit((SymbolExp *)e); } + virtual void visit(VarExp *e) { visit((SymbolExp *)e); } + virtual void visit(OverExp *e) { visit((Expression *)e); } + virtual void visit(FuncExp *e) { visit((Expression *)e); } + virtual void visit(DeclarationExp *e) { visit((Expression *)e); } + virtual void visit(TypeidExp *e) { visit((Expression *)e); } + virtual void visit(TraitsExp *e) { visit((Expression *)e); } + virtual void visit(HaltExp *e) { visit((Expression *)e); } + virtual void visit(IsExp *e) { visit((Expression *)e); } + virtual void visit(UnaExp *e) { visit((Expression *)e); } + virtual void visit(BinExp *e) { visit((Expression *)e); } + virtual void visit(BinAssignExp *e) { visit((BinExp *)e); } + virtual void visit(CompileExp *e) { visit((UnaExp *)e); } + virtual void visit(FileExp *e) { visit((UnaExp *)e); } + virtual void visit(AssertExp *e) { visit((UnaExp *)e); } + virtual void visit(DotIdExp *e) { visit((UnaExp *)e); } + virtual void visit(DotTemplateExp *e) { visit((UnaExp *)e); } + virtual void visit(DotVarExp *e) { visit((UnaExp *)e); } + virtual void visit(DotTemplateInstanceExp *e) { visit((UnaExp *)e); } + virtual void visit(DelegateExp *e) { visit((UnaExp *)e); } + virtual void visit(DotTypeExp *e) { visit((UnaExp *)e); } + virtual void visit(CallExp *e) { visit((UnaExp *)e); } + virtual void visit(AddrExp *e) { visit((UnaExp *)e); } + virtual void visit(PtrExp *e) { visit((UnaExp *)e); } + virtual void visit(NegExp *e) { visit((UnaExp *)e); } + virtual void visit(UAddExp *e) { visit((UnaExp *)e); } + virtual void visit(ComExp *e) { visit((UnaExp *)e); } + virtual void visit(NotExp *e) { visit((UnaExp *)e); } + virtual void visit(BoolExp *e) { visit((UnaExp *)e); } + virtual void visit(DeleteExp *e) { visit((UnaExp *)e); } + virtual void visit(CastExp *e) { visit((UnaExp *)e); } + virtual void visit(VectorExp *e) { visit((UnaExp *)e); } + virtual void visit(SliceExp *e) { visit((UnaExp *)e); } + virtual void visit(ArrayLengthExp *e) { visit((UnaExp *)e); } + virtual void visit(IntervalExp *e) { visit((Expression *)e); } + virtual void visit(DelegatePtrExp *e) { visit((UnaExp *)e); } + virtual void visit(DelegateFuncptrExp *e) { visit((UnaExp *)e); } + virtual void visit(ArrayExp *e) { visit((UnaExp *)e); } + virtual void visit(DotExp *e) { visit((BinExp *)e); } + virtual void visit(CommaExp *e) { visit((BinExp *)e); } + virtual void visit(IndexExp *e) { visit((BinExp *)e); } + virtual void visit(PostExp *e) { visit((BinExp *)e); } + virtual void visit(PreExp *e) { visit((UnaExp *)e); } + virtual void visit(AssignExp *e) { visit((BinExp *)e); } + virtual void visit(ConstructExp *e) { visit((AssignExp *)e); } + virtual void visit(BlitExp *e) { visit((AssignExp *)e); } + virtual void visit(AddAssignExp *e) { visit((BinAssignExp *)e); } + virtual void visit(MinAssignExp *e) { visit((BinAssignExp *)e); } + virtual void visit(MulAssignExp *e) { visit((BinAssignExp *)e); } + virtual void visit(DivAssignExp *e) { visit((BinAssignExp *)e); } + virtual void visit(ModAssignExp *e) { visit((BinAssignExp *)e); } + virtual void visit(AndAssignExp *e) { visit((BinAssignExp *)e); } + virtual void visit(OrAssignExp *e) { visit((BinAssignExp *)e); } + virtual void visit(XorAssignExp *e) { visit((BinAssignExp *)e); } + virtual void visit(PowAssignExp *e) { visit((BinAssignExp *)e); } + virtual void visit(ShlAssignExp *e) { visit((BinAssignExp *)e); } + virtual void visit(ShrAssignExp *e) { visit((BinAssignExp *)e); } + virtual void visit(UshrAssignExp *e) { visit((BinAssignExp *)e); } + virtual void visit(CatAssignExp *e) { visit((BinAssignExp *)e); } + virtual void visit(AddExp *e) { visit((BinExp *)e); } + virtual void visit(MinExp *e) { visit((BinExp *)e); } + virtual void visit(CatExp *e) { visit((BinExp *)e); } + virtual void visit(MulExp *e) { visit((BinExp *)e); } + virtual void visit(DivExp *e) { visit((BinExp *)e); } + virtual void visit(ModExp *e) { visit((BinExp *)e); } + virtual void visit(PowExp *e) { visit((BinExp *)e); } + virtual void visit(ShlExp *e) { visit((BinExp *)e); } + virtual void visit(ShrExp *e) { visit((BinExp *)e); } + virtual void visit(UshrExp *e) { visit((BinExp *)e); } + virtual void visit(AndExp *e) { visit((BinExp *)e); } + virtual void visit(OrExp *e) { visit((BinExp *)e); } + virtual void visit(XorExp *e) { visit((BinExp *)e); } + virtual void visit(OrOrExp *e) { visit((BinExp *)e); } + virtual void visit(AndAndExp *e) { visit((BinExp *)e); } + virtual void visit(CmpExp *e) { visit((BinExp *)e); } + virtual void visit(InExp *e) { visit((BinExp *)e); } + virtual void visit(RemoveExp *e) { visit((BinExp *)e); } + virtual void visit(EqualExp *e) { visit((BinExp *)e); } + virtual void visit(IdentityExp *e) { visit((BinExp *)e); } + virtual void visit(CondExp *e) { visit((BinExp *)e); } + virtual void visit(DefaultInitExp *e) { visit((Expression *)e); } + virtual void visit(FileInitExp *e) { visit((DefaultInitExp *)e); } + virtual void visit(LineInitExp *e) { visit((DefaultInitExp *)e); } + virtual void visit(ModuleInitExp *e) { visit((DefaultInitExp *)e); } + virtual void visit(FuncInitExp *e) { visit((DefaultInitExp *)e); } + virtual void visit(PrettyFuncInitExp *e) { visit((DefaultInitExp *)e); } + virtual void visit(ClassReferenceExp *e) { visit((Expression *)e); } + virtual void visit(VoidInitExp *e) { visit((Expression *)e); } + virtual void visit(ThrownExceptionExp *e) { visit((Expression *)e); } +#ifdef IN_GCC + virtual void visit(WrappedExp *e) { visit((Expression *)e); } +#endif +}; + +class StoppableVisitor : public Visitor +{ +public: + bool stop; + StoppableVisitor() : stop(false) {} }; #endif /* DMD_VISITOR_H */ diff --git a/gcc/d/intrinsics.def b/gcc/d/intrinsics.def new file mode 100644 index 000000000..fc6e992bf --- /dev/null +++ b/gcc/d/intrinsics.def @@ -0,0 +1,152 @@ +// intrinsics.def -- D frontend for GCC. +// Copyright (C) 2014 Free Software Foundation, Inc. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +// Definitions for intrinsics of the D compiler. + +// Define all D intrinsic functions. +// The first parameter: +// CODE - The enum code used to refer this intrinsic. +// The second parameter: +// ALIAS - The enum code used to refer the function DECL_FUNCTION_CODE, +// if there are multiple modules or decos for a single intrinsic, they +// would all refer to this code. +// The third parameter: +// NAME - The name of the intrinsic. +// The fourth parameter: +// MODULE - The list of modules which the intrinsic could belong to. +// The fifth parameter: +// DECO - the signature decoration of the intrinsic. +// Template intrinsics reserve this for the template parameter strings. + +// Helper macros. + +// Modules +#define CORE_BITOP "core.bitop" +#define CORE_VARARG "core.stdc.stdarg" +#define CORE_MATH "core.math" +#define STD_MATH "std.math" + +// Signature decorations +#define FN_SAFE_NOGC_PURE_NOTHROW "FNaNbNiNf" // @safe @nogc pure nothrow function +#define FN_SAFE_PURE_NOTHROW "FNaNbNf" // @safe pure nothrow function +#define FN_NOGC_PURE_NOTHROW "FNaNbNi" // @system @nogc pure nothrow function +#define FN_PURE_NOTHROW "FNaNb" // @system pure nothrow function +#define FN_NONE NULL + +// Parameter and return types +#define INT_UINT "kZi" // int(uint) +#define INT_ULONG "mZi" // int(ulong) +#define UINT_UINT "kZk" // uint(uint) +#define INT_PTR_UINT_UINT "PkkZi" // int(uint*, uint) +#define INT_PTR_ULONG_ULONG "PmmZi" // int(ulong*, ulong) +#define REAL_REAL "eZe" // real(real) +#define REAL_REAL_INT "eiZe" // real(real, int) +#define LONG_REAL "eZl" // long(real) +#define FLOAT_FLOAT "fZf" // float(float) +#define DOUBLE_DOUBLE "dZd" // double(double) + +#define SAFE_NOGC_PURE_NOTHROW_INT_UINT FN_SAFE_NOGC_PURE_NOTHROW INT_UINT +#define SAFE_NOGC_PURE_NOTHROW_INT_ULONG FN_SAFE_NOGC_PURE_NOTHROW INT_ULONG +#define SAFE_NOGC_PURE_NOTHROW_UINT_UINT FN_SAFE_NOGC_PURE_NOTHROW UINT_UINT +#define SAFE_NOGC_PURE_NOTHROW_LONG_REAL FN_SAFE_NOGC_PURE_NOTHROW LONG_REAL +#define SAFE_NOGC_PURE_NOTHROW_FLOAT_FLOAT FN_SAFE_NOGC_PURE_NOTHROW FLOAT_FLOAT +#define SAFE_NOGC_PURE_NOTHROW_DOUBLE_DOUBLE FN_SAFE_NOGC_PURE_NOTHROW DOUBLE_DOUBLE +#define SAFE_NOGC_PURE_NOTHROW_REAL_REAL FN_SAFE_NOGC_PURE_NOTHROW REAL_REAL +#define SAFE_NOGC_PURE_NOTHROW_REAL_REAL_INT FN_SAFE_NOGC_PURE_NOTHROW REAL_REAL_INT +#define NOGC_PURE_NOTHROW_INT_PTR_UINT_UINT FN_NOGC_PURE_NOTHROW INT_PTR_UINT_UINT +#define NOGC_PURE_NOTHROW_INT_PTR_ULONG_ULONG FN_NOGC_PURE_NOTHROW INT_PTR_ULONG_ULONG +#define DECO_NONE FN_NONE + +// core.bitop intrinsics + +DEF_D_INTRINSIC (BSF, BSF, "bsf", CORE_BITOP, SAFE_NOGC_PURE_NOTHROW_INT_UINT) +DEF_D_INTRINSIC (BSR, BSR, "bsr", CORE_BITOP, SAFE_NOGC_PURE_NOTHROW_INT_UINT) +DEF_D_INTRINSIC (BTC, BTC, "btc", CORE_BITOP, NOGC_PURE_NOTHROW_INT_PTR_UINT_UINT) +DEF_D_INTRINSIC (BTR, BTR, "btr", CORE_BITOP, NOGC_PURE_NOTHROW_INT_PTR_UINT_UINT) +DEF_D_INTRINSIC (BTS, BTS, "bts", CORE_BITOP, NOGC_PURE_NOTHROW_INT_PTR_UINT_UINT) +DEF_D_INTRINSIC (BSF64, BSF, "bsf", CORE_BITOP, SAFE_NOGC_PURE_NOTHROW_INT_ULONG) +DEF_D_INTRINSIC (BSR64, BSR, "bsr", CORE_BITOP, SAFE_NOGC_PURE_NOTHROW_INT_ULONG) +DEF_D_INTRINSIC (BTC64, BTC, "btc", CORE_BITOP, NOGC_PURE_NOTHROW_INT_PTR_ULONG_ULONG) +DEF_D_INTRINSIC (BTR64, BTR, "btr", CORE_BITOP, NOGC_PURE_NOTHROW_INT_PTR_ULONG_ULONG) +DEF_D_INTRINSIC (BTS64, BTS, "bts", CORE_BITOP, NOGC_PURE_NOTHROW_INT_PTR_ULONG_ULONG) +DEF_D_INTRINSIC (BSWAP, BSWAP, "bswap", CORE_BITOP, SAFE_NOGC_PURE_NOTHROW_UINT_UINT) + +// core.math intrinsics + +DEF_D_INTRINSIC (COS, COS, "cos", CORE_MATH, SAFE_NOGC_PURE_NOTHROW_REAL_REAL) +DEF_D_INTRINSIC (FABS, FABS, "fabs", CORE_MATH, SAFE_NOGC_PURE_NOTHROW_REAL_REAL) +DEF_D_INTRINSIC (LDEXP, LDEXP, "ldexp", CORE_MATH, SAFE_NOGC_PURE_NOTHROW_REAL_REAL_INT) +DEF_D_INTRINSIC (RINT, RINT, "rint", CORE_MATH, SAFE_NOGC_PURE_NOTHROW_REAL_REAL) +DEF_D_INTRINSIC (RNDTOL, RNDTOL, "rndtol", CORE_MATH, SAFE_NOGC_PURE_NOTHROW_LONG_REAL) +DEF_D_INTRINSIC (SIN, SIN, "sin", CORE_MATH, SAFE_NOGC_PURE_NOTHROW_REAL_REAL) +DEF_D_INTRINSIC (SQRTF, SQRTF, "sqrt", CORE_MATH, SAFE_NOGC_PURE_NOTHROW_FLOAT_FLOAT) +DEF_D_INTRINSIC (SQRT, SQRT, "sqrt", CORE_MATH, SAFE_NOGC_PURE_NOTHROW_DOUBLE_DOUBLE) +DEF_D_INTRINSIC (SQRTL, SQRTL, "sqrt", CORE_MATH, SAFE_NOGC_PURE_NOTHROW_REAL_REAL) + +// std.math intrinsics + +DEF_D_INTRINSIC (STD_COS, COS, "cos", STD_MATH, SAFE_NOGC_PURE_NOTHROW_REAL_REAL) +DEF_D_INTRINSIC (STD_FABS, FABS, "fabs", STD_MATH, SAFE_NOGC_PURE_NOTHROW_REAL_REAL) +DEF_D_INTRINSIC (STD_LDEXP, LDEXP, "ldexp", STD_MATH, SAFE_NOGC_PURE_NOTHROW_REAL_REAL_INT) +DEF_D_INTRINSIC (STD_RINT, RINT, "rint", STD_MATH, SAFE_NOGC_PURE_NOTHROW_REAL_REAL) +DEF_D_INTRINSIC (STD_RNDTOL, RNDTOL, "rndtol", STD_MATH, SAFE_NOGC_PURE_NOTHROW_LONG_REAL) +DEF_D_INTRINSIC (STD_SIN, SIN, "sin", STD_MATH, SAFE_NOGC_PURE_NOTHROW_REAL_REAL) +DEF_D_INTRINSIC (STD_SQRTF, SQRTF, "sqrt", STD_MATH, SAFE_NOGC_PURE_NOTHROW_FLOAT_FLOAT) +DEF_D_INTRINSIC (STD_SQRT, SQRT, "sqrt", STD_MATH, SAFE_NOGC_PURE_NOTHROW_DOUBLE_DOUBLE) +DEF_D_INTRINSIC (STD_SQRTL, SQRTL, "sqrt", STD_MATH, SAFE_NOGC_PURE_NOTHROW_REAL_REAL) + +// core.stdc.stdarg intrinsics + +DEF_D_INTRINSIC (VA_ARG, VA_ARG, "va_arg", CORE_VARARG, "void(ref va_list ap, ref T parmn)") +DEF_D_INTRINSIC (C_VA_ARG, C_VA_ARG, "va_arg", CORE_VARARG, "T(ref va_list ap)") +DEF_D_INTRINSIC (VASTART, VASTART, "va_start", CORE_VARARG, "void(out va_list ap, ref T parmn)") + + +// Remove helper macros +#undef DEF_D_INTRINSIC +#undef CORE_BITOP +#undef CORE_VARARG +#undef CORE_MATH +#undef STD_MATH +#undef FN_SAFE_NOGC_PURE_NOTHROW +#undef FN_SAFE_PURE_NOTHROW +#undef FN_NOGC_PURE_NOTHROW +#undef FN_PURE_NOTHROW +#undef FN_NONE +#undef INT_UINT +#undef INT_ULONG +#undef UINT_UINT +#undef INT_PTR_UINT_UINT +#undef INT_PTR_ULONG_ULONG +#undef REAL_REAL +#undef REAL_REAL_INT +#undef LONG_REAL +#undef FLOAT_FLOAT +#undef DOUBLE_DOUBLE +#undef SAFE_NOGC_PURE_NOTHROW_INT_UINT +#undef SAFE_NOGC_PURE_NOTHROW_INT_ULONG +#undef SAFE_NOGC_PURE_NOTHROW_UINT_UINT +#undef SAFE_NOGC_PURE_NOTHROW_LONG_REAL +#undef SAFE_NOGC_PURE_NOTHROW_FLOAT_FLOAT +#undef SAFE_NOGC_PURE_NOTHROW_DOUBLE_DOUBLE +#undef SAFE_NOGC_PURE_NOTHROW_REAL_REAL +#undef SAFE_NOGC_PURE_NOTHROW_REAL_REAL_INT +#undef NOGC_PURE_NOTHROW_INT_PTR_UINT_UINT +#undef NOGC_PURE_NOTHROW_INT_PTR_ULONG_ULONG + +#undef DECO_NONE + diff --git a/gcc/d/lang.opt b/gcc/d/lang.opt index 5df6e80d9..6975e7eb0 100644 --- a/gcc/d/lang.opt +++ b/gcc/d/lang.opt @@ -77,6 +77,10 @@ fdoc-inc= D Joined RejectNegative -fdoc-inc= Include a Ddoc macro file +fd-vgc +D +List all hidden GC allocations + fd-verbose D Print information about D language processing to stdout diff --git a/gcc/d/longdouble.h b/gcc/d/longdouble.h index 03b2d2369..2017aa21f 100644 --- a/gcc/d/longdouble.h +++ b/gcc/d/longdouble.h @@ -32,10 +32,10 @@ struct longdouble NumModes }; - static void init (void); + static void init(); - const real_value& rv (void) const; - real_value& rv (void); + const real_value& rv() const; + real_value& rv(); // No constructor to be able to use this class in a union. template longdouble& operator = (T x) @@ -59,21 +59,21 @@ struct longdouble void set (bool d); // Rvalue operators. - operator float (void); - operator double (void); - operator real_value& (void); + operator float(); + operator double(); + operator real_value&(); - operator int8_t (void); - operator int16_t (void); - operator int32_t (void); - operator int64_t (void); + operator int8_t(); + operator int16_t(); + operator int32_t(); + operator int64_t(); - operator uint8_t (void); - operator uint16_t (void); - operator uint32_t (void); - operator uint64_t (void); + operator uint8_t(); + operator uint16_t(); + operator uint32_t(); + operator uint64_t(); - operator bool (void); + operator bool(); // Arithmetic operators. longdouble operator + (const longdouble& r); @@ -82,7 +82,7 @@ struct longdouble longdouble operator / (const longdouble& r); longdouble operator % (const longdouble& r); - longdouble operator - (void); + longdouble operator -(); // Comparison operators. bool operator < (const longdouble& r); @@ -96,7 +96,7 @@ struct longdouble int formatHex (char fmt, char *buf, unsigned buf_size) const; // for debugging: - void dump (void); + void dump(); private: longdouble from_int (Type *type, int64_t d); diff --git a/gcc/d/runtime.def b/gcc/d/runtime.def new file mode 100644 index 000000000..435c38e7c --- /dev/null +++ b/gcc/d/runtime.def @@ -0,0 +1,232 @@ +// runtime.def -- D frontend for GCC. +// Copyright (C) 2014 Free Software Foundation, Inc. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// . + +// Definitions for the D runtime functions. +// Most are extern(C) - for those that are not, correct mangling must be ensured. + +// Define all D runtime functions. +// The first parameter: +// CODE - The enum code used to refer this function. +// The second parameter: +// NAME - The name of the function. +// The third parameter: +// PARAMS - The parameter type list of the function. +// The fourth parameter: +// TYPE - The return type of the function. +// The fifth parameter: +// FLAGS - Flags to describe attributes of the function. + +// Helper macros. +#define CONST(T) (T)->constOf() +#define ARRAY(T) (T)->arrayOf() +#define POINTER(T) (T)->pointerTo() +#define ARRAYPTR(T) POINTER(ARRAY(T)) + +#define STRING ARRAY(Type::tchar) +#define WSTRING ARRAY(Type::twchar) +#define DSTRING ARRAY(Type::tdchar) +#define DCHAR Type::tdchar +#define SIZE_T Type::tsize_t +#define BYTE Type::tint8 +#define INT Type::tint32 +#define UINT Type::tuns32 +#define BOOL Type::tbool +#define VOID Type::tvoid +#define VOIDPTR Type::tvoidptr +#define TYPEINFO Type::dtypeinfo->type +#define CLASSINFO Type::typeinfoclass->type +#define OBJECT ClassDeclaration::object->type + +// Parameter type helper macros. +#define P0() 0 +#define P1(T1) 1, T1 +#define P2(T1, T2) 2, T1, T2 +#define P3(T1, T2, T3) 3, T1, T2, T3 +#define P4(T1, T2, T3, T4) 4, T1, T2, T3, T4 + +// Flag helper macros +#define FN_NONE LCFnone +#define FN_THROWS LCFthrows +#define FN_MALLOC LCFmalloc +#define FN_VARARGS LCFvarargs + + +// Used when an assert() contract fails. +DEF_D_RUNTIME(ASSERT, "_d_assert", P2(STRING, UINT), VOID, FN_THROWS) +DEF_D_RUNTIME(ASSERT_MSG, "_d_assert_msg", P3(STRING, STRING, UINT), VOID, FN_THROWS) + +// Used when an assert() contract fails in a unittest function. +DEF_D_RUNTIME(UNITTEST, "_d_unittest", P2(STRING, UINT), VOID, FN_THROWS) +DEF_D_RUNTIME(UNITTEST_MSG, "_d_unittest_msg", P3(STRING, STRING, UINT), VOID, FN_THROWS) + +// Used when an array index outside the bounds of its range. +DEF_D_RUNTIME(ARRAY_BOUNDS, "_d_arraybounds", P2(STRING, UINT), VOID, FN_THROWS) + +// Used when new'ing a class. +DEF_D_RUNTIME(NEWCLASS, "_d_newclass", P1(CONST(CLASSINFO)), OBJECT, FN_NONE) + +// Used when calling delete on a class or interface. +DEF_D_RUNTIME(DELCLASS, "_d_delclass", P1(VOIDPTR), VOID, FN_NONE) +DEF_D_RUNTIME(DELINTERFACE, "_d_delinterface", P1(VOIDPTR), VOID, FN_NONE) + +// Same as deleting a class, but used for stack-allocated classes. +DEF_D_RUNTIME(CALLFINALIZER, "_d_callfinalizer", P1(VOIDPTR), VOID, FN_NONE) +DEF_D_RUNTIME(CALLINTERFACEFINALIZER, "_d_callinterfacefinalizer", P1(VOIDPTR), VOID, FN_NONE) + +// Used for casting to a class or interface. +DEF_D_RUNTIME(DYNAMIC_CAST, "_d_dynamic_cast", P2(OBJECT, CLASSINFO), OBJECT, FN_NONE) +DEF_D_RUNTIME(INTERFACE_CAST, "_d_interface_cast", P2(OBJECT, CLASSINFO), OBJECT, FN_NONE) + +// Used when new'ing a pointer. The 'i' variant is for when the initialiser is non-zero. +DEF_D_RUNTIME(NEWITEMT, "_d_newitemT", P1(CONST(TYPEINFO)), VOIDPTR, FN_NONE) +DEF_D_RUNTIME(NEWITEMIT, "_d_newitemiT", P1(CONST(TYPEINFO)), VOIDPTR, FN_NONE) + +// Used when calling delete on a pointer. +DEF_D_RUNTIME(DELMEMORY, "_d_delmemory", P1(POINTER(VOIDPTR)), VOID, FN_NONE) + +// Used when new'ing an array. The 'i' variant is for when the initialiser is +// non-zero, and the 'm' variant is when initialising a multi-dimensional array. +DEF_D_RUNTIME(NEWARRAYT, "_d_newarrayT", P2(CONST(TYPEINFO), SIZE_T), ARRAY(VOID), FN_NONE) +DEF_D_RUNTIME(NEWARRAYIT, "_d_newarrayiT", P2(CONST(TYPEINFO), SIZE_T), ARRAY(VOID), FN_NONE) +DEF_D_RUNTIME(NEWARRAYMTX, "_d_newarraymTX", P3(CONST(TYPEINFO), SIZE_T, SIZE_T), ARRAY(VOID), FN_NONE) +DEF_D_RUNTIME(NEWARRAYMITX, "_d_newarraymiTX", P3(CONST(TYPEINFO), SIZE_T, SIZE_T), ARRAY(VOID), FN_NONE) + +// Used for allocating array literal expressions on heap. +DEF_D_RUNTIME(ARRAYLITERALTX, "_d_arrayliteralTX", P2(CONST(TYPEINFO), SIZE_T), VOIDPTR, FN_NONE) + +// Used when calling delete on an array. +DEF_D_RUNTIME(DELARRAYT, "_d_delarray_t", P2(ARRAYPTR(VOID), CONST(TYPEINFO)), VOID, FN_NONE) + +// Used for value equality (x == y) and comparisons (x < y) of non-trivial arrays. +// Such as an array of structs or classes. +DEF_D_RUNTIME(ADEQ2, "_adEq2", P3(ARRAY(VOID), ARRAY(VOID), CONST(TYPEINFO)), INT, FN_NONE) +DEF_D_RUNTIME(ADCMP2, "_adCmp2", P3(ARRAY(VOID), ARRAY(VOID), CONST(TYPEINFO)), INT, FN_NONE) + +// Used when casting from one array type to another where the index type +// sizes differ. Such as from int[] to short[]. +DEF_D_RUNTIME(ARRAYCAST, "_d_arraycast", P3(SIZE_T, SIZE_T, ARRAY(VOID)), ARRAY(VOID), FN_NONE) + +// Used for (arr.length = n) expressions. The 'i' variant is for when the +// initialiser is non-zero. +DEF_D_RUNTIME(ARRAYSETLENGTHT, "_d_arraysetlengthT", P3(CONST(TYPEINFO), SIZE_T, ARRAYPTR(VOID)), ARRAY(VOID), FN_NONE) +DEF_D_RUNTIME(ARRAYSETLENGTHIT, "_d_arraysetlengthiT", P3(CONST(TYPEINFO), SIZE_T, ARRAYPTR(VOID)), ARRAY(VOID), FN_NONE) + +// Used for allocating closures on heap. +DEF_D_RUNTIME(ALLOCMEMORY, "_d_allocmemory", P1(SIZE_T), VOIDPTR, FN_MALLOC) + +// Used for copying an array into a slice, adds an enforcment that the source +// and destination are equal in size and do not overlap. +DEF_D_RUNTIME(ARRAYCOPY, "_d_arraycopy", P3(SIZE_T, ARRAY(VOID), ARRAY(VOID)), ARRAY(VOID), FN_NONE) + +// Used for array assignments from an existing array. +// The 'set' variant is for when the assignment value is a single element. +DEF_D_RUNTIME(ARRAYASSIGN, "_d_arrayassign", P3(CONST(TYPEINFO), ARRAY(VOID), ARRAY(VOID)), ARRAY(VOID), FN_NONE) +DEF_D_RUNTIME(ARRAYSETASSIGN, "_d_arraysetassign", P4(VOIDPTR, VOIDPTR, SIZE_T, CONST(TYPEINFO)), VOIDPTR, FN_NONE) + +// Used for constructing a new array from an existing array. +// The 'set' variant is for when the constructor value is a single element. +DEF_D_RUNTIME(ARRAYCTOR, "_d_arrayctor", P3(CONST(TYPEINFO), ARRAY(VOID), ARRAY(VOID)), ARRAY(VOID), FN_NONE) +DEF_D_RUNTIME(ARRAYSETCTOR, "_d_arraysetctor", P4(VOIDPTR, VOIDPTR, SIZE_T, CONST(TYPEINFO)), VOIDPTR, FN_NONE) + +// Used for concatenating two or more arrays together. Then 'n' variant is +// for when there is more than two arrays to handle. +DEF_D_RUNTIME(ARRAYCATT, "_d_arraycatT", P3(CONST(TYPEINFO), ARRAY(BYTE), ARRAY(BYTE)), ARRAY(BYTE), FN_NONE) +DEF_D_RUNTIME(ARRAYCATNT, "_d_arraycatnT", P2(CONST(TYPEINFO), UINT), ARRAY(VOID), FN_VARARGS) + +// Used for appending a single element to an array. +DEF_D_RUNTIME(ARRAYAPPENDCTX, "_d_arrayappendcTX", P3(CONST(TYPEINFO), ARRAYPTR(BYTE), SIZE_T), ARRAY(BYTE), FN_NONE) + +// Same as appending a single element to an array, but specific for when the +// source is a UTF-32 character, and the destination is a UTF-8 or 16 array. +DEF_D_RUNTIME(ARRAYAPPENDCD, "_d_arrayappendcd", P2(ARRAYPTR(BYTE), DCHAR), ARRAY(VOID), FN_NONE) +DEF_D_RUNTIME(ARRAYAPPENDWD, "_d_arrayappendwd", P2(ARRAYPTR(BYTE), DCHAR), ARRAY(VOID), FN_NONE) + +// Used for appending an existing array to another. +DEF_D_RUNTIME(ARRAYAPPENDT, "_d_arrayappendT", P3(TYPEINFO, ARRAYPTR(BYTE), ARRAY(BYTE)), ARRAY(VOID), FN_NONE) + +// Used for allocating a new associative array. +DEF_D_RUNTIME(ASSOCARRAYLITERALTX, "_d_assocarrayliteralTX", P3(CONST(TYPEINFO), ARRAY(VOID), ARRAY(VOID)), VOIDPTR, FN_NONE) + +// Used for value equality of two associative arrays. +DEF_D_RUNTIME(AAEQUAL, "_aaEqual", P3(CONST(TYPEINFO), AA, AA), INT, FN_NONE) + +// Used to determine is a key exists in an associative array. +DEF_D_RUNTIME(AAINX, "_aaInX", P3(AA, CONST(TYPEINFO), VOIDPTR), VOIDPTR, FN_NONE) + +// Used to retrieve a value from an associative array index by a key. +// The 'Rvalue' variant returns null if the key is not found, where as aaGetX +// will create new key entry for assignment. +DEF_D_RUNTIME(AAGETX, "_aaGetX", P4(POINTER(AA), CONST(TYPEINFO), SIZE_T, VOIDPTR), VOIDPTR, FN_NONE) +DEF_D_RUNTIME(AAGETRVALUEX, "_aaGetRvalueX", P4(AA, CONST(TYPEINFO), SIZE_T, VOIDPTR), VOIDPTR, FN_NONE) + +// Used when calling delete on a key entry in an associative array. +DEF_D_RUNTIME(AADELX, "_aaDelX", P3(AA, CONST(TYPEINFO), VOIDPTR), BOOL, FN_NONE) + +// Used for throw() expressions. +DEF_D_RUNTIME(THROW, "_d_throw", P1(OBJECT), VOID, FN_THROWS) + +// When invariant() contracts are turned on, used after testing whether a +// class != null for validating the state of a class. +DEF_D_RUNTIME(INVARIANT, "_D9invariant12_d_invariantFC6ObjectZv", P1(OBJECT), VOID, FN_NONE) + +// Used when performing a switch/cases on a string. The 'u' and 'd' variants +// are for UTF-16 and UTF-32 strings respectively. +DEF_D_RUNTIME(SWITCH_STRING, "_d_switch_string", P2(ARRAY(STRING), STRING), INT, FN_NONE) +DEF_D_RUNTIME(SWITCH_USTRING, "_d_switch_ustring", P2(ARRAY(WSTRING), WSTRING), INT, FN_NONE) +DEF_D_RUNTIME(SWITCH_DSTRING, "_d_switch_dstring", P2(ARRAY(DSTRING), DSTRING), INT, FN_NONE) + +// Used when throwing an error that a switch statement has no default case, +// and yet none of the existing cases matched. +DEF_D_RUNTIME(SWITCH_ERROR, "_d_switch_error", P2(STRING, UINT), VOID, FN_THROWS) + +// Used as the symbol to instead put in a class vtable if a method doesn't +// override, but instead conflicts with another method found in a base class. +DEF_D_RUNTIME(HIDDEN_FUNC, "_d_hidden_func", P1(VOIDPTR), VOID, FN_THROWS) + +// Remove helper macros +#undef CONST +#undef ARRAY +#undef POINTER +#undef ARRAYPTR + +#undef STRING +#undef WSTRING +#undef DSTRING +#undef DCHAR +#undef SIZE_T +#undef BYTE +#undef INT +#undef UINT +#undef BOOL +#undef VOID +#undef VOIDPTR +#undef TYPEINFO +#undef CLASSINFO +#undef OBJECT + +#undef FPARAMS +#undef P0 +#undef P1 +#undef P2 +#undef P3 +#undef P4 + +#undef FN_NONE +#undef FN_THROWS +#undef FN_MALLOC +#undef FN_VARARGS + diff --git a/gcc/d/toir.cc b/gcc/d/toir.cc index f6add0ce5..0920ce213 100644 --- a/gcc/d/toir.cc +++ b/gcc/d/toir.cc @@ -443,19 +443,6 @@ class IRVisitor : public Visitor } } - // - void visit(DtorExpStatement *s) - { - FuncDeclaration *fd = this->irs_->func; - gcc_assert(fd != NULL); - - // Do not call destructor if var is returned via NRVO. - bool nodtor = (fd->nrvo_can && fd->nrvo_var == s->var); - - if (!nodtor) - this->visit((ExpStatement *)s); - } - // void visit(CompoundStatement *s) { @@ -640,21 +627,6 @@ class IRVisitor : public Visitor sorry("D inline assembler statements are not supported in GDC."); } - // - void visit(ImportStatement *s) - { - if (s->imports == NULL) - return; - - for (size_t i = 0; i < s->imports->dim; i++) - { - Dsymbol *dsym = (*s->imports)[i]; - - if (dsym != NULL) - dsym->toObjFile(0); - } - } - // Build a GCC extended assembler expression, whose components are // an INSN string, some OUTPUTS, some INPUTS, and some CLOBBERS. void visit(ExtAsmStatement *s) @@ -663,6 +635,7 @@ class IRVisitor : public Visitor tree outputs = NULL_TREE; tree inputs = NULL_TREE; tree clobbers = NULL_TREE; + tree labels = NULL_TREE; this->irs_->doLineNote(s->loc); @@ -695,7 +668,7 @@ class IRVisitor : public Visitor // Collect all clobber arguments. if (s->clobbers) { - for(size_t i = 0; i < s->clobbers->dim; i++) + for (size_t i = 0; i < s->clobbers->dim; i++) { StringExp *clobber = (StringExp *)(*s->clobbers)[i]; tree val = build_string(clobber->len, (char *)clobber->string); @@ -703,10 +676,28 @@ class IRVisitor : public Visitor } } - // TODO: Add frontend support for 'goto asm'. + // Collect all goto labels, these should have been already checked + // by the front-end, so pass down the label symbol to the backend. + if (s->labels) + { + for (size_t i = 0; i < s->labels->dim; i++) + { + Identifier *ident = (*s->labels)[i]; + GotoStatement *gs = (*s->gotos)[i]; + + gcc_assert(gs->label->statement != NULL); + gcc_assert(gs->tf == gs->label->statement->tf); + + tree name = build_string(ident->len, ident->string); + tree label = this->irs_->getLabelTree(gs->label); + + labels = chainon(labels, build_tree_list(name, label)); + } + } + tree exp = build5(ASM_EXPR, void_type_node, build_string(insn->len, (char *)insn->string), - outputs, inputs, clobbers, NULL_TREE); + outputs, inputs, clobbers, labels); SET_EXPR_LOCATION(exp, input_location); // If the extended syntax was not used, mark the ASM_EXPR. @@ -719,6 +710,20 @@ class IRVisitor : public Visitor this->irs_->addExp(exp); } + // + void visit(ImportStatement *s) + { + if (s->imports == NULL) + return; + + for (size_t i = 0; i < s->imports->dim; i++) + { + Dsymbol *dsym = (*s->imports)[i]; + + if (dsym != NULL) + dsym->toObjFile(0); + } + } }; diff --git a/gcc/testsuite/gdc.test/compilable/aliasdecl.d b/gcc/testsuite/gdc.test/compilable/aliasdecl.d index cb56e987d..90e21effa 100644 --- a/gcc/testsuite/gdc.test/compilable/aliasdecl.d +++ b/gcc/testsuite/gdc.test/compilable/aliasdecl.d @@ -11,6 +11,11 @@ alias X4 = void delegate() const, X5 = Test!int; static assert(is(X4 == void delegate() const)); static assert(is(X5.Type == int)); +alias FP5 = extern(C) pure nothrow @safe @nogc void function(), + DG5 = extern(D) pure nothrow @safe @nogc void delegate(); +static assert(FP5.stringof == "extern (C) void function() pure nothrow " /* ~ "@safe " */ ~ "@nogc"); +static assert(DG5.stringof == "void delegate() pure nothrow " /* ~ "@safe " */ ~ "@nogc"); + void main() { alias Y1 = int; diff --git a/gcc/testsuite/gdc.test/compilable/bug11735.d b/gcc/testsuite/gdc.test/compilable/bug11735.d index f26995620..b94cb6e77 100644 --- a/gcc/testsuite/gdc.test/compilable/bug11735.d +++ b/gcc/testsuite/gdc.test/compilable/bug11735.d @@ -10,6 +10,9 @@ print dstring يطبع الترميز الموحد يطبع الترميز الموحد يطبع الترميز الموحد +foo_str +foo_wstr +foo_dstr --- */ @@ -20,3 +23,14 @@ pragma(msg, "print dstring"d); pragma(msg, "يطبع الترميز الموحد"); pragma(msg, "يطبع الترميز الموحد"w); pragma(msg, "يطبع الترميز الموحد"d); + +void main() +{ + enum a = "foo_str"; + enum b = "foo_wstr"w; + enum c = "foo_dstr"d; + + pragma(msg, a); + pragma(msg, b); + pragma(msg, c); +} diff --git a/gcc/testsuite/gdc.test/compilable/bug6963.d b/gcc/testsuite/gdc.test/compilable/bug6963.d index 200a8e885..33d595cfe 100644 --- a/gcc/testsuite/gdc.test/compilable/bug6963.d +++ b/gcc/testsuite/gdc.test/compilable/bug6963.d @@ -4,12 +4,12 @@ /* TEST_OUTPUT: --- -output foo: 1e: pure nothrow @safe void(int x) -output foo: 3e: pure nothrow @safe void(int x) +output foo: 1e: pure nothrow @nogc @safe void(int x) +output foo: 3e: pure nothrow @nogc @safe void(int x) --- */ -alias void function(int) pure nothrow @safe FuncPtrType; +alias void function(int) pure nothrow @safe @nogc FuncPtrType; void foo1a(X)(X x) {} void foo1b(X)(X x) {} diff --git a/gcc/testsuite/gdc.test/compilable/compile1.d b/gcc/testsuite/gdc.test/compilable/compile1.d index ebb0c4573..a4ab0ea2a 100644 --- a/gcc/testsuite/gdc.test/compilable/compile1.d +++ b/gcc/testsuite/gdc.test/compilable/compile1.d @@ -212,6 +212,35 @@ static assert( !is(typeof( (){ ]; }))); +/************************************************** + 11991 +**************************************************/ + +void main() +{ + int Throwable; + int object; + try + { + } + catch + { + } +} + +/************************************************** + 11939 +**************************************************/ + +void test11939() +{ + scope(failure) + { + import object : Object; + } + throw new Exception(""); +} + /************************************************** 5796 **************************************************/ @@ -369,6 +398,25 @@ struct Bug7058 } +/***************************************************/ + +void test12094() +{ + auto n = null; + int *a; + int[int] b; + int[] c; + auto u = true ? null : a; + auto v = true ? null : b; + auto w = true ? null : c; + auto x = true ? n : a; + auto y = true ? n : b; + auto z = true ? n : c; + a = n; + b = n; + c = n; +} + /***************************************************/ template test8163(T...) @@ -397,6 +445,26 @@ alias test8163!(ubyte, ubyte, ubyte, ubyte, ubyte, ubyte, ubyte, ubyte) _BBBBBBB alias test8163!(ubyte, ubyte, ushort, float) _BBSf; +/***************************************************/ +// 4757 + +auto foo4757(T)(T) +{ + static struct Bar(T) + { + void spam() + { + foo4757(1); + } + } + return Bar!T(); +} + +void test4757() +{ + foo4757(1); +} + /***************************************************/ // 9348 @@ -443,6 +511,16 @@ static if (is(object.ModuleInfo == class)) __traits(classInstanceSize, ModuleInfo)); } +/***************************************************/ +// 10326 + +class C10326 +{ + int val; + invariant { assert(val == 0); } + invariant() { assert(val == 0); } +} + /***************************************************/ // 11554 @@ -451,3 +529,128 @@ static assert(is(E11554 == enum)); struct Bro11554(N...) {} static assert(!is(E11554 unused : Bro11554!M, M...)); + +/***************************************************/ +// 12302 + +template isCallable12302(T...) + if (T.length == 1) +{ + static if (is(typeof(& T[0].opCall) == delegate)) + enum bool isCallable12302 = true; + else + static if (is(typeof(& T[0].opCall) V : V*) && is(V == function)) + enum bool isCallable12302 = true; + else + enum bool isCallable12302 = true; +} + +class A12302 +{ + struct X {} + X x; + auto opDispatch(string s, TArgs...)(TArgs args) + { + mixin("return x."~s~"(args);"); + } +} + +A12302 func12302() { return null; } +enum b12302 = isCallable12302!func12302; + +/***************************************************/ +// 12476 + +template A12476(T) { } + +struct S12476(T) +{ + alias B = A12476!T; +} + +class C12476(T) +{ + alias B = A12476!T; +} + +struct Bar12476(alias Foo) +{ + Foo!int baz; + alias baz this; +} + +alias Identity12476(alias A) = A; + +alias sb12476 = Identity12476!(Bar12476!S12476.B); +alias cb12476 = Identity12476!(Bar12476!C12476.B); + +static assert(__traits(isSame, sb12476, A12476!int)); +static assert(__traits(isSame, cb12476, A12476!int)); + +/***************************************************/ +// 12506 + +import imports.a12506; +private bool[9] r12506a = f12506!(i => true)(); // OK +private immutable bool[9] r12506b = f12506!(i => true)(); // OK <- error + +/***************************************************/ +// 12555 + +class A12555(T) +{ + Undef12555 error; +} + +static assert(!__traits(compiles, { + class C : A12555!C { } +})); + +/***************************************************/ +// 11622 + +class A11622(T) +{ + B11622!T foo() + { + return new B11622!T; + } +} + +class B11622(T) : T +{ +} + +static assert(!__traits(compiles, { + class C : A11622!C { } +})); + +/***************************************************/ +// 12688 + +void writeln12688(A...)(A) {} + +struct S12688 +{ + int foo() @property { return 1; } +} + +void test12688() +{ + S12688 s; + s.foo.writeln12688; // ok + (s.foo).writeln12688; // ok <- ng +} + +/***************************************************/ +// 12703 + +struct S12703 +{ + this(int) {} +} + +final class C12703 +{ + S12703 s = S12703(1); +} diff --git a/gcc/testsuite/gdc.test/compilable/cppmangle.d b/gcc/testsuite/gdc.test/compilable/cppmangle.d index cfb0fafd4..663758904 100644 --- a/gcc/testsuite/gdc.test/compilable/cppmangle.d +++ b/gcc/testsuite/gdc.test/compilable/cppmangle.d @@ -271,7 +271,7 @@ static assert(test10058l.mangleof == "_Z10test10058lPFPvS_EPFS_PKvEPFS3_S_E"); // 11696 class Expression; -struct Loc; +struct Loc {} extern(C++) class CallExp diff --git a/gcc/testsuite/gdc.test/compilable/ddoc1.d b/gcc/testsuite/gdc.test/compilable/ddoc1.d index 252cbf0fb..fa5042a47 100644 --- a/gcc/testsuite/gdc.test/compilable/ddoc1.d +++ b/gcc/testsuite/gdc.test/compilable/ddoc1.d @@ -12,7 +12,7 @@ module abc; string foos = "foo"; alias int myint; /// -typedef int mytypedefint; +alias int mytypedefint; /** windy * city diff --git a/gcc/testsuite/gdc.test/compilable/ddoc10366.d b/gcc/testsuite/gdc.test/compilable/ddoc10366.d new file mode 100644 index 000000000..5bc5f641f --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ddoc10366.d @@ -0,0 +1,20 @@ +// PERMUTE_ARGS: +// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o- +// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 10366 + +/// +struct S(T) +{ + /// + void method() {} + + public + { + /// + struct Nested + { + /// + void nestedMethod() {} + } + } +} diff --git a/gcc/testsuite/gdc.test/compilable/ddoc11511.d b/gcc/testsuite/gdc.test/compilable/ddoc11511.d new file mode 100644 index 000000000..ba5829ec6 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ddoc11511.d @@ -0,0 +1,20 @@ +// PERMUTE_ARGS: +// REQUIRED_ARGS: -w -D -Dd${RESULTS_DIR}/compilable -o- +// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 11511 +module ddoc11511; + +/** +Params: +abcd = none1 +bcdef = none23 +... = doo +*/ +void foo(int abcd, int bcdef, ...); + +/** +Params: +abcd = none1 +bcdef = none23 +arr = doo +*/ +void foo(int abcd, int bcdef, int[] arr...); diff --git a/gcc/testsuite/gdc.test/compilable/ddoc11823.d b/gcc/testsuite/gdc.test/compilable/ddoc11823.d new file mode 100644 index 000000000..dfde4b68c --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ddoc11823.d @@ -0,0 +1,7 @@ +// PERMUTE_ARGS: +// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o- +// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 11823 +module ddoc11823; + +/// file function name is _file, arg defaults to __FILE__ but not __something__ +void file(string arg) { } diff --git a/gcc/testsuite/gdc.test/compilable/ddoc12706.d b/gcc/testsuite/gdc.test/compilable/ddoc12706.d new file mode 100644 index 000000000..399583cf8 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ddoc12706.d @@ -0,0 +1,9 @@ +// PERMUTE_ARGS: +// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o- +// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 12706 + +/// +void test()(string[] args) if (args[$]) +{ +} + diff --git a/gcc/testsuite/gdc.test/compilable/ddoc198.d b/gcc/testsuite/gdc.test/compilable/ddoc198.d new file mode 100644 index 000000000..16485b78b --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ddoc198.d @@ -0,0 +1,35 @@ +// EXTRA_SOURCES: extra-files/ddoc198.ddoc +// PERMUTE_ARGS: +// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o- +// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 198 + +module ddoc198; + +/// +interface I1 { } + +/// +class C1 { } + +/// +class Foo : C1, I1 { } + +/// +enum X { x = 1 } + +/// +enum Y : X { y = X.x } + +/// +struct S1 { } + +/// +enum enS : S1 { a = S1() } + +// disabled until class enums are possible +// enum enC : C1 { a = new C1() } + + +void main() +{ +} diff --git a/gcc/testsuite/gdc.test/compilable/ddoc648.d b/gcc/testsuite/gdc.test/compilable/ddoc648.d new file mode 100644 index 000000000..49c90973a --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ddoc648.d @@ -0,0 +1,90 @@ +// PERMUTE_ARGS: +// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o- +// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 648 + +module ddoc648; + +/// Mixin declaration +mixin template Mixin1() +{ + /// struct S + struct S { } +} + +/// class A +class A +{ + /// field x + int x; + + /// no docs for mixin statement (only for expanded members) + mixin Mixin1!(); +} + +/// class AB +class AB +{ + /// field x + int x; + + // no docs for mixin or its contents, must be a ddoc comment + mixin Mixin1!(); +} + +/// Mixin declaration2 +mixin template Mixin2() +{ + /// struct S2 + struct S2 { } +} + +/// Mixin declaration3 +mixin template Mixin3() +{ + /// another field + int f; + + /// no docs for mixin statement (only for expanded members) + mixin Mixin2!(); +} + +/// class B1 +class B1 +{ + /// no docs for mixin statement (only for expanded members) + mixin Mixin3!(); +} + + +/// Mixin declaration3 +mixin template Mixin4() +{ + /// another field + int f; + + // no docs at all for non-ddoc comment + mixin Mixin2!(); +} + +/// class B2 +class B2 +{ + /// no docs for mixin statement (only for expanded members) + mixin Mixin4!(); +} + +/// no docs for mixin statement (only for expanded members) +mixin Mixin3!(); + +/// +struct TS(T) +{ + mixin template MT() + { + } + + mixin MT; /// avoid calling semantic + + /// + int field; +} diff --git a/gcc/testsuite/gdc.test/compilable/ddoc7555.d b/gcc/testsuite/gdc.test/compilable/ddoc7555.d new file mode 100644 index 000000000..efbb54785 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ddoc7555.d @@ -0,0 +1,53 @@ +// PERMUTE_ARGS: +// REQUIRED_ARGS: -D -Dd${RESULTS_DIR}/compilable -o- +// POST_SCRIPT: compilable/extra-files/ddocAny-postscript.sh 7555 +module ddoc7555; + +/** +Dummy doc. + +$(X0 + DelimitedString + TokenString) + +$(X1 + DelimitedString + TokenString) + +$(X2 x,HexString) +$(X2 x, HexString) + +$(X3 x,x,HexString) +$(X3 x,x, HexString) + +$(X4 x,x,x,HexString) +$(X4 x,x,x, HexString) + +$(X5 x,x,x,x,HexString) +$(X5 x,x,x,x, HexString) + +$(X6 x,x,x,x,x,HexString) +$(X6 x,x,x,x,x, HexString) + +$(X7 x,x,x,x,x,x,HexString) +$(X7 x,x,x,x,x,x, HexString) + +$(X8 x,x,x,x,x,x,x,HexString) +$(X8 x,x,x,x,x,x,x, HexString) + +$(X9 x,x,x,x,x,x,x,x,HexString) +$(X9 x,x,x,x,x,x,x,x, HexString) + +Macros: + X0=$0 + X1=$1 + X2=$2 + X3=$3 + X4=$4 + X5=$5 + X6=$6 + X7=$7 + X8=$8 + X9=$9 +*/ +void dummy(); diff --git a/gcc/testsuite/gdc.test/compilable/ddocunittest.d b/gcc/testsuite/gdc.test/compilable/ddocunittest.d index a4ce4b562..986187eda 100644 --- a/gcc/testsuite/gdc.test/compilable/ddocunittest.d +++ b/gcc/testsuite/gdc.test/compilable/ddocunittest.d @@ -173,6 +173,108 @@ unittest foo(4); } +// ------------------------------------ +// insert import declaration between documented function and unittests + +/// +void fooImport() {} +import core.stdc.stdio; +/// test +unittest { fooImport(); } + +/// +void fooStaticImport() {} +static import core.stdc.stdlib; +/// test +unittest { fooStaticImport(); } + +/// +void fooPublicImport() {} +public import core.stdc.string; +/// test +unittest { fooPublicImport(); } + +/// +void fooSelectiveImport() {} +import core.stdc.ctype : isalpha; +/// test +unittest { fooSelectiveImport(); } + +/// +void fooRenamedImport() {} +import io = core.stdc.stdio; +/// test +unittest { fooRenamedImport(); } + +// ------------------------------------ +// documented unittest after conditional declarations + +static if (true) + void fooConditionalDecl1a() {} /** */ +unittest { int x1a; } /// + +static if (true) +{ void fooConditionalDecl1b() {} /** */ } +unittest { int x1b; } /// + +static if (false) + void fooConditionalDecl2a() {} /** */ +unittest { int x2a; } /// + +static if (false) +{ void fooConditionalDecl2b() {} /** */ } +unittest { int x2b; } /// + +static if (true) +{ void fooConditionalDecl3a() {} /** */ } +else +{ void barConditionalDecl3a() {} /** */ } +unittest { int x3a; } /// + +static if (true) +{ void fooConditionalDecl3b() {} /** */ } +else +{ void barConditionalDecl3b() {} /** */ } +unittest { int x3b; } /// + +static if (false) + void fooConditionalDecl4a() {} /** */ +else + void barConditionalDecl4a() {} /** */ +unittest { int x4a; } /// + +static if (false) +{ void fooConditionalDecl4b() {} /** */ } +else +{ void barConditionalDecl4b() {} /** */ } +unittest { int x4b; } /// + +static if (true) +{} +else + void barConditionalDecl5a() {} /** */ +unittest { int x5a; } /// + +static if (true) +{} +else +{ void barConditionalDecl5b() {} /** */ } +unittest { int x5b; } /// + +static if (false) +{} +else + void barConditionalDecl6a() {} /** */ +/// +unittest { int x6a; } + +static if (false) +{} +else +{ void barConditionalDecl6b() {} /** */ } +/// +unittest { int x6b; } + // ------------------------------------ // 9474 @@ -288,6 +390,34 @@ unittest assert(!balancedParens10519(s, '(', ')')); } +// ------------------------------------ +// Issue 12097 + +/// declaration +struct S12097 +{ + /// method + void foo() {} +} + +/// ditto +void f12097() {} + +/// ddoc code 1 +unittest +{ + int a = 1; +} + +/// ditto +struct T12097(T) {} + +/// ddoc code 2 +unittest +{ + int[] arr; +} + // ------------------------------------ void main() { } diff --git a/gcc/testsuite/gdc.test/compilable/deprecate12979a.d b/gcc/testsuite/gdc.test/compilable/deprecate12979a.d new file mode 100644 index 000000000..b15823b77 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/deprecate12979a.d @@ -0,0 +1,27 @@ +// REQUIRED_ARGS: -dw +// PERMUTE_ARGS: + +/* +TEST_OUTPUT: +--- +compilable/deprecate12979a.d(13): Deprecation: asm statement is assumed to throw - mark it with 'nothrow' if it does not +--- +*/ + +void foo() nothrow +{ + version(GNU) + { + asm + { + ""; + } + } + else + { + asm + { + ret; + } + } +} diff --git a/gcc/testsuite/gdc.test/compilable/deprecate2.d b/gcc/testsuite/gdc.test/compilable/deprecate2.d index 85d8573f0..e70ef441c 100644 --- a/gcc/testsuite/gdc.test/compilable/deprecate2.d +++ b/gcc/testsuite/gdc.test/compilable/deprecate2.d @@ -9,7 +9,7 @@ **************************************************/ alias float[2] vector2; -typedef vector2 point2; // if I change this typedef to alias it works fine +alias vector2 point2; // if I change this typedef to alias it works fine float distance(point2 a, point2 b) { diff --git a/gcc/testsuite/gdc.test/compilable/diag10768.d b/gcc/testsuite/gdc.test/compilable/diag10768.d index c26b69057..3dc3ba109 100644 --- a/gcc/testsuite/gdc.test/compilable/diag10768.d +++ b/gcc/testsuite/gdc.test/compilable/diag10768.d @@ -2,7 +2,7 @@ /* TEST_OUTPUT: --- -compilable/diag10768.d(36): Deprecation: overriding base class function without using override attribute is deprecated (diag10768.Foo.frop overrides diag10768.Frop.frop) +compilable/diag10768.d(36): Deprecation: implicitly overriding base class method diag10768.Frop.frop with diag10768.Foo.frop deprecated; add 'override' attribute --- */ diff --git a/gcc/testsuite/gdc.test/compilable/diag11066.d b/gcc/testsuite/gdc.test/compilable/diag11066.d new file mode 100644 index 000000000..3d9383189 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/diag11066.d @@ -0,0 +1,13 @@ +// REQUIRED_ARGS: -w -profile +/* +TEST_OUTPUT: +--- +--- +*/ + +void main() +{ + string s; + foreach (dchar c; s) // affected by dchar + return; +} diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/ddoc10366.html b/gcc/testsuite/gdc.test/compilable/extra-files/ddoc10366.html new file mode 100644 index 000000000..218742871 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/extra-files/ddoc10366.html @@ -0,0 +1,28 @@ + + + ddoc10366 + +

ddoc10366

+

+
struct S(T); +
+


+
void method(); +
+


+
+
struct Nested; +
+


+
void nestedMethod(); +
+


+
+
+
+
+
+
+ +
Page generated by Ddoc. + diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/ddoc11511.html b/gcc/testsuite/gdc.test/compilable/extra-files/ddoc11511.html new file mode 100644 index 000000000..00caa60d4 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/extra-files/ddoc11511.html @@ -0,0 +1,34 @@ + + + ddoc11511 + +

ddoc11511

+

+
void foo(int abcd, int bcdef, ...); +
+
Params:
+ + + + + + +
int abcdnone1
int bcdefnone23
...doo

+ +
+
void foo(int abcd, int bcdef, int[] arr...); +
+
Params:
+ + + + + + +
int abcdnone1
int bcdefnone23
int[] arrdoo

+ +
+
+ +
Page generated by Ddoc. + diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/ddoc11823.html b/gcc/testsuite/gdc.test/compilable/extra-files/ddoc11823.html new file mode 100644 index 000000000..eaa428631 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/extra-files/ddoc11823.html @@ -0,0 +1,15 @@ + + + ddoc11823 + +

ddoc11823

+

+
void file(string arg); +
+
file function name is file, arg defaults to __FILE__ but not _something__

+ +
+
+ +
Page generated by Ddoc. + diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/ddoc12706.html b/gcc/testsuite/gdc.test/compilable/extra-files/ddoc12706.html new file mode 100644 index 000000000..cc6e1996a --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/extra-files/ddoc12706.html @@ -0,0 +1,14 @@ + + + ddoc12706 + +

ddoc12706

+

+
void test()(string[] args) if (args[$]); +
+


+
+
+ +
Page generated by Ddoc. + diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/ddoc198.ddoc b/gcc/testsuite/gdc.test/compilable/extra-files/ddoc198.ddoc new file mode 100644 index 000000000..4b69e8f6b --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/extra-files/ddoc198.ddoc @@ -0,0 +1,2 @@ +DDOC_PSYMBOL = $0 +DDOC_PSUPER_SYMBOL = $0 diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/ddoc198.html b/gcc/testsuite/gdc.test/compilable/extra-files/ddoc198.html new file mode 100644 index 000000000..559f46f68 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/extra-files/ddoc198.html @@ -0,0 +1,38 @@ + + + ddoc198 + +

ddoc198

+

+
interface I1; +
+


+
+
class C1; +
+


+
+
class Foo: ddoc198.C1, ddoc198.I1; +
+


+
+
enum X: int; +
+


+
+
enum Y: X; +
+


+
+
struct S1; +
+


+
+
enum enS: S1; +
+


+
+
+ +
Page generated by Ddoc. + diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/ddoc648.html b/gcc/testsuite/gdc.test/compilable/extra-files/ddoc648.html new file mode 100644 index 000000000..c363c4464 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/extra-files/ddoc648.html @@ -0,0 +1,123 @@ + + + ddoc648 + +

ddoc648

+

+
template Mixin1()
+
Mixin declaration

+ +
struct S; +
+
struct S

+ +
+
+
+
class A; +
+
class A

+ +
int x; +
+
field x

+ +
+
struct S; +
+
struct S

+ +
+
+
+
class AB; +
+
class AB

+ +
int x; +
+
field x

+ +
+
+
+
template Mixin2()
+
Mixin declaration2

+ +
struct S2; +
+
struct S2

+ +
+
+
+
template Mixin3()
+
Mixin declaration3

+ +
int f; +
+
another field

+ +
+
+
+
class B1; +
+
class B1

+ +
int f; +
+
another field

+ +
+
struct S2; +
+
struct S2

+ +
+
+
+
template Mixin4()
+
Mixin declaration3

+ +
int f; +
+
another field

+ +
+
+
+
class B2; +
+
class B2

+ +
int f; +
+
another field

+ +
+
+
+
int f; +
+
another field

+ +
+
struct S2; +
+
struct S2

+ +
+
struct TS(T); +
+


+
int field; +
+


+
+
+
+
+ +
Page generated by Ddoc. + diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/ddoc7555.html b/gcc/testsuite/gdc.test/compilable/extra-files/ddoc7555.html new file mode 100644 index 000000000..4fc09e58b --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/extra-files/ddoc7555.html @@ -0,0 +1,56 @@ + + + ddoc7555 + +

ddoc7555

+

+
void dummy(); +
+
Dummy doc. +

+ DelimitedString + TokenString +

+ + DelimitedString + TokenString +

+ +HexString +HexString +

+ +HexString +HexString +

+ +HexString +HexString +

+ +HexString +HexString +

+ +HexString +HexString +

+ +HexString +HexString +

+ +HexString +HexString +

+ +HexString +HexString + +

+ +
+
+ +
Page generated by Ddoc. + diff --git a/gcc/testsuite/gdc.test/compilable/inlineheader.d b/gcc/testsuite/gdc.test/compilable/extra-files/header1.d similarity index 95% rename from gcc/testsuite/gdc.test/compilable/inlineheader.d rename to gcc/testsuite/gdc.test/compilable/extra-files/header1.d index 6f043fd73..2b206380b 100644 --- a/gcc/testsuite/gdc.test/compilable/inlineheader.d +++ b/gcc/testsuite/gdc.test/compilable/extra-files/header1.d @@ -1,8 +1,3 @@ -// PERMUTE_ARGS: -// REQUIRED_ARGS: -H -Hd${RESULTS_DIR}/compilable -inline -// POST_SCRIPT: compilable/extra-files/inlineheader-postscript.sh -// REQUIRED_ARGS: -d - module foo.bar; import core.vararg; @@ -13,9 +8,9 @@ pragma(msg, "Hello World"); static assert(true, "message"); -typedef double mydbl = 10; +alias double mydbl; -int main() +int testmain() in { assert(1+(2+3) == -(1 - 2*3)); @@ -411,3 +406,17 @@ template Test10334(T...) {} /// mixin Test10334!int a; /// mixin Test10334!(int,long) b; /// mixin Test10334!"str" c; /// + +// 12266 +auto clamp12266a(T1, T2, T3)(T1 x, T2 min_val, T3 max_val) +{ + return 0; +} +pure clamp12266b(T1, T2, T3)(T1 x, T2 min_val, T3 max_val) +{ + return 0; +} +@disable pure clamp12266c(T1, T2, T3)(T1 x, T2 min_val, T3 max_val) +{ + return 0; +} diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/header.di b/gcc/testsuite/gdc.test/compilable/extra-files/header1.di similarity index 92% rename from gcc/testsuite/gdc.test/compilable/extra-files/header.di rename to gcc/testsuite/gdc.test/compilable/extra-files/header1.di index d9c5f6f61..5e3a42b36 100644 --- a/gcc/testsuite/gdc.test/compilable/extra-files/header.di +++ b/gcc/testsuite/gdc.test/compilable/extra-files/header1.di @@ -4,8 +4,8 @@ import std.stdio; pragma (lib, "test"); pragma (msg, "Hello World"); static assert(true, "message"); -typedef double mydbl = 10; -int main(); +alias double mydbl; +int testmain(); struct S { int m; @@ -299,10 +299,8 @@ int bar11(T)() } struct S6360 { - @property const pure nothrow long weeks1(); - - const nothrow pure @property long weeks2(); - + const pure nothrow @property long weeks1(); + const pure nothrow @property long weeks2(); } struct S12 { @@ -396,3 +394,15 @@ template Test10334(T...) mixin Test10334!int a; mixin Test10334!(int, long) b; mixin Test10334!"str" c; +auto clamp12266a(T1, T2, T3)(T1 x, T2 min_val, T3 max_val) +{ + return 0; +} +pure clamp12266b(T1, T2, T3)(T1 x, T2 min_val, T3 max_val) +{ + return 0; +} +@disable pure clamp12266c(T1, T2, T3)(T1 x, T2 min_val, T3 max_val) +{ + return 0; +} diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/header12567a.di b/gcc/testsuite/gdc.test/compilable/extra-files/header12567a.di new file mode 100644 index 000000000..e45bf4fd4 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/extra-files/header12567a.di @@ -0,0 +1,2 @@ +deprecated module header12567a; +void main(); diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/header12567b.di b/gcc/testsuite/gdc.test/compilable/extra-files/header12567b.di new file mode 100644 index 000000000..521a63563 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/extra-files/header12567b.di @@ -0,0 +1,2 @@ +deprecated("message") module header12567b; +void main(); diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/inlineheader.di b/gcc/testsuite/gdc.test/compilable/extra-files/header1i.di similarity index 93% rename from gcc/testsuite/gdc.test/compilable/extra-files/inlineheader.di rename to gcc/testsuite/gdc.test/compilable/extra-files/header1i.di index 8d711d1c7..8f9635068 100644 --- a/gcc/testsuite/gdc.test/compilable/extra-files/inlineheader.di +++ b/gcc/testsuite/gdc.test/compilable/extra-files/header1i.di @@ -4,8 +4,8 @@ import std.stdio; pragma (lib, "test"); pragma (msg, "Hello World"); static assert(true, "message"); -typedef double mydbl = 10; -int main() +alias double mydbl; +int testmain() in { assert(1 + (2 + 3) == -(1 - 2 * 3)); @@ -447,18 +447,16 @@ int bar11(T)() } struct S6360 { - @property const pure nothrow long weeks1() + const pure nothrow @property long weeks1() { return 0; } - - const nothrow pure @property long weeks2() + const pure nothrow @property long weeks2() { return 0; } - } struct S12 { @@ -558,3 +556,15 @@ template Test10334(T...) mixin Test10334!int a; mixin Test10334!(int, long) b; mixin Test10334!"str" c; +auto clamp12266a(T1, T2, T3)(T1 x, T2 min_val, T3 max_val) +{ + return 0; +} +pure clamp12266b(T1, T2, T3)(T1 x, T2 min_val, T3 max_val) +{ + return 0; +} +@disable pure clamp12266c(T1, T2, T3)(T1 x, T2 min_val, T3 max_val) +{ + return 0; +} diff --git a/gcc/testsuite/gdc.test/compilable/xheader.d b/gcc/testsuite/gdc.test/compilable/extra-files/header2.d similarity index 90% rename from gcc/testsuite/gdc.test/compilable/xheader.d rename to gcc/testsuite/gdc.test/compilable/extra-files/header2.d index f1fd60b74..a8dafefa4 100644 --- a/gcc/testsuite/gdc.test/compilable/xheader.d +++ b/gcc/testsuite/gdc.test/compilable/extra-files/header2.d @@ -1,7 +1,3 @@ -// PERMUTE_ARGS: -// REQUIRED_ARGS: -H -Hd${RESULTS_DIR}/compilable -// POST_SCRIPT: compilable/extra-files/xheader-postscript.sh - // for D 2.0 only class C { } diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/xheader.di b/gcc/testsuite/gdc.test/compilable/extra-files/header2.di similarity index 99% rename from gcc/testsuite/gdc.test/compilable/extra-files/xheader.di rename to gcc/testsuite/gdc.test/compilable/extra-files/header2.di index e359e5f36..b69cbe0a0 100644 --- a/gcc/testsuite/gdc.test/compilable/extra-files/xheader.di +++ b/gcc/testsuite/gdc.test/compilable/extra-files/header2.di @@ -15,7 +15,6 @@ struct Foo3 class C3 { @property int get(); - } T foo3(T)() { diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/inlinexheader.di b/gcc/testsuite/gdc.test/compilable/extra-files/header2i.di similarity index 99% rename from gcc/testsuite/gdc.test/compilable/extra-files/inlinexheader.di rename to gcc/testsuite/gdc.test/compilable/extra-files/header2i.di index 911b54776..51d950d46 100644 --- a/gcc/testsuite/gdc.test/compilable/extra-files/inlinexheader.di +++ b/gcc/testsuite/gdc.test/compilable/extra-files/header2i.di @@ -33,7 +33,6 @@ class C3 return 0; } - } T foo3(T)() { diff --git a/gcc/testsuite/gdc.test/compilable/extra-files/json.out b/gcc/testsuite/gdc.test/compilable/extra-files/json.out index 9d1d35b31..b271de03b 100644 --- a/gcc/testsuite/gdc.test/compilable/extra-files/json.out +++ b/gcc/testsuite/gdc.test/compilable/extra-files/json.out @@ -7,38 +7,45 @@ "name" : "_staticCtor1", "kind" : "function", "line" : 8, + "char" : 1, "storageClass" : [ "static" ], "deco" : "FZv", - "endline" : 8 + "endline" : 8, + "endchar" : 16 }, { "name" : "_staticDtor2", "kind" : "function", "line" : 10, + "char" : 1, "storageClass" : [ "static" ], "deco" : "FZv", - "endline" : 10 + "endline" : 10, + "endchar" : 17 }, { "name" : "myInt", "kind" : "alias", "line" : 13, + "char" : 11, "deco" : "i" }, { "name" : "x", "kind" : "variable", "line" : 14, + "char" : 7, "deco" : "i", "originalType" : "myInt" }, { "kind" : "template", "line" : 16, + "char" : 1, "name" : "Foo", "parameters" : [ { @@ -51,11 +58,13 @@ "name" : "Foo", "kind" : "struct", "line" : 16, + "char" : 1, "members" : [ { "name" : "t", "kind" : "variable", "line" : 16, + "char" : 19, "type" : "T" } ] @@ -65,6 +74,7 @@ { "kind" : "template", "line" : 17, + "char" : 1, "name" : "Bar", "parameters" : [ { @@ -78,11 +88,13 @@ "name" : "Bar", "kind" : "class", "line" : 17, + "char" : 1, "members" : [ { "name" : "t", "kind" : "variable", "line" : 17, + "char" : 25, "deco" : "i", "init" : "T" } @@ -93,6 +105,7 @@ { "kind" : "template", "line" : 18, + "char" : 1, "name" : "Baz", "parameters" : [ { @@ -105,11 +118,13 @@ "name" : "Baz", "kind" : "interface", "line" : 18, + "char" : 1, "members" : [ { "name" : "t", "kind" : "function", "line" : 18, + "char" : 28, "type" : "const T[0]()" } ] @@ -119,6 +134,7 @@ { "kind" : "template", "line" : 20, + "char" : 1, "name" : "P", "parameters" : [ { @@ -132,6 +148,7 @@ "name" : "Bar2", "kind" : "class", "line" : 22, + "char" : 1, "base" : "Bar", "interfaces" : [ "Baz" @@ -141,33 +158,40 @@ "name" : "this", "kind" : "constructor", "line" : 23, + "char" : 5, "deco" : "FZC4json4Bar2", "originalType" : "()", - "endline" : 23 + "endline" : 23, + "endchar" : 13 }, { "name" : "~this", "kind" : "destructor", "line" : 24, + "char" : 5, "deco" : "FZv", - "endline" : 24 + "endline" : 24, + "endchar" : 14 }, { "name" : "foo", "kind" : "function", "line" : 26, + "char" : 12, "storageClass" : [ "static" ], "deco" : "FZv", "originalType" : "()", - "endline" : 26 + "endline" : 26, + "endchar" : 19 }, { "name" : "baz", "kind" : "function", "protection" : "protected", "line" : 27, + "char" : 32, "storageClass" : [ "abstract" ], @@ -177,11 +201,13 @@ "name" : "t", "kind" : "function", "line" : 28, + "char" : 18, "storageClass" : [ "override" ], "deco" : "xFZi", "endline" : 28, + "endchar" : 40, "overrides" : [ "json.Baz!(int, 2, null).Baz.t" ] @@ -192,6 +218,7 @@ "name" : "Bar3", "kind" : "class", "line" : 31, + "char" : 1, "base" : "Bar2", "members" : [ { @@ -199,12 +226,14 @@ "kind" : "variable", "protection" : "private", "line" : 32, + "char" : 14, "deco" : "i", }, { "name" : "this", "kind" : "constructor", "line" : 33, + "char" : 5, "deco" : "FiZC4json4Bar3", "originalType" : "(int i)", "parameters" : [ @@ -213,18 +242,21 @@ "deco" : "i" } ], - "endline" : 33 + "endline" : 33, + "endchar" : 28 }, { "name" : "baz", "kind" : "function", "protection" : "protected", "line" : 35, + "char" : 32, "storageClass" : [ "override" ], "deco" : "FZS4json10__T3FooTiZ3Foo", "endline" : 35, + "endchar" : 61, "overrides" : [ "json.Bar2.baz" ] @@ -235,11 +267,13 @@ "name" : "Foo2", "kind" : "struct", "line" : 38, + "char" : 1, "members" : [ { "name" : "bar2", "kind" : "variable", "line" : 39, + "char" : 7, "deco" : "C4json4Bar2", "originalType" : "Bar2", }, @@ -247,23 +281,27 @@ "name" : "U", "kind" : "union", "line" : 40, + "char" : 2, "members" : [ { "name" : "s", "kind" : "variable", "line" : 42, + "char" : 10, "deco" : "s", }, { "name" : "i", "kind" : "variable", "line" : 43, + "char" : 8, "deco" : "i", }, { "name" : "o", "kind" : "variable", "line" : 45, + "char" : 10, "deco" : "C6Object", "originalType" : "Object", } @@ -275,11 +313,9 @@ "name" : "bar", "kind" : "function", "line" : 52, - "storageClass" : [ - "@trusted" - ], + "char" : 16, "deco" : "FNeKkC4json4Bar2Zi", - "originalType" : "myInt(ref uint blah, Bar2 foo = new Bar3(7))", + "originalType" : "@trusted myInt(ref uint blah, Bar2 foo = new Bar3(7))", "parameters" : [ { "name" : "blah", @@ -294,23 +330,23 @@ "default" : "cast(Bar2)new Bar3(7)" } ], - "endline" : 55 + "endline" : 55, + "endchar" : 1 }, { "name" : "outer", "kind" : "function", "line" : 57, - "storageClass" : [ - "@property" - ], + "char" : 15, "deco" : "FNbNdZi", - "originalType" : "nothrow int()", - "endline" : 74 + "endline" : 74, + "endchar" : 1 }, { "name" : "imports.jsonimport1", "kind" : "import", "line" : 77, + "char" : 8, "protection" : "private", "selective" : [ "target1", @@ -321,6 +357,7 @@ "name" : "imports.jsonimport2", "kind" : "import", "line" : 78, + "char" : 8, "protection" : "private", "renamed" : { "alias1" : "target1", @@ -331,6 +368,7 @@ "name" : "imports.jsonimport3", "kind" : "import", "line" : 79, + "char" : 8, "protection" : "private", "renamed" : { "alias3" : "target1", @@ -344,16 +382,19 @@ "name" : "imports.jsonimport4", "kind" : "import", "line" : 80, + "char" : 8, "protection" : "private" }, { "name" : "S", "kind" : "struct", "line" : 82, + "char" : 1, "members" : [ { "kind" : "template", "line" : 85, + "char" : 5, "name" : "this", "parameters" : [ { @@ -366,6 +407,7 @@ "name" : "this", "kind" : "constructor", "line" : 85, + "char" : 5, "type" : "(T t)", "parameters" : [ { @@ -373,7 +415,8 @@ "type" : "T" } ], - "endline" : 85 + "endline" : 85, + "endchar" : 20 } ] } @@ -383,6 +426,7 @@ "kind" : "template", "protection" : "private", "line" : 89, + "char" : 9, "name" : "S1_9755", "parameters" : [ { @@ -395,6 +439,7 @@ "name" : "S1_9755", "kind" : "struct", "line" : 89, + "char" : 9, "members" : [] } ] @@ -403,6 +448,7 @@ "kind" : "template", "protection" : "package", "line" : 90, + "char" : 9, "name" : "S2_9755", "parameters" : [ { @@ -415,6 +461,7 @@ "name" : "S2_9755", "kind" : "struct", "line" : 90, + "char" : 9, "members" : [] } ] @@ -423,11 +470,13 @@ "name" : "C_9755", "kind" : "class", "line" : 92, + "char" : 1, "members" : [ { "kind" : "template", "protection" : "protected", "line" : 94, + "char" : 22, "name" : "CI_9755", "parameters" : [ { @@ -440,6 +489,7 @@ "name" : "CI_9755", "kind" : "class", "line" : 94, + "char" : 22, "members" : [] } ] @@ -450,12 +500,71 @@ "name" : "c_10011", "kind" : "variable", "line" : 98, + "char" : 14, "storageClass" : [ "const" ], "deco" : "xC6Object", "originalType" : "Object", "init" : "Object()" + }, + { + "name" : "Numbers", + "kind" : "enum", + "line" : 101, + "char" : 1, + "baseDeco" : "i", + "members" : [ + { + "name" : "unspecified1", + "kind" : "enum member", + "value" : "0", + "line" : 103, + "char" : 5 + }, + { + "name" : "one", + "kind" : "enum member", + "value" : "2", + "line" : 104, + "char" : 5 + }, + { + "name" : "two", + "kind" : "enum member", + "value" : "3", + "line" : 105, + "char" : 5 + }, + { + "name" : "FILE_NOT_FOUND", + "kind" : "enum member", + "value" : "101", + "line" : 106, + "char" : 5 + }, + { + "name" : "unspecified3", + "kind" : "enum member", + "value" : "102", + "line" : 107, + "char" : 5 + }, + { + "name" : "unspecified4", + "kind" : "enum member", + "value" : "103", + "line" : 108, + "char" : 5 + }, + { + "name" : "four", + "kind" : "enum member", + "value" : "4", + "line" : 109, + "char" : 5 + } + ] } ] } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail260.d b/gcc/testsuite/gdc.test/compilable/fail260.d similarity index 54% rename from gcc/testsuite/gdc.test/fail_compilation/fail260.d rename to gcc/testsuite/gdc.test/compilable/fail260.d index c5affbad6..96f4d1bdf 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail260.d +++ b/gcc/testsuite/gdc.test/compilable/fail260.d @@ -1,13 +1,6 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail260.d(26): Error: template instance Static!(4u, 4u) Static!(4u, 4u) is nested in both Static and Static -fail_compilation/fail260.d(32): Error: template instance fail260.Static!(1, 4).Static.MultReturn!(Static!(1, 4), Static!(4, 1)) error instantiating -fail_compilation/fail260.d(45): instantiated from here: opMultVectors!(Static!(4, 1)) -fail_compilation/fail260.d(45): Error: template instance fail260.Static!(1, 4).Static.opMultVectors!(Static!(4, 1)) error instantiating ---- -*/ +// PERMUTE_ARGS: // REQUIRED_ARGS: -d + struct Static(uint width2, uint height2) { immutable width = width2; @@ -26,12 +19,10 @@ struct Static(uint width2, uint height2) alias Static!(M2.width, M1.height) MultReturn; } - void opMultVectors(M2)(M2 b) { alias MultReturn!(Static, M2) ret_matrix; } - } void test() diff --git a/gcc/testsuite/gdc.test/compilable/fwdref12201.d b/gcc/testsuite/gdc.test/compilable/fwdref12201.d index 8650997bf..d344a14f7 100644 --- a/gcc/testsuite/gdc.test/compilable/fwdref12201.d +++ b/gcc/testsuite/gdc.test/compilable/fwdref12201.d @@ -3,7 +3,7 @@ template T() { - imports.fwdref12201a.FILE* fp; + alias imports.fwdref12201a.FILE* FP; } struct S @@ -11,3 +11,39 @@ struct S mixin T; import imports.fwdref12201a; } + +union U +{ + mixin T; + import imports.fwdref12201a; +} + +class C +{ + mixin T; + import imports.fwdref12201a; +} + +interface I +{ + mixin T; + import imports.fwdref12201a; +} + + +template TI() +{ + mixin T; + import imports.fwdref12201a; +} +mixin template TM() +{ + mixin T; + import imports.fwdref12201a; +} +struct S2 +{ + alias ti = TI!(); + + mixin TM; +} diff --git a/gcc/testsuite/gdc.test/compilable/fwdref12543.d b/gcc/testsuite/gdc.test/compilable/fwdref12543.d new file mode 100644 index 000000000..b553f8972 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/fwdref12543.d @@ -0,0 +1,7 @@ +// PERMUTE_ARGS: + +class C12543; + +static assert(C12543.sizeof == (void*).sizeof); +static assert(C12543.alignof == (void*).sizeof); +static assert(C12543.mangleof == "C11fwdref125436C12543"); diff --git a/gcc/testsuite/gdc.test/compilable/header.d b/gcc/testsuite/gdc.test/compilable/header.d deleted file mode 100644 index 5ad497d58..000000000 --- a/gcc/testsuite/gdc.test/compilable/header.d +++ /dev/null @@ -1,413 +0,0 @@ -// PERMUTE_ARGS: -dw -// REQUIRED_ARGS: -H -Hd${RESULTS_DIR}/compilable -// POST_SCRIPT: compilable/extra-files/header-postscript.sh -// REQUIRED_ARGS: -d - -module foo.bar; - -import core.vararg; -import std.stdio; - -pragma(lib, "test"); -pragma(msg, "Hello World"); - -static assert(true, "message"); - -typedef double mydbl = 10; - -int main() -in -{ - assert(1+(2+3) == -(1 - 2*3)); -} -out (result) -{ - assert(result == 0); -} -body -{ - float f = float.infinity; - int i = cast(int) f; - writeln((i,1),2); - writeln(cast(int)float.max); - assert(i == cast(int)float.max); - assert(i == 0x80000000); - return 0; -} - -struct S { int m, n; } - -template Foo(T, int V) -{ - void foo(...) - { - static if (is(Object _ : X!TL, alias X, TL...)) {} // Bugzilla 10044 - - auto x = __traits(hasMember, Object, "noMember"); - auto y = is(Object : X!TL, alias X, TL...); - assert(!x && !y, "message"); - - S s = { 1,2 }; - auto a = [1, 2, 3]; - auto aa = [1:1, 2:2, 3:3]; - - int n,m; - } - - int bar(double d, int x) - { - if (d) - { d++; - } - else - d--; - - asm - { naked ; - mov EAX, 3; - } - - for (;;) - { - d = d + 1; - } - - for (int i = 0; i < 10; i++) - { - d = i ? d + 1 : 5; - } - - char[] s; - foreach (char c; s) - { - d *= 2; - if (d) - break; - else - continue; - } - - switch (V) - { - case 1: - case 2: break; - case 3: goto case 1; - case 4: goto default; - default: - d /= 8; - break; - } - - enum Label { A, B, C }; - void fswitch(Label l) - { - final switch (l) - { - case A: break; - case B: break; - case C: break; - } - } - - loop: - while (x) - { - x--; - if (x) - break loop; - else - continue loop; - } - - do - { - x++; - } while (x < 10); - - try - { - bar(1, 2); - } - catch (Object o) - { - x++; - } - finally - { - x--; - } - - Object o; - synchronized (o) - { - x = ~x; - } - - synchronized - { - x = x < 3; - } - - with (o) - { - toString(); - } - } -} - -static this() -{ -} - -static ~this() -{ -} - -interface iFoo{} -class xFoo: iFoo{} - -interface iFoo2{} -class xFoo2: iFoo, iFoo2{} - -class Foo3 -{ - this(int a, ...){} - this(int* a){} -} - -alias int myint; - -static notquit = 1; - -class Test -{ - void a() {} - void b() {} - void c() {} - void d() {} - void e() {} - void f() {} - void g() {} - void h() {} - void i() {} - void j() {} - void k() {} - void l() {} - void m() {} - void n() {} - void o() {} - void p() {} - void q() {} - void r() {} - void s() {} - void t() {} - void u() {} - void v() {} - void w() {} - void x() {} - void y() {} - void z() {} - - void aa() {} - void bb() {} - void cc() {} - void dd() {} - void ee() {} // Try adding or removing some functions here to see the effect. - - template A(T) { } - - alias A!(uint) getHUint; - alias A!(int) getHInt; - alias A!(float) getHFloat; - alias A!(ulong) getHUlong; - alias A!(long) getHLong; - alias A!(double) getHDouble; - alias A!(byte) getHByte; - alias A!(ubyte) getHUbyte; - alias A!(short) getHShort; - alias A!(ushort) getHUShort; - alias A!(real) getHReal; -} - -template templ( T ) -{ - void templ( T val ) - { - pragma( msg, "Invalid destination type." ); - } -} - -static char[] charArray = [ '\"', '\'' ]; - -class Point -{ - auto x = 10; - uint y = 20; -} - -template Foo2(bool bar) -{ - void test() - { - static if(bar) - { - int i; - } - else - { - } - static if(!bar) - { - } - else - { - } - } -} - - -template Foo4() -{ - void bar() - { - } -} - -template Foo4x( T... ) {} - -class Baz4 -{ - mixin Foo4 foo; - mixin Foo4x!(int, "str") foox; - - alias foo.bar baz; -} - -int test(T)(T t) -{ - if (auto o = cast(Object)t) return 1; - return 0; -} - -enum x6 = 1; - -bool foo6(int a, int b, int c, int d) -{ - return (a < b) != (c < d); -} - -auto foo7(int x) -{ - return 5; -} - -class D8{} -void func8() -{ - scope a= new D8(); -} - -T func9(T)() if (true) -{ - T i; - scope(exit) i= 1; - scope(success) i = 2; - scope(failure) i = 3; - return i; -} - -template V10(T) -{ - void func() - { - for(int i,j=4; i<3;i++) - { - } - } -} - -int foo11(int function() fn) -{ - return fn(); -} - -int bar11(T)() -{ - return foo11(function int (){ return 0; }); -} - - -struct S6360 -{ - @property long weeks1() const pure nothrow { return 0; } - - @property const pure nothrow long weeks2() { return 0; } -} - - -struct S12 -{ - /// postfix storage class and constructor - this(int n) nothrow{} - - /// prefix storage class (==StorageClassDeclaration) and constructor - nothrow this(string s){} -} - -/// dummy -struct T12 -{ - /// postfix storage class and template constructor - this()(int args) immutable { } - - /// prefix storage class (==StorageClassDeclaration) and template constructor - immutable this(A...)(A args){ } -} - - -// 6591 -import std.stdio : writeln, F = File; - -void foo6591()() -{ - import std.stdio : writeln, F = File; -} - - -// 8081 -version(unittest) { - pure nothrow unittest {} - pure nothrow unittest {} - - public unittest {} - extern(C) unittest {} - align unittest {} -} - - -// 10334 - -template Foo10334(T) if (Bar10334!()) {} /// -template Foo10334(T) if (Bar10334!100) {} /// -template Foo10334(T) if (Bar10334!3.14) {} /// -template Foo10334(T) if (Bar10334!"str") {} /// -template Foo10334(T) if (Bar10334!1.4i) {} /// -template Foo10334(T) if (Bar10334!null) {} /// -template Foo10334(T) if (Bar10334!true) {} /// -template Foo10334(T) if (Bar10334!false) {} /// -template Foo10334(T) if (Bar10334!'A') {} /// -template Foo10334(T) if (Bar10334!int) {} /// -template Foo10334(T) if (Bar10334!string) {} /// -template Foo10334(T) if (Bar10334!this) {} /// -template Foo10334(T) if (Bar10334!([1,2,3])) {} /// -template Foo10334(T) if (Bar10334!(Baz10334!())) {} /// -template Foo10334(T) if (Bar10334!(Baz10334!T)) {} /// -template Foo10334(T) if (Bar10334!(Baz10334!100)) {} /// -template Foo10334(T) if (Bar10334!(.foo)) {} /// -template Foo10334(T) if (Bar10334!(const int)) {} /// -template Foo10334(T) if (Bar10334!(shared T)) {} /// - -template Test10334(T...) {} /// -mixin Test10334!int a; /// -mixin Test10334!(int,long) b; /// -mixin Test10334!"str" c; /// diff --git a/gcc/testsuite/gdc.test/compilable/ice11777.d b/gcc/testsuite/gdc.test/compilable/ice11777.d new file mode 100644 index 000000000..704f35c9b --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ice11777.d @@ -0,0 +1,14 @@ +void f(void delegate(int)) {} + +class C +{ + int i; + this() + { + f((a){}); + /* (a){} is a template lambda, so FuncExp::semantic -> TemplateDeclaration::semantic + * will save the scope in TemplateDeclaration::scope with fieldinit. Later push/pop + * of the scope for template lambda body semantics will violate the assertion in Scope::pop(). + */ + } +} diff --git a/gcc/testsuite/gdc.test/compilable/ice11906.d b/gcc/testsuite/gdc.test/compilable/ice11906.d new file mode 100644 index 000000000..f568701b4 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ice11906.d @@ -0,0 +1,10 @@ +// REQUIRED_ARGS: -o- +// PERMUTE_ARGS: + +nothrow /*extern(Windows) */export int GetModuleHandleA(const char* lpModuleName); + +void main() +{ + /*extern(Windows) */int function(const char*) f; + assert(f != &GetModuleHandleA); +} diff --git a/gcc/testsuite/gdc.test/compilable/ice12002.d b/gcc/testsuite/gdc.test/compilable/ice12002.d new file mode 100644 index 000000000..c80c9abdf --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ice12002.d @@ -0,0 +1,24 @@ +// REQUIRED_ARGS: -inline +// PERMUTE_ARGS: + +void doFormat(void delegate(dchar) putc, TypeInfo[] arguments) +{ + void formatArg(char fc) + { + const(char)* prefix = ""; + + void putstr(const char[] s) + { + //if (flags & FL0pad) + { + while (*prefix) + putc(*prefix++); + } + + foreach (dchar c; s) + putc(c); + } + + putstr(null); + } +} diff --git a/gcc/testsuite/gdc.test/compilable/ice12956.d b/gcc/testsuite/gdc.test/compilable/ice12956.d new file mode 100644 index 000000000..69d182e4f --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ice12956.d @@ -0,0 +1,30 @@ +// REQUIRED_ARGS: -o- +// PERMUTE_ARGS: + +template isCallable(T...) +{ + static if (is(typeof(& T[0].opCall) == delegate)) + { + enum bool isCallable = true; + } + else static if (is(typeof(& T[0].opCall) V : V*) && is(V == function)) + { + enum bool isCallable = true; + } + else + enum bool isCallable = false; +} + +@property auto injectChain(Injectors...)() +{ + return &ChainTemplates!(Injectors); +} + +template ChainTemplates(Templates...) +{ + alias Head = Templates[0]; + alias Tail = Templates[1..$]; + alias Head!(Tail) ChainTemplates; +} + +static assert(!isCallable!(injectChain)); diff --git a/gcc/testsuite/gdc.test/compilable/ice13071.d b/gcc/testsuite/gdc.test/compilable/ice13071.d new file mode 100644 index 000000000..b180c1756 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ice13071.d @@ -0,0 +1,13 @@ +// REQUIRED_ARGS: -o- +// PERMUTE_ARGS: + +T foo(T)() +{ + __gshared int[] bar = []; + return T.init; +} + +void main() +{ + foo!char(); +} diff --git a/gcc/testsuite/gdc.test/compilable/ice13088.d b/gcc/testsuite/gdc.test/compilable/ice13088.d new file mode 100644 index 000000000..326796fed --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ice13088.d @@ -0,0 +1,19 @@ +// REQUIRED_ARGS: -o- +// PERMUTE_ARGS: + +struct X +{ + void mfoo(this T)() {} +} +void test() +{ + shared const X scx; + + scx.mfoo(); +} + +struct Vec +{ + int x; + void sc() shared const {} +} diff --git a/gcc/testsuite/gdc.test/compilable/ice13245.d b/gcc/testsuite/gdc.test/compilable/ice13245.d new file mode 100644 index 000000000..83ac0fa9e --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ice13245.d @@ -0,0 +1,5 @@ +// REQUIRED_ARGS: -o- +// PERMUTE_ARGS: + +template T(alias f) {} +static assert(!is(T!( (int x){ return invalid; } ))); diff --git a/gcc/testsuite/gdc.test/compilable/ice13323.d b/gcc/testsuite/gdc.test/compilable/ice13323.d new file mode 100644 index 000000000..d5d471f65 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ice13323.d @@ -0,0 +1,10 @@ +// REQUIRED_ARGS: -o- +// PERMUTE_ARGS: + +struct UDA {} + +struct S +{ + @UDA: + import object; +} diff --git a/gcc/testsuite/gdc.test/compilable/ice8392.d b/gcc/testsuite/gdc.test/compilable/ice8392.d new file mode 100644 index 000000000..5a374139e --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/ice8392.d @@ -0,0 +1,12 @@ +// EXTRA_SOURCES: imports/a8392.d + +module ice8392; + +struct A +{ +} + +auto fooa(alias handler)(A a) +{ + return handler(null); +} diff --git a/gcc/testsuite/gdc.test/compilable/imports/a12506.d b/gcc/testsuite/gdc.test/compilable/imports/a12506.d new file mode 100644 index 000000000..b4307c3c3 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/a12506.d @@ -0,0 +1,3 @@ +module imports.a12506; + +auto f12506(alias fun)() { return fun(1); } diff --git a/gcc/testsuite/gdc.test/compilable/imports/a12567.d b/gcc/testsuite/gdc.test/compilable/imports/a12567.d new file mode 100644 index 000000000..053e3d8d7 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/a12567.d @@ -0,0 +1,4 @@ +deprecated("This module will be removed in future release.") +module imports.a12567; + +void foo() {} diff --git a/gcc/testsuite/gdc.test/compilable/imports/a8392.d b/gcc/testsuite/gdc.test/compilable/imports/a8392.d new file mode 100644 index 000000000..4677b50bb --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/a8392.d @@ -0,0 +1,15 @@ +module imports.a8392; + +import ice8392; + +class B +{ + this(B); +} + +void foob(A a, B b) +{ + a.fooa!((arg){ + return new B(b); + }); +} diff --git a/gcc/testsuite/gdc.test/compilable/imports/imp12242a.d b/gcc/testsuite/gdc.test/compilable/imports/imp12242a.d new file mode 100644 index 000000000..5e5259dc7 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/imp12242a.d @@ -0,0 +1,17 @@ +module imports.imp12242a; + +public: +import imports.imp12242a1; // std.string +import imports.imp12242a2; // std.algorithm + + +private mixin template MixTmp(T, int x) +{ + template foo(U) if (is(U == T)) + { + enum foo = x; + } +} + +mixin MixTmp!(int, 1); +mixin MixTmp!(long, 2); diff --git a/gcc/testsuite/gdc.test/compilable/imports/imp12242a1.d b/gcc/testsuite/gdc.test/compilable/imports/imp12242a1.d new file mode 100644 index 000000000..5c5e52330 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/imp12242a1.d @@ -0,0 +1,8 @@ +module imports.imp12242a1; + +// std.string.strip +int stripA(C)(C[] str) @safe pure + if (is(immutable C == immutable char)) +{ + return 1; +} diff --git a/gcc/testsuite/gdc.test/compilable/imports/imp12242a2.d b/gcc/testsuite/gdc.test/compilable/imports/imp12242a2.d new file mode 100644 index 000000000..c0d789031 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/imp12242a2.d @@ -0,0 +1,11 @@ +module imports.imp12242a2; + +// std.algorithm.strip +auto stripA(R, E)(R range, E element) +{ + return 2; +} +auto stripA(alias pred, R)(R range) +{ + return 3; +} diff --git a/gcc/testsuite/gdc.test/compilable/imports/imp12242b.d b/gcc/testsuite/gdc.test/compilable/imports/imp12242b.d new file mode 100644 index 000000000..886104f54 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/imp12242b.d @@ -0,0 +1,17 @@ +module imports.imp12242b; + +public: +import imports.imp12242b1; // std.string +import imports.imp12242b2; // std.algorithm + + +private mixin template MixTmp(T, int x) +{ + template foo(U) if (is(U == T)) + { + enum foo = x; + } +} + +mixin MixTmp!(float, 3); +mixin MixTmp!(real, 4); diff --git a/gcc/testsuite/gdc.test/compilable/imports/imp12242b1.d b/gcc/testsuite/gdc.test/compilable/imports/imp12242b1.d new file mode 100644 index 000000000..e23fdc8da --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/imp12242b1.d @@ -0,0 +1,8 @@ +module imports.imp12242b1; + +// std.string.strip +int stripB(C)(C[] str) @safe pure + if (is(immutable C == immutable char)) +{ + return 1; +} diff --git a/gcc/testsuite/gdc.test/compilable/imports/imp12242b2.d b/gcc/testsuite/gdc.test/compilable/imports/imp12242b2.d new file mode 100644 index 000000000..0f5599887 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/imports/imp12242b2.d @@ -0,0 +1,11 @@ +module imports.imp12242b2; + +// std.algorithm.strip +auto stripB(R, E)(R range, E element) +{ + return 2; +} +auto stripB(alias pred, R)(R range) +{ + return 3; +} diff --git a/gcc/testsuite/gdc.test/compilable/imports/test55a.d b/gcc/testsuite/gdc.test/compilable/imports/test55a.d index 01986172b..e6fa224d0 100644 --- a/gcc/testsuite/gdc.test/compilable/imports/test55a.d +++ b/gcc/testsuite/gdc.test/compilable/imports/test55a.d @@ -3,12 +3,12 @@ module imports.test55a; import test55; class Arm { - typedef int ListHead; + alias int ListHead; MessageQueue.ListHead mqueue; } class Arm2 { - typedef int ListHead; + alias int ListHead; Queue2.ListHead mqueue; } diff --git a/gcc/testsuite/gdc.test/compilable/inlinexheader.d b/gcc/testsuite/gdc.test/compilable/inlinexheader.d deleted file mode 100644 index 7e51a7d1d..000000000 --- a/gcc/testsuite/gdc.test/compilable/inlinexheader.d +++ /dev/null @@ -1,91 +0,0 @@ -// PERMUTE_ARGS: -// REQUIRED_ARGS: -H -Hd${RESULTS_DIR}/compilable -inline -// POST_SCRIPT: compilable/extra-files/inlinexheader-postscript.sh - -// for D 2.0 only - -class C { } - -void foo(const C c, const(char)[] s, const int* q, const (int*) p) -{ -} - -void bar(in void *p) -{ -} - -void f(void function() f2); - -class C2; -void foo2(const C2 c); - -struct Foo3 -{ - int k; - ~this() { k = 1; } - this(this) { k = 2; } -} - - -class C3 { @property int get() { return 0; } } - -T foo3(T)() {} - -struct S4A(T) -{ - T x; -} - -struct S4B(T) if (1) -{ - T x; -} - -union U4A(T) -{ - T x; -} - -union U4B(T) if (2*4 == 8) -{ - T x; -} - -class C4A(T) -{ - T x; -} - -class C4B(T) if (true) { T x; } - -class C4C(T) : C4A!int if (!false) -{ - T x; -} - -class C4D(T) if (!false) : C4B!long, C4C!(int[]) -{ - T x; -} - -interface I4(T) if ((int[1]).length == 1) { T x; } - -// eponymous template case -template MyClass4(T) - if (is(typeof(T.subtype))) -{ - alias HelperSymbol = T.subtype; - class MyClass4 {} -} - -auto flit = 3 / 2.0; - -// 11217 -void foo11217()( const int[] arr) {} -void foo11217()(immutable int[] arr) {} -void foo11217()( ref int[] arr) {} -void foo11217()( lazy int[] arr) {} -void foo11217()( auto ref int[] arr) {} -void foo11217()( scope int[] arr) {} -void foo11217()( in int[] arr) {} -void foo11217()( inout int[] arr) {} diff --git a/gcc/testsuite/gdc.test/compilable/interpret3.d b/gcc/testsuite/gdc.test/compilable/interpret3.d index 8ffbf2d96..b002b8ed4 100644 --- a/gcc/testsuite/gdc.test/compilable/interpret3.d +++ b/gcc/testsuite/gdc.test/compilable/interpret3.d @@ -5,6 +5,8 @@ template compiles(int T) bool compiles = true; } +alias TypeTuple(T...) = T; + /************************************************** 3901 Arbitrary struct assignment, ref return **************************************************/ @@ -180,6 +182,19 @@ static assert(retRefTest2()==2); static assert(retRefTest3()==26); static assert(retRefTest4()==218); +/************************************************** + Bug 7887 assign to returned reference +**************************************************/ + +bool test7887() +{ + ref int f(ref int x) { return x; } + int a; + f(a) = 42; + return (a == 42); +} +static assert(test7887()); + /************************************************** Bug 7473 struct non-ref **************************************************/ @@ -1280,9 +1295,14 @@ struct Zadok z[0] = 56; auto zs = z[]; fog(zs) = [56, 6, 8]; - assert(z[1] == 6); + assert(z[0] == 56); - return z[2]; + assert(z[1] == 61); + assert(z[2] == 61); + + assert(zs[0] == 56); + assert(zs[1] == 6); + return zs[2]; } } @@ -2564,6 +2584,28 @@ bool bug10858() } static assert(bug10858()); +/************************************************** + 12528 - painting inout type for value type literals +**************************************************/ + +inout(T)[] dup12528(T)(inout(T)[] a) +{ + inout(T)[] res; + foreach (ref e; a) + res ~= e; + return res; +} + +enum arr12528V1 = dup12528([0]); +enum arr12528V2 = dup12528([0, 1]); +static assert(arr12528V1 == [0]); +static assert(arr12528V2 == [0, 1]); + +enum arr12528C1 = dup12528([new immutable Object]); +enum arr12528C2 = dup12528([new immutable Object, new immutable Object]); +static assert(arr12528C1.length == 1); +static assert(arr12528C2.length == 2 && arr12528C2[0] !is arr12528C2[1]); + /************************************************** 9745 Allow pointers to static variables **************************************************/ @@ -2677,6 +2719,20 @@ static assert(!bug4065("xx")); static assert(bug4065("aa")); static assert(bug4065("bb")); +/************************************************** + 12689 - assigning via pointer from 'in' expression +**************************************************/ + +int g12689() +{ + int[int] aa; + aa[1] = 13; + assert(*(1 in aa) == 13); + *(1 in aa) = 42; + return aa[1]; +} +static assert(g12689() == 42); + /************************************************** Pointers in ? : **************************************************/ @@ -3725,6 +3781,19 @@ int isItSafeToDance() static assert(isItSafeToDance()); +/************************************************** + 12296 CTFE rejects const compatible AA pointer cast +**************************************************/ + +int test12296() +{ + immutable x = [5 : 4]; + auto aa = &x; + const(int[int])* y = aa; + return 1; +} +static assert(test12296()); + /************************************************** 9170 Allow reinterpret casts float<->int **************************************************/ @@ -4172,6 +4241,47 @@ bool f12016(immutable B12016 b) static assert(f12016(new immutable C12016)); +/************************************************** + 10610 ice immutable implicit conversion +**************************************************/ + +class Bug10610(T) +{ + int baz() immutable { + return 1; + } + static immutable(Bug10610!T) min = new Bug10610!T(); +} + +void ice10610() +{ + alias T10610 = Bug10610!(int); + static assert (T10610.min.baz()); +} + +/************************************************** + 13141 regression fix caused by 10610 +**************************************************/ + +struct MapResult13141(alias pred) +{ + int[] range; + @property empty() { return range.length == 0; } + @property front() { return pred(range[0]); } + void popFront() { range = range[1..$]; } +} + +string[] array13141(R)(R r) +{ + typeof(return) result; + foreach (e; r) + result ~= e; + return result; +} + +//immutable string[] splitterNames = [4].map!(e => "4").array(); +immutable string[] splitterNames13141 = MapResult13141!(e => "4")([4]).array13141(); + /************************************************** 11587 AA compare **************************************************/ @@ -5900,6 +6010,117 @@ string f10782() } mixin(f10782()); +/************************************************** + 10929 NRVO support in CTFE +**************************************************/ + +struct S10929 +{ + this(this) + { + postblitCount++; + } + ~this() + { + dtorCount++; + } + int payload; + int dtorCount; + int postblitCount; +} + +auto makeS10929() +{ + auto s = S10929(42, 0, 0); + return s; +} + +bool test10929() +{ + auto s = makeS10929(); + assert(s.postblitCount == 0); + assert(s.dtorCount == 0); + return true; +}; +static assert(test10929()); + +/************************************************** + 9245 - support postblit call on array assignments +**************************************************/ + +bool test9245() +{ + int postblits = 0; + struct S + { + this(this) + { + ++postblits; + } + } + + S s; + S[2] a; + assert(postblits == 0); + + { + S[2] arr = s; + assert(postblits == 2); + arr[] = s; + assert(postblits == 4); + postblits = 0; + + S[2] arr2 = arr; + assert(postblits == 2); + arr2 = arr; + assert(postblits == 4); + postblits = 0; + + const S[2] constArr = s; + assert(postblits == 2); + postblits = 0; + + const S[2] constArr2 = arr; + assert(postblits == 2); + postblits = 0; + } + { + S[2][2] arr = s; + assert(postblits == 4); + arr[] = a; + assert(postblits == 8); + postblits = 0; + + S[2][2] arr2 = arr; + assert(postblits == 4); + arr2 = arr; + assert(postblits == 8); + postblits = 0; + + const S[2][2] constArr = s; + assert(postblits == 4); + postblits = 0; + + const S[2][2] constArr2 = arr; + assert(postblits == 4); + postblits = 0; + } + + return true; +} +static assert(test9245()); + +/************************************************** + 12906 don't call postblit on blit initializing +**************************************************/ + +struct S12906 { this(this) { assert(0); } } + +static assert({ + S12906[1] arr; + return true; +}()); + /************************************************** 11510 support overlapped field access in CTFE **************************************************/ @@ -5984,6 +6205,32 @@ bool test11941b() } static assert(test11941b()); +/************************************************** + 11535 - element-wise assignment from string to ubyte array literal +**************************************************/ + +struct Hash11535 +{ + ubyte[6] _buffer; + + void put(scope const(ubyte)[] data...) + { + uint i = 0, index = 0; + auto inputLen = data.length; + + (&_buffer[index])[0 .. inputLen-i] = (&data[i])[0 .. inputLen-i]; + } +} + +auto md5_digest11535(T...)(scope const T data) +{ + Hash11535 hash; + hash.put(cast(const(ubyte[]))data[0]); + return hash._buffer; +} + +static assert(md5_digest11535(`TEST`) == [84, 69, 83, 84, 0, 0]); + /************************************************** 11540 - goto label + try-catch-finally / with statement **************************************************/ @@ -6231,3 +6478,202 @@ bool test11664() return true; } static assert(test11664()); + +/************************************************** + 12110 - operand of dereferencing does not need to be an lvalue +**************************************************/ + +struct SliceOverIndexed12110 +{ + Uint24Array12110* arr; + + @property front(uint val) + { + arr.dupThisReference(); + } +} + +struct Uint24Array12110 +{ + ubyte[] data; + + this(ubyte[] range) + { + data = range; + SliceOverIndexed12110(&this).front = 0; + assert(data.length == range.length * 2); + } + + void dupThisReference() + { + auto new_data = new ubyte[data.length * 2]; + data = new_data; + } +} + +static m12110 = Uint24Array12110([0x80]); + +/************************************************** + 12310 - heap allocation for built-in sclar types +**************************************************/ + +bool test12310() +{ + auto p1 = new int, p2 = p1; + assert(*p1 == 0); + assert(*p2 == 0); + *p1 = 10; + assert(*p1 == 10); + assert(*p2 == 10); + + auto q1 = new int(3), q2 = q1; + assert(*q1 == 3); + assert(*q2 == 3); + *q1 = 20; + assert(*q1 == 20); + assert(*q2 == 20); + + return true; +} +static assert(test12310()); + +/************************************************** + 12499 - initialize TupleDeclaraion in CTFE +**************************************************/ + +auto f12499() +{ + //Initialize 3 ints to 5. + TypeTuple!(int, int, int) a = 5; + return a[0]; //Error: variable _a_field_0 cannot be read at compile time +} +static assert(f12499() == 5); + +/************************************************** + 12602 - slice in struct literal members +**************************************************/ + +struct Result12602 +{ + uint[] source; +} + +auto wrap12602a(uint[] r) +{ + return Result12602(r); +} + +auto wrap12602b(uint[] r) +{ + Result12602 x; + x.source = r; + return x; +} + +auto testWrap12602a() +{ + uint[] dest = [1, 2, 3, 4]; + + auto ra = wrap12602a(dest[0..2]); + auto rb = wrap12602a(dest[2..4]); + + foreach (i; 0..2) + rb.source[i] = ra.source[i]; + + assert(ra.source == [1,2]); + assert(rb.source == [1,2]); + assert(&ra.source[0] == &dest[0]); + assert(&rb.source[0] == &dest[2]); + assert(dest == [1,2,1,2]); + return dest; +} + +auto testWrap12602b() +{ + uint[] dest = [1, 2, 3, 4]; + + auto ra = wrap12602b(dest[0..2]); + auto rb = wrap12602b(dest[2..4]); + + foreach (i; 0..2) + rb.source[i] = ra.source[i]; + + assert(ra.source == [1,2]); + assert(rb.source == [1,2]); + assert(&ra.source[0] == &dest[0]); + assert(&rb.source[0] == &dest[2]); + assert(dest == [1,2,1,2]); + return dest; +} + +auto testWrap12602c() +{ + uint[] dest = [1, 2, 3, 4]; + + auto ra = Result12602(dest[0..2]); + auto rb = Result12602(dest[2..4]); + + foreach (i; 0..2) + rb.source[i] = ra.source[i]; + + assert(ra.source == [1,2]); + assert(rb.source == [1,2]); + assert(&ra.source[0] == &dest[0]); + assert(&rb.source[0] == &dest[2]); + assert(dest == [1,2,1,2]); + return dest; +} + +auto testWrap12602d() +{ + uint[] dest = [1, 2, 3, 4]; + + Result12602 ra; ra.source = dest[0..2]; + Result12602 rb; rb.source = dest[2..4]; + + foreach (i; 0..2) + rb.source[i] = ra.source[i]; + + assert(ra.source == [1,2]); + assert(rb.source == [1,2]); + assert(&ra.source[0] == &dest[0]); + assert(&rb.source[0] == &dest[2]); + assert(dest == [1,2,1,2]); + return dest; +} + +static assert(testWrap12602a() == [1,2,1,2]); +static assert(testWrap12602b() == [1,2,1,2]); +static assert(testWrap12602c() == [1,2,1,2]); +static assert(testWrap12602d() == [1,2,1,2]); + +/************************************************** + 12677 - class type initializing from DotVarExp +**************************************************/ + +final class C12677 +{ + TypeTuple!(Object, int[]) _test; + this() + { + auto t0 = _test[0]; // + auto t1 = _test[1]; // + assert(t0 is null); + assert(t1 is null); + } +} + +struct S12677 +{ + auto f = new C12677(); +} + +/************************************************** + 12851 - interpret function local const static array +**************************************************/ + +void test12851() +{ + const int[5] arr; + alias staticZip = TypeTuple!(arr[0]); +} diff --git a/gcc/testsuite/gdc.test/compilable/json.d b/gcc/testsuite/gdc.test/compilable/json.d index daeff3d4a..4f31d3411 100644 --- a/gcc/testsuite/gdc.test/compilable/json.d +++ b/gcc/testsuite/gdc.test/compilable/json.d @@ -97,3 +97,15 @@ class C_9755 /** Issue 10011 - init property is wrong for object initializer. */ const Object c_10011 = new Object(); + +/// +enum Numbers +{ + unspecified1, + one = 2, + two = 3, + FILE_NOT_FOUND = 101, + unspecified3, + unspecified4, + four = 4, +} diff --git a/gcc/testsuite/gdc.test/compilable/nogc.d b/gcc/testsuite/gdc.test/compilable/nogc.d new file mode 100644 index 000000000..fe6e91b84 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/nogc.d @@ -0,0 +1,96 @@ +// REQUIRED_ARGS: -o- + +/***************** Covariance ******************/ + +class C1 +{ + void foo() @nogc; + void bar(); +} + +class D1 : C1 +{ + override void foo(); // no error + override void bar() @nogc; // no error +} + +/******************************************/ +// __traits(compiles) + +static assert(__traits(compiles, new Object())); + +void foo_compiles() {} + +@nogc void test_compiles() +{ + auto fp = &foo_compiles; + static assert(!__traits(compiles, foo_compiles())); + static assert(!__traits(compiles, fp())); + static assert(!__traits(compiles, (*fp)())); + + static assert(!__traits(compiles, [1,2,3])); + static assert(!__traits(compiles, [1:1, 2:2])); + + struct Struct {} + static assert(!__traits(compiles, new int)); + static assert(!__traits(compiles, new Struct())); + static assert(!__traits(compiles, new Object())); + + int* p; + static assert(!__traits(compiles, delete p)); + + int[int] aa; + static assert(!__traits(compiles, aa[0])); + + int[] a; + static assert(!__traits(compiles, a.length = 1)); + static assert(!__traits(compiles, a.length += 1)); + static assert(!__traits(compiles, a.length -= 1)); + + static assert(!__traits(compiles, a ~= 1)); + static assert(!__traits(compiles, a ~ a)); +} + +/******************************************/ +// 12630 + +void test12630() @nogc +{ + // All of these declarations should cause no errors. + + static const ex1 = new Exception("invalid"); + //enum ex2 = new Exception("invalid"); + + static const arr1 = [[1,2], [3, 4]]; + enum arr2 = [[1,2], [3, 4]]; + + //static const aa1 = [1:1, 2:2]; + enum aa2 = [1:1, 2:2]; + + //static const v1 = aa1[1]; + enum v2 = aa2[1]; + + Object o; + static const del1 = (delete o).sizeof; + enum del2 = (delete o).sizeof; + + int[] a; + static const len1 = (a.length = 1).sizeof; + enum len2 = (a.length = 1).sizeof; + + static const cata1 = (a ~= 1).sizeof; + enum cata2 = (a ~= 1).sizeof; + + static const cat1 = (a ~ a).sizeof; + enum cat2 = (a ~ a).sizeof; +} + +/******************************************/ +// 12642 + +import core.simd; + +ulong2 test12642() @nogc +{ + return [0, 0]; +} diff --git a/gcc/testsuite/gdc.test/compilable/parse10199.d b/gcc/testsuite/gdc.test/compilable/parse10199.d index 49593106c..8b82527a3 100644 --- a/gcc/testsuite/gdc.test/compilable/parse10199.d +++ b/gcc/testsuite/gdc.test/compilable/parse10199.d @@ -4,3 +4,20 @@ void main() goto label; label: } + +/***************************************************/ +// 12460 + +void f12460(T)() +{ + static if (is(T == int)) + { + goto end; + } +end: +} + +void test12460() +{ + f12460!int(); +} diff --git a/gcc/testsuite/gdc.test/compilable/parse11957.d b/gcc/testsuite/gdc.test/compilable/parse11957.d new file mode 100644 index 000000000..37495fdca --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/parse11957.d @@ -0,0 +1,13 @@ + +extern(C++) class C +{ + void x() {} +} + +void main() +{ + extern(C++) class D : C + { + override void x() {} + } +} diff --git a/gcc/testsuite/gdc.test/compilable/parse13049.d b/gcc/testsuite/gdc.test/compilable/parse13049.d new file mode 100644 index 000000000..0b86fd1c3 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/parse13049.d @@ -0,0 +1,3 @@ +enum mangle(T) = T.mangleof; +alias Func = void function(scope int); // OK +static assert(mangle!Func == mangle!(void function(scope int))); // OK <- NG diff --git a/gcc/testsuite/gdc.test/compilable/test10150.d b/gcc/testsuite/gdc.test/compilable/test10150.d deleted file mode 100644 index ba6cc5146..000000000 --- a/gcc/testsuite/gdc.test/compilable/test10150.d +++ /dev/null @@ -1,36 +0,0 @@ -// REQUIRED_ARGS: -o- -// PERMUTE_ARGS: - -void foo() {} -alias F = typeof(foo); - -class C -{ - const static void fc() {} - immutable static void fi() {} - inout static void fw() {} - shared static void fs() {} - shared const static void fsc() {} - shared inout static void fsw() {} - - static assert(is(typeof(fc) == F)); - static assert(is(typeof(fi) == F)); - static assert(is(typeof(fw) == F)); - static assert(is(typeof(fs) == F)); - static assert(is(typeof(fsc) == F)); - static assert(is(typeof(fsw) == F)); -} - -const { void fc() {} } -immutable { void fi() {} } -inout { void fw() {} } -shared { void fs() {} } -shared const { void fsc() {} } -shared inout { void fsw() {} } - -static assert(is(typeof(fc) == F)); -static assert(is(typeof(fi) == F)); -static assert(is(typeof(fw) == F)); -static assert(is(typeof(fs) == F)); -static assert(is(typeof(fsc) == F)); -static assert(is(typeof(fsw) == F)); diff --git a/gcc/testsuite/gdc.test/compilable/test10520.d b/gcc/testsuite/gdc.test/compilable/test10520.d new file mode 100644 index 000000000..9a24d8405 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test10520.d @@ -0,0 +1,11 @@ +// REQUIRED_ARGS: -debug -profile + +// Issue 10520 [profile+nothrow] Building with profiler results in "is not nothrow" error on some contracts + +void f() { } + +void g()() +in { f(); } // OK <- Error: 'main.f' is not nothrow +body { } + +alias gi = g!(); diff --git a/gcc/testsuite/gdc.test/compilable/test11471.d b/gcc/testsuite/gdc.test/compilable/test11471.d new file mode 100644 index 000000000..1df7e80f2 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test11471.d @@ -0,0 +1,10 @@ +// REQUIRED_ARGS: -profile + +void main() nothrow +{ + // Error: asm statements are assumed to throw + version(GNU) + asm { ""; } + else + asm { nop; } +} diff --git a/gcc/testsuite/gdc.test/compilable/test12009.d b/gcc/testsuite/gdc.test/compilable/test12009.d new file mode 100644 index 000000000..be6049bf7 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test12009.d @@ -0,0 +1,36 @@ +struct RefCounted(T) +{ + struct RefCountedStore + { + private struct Impl + { + T _payload; + } + + private Impl* _store; + } + RefCountedStore _refCounted; + + ~this() + { + import core.stdc.stdlib : free; + } +} + +struct GroupBy(R) +{ + struct SharedInput + { + Group unused; + } + + struct Group + { + private RefCounted!SharedInput _allGroups; + } +} + +void main() +{ + GroupBy!(int[]) g1; +} diff --git a/gcc/testsuite/gdc.test/compilable/test12523.d b/gcc/testsuite/gdc.test/compilable/test12523.d new file mode 100644 index 000000000..8a8eae150 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test12523.d @@ -0,0 +1,15 @@ +void test12523(inout(int)) +{ + void check(T)() + { + T[] a; + foreach (ref e; a) + static assert(is(typeof(e) == T)); + } + + check!(int)(); + check!(inout(int))(); + check!(inout(const(int)))(); + check!(const(int))(); + check!(immutable(int))(); +} diff --git a/gcc/testsuite/gdc.test/compilable/test12567a.d b/gcc/testsuite/gdc.test/compilable/test12567a.d new file mode 100644 index 000000000..53cc377e6 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test12567a.d @@ -0,0 +1,11 @@ +// REQUIRED_ARGS: +// PERMUTE_ARGS: +/* +TEST_OUTPUT: +--- +--- +*/ +deprecated +module test12567a; + +void main() {} diff --git a/gcc/testsuite/gdc.test/compilable/test12567b.d b/gcc/testsuite/gdc.test/compilable/test12567b.d new file mode 100644 index 000000000..2e37d31c7 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test12567b.d @@ -0,0 +1,11 @@ +// REQUIRED_ARGS: +// PERMUTE_ARGS: +/* +TEST_OUTPUT: +--- +--- +*/ +deprecated("message") +module test12567b; + +void main() {} diff --git a/gcc/testsuite/gdc.test/compilable/test12567c.d b/gcc/testsuite/gdc.test/compilable/test12567c.d new file mode 100644 index 000000000..cadc375bf --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test12567c.d @@ -0,0 +1,11 @@ +// REQUIRED_ARGS: +// PERMUTE_ARGS: +/* +TEST_OUTPUT: +--- +compilable/test12567c.d(9): Deprecation: module imports.a12567 is deprecated - This module will be removed in future release. +--- +*/ +import imports.a12567; + +void main() { foo(); } diff --git a/gcc/testsuite/gdc.test/compilable/test12567d.d b/gcc/testsuite/gdc.test/compilable/test12567d.d new file mode 100644 index 000000000..512f0b76e --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test12567d.d @@ -0,0 +1,10 @@ +// REQUIRED_ARGS: -d +// PERMUTE_ARGS: +/* +TEST_OUTPUT: +--- +--- +*/ +import imports.a12567; + +void main() { foo(); } diff --git a/gcc/testsuite/gdc.test/compilable/test12593.d b/gcc/testsuite/gdc.test/compilable/test12593.d new file mode 100644 index 000000000..1b2dcacc0 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test12593.d @@ -0,0 +1,12 @@ +int[R] aa; // Place before the declaration of key struct + +struct R +{ + int opCmp(ref const R) const { return 0; } + + //bool opEquals(ref const R) const { return true; } + //size_t toHash() const nothrow @safe { return 0; } +} + +void main() +{} diff --git a/gcc/testsuite/gdc.test/compilable/test12967.d b/gcc/testsuite/gdc.test/compilable/test12967.d new file mode 100644 index 000000000..dd2383868 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test12967.d @@ -0,0 +1,64 @@ +// REQUIRED_ARGS: -o- +// PERMUTE_ARGS: + +void foo() {} +alias F = typeof(foo); + +const { void block_c() {} } +immutable { void block_i() {} } +inout { void block_w() {} } +shared { void block_s() {} } +shared const { void block_sc() {} } +shared inout { void block_sw() {} } + +static assert(is(typeof(block_c) == F)); +static assert(is(typeof(block_i) == F)); +static assert(is(typeof(block_w) == F)); +static assert(is(typeof(block_s) == F)); +static assert(is(typeof(block_sc) == F)); +static assert(is(typeof(block_sw) == F)); + +version (all) { const: void label_c() {} } +version (all) { immutable: void label_i() {} } +version (all) { inout: void label_w() {} } +version (all) { shared: void label_s() {} } +version (all) { shared const: void label_sc() {} } +version (all) { shared inout: void label_sw() {} } + +static assert(is(typeof(label_c) == F)); +static assert(is(typeof(label_i) == F)); +static assert(is(typeof(label_w) == F)); +static assert(is(typeof(label_s) == F)); +static assert(is(typeof(label_sc) == F)); +static assert(is(typeof(label_sw) == F)); + +class C +{ + const { static void block_c() {} } + immutable { static void block_i() {} } + inout { static void block_w() {} } + shared { static void block_s() {} } + shared const { static void block_sc() {} } + shared inout { static void block_sw() {} } + + static assert(is(typeof(block_c) == F)); + static assert(is(typeof(block_i) == F)); + static assert(is(typeof(block_w) == F)); + static assert(is(typeof(block_s) == F)); + static assert(is(typeof(block_sc) == F)); + static assert(is(typeof(block_sw) == F)); + + version (all) { const: static void label_c() {} } + version (all) { immutable: static void label_i() {} } + version (all) { inout: static void label_w() {} } + version (all) { shared: static void label_s() {} } + version (all) { shared const: static void label_sc() {} } + version (all) { shared inout: static void label_sw() {} } + + static assert(is(typeof(label_c) == F)); + static assert(is(typeof(label_i) == F)); + static assert(is(typeof(label_w) == F)); + static assert(is(typeof(label_s) == F)); + static assert(is(typeof(label_sc) == F)); + static assert(is(typeof(label_sw) == F)); +} diff --git a/gcc/testsuite/gdc.test/compilable/test12979a.d b/gcc/testsuite/gdc.test/compilable/test12979a.d new file mode 100644 index 000000000..14ca6efc0 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test12979a.d @@ -0,0 +1,5 @@ +void parse() +{ + asm pure nothrow @nogc @trusted {} + asm @safe {} +} diff --git a/gcc/testsuite/gdc.test/compilable/test12979b.d b/gcc/testsuite/gdc.test/compilable/test12979b.d new file mode 100644 index 000000000..41c76a251 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test12979b.d @@ -0,0 +1,64 @@ +// REQUIRED_ARGS: -w -de + +void foo() pure nothrow @nogc @safe +{ + version(GNU) + { + asm pure nothrow @nogc @trusted + { + ""; + } + } + else + { + asm pure nothrow @nogc @trusted + { + ret; + } + } +} + +void bar()() +{ + version(GNU) + { + asm pure nothrow @nogc @trusted + { + ""; + } + } + else + { + asm pure nothrow @nogc @trusted + { + ret; + } + } +} + +static assert(__traits(compiles, () pure nothrow @nogc @safe => bar())); + +void baz()() +{ + version(GNU) + { + asm + { + ""; + } + } + else + { + asm + { + ret; + } + } +} + +// wait for deprecation of asm pure inference +// static assert(!__traits(compiles, () pure => baz())); +static assert(!__traits(compiles, () nothrow => baz())); +// wait for deprecation of asm @nogc inference +// static assert(!__traits(compiles, () @nogc => baz())); +static assert(!__traits(compiles, () @safe => baz())); diff --git a/gcc/testsuite/gdc.test/compilable/test13008.d b/gcc/testsuite/gdc.test/compilable/test13008.d new file mode 100644 index 000000000..fd1f41db9 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test13008.d @@ -0,0 +1,10 @@ +// REQUIRED_ARGS: -o- +// PERMUTE_ARGS: -d -de -dw +/* +TEST_OUTPUT* +--- +--- +*/ +deprecated class Dep { } +deprecated Dep depFunc1(); // error +deprecated void depFunc2(Dep); // error diff --git a/gcc/testsuite/gdc.test/compilable/test13053.d b/gcc/testsuite/gdc.test/compilable/test13053.d new file mode 100644 index 000000000..3f8b4219c --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test13053.d @@ -0,0 +1,13 @@ +// PERMUTE_ARGS: -w -wi +/* +TEST_OUTPUT: +--- +--- +*/ + +@system: + +struct S +{ + int[] a; +} diff --git a/gcc/testsuite/gdc.test/compilable/test13193.d b/gcc/testsuite/gdc.test/compilable/test13193.d new file mode 100644 index 000000000..58cb617c4 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test13193.d @@ -0,0 +1,126 @@ +// REQUIRED_ARGS: -O -inline -c + + +final class SharedLib { + void getSymbol() {return getSymbolImpl();} + void getSymbolImpl() {return getSymbol_();} + /* add more intermediate functions to go slower */ + void getSymbol_() {} +} + + +void test13193() +{ +SharedLib ssllib; +void bindFunc() {ssllib.getSymbol();} + bindFunc(); /* add more of these to go slower */ + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); /* 10 */ + + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); /* 20 */ + + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); /* 30 */ + + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); /* 40 */ + + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); /* 50 */ + + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); /* 60 */ + + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); /* 70 */ + + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); /* 80 */ + + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); /* 90 */ + + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); + bindFunc(); /* 100 */ +} + diff --git a/gcc/testsuite/gdc.test/compilable/test13194.d b/gcc/testsuite/gdc.test/compilable/test13194.d new file mode 100644 index 000000000..cefa7ba0f --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test13194.d @@ -0,0 +1,17 @@ +module test13194; + +class C13194 +{ + static Object o = void; +} + +struct S13194 +{ + static Object o = void; +} + +union U13194 +{ + static Object o = void; +} + diff --git a/gcc/testsuite/gdc.test/compilable/test55.d b/gcc/testsuite/gdc.test/compilable/test55.d index 8354229ea..0dd7b7b41 100644 --- a/gcc/testsuite/gdc.test/compilable/test55.d +++ b/gcc/testsuite/gdc.test/compilable/test55.d @@ -6,7 +6,7 @@ public import imports.test55a; class Queue { - typedef int ListHead; + alias int ListHead; Arm a; } @@ -14,7 +14,7 @@ class MessageQueue : Queue { } class Queue2 { - typedef int ListHead; + alias int ListHead; Arm2 a; } diff --git a/gcc/testsuite/gdc.test/compilable/test602.d b/gcc/testsuite/gdc.test/compilable/test602.d index 90c0978fc..c6ddd4881 100644 --- a/gcc/testsuite/gdc.test/compilable/test602.d +++ b/gcc/testsuite/gdc.test/compilable/test602.d @@ -182,6 +182,7 @@ static assert(!__traits(compiles, (bool b) if (b) goto label; })); +/* // Goto into foreach loop static assert(!__traits(compiles, (bool b) { @@ -192,7 +193,9 @@ static assert(!__traits(compiles, (bool b) assert(i); } })); +*/ +/* // Goto into foreach loop backwards static assert(!__traits(compiles, (bool b) { @@ -203,6 +206,7 @@ static assert(!__traits(compiles, (bool b) } if (b) goto label; })); +*/ // Goto into if block with variable static assert(!__traits(compiles, (bool b) @@ -343,7 +347,7 @@ static assert(!__traits(compiles, (bool b) })); // Goto into scope(failure) -static assert(__traits(compiles, (bool b) +static assert(!__traits(compiles, (bool b) { if (b) goto label; @@ -383,6 +387,7 @@ static assert(!__traits(compiles, (bool b) })); /***************************************************/ +// 11659 int test11659() { @@ -391,3 +396,21 @@ int test11659() LABEL: return mixin(expr); } + +/***************************************************/ +// 13321 + +void test13321(bool b) +{ + static struct Foo + { + this(int) {} + } + + Foo x; + if (b) + goto EXIT; + x = Foo(1); + EXIT: +} + diff --git a/gcc/testsuite/gdc.test/compilable/test8296.d b/gcc/testsuite/gdc.test/compilable/test8296.d new file mode 100644 index 000000000..d27ba15f3 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test8296.d @@ -0,0 +1,33 @@ +struct bar2 +{ + int i; + @disable this(); + this(int i) + { + this.i = i; + } +} + +class InnerBar { + bar2 b; + + this() + { + b = bar2(0); + } +} + +struct bar1 +{ + InnerBar b; +} + +class Foo +{ + bar1 m_bar1; +} + +void main(string[] args) +{ + auto foo = new Foo(); +} \ No newline at end of file diff --git a/gcc/testsuite/gdc.test/compilable/test9570.d b/gcc/testsuite/gdc.test/compilable/test9570.d new file mode 100644 index 000000000..c9df59cf3 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/test9570.d @@ -0,0 +1,59 @@ +// REQUIRED_ARGS: -o- +// PERMUTE_ARGS: + +void main() +{ + ubyte[256] data; + + foreach (immutable i; 0..256) data[i] = i; + foreach ( const i; 0..256) data[i] = i; + foreach ( i; 0..256) static assert(!__traits(compiles, (data[i] = i))); + foreach (immutable int i; 0..256) data[i] = i; + foreach ( const int i; 0..256) data[i] = i; + foreach ( int i; 0..256) static assert(!__traits(compiles, (data[i] = i))); + foreach (immutable(int) i; 0..256) data[i] = i; + foreach ( const(int) i; 0..256) data[i] = i; + foreach ( int i; 0..256) static assert(!__traits(compiles, (data[i] = i))); + foreach (immutable(ulong) i; 0..256) data[i] = i; + foreach ( const(ulong) i; 0..256) data[i] = i; + foreach ( ulong i; 0..256) static assert(!__traits(compiles, (data[i] = i))); + + foreach (immutable i, x; data) data[i] = i; + foreach ( const i, x; data) data[i] = i; + foreach ( i, x; data) static assert(!__traits(compiles, (data[i] = i))); + foreach (immutable int i, x; data) data[i] = i; + foreach ( const int i, x; data) data[i] = i; + foreach ( int i, x; data) static assert(!__traits(compiles, (data[i] = i))); + foreach (immutable(int) i, x; data) data[i] = i; + foreach ( const(int) i, x; data) data[i] = i; + foreach ( int i, x; data) static assert(!__traits(compiles, (data[i] = i))); + foreach (immutable(ulong) i, x; data) data[i] = i; + foreach ( const(ulong) i, x; data) data[i] = i; + foreach ( ulong i, x; data) static assert(!__traits(compiles, (data[i] = i))); + + foreach_reverse (immutable i; 0..256) data[i] = i; + foreach_reverse ( const i; 0..256) data[i] = i; + foreach_reverse ( i; 0..256) static assert(!__traits(compiles, (data[i] = i))); + foreach_reverse (immutable int i; 0..256) data[i] = i; + foreach_reverse ( const int i; 0..256) data[i] = i; + foreach_reverse ( int i; 0..256) static assert(!__traits(compiles, (data[i] = i))); + foreach_reverse (immutable(int) i; 0..256) data[i] = i; + foreach_reverse ( const(int) i; 0..256) data[i] = i; + foreach_reverse ( int i; 0..256) static assert(!__traits(compiles, (data[i] = i))); + foreach_reverse (immutable(ulong) i; 0..256) data[i] = i; + foreach_reverse ( const(ulong) i; 0..256) data[i] = i; + foreach_reverse ( ulong i; 0..256) static assert(!__traits(compiles, (data[i] = i))); + + foreach_reverse (immutable i, x; data) data[i] = i; + foreach_reverse ( const i, x; data) data[i] = i; + foreach_reverse ( i, x; data) static assert(!__traits(compiles, (data[i] = i))); + //foreach_reverse (immutable int i, x; data) data[i] = i; + //foreach_reverse ( const int i, x; data) data[i] = i; + //foreach_reverse ( int i, x; data) static assert(!__traits(compiles, (data[i] = i))); + //foreach_reverse (immutable(int) i, x; data) data[i] = i; + //foreach_reverse ( const(int) i, x; data) data[i] = i; + //foreach_reverse ( int i, x; data) static assert(!__traits(compiles, (data[i] = i))); + foreach_reverse (immutable(ulong) i, x; data) data[i] = i; + foreach_reverse ( const(ulong) i, x; data) data[i] = i; + foreach_reverse ( ulong i, x; data) static assert(!__traits(compiles, (data[i] = i))); +} diff --git a/gcc/testsuite/gdc.test/compilable/testInference.d b/gcc/testsuite/gdc.test/compilable/testInference.d index dcde8f9c8..dc88398fb 100644 --- a/gcc/testsuite/gdc.test/compilable/testInference.d +++ b/gcc/testsuite/gdc.test/compilable/testInference.d @@ -48,7 +48,7 @@ void fECPa() { } h(); } - static assert( is(typeof(&g!()) == void delegate() pure nothrow @safe)); + static assert( is(typeof(&g!()) == void delegate() pure nothrow @nogc @safe)); static assert(!is(typeof(&g!()) == void delegate())); } @@ -62,6 +62,22 @@ void fECPb() { static assert( is(typeof(&g!()) == void delegate())); } +/***************************************************/ +// 5635 + +pure bool foo5635(R = int)(string x) +{ + bool result = false; + foreach (dchar d; x) + result = true; + return result; +} + +void test5635() +{ + foo5635("hi"); +} + /***************************************************/ // 5936 @@ -84,6 +100,69 @@ void test6351() alias bug6351!(deleg6351) baz6531; } +/***************************************************/ +// 6359 + +void impure6359() nothrow @safe @nogc {} +void throwable6359() pure @safe @nogc {} +void system6359() pure nothrow @nogc {} +void gcable6359() pure nothrow @safe {} + +int global6359; + +void f6359() pure nothrow @safe @nogc +{ + static assert(!__traits(compiles, impure6359())); + static assert(!__traits(compiles, throwable6359())); + static assert(!__traits(compiles, system6359())); + static assert(!__traits(compiles, gcable6359())); + static assert(!__traits(compiles, global6359++)); + + //static assert(!__traits(compiles, { impure6359(); }())); // BUG: blocked by issue 9148. + static assert(!__traits(compiles, { throwable6359(); }())); + static assert(!__traits(compiles, { system6359(); }())); + static assert(!__traits(compiles, { gcable6359(); }())); + static assert(!__traits(compiles, { global6359++; }())); +} + +void g6359()() pure nothrow @safe @nogc +{ + static assert(!__traits(compiles, impure6359())); + static assert(!__traits(compiles, throwable6359())); + static assert(!__traits(compiles, system6359())); + static assert(!__traits(compiles, gcable6359())); + static assert(!__traits(compiles, global6359++)); + + //static assert(!__traits(compiles, { impure6359(); }())); // BUG: blocked by issue 9148. + static assert(!__traits(compiles, { throwable6359(); }())); + static assert(!__traits(compiles, { system6359(); }())); + static assert(!__traits(compiles, { gcable6359(); }())); + static assert(!__traits(compiles, { global6359++; }())); +} + +// attribute inference is not affected by the expressions inside __traits(compiles) +void h6359()() +{ + static assert( __traits(compiles, impure6359())); + static assert( __traits(compiles, throwable6359())); + static assert( __traits(compiles, system6359())); + static assert( __traits(compiles, gcable6359())); + static assert( __traits(compiles, global6359++)); + + static assert( __traits(compiles, { impure6359(); }())); + static assert( __traits(compiles, { throwable6359(); }())); + static assert( __traits(compiles, { system6359(); }())); + static assert( __traits(compiles, { gcable6359(); }())); + //static assert( __traits(compiles, { global6359++; }())); // BUG: blocked by issue 9148. +} + +void test6359() pure nothrow @safe @nogc +{ + f6359(); + g6359(); + h6359(); +} + /***************************************************/ // 7017 @@ -176,9 +255,9 @@ extern(C) void testC8504() {} void test8504() { - static assert(typeof(foo8504!()).stringof == "pure nothrow @safe void()"); - static assert(typeof(foo8504!()).mangleof == "FNaNbNfZv"); - static assert(foo8504!().mangleof == "_D13testInference12__T7foo8504Z7foo8504FNaNbNfZv"); + static assert(typeof(foo8504!()).stringof == "pure nothrow @nogc @safe void()"); + static assert(typeof(foo8504!()).mangleof == "FNaNbNiNfZv"); + static assert(foo8504!().mangleof == "_D13testInference12__T7foo8504Z7foo8504FNaNbNiNfZv"); auto fp1 = toDelegate8504a(&testC8504); auto fp2 = toDelegate8504b(&testC8504); @@ -242,14 +321,14 @@ struct S5933 double foo()(double a) { return a * a; } } // outside function -static assert(typeof(foo5933!()).stringof == "pure nothrow @safe int(int a)"); -static assert(typeof(S5933.init.foo!()).stringof == "pure nothrow @safe double(double a)"); +static assert(typeof(foo5933!()).stringof == "pure nothrow @nogc @safe int(int a)"); +static assert(typeof(S5933.init.foo!()).stringof == "pure nothrow @nogc @safe double(double a)"); void test5933() { // inside function - static assert(typeof(foo5933!()).stringof == "pure nothrow @safe int(int a)"); - static assert(typeof(S5933.init.foo!()).stringof == "pure nothrow @safe double(double a)"); + static assert(typeof(foo5933!()).stringof == "pure nothrow @nogc @safe int(int a)"); + static assert(typeof(S5933.init.foo!()).stringof == "pure nothrow @nogc @safe double(double a)"); } /***************************************************/ @@ -375,6 +454,87 @@ pure void test10296() } /***************************************************/ +// 12025 + +struct Foo12025 +{ + int[5] bar; +} + +void test12025a() pure +{ + enum n1 = typeof(Foo12025.bar).length; // OK + enum n2 = Foo12025.bar .length; // OK <- error + + auto x1 = typeof(Foo12025.bar).length; // OK + auto x2 = Foo12025.bar .length; // OK <- error +} + +void test12025b() pure +{ + static int[5] bar; + + enum n1 = typeof(bar).length; // OK + enum n2 = bar .length; // OK <- error + + auto x1 = typeof(bar).length; // OK + auto x2 = bar .length; // OK <- error +} + +/***************************************************/ +// 12542 + +int logOf12542(T)(T n) +{ + if (n) + return 1 + logOf12542(n/2); + return 0; +} + +void test12542() @safe nothrow pure +{ + int log = logOf12542(9); +} + +/***************************************************/ +// 12704 + +void foo12704() @system; +alias FP12704 = typeof(function() { foo12704(); }); +static assert(is(FP12704 == void function() @system)); + +/***************************************************/ +// 12970 + +@system { @safe void f12970a() {} } +@system { void f12970b() @safe {} } +static assert(is(typeof(&f12970a) == void function() @safe)); +static assert(is(typeof(&f12970b) == void function() @safe)); + +@system { @trusted void f12970c() {} } +@system { void f12970d() @trusted {} } +static assert(is(typeof(&f12970c) == void function() @trusted)); +static assert(is(typeof(&f12970d) == void function() @trusted)); + +@safe { @system void f12970e() {} } +@safe { void f12970f() @system {} } +static assert(is(typeof(&f12970e) == void function() @system)); +static assert(is(typeof(&f12970f) == void function() @system)); + +@safe { @trusted void f12970g() {} } +@safe { void f12970h() @trusted {} } +static assert(is(typeof(&f12970g) == void function() @trusted)); +static assert(is(typeof(&f12970h) == void function() @trusted)); + +@trusted { @safe void f12970i() {} } +@trusted { void f12970j() @safe {} } +static assert(is(typeof(&f12970i) == void function() @safe)); +static assert(is(typeof(&f12970j) == void function() @safe)); + +@trusted { @system void f12970k() {} } +@trusted { void f12970l() @system {} } +static assert(is(typeof(&f12970k) == void function() @system)); +static assert(is(typeof(&f12970l) == void function() @system)); // Add more tests regarding inferences later. diff --git a/gcc/testsuite/gdc.test/compilable/testVRP.d b/gcc/testsuite/gdc.test/compilable/testVRP.d index 8999a9c59..581f45af3 100644 --- a/gcc/testsuite/gdc.test/compilable/testVRP.d +++ b/gcc/testsuite/gdc.test/compilable/testVRP.d @@ -306,3 +306,26 @@ void test9617a() } } } + +void test10018(ubyte value) +{ + const int c = value; + ubyte b = c; + static assert(!__traits(compiles, b = c - 1)); + static assert(!__traits(compiles, b = c + 1)); + immutable int i = value; + b = i; + static assert(!__traits(compiles, b = i - 1)); + static assert(!__traits(compiles, b = i + 1)); +} + +void test13001(bool unknown) +{ + foreach (const i; 0..unknown?2:3) + { + ubyte b = i; + static assert(!__traits(compiles, b = i - 1)); + b = i + 253; + static assert(!__traits(compiles, b = i + 254)); + } +} diff --git a/gcc/testsuite/gdc.test/compilable/testheader1.d b/gcc/testsuite/gdc.test/compilable/testheader1.d new file mode 100644 index 000000000..4e9a07714 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/testheader1.d @@ -0,0 +1,6 @@ +// EXTRA_SOURCES: extra-files/header1.d +// REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/header1.di +// PERMUTE_ARGS: -d -dw +// POST_SCRIPT: compilable/extra-files/header-postscript.sh header1 + +void main() {} diff --git a/gcc/testsuite/gdc.test/compilable/testheader12567a.d b/gcc/testsuite/gdc.test/compilable/testheader12567a.d new file mode 100644 index 000000000..27c147568 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/testheader12567a.d @@ -0,0 +1,7 @@ +// REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/header12567a.di +// PERMUTE_ARGS: +// POST_SCRIPT: compilable/extra-files/header-postscript.sh header12567a + +deprecated module header12567a; + +void main() {} diff --git a/gcc/testsuite/gdc.test/compilable/testheader12567b.d b/gcc/testsuite/gdc.test/compilable/testheader12567b.d new file mode 100644 index 000000000..93393c509 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/testheader12567b.d @@ -0,0 +1,7 @@ +// REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/header12567b.di +// PERMUTE_ARGS: +// POST_SCRIPT: compilable/extra-files/header-postscript.sh header12567b + +deprecated("message") module header12567b; + +void main() {} diff --git a/gcc/testsuite/gdc.test/compilable/testheader1i.d b/gcc/testsuite/gdc.test/compilable/testheader1i.d new file mode 100644 index 000000000..985999259 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/testheader1i.d @@ -0,0 +1,6 @@ +// EXTRA_SOURCES: extra-files/header1.d +// REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/header1i.di -inline +// PERMUTE_ARGS: -d -dw +// POST_SCRIPT: compilable/extra-files/header-postscript.sh header1i + +void main() {} diff --git a/gcc/testsuite/gdc.test/compilable/testheader2.d b/gcc/testsuite/gdc.test/compilable/testheader2.d new file mode 100644 index 000000000..003d56467 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/testheader2.d @@ -0,0 +1,6 @@ +// EXTRA_SOURCES: extra-files/header2.d +// REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/header2.di +// PERMUTE_ARGS: +// POST_SCRIPT: compilable/extra-files/header-postscript.sh header2 + +void main() {} diff --git a/gcc/testsuite/gdc.test/compilable/testheader2i.d b/gcc/testsuite/gdc.test/compilable/testheader2i.d new file mode 100644 index 000000000..79662edd1 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/testheader2i.d @@ -0,0 +1,6 @@ +// EXTRA_SOURCES: extra-files/header2.d +// REQUIRED_ARGS: -o- -H -Hf${RESULTS_DIR}/compilable/header2i.di -inline +// PERMUTE_ARGS: +// POST_SCRIPT: compilable/extra-files/header-postscript.sh header2i + +void main() {} diff --git a/gcc/testsuite/gdc.test/compilable/testimport12242.d b/gcc/testsuite/gdc.test/compilable/testimport12242.d new file mode 100644 index 000000000..1d1cccd01 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/testimport12242.d @@ -0,0 +1,26 @@ +// PERMUTE_ARGS: + +module testimport12242; + +import imports.imp12242a; // test // stripA == OverloadSet +import imports.imp12242a1; // std.string // stripA == template + +import imports.imp12242b1; // std.string // stripB == template +import imports.imp12242b; // test // stripB == OverloadSet + +void main() +{ + static assert(stripA(" af ") == 1); + static assert(" af ".stripA() == 1); // UFCS (1) + static assert(" af ".stripA == 1); // UFCS (2) + + static assert(stripB(" af ") == 1); + static assert(" af ".stripB() == 1); // UFCS (1) + static assert(" af ".stripB == 1); // UFCS (2) + + + static assert(foo!int == 1); + static assert(foo!long == 2); + static assert(foo!float == 3); + static assert(foo!real == 4); +} diff --git a/gcc/testsuite/gdc.test/compilable/vgc1.d b/gcc/testsuite/gdc.test/compilable/vgc1.d new file mode 100644 index 000000000..46fb144af --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/vgc1.d @@ -0,0 +1,85 @@ +// REQUIRED_ARGS: -vgc -o- +// PERMUTE_ARGS: + +/***************** NewExp *******************/ + +struct S1 { } +struct S2 { this(int); } +struct S3 { this(int) @nogc; } +struct S4 { new(size_t); } +struct S5 { @nogc new(size_t); } + +/* +TEST_OUTPUT: +--- +compilable/vgc1.d(27): vgc: 'new' causes GC allocation +compilable/vgc1.d(29): vgc: 'new' causes GC allocation +compilable/vgc1.d(30): vgc: 'new' causes GC allocation +compilable/vgc1.d(32): vgc: 'new' causes GC allocation +compilable/vgc1.d(33): vgc: 'new' causes GC allocation +compilable/vgc1.d(34): vgc: 'new' causes GC allocation +compilable/vgc1.d(38): vgc: 'new' causes GC allocation +--- +*/ + +void testNew() +{ + int* p1 = new int; + + int[] a1 = new int[3]; + int[][] a2 = new int[][](2, 3); + + S1* ps1 = new S1(); + S2* ps2 = new S2(1); + S3* ps3 = new S3(1); + S4* ps4 = new S4; // no error + S5* ps5 = new S5; // no error + + Object o1 = new Object(); +} + +/* +TEST_OUTPUT: +--- +compilable/vgc1.d(55): vgc: 'new' causes GC allocation +compilable/vgc1.d(57): vgc: 'new' causes GC allocation +compilable/vgc1.d(58): vgc: 'new' causes GC allocation +compilable/vgc1.d(60): vgc: 'new' causes GC allocation +compilable/vgc1.d(61): vgc: 'new' causes GC allocation +compilable/vgc1.d(62): vgc: 'new' causes GC allocation +--- +*/ + +void testNewScope() +{ + scope int* p1 = new int; + + scope int[] a1 = new int[3]; + scope int[][] a2 = new int[][](2, 3); + + scope S1* ps1 = new S1(); + scope S2* ps2 = new S2(1); + scope S3* ps3 = new S3(1); + scope S4* ps4 = new S4; // no error + scope S5* ps5 = new S5; // no error + + scope Object o1 = new Object(); // no error + scope o2 = new Object(); // no error +} + +/***************** DeleteExp *******************/ + +/* +TEST_OUTPUT: +--- +compilable/vgc1.d(82): vgc: 'delete' requires GC +compilable/vgc1.d(83): vgc: 'delete' requires GC +compilable/vgc1.d(84): vgc: 'delete' requires GC +--- +*/ +void testDelete(int* p, Object o, S1* s) +{ + delete p; + delete o; + delete s; +} diff --git a/gcc/testsuite/gdc.test/compilable/vgc2.d b/gcc/testsuite/gdc.test/compilable/vgc2.d new file mode 100644 index 000000000..b1a7f187a --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/vgc2.d @@ -0,0 +1,104 @@ +// REQUIRED_ARGS: -vgc -o- +// PERMUTE_ARGS: + +/***************** CatExp *******************/ + +/* +TEST_OUTPUT: +--- +compilable/vgc2.d(21): vgc: operator ~ may cause GC allocation +compilable/vgc2.d(22): vgc: operator ~ may cause GC allocation +compilable/vgc2.d(23): vgc: operator ~ may cause GC allocation +compilable/vgc2.d(25): vgc: operator ~ may cause GC allocation +compilable/vgc2.d(26): vgc: operator ~ may cause GC allocation +compilable/vgc2.d(27): vgc: operator ~ may cause GC allocation +compilable/vgc2.d(28): vgc: operator ~ may cause GC allocation +compilable/vgc2.d(29): vgc: operator ~ may cause GC allocation +--- +*/ +void testCat(int[] a, string s) +{ + int[] a1 = a ~ a; + int[] a2 = a ~ 1; + int[] a3 = 1 ~ a; + + string s1 = s ~ s; + string s2 = s ~ "a"; + string s3 = "a" ~ s; + string s4 = s ~ 'c'; + string s5 = 'c' ~ s; + + string s6 = "a" ~ "b"; // no error + string s7 = "a" ~ 'c'; // no error + string s8 = 'c' ~ "b"; // no error +} + +/***************** CatAssignExp *******************/ + +/* +TEST_OUTPUT: +--- +compilable/vgc2.d(48): vgc: operator ~= may cause GC allocation +compilable/vgc2.d(50): vgc: operator ~= may cause GC allocation +compilable/vgc2.d(51): vgc: operator ~= may cause GC allocation +--- +*/ +void testCatAssign(int[] a, string s) +{ + a ~= 1; + + s ~= "a"; + s ~= 'c'; +} + +/***************** ArrayLiteralExp *******************/ + +int* barA(); + +/* +TEST_OUTPUT: +--- +compilable/vgc2.d(70): vgc: array literal may cause GC allocation +compilable/vgc2.d(71): vgc: array literal may cause GC allocation +--- +*/ +void testArray() +{ + enum arrLiteral = [null, null]; + + int* p; + auto a = [p, p, barA()]; + a = arrLiteral; +} + +/***************** AssocArrayLiteralExp *******************/ + +/* +TEST_OUTPUT: +--- +compilable/vgc2.d(87): vgc: associative array literal may cause GC allocation +compilable/vgc2.d(88): vgc: associative array literal may cause GC allocation +--- +*/ +void testAssocArray() +{ + enum aaLiteral = [10: 100]; + + auto aa = [1:1, 2:3, 4:5]; + aa = aaLiteral; +} + +/***************** IndexExp *******************/ + +/* +TEST_OUTPUT: +--- +compilable/vgc2.d(102): vgc: indexing an associative array may cause GC allocation +compilable/vgc2.d(103): vgc: indexing an associative array may cause GC allocation +--- +*/ +void testIndex(int[int] aa) +{ + aa[1] = 0; + int n = aa[1]; +} diff --git a/gcc/testsuite/gdc.test/compilable/vgc3.d b/gcc/testsuite/gdc.test/compilable/vgc3.d new file mode 100644 index 000000000..4bf889ce9 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/vgc3.d @@ -0,0 +1,68 @@ +// REQUIRED_ARGS: -vgc -o- +// PERMUTE_ARGS: + +/***************** AssignExp *******************/ + +/* +TEST_OUTPUT: +--- +compilable/vgc3.d(16): vgc: setting 'length' may cause GC allocation +compilable/vgc3.d(17): vgc: setting 'length' may cause GC allocation +compilable/vgc3.d(18): vgc: setting 'length' may cause GC allocation +--- +*/ +void testArrayLength(int[] a) +{ + a.length = 3; + a.length += 1; + a.length -= 1; +} + +/***************** CallExp *******************/ + +void barCall(); + +/* +TEST_OUTPUT: +--- +--- +*/ + + +void testCall() +{ + auto fp = &barCall; + (*fp)(); + barCall(); +} + +/****************** Closure ***********************/ + +@nogc void takeDelegate2(scope int delegate() dg) {} +@nogc void takeDelegate3( int delegate() dg) {} + +/* +TEST_OUTPUT: +--- +compilable/vgc3.d(51): vgc: using closure causes GC allocation +compilable/vgc3.d(63): vgc: using closure causes GC allocation +--- +*/ +auto testClosure1() +{ + int x; + int bar() { return x; } + return &bar; +} +void testClosure2() +{ + int x; + int bar() { return x; } + takeDelegate2(&bar); // no error +} +void testClosure3() +{ + int x; + int bar() { return x; } + takeDelegate3(&bar); +} diff --git a/gcc/testsuite/gdc.test/compilable/warn3882.d b/gcc/testsuite/gdc.test/compilable/warn3882.d new file mode 100644 index 000000000..07e145ee6 --- /dev/null +++ b/gcc/testsuite/gdc.test/compilable/warn3882.d @@ -0,0 +1,66 @@ +// PERMUTE_ARGS: -w -wi -debug +/* +TEST_OUTPUT: +--- +--- +*/ + +@safe pure nothrow void strictVoidReturn(T)(T x) {} +@safe pure nothrow void nonstrictVoidReturn(T)(ref T x) {} + +void main() +{ + int x = 3; + strictVoidReturn(x); + nonstrictVoidReturn(x); +} + +/******************************************/ +// 12619 + +extern (C) @system nothrow pure void* memcpy(void* s1, in void* s2, size_t n); +// -> weakly pure + +void test12619() pure +{ + ubyte[10] a, b; + debug memcpy(a.ptr, b.ptr, 5); // memcpy call should have side effect +} + +/******************************************/ +// 12760 + +struct S12760(T) +{ + T i; + this(T j) inout {} +} + +struct K12760 +{ + S12760!int nullable; + + this(int) + { + nullable = 0; // weak purity + } +} + +/******************************************/ +// 12909 + +int f12909(immutable(int[])[int] aa) pure nothrow +{ + aa[0] = []; + return 0; +} + +void test12909() +{ + immutable(int[])[int] aa; + f12909(aa); + + // from 12910 + const(int[])[int] makeAA() { return null; } // to make r-value + makeAA().rehash(); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecate12979a.d b/gcc/testsuite/gdc.test/fail_compilation/deprecate12979a.d new file mode 100644 index 000000000..f823af295 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/deprecate12979a.d @@ -0,0 +1,18 @@ +// REQUIRED_ARGS: -de +// PERMUTE_ARGS: + +/* +TEST_OUTPUT: +--- +fail_compilation/deprecate12979a.d(14): Deprecation: asm statement is assumed to throw - mark it with 'nothrow' if it does not +fail_compilation/deprecate12979a.d(12): Error: function 'deprecate12979a.foo' is nothrow yet may throw +--- +*/ + +void foo() nothrow +{ + asm + { + ret; + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecate12979b.d b/gcc/testsuite/gdc.test/fail_compilation/deprecate12979b.d new file mode 100644 index 000000000..0d675cb55 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/deprecate12979b.d @@ -0,0 +1,17 @@ +// REQUIRED_ARGS: -de +// PERMUTE_ARGS: + +/* +TEST_OUTPUT: +--- +fail_compilation/deprecate12979b.d(13): Deprecation: asm statement is assumed to be impure - mark it with 'pure' if it is not +--- +*/ + +void foo() pure +{ + asm + { + ret; + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecate12979c.d b/gcc/testsuite/gdc.test/fail_compilation/deprecate12979c.d new file mode 100644 index 000000000..782700469 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/deprecate12979c.d @@ -0,0 +1,17 @@ +// REQUIRED_ARGS: -de +// PERMUTE_ARGS: + +/* +TEST_OUTPUT: +--- +fail_compilation/deprecate12979c.d(13): Deprecation: asm statement is assumed to use the GC - mark it with '@nogc' if it does not +--- +*/ + +void foo() @nogc +{ + asm + { + ret; + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/deprecate12979d.d b/gcc/testsuite/gdc.test/fail_compilation/deprecate12979d.d new file mode 100644 index 000000000..afff56710 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/deprecate12979d.d @@ -0,0 +1,16 @@ +// PERMUTE_ARGS: + +/* +TEST_OUTPUT: +--- +fail_compilation/deprecate12979d.d(12): Error: asm statement is assumed to be @system - mark it with '@trusted' if it is not +--- +*/ + +void foo() @safe +{ + asm + { + ret; + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10169.d b/gcc/testsuite/gdc.test/fail_compilation/diag10169.d new file mode 100644 index 000000000..f1efb403f --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag10169.d @@ -0,0 +1,12 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag10169.d(11): Error: struct imports.a10169.B member x is not accessible +--- +*/ +import imports.a10169; + +void main() +{ + auto a = B.init.x; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10221.d b/gcc/testsuite/gdc.test/fail_compilation/diag10221.d new file mode 100644 index 000000000..db84194d1 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag10221.d @@ -0,0 +1,11 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag10221.d(10): Error: cannot implicitly convert expression (256) of type int to ubyte +--- +*/ + +void main() +{ + foreach(ref ubyte i; 0..256) {} +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10221a.d b/gcc/testsuite/gdc.test/fail_compilation/diag10221a.d new file mode 100644 index 000000000..923b80c27 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag10221a.d @@ -0,0 +1,11 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag10221a.d(10): Error: cannot implicitly convert expression (257) of type int to ubyte +--- +*/ + +void main() +{ + foreach(ubyte i; 0..257) {} +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag10415.d b/gcc/testsuite/gdc.test/fail_compilation/diag10415.d index 721770f97..02021dd22 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag10415.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag10415.d @@ -1,8 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/diag10415.d(34): Error: function diag10415.C.x () const is not callable using argument types (int) const -fail_compilation/diag10415.d(37): Error: d.x is not an lvalue +fail_compilation/diag10415.d(36): Error: None of the overloads of 'x' are callable using argument types (int) const, candidates are: +fail_compilation/diag10415.d(13): diag10415.C.x() +fail_compilation/diag10415.d(18): diag10415.C.x(int _param_0) +fail_compilation/diag10415.d(39): Error: d.x is not an lvalue --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag11078.d b/gcc/testsuite/gdc.test/fail_compilation/diag11078.d index 8d5499d44..0e89eea78 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag11078.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag11078.d @@ -1,7 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/diag11078.d(17): Error: function diag11078.S1.value () is not callable using argument types (double) +fail_compilation/diag11078.d(19): Error: None of the overloads of 'value' are callable using argument types (double), candidates are: +fail_compilation/diag11078.d(12): diag11078.S1.value() +fail_compilation/diag11078.d(13): diag11078.S1.value(int n) --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag11769.d b/gcc/testsuite/gdc.test/fail_compilation/diag11769.d index 2b2344ff7..83a702f89 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag11769.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag11769.d @@ -2,9 +2,9 @@ TEST_OUTPUT: --- fail_compilation/diag11769.d(18): Error: diag11769.foo!string.bar called with argument types (string) matches both: - fail_compilation/diag11769.d(13): diag11769.foo!string.bar(immutable(wchar)[] _param_0) +fail_compilation/diag11769.d(13): diag11769.foo!string.bar(immutable(wchar)[] _param_0) and: - fail_compilation/diag11769.d(14): diag11769.foo!string.bar(immutable(dchar)[] _param_0) +fail_compilation/diag11769.d(14): diag11769.foo!string.bar(immutable(dchar)[] _param_0) --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag11819a.d b/gcc/testsuite/gdc.test/fail_compilation/diag11819a.d new file mode 100644 index 000000000..338f9b275 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag11819a.d @@ -0,0 +1,51 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag11819a.d(30): Error: unrecognized trait 'DoesNotExist' +fail_compilation/diag11819a.d(31): Error: unrecognized trait 'IsAbstractClass', did you mean 'isAbstractClass'? +fail_compilation/diag11819a.d(32): Error: unrecognized trait 'IsArithmetic', did you mean 'isArithmetic'? +fail_compilation/diag11819a.d(33): Error: unrecognized trait 'IsAssociativeArray', did you mean 'isAssociativeArray'? +fail_compilation/diag11819a.d(34): Error: unrecognized trait 'IsFinalClass', did you mean 'isFinalClass'? +fail_compilation/diag11819a.d(35): Error: unrecognized trait 'IsPOD', did you mean 'isPOD'? +fail_compilation/diag11819a.d(36): Error: unrecognized trait 'IsNested', did you mean 'isNested'? +fail_compilation/diag11819a.d(37): Error: unrecognized trait 'IsFloating', did you mean 'isFloating'? +fail_compilation/diag11819a.d(38): Error: unrecognized trait 'IsIntegral', did you mean 'isIntegral'? +fail_compilation/diag11819a.d(39): Error: unrecognized trait 'IsScalar', did you mean 'isScalar'? +fail_compilation/diag11819a.d(40): Error: unrecognized trait 'IsStaticArray', did you mean 'isStaticArray'? +fail_compilation/diag11819a.d(41): Error: unrecognized trait 'IsUnsigned', did you mean 'isUnsigned'? +fail_compilation/diag11819a.d(42): Error: unrecognized trait 'IsVirtualFunction', did you mean 'isVirtualFunction'? +fail_compilation/diag11819a.d(43): Error: unrecognized trait 'IsVirtualMethod', did you mean 'isVirtualMethod'? +fail_compilation/diag11819a.d(44): Error: unrecognized trait 'IsAbstractFunction', did you mean 'isAbstractFunction'? +fail_compilation/diag11819a.d(45): Error: unrecognized trait 'IsFinalFunction', did you mean 'isFinalFunction'? +fail_compilation/diag11819a.d(46): Error: unrecognized trait 'IsOverrideFunction', did you mean 'isOverrideFunction'? +fail_compilation/diag11819a.d(47): Error: unrecognized trait 'IsStaticFunction', did you mean 'isStaticFunction'? +fail_compilation/diag11819a.d(48): Error: unrecognized trait 'IsRef', did you mean 'isRef'? +fail_compilation/diag11819a.d(49): Error: unrecognized trait 'IsOut', did you mean 'isOut'? +fail_compilation/diag11819a.d(50): Error: unrecognized trait 'IsLazy', did you mean 'isLazy'? +--- +*/ + +void main() +{ + if (__traits(DoesNotExist)) { } + if (__traits(IsAbstractClass)) { } + if (__traits(IsArithmetic)) { } + if (__traits(IsAssociativeArray)) { } + if (__traits(IsFinalClass)) { } + if (__traits(IsPOD)) { } + if (__traits(IsNested)) { } + if (__traits(IsFloating)) { } + if (__traits(IsIntegral)) { } + if (__traits(IsScalar)) { } + if (__traits(IsStaticArray)) { } + if (__traits(IsUnsigned)) { } + if (__traits(IsVirtualFunction)) { } + if (__traits(IsVirtualMethod)) { } + if (__traits(IsAbstractFunction)) { } + if (__traits(IsFinalFunction)) { } + if (__traits(IsOverrideFunction)) { } + if (__traits(IsStaticFunction)) { } + if (__traits(IsRef)) { } + if (__traits(IsOut)) { } + if (__traits(IsLazy)) { } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag11819b.d b/gcc/testsuite/gdc.test/fail_compilation/diag11819b.d new file mode 100644 index 000000000..a7e6046c1 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag11819b.d @@ -0,0 +1,47 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag11819b.d(28): Error: unrecognized trait 'HasMember', did you mean 'hasMember'? +fail_compilation/diag11819b.d(29): Error: unrecognized trait 'Identifier', did you mean 'identifier'? +fail_compilation/diag11819b.d(30): Error: unrecognized trait 'GetProtection', did you mean 'getProtection'? +fail_compilation/diag11819b.d(31): Error: unrecognized trait 'Parent', did you mean 'parent'? +fail_compilation/diag11819b.d(32): Error: unrecognized trait 'GetMember', did you mean 'getMember'? +fail_compilation/diag11819b.d(33): Error: unrecognized trait 'GetOverloads', did you mean 'getOverloads'? +fail_compilation/diag11819b.d(34): Error: unrecognized trait 'GetVirtualFunctions', did you mean 'getVirtualFunctions'? +fail_compilation/diag11819b.d(35): Error: unrecognized trait 'GetVirtualMethods', did you mean 'getVirtualMethods'? +fail_compilation/diag11819b.d(36): Error: unrecognized trait 'ClassInstanceSize', did you mean 'classInstanceSize'? +fail_compilation/diag11819b.d(37): Error: unrecognized trait 'AllMembers', did you mean 'allMembers'? +fail_compilation/diag11819b.d(38): Error: unrecognized trait 'DerivedMembers', did you mean 'derivedMembers'? +fail_compilation/diag11819b.d(39): Error: unrecognized trait 'IsSame', did you mean 'isSame'? +fail_compilation/diag11819b.d(40): Error: unrecognized trait 'Compiles', did you mean 'compiles'? +fail_compilation/diag11819b.d(41): Error: unrecognized trait 'Parameters', did you mean 'parameters'? +fail_compilation/diag11819b.d(42): Error: unrecognized trait 'GetAliasThis', did you mean 'getAliasThis'? +fail_compilation/diag11819b.d(43): Error: unrecognized trait 'GetAttributes', did you mean 'getAttributes'? +fail_compilation/diag11819b.d(44): Error: unrecognized trait 'GetFunctionAttributes', did you mean 'getFunctionAttributes'? +fail_compilation/diag11819b.d(45): Error: unrecognized trait 'GetUnitTests', did you mean 'getUnitTests'? +fail_compilation/diag11819b.d(46): Error: unrecognized trait 'GetVirtualIndex', did you mean 'getVirtualIndex'? +--- +*/ + +void main() +{ + if (__traits(HasMember)) { } + if (__traits(Identifier)) { } + if (__traits(GetProtection)) { } + if (__traits(Parent)) { } + if (__traits(GetMember)) { } + if (__traits(GetOverloads)) { } + if (__traits(GetVirtualFunctions)) { } + if (__traits(GetVirtualMethods)) { } + if (__traits(ClassInstanceSize)) { } + if (__traits(AllMembers)) { } + if (__traits(DerivedMembers)) { } + if (__traits(IsSame)) { } + if (__traits(Compiles)) { } + if (__traits(Parameters)) { } + if (__traits(GetAliasThis)) { } + if (__traits(GetAttributes)) { } + if (__traits(GetFunctionAttributes)) { } + if (__traits(GetUnitTests)) { } + if (__traits(GetVirtualIndex)) { } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12063.d b/gcc/testsuite/gdc.test/fail_compilation/diag12063.d new file mode 100644 index 000000000..e029810b1 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag12063.d @@ -0,0 +1,15 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag12063.d(11): Error: no property 'max' for type 'Foo' +fail_compilation/diag12063.d(14): Error: incompatible types for ((Foo()) + (1)): 'Bar' and 'int' +--- +*/ + +struct Foo {} + +enum Bar : Foo +{ + a = Foo(), + b +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12280.d b/gcc/testsuite/gdc.test/fail_compilation/diag12280.d new file mode 100644 index 000000000..9bba833e9 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag12280.d @@ -0,0 +1,18 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag12280.d(15): Error: undefined identifier nonexistent +fail_compilation/diag12280.d(13): Error: template instance diag12280.f!10 error instantiating +fail_compilation/diag12280.d(18): 11 recursive instantiations from here: f!0 +--- +*/ + +void f(int i)() +{ + static if (i < 10) + f!(i + 1); + else + nonexistent(); +} + +alias f0 = f!0; diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12312.d b/gcc/testsuite/gdc.test/fail_compilation/diag12312.d new file mode 100644 index 000000000..7120a8f0d --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag12312.d @@ -0,0 +1,11 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag12312.d(10): Error: variable diag12312.main.arr void[16] does not have a default initializer +--- +*/ + +void main() +{ + void[16] arr; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12380.d b/gcc/testsuite/gdc.test/fail_compilation/diag12380.d new file mode 100644 index 000000000..3a9e13fab --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag12380.d @@ -0,0 +1,13 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag12380.d(12): Error: cannot implicitly convert expression (cast(E)0) of type E to void* +--- +*/ + +enum E { a, b, } + +void main() +{ + void* a = E.init; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12432.d b/gcc/testsuite/gdc.test/fail_compilation/diag12432.d new file mode 100644 index 000000000..10755d84a --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag12432.d @@ -0,0 +1,61 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag12432.d(55): Error: cannot infer argument types, expected 1 argument, not 2 +fail_compilation/diag12432.d(56): Error: cannot infer argument types, expected 2 arguments, not 3 +fail_compilation/diag12432.d(57): Error: cannot infer argument types, expected 1 argument, not 2 +fail_compilation/diag12432.d(58): Error: cannot infer argument types, expected 1 argument, not 2 +fail_compilation/diag12432.d(59): Error: cannot infer argument types, expected 2 arguments, not 3 +fail_compilation/diag12432.d(60): Error: cannot infer argument types, expected 2 arguments, not 3 +--- +*/ + +struct R1 +{ + @property int front() { return 0; } + enum bool empty = false; + void popFront() { } +} + +struct Tuple(T...) +{ + T t; + alias t this; +} + +struct R2 +{ + @property Tuple!(int, float) front() { return typeof(return).init; } + enum bool empty = false; + void popFront() { } +} + +struct OpApply1Func +{ + int opApply(int function(int)) { return 0; } +} + +struct OpApply1Deleg +{ + int opApply(int delegate(int)) { return 0; } +} + +struct OpApply2Func +{ + int opApply(int function(int, float)) { return 0; } +} + +struct OpApply2Deleg +{ + int opApply(int delegate(int, float)) { return 0; } +} + +void main() +{ + foreach (a, b; R1()) { } + foreach (a, b, c; R2()) { } + foreach (a, b; OpApply1Func()) { } + foreach (a, b; OpApply1Deleg()) { } + foreach (a, b, c; OpApply2Func()) { } + foreach (a, b, c; OpApply2Deleg()) { } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12480.d b/gcc/testsuite/gdc.test/fail_compilation/diag12480.d new file mode 100644 index 000000000..25ebf5c71 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag12480.d @@ -0,0 +1,12 @@ +// REQUIRED_ARGS: -m32 +/* +TEST_OUTPUT: +--- +fail_compilation/diag12480.d(12): Error: static assert (2u == 3u) is false +--- +*/ + +module diag12480; + +static immutable arr = ["a", "b"]; +static assert(arr.length == 3); diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12598.d b/gcc/testsuite/gdc.test/fail_compilation/diag12598.d new file mode 100644 index 000000000..63eb66aac --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag12598.d @@ -0,0 +1,21 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag12598.d(13): Error: struct 'lines' is a type, not an lvalue +--- +*/ + +class C +{ + void f() + { + import imports.diag12598a; + lines ~= ""; + } + + string[] lines; +} + +void main() +{ +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12640.d b/gcc/testsuite/gdc.test/fail_compilation/diag12640.d new file mode 100644 index 000000000..962cccea1 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag12640.d @@ -0,0 +1,30 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag12640.d(14): Error: undefined identifier asdf +fail_compilation/diag12640.d(23): Error: undefined identifier asdf +--- +*/ + +void main() +{ + switch (1) + { + case 0: + asdf; + break; + + default: + } + + switch (1) + { + default: + asdf; + break; + + case 0: + } + +} + diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12678.d b/gcc/testsuite/gdc.test/fail_compilation/diag12678.d new file mode 100644 index 000000000..afe56fb51 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag12678.d @@ -0,0 +1,27 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag12678.d(19): Error: const field 'cf1' initialized multiple times +fail_compilation/diag12678.d(22): Error: immutable field 'if1' initialized multiple times +fail_compilation/diag12678.d(25): Error: const field 'cf2' initialization is not allowed in loops or after labels +--- +*/ + +struct S +{ + const int cf1; + const int cf2; + immutable int if1; + + this(int x) + { + cf1 = x; + cf1 = x; + + if1 = x; + if1 = x; + + foreach (i; 0 .. 5) + cf2 = x; + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag12777.d b/gcc/testsuite/gdc.test/fail_compilation/diag12777.d new file mode 100644 index 000000000..95ce80f5a --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag12777.d @@ -0,0 +1,23 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag12777.d(14): Error: cannot modify const expression this.v +fail_compilation/diag12777.d(15): Error: cannot modify immutable expression this.v +fail_compilation/diag12777.d(21): Error: cannot modify const expression this.v +fail_compilation/diag12777.d(22): Error: cannot modify immutable expression this.v +--- +*/ + +struct S +{ + int v; + void fun() const { v++; } + void gun() immutable { v++; } +} + +class C +{ + int v; + void fun() const { v++; } + void gun() immutable { v++; } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag13082.d b/gcc/testsuite/gdc.test/fail_compilation/diag13082.d new file mode 100644 index 000000000..13259cef0 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag13082.d @@ -0,0 +1,24 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag13082.d(22): Error: constructor diag13082.C.this (int a) is not callable using argument types (string) +fail_compilation/diag13082.d(23): Error: constructor diag13082.S.this (int a) is not callable using argument types (string) +--- +*/ + +class C +{ + this(int a) {} +} + +struct S +{ + this(int a) {} +} + +void main() +{ + string b; + auto c = new C(b); + auto s = new S(b); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag13142.d b/gcc/testsuite/gdc.test/fail_compilation/diag13142.d new file mode 100644 index 000000000..abcf1b401 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag13142.d @@ -0,0 +1,27 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag13142.d(25): Error: cannot implicitly convert expression (3) of type int to TYPE +--- +*/ + +class Button +{ + enum TYPE // button type + { + COMMAND, + CHECK, + OPTION, + } +} + +class Toolbar +{ + enum ButtonTYPE // button type + { + COMMAND = Button.TYPE.COMMAND, + CHECK = Button.TYPE.CHECK, + OPTION = Button.TYPE.OPTION, + DELIMETER = Button.TYPE.max + 1 + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag13333.d b/gcc/testsuite/gdc.test/fail_compilation/diag13333.d new file mode 100644 index 000000000..f318a31ae --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag13333.d @@ -0,0 +1,40 @@ +/* +TEST_OUTPUT* +--- +fail_compilation/diag13333.d(29): Error: template instance VariantN!(maxSize!(S), T) recursive template expansion +fail_compilation/diag13333.d(29): Error: template instance diag13333.maxSize!(S) error instantiating +fail_compilation/diag13333.d(34): instantiated from here: Algebraic!(S) +--- +*/ + +template maxSize(T...) +{ + static if (T.length == 1) + { + enum size_t maxSize = T[0].sizeof; + } + else + { + enum size_t maxSize = T[0].sizeof >= maxSize!(T[1 .. $]) + ? T[0].sizeof : maxSize!(T[1 .. $]); + } +} + +struct VariantN(size_t maxDataSize, AllowedTypesX...) +{ +} + +template Algebraic(T...) +{ + alias Algebraic = VariantN!(maxSize!T, T); +} + +struct DummyScope +{ + alias A = Algebraic!S; + + static struct S // <- class + { + A entity; + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag1566.d b/gcc/testsuite/gdc.test/fail_compilation/diag1566.d new file mode 100644 index 000000000..28ffc8d48 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag1566.d @@ -0,0 +1,30 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag1566.d(23): Error: multiple ! arguments are not allowed +fail_compilation/diag1566.d(24): Error: multiple ! arguments are not allowed +fail_compilation/diag1566.d(25): Error: multiple ! arguments are not allowed +fail_compilation/diag1566.d(26): Error: multiple ! arguments are not allowed +fail_compilation/diag1566.d(28): Error: multiple ! arguments are not allowed +fail_compilation/diag1566.d(29): Error: multiple ! arguments are not allowed +--- +*/ + +template T(int n) +{ + template T(char c) + { + alias long T; + } +} + +void main() +{ + static assert(is(long == T!(3)!('b'))); + static assert(is(long == T! 3 ! 'b' )); + static assert(is(long == T!(3)! 'b' )); + static assert(is(long == T! 3 !('b'))); + + static assert(is(long == T!(3)! 'b' !"s")); + static assert(is(long == T! 3 !('b')!"s")); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag3672.d b/gcc/testsuite/gdc.test/fail_compilation/diag3672.d new file mode 100644 index 000000000..39b321bc9 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag3672.d @@ -0,0 +1,57 @@ +// PERMUTE_ARGS: +// REQUIRED_ARGS: -de +/* +TEST_OUTPUT: +--- +fail_compilation/diag3672.d(36): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(x, 1) instead. +fail_compilation/diag3672.d(37): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(x, 1) instead. +fail_compilation/diag3672.d(38): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"-="(x, 1) instead. +fail_compilation/diag3672.d(39): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"-="(x, 1) instead. +fail_compilation/diag3672.d(40): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(x, 1) instead. +fail_compilation/diag3672.d(41): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(x, 2) instead. +fail_compilation/diag3672.d(42): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"-="(x, 3) instead. +fail_compilation/diag3672.d(43): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"|="(x, y) instead. +fail_compilation/diag3672.d(44): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"*="(x, y) instead. +fail_compilation/diag3672.d(45): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"/="(x, y) instead. +fail_compilation/diag3672.d(46): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"%="(x, y) instead. +fail_compilation/diag3672.d(47): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"&="(x, y) instead. +fail_compilation/diag3672.d(48): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"^="(x, y) instead. +fail_compilation/diag3672.d(49): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"<<="(x, y) instead. +fail_compilation/diag3672.d(50): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!">>="(x, y) instead. +fail_compilation/diag3672.d(51): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!">>>="(x, y) instead. +fail_compilation/diag3672.d(52): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"^^="(x, y) instead. +fail_compilation/diag3672.d(53): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(ptr, 1) instead. +fail_compilation/diag3672.d(54): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(ptr, 1) instead. +fail_compilation/diag3672.d(55): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"-="(ptr, 1) instead. +fail_compilation/diag3672.d(56): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"-="(ptr, 1) instead. +--- +*/ +shared int x; +shared int y; +shared int* ptr; +shared static this() { ptr = new int; } // silence null-dereference errors + +void main() +{ + ++x; + x++; + --x; + x--; + x += 1; + x += 2; + x -= 3; + x |= y; + x *= y; + x /= y; + x %= y; + x &= y; + x ^= y; + x <<= y; + x >>= y; + x >>>= y; + x ^^= y; + ++ptr; + ptr++; + --ptr; + ptr--; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag3672a.d b/gcc/testsuite/gdc.test/fail_compilation/diag3672a.d new file mode 100644 index 000000000..f70e441f2 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag3672a.d @@ -0,0 +1,34 @@ +// PERMUTE_ARGS: +// REQUIRED_ARGS: -de +/* +TEST_OUTPUT: +--- +fail_compilation/diag3672a.d(16): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(ns.x, 1) instead. +fail_compilation/diag3672a.d(18): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(s.sx, 1) instead. +--- +*/ +class NS { shared int x; } +shared class S { int sx; } + +void main() +{ + NS ns = new NS; + ns.x++; + S s = new S; + s.sx++; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/diag3672a.d(32): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(s.var, 1) instead. +fail_compilation/diag3672a.d(33): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"-="(s.var, 2) instead. +--- +*/ +void test13003() +{ + struct S { int var; } + shared S s; + s.var++; + s.var -= 2; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag4479.d b/gcc/testsuite/gdc.test/fail_compilation/diag4479.d index 6c0d1c16e..553f9efdd 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag4479.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag4479.d @@ -2,7 +2,7 @@ /* TEST_OUTPUT: --- -fail_compilation/diag4479.d(10): Error: module imports.fail4479mod from file fail_compilation/imports/fail4479.d must be imported as module 'imports.fail4479mod' +fail_compilation/diag4479.d(10): Error: module imports.fail4479mod from file fail_compilation/imports/fail4479.d must be imported with 'import imports.fail4479mod;' --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag4596.d b/gcc/testsuite/gdc.test/fail_compilation/diag4596.d new file mode 100644 index 000000000..3f725de3f --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag4596.d @@ -0,0 +1,21 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag4596.d(15): Error: cannot modify 'this' reference +fail_compilation/diag4596.d(16): Error: cannot modify 'this' reference +fail_compilation/diag4596.d(18): Error: cannot modify 'super' reference +fail_compilation/diag4596.d(19): Error: cannot modify 'super' reference +--- +*/ + +class NoGo4596 +{ + void fun() + { + this = new NoGo4596; + (1?this:this) = new NoGo4596; + + super = new Object; + (1?super:super) = new Object; + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag6373.d b/gcc/testsuite/gdc.test/fail_compilation/diag6373.d index 5c9a39ecf..2384b3c42 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag6373.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag6373.d @@ -2,7 +2,7 @@ REQUIRED_ARGS: -de TEST_OUTPUT: --- -fail_compilation/diag6373.d(7): Deprecation: class diag6373.Bar use of diag6373.Foo.method(double x) hidden by Bar is deprecated. Use 'alias Foo.method method;' to introduce base class overload set. +fail_compilation/diag6373.d(7): Deprecation: class diag6373.Bar use of diag6373.Foo.method(double x) hidden by Bar is deprecated; use 'alias method = Foo.method;' to introduce base class overload set --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag6677.d b/gcc/testsuite/gdc.test/fail_compilation/diag6677.d new file mode 100644 index 000000000..06edfab8c --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag6677.d @@ -0,0 +1,29 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag6677.d(1): Error: static constructors cannot be const +fail_compilation/diag6677.d(2): Error: static constructors cannot be inout +fail_compilation/diag6677.d(3): Error: static constructors cannot be immutable +fail_compilation/diag6677.d(4): Error: to create a shared static constructor, use 'shared static this' +fail_compilation/diag6677.d(5): Error: to create a shared static constructor, use 'shared static this' +fail_compilation/diag6677.d(5): Error: static constructors cannot be const +fail_compilation/diag6677.d(7): Error: static constructors cannot be const +fail_compilation/diag6677.d(8): Error: static constructors cannot be inout +fail_compilation/diag6677.d(9): Error: static constructors cannot be immutable +fail_compilation/diag6677.d(10): Error: static constructors cannot be shared +fail_compilation/diag6677.d(11): Error: static constructors cannot be const shared +--- +*/ + +#line 1 +static this() const { } +static this() inout { } +static this() immutable { } +static this() shared { } +static this() const shared { } + +shared static this() const { } +shared static this() inout { } +shared static this() immutable { } +shared static this() shared { } +shared static this() const shared { } diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag7747.d b/gcc/testsuite/gdc.test/fail_compilation/diag7747.d new file mode 100644 index 000000000..d13b1256d --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag7747.d @@ -0,0 +1,13 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag7747.d(8): Error: forward reference to inferred return type of function call 'fact' +--- +*/ + +auto fact(int n) { return n > 1 ? fact(n - 1) : 0; } + +void main() +{ + fact(1); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8101.d b/gcc/testsuite/gdc.test/fail_compilation/diag8101.d new file mode 100644 index 000000000..acd440a06 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag8101.d @@ -0,0 +1,63 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag8101.d(56): Error: function diag8101.f_0 (int) is not callable using argument types () +fail_compilation/diag8101.d(57): Error: None of the overloads of 'f_1' are callable using argument types (), candidates are: +fail_compilation/diag8101.d(32): diag8101.f_1(int) +fail_compilation/diag8101.d(33): diag8101.f_1(int, int) +fail_compilation/diag8101.d(58): Error: None of the overloads of 'f_2' are callable using argument types (), candidates are: +fail_compilation/diag8101.d(35): diag8101.f_2(int) +fail_compilation/diag8101.d(36): diag8101.f_2(int, int) +fail_compilation/diag8101.d(37): diag8101.f_2(int, int, int) +fail_compilation/diag8101.d(38): diag8101.f_2(int, int, int, int) +fail_compilation/diag8101.d(39): diag8101.f_2(int, int, int, int, int) +fail_compilation/diag8101.d(58): ... (1 more, -v to show) ... +fail_compilation/diag8101.d(60): Error: template diag8101.t_0 cannot deduce function from argument types !()(), candidates are: +fail_compilation/diag8101.d(42): diag8101.t_0(T1)() +fail_compilation/diag8101.d(61): Error: template diag8101.t_1 cannot deduce function from argument types !()(), candidates are: +fail_compilation/diag8101.d(44): diag8101.t_1(T1)() +fail_compilation/diag8101.d(45): diag8101.t_1(T1, T2)() +fail_compilation/diag8101.d(62): Error: template diag8101.t_2 cannot deduce function from argument types !()(), candidates are: +fail_compilation/diag8101.d(47): diag8101.t_2(T1)() +fail_compilation/diag8101.d(48): diag8101.t_2(T1, T2)() +fail_compilation/diag8101.d(49): diag8101.t_2(T1, T2, T3)() +fail_compilation/diag8101.d(50): diag8101.t_2(T1, T2, T3, T4)() +fail_compilation/diag8101.d(51): diag8101.t_2(T1, T2, T3, T4, T5)() +fail_compilation/diag8101.d(62): ... (1 more, -v to show) ... +--- +*/ + +void f_0(int); + +void f_1(int); +void f_1(int, int); + +void f_2(int); +void f_2(int, int); +void f_2(int, int, int); +void f_2(int, int, int, int); +void f_2(int, int, int, int, int); +void f_2(int, int, int, int, int, int); + +void t_0(T1)(); + +void t_1(T1)(); +void t_1(T1, T2)(); + +void t_2(T1)(); +void t_2(T1, T2)(); +void t_2(T1, T2, T3)(); +void t_2(T1, T2, T3, T4)(); +void t_2(T1, T2, T3, T4, T5)(); +void t_2(T1, T2, T3, T4, T5, T6)(); + +void main() +{ + f_0(); + f_1(); + f_2(); + + t_0(); + t_1(); + t_2(); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8101b.d b/gcc/testsuite/gdc.test/fail_compilation/diag8101b.d new file mode 100644 index 000000000..74a895e45 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag8101b.d @@ -0,0 +1,34 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag8101b.d(26): Error: None of the overloads of 'foo' are callable using argument types (double), candidates are: +fail_compilation/diag8101b.d(17): diag8101b.S.foo(int _param_0) +fail_compilation/diag8101b.d(18): diag8101b.S.foo(int _param_0, int _param_1) +fail_compilation/diag8101b.d(28): Error: function diag8101b.S.bar (int _param_0) is not callable using argument types (double) +fail_compilation/diag8101b.d(31): Error: None of the overloads of 'foo' are callable using a const object, candidates are: +fail_compilation/diag8101b.d(17): diag8101b.S.foo(int _param_0) +fail_compilation/diag8101b.d(18): diag8101b.S.foo(int _param_0, int _param_1) +fail_compilation/diag8101b.d(33): Error: mutable method diag8101b.S.bar is not callable using a const object +--- +*/ + +struct S +{ + void foo(int) { } + void foo(int, int) { } + + void bar(int) { } +} + +void main() +{ + S s; + s.foo(1.0); + + s.bar(1.0); + + const(S) cs; + cs.foo(1); + + cs.bar(1); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8178.d b/gcc/testsuite/gdc.test/fail_compilation/diag8178.d index 864388500..491a62557 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag8178.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag8178.d @@ -1,14 +1,15 @@ /* TEST_OUTPUT: --- -fail_compilation/diag8178.d(5): Error: Cannot modify 's' +fail_compilation/diag8178.d(14): Error: cannot modify manifest constant 's' --- */ -#line 1 -struct Foo { +struct Foo +{ enum string s = ""; } -void main() { +void main() +{ Foo.s = ""; } diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8648.d b/gcc/testsuite/gdc.test/fail_compilation/diag8648.d index 6f7a944d7..7f377abde 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag8648.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag8648.d @@ -1,15 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/diag8648.d(18): Error: undefined identifier X -fail_compilation/diag8648.d(29): Error: template diag8648.foo cannot deduce function from argument types !()(Foo!(int, 1)), candidates are: -fail_compilation/diag8648.d(18): diag8648.foo(T, n)(X!(T, n)) -fail_compilation/diag8648.d(20): Error: undefined identifier a -fail_compilation/diag8648.d(31): Error: template diag8648.bar cannot deduce function from argument types !()(Foo!(int, 1)), candidates are: -fail_compilation/diag8648.d(20): diag8648.bar(T)(Foo!(T, a)) -fail_compilation/diag8648.d(20): Error: undefined identifier a -fail_compilation/diag8648.d(32): Error: template diag8648.bar cannot deduce function from argument types !()(Foo!(int, f)), candidates are: -fail_compilation/diag8648.d(20): diag8648.bar(T)(Foo!(T, a)) +fail_compilation/diag8648.d(12): Error: undefined identifier X +fail_compilation/diag8648.d(14): Error: undefined identifier a +fail_compilation/diag8648.d(14): Error: undefined identifier a --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8777.d b/gcc/testsuite/gdc.test/fail_compilation/diag8777.d new file mode 100644 index 000000000..f289da92a --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag8777.d @@ -0,0 +1,44 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag8777.d(12): Error: constructor diag8777.Foo1.this missing initializer for immutable field x +fail_compilation/diag8777.d(12): Error: constructor diag8777.Foo1.this missing initializer for const field y +--- +*/ +class Foo1 +{ + immutable int[5] x; + const int[5] y; + this() {} +} + +/* +TEST_OUTPUT: +--- +fail_compilation/diag8777.d(25): Error: cannot modify immutable expression x +fail_compilation/diag8777.d(28): Error: cannot modify const expression y +--- +*/ +void test2() +{ + immutable int x; + x = 1; + + const int y; + y = 1; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/diag8777.d(42): Error: cannot remove key from immutable associative array hashx +fail_compilation/diag8777.d(43): Error: cannot remove key from const associative array hashy +--- +*/ +immutable(int[int]) hashx; +const(int[int]) hashy; +void test3() +{ + hashx.remove(1); + hashy.remove(1); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8777a.d b/gcc/testsuite/gdc.test/fail_compilation/diag8777a.d deleted file mode 100644 index 3b481eb52..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/diag8777a.d +++ /dev/null @@ -1,12 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/diag8777a.d(3): Error: constructor diag8777a.Foo.this missing initializer for immutable field bar ---- -*/ - -#line 1 -class Foo { - immutable int[5] bar; - this() {} -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8777b.d b/gcc/testsuite/gdc.test/fail_compilation/diag8777b.d deleted file mode 100644 index 3c857bea8..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/diag8777b.d +++ /dev/null @@ -1,12 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/diag8777b.d(3): Error: constructor diag8777b.Foo.this missing initializer for const field bar ---- -*/ - -#line 1 -class Foo { - const int[5] bar; - this() {} -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8777c.d b/gcc/testsuite/gdc.test/fail_compilation/diag8777c.d deleted file mode 100644 index eca43fca4..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/diag8777c.d +++ /dev/null @@ -1,13 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/diag8777c.d(4): Error: cannot modify const expression x ---- -*/ - -#line 1 -void test() -{ - const int x; - x = 1; -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8777d.d b/gcc/testsuite/gdc.test/fail_compilation/diag8777d.d deleted file mode 100644 index d7fb2b039..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/diag8777d.d +++ /dev/null @@ -1,13 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/diag8777d.d(4): Error: cannot modify immutable expression x ---- -*/ - -#line 1 -void test() -{ - immutable int x; - x = 1; -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8777e.d b/gcc/testsuite/gdc.test/fail_compilation/diag8777e.d deleted file mode 100644 index 25225b41d..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/diag8777e.d +++ /dev/null @@ -1,14 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/diag8777e.d(5): Error: cannot remove key from immutable associative array hash ---- -*/ - -#line 1 -immutable(int[int]) hash; - -void main() -{ - hash.remove(1); -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag8777f.d b/gcc/testsuite/gdc.test/fail_compilation/diag8777f.d deleted file mode 100644 index 74130c478..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/diag8777f.d +++ /dev/null @@ -1,14 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/diag8777f.d(5): Error: cannot remove key from const associative array hash ---- -*/ - -#line 1 -const(int[int]) hash; - -void main() -{ - hash.remove(1); -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9191.d b/gcc/testsuite/gdc.test/fail_compilation/diag9191.d index 0b2cf0dbb..50e5445d3 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag9191.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag9191.d @@ -2,7 +2,7 @@ TEST_OUTPUT: --- fail_compilation/diag9191.d(16): Error: function diag9191.C1.aaa does not override any function, did you mean to override 'diag9191.B1.aa'? -fail_compilation/diag9191.d(21): Error: function diag9191.C2.aaa does not override any function, did you mean to override 'diag9191.I1.a'? +fail_compilation/diag9191.d(21): Error: function diag9191.C2.aaa does not override any function fail_compilation/diag9191.d(31): Error: function diag9191.C3.foo does not override any function, did you mean to override 'diag9191.B2._foo'? fail_compilation/diag9191.d(36): Error: function diag9191.C4.toStringa does not override any function, did you mean to override 'object.Object.toString'? --- diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9247.d b/gcc/testsuite/gdc.test/fail_compilation/diag9247.d index 65373d696..e22634559 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag9247.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag9247.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/diag9247.d(11): Error: cannot return opaque struct S by value -fail_compilation/diag9247.d(12): Error: cannot return opaque struct S by value +fail_compilation/diag9247.d(11): Error: functions cannot return opaque type S by value +fail_compilation/diag9247.d(12): Error: functions cannot return opaque type S by value --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag9635.d b/gcc/testsuite/gdc.test/fail_compilation/diag9635.d index 421bf6ade..0e15aa2b9 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/diag9635.d +++ b/gcc/testsuite/gdc.test/fail_compilation/diag9635.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/diag9635.d(17): Error: need 'this' for 'i' of type 'int' -fail_compilation/diag9635.d(18): Error: need 'this' for 'foo' of type 'pure nothrow @safe void()' +fail_compilation/diag9635.d(18): Error: need 'this' for 'foo' of type 'pure nothrow @nogc @safe void()' --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/diag_err1.d b/gcc/testsuite/gdc.test/fail_compilation/diag_err1.d new file mode 100644 index 000000000..c4eb7fd2c --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/diag_err1.d @@ -0,0 +1,25 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/diag_err1.d(21): Error: undefined identifier x +fail_compilation/diag_err1.d(21): while evaluating pragma(msg, [1, 2, x].length) +fail_compilation/diag_err1.d(22): Error: undefined identifier x +fail_compilation/diag_err1.d(22): Error: undefined identifier y +fail_compilation/diag_err1.d(22): while evaluating pragma(msg, (x + y).sizeof) +fail_compilation/diag_err1.d(23): Error: undefined identifier x +fail_compilation/diag_err1.d(23): while evaluating pragma(msg, (n += x).sizeof) +fail_compilation/diag_err1.d(24): Error: incompatible types for ((s) ~ (n)): 'string' and 'int' +fail_compilation/diag_err1.d(24): while evaluating pragma(msg, (s ~ n).sizeof) +--- +*/ + +void main() +{ + int n; + string s; + + pragma(msg, [1,2,x].length); + pragma(msg, (x + y).sizeof); + pragma(msg, (n += x).sizeof); + pragma(msg, (s ~ n).sizeof); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/extra-files/bar11453.d b/gcc/testsuite/gdc.test/fail_compilation/extra-files/bar11453.d new file mode 100644 index 000000000..126e0f9c7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/extra-files/bar11453.d @@ -0,0 +1 @@ +module foo11453.bar11453; diff --git a/gcc/testsuite/gdc.test/fail_compilation/extra-files/foo11453.d b/gcc/testsuite/gdc.test/fail_compilation/extra-files/foo11453.d new file mode 100644 index 000000000..c97e3e1ed --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/extra-files/foo11453.d @@ -0,0 +1 @@ +module foo11453; diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10115.d b/gcc/testsuite/gdc.test/fail_compilation/fail10115.d index 4eb29da2d..a2c966fc7 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail10115.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail10115.d @@ -1,9 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/fail10115.d(35): Error: cannot have out parameter of type S because the default construction is disbaled -fail_compilation/fail10115.d(35): Error: cannot have out parameter of type E because the default construction is disbaled -fail_compilation/fail10115.d(35): Error: cannot have out parameter of type U because the default construction is disbaled +fail_compilation/fail10115.d(35): Error: cannot have out parameter of type S because the default construction is disabled +fail_compilation/fail10115.d(35): Error: cannot have out parameter of type E because the default construction is disabled +fail_compilation/fail10115.d(35): Error: cannot have out parameter of type U because the default construction is disabled fail_compilation/fail10115.d(40): Error: struct fail10115.S default construction is disabled fail_compilation/fail10115.d(41): Error: struct fail10115.S default construction is disabled fail_compilation/fail10115.d(42): Error: union fail10115.U default construction is disabled diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail102.d b/gcc/testsuite/gdc.test/fail_compilation/fail102.d deleted file mode 100644 index 530503adc..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail102.d +++ /dev/null @@ -1,25 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail102.d(11): Error: return statements cannot be in finally, scope(exit) or scope(success) bodies -fail_compilation/fail102.d(11): Error: cannot return non-void from void function ---- -*/ -void foo1() -{ - scope(exit) - return 0; -} - -/* -TEST_OUTPUT: ---- -fail_compilation/fail102.d(24): Error: return statements cannot be in finally, scope(exit) or scope(success) bodies -fail_compilation/fail102.d(24): Error: cannot return non-void from void function ---- -*/ -void foo2() -{ - scope(success) - return 0; -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10277.d b/gcc/testsuite/gdc.test/fail_compilation/fail10277.d index ca8d98ff0..6173d37c2 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail10277.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail10277.d @@ -7,7 +7,6 @@ fail_compilation/imports/fail10277.d(3): Error: class TypeInfo only object.d can fail_compilation/imports/fail10277.d(4): Error: class TypeInfo_Class only object.d can define this reserved class name fail_compilation/imports/fail10277.d(5): Error: class TypeInfo_Interface only object.d can define this reserved class name fail_compilation/imports/fail10277.d(6): Error: class TypeInfo_Struct only object.d can define this reserved class name -fail_compilation/imports/fail10277.d(7): Error: class TypeInfo_Typedef only object.d can define this reserved class name fail_compilation/imports/fail10277.d(8): Error: class TypeInfo_Pointer only object.d can define this reserved class name fail_compilation/imports/fail10277.d(9): Error: class TypeInfo_Array only object.d can define this reserved class name fail_compilation/imports/fail10277.d(10): Error: class TypeInfo_AssociativeArray only object.d can define this reserved class name diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10285.d b/gcc/testsuite/gdc.test/fail_compilation/fail10285.d new file mode 100644 index 000000000..c94ed72eb --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail10285.d @@ -0,0 +1,10 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail10285.d(9): Error: no identifier for declarator int +--- +*/ +enum +{ + int = 5 +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10346.d b/gcc/testsuite/gdc.test/fail_compilation/fail10346.d index df18d7ac0..dea05d69f 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail10346.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail10346.d @@ -1,9 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail10346.d(11): Error: undefined identifier T -fail_compilation/fail10346.d(15): Error: template fail10346.bar cannot deduce function from argument types !(10)(Foo!int), candidates are: -fail_compilation/fail10346.d(11): fail10346.bar(T x, T)(Foo!T) +fail_compilation/fail10346.d(9): Error: undefined identifier T --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10481.d b/gcc/testsuite/gdc.test/fail_compilation/fail10481.d index 283becb5b..a1ba0e5f8 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail10481.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail10481.d @@ -1,8 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail10481.d(11): Error: undefined identifier T1, did you mean alias T0? -fail_compilation/fail10481.d(15): Error: cannot resolve type for get!(A) +fail_compilation/fail10481.d(10): Error: undefined identifier T1, did you mean alias T0? --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail10630.d b/gcc/testsuite/gdc.test/fail_compilation/fail10630.d index 289596126..738bdd14e 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail10630.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail10630.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail10630.d(12): Error: cannot have out parameter of type S because the default construction is disbaled +fail_compilation/fail10630.d(12): Error: cannot have out parameter of type S because the default construction is disabled --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail108.d b/gcc/testsuite/gdc.test/fail_compilation/fail108.d index 3bd408211..4bf8b036c 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail108.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail108.d @@ -2,7 +2,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail108.d(14): Error: typedef test1.foo circular definition +fail_compilation/fail108.d(14): Error: use alias instead of typedef +fail_compilation/fail108.d(15): Error: use alias instead of typedef --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail112.d b/gcc/testsuite/gdc.test/fail_compilation/fail112.d deleted file mode 100644 index 455250c73..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail112.d +++ /dev/null @@ -1,13 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail112.d(11): Error: functions cannot return a function ---- -*/ - -void func(int a) { } -//typedef int ft(int); - -typeof(func) test() -{ -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail113.d b/gcc/testsuite/gdc.test/fail_compilation/fail113.d index 508da4d96..8271b025d 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail113.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail113.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail113.d(10): Error: forward reference to test +fail_compilation/fail113.d(10): Error: forward reference to 'test' --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail114.d b/gcc/testsuite/gdc.test/fail_compilation/fail114.d index 531afbd91..5bb9cec8a 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail114.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail114.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail114.d(12): Error: forward reference to funcA +fail_compilation/fail114.d(12): Error: forward reference to 'funcA' --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11426.d b/gcc/testsuite/gdc.test/fail_compilation/fail11426.d index a3e66e6d5..64aa43c57 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail11426.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail11426.d @@ -2,7 +2,7 @@ TEST_OUTPUT: --- fail_compilation/fail11426.d(15): Error: cannot implicitly convert expression (udarr) of type uint[] to int[] -fail_compilation/fail11426.d(16): Error: cannot implicitly convert expression (usarr[]) of type uint[] to int[] +fail_compilation/fail11426.d(16): Error: cannot implicitly convert expression (usarr) of type uint[1] to int[] fail_compilation/fail11426.d(18): Error: cannot implicitly convert expression (udarr) of type uint[] to int[] fail_compilation/fail11426.d(19): Error: cannot implicitly convert expression (usarr) of type uint[1] to int[] --- diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11453a.d b/gcc/testsuite/gdc.test/fail_compilation/fail11453a.d new file mode 100644 index 000000000..488bc6e0f --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail11453a.d @@ -0,0 +1,10 @@ +// REQUIRED_ARGS: -Ifail_compilation/extra-files +// EXTRA_SOURCES: extra-files/foo11453.d extra-files/bar11453.d +/* +TEST_OUTPUT +--- +fail_compilation/extra-files/bar11453.d(1): Error: package name 'foo11453' conflicts with usage as a module name in file fail_compilation/extra-files/foo11453.d +--- +*/ + +void main() {} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11453b.d b/gcc/testsuite/gdc.test/fail_compilation/fail11453b.d new file mode 100644 index 000000000..c7bdce5ef --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail11453b.d @@ -0,0 +1,10 @@ +// REQUIRED_ARGS: -Ifail_compilation/extra-files +// EXTRA_SOURCES: extra-files/bar11453.d extra-files/foo11453.d +/* +TEST_OUTPUT +--- +fail_compilation/extra-files/foo11453.d(1): Error: module foo11453 from file fail_compilation/extra-files/foo11453.d conflicts with package name foo11453 +--- +*/ + +void main() {} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11503a.d b/gcc/testsuite/gdc.test/fail_compilation/fail11503a.d new file mode 100644 index 000000000..28f7befdf --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail11503a.d @@ -0,0 +1,21 @@ +struct S +{ + immutable(S)* s; + this(int) immutable pure + { + s = &this; + } + int data; +} + +immutable(S)* makes() pure +{ + return new immutable S(0); +} + +void main() +{ + S* s = makes(); // s is mutable and contains an immutable reference to itself + //s.s.data = 7; // this is immutable + s.data = 3; // but this is not!!! +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11503b.d b/gcc/testsuite/gdc.test/fail_compilation/fail11503b.d new file mode 100644 index 000000000..80549de8e --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail11503b.d @@ -0,0 +1,13 @@ +immutable int[] x = [1, 2, 3]; + +auto makes() pure +{ + return x; +} + +int main() +{ + auto a = x; + int[] b = makes(); + return b[1]; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11503c.d b/gcc/testsuite/gdc.test/fail_compilation/fail11503c.d new file mode 100644 index 000000000..dc45eefc0 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail11503c.d @@ -0,0 +1,15 @@ +struct Data +{ + char[256] buffer; + @property const(char)[] filename() const pure nothrow + { + return buffer[]; + } +} + +void main() +{ + Data d; + string f = d.filename; + d.buffer[0] = 'a'; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11503d.d b/gcc/testsuite/gdc.test/fail_compilation/fail11503d.d new file mode 100644 index 000000000..d96e2a8b2 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail11503d.d @@ -0,0 +1,22 @@ +struct Data2 +{ + char buffer; +} + +@property const(char)[] filename(const ref Data2 d) pure nothrow +{ + return (&d.buffer)[0 .. 1]; +} + +@property const(char)[] filename2(const Data2* d) pure nothrow +{ + return (&d.buffer)[0 .. 1]; +} + +void main() +{ + Data2 d; + string f = d.filename; + string g = (&d).filename2; + d.buffer = 'a'; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11542.d b/gcc/testsuite/gdc.test/fail_compilation/fail11542.d new file mode 100644 index 000000000..0550b8a98 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail11542.d @@ -0,0 +1,73 @@ +// REQUIRED_ARGS: -o- +// PERMUTE_ARGS: + +/* +TEST_OUTPUT: +--- +fail_compilation/fail11542.d(16): Error: object.Exception is thrown but not caught +fail_compilation/fail11542.d(13): Error: function 'fail11542.test_success1' is nothrow yet may throw +fail_compilation/fail11542.d(26): Error: object.Exception is thrown but not caught +fail_compilation/fail11542.d(23): Error: function 'fail11542.test_success3' is nothrow yet may throw +--- +*/ +void test_success1() nothrow +{ + scope(success) {} + throw new Exception(""); // error +} +void test_success2() nothrow +{ + scope(success) {} + throw new Error(""); // no error +} +void test_success3() nothrow +{ + scope(success) assert(0); + throw new Exception(""); // error +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail11542.d(39): Error: object.Exception is thrown but not caught +fail_compilation/fail11542.d(36): Error: function 'fail11542.test_failure1' is nothrow yet may throw +--- +*/ +void test_failure1() nothrow +{ + scope(failure) {} + throw new Exception(""); // error +} +void test_failure2() nothrow +{ + scope(failure) {} + throw new Error(""); // no error +} +void est_failure3() nothrow +{ + scope(failure) assert(0); + throw new Exception(""); // no error +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail11542.d(62): Error: object.Exception is thrown but not caught +fail_compilation/fail11542.d(59): Error: function 'fail11542.test_exit1' is nothrow yet may throw +--- +*/ +void test_exit1() nothrow +{ + scope(exit) {} + throw new Exception(""); // error +} +void test_exit2() nothrow +{ + scope(exit) {} + throw new Error(""); // no error +} +void test_exit3() nothrow +{ + scope(exit) assert(0); + throw new Exception(""); // no error +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11545.d b/gcc/testsuite/gdc.test/fail_compilation/fail11545.d new file mode 100644 index 000000000..514cb8785 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail11545.d @@ -0,0 +1,20 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail11545.d(14): Error: need 'this' for 'x' of type 'int' +fail_compilation/fail11545.d(18): Error: need 'this' for 'x' of type 'int' +--- +*/ + +class C +{ + int x = 42; + + int function() f1 = function() { + return x; + }; + + int function() f2 = { + return x; + }; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11591.d b/gcc/testsuite/gdc.test/fail_compilation/fail11591.d deleted file mode 100644 index d10fc61a4..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail11591.d +++ /dev/null @@ -1,14 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail11591.d(13): Error: associative array key type T does not have 'const int opCmp(ref const T)' member function ---- -*/ - -struct T { - bool opCmp(int i) { return false; } -} - -void main() { - int[T] aa; -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail11591b.d b/gcc/testsuite/gdc.test/fail_compilation/fail11591b.d new file mode 100644 index 000000000..42e89c964 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail11591b.d @@ -0,0 +1,34 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail11591b.d(16): Error: AA key type S11591 does not have 'bool opEquals(ref const S11591) const' +--- +*/ + +struct S11591 +{ + bool opEquals(int i) { return false; } + Object o; // needed to suppress compiler generated opEquals +} + +void test11591() +{ + int[S11591] aa; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail11591b.d(30): Error: AA key type S12307a does not have 'bool opEquals(ref const S12307a) const' +fail_compilation/fail11591b.d(31): Error: AA key type S12307b does not have 'bool opEquals(ref const S12307b) const' +--- +*/ +struct S12307a { bool opEquals(T : typeof(this))(T) { return false; } } + +void test12307() +{ + int[S12307a] aa1; // a + int[S12307b] aa2; // b +} + +struct S12307b { bool opEquals(T : typeof(this))(T) { return false; } } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail120.d b/gcc/testsuite/gdc.test/fail_compilation/fail120.d index 625e0f2b2..ae0f5b109 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail120.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail120.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail120.d(12): Error: delegate fail120.Foo.__lambda4 function literals cannot be class members -fail_compilation/fail120.d(13): Error: delegate fail120.Foo.__lambda5 function literals cannot be class members +fail_compilation/fail120.d(12): Error: need 'this' for 'nodes' of type 'int[2]' +fail_compilation/fail120.d(13): Error: need 'this' for 'nodes' of type 'int[2]' --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12047.d b/gcc/testsuite/gdc.test/fail_compilation/fail12047.d index 592dd729b..980c996a7 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail12047.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12047.d @@ -18,4 +18,4 @@ fail_compilation/fail12047.d(21): Error: undefined identifier asdf @asdf struct S {} @asdf class C {} @asdf interface I {} -@asdf typedef int myint; +@asdf alias int myint; diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12236.d b/gcc/testsuite/gdc.test/fail_compilation/fail12236.d new file mode 100644 index 000000000..1482a3a6d --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12236.d @@ -0,0 +1,33 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail12236.d(16): Error: forward reference to inferred return type of function call 'f1' +fail_compilation/fail12236.d(16): while evaluating pragma(msg, f1.mangleof) +fail_compilation/fail12236.d(21): Error: forward reference to f2 +fail_compilation/fail12236.d(21): while evaluating pragma(msg, f2(T)(T).mangleof) +fail_compilation/fail12236.d(27): Error: template instance fail12236.f2!int error instantiating +fail_compilation/fail12236.d(31): Error: forward reference to __lambda1 +fail_compilation/fail12236.d(31): while evaluating pragma(msg, __lambda1.mangleof) +--- +*/ + +auto f1(int) +{ + pragma(msg, f1.mangleof); // forward reference error +} + +auto f2(T)(T) +{ + pragma(msg, f2.mangleof); // error <- weird output: "v" +} + +void main() +{ + f1(1); + f2(1); + + (a) { + int x; + pragma(msg, __traits(parent, x).mangleof); + } (1); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12255.d b/gcc/testsuite/gdc.test/fail_compilation/fail12255.d new file mode 100644 index 000000000..4531e86e8 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12255.d @@ -0,0 +1,139 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail12255.d(29): Error: AA key type SC1 does not have 'bool opEquals(ref const SC1) const' +fail_compilation/fail12255.d(30): Error: AA key type SC2 does not support const equality +fail_compilation/fail12255.d(35): Error: AA key type SD1 should have 'size_t toHash() const nothrow @safe' if opEquals defined +fail_compilation/fail12255.d(36): Error: AA key type SD2 supports const equality but doesn't support const hashing +fail_compilation/fail12255.d(40): Error: AA key type SE1 should have 'size_t toHash() const nothrow @safe' if opEquals defined +fail_compilation/fail12255.d(41): Error: AA key type SE2 supports const equality but doesn't support const hashing +--- +*/ + +void main() +{ + /* Key comparison and hashing are based on object bit representation, + * and they fully supported in runtime (TypeInfo.equals and TypeInfo.getHash) + */ + int[SA1] a1; // OK + int[SA2] a2; // OK + + /* If only toHash is defined, AA assumes that is customized object hashing. + */ + int[SB1] b1; // OK + int[SB2] b2; // OK + + /* If key does not support const equality, + * it is disallowed, because TypeInfo.equals will throw Error. + */ + int[SC1] c1; // NG + int[SC2] c2; // NG + + /* If opEquals defined for const equality, corresponding toHash method + * is required to guarantee (a != b || a.toHash() == b.toHash()). + */ + int[SD1] d1; // NG + int[SD2] d2; // NG + + /* same as SD cases + */ + int[SE1] e1; // NG + int[SE2] e2; // NG +} + +struct SA1 { int val; } +struct SA2 { SA1 s; } + +struct SB1 +{ + // AA assumes this is specialized hashing (?) + size_t toHash() const nothrow @safe { return 0; } +} +struct SB2 +{ + SB1 s; + // implicit generated toHash() calls s.toHash(). +} + +struct SC1 +{ + // does not support const equality + bool opEquals(typeof(this)) /*const*/ { return true; } +} +struct SC2 +{ + SC1 s; +} + +struct SD1 +{ + // Supports const equality, but + // does not have corresponding toHash() + bool opEquals(typeof(this)) const { return true; } +} +struct SD2 +{ + SD1 s; +} + +struct SE1 +{ + // Supports const equality, but + // does not have corresponding valid toHash() + bool opEquals(typeof(this)) const { return true; } + size_t toHash() @system { return 0; } +} +struct SE2 +{ + SE1 s; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail12255.d(108): Error: bottom of AA key type SC1 does not have 'bool opEquals(ref const SC1) const' +fail_compilation/fail12255.d(109): Error: bottom of AA key type SC2 does not support const equality +fail_compilation/fail12255.d(110): Error: bottom of AA key type SD1 should have 'size_t toHash() const nothrow @safe' if opEquals defined +fail_compilation/fail12255.d(111): Error: bottom of AA key type SD2 supports const equality but doesn't support const hashing +fail_compilation/fail12255.d(112): Error: bottom of AA key type SE1 should have 'size_t toHash() const nothrow @safe' if opEquals defined +fail_compilation/fail12255.d(113): Error: bottom of AA key type SE2 supports const equality but doesn't support const hashing +--- +*/ +void testSArray() +{ + int[SA1[1]] a1; // OK + int[SA2[1]] a2; // OK + int[SB1[1]] b1; // OK + int[SB2[1]] b2; // OK + int[SC1[1]] c1; // NG + int[SC2[1]] c2; // NG + int[SD1[1]] d1; // NG + int[SD2[1]] d2; // NG + int[SE1[1]] e1; // NG + int[SE2[1]] e2; // NG +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail12255.d(133): Error: bottom of AA key type SC1 does not have 'bool opEquals(ref const SC1) const' +fail_compilation/fail12255.d(134): Error: bottom of AA key type SC2 does not support const equality +fail_compilation/fail12255.d(135): Error: bottom of AA key type SD1 should have 'size_t toHash() const nothrow @safe' if opEquals defined +fail_compilation/fail12255.d(136): Error: bottom of AA key type SD2 supports const equality but doesn't support const hashing +fail_compilation/fail12255.d(137): Error: bottom of AA key type SE1 should have 'size_t toHash() const nothrow @safe' if opEquals defined +fail_compilation/fail12255.d(138): Error: bottom of AA key type SE2 supports const equality but doesn't support const hashing +--- +*/ +void testDArray() +{ + int[SA1[]] a1; // OK + int[SA2[]] a2; // OK + int[SB1[]] b1; // OK + int[SB2[]] b2; // OK + int[SC1[]] c1; // NG + int[SC2[]] c2; // NG + int[SD1[]] d1; // NG + int[SD2[]] d2; // NG + int[SE1[]] e1; // NG + int[SE2[]] e2; // NG +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12378.d b/gcc/testsuite/gdc.test/fail_compilation/fail12378.d new file mode 100644 index 000000000..d42160924 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12378.d @@ -0,0 +1,143 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail12378.d(18): Error: undefined identifier ANYTHING +fail_compilation/fail12378.d(18): Error: undefined identifier GOES +fail_compilation/fail12378.d(89): instantiated from here: MapResultS!((x0) => ANYTHING - GOES, Result) +fail_compilation/fail12378.d(17): instantiated from here: mapS!(Result) +fail_compilation/fail12378.d(98): instantiated from here: __lambda1!int +fail_compilation/fail12378.d(89): instantiated from here: MapResultS!((y0) => iota(2).mapS!((x0) => ANYTHING - GOES), Result) +fail_compilation/fail12378.d(16): instantiated from here: mapS!(Result) +--- +*/ +void testS() +{ + auto r = + iota(1).mapS!(y0 => + iota(2).mapS!(x0 => + ANYTHING-GOES + ) + ); +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail12378.d(40): Error: undefined identifier ANYTHING +fail_compilation/fail12378.d(40): Error: undefined identifier GOES +fail_compilation/fail12378.d(110): instantiated from here: MapResultC!((x0) => ANYTHING - GOES, Result) +fail_compilation/fail12378.d(39): instantiated from here: mapC!(Result) +fail_compilation/fail12378.d(121): instantiated from here: __lambda1!int +fail_compilation/fail12378.d(110): instantiated from here: MapResultC!((y0) => iota(2).mapC!((x0) => ANYTHING - GOES), Result) +fail_compilation/fail12378.d(38): instantiated from here: mapC!(Result) +--- +*/ +void testC() +{ + auto r = + iota(1).mapC!(y0 => + iota(2).mapC!(x0 => + ANYTHING-GOES + ) + ); +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail12378.d(62): Error: undefined identifier ANYTHING +fail_compilation/fail12378.d(62): Error: undefined identifier GOES +fail_compilation/fail12378.d(133): instantiated from here: MapResultI!((x0) => ANYTHING - GOES, Result) +fail_compilation/fail12378.d(61): instantiated from here: mapI!(Result) +fail_compilation/fail12378.d(141): instantiated from here: __lambda1!int +fail_compilation/fail12378.d(133): instantiated from here: MapResultI!((y0) => iota(2).mapI!((x0) => ANYTHING - GOES), Result) +fail_compilation/fail12378.d(60): instantiated from here: mapI!(Result) +--- +*/ +void testI() +{ + auto r = + iota(1).mapI!(y0 => + iota(2).mapI!(x0 => + ANYTHING-GOES + ) + ); +} + +auto iota(E)(E end) +{ + alias Value = E; + + static struct Result + { + private Value current, pastLast; + + @property inout(Value) front() inout { return current; } + } + + return Result(0, end); +} + +template mapS(fun...) +{ + auto mapS(R)(R r) + { + alias AppliedReturnType(alias f) = typeof(f(r.front)); + static assert(!is(AppliedReturnType!fun == void), + "Mapping function must not return void."); + + return MapResultS!(fun, R)(r); + } +} +struct MapResultS(alias fun, R) +{ + R _input; + + @property auto ref front() + { + return fun(_input.front); + } +} + +template mapC(fun...) +{ + auto mapC(R)(R r) + { + alias AppliedReturnType(alias f) = typeof(f(r.front)); + static assert(!is(AppliedReturnType!fun == void), + "Mapping function must not return void."); + + return new MapResultC!(fun, R)(r); + } +} +class MapResultC(alias fun, R) +{ + R _input; + + this(R r) { _input = r; } + + @property auto ref front() + { + return fun(_input.front); + } +} + +template mapI(fun...) +{ + auto mapI(R)(R r) + { + alias AppliedReturnType(alias f) = typeof(f(r.front)); + static assert(!is(AppliedReturnType!fun == void), + "Mapping function must not return void."); + + return MapResultI!(fun, R).init; + } +} +interface MapResultI(alias fun, R) +{ + static @property auto ref front() + { + R _input; + return fun(_input.front); + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12390.d b/gcc/testsuite/gdc.test/fail_compilation/fail12390.d new file mode 100644 index 000000000..f0479b9be --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12390.d @@ -0,0 +1,15 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail12390.d(14): Error: == has no effect in expression (fun().i == 4) +--- +*/ + +struct S { int i; } + +S fun() { return S(42); } + +void main() +{ + fun().i == 4; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12436.d b/gcc/testsuite/gdc.test/fail_compilation/fail12436.d new file mode 100644 index 000000000..53e55d0c5 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12436.d @@ -0,0 +1,64 @@ +alias void FuncType(); + +struct Opaque; + +template Tuple(T...) { alias T Tuple; } +alias Tuple!(int, int) TupleType; + +/******************************************/ +// return type + +/* +TEST_OUTPUT: +--- +fail_compilation/fail12436.d(18): Error: functions cannot return a function +fail_compilation/fail12436.d(19): Error: functions cannot return a tuple +--- +*/ +FuncType test1(); +TupleType test2(); + +/* +TEST_OUTPUT: +--- +fail_compilation/fail12436.d(28): Error: functions cannot return opaque type Opaque by value +fail_compilation/fail12436.d(29): Error: functions cannot return opaque type Opaque[1] by value +--- +*/ +Opaque ret12436a(); // error +Opaque[1] ret12436b(); // error +Opaque* ret12436c(); // no error +Opaque[] ret12436d(); // no error +Opaque[]* ret12436e(); // no error + +ref Opaque ret12436f(); // no error +ref Opaque[1] ret12436g(); // no error + +/******************************************/ +// parameter type + +/* +TEST_OUTPUT: +--- +fail_compilation/fail12436.d(46): Error: cannot have parameter of function type void() +--- +*/ +void test3(FuncType) {} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail12436.d(55): Error: cannot have parameter of opaque type Opaque by value +fail_compilation/fail12436.d(56): Error: cannot have parameter of opaque type Opaque[1] by value +--- +*/ +void param12436a(Opaque); // error +void param12436b(Opaque[1]); // error +void param12436c(Opaque*); // no error +void param12436d(Opaque[]); // no error +void param12436e(Opaque[]*); // no error + +void param12436f(ref Opaque); // no error +void param12436g(ref Opaque[1]); // no error +void param12436h(out Opaque); // no error +void param12436i(out Opaque[1]); // no error diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12485.d b/gcc/testsuite/gdc.test/fail_compilation/fail12485.d new file mode 100644 index 000000000..71f8698db --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12485.d @@ -0,0 +1,11 @@ +void dorecursive() +{ + recursive([0]); +} + +void recursive(R)(R r) +{ + import std.algorithm; + recursive( r.filter!(e=>true) ); +} + diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail125.d b/gcc/testsuite/gdc.test/fail_compilation/fail125.d index 66d00f93c..0e0f5ea8a 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail125.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail125.d @@ -1,14 +1,13 @@ /* TEST_OUTPUT: --- -fail_compilation/fail125.d(16): Error: array index [2] is outside array bounds [0 .. 2] -fail_compilation/fail125.d(16): Error: cannot implicitly convert expression (tuple(a, b)) of type (int, int) to int -fail_compilation/fail125.d(19): Error: template instance fail125.main.recMove!(1, a, b) error instantiating -fail_compilation/fail125.d(26): instantiated from here: recMove!(0, a, b) -fail_compilation/fail125.d(26): Error: template instance fail125.main.recMove!(0, a, b) error instantiating +fail_compilation/fail125.d(15): Error: array index [2] is outside array bounds [0 .. 2] +fail_compilation/fail125.d(18): Error: template instance fail125.main.recMove!(1, a, b) error instantiating +fail_compilation/fail125.d(25): instantiated from here: recMove!(0, a, b) --- */ + template recMove(int i, X...) { void recMove() diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12567.d b/gcc/testsuite/gdc.test/fail_compilation/fail12567.d new file mode 100644 index 000000000..e1ecd19fe --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12567.d @@ -0,0 +1,8 @@ +// REQUIRED_ARGS: -o- +/* +TEST_OUTPUT: +--- +fail_compilation/fail12567.d(8): Error: string expected, not '"a" ~ "b"' +--- +*/ +deprecated("a" ~ "b") module fail12567; diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail126.d b/gcc/testsuite/gdc.test/fail_compilation/fail126.d index bf5d1e704..280fde90a 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail126.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail126.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail126.d(8): Error: forward reference to test +fail_compilation/fail126.d(8): Error: forward reference to 'test' --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12604.d b/gcc/testsuite/gdc.test/fail_compilation/fail12604.d new file mode 100644 index 000000000..af25db688 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12604.d @@ -0,0 +1,79 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail12604.d(14): Error: mismatched array lengths, 1 and 3 +fail_compilation/fail12604.d(15): Error: mismatched array lengths, 1 and 3 +fail_compilation/fail12604.d(17): Error: mismatched array lengths, 1 and 3 +fail_compilation/fail12604.d(18): Error: mismatched array lengths, 1 and 3 +fail_compilation/fail12604.d(20): Error: cannot implicitly convert expression ([65536]) of type int[] to short[] +fail_compilation/fail12604.d(21): Error: cannot implicitly convert expression ([65536, 2, 3]) of type int[] to short[] +--- +*/ +void main() +{ + int[1] a1 = [1,2,3]; + short[1] a2 = [1,2,3]; + + int[1] b1; b1 = [1,2,3]; + short[1] b2; b2 = [1,2,3]; + + short[1] c = [65536]; + short[1] d = [65536,2,3]; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail12604.d(39): Error: mismatched array lengths, 2 and 3 +fail_compilation/fail12604.d(40): Error: mismatched array lengths, 2 and 3 +fail_compilation/fail12604.d(41): Error: mismatched array lengths, 2 and 3 +fail_compilation/fail12604.d(42): Error: mismatched array lengths, 2 and 3 +fail_compilation/fail12604.d(43): Error: mismatched array lengths, 2 and 3 +fail_compilation/fail12604.d(44): Error: mismatched array lengths, 2 and 3 +fail_compilation/fail12604.d(45): Error: mismatched array lengths, 2 and 3 +fail_compilation/fail12604.d(46): Error: mismatched array lengths, 2 and 3 +--- +*/ +void test12606a() // AssignExp::semantic +{ + uint[2] a1 = [1, 2, 3][]; + ushort[2] a2 = [1, 2, 3][]; + uint[2] a3 = [1, 2, 3][0 .. 3]; + ushort[2] a4 = [1, 2, 3][0 .. 3]; + a1 = [1, 2, 3][]; + a2 = [1, 2, 3][]; + a3 = [1, 2, 3][0 .. 3]; + a4 = [1, 2, 3][0 .. 3]; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail12604.d(60): Error: mismatched array lengths, 2 and 3 +fail_compilation/fail12604.d(61): Error: mismatched array lengths, 2 and 3 +fail_compilation/fail12604.d(62): Error: mismatched array lengths, 2 and 3 +fail_compilation/fail12604.d(63): Error: mismatched array lengths, 2 and 3 +--- +*/ +void test12606b() // ExpInitializer::semantic +{ + static uint[2] a1 = [1, 2, 3][]; + static uint[2] a2 = [1, 2, 3][0 .. 3]; + static ushort[2] a3 = [1, 2, 3][]; + static ushort[2] a4 = [1, 2, 3][0 .. 3]; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail12604.d(77): Error: mismatched array lengths, 4 and 3 +fail_compilation/fail12604.d(78): Error: mismatched array lengths, 4 and 3 +--- +*/ +void testc() +{ + int[4] sa1; + int[3] sa2; + sa1[0..4] = [1,2,3]; + sa1[0..4] = sa2; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12622.d b/gcc/testsuite/gdc.test/fail_compilation/fail12622.d new file mode 100644 index 000000000..c69b77b83 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12622.d @@ -0,0 +1,30 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail12622.d(25): Error: pure function 'fail12622.foo' cannot call impure function pointer 'fp' +fail_compilation/fail12622.d(25): Error: @nogc function 'fail12622.foo' cannot call non-@nogc function pointer 'fp' +fail_compilation/fail12622.d(25): Error: safe function 'fail12622.foo' cannot call system function pointer 'fp' +fail_compilation/fail12622.d(27): Error: pure function 'fail12622.foo' cannot call impure function pointer 'fp' +fail_compilation/fail12622.d(27): Error: @nogc function 'fail12622.foo' cannot call non-@nogc function pointer 'fp' +fail_compilation/fail12622.d(27): Error: safe function 'fail12622.foo' cannot call system function pointer 'fp' +fail_compilation/fail12622.d(29): Error: pure function 'fail12622.foo' cannot call impure function 'fail12622.bar' +fail_compilation/fail12622.d(29): Error: safe function 'fail12622.foo' cannot call system function 'fail12622.bar' +fail_compilation/fail12622.d(29): Error: @nogc function 'fail12622.foo' cannot call non-@nogc function 'fail12622.bar' +--- +*/ +// Note that, today nothrow violation errors are accidentally hidden. + + + +void bar(); + +pure nothrow @nogc @safe void foo() +{ + auto fp = &bar; + + (*fp)(); + + fp(); + + bar(); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12749.d b/gcc/testsuite/gdc.test/fail_compilation/fail12749.d new file mode 100644 index 000000000..149d12098 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12749.d @@ -0,0 +1,62 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail12749.d(19): Error: immutable field 'inum' initialization is not allowed in foreach loop +fail_compilation/fail12749.d(20): Error: const field 'cnum' initialization is not allowed in foreach loop +fail_compilation/fail12749.d(25): Error: immutable field 'inum' initialization is not allowed in nested function 'set' +fail_compilation/fail12749.d(26): Error: const field 'cnum' initialization is not allowed in nested function 'set' +--- +*/ +struct S +{ + immutable int inum; + const int cnum; + + this(int i) + { + foreach (n; Aggr()) + { + inum = i; + cnum = i; + } + + void set(int i) + { + inum = i; + cnum = i; + } + } +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail12749.d(48): Error: immutable variable 'inum' initialization is not allowed in foreach loop +fail_compilation/fail12749.d(49): Error: const variable 'cnum' initialization is not allowed in foreach loop +fail_compilation/fail12749.d(54): Error: immutable variable 'inum' initialization is not allowed in nested function 'set' +fail_compilation/fail12749.d(55): Error: const variable 'cnum' initialization is not allowed in nested function 'set' +--- +*/ +immutable int inum; +const int cnum; +static this() +{ + int i = 10; + + foreach (n; Aggr()) + { + inum = i; + cnum = i; + } + + void set(int i) + { + inum = i; + cnum = i; + } +} + +struct Aggr +{ + int opApply(int delegate(int) dg) { return dg(1); } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail128.d b/gcc/testsuite/gdc.test/fail_compilation/fail128.d deleted file mode 100644 index 3eb455e61..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail128.d +++ /dev/null @@ -1,8 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail128.d(8): Error: arithmetic/string type expected for value-parameter, not void* ---- -*/ - -template T(void *p) {} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12809.d b/gcc/testsuite/gdc.test/fail_compilation/fail12809.d new file mode 100644 index 000000000..bdd02c211 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12809.d @@ -0,0 +1,80 @@ +// REQUIRED_ARGS: -o- +// PERMUTE_ARGS: + +bool cond; + +/* +TEST_OUTPUT: +--- +fail_compilation/fail12809.d(19): Error: object.Exception is thrown but not caught +fail_compilation/fail12809.d(16): Error: function 'fail12809.test_finally1' is nothrow yet may throw +fail_compilation/fail12809.d(35): Error: object.Exception is thrown but not caught +fail_compilation/fail12809.d(39): Error: object.Exception is thrown but not caught +fail_compilation/fail12809.d(32): Error: function 'fail12809.test_finally3' is nothrow yet may throw +--- +*/ +void test_finally1() nothrow +{ + try + throw new Exception(""); // error + finally + {} +} + +void test_finally2() nothrow +{ + try + throw new Exception(""); // no error + finally + assert(0); // unconditional halt +} + +void test_finally3() nothrow +{ + try + throw new Exception(""); // error + finally + { + if (cond) + throw new Exception(""); // error + assert(0); // conditional halt + } +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail12809.d(59): Error: object.Exception is thrown but not caught +fail_compilation/fail12809.d(54): Error: function 'fail12809.test_finally4' is nothrow yet may throw +fail_compilation/fail12809.d(75): Error: object.Exception is thrown but not caught +fail_compilation/fail12809.d(79): Error: object.Exception is thrown but not caught +fail_compilation/fail12809.d(70): Error: function 'fail12809.test_finally6' is nothrow yet may throw +--- +*/ +void test_finally4() nothrow +{ + try + {} + finally + throw new Exception(""); // error +} + +void test_finally5() nothrow +{ + try + assert(0); // unconditional halt + finally + throw new Exception(""); // no error +} + +void test_finally6() nothrow +{ + try + { + if (cond) + throw new Exception(""); // error + assert(0); // conditional halt + } + finally + throw new Exception(""); // error +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12901.d b/gcc/testsuite/gdc.test/fail_compilation/fail12901.d new file mode 100644 index 000000000..8d62c3e04 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12901.d @@ -0,0 +1,14 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail12901.d(11): Error: constructor fail12901.S.this in and out contracts require function body +--- +*/ + +struct S +{ + int a; + this(int n) + in { a = n; } + // no body +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail12932.d b/gcc/testsuite/gdc.test/fail_compilation/fail12932.d new file mode 100644 index 000000000..cdeb1ce8b --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail12932.d @@ -0,0 +1,19 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail12932.d(11): Error: array literal in @nogc function foo may cause GC allocation +fail_compilation/fail12932.d(15): Error: array literal in @nogc function foo may cause GC allocation +--- +*/ + +int* foo() @nogc +{ + foreach (ref e; [1,2,3]) + { + } + + foreach (ref e; [1,2,3]) + { + return &e; + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail130.d b/gcc/testsuite/gdc.test/fail_compilation/fail130.d deleted file mode 100644 index 844c1838d..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail130.d +++ /dev/null @@ -1,15 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail130.d(12): Error: functions cannot return a tuple ---- -*/ - -template Tuple(T...) { alias T Tuple; } - -alias Tuple!(int,int) TType; - -TType foo() -{ - return TType; -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13187.d b/gcc/testsuite/gdc.test/fail_compilation/fail13187.d new file mode 100644 index 000000000..3799d7711 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail13187.d @@ -0,0 +1,13 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail13187.d(12): Error: pure function 'fail13187.test' cannot access mutable static data 'my_func_ptr' +--- +*/ + +int function(int) pure my_func_ptr; + +void test() pure +{ + my_func_ptr(1); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail13424.d b/gcc/testsuite/gdc.test/fail_compilation/fail13424.d new file mode 100644 index 000000000..5b703623f --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail13424.d @@ -0,0 +1,11 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail13424.d(10): Error: delegate fail13424.S.__lambda2 cannot be class members +--- +*/ + +struct S +{ + void delegate(dchar) onChar = (dchar) {}; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail138.d b/gcc/testsuite/gdc.test/fail_compilation/fail138.d deleted file mode 100644 index a8c2c9ee5..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail138.d +++ /dev/null @@ -1,16 +0,0 @@ -// REQUIRED_ARGS: -d -/* -TEST_OUTPUT: ---- -fail_compilation/fail138.d(14): Error: void initializer has no value ---- -*/ - -typedef int T = void; - -void main() -{ - T x = void; - x = x.init; -} - diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail139.d b/gcc/testsuite/gdc.test/fail_compilation/fail139.d index 215de35ad..22fc616c8 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail139.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail139.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail139.d(8): Error: forward reference to test +fail_compilation/fail139.d(8): Error: forward reference to 'test' --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail156.d b/gcc/testsuite/gdc.test/fail_compilation/fail156.d index 61c7f753b..cfd5b83a0 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail156.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail156.d @@ -7,7 +7,7 @@ fail_compilation/fail156.d(40): Error: overlapping initialization for y --- */ -typedef int myint = 4; +alias int myint; struct S { @@ -18,7 +18,7 @@ struct S int y; } int j = 3; - myint k; + myint k = 4; } void main() diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail163.d b/gcc/testsuite/gdc.test/fail_compilation/fail163.d index 92656cae5..71f75d410 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail163.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail163.d @@ -1,14 +1,90 @@ /* TEST_OUTPUT: --- -fail_compilation/fail163.d(13): Error: cannot implicitly convert expression (q) of type const(char)[] to char[] +fail_compilation/fail163.d(11): Error: cannot implicitly convert expression (q) of type const(char)[] to char[] --- */ - -void foo() +void test1() { char[] p; const(char)[] q; - p = q; } + +/* +TEST_OUTPUT: +--- +fail_compilation/fail163.d(24): Error: cannot implicitly convert expression (p) of type const(int***) to const(int)*** +--- +*/ +void test2() +{ + const int*** p; + const(int)*** cp; + cp = p; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail163.d(37): Error: cannot modify const expression p +--- +*/ +void test3() +{ + const(uint***) p; + const(int)*** cp; + p = cp; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail163.d(50): Error: cannot implicitly convert expression (cp) of type const(int)***[] to const(uint***)[] +--- +*/ +void test4() +{ + const(uint***)[] p; + const(int)***[] cp; + p = cp; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail163.d(63): Error: cannot modify const expression *p +--- +*/ +void test5() +{ + int x; + const(int)* p = &x; + *p = 3; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail163.d(76): Error: cannot implicitly convert expression (& x) of type int* to immutable(int)* +fail_compilation/fail163.d(77): Error: cannot modify immutable expression *p +--- +*/ +void test6() +{ + int x; + immutable(int)* p = &x; + *p = 3; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail163.d(89): Error: cannot implicitly convert expression (& x) of type const(int)* to int* +--- +*/ +void test7() +{ + const(int) x = 3; + int* p = &x; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail164.d b/gcc/testsuite/gdc.test/fail_compilation/fail164.d deleted file mode 100644 index 5efd4731b..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail164.d +++ /dev/null @@ -1,13 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail164.d(12): Error: cannot implicitly convert expression (p) of type const(int***) to const(int)*** ---- -*/ - -void foo() -{ - const int*** p; - const(int)*** cp; - cp = p; -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail165.d b/gcc/testsuite/gdc.test/fail_compilation/fail165.d deleted file mode 100644 index 190c0c164..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail165.d +++ /dev/null @@ -1,14 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail165.d(13): Error: cannot modify const expression p ---- -*/ - - -void foo() -{ - const(uint***) p; - const(int)*** cp; - p = cp; -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail166.d b/gcc/testsuite/gdc.test/fail_compilation/fail166.d deleted file mode 100644 index 3ee149463..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail166.d +++ /dev/null @@ -1,13 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail166.d(12): Error: cannot implicitly convert expression (cp) of type const(int)***[] to const(uint***)[] ---- -*/ - -void foo() -{ - const(uint***)[] p; - const(int)***[] cp; - p = cp; -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail167.d b/gcc/testsuite/gdc.test/fail_compilation/fail167.d deleted file mode 100644 index b99243c6e..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail167.d +++ /dev/null @@ -1,13 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail167.d(12): Error: cannot modify const expression *p ---- -*/ - -void main() -{ - int x; - const(int)* p = &x; - *p = 3; -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail168.d b/gcc/testsuite/gdc.test/fail_compilation/fail168.d deleted file mode 100644 index 04f86ae7f..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail168.d +++ /dev/null @@ -1,14 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail168.d(12): Error: cannot implicitly convert expression (& x) of type int* to immutable(int)* -fail_compilation/fail168.d(13): Error: cannot modify immutable expression *p ---- -*/ - -void main() -{ - int x; - immutable(int)* p = &x; - *p = 3; -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail171.d b/gcc/testsuite/gdc.test/fail_compilation/fail171.d deleted file mode 100644 index a305dd6b7..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail171.d +++ /dev/null @@ -1,12 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail171.d(11): Error: cannot implicitly convert expression (& x) of type const(int)* to int* ---- -*/ - -void main() -{ - const(int) x = 3; - int* p = &x; -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail172.d b/gcc/testsuite/gdc.test/fail_compilation/fail172.d index afec24808..c7ccdc16b 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail172.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail172.d @@ -1,8 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/fail172.d(17): Error: can only initialize const member x inside constructor -fail_compilation/fail172.d(20): Error: can only initialize const member x inside constructor +fail_compilation/fail172.d(25): Error: cannot modify const expression c1.x +fail_compilation/fail172.d(26): Error: cannot modify const expression c2.x +fail_compilation/fail172.d(30): Error: cannot modify const expression s1.x +fail_compilation/fail172.d(31): Error: cannot modify const expression s2.x --- */ @@ -11,11 +13,20 @@ class C int x; } +struct S +{ + int x; +} + void main() { const(C) c1 = new C(); + const C c2 = new C(); c1.x = 3; - - const C c2 = new C(); c2.x = 3; + + const(S) s1; + const S s2; + s1.x = 3; + s2.x = 3; } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail174.d b/gcc/testsuite/gdc.test/fail_compilation/fail174.d deleted file mode 100644 index 52308d786..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail174.d +++ /dev/null @@ -1,21 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail174.d(17): Error: can only initialize const member x inside constructor -fail_compilation/fail174.d(20): Error: can only initialize const member x inside constructor ---- -*/ - -struct S -{ - int x; -} - -void main() -{ - const(S) s1; - s1.x = 3; - - const S s2; - s2.x = 3; -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail176.d b/gcc/testsuite/gdc.test/fail_compilation/fail176.d index 541e31ca8..908d08cd3 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail176.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail176.d @@ -1,14 +1,20 @@ /* TEST_OUTPUT: --- -fail_compilation/fail176.d(13): Error: cannot modify immutable expression b[1] +fail_compilation/fail176.d(13): Error: cannot modify immutable expression a[1] +fail_compilation/fail176.d(16): Error: cannot modify immutable expression b[1] +fail_compilation/fail176.d(19): Error: cannot modify const expression c[1] --- */ void foo() { auto a = "abc"; + a[1] = 'd'; + immutable char[3] b = "abc"; - //const char[3] b = "abc"; b[1] = 'd'; + + const char[3] c = "abc"; + c[1] = 'd'; } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail177.d b/gcc/testsuite/gdc.test/fail_compilation/fail177.d index 5ecc3ad2b..1e1207732 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail177.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail177.d @@ -3,9 +3,9 @@ TEST_OUTPUT: --- fail_compilation/fail177.d(22): Error: cannot modify immutable expression j fail_compilation/fail177.d(24): Error: cannot modify const expression i -fail_compilation/fail177.d(26): Error: can only initialize const member x inside constructor +fail_compilation/fail177.d(26): Error: cannot modify const expression s1.x fail_compilation/fail177.d(27): Error: cannot modify const expression *s1.p -fail_compilation/fail177.d(29): Error: can only initialize const member x inside constructor +fail_compilation/fail177.d(29): Error: cannot modify const expression s2.x fail_compilation/fail177.d(30): Error: cannot modify const expression *s2.p --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail180.d b/gcc/testsuite/gdc.test/fail_compilation/fail180.d index e9bdd2b9d..574787f46 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail180.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail180.d @@ -1,10 +1,10 @@ /* TEST_OUTPUT: --- -fail_compilation/fail180.d(23): Error: can only initialize const member x inside constructor -fail_compilation/fail180.d(24): Error: can only initialize const member x inside constructor -fail_compilation/fail180.d(38): Error: can only initialize const member x inside constructor -fail_compilation/fail180.d(39): Error: can only initialize const member x inside constructor +fail_compilation/fail180.d(23): Error: cannot modify const expression this.x +fail_compilation/fail180.d(24): Error: cannot modify const expression this.x +fail_compilation/fail180.d(38): Error: cannot modify const expression this.x +fail_compilation/fail180.d(39): Error: cannot modify const expression this.x fail_compilation/fail180.d(50): Error: variable fail180.main.t final cannot be applied to variable, perhaps you meant const? fail_compilation/fail180.d(62): Error: variable fail180.test.d final cannot be applied to variable, perhaps you meant const? --- diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail187.d b/gcc/testsuite/gdc.test/fail_compilation/fail187.d index 27edd814f..b985493d5 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail187.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail187.d @@ -8,8 +8,8 @@ fail_compilation/fail187.d(16): Error: catch at fail_compilation/fail187.d(20) h // On DMD 2.000 bug only with typedef, not alias -typedef Exception A; -typedef Exception B; +alias Exception A; +alias Exception B; void main() { diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail1900.d b/gcc/testsuite/gdc.test/fail_compilation/fail1900.d index 8077272a4..cabfbdff8 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail1900.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail1900.d @@ -2,9 +2,9 @@ TEST_OUTPUT: --- fail_compilation/fail1900.d(26): Error: template fail1900.Mix1a!().Foo matches more than one template declaration: - fail_compilation/fail1900.d(13):Foo(ubyte x) +fail_compilation/fail1900.d(13): Foo(ubyte x) and - fail_compilation/fail1900.d(14):Foo(byte x) +fail_compilation/fail1900.d(14): Foo(byte x) --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail218.d b/gcc/testsuite/gdc.test/fail_compilation/fail218.d index 37c934931..e180e77c7 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail218.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail218.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail218.d(15): Error: Cannot modify '", "' +fail_compilation/fail218.d(15): Error: cannot modify string literal ", " --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail236.d b/gcc/testsuite/gdc.test/fail_compilation/fail236.d index c583dee63..5afe8acf9 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail236.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail236.d @@ -1,9 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail236.d(14): Error: undefined identifier x -fail_compilation/fail236.d(22): Error: template fail236.Templ2 cannot deduce function from argument types !()(int), candidates are: -fail_compilation/fail236.d(12): fail236.Templ2(alias a)(x) +fail_compilation/fail236.d(12): Error: undefined identifier x --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail238_m32.d b/gcc/testsuite/gdc.test/fail_compilation/fail238_m32.d index c7637dce5..5b06cf6ff 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail238_m32.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail238_m32.d @@ -2,12 +2,11 @@ /* TEST_OUTPUT: --- -fail_compilation/fail238_m32.d(22): Error: cannot implicitly convert expression ("a") of type string to uint -fail_compilation/fail238_m32.d(25): Error: Cannot interpret X!() at compile time -fail_compilation/fail238_m32.d(30): Error: template instance fail238_m32.A!"a" error instantiating -fail_compilation/fail238_m32.d(36): instantiated from here: M!(q) -fail_compilation/fail238_m32.d(36): Error: template instance fail238_m32.main.M!(q) error instantiating -fail_compilation/fail238_m32.d(36): while evaluating pragma(msg, M!(q)) +fail_compilation/fail238_m32.d(21): Error: cannot implicitly convert expression ("a") of type string to uint +fail_compilation/fail238_m32.d(24): Error: Cannot interpret X!() at compile time +fail_compilation/fail238_m32.d(29): Error: template instance fail238_m32.A!"a" error instantiating +fail_compilation/fail238_m32.d(35): instantiated from here: M!(q) +fail_compilation/fail238_m32.d(35): while evaluating pragma(msg, M!(q)) --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail238_m64.d b/gcc/testsuite/gdc.test/fail_compilation/fail238_m64.d index 927e76d7d..6d44e1b2e 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail238_m64.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail238_m64.d @@ -2,12 +2,11 @@ /* TEST_OUTPUT: --- -fail_compilation/fail238_m64.d(22): Error: cannot implicitly convert expression ("a") of type string to ulong -fail_compilation/fail238_m64.d(25): Error: Cannot interpret X!() at compile time -fail_compilation/fail238_m64.d(30): Error: template instance fail238_m64.A!"a" error instantiating -fail_compilation/fail238_m64.d(36): instantiated from here: M!(q) -fail_compilation/fail238_m64.d(36): Error: template instance fail238_m64.main.M!(q) error instantiating -fail_compilation/fail238_m64.d(36): while evaluating pragma(msg, M!(q)) +fail_compilation/fail238_m64.d(21): Error: cannot implicitly convert expression ("a") of type string to ulong +fail_compilation/fail238_m64.d(24): Error: Cannot interpret X!() at compile time +fail_compilation/fail238_m64.d(29): Error: template instance fail238_m64.A!"a" error instantiating +fail_compilation/fail238_m64.d(35): instantiated from here: M!(q) +fail_compilation/fail238_m64.d(35): while evaluating pragma(msg, M!(q)) --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail243t.d b/gcc/testsuite/gdc.test/fail_compilation/fail243t.d index ee0f22636..fb6a524af 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail243t.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail243t.d @@ -2,7 +2,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail243t.d(16): Deprecation: use of typedef is deprecated; use alias instead +fail_compilation/fail243t.d(16): Error: use alias instead of typedef --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail269.d b/gcc/testsuite/gdc.test/fail_compilation/fail269.d index dcac1cbbd..83fdefbc4 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail269.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail269.d @@ -1,7 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail269.d(11): Error: circular initialization of b +fail_compilation/fail269.d(12): Error: circular initialization of a +fail_compilation/fail269.d(19): Error: circular initialization of bug7209 --- */ @@ -15,3 +16,4 @@ else const int a = .b; const int b = .a; } +enum int bug7209 = bug7209; diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail278.d b/gcc/testsuite/gdc.test/fail_compilation/fail278.d index 16c64835a..8437acd64 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail278.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail278.d @@ -1,10 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/fail278.d(12): Error: template instance NONEXISTENT!() template 'NONEXISTENT' is not defined -fail_compilation/fail278.d(13): Error: template instance fail278.F!() error instantiating -fail_compilation/fail278.d(14): instantiated from here: Bar!(Foo) -fail_compilation/fail278.d(14): Error: template instance fail278.Bar!(Foo) error instantiating +fail_compilation/fail278.d(11): Error: template instance NONEXISTENT!() template 'NONEXISTENT' is not defined +fail_compilation/fail278.d(12): Error: template instance fail278.F!() error instantiating +fail_compilation/fail278.d(13): instantiated from here: Bar!(Foo) --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail287.d b/gcc/testsuite/gdc.test/fail_compilation/fail287.d index 0cfbf5879..da7cf5400 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail287.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail287.d @@ -2,7 +2,7 @@ TEST_OUTPUT: --- fail_compilation/fail287.d(14): Error: had 299 cases which is more than 256 cases in case range -fail_compilation/fail287.d(12): Deprecation: non-final switch statement without a default is deprecated +fail_compilation/fail287.d(12): Deprecation: switch statement without a default is deprecated; use 'final switch' or add 'default: assert(0);' or add 'default: break;' --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail303.d b/gcc/testsuite/gdc.test/fail_compilation/fail303.d index 71fea3fae..98223fa3b 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail303.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail303.d @@ -5,13 +5,13 @@ fail_compilation/fail303.d(19): Error: double /= cdouble is undefined. Did you m fail_compilation/fail303.d(20): Error: ireal *= ireal is an undefined operation fail_compilation/fail303.d(21): Error: ireal *= creal is undefined. Did you mean ireal *= creal.im ? fail_compilation/fail303.d(22): Error: ireal %= creal is undefined. Did you mean ireal %= creal.im ? -fail_compilation/fail303.d(22): Error: cannot perform modulo complex arithmetic fail_compilation/fail303.d(23): Error: ireal += real is undefined (result is complex) fail_compilation/fail303.d(24): Error: ireal -= creal is undefined (result is complex) fail_compilation/fail303.d(25): Error: double -= idouble is undefined (result is complex) --- */ + void main() { ireal x = 3.0i; diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail315.d b/gcc/testsuite/gdc.test/fail_compilation/fail315.d index a61ea908a..da9e88517 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail315.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail315.d @@ -1,11 +1,11 @@ /* TEST_OUTPUT: --- -fail_compilation/fail315.d-mixin-16(16): Error: found ';' when expecting ']' -fail_compilation/fail315.d-mixin-16(16): Error: found '}' when expecting ';' following return statement -fail_compilation/fail315.d-mixin-16(16): Error: expression expected, not ')' -fail_compilation/fail315.d-mixin-16(16): Error: found 'EOF' when expecting ')' -fail_compilation/fail315.d-mixin-16(16): Error: found 'EOF' when expecting ';' following statement +fail_compilation/fail315.d-mixin-16(16): Error: found ';' when expecting ',' +fail_compilation/fail315.d-mixin-16(16): Error: expression expected, not '}' +fail_compilation/fail315.d-mixin-16(16): Error: found 'EOF' when expecting ',' +fail_compilation/fail315.d-mixin-16(16): Error: found 'EOF' when expecting ']' +fail_compilation/fail315.d-mixin-16(16): Error: found 'EOF' when expecting ';' following return statement fail_compilation/fail315.d-mixin-16(16): Error: found 'EOF' when expecting '}' following compound statement fail_compilation/fail315.d(21): Error: template instance fail315.foo!() error instantiating --- diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail327.d b/gcc/testsuite/gdc.test/fail_compilation/fail327.d index 702b229f2..ab872357b 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail327.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail327.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail327.d(10): Error: inline assembler not allowed in @safe function foo +fail_compilation/fail327.d(10): Error: asm statement is assumed to be @system - mark it with '@trusted' if it is not --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail329.d b/gcc/testsuite/gdc.test/fail_compilation/fail329.d index 3b58b55b1..4c9321e02 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail329.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail329.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail329.d(39): Deprecation: overriding base class function without using override attribute is deprecated (fail329.B.foo overrides fail329.A.foo) +fail_compilation/fail329.d(39): Deprecation: implicitly overriding base class method fail329.A.foo with fail329.B.foo deprecated; add 'override' attribute fail_compilation/fail329.d(29): Error: variable fail329.A.foo.__ensure.result cannot modify result 'result' in contract --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail333.d b/gcc/testsuite/gdc.test/fail_compilation/fail333.d index 53d00cc12..717de24e6 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail333.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail333.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail333.d(8): Error: forward reference to test +fail_compilation/fail333.d(8): Error: forward reference to 'test' --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail352.d b/gcc/testsuite/gdc.test/fail_compilation/fail352.d index fb1e022b8..757dd5e28 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail352.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail352.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail352.d(18): Error: cannot infer argument types +fail_compilation/fail352.d(18): Error: cannot infer argument types, expected 1 argument, not 2 --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail3672.d b/gcc/testsuite/gdc.test/fail_compilation/fail3672.d new file mode 100644 index 000000000..2dbe14911 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail3672.d @@ -0,0 +1,38 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail3672.d(28): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(*p, 1) instead. +fail_compilation/fail3672.d(32): Deprecation: Read-modify-write operations are not allowed for shared variables. Use core.atomic.atomicOp!"+="(*sfp, 1) instead. +fail_compilation/fail3672.d(32): Error: '*sfp += 1' is not a scalar, it is a shared(SF) +fail_compilation/fail3672.d(32): Error: incompatible types for ((*sfp) += (1)): 'shared(SF)' and 'int' +--- +*/ +struct SF // should fail +{ + void opOpAssign(string op, T)(T rhs) + { + } +} + +struct SK // ok +{ + void opOpAssign(string op, T)(T rhs) shared + { + } +} + +void main() +{ + shared int x; + auto p = &x; + *p += 1; // fail + + shared SF sf; + auto sfp = &sf; + *sfp += 1; // fail + + shared SK sk; + auto skp = &sk; + sk += 1; // ok + *skp += 1; // ok +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail3882.d b/gcc/testsuite/gdc.test/fail_compilation/fail3882.d new file mode 100644 index 000000000..b7d8a9789 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail3882.d @@ -0,0 +1,48 @@ +// REQUIRED_ARGS: -w +// PERMUTE_ARGS: -debug + +/******************************************/ +// 3882 + +/* +TEST_OUTPUT: +--- +fail_compilation/fail3882.d(23): Warning: calling fail3882.strictlyPure!int.strictlyPure without side effects discards return value of type int, prepend a cast(void) if intentional +fail_compilation/fail3882.d(27): Warning: calling fp without side effects discards return value of type int, prepend a cast(void) if intentional +--- +*/ + +@safe pure nothrow T strictlyPure(T)(T x) +{ + return x*x; +} + +void main() +{ + int x = 3; + strictlyPure(x); + + // 12649 + auto fp = &strictlyPure!int; + fp(x); +} + +/******************************************/ +// bugfix in TypeFunction::purityLevel + +/* +TEST_OUTPUT: +--- +fail_compilation/fail3882.d(46): Warning: calling fail3882.f1 without side effects discards return value of type int, prepend a cast(void) if intentional +fail_compilation/fail3882.d(47): Warning: calling fail3882.f2 without side effects discards return value of type int, prepend a cast(void) if intentional +--- +*/ + +nothrow pure int f1(immutable(int)[] a) { return 0; } +nothrow pure int f2(immutable(int)* p) { return 0; } + +void test_bug() +{ + f1([]); + f2(null); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4.d b/gcc/testsuite/gdc.test/fail_compilation/fail4.d deleted file mode 100644 index 9d9edb87a..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail4.d +++ /dev/null @@ -1,12 +0,0 @@ -// REQUIRED_ARGS: -d -/* -TEST_OUTPUT: ---- -fail_compilation/fail4.d(12): Error: typedef fail4.foo circular definition ---- -*/ - -// On DMD0.165 fails only with typedef, not alias - -typedef foo bar; -typedef bar foo; diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4082.d b/gcc/testsuite/gdc.test/fail_compilation/fail4082.d new file mode 100644 index 000000000..77ae061fa --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail4082.d @@ -0,0 +1,34 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail4082.d(14): Error: 'fail4082.Foo.~this' is not nothrow +fail_compilation/fail4082.d(12): Error: function 'fail4082.test1' is nothrow yet may throw +--- +*/ +struct Foo +{ + ~this() { throw new Exception(""); } +} +nothrow void test1() +{ + Foo f; + + goto NEXT; +NEXT: + ; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail4082.d(32): Error: 'fail4082.Bar.~this' is not nothrow +fail_compilation/fail4082.d(32): Error: function 'fail4082.test2' is nothrow yet may throw +--- +*/ +struct Bar +{ + ~this() { throw new Exception(""); } +} +nothrow void test2(Bar t) +{ +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4082a.d b/gcc/testsuite/gdc.test/fail_compilation/fail4082a.d deleted file mode 100644 index 7c1d890a9..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail4082a.d +++ /dev/null @@ -1,8 +0,0 @@ -struct Foo { - ~this() { throw new Exception(""); } -} -nothrow void main() { - Foo f; - goto NEXT; - NEXT:; -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail4082b.d b/gcc/testsuite/gdc.test/fail_compilation/fail4082b.d deleted file mode 100644 index 961bb74a0..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail4082b.d +++ /dev/null @@ -1,4 +0,0 @@ -struct Foo { - ~this() { throw new Exception(""); } -} -nothrow void b(Foo t) {} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail52.d b/gcc/testsuite/gdc.test/fail_compilation/fail52.d index b89aebf8e..3968f0d53 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail52.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail52.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail52.d(10): Error: class fail52.B circular inheritance -fail_compilation/fail52.d(11): Deprecation: overriding base class function without using override attribute is deprecated (fail52.C.g overrides fail52.B.g) +fail_compilation/fail52.d(11): Error: class fail52.C circular inheritance +fail_compilation/fail52.d(10): Deprecation: implicitly overriding base class method fail52.C.g with fail52.B.g deprecated; add 'override' attribute --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail6572.d b/gcc/testsuite/gdc.test/fail_compilation/fail6572.d index a6344a4f6..ef952a2d0 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail6572.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail6572.d @@ -2,7 +2,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail6572.d(9): Deprecation: use of typedef is deprecated; use alias instead +fail_compilation/fail6572.d(9): Error: use alias instead of typedef --- */ @@ -12,7 +12,7 @@ typedef int y; /* TEST_OUTPUT: --- -fail_compilation/fail6572.d(18): Deprecation: use of typedef is deprecated; use alias instead +fail_compilation/fail6572.d(18): Error: use alias instead of typedef --- */ typedef struct S { } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail66.d b/gcc/testsuite/gdc.test/fail_compilation/fail66.d index 93287010d..5c352a520 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail66.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail66.d @@ -1,15 +1,91 @@ /* TEST_OUTPUT: --- -fail_compilation/fail66.d(12): Error: constructor fail66.C.this missing initializer for const field y +fail_compilation/fail66.d(11): Error: constructor fail66.C1.this missing initializer for const field y --- */ -class C +class C1 { const int y; + this() {} +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail66.d(28): Error: cannot modify const expression c.y +--- +*/ +class C2 +{ + const int y; + this() { y = 7; } +} +void test2() +{ + C2 c = new C2(); + c.y = 3; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail66.d(43): Error: cannot modify const expression this.y +--- +*/ +class C3 +{ + const int y; + this() { y = 7; } + void foo() + { + y = 6; + } +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail66.d(59): Error: cannot modify const expression x +--- +*/ +class C4 +{ + static const int x; + static this() { x = 5; } + void foo() + { + x = 4; + } +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail66.d(73): Error: cannot modify const expression z5 +--- +*/ +const int z5; +static this() { z5 = 3; } +void test5() +{ + z5 = 4; +} +/* +TEST_OUTPUT: +--- +fail_compilation/fail66.d(89): Error: cannot modify const expression c.y +--- +*/ +class C6 +{ + const int y; this() { + C6 c = this; + y = 7; + c.y = 8; } } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail6652.d b/gcc/testsuite/gdc.test/fail_compilation/fail6652.d new file mode 100644 index 000000000..45a3f5a3f --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail6652.d @@ -0,0 +1,37 @@ +// PERMUTE_ARGS: -w -dw -de -d + +/******************************************/ +// 6652 + +/* +TEST_OUTPUT: +--- +fail_compilation/fail6652.d(20): Error: cannot modify const expression i +fail_compilation/fail6652.d(25): Error: cannot modify const expression i +fail_compilation/fail6652.d(30): Error: cannot modify const expression i +fail_compilation/fail6652.d(35): Error: cannot modify const expression i +--- +*/ + +void main() +{ + foreach (const i; 0..2) + { + ++i; + } + + foreach (ref const i; 0..2) + { + ++i; + } + + foreach (const i, e; [1,2,3,4,5]) + { + ++i; + } + + foreach (ref const i, e; [1,2,3,4,5]) + { + ++i; + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail6652a.d b/gcc/testsuite/gdc.test/fail_compilation/fail6652a.d deleted file mode 100644 index 8a7c6d472..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail6652a.d +++ /dev/null @@ -1,25 +0,0 @@ -// PERMUTE_ARGS: -w -dw -de -d - -/******************************************/ -// 6652 - -/* -TEST_OUTPUT: ---- -fail_compilation/fail6652a.d(18): Error: cannot modify const expression i -fail_compilation/fail6652a.d(23): Error: cannot modify const expression i ---- -*/ - -void main() -{ - foreach (const i; 0..2) - { - ++i; - } - - foreach (ref const i; 0..2) - { - ++i; - } -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail6652b.d b/gcc/testsuite/gdc.test/fail_compilation/fail6652b.d deleted file mode 100644 index 0357db88a..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail6652b.d +++ /dev/null @@ -1,25 +0,0 @@ -// PERMUTE_ARGS: -w -dw -de -d - -/******************************************/ -// 6652 - -/* -TEST_OUTPUT: ---- -fail_compilation/fail6652b.d(18): Error: cannot modify const expression i -fail_compilation/fail6652b.d(23): Error: cannot modify const expression i ---- -*/ - -void main() -{ - foreach (const i, e; [1,2,3,4,5]) - { - ++i; - } - - foreach (ref const i, e; [1,2,3,4,5]) - { - ++i; - } -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail67.d b/gcc/testsuite/gdc.test/fail_compilation/fail67.d deleted file mode 100644 index 5ffddd605..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail67.d +++ /dev/null @@ -1,26 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail67.d(22): Error: can only initialize const member y inside constructor ---- -*/ - -class C -{ - const int y; - - this() - { - y = 7; - } -} - -int main() -{ - C c = new C(); - - c.y = 3; - - return 0; -} - diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail68.d b/gcc/testsuite/gdc.test/fail_compilation/fail68.d deleted file mode 100644 index 7ad78913c..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail68.d +++ /dev/null @@ -1,21 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail68.d(14): Error: can only initialize const member y inside constructor ---- -*/ - -class C -{ - const int y; - - void foo() - { - y = 6; - } - - this() - { - y = 7; - } -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail6889.d b/gcc/testsuite/gdc.test/fail_compilation/fail6889.d new file mode 100644 index 000000000..feb6e4f7b --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail6889.d @@ -0,0 +1,130 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail6889.d(16): Error: cannot goto out of scope(success) block +fail_compilation/fail6889.d(17): Error: cannot goto in to scope(success) block +fail_compilation/fail6889.d(19): Error: return statements cannot be in scope(success) bodies +fail_compilation/fail6889.d(23): Error: continue is not inside scope(success) bodies +fail_compilation/fail6889.d(24): Error: break is not inside scope(success) bodies +fail_compilation/fail6889.d(29): Error: continue is not inside scope(success) bodies +fail_compilation/fail6889.d(30): Error: break is not inside scope(success) bodies +--- +*/ +void test_success() +{ +L1: + scope(success) { L2: goto L1; } // NG + goto L2; // NG + + scope(success) { return; } // NG (from fail102.d) + + foreach (i; 0..1) + { + scope(success) continue; // NG + scope(success) break; // NG + } + + foreach (i; Aggr()) + { + scope(success) continue; // NG + scope(success) break; // NG + } + /+ + // is equivalent with: + switch ( + Aggr().opApply((int i){ + scope(success) return 0; // NG + scope(success) return 1; // NG + return 0; + })) + { + default: break; + } + +/ +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail6889.d(56): Error: cannot goto in to scope(failure) block +--- +*/ +void test_failure() +{ +L1: + scope(failure) { L2: goto L1; } // OK + goto L2; // NG + + scope(failure) { return; } // OK + + foreach (i; 0..1) + { + scope(failure) continue; // OK + scope(failure) break; // OK + } + + foreach (i; Aggr()) + { + scope(failure) continue; // OK + scope(failure) break; // OK + } + /+ + // is equivalent with: + switch ( + Aggr().opApply((int i){ + scope(failure) return 0; // OK + scope(failure) return 1; // OK + return 0; + })) + { + default: break; + } + +/ +} + +/* +TEST_OUTPUT: +--- +fail_compilation/fail6889.d(100): Error: cannot goto out of scope(exit) block +fail_compilation/fail6889.d(101): Error: cannot goto in to scope(exit) block +fail_compilation/fail6889.d(103): Error: return statements cannot be in scope(exit) bodies +fail_compilation/fail6889.d(107): Error: continue is not inside scope(exit) bodies +fail_compilation/fail6889.d(108): Error: break is not inside scope(exit) bodies +fail_compilation/fail6889.d(113): Error: continue is not inside scope(exit) bodies +fail_compilation/fail6889.d(114): Error: break is not inside scope(exit) bodies +--- +*/ +void test_exit() +{ +L1: + scope(exit) { L2: goto L1; } // NG + goto L2; // NG + + scope(exit) { return; } // NG (from fail102.d) + + foreach (i; 0..1) + { + scope(exit) continue; // NG + scope(exit) break; // NG + } + + foreach (i; Aggr()) + { + scope(exit) continue; // NG + scope(exit) break; // NG + } + /+ + // is equivalent with: + switch ( + Aggr().opApply((int i){ + scope(exit) return 0; // NG + scope(exit) return 1; // NG + return 0; + })) + { + default: break; + } + +/ +} + +struct Aggr { int opApply(int delegate(int) dg) { return dg(0); } } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail69.d b/gcc/testsuite/gdc.test/fail_compilation/fail69.d deleted file mode 100644 index 30a8e34b4..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail69.d +++ /dev/null @@ -1,21 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail69.d(14): Error: can only initialize static const member x inside static constructor ---- -*/ - -class C -{ - static const int x; - - void foo() - { - x = 4; - } - - static this() - { - x = 5; - } -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail70.d b/gcc/testsuite/gdc.test/fail_compilation/fail70.d deleted file mode 100644 index 5fa06ee1b..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail70.d +++ /dev/null @@ -1,21 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail70.d(17): Error: can only initialize const member z inside constructor ---- -*/ - -const int z; - -static this() -{ - z = 3; -} - -int main() -{ - z = 4; - - return 0; -} - diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail71.d b/gcc/testsuite/gdc.test/fail_compilation/fail71.d deleted file mode 100644 index 700a0ec2b..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/fail71.d +++ /dev/null @@ -1,19 +0,0 @@ -/* -TEST_OUTPUT: ---- -fail_compilation/fail71.d(17): Error: can only initialize const member y inside constructor ---- -*/ - -class C -{ - const int y; - - this() - { - C c = this; - - y = 7; - c.y = 8; - } -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail8373.d b/gcc/testsuite/gdc.test/fail_compilation/fail8373.d new file mode 100644 index 000000000..2bb29b0bf --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/fail8373.d @@ -0,0 +1,23 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/fail8373.d(21): Error: fail8373.fun1 called with argument types (int) matches both: +fail_compilation/fail8373.d(15): fail8373.fun1!().fun1!(int).fun1(int) +and: +fail_compilation/fail8373.d(16): fail8373.fun1!(int).fun1(int) +fail_compilation/fail8373.d(22): Error: fail8373.fun2 called with argument types (int) matches both: +fail_compilation/fail8373.d(18): fail8373.fun2!(int).fun2(int) +and: +fail_compilation/fail8373.d(19): fail8373.fun2!().fun2!(int).fun2(int) +--- +*/ + +template fun1(a...) { auto fun1(T...)(T args){ return 1; } } + auto fun1(T...)(T args){ return 2; } + + auto fun2(T...)(T args){ return 2; } +template fun2(a...) { auto fun2(T...)(T args){ return 1; } } + +enum x1 = fun1(0); +enum x2 = fun2(0); + diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail8664.d b/gcc/testsuite/gdc.test/fail_compilation/fail8664.d index 70fe021d4..ea5a16991 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail8664.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail8664.d @@ -3,7 +3,8 @@ /* TEST_OUTPUT: --- -fail_compilation/fail8664.d(11): Error: typedef fail8664.foo circular definition +fail_compilation/fail8664.d(11): Error: use alias instead of typedef +fail_compilation/fail8664.d(12): Error: use alias instead of typedef --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9368.d b/gcc/testsuite/gdc.test/fail_compilation/fail9368.d index 388e991a4..f2272c1a9 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail9368.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail9368.d @@ -15,7 +15,7 @@ enum E void main() { - typedef E F; + alias E F; F f; final switch (f) { diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9562.d b/gcc/testsuite/gdc.test/fail_compilation/fail9562.d index 410eace4a..6f9ac1f29 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail9562.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail9562.d @@ -6,8 +6,8 @@ TEST_OUTPUT: fail_compilation/fail9562.d(17): Error: int[] is not an expression fail_compilation/fail9562.d(18): Error: int[] is not an expression fail_compilation/fail9562.d(19): Error: int[] is not an expression -fail_compilation/fail9562.d(20): Error: int[] is not an expression -fail_compilation/fail9562.d(21): Error: int[] is not an expression +fail_compilation/fail9562.d(20): Error: no property 'dup' for type 'int[]' +fail_compilation/fail9562.d(21): Error: no property 'idup' for type 'int[]' --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9665a.d b/gcc/testsuite/gdc.test/fail_compilation/fail9665a.d index ae10bf1ca..f3a9786a1 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail9665a.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail9665a.d @@ -7,16 +7,9 @@ /+ TEST_OUTPUT: --- -fail_compilation/fail9665a.d(106): Error: multiple field v initialization -fail_compilation/fail9665a.d(116): Error: multiple field v initialization -fail_compilation/fail9665a.d(121): Error: multiple field v initialization -fail_compilation/fail9665a.d(126): Error: multiple field v initialization -fail_compilation/fail9665a.d(136): Error: multiple field v initialization -fail_compilation/fail9665a.d(141): Error: multiple field v initialization -fail_compilation/fail9665a.d(146): Error: multiple field v initialization +fail_compilation/fail9665a.d(19): Error: immutable field 'v' initialized multiple times --- +/ -#line 100 struct S1A { immutable int v; @@ -27,6 +20,14 @@ struct S1A } } +/+ +TEST_OUTPUT: +--- +fail_compilation/fail9665a.d(37): Error: immutable field 'v' initialized multiple times +fail_compilation/fail9665a.d(42): Error: immutable field 'v' initialized multiple times +fail_compilation/fail9665a.d(47): Error: immutable field 'v' initialized multiple times +--- ++/ struct S1B { immutable int v; @@ -47,6 +48,14 @@ struct S1B } } +/+ +TEST_OUTPUT: +--- +fail_compilation/fail9665a.d(65): Error: immutable field 'v' initialized multiple times +fail_compilation/fail9665a.d(70): Error: immutable field 'v' initialized multiple times +fail_compilation/fail9665a.d(75): Error: immutable field 'v' initialized multiple times +--- ++/ struct S1C { immutable int v; @@ -73,14 +82,13 @@ struct S1C /+ TEST_OUTPUT: --- -fail_compilation/fail9665a.d(206): Error: field v initializing not allowed in loops or after labels -fail_compilation/fail9665a.d(211): Error: field v initializing not allowed in loops or after labels -fail_compilation/fail9665a.d(216): Error: multiple field v initialization -fail_compilation/fail9665a.d(221): Error: multiple field v initialization -fail_compilation/fail9665a.d(226): Error: multiple field v initialization +fail_compilation/fail9665a.d(98): Error: immutable field 'v' initialization is not allowed in loops or after labels +fail_compilation/fail9665a.d(103): Error: immutable field 'v' initialization is not allowed in loops or after labels +fail_compilation/fail9665a.d(108): Error: immutable field 'v' initialized multiple times +fail_compilation/fail9665a.d(113): Error: immutable field 'v' initialized multiple times +fail_compilation/fail9665a.d(118): Error: immutable field 'v' initialized multiple times --- +/ -#line 200 struct S2 { immutable int v; @@ -117,11 +125,10 @@ struct S2 /+ TEST_OUTPUT: --- -fail_compilation/fail9665a.d(307): Error: multiple field v initialization -fail_compilation/fail9665a.d(311): Error: multiple field w initialization +fail_compilation/fail9665a.d(139): Error: immutable field 'v' initialized multiple times +fail_compilation/fail9665a.d(143): Error: immutable field 'w' initialized multiple times --- +/ -#line 300 struct S3 { int v; @@ -129,11 +136,11 @@ struct S3 this(int) immutable { v = 1; - v = 2; // multiplie initialization + v = 2; // multiple initialization if (true) w = 1; - w = 2; // multiplie initialization + w = 2; // multiple initialization } } @@ -143,17 +150,16 @@ struct S3 /+ TEST_OUTPUT: --- -fail_compilation/fail9665a.d(406): Error: multiple field v initialization +fail_compilation/fail9665a.d(162): Error: immutable field 'v' initialized multiple times --- +/ -#line 400 struct S4 { immutable int v; this(int) { static assert(__traits(compiles, v = 1)); - v = 1; // multiplie initialization + v = 1; // multiple initialization } } diff --git a/gcc/testsuite/gdc.test/fail_compilation/fail9665b.d b/gcc/testsuite/gdc.test/fail_compilation/fail9665b.d index 25ea37740..0fcc1d25f 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/fail9665b.d +++ b/gcc/testsuite/gdc.test/fail_compilation/fail9665b.d @@ -11,15 +11,14 @@ struct X /+ TEST_OUTPUT: --- -fail_compilation/fail9665b.d(110): Error: one path skips field x2 -fail_compilation/fail9665b.d(111): Error: one path skips field x3 -fail_compilation/fail9665b.d(113): Error: one path skips field x5 -fail_compilation/fail9665b.d(114): Error: one path skips field x6 -fail_compilation/fail9665b.d(108): Error: constructor fail9665b.S1.this field x1 must be initialized in constructor -fail_compilation/fail9665b.d(108): Error: constructor fail9665b.S1.this field x4 must be initialized in constructor +fail_compilation/fail9665b.d(32): Error: one path skips field x2 +fail_compilation/fail9665b.d(33): Error: one path skips field x3 +fail_compilation/fail9665b.d(35): Error: one path skips field x5 +fail_compilation/fail9665b.d(36): Error: one path skips field x6 +fail_compilation/fail9665b.d(30): Error: field x1 must be initialized in constructor +fail_compilation/fail9665b.d(30): Error: field x4 must be initialized in constructor --- +/ -#line 100 struct S1 { X x1; @@ -44,16 +43,15 @@ struct S1 /+ TEST_OUTPUT: --- -fail_compilation/fail9665b.d(210): Error: one path skips field x2 -fail_compilation/fail9665b.d(211): Error: one path skips field x3 -fail_compilation/fail9665b.d(213): Error: one path skips field x5 -fail_compilation/fail9665b.d(214): Error: one path skips field x6 -fail_compilation/fail9665b.d(208): Error: constructor fail9665b.S2!(X).S2.this field x1 must be initialized in constructor, because it is nested struct -fail_compilation/fail9665b.d(208): Error: constructor fail9665b.S2!(X).S2.this field x4 must be initialized in constructor, because it is nested struct -fail_compilation/fail9665b.d(221): Error: template instance fail9665b.S2!(X) error instantiating +fail_compilation/fail9665b.d(65): Error: one path skips field x2 +fail_compilation/fail9665b.d(66): Error: one path skips field x3 +fail_compilation/fail9665b.d(68): Error: one path skips field x5 +fail_compilation/fail9665b.d(69): Error: one path skips field x6 +fail_compilation/fail9665b.d(63): Error: field x1 must be initialized in constructor, because it is nested struct +fail_compilation/fail9665b.d(63): Error: field x4 must be initialized in constructor, because it is nested struct +fail_compilation/fail9665b.d(76): Error: template instance fail9665b.S2!(X) error instantiating --- +/ -#line 200 struct S2(X) { X x1; @@ -62,18 +60,18 @@ struct S2(X) X[2] x4; X[2] x5; X[2] x6; - this(int) + this(X x) { - if (true) x2 = X(1); - auto x = true ? (x3 = X(1), 1) : 2; + if (true) x2 = x; + auto a = true ? (x3 = x, 1) : 2; - if (true) x5 = X(1); - auto m = true ? (x6 = X(1), 1) : 2; + if (true) x5 = x; + auto b = true ? (x6 = x, 1) : 2; } } void test2() { struct X { this(int) {} } static assert(X.tupleof.length == 1); - S2!(X) s; + S2!(X) s = X(1); } diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10076.d b/gcc/testsuite/gdc.test/fail_compilation/ice10076.d index 940e76029..8337d7f45 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice10076.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice10076.d @@ -1,12 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/ice10076.d(21): Error: template instance getMembersAndAttributesWhere!() template 'getMembersAndAttributesWhere' is not defined -fail_compilation/ice10076.d(26): Error: template instance ice10076.getValidaterAttrs!string error instantiating -fail_compilation/ice10076.d(16): instantiated from here: validate!string -fail_compilation/ice10076.d(26): Error: forward reference to 'getMembersAndAttributesWhere!().Elements' -fail_compilation/ice10076.d(26): Error: forward reference to 'getMembersAndAttributesWhere!().Elements' -fail_compilation/ice10076.d(16): Error: template instance ice10076.validate!string error instantiating +fail_compilation/ice10076.d(18): Error: template instance getMembersAndAttributesWhere!() template 'getMembersAndAttributesWhere' is not defined +fail_compilation/ice10076.d(23): Error: template instance ice10076.getValidaterAttrs!string error instantiating +fail_compilation/ice10076.d(13): instantiated from here: validate!string --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10212.d b/gcc/testsuite/gdc.test/fail_compilation/ice10212.d index 937737c1a..ccc33a97b 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice10212.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice10212.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/ice10212.d(13): Error: mismatched function return type inference of int function() pure nothrow @safe and int -fail_compilation/ice10212.d(13): Error: cannot implicitly convert expression (__lambda1) of type int function() pure nothrow @safe to int +fail_compilation/ice10212.d(13): Error: mismatched function return type inference of int function() pure nothrow @nogc @safe and int +fail_compilation/ice10212.d(13): Error: cannot implicitly convert expression (__lambda1) of type int function() pure nothrow @nogc @safe to int --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10259.d b/gcc/testsuite/gdc.test/fail_compilation/ice10259.d index fa902fa57..ffa659091 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice10259.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice10259.d @@ -1,7 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/ice10259.d(12): Error: delegate ice10259.D.__lambda3 function literals cannot be class members +fail_compilation/ice10259.d(13): Error: circular reference to 'ice10259.D.d' +fail_compilation/ice10259.d(13): called from here: (*function () => x)() fail_compilation/ice10259.d(15): Error: variable ice10259.x : Unable to initialize enum with class or pointer to struct. Use static const variable instead. --- */ @@ -11,5 +12,21 @@ class D int x; D d = { auto x = new D(); return x; }(); } - enum x = new D; + + +/* +TEST_OUTPUT: +--- +fail_compilation/ice10259.d(30): Error: circular reference to 'ice10259.D2.d' +fail_compilation/ice10259.d(30): called from here: (*function () => x)() +fail_compilation/ice10259.d(32): Error: variable ice10259.x2 : Unable to initialize enum with class or pointer to struct. Use static const variable instead. +--- +*/ + +class D2 +{ + int x; + D2 d = function { auto x = new D2(); return x; }(); +} +enum x2 = new D2; diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10610.d b/gcc/testsuite/gdc.test/fail_compilation/ice10610.d deleted file mode 100644 index 2cfd1a0d6..000000000 --- a/gcc/testsuite/gdc.test/fail_compilation/ice10610.d +++ /dev/null @@ -1,15 +0,0 @@ -// 10610 CTFE ice - -class Bug10610(T) -{ - int baz() immutable { - return 1; - } - static immutable(Bug10610!T) min = new Bug10610!T(); -} - -void ice10610() -{ - alias T10610 = Bug10610!(int); - static assert (T10610.min.baz()); -} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10616.d b/gcc/testsuite/gdc.test/fail_compilation/ice10616.d index 20823a967..6ccd1c31b 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice10616.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice10616.d @@ -1,8 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice10616.d(9): Error: no property 'B' for type 'ice10616.A' -fail_compilation/ice10616.d(9): Error: A.B is used as a type +fail_compilation/ice10616.d(8): Error: class ice10616.A is forward referenced when looking for 'B' --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10624.d b/gcc/testsuite/gdc.test/fail_compilation/ice10624.d index 3628fc902..1e8633de4 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice10624.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice10624.d @@ -3,8 +3,7 @@ TEST_OUTPUT: --- fail_compilation/ice10624.d(38): Error: need member function opCmp() for struct Tuple!(Msg) to compare fail_compilation/ice10624.d(48): Error: template instance ice10624.Variant.handler!(Tuple!(Msg)) error instantiating -fail_compilation/ice10624.d(22): instantiated from here: opAssign!(Tuple!(Msg)) -fail_compilation/ice10624.d(22): Error: template instance ice10624.Variant.opAssign!(Tuple!(Msg)) error instantiating +fail_compilation/ice10624.d(21): instantiated from here: opAssign!(Tuple!(Msg)) --- */ @@ -22,6 +21,7 @@ void main() data = Tuple!Msg(); } + struct Variant { ptrdiff_t function() fptr = &handler!(void); diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice10922.d b/gcc/testsuite/gdc.test/fail_compilation/ice10922.d index e2631ad6e..1c406124d 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice10922.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice10922.d @@ -1,8 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice10922.d(11): Error: forward reference to inferred return type of function call self(n - 1u) -fail_compilation/ice10922.d(11): Error: forward reference to inferred return type of function call self(n - 2u) +fail_compilation/ice10922.d(9): Error: delegate ice10922.__lambda4 (const(uint) n) is not callable using argument types () --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11518.d b/gcc/testsuite/gdc.test/fail_compilation/ice11518.d index 850a4c22a..cfceb64ed 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice11518.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice11518.d @@ -2,9 +2,9 @@ TEST_OUTPUT: --- fail_compilation/ice11518.d(17): Error: class ice11518.B matches more than one template declaration: - fail_compilation/ice11518.d(12):B(T : A!T) +fail_compilation/ice11518.d(12): B(T : A!T) and - fail_compilation/ice11518.d(13):B(T : A!T) +fail_compilation/ice11518.d(13): B(T : A!T) --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11553.d b/gcc/testsuite/gdc.test/fail_compilation/ice11553.d index c89db5392..a8ead1375 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice11553.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice11553.d @@ -1,8 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice11553.d(22): Error: recursive template expansion while looking for A!().A() -fail_compilation/ice11553.d(22): Error: expression template A() of type void does not have a boolean value +fail_compilation/ice11553.d(21): Error: recursive template expansion while looking for A!().A() --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11822.d b/gcc/testsuite/gdc.test/fail_compilation/ice11822.d index fea84789b..4ec46c3a2 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice11822.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice11822.d @@ -4,33 +4,28 @@ /* TEST_OUTPUT: --- -fail_compilation/ice11822.d(37): Deprecation: function ice11822.d is deprecated -fail_compilation/ice11822.d(24): instantiated from here: S!(__lambda1) -fail_compilation/ice11822.d(37): instantiated from here: g!((n) => d(i)) -fail_compilation/ice11822.d(37): Error: template instance ice11822.h.g!((n) => d(i)) error instantiating +fail_compilation/ice11822.d(32): Deprecation: function ice11822.d is deprecated +fail_compilation/ice11822.d(21): instantiated from here: S!(__lambda1) +fail_compilation/ice11822.d(32): instantiated from here: g!((n) => d(i)) --- */ - struct S(alias pred) { this(int) { pred(1); } void f() { pred(2); } } - auto g(alias pred)() { return S!pred(3); } - deprecated bool d(int) { return true; } - auto h() { int i; diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11850.d b/gcc/testsuite/gdc.test/fail_compilation/ice11850.d index 4ff792271..9d1f172a5 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice11850.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice11850.d @@ -1,12 +1,9 @@ /* TEST_OUTPUT: --- -fail_compilation/ice11850.d(17): Error: incompatible types for ((a) < ([0])): 'uint[]' and 'int[]' +fail_compilation/ice11850.d(14): Error: incompatible types for ((a) < ([0])): 'uint[]' and 'int[]' fail_compilation/imports/a11850.d(9): instantiated from here: FilterResult!(__lambda1, uint[][]) -fail_compilation/ice11850.d(17): instantiated from here: filter!(uint[][]) -fail_compilation/ice11850.d(17): Error: template instance ice11850.main.filter!((a) => a < [0]).filter!(uint[][]) error instantiating -fail_compilation/ice11850.d(17): Error: template imports.a11850.filter cannot deduce function from argument types !((a) => a < [0])(uint[][]), candidates are: -fail_compilation/imports/a11850.d(5): imports.a11850.filter(alias pred) +fail_compilation/ice11850.d(14): instantiated from here: filter!(uint[][]) --- */ @@ -16,6 +13,3 @@ void main() { filter!(a => a < [0])([[0u]]); } - - - diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11919.d b/gcc/testsuite/gdc.test/fail_compilation/ice11919.d index 2693fc4ce..edb31d4cf 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice11919.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice11919.d @@ -1,16 +1,13 @@ /* TEST_OUTPUT: --- -fail_compilation/ice11919.d(20): Error: Cannot interpret foo at compile time -fail_compilation/ice11919.d(20): Error: Cannot interpret foo at compile time +fail_compilation/ice11919.d(17): Error: Cannot interpret foo at compile time fail_compilation/imports/a11919.d(4): Error: template instance a11919.doBar!(Foo).doBar.zoo!(t) error instantiating fail_compilation/imports/a11919.d(11): instantiated from here: doBar!(Foo) -fail_compilation/ice11919.d(28): instantiated from here: doBar!(Bar) -fail_compilation/imports/a11919.d(11): Error: template instance a11919.doBar!(Foo) error instantiating -fail_compilation/ice11919.d(28): instantiated from here: doBar!(Bar) -fail_compilation/ice11919.d(28): Error: template instance a11919.doBar!(Bar) error instantiating +fail_compilation/ice11919.d(25): instantiated from here: doBar!(Bar) --- */ + import imports.a11919; enum foo; diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice11926.d b/gcc/testsuite/gdc.test/fail_compilation/ice11926.d index 94b86758c..ccd6e12fc 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice11926.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice11926.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/ice11926.d(11): Error: undefined identifier const(a) -fail_compilation/ice11926.d(12): Error: undefined identifier const(b) +fail_compilation/ice11926.d(11): Error: no identifier for declarator const(a) +fail_compilation/ice11926.d(12): Error: no identifier for declarator const(b) --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12174.d b/gcc/testsuite/gdc.test/fail_compilation/ice12174.d new file mode 100644 index 000000000..81fef2d7f --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12174.d @@ -0,0 +1,51 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12174.d(12): Error: no property 'sum' for type 'int[]' +fail_compilation/ice12174.d(20): Error: CTFE failed because of previous errors in this +fail_compilation/ice12174.d(13): called from here: filter([1, 2, 3]) +--- +*/ + +void main() +{ + enum foo3 = (int n) => [1,2,3].sum; + enum bar3 = [1,2,3].filter!(n => n % foo3(n) == 0); +} + +template filter(alias pred) +{ + auto filter(Range)(Range rs) + { + return FilterResult!(pred, Range)(rs); + } +} + +private struct FilterResult(alias pred, R) +{ + R _input; + + this(R r) + { + _input = r; + while (_input.length && !pred(_input[0])) + { + _input = _input[1..$]; + } + } + + @property bool empty() { return _input.length == 0; } + + @property auto ref front() + { + return _input[0]; + } + + void popFront() + { + do + { + _input = _input[1..$]; + } while (_input.length && !pred(_input[0])); + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12179.d b/gcc/testsuite/gdc.test/fail_compilation/ice12179.d new file mode 100644 index 000000000..5bf10a75e --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12179.d @@ -0,0 +1,83 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12179.d(25): Error: array operation a[] + a[] without assignment not implemented +fail_compilation/ice12179.d(26): Error: array operation a[] - a[] without assignment not implemented +fail_compilation/ice12179.d(27): Error: array operation a[] * a[] without assignment not implemented +fail_compilation/ice12179.d(28): Error: array operation a[] / a[] without assignment not implemented +fail_compilation/ice12179.d(29): Error: array operation a[] % a[] without assignment not implemented +fail_compilation/ice12179.d(30): Error: array operation a[] ^ a[] without assignment not implemented +fail_compilation/ice12179.d(31): Error: array operation a[] & a[] without assignment not implemented +fail_compilation/ice12179.d(32): Error: array operation a[] | a[] without assignment not implemented +fail_compilation/ice12179.d(33): Error: array operation a[] ^^ 10 without assignment not implemented +fail_compilation/ice12179.d(34): Error: array operation -a[] without assignment not implemented +fail_compilation/ice12179.d(35): Error: array operation ~a[] without assignment not implemented +fail_compilation/ice12179.d(40): Error: array operation [1] + a[] without assignment not implemented +fail_compilation/ice12179.d(41): Error: array operation [1] + a[] without assignment not implemented +--- +*/ + +void main() +{ + void foo(int[]) {} + int[1] a; + + foo(a[] + a[]); + foo(a[] - a[]); + foo(a[] * a[]); + foo(a[] / a[]); + foo(a[] % a[]); + foo(a[] ^ a[]); + foo(a[] & a[]); + foo(a[] | a[]); + foo(a[] ^^ 10); + foo(-a[]); + foo(~a[]); + + // from issue 11992 + int[] arr1; + int[][] arr2; + arr1 ~= [1] + a[]; // NG + arr2 ~= [1] + a[]; // NG +} + +// from issue 12769 +/* +TEST_OUTPUT: +--- +fail_compilation/ice12179.d(55): Error: array operation -a[] without assignment not implemented +fail_compilation/ice12179.d(57): Error: array operation (-a[])[0..4] without assignment not implemented +--- +*/ +float[] f12769(float[] a) +{ + if (a.length < 4) + return -a[]; + else + return (-a[])[0..4]; +} + +/* +TEST_OUTPUT: +--- +fail_compilation/ice12179.d(74): Error: array operation a[] - a[] without assignment not implemented +fail_compilation/ice12179.d(76): Error: array operation a[] - a[] without assignment not implemented +fail_compilation/ice12179.d(77): Error: array operation a[] - a[] without assignment not implemented +fail_compilation/ice12179.d(80): Error: array operation a[] - a[] without assignment not implemented +fail_compilation/ice12179.d(82): Error: array operation a[] - a[] without assignment not implemented +--- +*/ +void test13208() +{ + int[] a; + + auto arr = [a[] - a[]][0]; + + auto aa1 = [1 : a[] - a[]]; + auto aa2 = [a[] - a[] : 1]; + + struct S { int[] a; } + auto s = S(a[] - a[]); + + auto n = int(a[] - a[]); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12235.d b/gcc/testsuite/gdc.test/fail_compilation/ice12235.d new file mode 100644 index 000000000..e18bf0473 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12235.d @@ -0,0 +1,17 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12235.d(14): Error: forward reference to __lambda1 +fail_compilation/ice12235.d(15): Error: forward reference to __lambda1 +fail_compilation/ice12235.d(15): while evaluating pragma(msg, __lambda1.mangleof) +--- +*/ + +void main() +{ + (){ + int x; + enum s = __traits(parent, x).mangleof; + pragma(msg, __traits(parent, x).mangleof); + }(); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12350.d b/gcc/testsuite/gdc.test/fail_compilation/ice12350.d new file mode 100644 index 000000000..e2ca91945 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12350.d @@ -0,0 +1,31 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12350.d(10): Error: enum ice12350.MyUDC is forward referenced looking for base type +fail_compilation/ice12350.d(15): Error: type MyUDC has no value +fail_compilation/ice12350.d(30): Error: template instance ice12350.testAttrs!(MyStruct) error instantiating +--- +*/ + +enum MyUDC; + +struct MyStruct +{ + int a; + @MyUDC int b; +} + +void testAttrs(T)(const ref T t) +if (is(T == struct)) +{ + foreach (name; __traits(allMembers, T)) + { + auto tr = __traits(getAttributes, __traits(getMember, t, name)); + } +} + +void main() +{ + MyStruct s; + testAttrs(s); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12362.d b/gcc/testsuite/gdc.test/fail_compilation/ice12362.d new file mode 100644 index 000000000..aefc59759 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12362.d @@ -0,0 +1,13 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12362.d(9): Error: enum ice12362.foo is forward referenced looking for base type +fail_compilation/ice12362.d(12): Error: Cannot interpret foo at compile time +--- +*/ + +enum foo; +void main() +{ + enum bar = foo; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12397.d b/gcc/testsuite/gdc.test/fail_compilation/ice12397.d new file mode 100644 index 000000000..f112aa3f3 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12397.d @@ -0,0 +1,16 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12397.d(12): Error: undefined identifier tokenLookup +--- +*/ + +struct DSplitter +{ + enum Token : int + { + max = tokenLookup.length + } + + immutable string[Token.max] tokenText; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12497.d b/gcc/testsuite/gdc.test/fail_compilation/ice12497.d new file mode 100644 index 000000000..f18cafb21 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12497.d @@ -0,0 +1,18 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12497.d(15): Error: argument to mixin must be a string, not (foo()) of type void +fail_compilation/ice12497.d(17): Error: argument to mixin must be a string, not (foo()) of type void +--- +*/ + +void foo() {} + +void main() +{ + struct S + { + mixin(foo()); // MixinDeclaration + } + mixin(foo()); // MixinStatement +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12501.d b/gcc/testsuite/gdc.test/fail_compilation/ice12501.d new file mode 100644 index 000000000..117f97975 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12501.d @@ -0,0 +1,44 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12501.d(29): Error: foo (int value) is not callable using argument types (int, int) +fail_compilation/ice12501.d(29): Error: foo (int value) is not callable using argument types (int, int) +fail_compilation/ice12501.d(43): Error: template instance ice12501.reduce!(foo, foo).reduce!(Tuple!(int, int), int[]) error instantiating +--- +*/ + +struct Tuple(T...) +{ + alias Types = T; + T field; + alias field this; +} +Tuple!A tuple(A...)(A args) { return typeof(return)(args); } + +template reduce(fun...) +{ + auto reduce(Args...)(Args args) + { + alias seed = args[0]; + alias r = args[1]; + Args[0] result = seed; + for (; r.length != 0; r = r[1..$]) + { + foreach (i, Unused; Args[0].Types) + { + result[i] = fun[i](result[i], r[0]); + } + } + return result; + } +} + +int foo(int value) +{ + return value; +} + +void main() +{ + reduce!(foo, foo)(tuple(0, 0), [ 1 ]); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12534.d b/gcc/testsuite/gdc.test/fail_compilation/ice12534.d new file mode 100644 index 000000000..7548925ac --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12534.d @@ -0,0 +1,15 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12534.d(14): Error: static assert (is(exprs[0 .. 0])) is false +--- +*/ + +alias TypeTuple(T...) = T; + +void main() +{ + int x, y; + alias exprs = TypeTuple!(x, y); + static assert(is(exprs[0..0])); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12539.d b/gcc/testsuite/gdc.test/fail_compilation/ice12539.d new file mode 100644 index 000000000..e1ad9941b --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12539.d @@ -0,0 +1,16 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12539.d(15): Error: array index [0] is outside array bounds [0 .. 0] +--- +*/ + +alias TypeTuple(E...) = E; + +void main () +{ + int[string] map; + + alias Foo = TypeTuple!(); + auto a = map[Foo[0]]; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12554.d b/gcc/testsuite/gdc.test/fail_compilation/ice12554.d new file mode 100644 index 000000000..9f9471ce1 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12554.d @@ -0,0 +1,55 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12554.d(18): Error: pure function 'D main' cannot call impure function 'ice12554.array!(MapResult!((y) => x)).array' +fail_compilation/ice12554.d(37): instantiated from here: MapResult!((x) => foo.map!(MapResultS, (y) => x).array) +fail_compilation/ice12554.d(18): instantiated from here: map!(int[]) +fail_compilation/ice12554.d(21): Error: pure function 'D main' cannot call impure function 'ice12554.array!(MapResult!((y) => x)).array' +fail_compilation/ice12554.d(37): instantiated from here: MapResult!((x) => foo.map!(MapResultC, (y) => x).array) +fail_compilation/ice12554.d(21): instantiated from here: map!(int[]) +--- +*/ + +void main() pure +{ + int[] foo; + + // if indirectly instantiated aggregate is struct (== MapResultS) + foo.map!(MapResultS, x => foo.map!(MapResultS, y => x).array); + + // if indirectly instantiated aggregate is class (== MapResultC) + foo.map!(MapResultC, x => foo.map!(MapResultC, y => x).array); +} + +T array(T)(T a) +{ + static int g; g = 1; // impure operation + return a; +} + +template map(alias MapResult, fun...) +{ + auto map(Range)(Range r) + { + alias AppliedReturnType(alias f) = typeof(f(r[0])); + static assert(!is(AppliedReturnType!fun == void)); + + return MapResult!(fun).init; + } +} + +struct MapResultS(alias fun) +{ + @property front() + { + return fun(1); + } +} + +class MapResultC(alias fun) +{ + @property front() + { + return fun(1); + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12574.d b/gcc/testsuite/gdc.test/fail_compilation/ice12574.d new file mode 100644 index 000000000..362c359de --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12574.d @@ -0,0 +1,54 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12574.d(40): Error: tuple index 2 exceeds length 2 +fail_compilation/ice12574.d(53): Error: template instance ice12574.reduce!("a", "a").reduce!(Tuple!(int, int, int)) error instantiating +--- +*/ + +struct Tuple(T...) +{ + alias Types = T; + T field; + alias field this; +} +Tuple!A tuple(A...)(A args) { return typeof(return)(args); } + +template binaryFun(alias fun) +{ + static if (is(typeof(fun) : string)) + { + auto binaryFun(ElementType1, ElementType2)(auto ref ElementType1 __a, auto ref ElementType2 __b) + { + mixin("alias "~"a"~" = __a ;"); + mixin("alias "~"b"~" = __b ;"); + return mixin(fun); + } + } + else + { + alias binaryFun = fun; + } +} + +template reduce(fun...) +{ + auto reduce(Seed)(Seed result) + { + foreach (i, Unused; Seed.Types) + { + result[i] = binaryFun!(fun[i])(1, 1); // here + } + return result; + } +} + +int foo(int value) +{ + return value; +} + +void main() +{ + reduce!("a", "a")(tuple(1, 1, 1)); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12581.d b/gcc/testsuite/gdc.test/fail_compilation/ice12581.d new file mode 100644 index 000000000..976e10385 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12581.d @@ -0,0 +1,22 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12581.d(21): Error: undefined identifier undef +--- +*/ + +struct S +{ + int[3] a; + alias a this; +} +struct T +{ + S s; + alias s this; +} +void main() +{ + T x; + x[] = (undef = 1); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12673.d b/gcc/testsuite/gdc.test/fail_compilation/ice12673.d new file mode 100644 index 000000000..57f0ec00c --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12673.d @@ -0,0 +1,4 @@ +void main() +{ + static assert(__traits(compiles, { abcd(); })); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12727.d b/gcc/testsuite/gdc.test/fail_compilation/ice12727.d new file mode 100644 index 000000000..027894c51 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12727.d @@ -0,0 +1,28 @@ +/* +TEST_OUTPUT: +---- +fail_compilation/ice12727.d(16): Error: alias ice12727.IndexTuple!(1, 0).IndexTuple recursive alias declaration +fail_compilation/ice12727.d(23): Error: template instance ice12727.IndexTuple!(1, 0) error instantiating +fail_compilation/ice12727.d(27): instantiated from here: Matrix!(float, 3) +fail_compilation/ice12727.d(28): instantiated from here: Vector!(float, 3) +---- +*/ + +template IndexTuple(int e, int s = 0, T...) +{ + static if (s == e) + alias IndexTuple = T; + else + alias IndexTuple = IndexTuple!(e); +} + +struct Matrix(T, int N = M) +{ + pure decomposeLUP() + { + foreach (j; IndexTuple!(1)) {} + } +} + +alias Vector(T, int M) = Matrix!(T, M); +alias Vector3 = Vector!(float, 3); diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12836.d b/gcc/testsuite/gdc.test/fail_compilation/ice12836.d new file mode 100644 index 000000000..3944c968e --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12836.d @@ -0,0 +1,9 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12836.d(9): Error: undefined identifier C +fail_compilation/ice12836.d(9): Error: undefined identifier K +--- +*/ + +immutable C L = 1 << K; diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12838.d b/gcc/testsuite/gdc.test/fail_compilation/ice12838.d new file mode 100644 index 000000000..04072280d --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12838.d @@ -0,0 +1,28 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12838.d(27): Error: cannot implicitly convert expression (1) of type int to string +--- +*/ + +struct Tuple(T...) +{ + T field; + alias field this; +} + +struct Data +{ + string a; +} + +template toTuple(T) +{ + mixin(`alias toTuple = Tuple!(string);`); +} + +void main() +{ + toTuple!Data a; + a[0] = 1; // ICE! +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12841.d b/gcc/testsuite/gdc.test/fail_compilation/ice12841.d new file mode 100644 index 000000000..f1670deac --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12841.d @@ -0,0 +1,25 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12841.d(23): Error: taskPool().amap!"a.result()" is not an lvalue +fail_compilation/ice12841.d(24): Error: amap!"a.result()" is not an lvalue +--- +*/ + +@property TaskPool taskPool() @trusted { return new TaskPool; } + +final class TaskPool +{ + template amap(functions...) + { + auto amap(Args...)(Args args) + { + } + } +} + +void main() +{ + auto dg = &(taskPool.amap!"a.result()"); + auto fp = &(TaskPool.amap!"a.result()"); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12850.d b/gcc/testsuite/gdc.test/fail_compilation/ice12850.d new file mode 100644 index 000000000..9e1cb0e16 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12850.d @@ -0,0 +1,13 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12850.d(12): Error: cannot implicitly convert expression (0) of type int to string +--- +*/ +alias TypeTuple(TL...) = TL; + +void main() +{ + int[string] arr; + alias staticZip = TypeTuple!(arr[0]); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12902.d b/gcc/testsuite/gdc.test/fail_compilation/ice12902.d new file mode 100644 index 000000000..e5ada0947 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12902.d @@ -0,0 +1,21 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12902.d(20): Error: variable ice12902.main.__dollar type void is inferred from initializer s.opDollar(), and variables cannot be of type void +fail_compilation/ice12902.d(20): Error: expression s.opDollar() is void and has no value +--- +*/ + +struct S +{ + void opDollar() { } + void opIndex() { } + void opIndexAssign() { } + void opSliceAssign() { } +} + +void main() +{ + S s; + s[] = s[$]; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice12907.d b/gcc/testsuite/gdc.test/fail_compilation/ice12907.d new file mode 100644 index 000000000..437cec2f7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice12907.d @@ -0,0 +1,11 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice12907.d(10): Error: template lambda has no value +--- +*/ + +auto f(void function() g) +{ + return x => (*g)(); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13024.d b/gcc/testsuite/gdc.test/fail_compilation/ice13024.d new file mode 100644 index 000000000..f86da69cb --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice13024.d @@ -0,0 +1,16 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice13024.d(15): Error: cannot implicitly convert expression (t.x) of type A to B +--- +*/ + +enum A { a } +enum B { b } +struct T { A x; B y; } +void main() +{ + T t; + auto r1 = [cast(int)(t.x), cast(int)(t.y)]; // OK + auto r3 = [t.x, t.y]; // crash +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13027.d b/gcc/testsuite/gdc.test/fail_compilation/ice13027.d new file mode 100644 index 000000000..04ccdf3e4 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice13027.d @@ -0,0 +1,10 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice13027.d(9): Error: template instance b!"c" template 'b' is not defined +--- +*/ +void main() +{ + scope a = b!"c"; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13081.d b/gcc/testsuite/gdc.test/fail_compilation/ice13081.d new file mode 100644 index 000000000..12b348403 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice13081.d @@ -0,0 +1,29 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice13081.d(17): Error: undefined identifier node +fail_compilation/ice13081.d(17): Error: undefined identifier data +fail_compilation/ice13081.d(17): Error: undefined identifier node +fail_compilation/ice13081.d(28): Error: template instance ice13081.Cube!(SparseDataStore) error instantiating +--- +*/ + +struct Cube(StorageT) +{ + StorageT datastore; + alias datastore this; + auto seed() + { + this[] = node.data ? data : node.data; + } +} + +class SparseDataStore +{ + auto opSlice() {} +} + +void main() +{ + Cube!SparseDataStore c; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13131.d b/gcc/testsuite/gdc.test/fail_compilation/ice13131.d new file mode 100644 index 000000000..13b5aff9d --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice13131.d @@ -0,0 +1,20 @@ +// EXTRA_SOURCES: imports/a13131parameters.d imports/a13131elec.d +/* +TEST_OUTPUT: +--- ++A ++B +fail_compilation/imports/a13131elec.d(10): Error: template instance elecConnOf!gconn template 'elecConnOf' is not defined +-B +-A +--- +*/ + +void main() +{ + struct Connectivity {} + auto L = Connectivity(); + + import imports.a13131elec; // [1] import + L.initElec; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13220.d b/gcc/testsuite/gdc.test/fail_compilation/ice13220.d new file mode 100644 index 000000000..3affd5485 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice13220.d @@ -0,0 +1,23 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice13220.d(22): Error: template instance test!0 does not match template declaration test(T)() +--- +*/ + +struct Tuple(T...) +{ + T field; + alias field this; +} + +template test(T) +{ + bool test() { return false; }; +} + +void main() +{ + Tuple!bool t; + t[0] = test!0(); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13221.d b/gcc/testsuite/gdc.test/fail_compilation/ice13221.d new file mode 100644 index 000000000..b00169e40 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice13221.d @@ -0,0 +1,23 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice13221.d(20): Error: variable r cannot be read at compile time +--- +*/ + +struct Tuple(T...) +{ + T field; + alias field this; +} + +template test(T) {} + +void main() +{ + foreach (r; 0 .. 0) + { + enum i = r; + test!(Tuple!bool[i]); + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13225.d b/gcc/testsuite/gdc.test/fail_compilation/ice13225.d new file mode 100644 index 000000000..8582264ac --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice13225.d @@ -0,0 +1,14 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice13225.d(13): Error: undefined identifier undefined +--- +*/ + +mixin template M(T) {} + +struct S +{ + mixin M!((typeof(this)) => 0); + mixin M!(() => undefined); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13259.d b/gcc/testsuite/gdc.test/fail_compilation/ice13259.d new file mode 100644 index 000000000..d6885842e --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice13259.d @@ -0,0 +1,8 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice13259.d(8): Error: non-constant nested delegate literal expression __dgliteral3 +--- +*/ + +auto dg = delegate {}; diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice13311.d b/gcc/testsuite/gdc.test/fail_compilation/ice13311.d new file mode 100644 index 000000000..2cea5dd76 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice13311.d @@ -0,0 +1,12 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/imports/a13311.d(8): Error: undefined identifier PieceTree +--- +*/ +module ice13311; + +struct TextPiece +{ + import imports.a13311; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice6538.d b/gcc/testsuite/gdc.test/fail_compilation/ice6538.d index dad6491c5..0be1df806 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice6538.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice6538.d @@ -6,9 +6,7 @@ /* TEST_OUTPUT: --- -fail_compilation/ice6538.d(23): Error: expression super is not a valid template value argument -fail_compilation/ice6538.d(28): Error: template ice6538.D.foo cannot deduce function from argument types !()(), candidates are: -fail_compilation/ice6538.d(23): ice6538.D.foo()() if (Sym!(super)) +fail_compilation/ice6538.d(21): Error: expression super is not a valid template value argument --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice7645.d b/gcc/testsuite/gdc.test/fail_compilation/ice7645.d index cc967cada..1aa3fad67 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice7645.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice7645.d @@ -2,7 +2,7 @@ TEST_OUTPUT: --- fail_compilation/ice7645.d(28): Error: need 'this' for 't' of type 'char' -fail_compilation/ice7645.d(31): Error: need 'this' for 'fn' of type 'pure nothrow @safe void()' +fail_compilation/ice7645.d(31): Error: need 'this' for 'fn' of type 'pure nothrow @nogc @safe void()' --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice8100.d b/gcc/testsuite/gdc.test/fail_compilation/ice8100.d new file mode 100644 index 000000000..a8a742c24 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice8100.d @@ -0,0 +1,12 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice8100.d(11): Error: class ice8100.Bar!bool.Bar is forward referenced when looking for 'Q' +fail_compilation/ice8100.d(11): Error: template instance ice8100.Foo!(Bar!bool) error instantiating +fail_compilation/ice8100.d(12): instantiated from here: Bar!bool +--- +*/ + +class Foo(T1) { T1.Q r; } +class Bar(T2) : Foo!(Bar!T2) {} +Bar!bool b; diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice8309.d b/gcc/testsuite/gdc.test/fail_compilation/ice8309.d new file mode 100644 index 000000000..5d5bb8329 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice8309.d @@ -0,0 +1,11 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice8309.d(10): Error: incompatible types for ((__lambda1) : (__lambda2)): 'double function() pure nothrow @nogc @safe' and 'int function() pure nothrow @nogc @safe' +--- +*/ + +void main() +{ + auto x = [()=>1.0, ()=>1]; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice8499.d b/gcc/testsuite/gdc.test/fail_compilation/ice8499.d new file mode 100644 index 000000000..08f8fe4ad --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/ice8499.d @@ -0,0 +1,19 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/ice8499.d(18): Error: undefined identifier i +--- +*/ + +struct Variant +{ + @property T get(T)() + { + struct X {} // necessary + } +} + +void main() +{ + (Variant()).get!(typeof(() => i)); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/ice9806.d b/gcc/testsuite/gdc.test/fail_compilation/ice9806.d index 4d4154d48..333d51ce6 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/ice9806.d +++ b/gcc/testsuite/gdc.test/fail_compilation/ice9806.d @@ -25,15 +25,12 @@ void test1() { /* TEST_OUTPUT: --- -fail_compilation/ice9806.d(39): Error: undefined identifier undefined_expr -fail_compilation/ice9806.d(42): Error: CTFE failed because of previous errors in foo2 -fail_compilation/ice9806.d(47): Error: template instance ice9806.S2!() error instantiating -fail_compilation/ice9806.d(40): Error: undefined identifier undefined_expr -fail_compilation/ice9806.d(43): Error: CTFE failed because of previous errors in bar2 -fail_compilation/ice9806.d(49): Error: template instance ice9806.C2!() error instantiating -fail_compilation/ice9806.d(41): Error: undefined identifier undefined_expr -fail_compilation/ice9806.d(44): Error: CTFE failed because of previous errors in baz2 -fail_compilation/ice9806.d(51): Error: template instance ice9806.I2!() error instantiating +fail_compilation/ice9806.d(36): Error: undefined identifier undefined_expr +fail_compilation/ice9806.d(44): Error: template instance ice9806.S2!() error instantiating +fail_compilation/ice9806.d(37): Error: undefined identifier undefined_expr +fail_compilation/ice9806.d(46): Error: template instance ice9806.C2!() error instantiating +fail_compilation/ice9806.d(38): Error: undefined identifier undefined_expr +fail_compilation/ice9806.d(48): Error: template instance ice9806.I2!() error instantiating --- */ int foo2()() { return undefined_expr; } diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/a10169.d b/gcc/testsuite/gdc.test/fail_compilation/imports/a10169.d new file mode 100644 index 000000000..91ec888fe --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/a10169.d @@ -0,0 +1,6 @@ +module imports.a10169; + +struct B +{ + private int x; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/a13131checkpoint.d b/gcc/testsuite/gdc.test/fail_compilation/imports/a13131checkpoint.d new file mode 100644 index 000000000..191fd6bf7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/a13131checkpoint.d @@ -0,0 +1,17 @@ +module imports.a13131checkpoint; + +mixin(createGlobalsMixins); // [3] mixin + +auto createGlobalsMixins() // [4] semantic3 +{ + pragma(msg, "+A"); + enum fullModuleName = "imports.a13131parameters"; // necessary + foreach (e ; __traits(derivedMembers, mixin(fullModuleName))) + { + // [5] see imports.parameters (it's listed in command line) + static if ( __traits(compiles, mixin(`__traits(getAttributes, `~fullModuleName~`.`~e~`)`))) {} + } + pragma(msg, "-A"); + + return ""; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/a13131elec.d b/gcc/testsuite/gdc.test/fail_compilation/imports/a13131elec.d new file mode 100644 index 000000000..658dbb531 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/a13131elec.d @@ -0,0 +1,10 @@ +module imports.a13131elec; + +import imports.a13131checkpoint; // [2] import + +void initElec(T)(T L) +{ + immutable cv = econn.velocities; // econn is invalid so generates ErrorExp +} + +alias econn = elecConnOf!gconn; // invalid declaration diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/a13131parameters.d b/gcc/testsuite/gdc.test/fail_compilation/imports/a13131parameters.d new file mode 100644 index 000000000..d8a5358bd --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/a13131parameters.d @@ -0,0 +1,13 @@ +module imports.a13131parameters; + +auto createParameterMixins() // auto is necessary to invoke semantic3 to calculate full signature +{ + pragma(msg, "+B"); + enum fullModuleName = "imports.a13131elec"; // necessary + foreach (e ; __traits(derivedMembers, mixin(fullModuleName))) + { + // will access yet-not semantic analyzed invalid symbol 'econn' in imports.elec + static if ( __traits(compiles, mixin(`__traits(getAttributes, `~fullModuleName~`.`~e~`)`))) {} + } + pragma(msg, "-B"); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/a13311.d b/gcc/testsuite/gdc.test/fail_compilation/imports/a13311.d new file mode 100644 index 000000000..b4f78a0a6 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/a13311.d @@ -0,0 +1,9 @@ +module imports.a13311; + +import ice13311; + +class Z +{ + TextPiece piece; + this(PieceTree owner) {} +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/diag12598a.d b/gcc/testsuite/gdc.test/fail_compilation/imports/diag12598a.d new file mode 100644 index 000000000..dee005452 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/diag12598a.d @@ -0,0 +1,3 @@ +module imports.diag12598a; + +struct lines { } diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152a.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152a.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152a.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152b.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152b.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152b.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152c.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152c.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152c.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152d.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152d.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152d.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152e.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152e.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152e.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152f.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152f.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152f.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152g.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152g.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152g.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152h.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152h.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152h.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152i.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152i.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152i.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152j.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152j.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152j.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152k.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152k.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152k.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152l.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152l.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152l.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152m.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152m.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152m.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152n.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152n.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152n.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152o.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152o.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152o.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152p.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152p.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152p.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152q.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152q.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152q.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152r.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152r.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152r.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152s.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152s.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152s.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152t.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152t.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152t.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152u.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152u.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152u.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152v.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152v.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152v.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152w.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152w.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152w.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152x.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152x.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152x.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152y.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152y.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152y.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/imports/test13152z.d b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152z.d new file mode 100644 index 000000000..a91ff0df7 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/imports/test13152z.d @@ -0,0 +1,26 @@ +public import imports.test13152a; +public import imports.test13152b; +public import imports.test13152c; +public import imports.test13152d; +public import imports.test13152e; +public import imports.test13152f; +public import imports.test13152g; +public import imports.test13152h; +public import imports.test13152i; +public import imports.test13152j; +public import imports.test13152k; +public import imports.test13152l; +public import imports.test13152m; +public import imports.test13152n; +public import imports.test13152o; +public import imports.test13152p; +public import imports.test13152q; +public import imports.test13152r; +public import imports.test13152s; +public import imports.test13152t; +public import imports.test13152u; +public import imports.test13152v; +public import imports.test13152w; +public import imports.test13152x; +public import imports.test13152y; +public import imports.test13152z; diff --git a/gcc/testsuite/gdc.test/fail_compilation/nogc1.d b/gcc/testsuite/gdc.test/fail_compilation/nogc1.d new file mode 100644 index 000000000..2e3f62b75 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/nogc1.d @@ -0,0 +1,85 @@ +// REQUIRED_ARGS: -o- +// PERMUTE_ARGS: + +/***************** NewExp *******************/ + +struct S1 { } +struct S2 { this(int); } +struct S3 { this(int) @nogc; } +struct S4 { new(size_t); } +struct S5 { @nogc new(size_t); } + +/* +TEST_OUTPUT: +--- +fail_compilation/nogc1.d(27): Error: cannot use 'new' in @nogc function testNew +fail_compilation/nogc1.d(29): Error: cannot use 'new' in @nogc function testNew +fail_compilation/nogc1.d(30): Error: cannot use 'new' in @nogc function testNew +fail_compilation/nogc1.d(32): Error: cannot use 'new' in @nogc function testNew +fail_compilation/nogc1.d(33): Error: @nogc function 'nogc1.testNew' cannot call non-@nogc function 'nogc1.S2.this' +fail_compilation/nogc1.d(34): Error: cannot use 'new' in @nogc function testNew +fail_compilation/nogc1.d(35): Error: operator new in @nogc function testNew may allocate +fail_compilation/nogc1.d(38): Error: cannot use 'new' in @nogc function testNew +--- +*/ +@nogc void testNew() +{ + int* p1 = new int; + + int[] a1 = new int[3]; + int[][] a2 = new int[][](2, 3); + + S1* ps1 = new S1(); + S2* ps2 = new S2(1); + S3* ps3 = new S3(1); + S4* ps4 = new S4; + S5* ps5 = new S5; // no error + + Object o1 = new Object(); +} + +/* +TEST_OUTPUT: +--- +fail_compilation/nogc1.d(55): Error: cannot use 'new' in @nogc function testNewScope +fail_compilation/nogc1.d(57): Error: cannot use 'new' in @nogc function testNewScope +fail_compilation/nogc1.d(58): Error: cannot use 'new' in @nogc function testNewScope +fail_compilation/nogc1.d(60): Error: cannot use 'new' in @nogc function testNewScope +fail_compilation/nogc1.d(61): Error: @nogc function 'nogc1.testNewScope' cannot call non-@nogc function 'nogc1.S2.this' +fail_compilation/nogc1.d(62): Error: cannot use 'new' in @nogc function testNewScope +fail_compilation/nogc1.d(63): Error: operator new in @nogc function testNewScope may allocate +--- +*/ +@nogc void testNewScope() +{ + scope int* p1 = new int; + + scope int[] a1 = new int[3]; + scope int[][] a2 = new int[][](2, 3); + + scope S1* ps1 = new S1(); + scope S2* ps2 = new S2(1); + scope S3* ps3 = new S3(1); + scope S4* ps4 = new S4; + scope S5* ps5 = new S5; // no error + + scope Object o1 = new Object(); // no error + scope o2 = new Object(); // no error +} + +/***************** DeleteExp *******************/ + +/* +TEST_OUTPUT: +--- +fail_compilation/nogc1.d(82): Error: cannot use 'delete' in @nogc function testDelete +fail_compilation/nogc1.d(83): Error: cannot use 'delete' in @nogc function testDelete +fail_compilation/nogc1.d(84): Error: cannot use 'delete' in @nogc function testDelete +--- +*/ +@nogc void testDelete(int* p, Object o, S1* s) +{ + delete p; + delete o; + delete s; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/nogc2.d b/gcc/testsuite/gdc.test/fail_compilation/nogc2.d new file mode 100644 index 000000000..432cd3db2 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/nogc2.d @@ -0,0 +1,104 @@ +// REQUIRED_ARGS: -o- +// PERMUTE_ARGS: + +/***************** CatExp *******************/ + +/* +TEST_OUTPUT: +--- +fail_compilation/nogc2.d(21): Error: cannot use operator ~ in @nogc function testCat +fail_compilation/nogc2.d(22): Error: cannot use operator ~ in @nogc function testCat +fail_compilation/nogc2.d(23): Error: cannot use operator ~ in @nogc function testCat +fail_compilation/nogc2.d(25): Error: cannot use operator ~ in @nogc function testCat +fail_compilation/nogc2.d(26): Error: cannot use operator ~ in @nogc function testCat +fail_compilation/nogc2.d(27): Error: cannot use operator ~ in @nogc function testCat +fail_compilation/nogc2.d(28): Error: cannot use operator ~ in @nogc function testCat +fail_compilation/nogc2.d(29): Error: cannot use operator ~ in @nogc function testCat +--- +*/ +@nogc void testCat(int[] a, string s) +{ + int[] a1 = a ~ a; + int[] a2 = a ~ 1; + int[] a3 = 1 ~ a; + + string s1 = s ~ s; + string s2 = s ~ "a"; + string s3 = "a" ~ s; + string s4 = s ~ 'c'; + string s5 = 'c' ~ s; + + string s6 = "a" ~ "b"; // no error + string s7 = "a" ~ 'c'; // no error + string s8 = 'c' ~ "b"; // no error +} + +/***************** CatAssignExp *******************/ + +/* +TEST_OUTPUT: +--- +fail_compilation/nogc2.d(48): Error: cannot use operator ~= in @nogc function testCatAssign +fail_compilation/nogc2.d(50): Error: cannot use operator ~= in @nogc function testCatAssign +fail_compilation/nogc2.d(51): Error: cannot use operator ~= in @nogc function testCatAssign +--- +*/ +@nogc void testCatAssign(int[] a, string s) +{ + a ~= 1; + + s ~= "a"; + s ~= 'c'; +} + +/***************** ArrayLiteralExp *******************/ + +@nogc int* barA(); + +/* +TEST_OUTPUT: +--- +fail_compilation/nogc2.d(70): Error: array literal in @nogc function testArray may cause GC allocation +fail_compilation/nogc2.d(71): Error: array literal in @nogc function testArray may cause GC allocation +--- +*/ +@nogc void testArray() +{ + enum arrLiteral = [null, null]; + + int* p; + auto a = [p, p, barA()]; + a = arrLiteral; +} + +/***************** AssocArrayLiteralExp *******************/ + +/* +TEST_OUTPUT: +--- +fail_compilation/nogc2.d(87): Error: associative array literal in @nogc function testAssocArray may cause GC allocation +fail_compilation/nogc2.d(88): Error: associative array literal in @nogc function testAssocArray may cause GC allocation +--- +*/ +@nogc void testAssocArray() +{ + enum aaLiteral = [10: 100]; + + auto aa = [1:1, 2:3, 4:5]; + aa = aaLiteral; +} + +/***************** IndexExp *******************/ + +/* +TEST_OUTPUT: +--- +fail_compilation/nogc2.d(102): Error: indexing an associative array in @nogc function testIndex may cause GC allocation +fail_compilation/nogc2.d(103): Error: indexing an associative array in @nogc function testIndex may cause GC allocation +--- +*/ +@nogc void testIndex(int[int] aa) +{ + aa[1] = 0; + int n = aa[1]; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/nogc3.d b/gcc/testsuite/gdc.test/fail_compilation/nogc3.d new file mode 100644 index 000000000..51e7bc47f --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/nogc3.d @@ -0,0 +1,68 @@ +// REQUIRED_ARGS: -o- +// PERMUTE_ARGS: + +/***************** AssignExp *******************/ + +/* +TEST_OUTPUT: +--- +fail_compilation/nogc3.d(16): Error: setting 'length' in @nogc function testArrayLength may cause GC allocation +fail_compilation/nogc3.d(17): Error: setting 'length' in @nogc function testArrayLength may cause GC allocation +fail_compilation/nogc3.d(18): Error: setting 'length' in @nogc function testArrayLength may cause GC allocation +--- +*/ +@nogc void testArrayLength(int[] a) +{ + a.length = 3; + a.length += 1; + a.length -= 1; +} + +/***************** CallExp *******************/ + +void barCall(); + +/* +TEST_OUTPUT: +--- +fail_compilation/nogc3.d(35): Error: @nogc function 'nogc3.testCall' cannot call non-@nogc function pointer 'fp' +fail_compilation/nogc3.d(36): Error: @nogc function 'nogc3.testCall' cannot call non-@nogc function 'nogc3.barCall' +--- +*/ +@nogc void testCall() +{ + auto fp = &barCall; + (*fp)(); + barCall(); +} + +/****************** Closure ***********************/ + +@nogc void takeDelegate2(scope int delegate() dg) {} +@nogc void takeDelegate3( int delegate() dg) {} + +/* +TEST_OUTPUT: +--- +fail_compilation/nogc3.d(51): Error: function nogc3.testClosure1 @nogc function allocates a closure with the GC +fail_compilation/nogc3.d(63): Error: function nogc3.testClosure3 @nogc function allocates a closure with the GC +--- +*/ +@nogc auto testClosure1() +{ + int x; + int bar() { return x; } + return &bar; +} +@nogc void testClosure2() +{ + int x; + int bar() { return x; } + takeDelegate2(&bar); // no error +} +@nogc void testClosure3() +{ + int x; + int bar() { return x; } + takeDelegate3(&bar); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/parse12924.d b/gcc/testsuite/gdc.test/fail_compilation/parse12924.d new file mode 100644 index 000000000..dacda4720 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/parse12924.d @@ -0,0 +1,20 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/parse12924.d(14): Error: declaration expected following attribute, not ';' +fail_compilation/parse12924.d(15): Error: declaration expected following attribute, not ';' +fail_compilation/parse12924.d(16): Error: declaration expected following attribute, not ';' +fail_compilation/parse12924.d(17): Error: declaration expected following attribute, not ';' +fail_compilation/parse12924.d(18): Error: declaration expected following attribute, not ';' +fail_compilation/parse12924.d(19): Error: declaration expected following attribute, not ';' +fail_compilation/parse12924.d(20): Error: declaration expected following attribute, not ';' +--- +*/ + +static; void f1() {} +deprecated; void f2() {} +deprecated(""); void f3() {} +extern(C); void f4() {} +public; void f5() {} +align(1); void f6() {} +@(1); void f7() {} diff --git a/gcc/testsuite/gdc.test/fail_compilation/parse12967a.d b/gcc/testsuite/gdc.test/fail_compilation/parse12967a.d new file mode 100644 index 000000000..90bccb783 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/parse12967a.d @@ -0,0 +1,43 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/parse12967a.d(14): Error: function parse12967a.pre_i1 without 'this' cannot be immutable +fail_compilation/parse12967a.d(15): Error: function parse12967a.pre_i2 without 'this' cannot be immutable +fail_compilation/parse12967a.d(16): Error: function parse12967a.pre_c1 without 'this' cannot be const +fail_compilation/parse12967a.d(17): Error: function parse12967a.pre_c2 without 'this' cannot be const +fail_compilation/parse12967a.d(18): Error: function parse12967a.pre_w1 without 'this' cannot be inout +fail_compilation/parse12967a.d(19): Error: function parse12967a.pre_w2 without 'this' cannot be inout +fail_compilation/parse12967a.d(20): Error: function parse12967a.pre_s1 without 'this' cannot be shared +fail_compilation/parse12967a.d(21): Error: function parse12967a.pre_s2 without 'this' cannot be shared +--- +*/ +immutable pre_i1() {} +immutable void pre_i2() {} +const pre_c1() {} +const void pre_c2() {} +inout pre_w1() {} +inout void pre_w2() {} +shared pre_s1() {} +shared void pre_s2() {} + +/* +TEST_OUTPUT: +--- +fail_compilation/parse12967a.d(36): Error: function parse12967a.post_i1 without 'this' cannot be immutable +fail_compilation/parse12967a.d(37): Error: function parse12967a.post_i2 without 'this' cannot be immutable +fail_compilation/parse12967a.d(38): Error: function parse12967a.post_c1 without 'this' cannot be const +fail_compilation/parse12967a.d(39): Error: function parse12967a.post_c2 without 'this' cannot be const +fail_compilation/parse12967a.d(40): Error: function parse12967a.post_w1 without 'this' cannot be inout +fail_compilation/parse12967a.d(41): Error: function parse12967a.post_w2 without 'this' cannot be inout +fail_compilation/parse12967a.d(42): Error: function parse12967a.post_s1 without 'this' cannot be shared +fail_compilation/parse12967a.d(43): Error: function parse12967a.post_s2 without 'this' cannot be shared +--- +*/ +auto post_i1() immutable {} +void post_i2() immutable {} +auto post_c1() const {} +void post_c2() const {} +auto post_w1() inout {} +void post_w2() inout {} +auto post_s1() shared {} +void post_s2() shared {} diff --git a/gcc/testsuite/gdc.test/fail_compilation/parse12967b.d b/gcc/testsuite/gdc.test/fail_compilation/parse12967b.d new file mode 100644 index 000000000..60d9d0994 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/parse12967b.d @@ -0,0 +1,41 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/parse12967b.d(24): Error: function parse12967b.C.pre_c without 'this' cannot be const +fail_compilation/parse12967b.d(25): Error: function parse12967b.C.pre_c without 'this' cannot be const +fail_compilation/parse12967b.d(26): Error: function parse12967b.C.pre_i without 'this' cannot be immutable +fail_compilation/parse12967b.d(27): Error: function parse12967b.C.pre_i without 'this' cannot be immutable +fail_compilation/parse12967b.d(28): Error: function parse12967b.C.pre_w without 'this' cannot be inout +fail_compilation/parse12967b.d(29): Error: function parse12967b.C.pre_w without 'this' cannot be inout +fail_compilation/parse12967b.d(30): Error: function parse12967b.C.pre_s without 'this' cannot be shared +fail_compilation/parse12967b.d(31): Error: function parse12967b.C.pre_s without 'this' cannot be shared +fail_compilation/parse12967b.d(33): Error: function parse12967b.C.post_c without 'this' cannot be const +fail_compilation/parse12967b.d(34): Error: function parse12967b.C.post_c without 'this' cannot be const +fail_compilation/parse12967b.d(35): Error: function parse12967b.C.post_i without 'this' cannot be immutable +fail_compilation/parse12967b.d(36): Error: function parse12967b.C.post_i without 'this' cannot be immutable +fail_compilation/parse12967b.d(37): Error: function parse12967b.C.post_w without 'this' cannot be inout +fail_compilation/parse12967b.d(38): Error: function parse12967b.C.post_w without 'this' cannot be inout +fail_compilation/parse12967b.d(39): Error: function parse12967b.C.post_s without 'this' cannot be shared +fail_compilation/parse12967b.d(40): Error: function parse12967b.C.post_s without 'this' cannot be shared +--- +*/ +class C +{ + const static pre_c() {} + const static void pre_c() {} + immutable static pre_i() {} + immutable static void pre_i() {} + inout static pre_w() {} + inout static void pre_w() {} + shared static pre_s() {} + shared static void pre_s() {} + + static post_c() const {} + static void post_c() const {} + static post_i() immutable {} + static void post_i() immutable {} + static post_w() inout {} + static void post_w() inout {} + static post_s() shared {} + static void post_s() shared {} +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/parseStc2.d b/gcc/testsuite/gdc.test/fail_compilation/parseStc2.d new file mode 100644 index 000000000..a0c66442a --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/parseStc2.d @@ -0,0 +1,67 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/parseStc2.d(11): Error: conflicting storage class const +fail_compilation/parseStc2.d(12): Error: conflicting attribute @system +fail_compilation/parseStc2.d(13): Error: conflicting attribute @safe +fail_compilation/parseStc2.d(14): Error: conflicting attribute @trusted +fail_compilation/parseStc2.d(15): Error: conflicting storage class __gshared +--- +*/ +immutable const void f4() {} +@safe @system void f4() {} +@trusted @safe void f4() {} +@system @trusted void f4() {} +shared __gshared f4() {} + +/* +TEST_OUTPUT: +--- +fail_compilation/parseStc2.d(26): Error: redundant storage class 'static' +fail_compilation/parseStc2.d(27): Error: redundant storage class 'pure' +fail_compilation/parseStc2.d(28): Error: redundant storage class '@property' +fail_compilation/parseStc2.d(29): Error: redundant storage class '@safe' +--- +*/ +static static void f1() {} +pure nothrow pure void f2() {} +@property extern(C) @property void f3() {} +deprecated("") @safe @safe void f4() {} +@(1) @(1) void f5() {} // OK + +/* +TEST_OUTPUT: +--- +fail_compilation/parseStc2.d(39): Error: redundant linkage extern (C) +fail_compilation/parseStc2.d(40): Error: conflicting linkage extern (C) and extern (C++) +--- +*/ +extern(C) extern(C) void f6() {} +extern(C) extern(C++) void f7() {} +extern(C++, foo) extern(C++, bar) void f8() {} // OK + +/* +TEST_OUTPUT: +--- +fail_compilation/parseStc2.d(50): Error: redundant protection attribute 'public' +fail_compilation/parseStc2.d(51): Error: conflicting protection attribute 'public' and 'private' +--- +*/ +public public void f9() {} +public private void f10() {} + +/* +TEST_OUTPUT: +--- +fail_compilation/parseStc2.d(63): Error: redundant alignment attribute align +fail_compilation/parseStc2.d(64): Error: redundant alignment attribute align(1) +fail_compilation/parseStc2.d(65): Error: conflicting alignment attribute align and align(1) +fail_compilation/parseStc2.d(66): Error: conflicting alignment attribute align(1) and align +fail_compilation/parseStc2.d(67): Error: conflicting alignment attribute align(1) and align(2) +--- +*/ +align align void f11() {} +align(1) align(1) void f12() {} +align align(1) void f13() {} +align(1) align void f14() {} +align(1) align(2) void f15() {} diff --git a/gcc/testsuite/gdc.test/fail_compilation/parseStc3.d b/gcc/testsuite/gdc.test/fail_compilation/parseStc3.d new file mode 100644 index 000000000..167d9970a --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/parseStc3.d @@ -0,0 +1,62 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/parseStc3.d(10): Error: redundant storage class 'pure' +fail_compilation/parseStc3.d(11): Error: redundant storage class 'nothrow' +fail_compilation/parseStc3.d(12): Error: redundant storage class '@nogc' +fail_compilation/parseStc3.d(13): Error: redundant storage class '@property' +--- +*/ +pure void f1() pure {} +nothrow void f2() nothrow {} +@nogc void f3() @nogc {} +@property void f4() @property {} +//ref int f5() ref { static int g; return g; } + +/* +TEST_OUTPUT: +--- +fail_compilation/parseStc3.d(24): Error: redundant storage class '@safe' +fail_compilation/parseStc3.d(25): Error: redundant storage class '@system' +fail_compilation/parseStc3.d(26): Error: redundant storage class '@trusted' +--- +*/ +@safe void f6() @safe {} +@system void f7() @system {} +@trusted void f8() @trusted {} + +/* +TEST_OUTPUT: +--- +fail_compilation/parseStc3.d(39): Error: conflicting storage class '@system' +fail_compilation/parseStc3.d(40): Error: conflicting storage class '@trusted' +fail_compilation/parseStc3.d(41): Error: conflicting storage class '@safe' +fail_compilation/parseStc3.d(42): Error: conflicting storage class '@trusted' +fail_compilation/parseStc3.d(43): Error: conflicting storage class '@safe' +fail_compilation/parseStc3.d(44): Error: conflicting storage class '@system' +--- +*/ +@safe void f9() @system {} +@safe void f10() @trusted {} +@system void f11() @safe {} +@system void f12() @trusted {} +@trusted void f13() @safe {} +@trusted void f14() @system {} + +/* +TEST_OUTPUT: +--- +fail_compilation/parseStc3.d(59): Error: conflicting attribute @system +fail_compilation/parseStc3.d(59): Error: conflicting storage class '@trusted' +fail_compilation/parseStc3.d(60): Error: conflicting attribute @system +fail_compilation/parseStc3.d(60): Error: redundant storage class '@system' +fail_compilation/parseStc3.d(61): Error: conflicting attribute @safe +fail_compilation/parseStc3.d(61): Error: redundant storage class '@system' +fail_compilation/parseStc3.d(62): Error: conflicting attribute @safe +fail_compilation/parseStc3.d(62): Error: redundant storage class '@trusted' +--- +*/ +@safe @system void f15() @trusted {} +@safe @system void f16() @system {} +@system @safe void f17() @system {} +@trusted @safe void f18() @trusted {} diff --git a/gcc/testsuite/gdc.test/fail_compilation/spell9644.d b/gcc/testsuite/gdc.test/fail_compilation/spell9644.d index 1cad46ffa..b8c9cf027 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/spell9644.d +++ b/gcc/testsuite/gdc.test/fail_compilation/spell9644.d @@ -8,7 +8,7 @@ fail_compilation/spell9644.d(21): Error: undefined identifier b fail_compilation/spell9644.d(22): Error: undefined identifier xx fail_compilation/spell9644.d(23): Error: undefined identifier cb, did you mean variable ab? fail_compilation/spell9644.d(24): Error: undefined identifier bc, did you mean variable abc? -fail_compilation/spell9644.d(25): Error: undefined identifier ccc, did you mean variable abc? +fail_compilation/spell9644.d(25): Error: undefined identifier ccc --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test12979.d b/gcc/testsuite/gdc.test/fail_compilation/test12979.d new file mode 100644 index 000000000..9bf5cf40a --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test12979.d @@ -0,0 +1,16 @@ +// PERMUTE_ARGS: + +/* +TEST_OUTPUT: +--- +fail_compilation/test12979.d(13): Error: const/immutable/shared/inout attributes are not allowed on asm blocks +--- +*/ + +void foo() +{ + asm const shared + { + ret; + } +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test13152.d b/gcc/testsuite/gdc.test/fail_compilation/test13152.d new file mode 100644 index 000000000..dffd0f349 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/test13152.d @@ -0,0 +1,12 @@ +/* +TEST_OUTPUT: +--- +fail_compilation/test13152.d(11): Error: undefined identifier x +--- +*/ +import imports.test13152a; + +void main() +{ + auto y = x; +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/test64.d b/gcc/testsuite/gdc.test/fail_compilation/test64.d index 204f4e051..bfc63f29f 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test64.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test64.d @@ -1,19 +1,19 @@ /* TEST_OUTPUT: --- -Error: module imports from file fail_compilation/imports/test64a.d conflicts with package name imports +fail_compilation/imports/test64a.d(1): Error: module imports from file fail_compilation/imports/test64a.d conflicts with package name imports --- */ // PERMUTE_ARGS: -import std.stdio; +//import std.stdio; import imports.test64a; -int main(char[][] args) +int main(string[] args) { - writefln(file1); + //writefln(file1); return 0; } diff --git a/gcc/testsuite/gdc.test/fail_compilation/test8556.d b/gcc/testsuite/gdc.test/fail_compilation/test8556.d index 602212a5e..a8b9aa37f 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test8556.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test8556.d @@ -1,11 +1,16 @@ /* TEST_OUTPUT: --- -fail_compilation/test8556.d(22): Error: template instance test8556.Grab!(Circle!(uint[])) does not match template declaration Grab(Range) if (!isSliceable!Range) -fail_compilation/test8556.d(53): Error: template instance test8556.grab!(Circle!(uint[])) error instantiating +fail_compilation/test8556.d(33): Error: circular initialization of isSliceable +fail_compilation/test8556.d(34): Error: circular initialization of isSliceable +fail_compilation/test8556.d(22): Error: template instance test8556.isSliceable!(Circle!(uint[])) error instantiating +fail_compilation/test8556.d(27): while looking for match for Grab!(Circle!(uint[])) +fail_compilation/test8556.d(58): Error: template instance test8556.grab!(Circle!(uint[])) error instantiating +fail_compilation/test8556.d(61): Error: Circle!(uint[]) cannot be sliced with [] --- */ + extern(C) int printf(const char*, ...); //+ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test8793.d b/gcc/testsuite/gdc.test/fail_compilation/test8793.d index aa2eb0bed..a541ad514 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test8793.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test8793.d @@ -3,7 +3,7 @@ TEST_OUTPUT: --- fail_compilation/test8793.d(13): Error: cannot implicitly convert expression (__lambda2) of type bool delegate(const(int) x) @system to bool delegate(const(int)) pure fail_compilation/test8793.d(14): Error: cannot implicitly convert expression (__lambda2) of type bool delegate(const(int) x) @system to bool delegate(const(int)) pure -fail_compilation/test8793.d(16): Error: cannot implicitly convert expression (__lambda2) of type bool delegate(const(int) x) nothrow @safe to bool delegate(const(int)) pure +fail_compilation/test8793.d(16): Error: cannot implicitly convert expression (__lambda2) of type bool delegate(const(int) x) nothrow @nogc @safe to bool delegate(const(int)) pure --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/test9176.d b/gcc/testsuite/gdc.test/fail_compilation/test9176.d index 34ed48d18..74931f382 100644 --- a/gcc/testsuite/gdc.test/fail_compilation/test9176.d +++ b/gcc/testsuite/gdc.test/fail_compilation/test9176.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/test9176.d(12): Error: forward reference to get +fail_compilation/test9176.d(12): Error: forward reference to inferred return type of function call 'get' --- */ diff --git a/gcc/testsuite/gdc.test/fail_compilation/testCols.d b/gcc/testsuite/gdc.test/fail_compilation/testCols.d new file mode 100644 index 000000000..68f67f705 --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/testCols.d @@ -0,0 +1,14 @@ +// REQUIRED_ARGS: -vcolumns +// PERMUTE_ARGS: + +/* +TEST_OUTPUT: +--- +fail_compilation/testCols.d(13,5): Error: undefined identifier nonexistent +--- +*/ + +void test() +{ + nonexistent(); +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/warn12809.d b/gcc/testsuite/gdc.test/fail_compilation/warn12809.d new file mode 100644 index 000000000..100f0fb9f --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/warn12809.d @@ -0,0 +1,34 @@ +// REQUIRED_ARGS: -o- -w + +/* +TEST_OUTPUT: +--- +fail_compilation/warn12809.d(25): Warning: statement is not reachable +fail_compilation/warn12809.d(33): Warning: statement is not reachable +--- +*/ + +//void test_unrachable1() +//{ +// try assert(0); +// finally +// { +// int x = 1; // unreachable +// } +//} + +void test_unrachable2() +{ + try assert(0); + finally {} + + int x = 1; // unreachable +} + +void test_unrachable3() +{ + try {} + finally assert(0); + + int x = 1; // unreachable +} diff --git a/gcc/testsuite/gdc.test/fail_compilation/warn1553.d b/gcc/testsuite/gdc.test/fail_compilation/warn1553.d new file mode 100644 index 000000000..dbe22cd9f --- /dev/null +++ b/gcc/testsuite/gdc.test/fail_compilation/warn1553.d @@ -0,0 +1,20 @@ +// REQUIRED_ARGS: -w +// PERMUTE_ARGS: + +/* +TEST_OUTPUT: +--- +fail_compilation/warn1553.d(19): Warning: cannot use foreach_reverse with a delegate +--- +*/ + +struct S +{ + int dg(int delegate(ref int a)) { return 0; } +} + +void main() +{ + S s; + foreach_reverse(a; &s.dg) {} +} diff --git a/gcc/testsuite/gdc.test/runnable/aliasthis.d b/gcc/testsuite/gdc.test/runnable/aliasthis.d index d7de13b31..256fc19f3 100644 --- a/gcc/testsuite/gdc.test/runnable/aliasthis.d +++ b/gcc/testsuite/gdc.test/runnable/aliasthis.d @@ -946,39 +946,6 @@ void test6711() i = 42; } assert(c.i == 42); - - struct Foo - { - string[string] strs; - alias strs this; - } - - struct Bar - { - Foo f; - alias f this; - } - - void test(T)() - { - T f; - f = ["first" : "a", "second" : "b"]; - with (f) - { - assert(length == 2); - rehash; - auto vs = values; - assert(vs == ["a", "b"] || vs == ["b", "a"]); - auto ks = keys; - assert(ks == ["first", "second"] || ks == ["second", "first"]); - foreach (k; byKey) { } - foreach (v; byValue) { } - assert(get("a", "default") == "default"); - } - } - - test!Foo; - test!Bar; } /**********************************************/ @@ -1301,11 +1268,11 @@ void test8735() // 9709 case alias A = Tuple9709!(1,int,"foo"); A a; - static assert(A[0] == 1); + //static assert(A[0] == 1); static assert(a[0] == 1); //static assert(is(A[1] == int)); //static assert(is(a[1] == int)); - static assert(A[2] == "foo"); + //static assert(A[2] == "foo"); static assert(a[2] == "foo"); } @@ -1619,6 +1586,32 @@ void test11261() } } +/***************************************************/ +// 11333 + +alias id11333(a...) = a; + +struct Unit11333 +{ + enum value = Unit11333.init.tupleof; + alias value this; +} + +void test11333() +{ + void foo() {} + + id11333!() unit; + unit = unit; // ok + foo(unit); // ok + + unit = Unit11333.value; // ok + foo(Unit11333.value); // ok + + Unit11333 unit2; + unit = unit2; // ok <- segfault +} + /***************************************************/ // 11800 @@ -1758,6 +1751,7 @@ int main() test10004(); test10180(); test10456(); + test11333(); test11800(); printf("Success\n"); diff --git a/gcc/testsuite/gdc.test/runnable/arrayop.d b/gcc/testsuite/gdc.test/runnable/arrayop.d index 46f6af043..f29bd4708 100644 --- a/gcc/testsuite/gdc.test/runnable/arrayop.d +++ b/gcc/testsuite/gdc.test/runnable/arrayop.d @@ -736,6 +736,50 @@ void test11525() assert(a.length == 1 && a[0].re == 0 && a[0].im == 8); } +/************************************************************************/ +// 12250 + +void f12250(inout int[] p, inout int[] q, int[] r) +{ + r[] = p[] + q[]; + assert(r == [5,7,9]); + r[] -= p[] - q[]; + assert(r == [8,10,12]); +} + +void test12250() +{ + immutable int[3] x = [1,2,3], y = [4,5,6]; + int[3] z; + f12250(x[], y[], z[]); +} + +/************************************************************************/ +// 12179 + +void test12179() +{ + void foo(int[]) {} + int[1] a; + + foo(a[] = a[]); + foo(a[] += a[]); + foo(a[] -= a[]); + foo(a[] *= a[]); + foo(a[] /= a[]); + foo(a[] %= a[]); + foo(a[] ^= a[]); + foo(a[] &= a[]); + foo(a[] |= a[]); + foo(a[] ^^= a[]); + + // from issue 11992 + int[] arr1; + int[][] arr2; + arr1 ~= (a[] = [1] + a[]); // OK + arr2 ~= (a[] = [1] + a[]); // OK +} + /************************************************************************/ int main() @@ -766,6 +810,7 @@ int main() test10684a(); test10684b(); test11525(); + test12250(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/assignable.d b/gcc/testsuite/gdc.test/runnable/assignable.d index 8fc05bc79..0cd51373e 100644 --- a/gcc/testsuite/gdc.test/runnable/assignable.d +++ b/gcc/testsuite/gdc.test/runnable/assignable.d @@ -1,4 +1,4 @@ -import std.c.stdio; +import core.stdc.stdio; template TypeTuple(T...){ alias T TypeTuple; } @@ -829,6 +829,262 @@ void test11187() static assert(is(const S : S)); } +/***************************************************/ +// 12131 + +struct X12131 +{ + void opAssign()(X12131 y) pure {} +} + +struct Y12131 +{ + X12131 a; +} + +void test12131() pure +{ + X12131 x; + x = X12131(); // OK + + Y12131 y; + y = Y12131(); // OK <- Error +} + +/***************************************************/ +// 12211 + +void test12211() +{ + int a = 0; + void foo(ref int x) + { + assert(x == 10); + assert(&x == &a); + x = 3; + } + foo(a = 10); + assert(a == 3); + foo(a += 7); + assert(a == 3); + + // array ops should make rvalue + int[3] sa, sb; + void bar(ref int[]) {} + static assert(!__traits(compiles, bar(sa[] = sb[]))); + static assert(!__traits(compiles, bar(sa[] += sb[]))); +} + +/***************************************************/ +// 4791 (dup of 12212) + +void test4791() +{ + int[2] na; + na = na; + + static struct S + { + static string res; + int n; + this(this) { ++n; res ~= "p" ~ cast(char)('0' + n); } + ~this() { res ~= "d" ~ cast(char)('0' + n); } + } + { + S[3] sa; + sa[0].n = 1, sa[1].n = 2, sa[2].n = 3; + + S.res = null; + sa = sa; + assert(S.res == "p2d1p3d2p4d3"); + assert(sa[0].n == 2 && sa[1].n == 3 && sa[2].n == 4); + + S.res = null; + } + assert(S.res == "d4d3d2"); +} + +/***************************************************/ +// 12212 + +void test12212() +{ + struct S + { + int x, y; + static int cpctor; + this(this) { cpctor++; } + } + + void funcVal(E)(E[3] x) {} + auto funcRef(E)(ref E[3] x) { return &x; } + ref get(E)(ref E[3] a){ return a; } + + { + int[3] a, b; + funcVal(a = b); + + auto p = funcRef(a = b); + assert(p == &a); + } + + { + S.cpctor = 0; + + S[3] a, b; + assert(S.cpctor == 0); + + S[3] c = a; + //printf("cpctpr = %d\n", S.cpctor); + assert(S.cpctor == 3); + S.cpctor = 0; + + c = a; + //printf("cpctpr = %d\n", S.cpctor); + assert(S.cpctor == 3); + S.cpctor = 0; + + c = (a = b); + //printf("cpctpr = %d\n", S.cpctor); + assert(S.cpctor == 6); + S.cpctor = 0; + + c = (get(a) = b); + //printf("cpctpr = %d\n", S.cpctor); + assert(S.cpctor == 6); + S.cpctor = 0; + } + { + S.cpctor = 0; + + S[3] a, b; + assert(S.cpctor == 0); + + funcVal(a = b); + //printf("cpctpr = %d\n", S.cpctor); + assert(S.cpctor == 6); + S.cpctor = 0; + + funcVal(get(a) = b); + //printf("cpctpr = %d\n", S.cpctor); + assert(S.cpctor == 6); + S.cpctor = 0; + } + { + S.cpctor = 0; + + S[3] a, b; + assert(S.cpctor == 0); + + S[3]* p; + + p = funcRef(a = b); + //printf("cpctpr = %d\n", S.cpctor); + assert(p == &a); + assert(S.cpctor == 3); + S.cpctor = 0; + + p = funcRef(get(a) = b); + assert(p == &a); + //printf("cpctpr = %d\n", S.cpctor); + assert(S.cpctor == 3); + S.cpctor = 0; + } +} + +/***************************************************/ +// 12650 + +void test12650() +{ + // AssignExp::toElem should make an lvalue of e1. + static class A1 + { + struct S { int a; } + + static foo(ref const(S) s) + { + assert(s.a == 2); + return &s; + } + + S s; + + this() + { + const v = S(2); + + // (this.s = v) will become ConstructExp + auto p = foo(s = v); + assert(p == &s); + } + } + assert(new A1().s.a == 2); + + static class A2 + { + static foo(ref int[2] sa) + { + assert(sa[1] == 2); + return &sa; + } + + int[2] sa; + + this() + { + // (this.sa = [1,2]) will become ConstructExp + auto p = foo(sa = [1,2]); + assert(p == &sa); + } + } + assert(new A2().sa[1] == 2); + + static class A3 + { + static foo(ref int n) + { + assert(n == 2); + return &n; + } + + int n; + + this() + { + const v = 2; + + // (this.n = v) will become ConstructExp + auto p = foo(n = v); + assert(p == &n); + } + } + assert(new A3().n == 2); +} + +/***************************************************/ +// 13044 + +void test13044() +{ + static struct Good + { + const int i; + } + + static struct Bad + { + const int i; + ~this() {} + } + + Good good1, good2; + static assert(!__traits(compiles, { good1 = good2; })); // OK + + Bad bad1, bad2; + static assert(!__traits(compiles, { bad1 = bad2; })); // OK <- fails +} + /***************************************************/ int main() @@ -852,6 +1108,12 @@ int main() test9154(); test9416(); test11187(); + test12131(); + test12211(); + test4791(); + test12212(); + test12650(); + test13044(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/bitops.d b/gcc/testsuite/gdc.test/runnable/bitops.d index 85aec4cea..ed6819dd6 100644 --- a/gcc/testsuite/gdc.test/runnable/bitops.d +++ b/gcc/testsuite/gdc.test/runnable/bitops.d @@ -67,6 +67,7 @@ void test2() } /*****************************************************/ + version (DigitalMars) void test3() { uint v; diff --git a/gcc/testsuite/gdc.test/runnable/bug12928.d b/gcc/testsuite/gdc.test/runnable/bug12928.d new file mode 100644 index 000000000..92ce78eed --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/bug12928.d @@ -0,0 +1,13 @@ +// PERMUTE_ARGS: -inline -g -O +import core.exception : RangeError; +void main(string[] args) +{ + int[2] a; + try + { + foreach(const i; 0..3) + a[i] = i; + assert(0); + } + catch(RangeError){} +} diff --git a/gcc/testsuite/gdc.test/runnable/bug7068.d b/gcc/testsuite/gdc.test/runnable/bug7068.d new file mode 100644 index 000000000..70e351e81 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/bug7068.d @@ -0,0 +1,11 @@ +// PERMUTE_ARGS: -inline -g -O -d +void main() +{ + auto darray1 = new int*[](10); + foreach(ref v; darray1) + v = new int; + auto darray2 = new int*[](10); + darray2[] = darray1[]; // calls memset instead of memcpy + foreach(i; 0 .. 10) + assert(darray1[i] == darray2[i]); +} diff --git a/gcc/testsuite/gdc.test/runnable/circular.d b/gcc/testsuite/gdc.test/runnable/circular.d index 9398169fb..a47f54d67 100644 --- a/gcc/testsuite/gdc.test/runnable/circular.d +++ b/gcc/testsuite/gdc.test/runnable/circular.d @@ -9,10 +9,10 @@ import std.c.stdio; import imports.circularA; class bclass {}; -typedef bclass Tclass; +alias bclass Tclass; struct bstruct {} -typedef bstruct Tstruct; +alias bstruct Tstruct; /************************************/ diff --git a/gcc/testsuite/gdc.test/runnable/constfold.d b/gcc/testsuite/gdc.test/runnable/constfold.d index 8b0cbf767..abc6dc61c 100644 --- a/gcc/testsuite/gdc.test/runnable/constfold.d +++ b/gcc/testsuite/gdc.test/runnable/constfold.d @@ -654,6 +654,27 @@ void test11159() assert(e_10_pow_20 == pow(10uL, 20)); } +/************************************/ +// 12306 + +void test12306() +{ + struct Point3D { ubyte x, y, z; } + + enum Point3D pt1 = {x:1, y:1, z:1}; + const Point3D pt2 = {x:1, y:1, z:1}; + immutable Point3D pt3 = {x:1, y:1, z:1}; + + int[pt1.z][pt1.y][pt1.x] a1; + int[pt2.z][pt2.y][pt2.x] a2; + int[pt3.z][pt3.y][pt3.x] a3; + + ubyte a = 1; + const Point3D ptx = {x:a, y:1, z:1}; + + static assert(!__traits(compiles, { int[ptx.z][ptx.y][ptx.x] ax; })); +} + /************************************/ int main() diff --git a/gcc/testsuite/gdc.test/runnable/ctorpowtests.d b/gcc/testsuite/gdc.test/runnable/ctorpowtests.d index 6699c85cf..b898aa720 100644 --- a/gcc/testsuite/gdc.test/runnable/ctorpowtests.d +++ b/gcc/testsuite/gdc.test/runnable/ctorpowtests.d @@ -8,9 +8,9 @@ int magicVariable() version(GNU) { version(X86) - asm { "nop;" ::: ;} + asm { "nop"; } else version(X86_64) - asm { "nop;" ::: ;} + asm { "nop"; } else static assert(""); } @@ -126,9 +126,9 @@ int containsAsm() { version(GNU) { version(X86) - asm { "nop;" ::: ;} + asm { "nop"; } else version(X86_64) - asm { "nop;" ::: ;} + asm { "nop"; } else static assert(""); } diff --git a/gcc/testsuite/gdc.test/runnable/declaration.d b/gcc/testsuite/gdc.test/runnable/declaration.d index 0088b7585..302e92257 100644 --- a/gcc/testsuite/gdc.test/runnable/declaration.d +++ b/gcc/testsuite/gdc.test/runnable/declaration.d @@ -277,6 +277,23 @@ void test10142() auto f = new File10142("dummy"); } +/***************************************************/ +// 11421 + +void test11421() +{ + // AAs in array + const a1 = [[1:2], [3:4]]; // ok <- error + const int[int][] a2 = [[1:2], [3:4]]; // ok + static assert(is(typeof(a1) == typeof(a2))); + + // AAs in AA + auto aa = [1:["a":1.0], 2:["b":2.0]]; + static assert(is(typeof(aa) == double[string][int])); + assert(aa[1]["a"] == 1.0); + assert(aa[2]["b"] == 2.0); +} + /***************************************************/ int main() @@ -290,6 +307,7 @@ int main() test8410(); test8942(); test10142(); + test11421(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/delegate.d b/gcc/testsuite/gdc.test/runnable/delegate.d index 2abecc6bf..cef4c6c0b 100644 --- a/gcc/testsuite/gdc.test/runnable/delegate.d +++ b/gcc/testsuite/gdc.test/runnable/delegate.d @@ -224,22 +224,6 @@ void test10() /********************************************************/ -class Foo11 { - int x = 7; - int func() { return x; } -} - -void test11() -{ - int delegate() dg; - Foo11 f = new Foo11; - dg.ptr = cast(void*)f; - dg.funcptr = &Foo11.func; - assert(dg() == 7); -} - -/********************************************************/ - class A12 { public: @@ -325,6 +309,31 @@ void test2472() /********************************************************/ +void testAssign() +{ + static class C + { + int a; + this(int a) { this.a = a; } + int funca() { return a; } + int funcb() { return a + 1; } + } + + auto x = new C(5); + auto y = new C(7); + + auto dg = &x.funca; + assert(dg() == 5); + dg.funcptr = &C.funcb; + assert(dg() == 6); + dg.ptr = cast(void*)y; + assert(dg() == 8); + dg.funcptr = &C.funca; + assert(dg() == 7); +} + +/********************************************************/ + int main() { test1(); @@ -337,11 +346,11 @@ int main() test8(); test9(); test10(); - test11(); test12(); test13(); test2472(); test8257(); + testAssign(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/deprecate1.d b/gcc/testsuite/gdc.test/runnable/deprecate1.d deleted file mode 100644 index 9c48bc6e3..000000000 --- a/gcc/testsuite/gdc.test/runnable/deprecate1.d +++ /dev/null @@ -1,1320 +0,0 @@ -// REQUIRED_ARGS: -d -// PERMUTE_ARGS: -dw - -// Test cases using deprecated features -module deprecate1; - -import core.stdc.stdio : printf; -import std.traits; -import std.math : isNaN; - - -/************************************** - volatile -**************************************/ -void test5a(int *j) -{ - int i; - - volatile i = *j; - volatile i = *j; -} - -void test5() -{ - int x; - - test5a(&x); -} - -// from test23 -static int i2 = 1; - -void test2() -{ - volatile { int i2 = 2; } - assert(i2 == 1); -} - -// bug 1200. Other tests in test42.d -void foo6e() { - volatile debug {} -} - - -/************************************** - octal literals -**************************************/ - -void test10() -{ - int b = 0b_1_1__1_0_0_0_1_0_1_0_1_0_; - assert(b == 3626); - - b = 0_1_2_3_4_; - printf("b = %d\n", b); - assert(b == 668); -} - -/************************************** - typedef -**************************************/ - -template func19( T ) -{ - typedef T function () fp = &erf; - T erf() - { - printf("erf()\n"); - return T.init; - } -} - -alias func19!( int ) F19; - -F19.fp tc; - -void test19() -{ - printf("tc = %p\n", tc); - assert(tc() == 0); -} - - -/**************************************/ - -// http://www.digitalmars.com/d/archives/digitalmars/D/bugs/578.html - -typedef void* T60; - -class A60 -{ - int List[T60][int][uint]; - - void GetMsgHandler(T60 h,uint Msg) - { - assert(Msg in List[h][0]); //Offending line - } -} - -void test60() -{ -} - -/**************************************/ -// http://www.digitalmars.com/d/archives/digitalmars/D/bugs/576.html - -typedef ulong[3] BBB59; - -template A59() -{ - void foo(BBB59 a) - { - printf("A.foo\n"); - bar(a); - } -} - -struct B59 -{ - mixin A59!(); - - void bar(BBB59 a) - { - printf("B.bar\n"); - } -} - -void test59() -{ - ulong[3] aa; - BBB59 a; - B59 b; - - b.foo(a); -} - -/***************************************/ -// From variadic.d - -template foo33(TA...) -{ - const TA[0] foo33=0; -} - -template bar33(TA...) -{ - const TA[0..1][0] bar33=TA[0..1][0].init; -} - -void test33() -{ - typedef int dummy33=0; - typedef int myint=3; - - assert(foo33!(int)==0); - assert(bar33!(int)==int.init); - assert(bar33!(myint)==myint.init); - assert(foo33!(int,dummy33)==0); - assert(bar33!(int,dummy33)==int.init); - assert(bar33!(myint,dummy33)==myint.init); -} - -/***************************************/ -// Bug 875 ICE(glue.c) - -void test41() -{ - double bongos(int flux, string soup) - { - return 0.0; - } - - auto foo = mk_future(& bongos, 99, "soup"[]); -} - -int mk_future(A, B...)(A cmd, B args) -{ - typedef ReturnType!(A) TReturn; - typedef ParameterTypeTuple!(A) TParams; - typedef B TArgs; - - alias Foo41!(TReturn, TParams, TArgs) TFoo; - - return 0; -} - -class Foo41(A, B, C) { - this(A delegate(B), C) - { - } -} - -// Typedef tests from test4.d -/* ================================ */ - -void test4_test26() -{ - typedef int foo = cast(foo)3; - foo x; - assert(x == cast(foo)3); - - typedef int bar = 4; - bar y; - assert(y == cast(bar)4); -} - -/* ================================ */ - -struct Foo28 -{ - int a; - int b = 7; -} - -void test4_test28() -{ - version (all) - { - int a; - int b = 1; - typedef int t = 2; - t c; - t d = cast(t)3; - - assert(int.init == 0); - assert(a.init == 0); - assert(b.init == 0); - assert(t.init == cast(t)2); - assert(c.init == cast(t)2); - printf("d.init = %d\n", d.init); - assert(d.init == cast(t)2); - - assert(Foo28.a.init == 0); - assert(Foo28.b.init == 0); - } - else - { - int a; - int b = 1; - typedef int t = 2; - t c; - t d = cast(t)3; - - assert(int.init == 0); - assert(a.init == 0); - assert(b.init == 1); - assert(t.init == cast(t)2); - assert(c.init == cast(t)2); - printf("d.init = %d\n", d.init); - assert(d.init == cast(t)3); - - assert(Foo28.a.init == 0); - assert(Foo28.b.init == 7); - } -} - -// from template4.d test 4 -void template4_test4() -{ - typedef char Typedef; - - static if (is(Typedef Char == typedef)) - { - static if (is(Char == char)) - { } - else static assert(0); - } - else static assert(0); -} - -// from structlit -typedef int myint10 = 4; - -struct S10 -{ - int i; - union - { int x = 2; - int y; - } - int j = 3; - myint10 k; -} - -void structlit_test10() -{ - S10 s = S10( 1 ); - assert(s.i == 1); - assert(s.x == 2); - assert(s.y == 2); - assert(s.j == 3); - assert(s.k == 4); - - static S10 t = S10( 1 ); - assert(t.i == 1); - assert(t.x == 2); - assert(t.y == 2); - assert(t.j == 3); - assert(t.k == 4); - - S10 u = S10( 1, 5 ); - assert(u.i == 1); - assert(u.x == 5); - assert(u.y == 5); - assert(u.j == 3); - assert(u.k == 4); - - static S10 v = S10( 1, 6 ); - assert(v.i == 1); - assert(v.x == 6); - assert(v.y == 6); - assert(v.j == 3); - assert(v.k == 4); -} - -/******************************************/ - -void test15_test1() -{ - int i; - bool[] b = new bool[10]; - for (i = 0; i < 10; i++) - assert(b[i] == false); - - typedef bool tbit = true; - tbit[] tb = new tbit[63]; - for (i = 0; i < 63; i++) - assert(tb[i] == true); -} - -void test15_test2() -{ - int i; - byte[] b = new byte[10]; - for (i = 0; i < 10; i++) - { //printf("b[%d] = %d\n", i, b[i]); - assert(b[i] == 0); - } - - typedef byte tbyte = 0x23; - tbyte[] tb = new tbyte[63]; - for (i = 0; i < 63; i++) - assert(tb[i] == 0x23); -} - - -void test15_test3() -{ - int i; - ushort[] b = new ushort[10]; - for (i = 0; i < 10; i++) - { //printf("b[%d] = %d\n", i, b[i]); - assert(b[i] == 0); - } - - typedef ushort tushort = 0x2345; - tushort[] tb = new tushort[63]; - for (i = 0; i < 63; i++) - assert(tb[i] == 0x2345); -} - - -void test15_test4() -{ - int i; - float[] b = new float[10]; - for (i = 0; i < 10; i++) - { //printf("b[%d] = %d\n", i, b[i]); - assert(isNaN(b[i])); - } - - typedef float tfloat = 0.0; - tfloat[] tb = new tfloat[63]; - for (i = 0; i < 63; i++) - assert(tb[i] == cast(tfloat)0.0); -} - -/************************************/ -// failed to compile on DMD0.050, only with typedef (alias was OK) - -typedef int[int] T11; -T11 a11; - -void test15_test11() -{ - 1 in a11; -} - -/************************************/ -// ICE(cgelem.c) on 0.050, alias was OK. -struct A12 -{ - int x; - int y; - int x1; - int x2; -} - -typedef A12 B12; - -template V12(T) -{ - T buf; - - T get() - { - return buf; - } -} - -alias V12!(B12) foo12; - - -void test15_test12() -{ - B12 b = foo12.get(); -} - -/************************************/ -// test15_test13. Failed to compile DMD0.050, OK with alias. -typedef int[] T13; -static T13 a13=[1,2,3]; - -/************************************************/ -// Failed to compile DMD0.050, OK with alias. - -typedef int foo3; - -foo3 [foo3] list3; - -void testaa_test3() -{ - list3[cast(foo3)5] = cast(foo3)2; - foo3 x = list3.keys[0]; // This line fails. - assert(x == cast(foo3)5); -} - - -/*****************************************/ - -void test20_test32() -{ - typedef int Type = 12; - static Type[5] var = [0:1, 3:2]; - - assert(var[0] == 1); - assert(var[1] == 12); - assert(var[2] == 12); - assert(var[3] == 2); - assert(var[4] == 12); -} - -/**************************************/ - -class A2 -{ - T opCast(T)() - { - auto s = T.stringof; - printf("A.opCast!(%.*s)\n", s.length, s.ptr); - return T.init; - } -} - - -void opover2_test2() -{ - auto a = new A2(); - - auto x = cast(int)a; - assert(x == 0); - - typedef int myint_BUG6712 = 7; - auto y = cast(myint_BUG6712)a; - assert(y == 7); -} - -/**************************************/ -// http://www.digitalmars.com/d/archives/10750.html -// test12.d, test7(). ICE(constfold.c) - -version(none) // This contains an incredibly nasty cast -{ -template base7( T ) -{ - void errfunc() { throw new Exception("no init"); } - typedef T safeptr = cast(T)&errfunc; // Nasty cast -} - -alias int function(int) mfp; -alias base7!(mfp) I_V_fp; -} - -typedef bool antibool = 1; -antibool[8] z21 = [ cast(antibool) 0, ]; - -void test12_test21() -{ - assert(z21[7]); -} - -/**************************************/ -// test12.d, test41. -// Might be related to this: -// http://www.digitalmars.com/d/archives/digitalmars/D/INVALID_HANDLE_VALUE_const_55633.html - -typedef void* H41; - -const H41 INVALID_H41_VALUE = cast(H41)-1; - -/*******************************************/ - -struct Ranged(T) -{ - T value, min, max, range; -} - -typedef Ranged!(float) Degree = {0f, 0f, 360f, 360f}; - -void test28_test5() -{ - static Degree a; - assert(a.value == 0f); - assert(a.min == 0f); - assert(a.max == 360f); - assert(a.range == 360f); -} - -/*******************************************/ - -void test28_test6() -{ - struct Foo - { - void foo() { } - } - - typedef Foo Bar; - - Bar a; - a.foo(); -} - -/*******************************************/ - -typedef uint socket_t = -1u; - -class Socket -{ - socket_t sock; - - void accept() - { - socket_t newsock; - } -} - -void test28_test16() -{ -} - -/*******************************************/ - -typedef int Xint = 42; - -void test28_test56() -{ - Xint[3][] x = new Xint[3][4]; - - foreach(Xint[3] i; x) { - foreach (Xint j; i) - assert(j == 42); - } -} - -void test28_test57() -{ - Xint[3][] x = new Xint[3][4]; - x.length = 200; - assert(x.length == 200); - - foreach(Xint[3] i; x) { - foreach (Xint j; i) - assert(j == 42); - } -} -/******************************************/ -// from runnable/traits.d -typedef int traits_myint; -static assert(__traits(isArithmetic, traits_myint) == true); -static assert(__traits(isScalar, traits_myint) == true); -static assert(__traits(isIntegral, traits_myint) == true); - -mixin template Members6674() -{ - static int i1; - static int i2; - static int i3; //comment out to make func2 visible - static int i4; //comment out to make func1 visible -} - -class Test6674 -{ - mixin Members6674; - - typedef void function() func1; - typedef bool function() func2; -} - -static assert([__traits(allMembers,Test6674)] == [ - "i1","i2","i3","i4", - "func1","func2", - "toString","toHash","opCmp","opEquals","Monitor","factory"]); - -/***************************************************/ -// Bug 1523. typedef only. - -struct BaseStruct { - int n; - char c; -} - -typedef BaseStruct MyStruct26; - -void myFunction(MyStruct26) {} - -void test42_test26() -{ - myFunction(MyStruct26(0, 'x')); -} - -/***************************************************/ -// typedef only - -class Foo30 -{ - int i; -} - -typedef Foo30 Bar30; - -void test42_test30() -{ - Bar30 testBar = new Bar30(); - auto j = testBar.i; -} - -/***************************************************/ -// test55. bug 1767 - -class DebugInfo -{ - typedef int CVHeaderType ; - enum anon:CVHeaderType{ CV_NONE, CV_DOS, CV_NT, CV_DBG } -} - -/***************************************************/ - -void test42_test102() -{ - typedef const(char)[] A; - A stripl(A s) - { - uint i; - return s[i .. $]; - } -} - -/***************************************************/ -// test42.d test107(). segfault 2.020 -class foo107 {} -typedef foo107 bar107; -void x107() -{ - bar107 a = new bar107(); - bar107 b = new bar107(); - bool c = (a == b); -} - -/***************************************************/ -// Bug 3668. segfault regression 2.032. - -void test42_test167() -{ - typedef byte[] Foo; - Foo bar; - foreach(value; bar){} -} - - -// Bug 190. Test42.d, test 225. - -typedef int avocado; -void eat(avocado x225 = .x225); -avocado x225; - -/***************************************************/ - -typedef ireal BUG3919; -alias typeof(BUG3919.init*BUG3919.init) ICE3919; -alias typeof(BUG3919.init/BUG3919.init) ICE3920; - -/*****************************************/ -// rejectsvalid 0.100. typedef only. - -void testswitch_test18() -{ - enum E { a, b } - typedef E F; - F i = E.a; - final switch (i) - { - case E.a: - case E.b: - break; - } -} - -/************************************/ - -void testconst_test7() -{ - typedef void *HANDLE; - const HANDLE INVALID_HANDLE_VALUE = cast(HANDLE)-1; -} - -/************************************/ - -void testconst_test67() -{ - typedef char mychar; - alias immutable(mychar)[] mystring; - - mystring s = cast(mystring)"hello"; - mychar c = s[0]; -} - -/************************************/ - -void testconst_test71() -{ - string s = "hello world"; - char c = s[0]; - - typedef char mychar; - alias immutable(mychar)[] mystring; - - mystring t = cast(mystring)("hello world"); - mychar d = t[0]; -} - -/************************************/ - -class foo86 { } - -typedef shared foo86 Y86; - -void testconst_test86() -{ - pragma(msg, Y86); - shared(Y86) x = new Y86; -} - -/************************************/ - -class Class19 : Throwable -{ - this() { super(""); } -} - -typedef Class19 Alias19; - -void testdstress_test19() -{ - try{ - throw new Alias19(); - }catch{ - return; - } - assert(0); -} -/************************************/ - -public static const uint U20 = (cast(uint)(-1)) >>> 2; - -typedef uint myType20; -public static const myType20 T20 = (cast(myType20)(-1)) >>> 2; - -void testdstress_test20() -{ - assert(U20 == 0x3FFFFFFF); - assert(T20 == 0x3FFFFFFF); -} - -/******************************************************/ - -class ABC { } - -typedef ABC DEF; - -TypeInfo foo() -{ - ABC c; - - return typeid(DEF); -} - -void testtypeid_test1() -{ - TypeInfo ti = foo(); - - TypeInfo_Typedef td = cast(TypeInfo_Typedef)ti; - assert(td); - - ti = td.base; - - TypeInfo_Class tc = cast(TypeInfo_Class)ti; - assert(tc); - - printf("%.*s\n", tc.info.name.length, tc.info.name.ptr); - assert(tc.info.name == "deprecate1.ABC"); -} - -/******************************************************/ - -union MyUnion5{ - int i; - byte b; -} - -typedef MyUnion5* Foo5; - -void testtypeid_test5() -{ - TypeInfo ti = typeid(Foo5); - assert(!(ti is null)); - assert(ti.tsize==(Foo5).sizeof); - assert(ti.toString()=="deprecate1.Foo5"); -} - -/***********************************/ -// test8.d test13() -typedef void *HWND; - -const HWND hWnd = cast(HWND)(null); - -/***********************************/ - -typedef int* IP; - -void test8_test21() -{ - int i = 5; - IP ip = cast(IP) &i; - assert(*ip == 5); -} - -/***********************************/ -// http://www.digitalmars.com/d/archives/7812.html - -typedef int delegate() DG; - -class A23 { - int foo() { return 7; } - DG pfoo() { return &this.foo; } //this is ok -} - -void test8_test23() -{ - int i; - A23 a = new A23; - DG dg = &a.foo; - i = dg(); - assert(i == 7); - DG dg2 = a.pfoo(); - i = dg2(); - assert(i == 7); -} - -/***********************************/ -// test8 test29() -typedef int[] tint; - -void Set( ref tint array, int newLength ) -{ - array.length= newLength; -} - -/***********************************/ - -struct V41 { int x; } - -typedef V41 W41 = { 3 }; - -class Node41 -{ - W41 v; -} - -void test8_test41() -{ - Node41 n = new Node41; - - printf("n.v.x == %d\n", n.v.x); - assert(n.v.x == 3); -} - -/***********************************/ -// http://www.digitalmars.com/d/archives/digitalmars/D/bugs/1801.html -// test8 test47. ICE(e2ir.c) - -typedef string Qwert47; - -string yuiop47(Qwert47 asdfg) -{ - return asdfg[4..6]; -} - -/***********************************/ -void test22_test28() -{ - typedef cdouble Y; - Y five = cast(Y) (4.0i + 0.4); -} - -/*******************************************/ -// Bug 184 - -struct vegetarian -{ - carrots areYummy; -} - -typedef ptrdiff_t carrots; - -void test23_test35() -{ - assert(vegetarian.sizeof == ptrdiff_t.sizeof); -} - -/*******************************************/ -// bug 185 -void test23_test36() -{ - typedef double type = 5.0; - - type t; - assert (t == type.init); - - type[] ts = new type[10]; - ts.length = 20; - foreach (i; ts) - { - assert(i == cast(type)5.0); - } -} - -/*******************************************/ - -typedef int Int1; -typedef int Int2; - -int show(Int1 v) -{ - printf("Int1: %d", v); - return 1; -} - -int show(Int2 v) { - printf("Int2: %d", v); - return 2; -} - -int show(int i) { - printf("int: %d", i); - return 3; -} - -int show(long l) { - printf("long: %d", l); - return 4; -} - -int show(uint u) { - printf("uint: %d", u); - return 5; -} - -void test23_test49() -{ int i; - - Int1 value1 = 42; - Int2 value2 = 69; - - i = show(value1 + value2); - assert(i == 3); - i = show(value2 + value1); - assert(i == 3); - i = show(2 * value1); - assert(i == 3); - i = show(value1 * 2); - assert(i == 3); - i = show(value1 + value1); - assert(i == 1); - i = show(value2 - value2); - assert(i == 2); - i = show(value1 + 2); - assert(i == 3); - i = show(3 + value2); - assert(i == 3); - i = show(3u + value2); - assert(i == 5); - i = show(value1 + 3u); - assert(i == 5); - - long l = 23; - i = show(value1 + l); - assert(i == 4); - i = show(l + value2); - assert(i == 4); - - short s = 105; - i = show(s + value1); - assert(i == 3); - i = show(value2 + s); - assert(i == 3); -} - -/*******************************************/ - -void test23_test51() -{ - typedef int fooQ = 10; - - int[3][] p = new int[3][5]; - int[3][] q = new int[3][](5); - - int[][] r = new int[][](5,3); - - for (int i = 0; i < 5; i++) - { - for (int j = 0; j < 3; j++) - { - assert(r[i][j] == 0); - r[i][j] = i * j; - } - } - - fooQ[][] s = new fooQ[][](5,3); - - for (int i = 0; i < 5; i++) - { - for (int j = 0; j < 3; j++) - { - assert(s[i][j] == 10); - s[i][j] = cast(fooQ)(i * j); - } - } - - typedef fooQ[][] barQ; - barQ b = new barQ(5,3); - - for (int i = 0; i < 5; i++) - { - for (int j = 0; j < 3; j++) - { - assert(b[i][j] == 10); - b[i][j] = cast(fooQ)(i * j); - } - } -} - -/*******************************************/ - -typedef ubyte x53 = void; - -void test23_test53() -{ - new x53[10]; -} - -/*******************************************/ - -void test23_test55() -{ - typedef uint myintX = 6; - - myintX[500][] data; - - data.length = 1; - assert(data.length == 1); - foreach (ref foo; data) - { - assert(foo.length == 500); - foreach (ref u; foo) - { //printf("u = %u\n", u); - assert(u == 6); - u = 23; - } - } - foreach (ref foo; data) - { - assert(foo.length == 500); - foreach (u; foo) - { assert(u == 23); - auto v = u; - v = 23; - } - } - - typedef byte mybyte = 7; - - mybyte[500][] mata; - - mata.length = 1; - assert(mata.length == 1); - foreach (ref foo; mata) - { - assert(foo.length == 500); - foreach (ref u; foo) - { //printf("u = %u\n", u); - assert(u == 7); - u = 24; - } - } - foreach (ref foo; mata) - { - assert(foo.length == 500); - foreach (u; foo) - { assert(u == 24); - auto v = u; - v = 24; - } - } -} - -/*******************************************/ - -typedef string String67; - -int f67( string c, string a ) -{ - return 1; -} - -int f67( string c, String67 a ) -{ - return 2; -} - -void test23_test67() -{ - printf("test67()\n"); - int i; - i = f67( "", "" ); - assert(i == 1); - i = f67( "", cast(String67)"" ); - assert(i == 2); - i = f67( null, "" ); - assert(i == 1); - i = f67( null, cast(String67)"" ); - assert(i == 2); -} - -/************************************************/ - -void test34_test14() -{ - typedef Exception TypedefException; - - try - { - } - catch(TypedefException e) - { - } -} - -/************************************************/ - -void test34_test52() -{ - - struct Foo { - typedef int Y; - } - with (Foo) { - Y y; - } -} - -/***************************************************/ - - -void test6289() -{ - typedef immutable(int)[] X; - static assert(is(typeof(X.init[]) == X)); - static assert(is(typeof((immutable(int[])).init[]) == immutable(int)[])); - static assert(is(typeof((const(int[])).init[]) == const(int)[])); - static assert(is(typeof((const(int[3])).init[]) == const(int)[])); -} - -/***************************************************/ -// 4237 - -struct Struct4237(T) { T value; } -void foo4237() -{ - typedef int Number = 1; - Struct4237!Number s; - pragma(msg, typeof(s).mangleof); - assert(s.value == 1); -} -void bar4237() -{ - typedef real Number = 2; - Struct4237!Number s; - pragma(msg, typeof(s).mangleof); - assert(s.value == 2); // Assertion failure -} -void test4237() -{ - foo4237(); bar4237(); -} - -/******************************************/ -// 3990 - -void test3990() -{ - int[] a1 = [5, 4, 3]; - assert(*a1 == 5); - alias typeof(a1) T1; - assert(is(typeof(*T1))); - - int[3] a2 = [5, 4, 3]; - assert(*a2 == 5); - alias typeof(a2) T2; - assert(is(typeof(*T2))); -} - -/******************************************/ -// from extra-files/test2.d - -typedef void* HANDLE18; - -HANDLE18 testx18() -{ - return null; -} - -void test18() -{ - assert(testx18() is null); -} - -/******************************************/ - -int main() -{ - test2(); - test5(); - test10(); - test19(); - test33(); - test41(); - test59(); - test60(); - test4_test26(); - test4_test28(); - structlit_test10(); - test15_test1(); - test15_test2(); - test15_test3(); - test15_test4(); - test15_test11(); - test15_test12(); - test20_test32(); - opover2_test2(); - test12_test21(); - test28_test5(); - test28_test6(); - test28_test16(); - test28_test56(); - test28_test57(); - testaa_test3(); - test42_test26(); - test42_test30(); - test42_test102(); - test42_test167(); - testswitch_test18(); - testconst_test7(); - testconst_test67(); - testconst_test71(); - testconst_test86(); - testdstress_test19(); - testdstress_test20(); - testtypeid_test1(); - testtypeid_test5(); - test8_test21(); - test8_test23(); - test8_test41(); - test22_test28(); - test23_test35(); - test23_test36(); - test23_test49(); - test23_test51(); - test23_test53(); - test23_test55(); - test23_test67(); - test34_test14(); - test34_test52(); - test3990(); - test6289(); - test4237(); - test18(); - - return 0; -} diff --git a/gcc/testsuite/gdc.test/runnable/eh.d b/gcc/testsuite/gdc.test/runnable/eh.d index 165056421..78c180acc 100644 --- a/gcc/testsuite/gdc.test/runnable/eh.d +++ b/gcc/testsuite/gdc.test/runnable/eh.d @@ -702,6 +702,56 @@ void test10964() /****************************************************/ +alias Action = void delegate(); + +class A10 +{ + invariant() + { + } + + public Action foo(Action a) + { + synchronized + { + B10 elements = new B10; + Action[] actions = [a]; + + elements.bar(actions); + + if (actions.length > 1) + elements.bar(actions); + return actions[0]; + } + return null; + } +} + +class B10 +{ + public bool bar(ref Action[]) + { + return false; + } +} + +class D10 +{ + void baz() + { + } +} + +void test12989() +{ + auto a = new A10; + auto d = new D10; + + assert(a.foo(&d.baz) == &d.baz); +} + +/****************************************************/ + int main() { printf("start\n"); @@ -722,6 +772,7 @@ int main() //test8(); // XBUG: Cannot goto into catch bug to prevent GCC middle-end ICE. test9(); test10964(); + test12989(); printf("finish\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/foreach.d b/gcc/testsuite/gdc.test/runnable/foreach.d index aa51445e8..21cc46a55 100644 --- a/gcc/testsuite/gdc.test/runnable/foreach.d +++ b/gcc/testsuite/gdc.test/runnable/foreach.d @@ -263,6 +263,22 @@ void test8() } } +/**************************************************/ + +struct S +{ + int opApply(int delegate(ref int a)) { return 0; } + int opApplyReverse(int delegate(ref int a)) { return 0; } + int dg(int delegate(ref int a)) { return 0; } +} + +void test9() +{ + S s; + foreach(a; s) {} + foreach_reverse(a; s) {} + foreach(a; &s.dg) {} +} /**************************************************/ @@ -276,6 +292,7 @@ int main() test6(); test7(); test8(); + test9(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/foreach5.d b/gcc/testsuite/gdc.test/runnable/foreach5.d index 7d9f80185..1ac8ad6cb 100644 --- a/gcc/testsuite/gdc.test/runnable/foreach5.d +++ b/gcc/testsuite/gdc.test/runnable/foreach5.d @@ -402,6 +402,36 @@ void test6659c() assert(value == 1); } +/***************************************/ + +// 10221 + +void test10221() +{ + // All of these should work, but most are too slow. Just check they compile. + foreach(char i; char.min..char.max+1) {} + if (0) foreach(wchar i; wchar.min..wchar.max+1) {} + if (0) foreach(dchar i; dchar.min..dchar.max+1) {} + foreach(byte i; byte.min..byte.max+1) {} + foreach(ubyte i; ubyte.min..ubyte.max+1) {} + if (0) foreach(short i; short.min..short.max+1) {} + if (0) foreach(ushort i; ushort.min..ushort.max+1) {} + if (0) foreach(int i; int.min..int.max+1U) {} + if (0) foreach(uint i; uint.min..uint.max+1L) {} + if (0) foreach(long i; long.min..long.max+1UL) {} + + foreach_reverse(char i; char.min..char.max+1) { assert(i == typeof(i).max); break; } + foreach_reverse(wchar i; wchar.min..wchar.max+1) { assert(i == typeof(i).max); break; } + foreach_reverse(dchar i; dchar.min..dchar.max+1) { assert(i == typeof(i).max); break; } + foreach_reverse(byte i; byte.min..byte.max+1) { assert(i == typeof(i).max); break; } + foreach_reverse(ubyte i; ubyte.min..ubyte.max+1) { assert(i == typeof(i).max); break; } + foreach_reverse(short i; short.min..short.max+1) { assert(i == typeof(i).max); break; } + foreach_reverse(ushort i; ushort.min..ushort.max+1) { assert(i == typeof(i).max); break; } + foreach_reverse(int i; int.min..int.max+1U) { assert(i == typeof(i).max); break; } + foreach_reverse(uint i; uint.min..uint.max+1L) { assert(i == typeof(i).max); break; } + foreach_reverse(long i; long.min..long.max+1UL) { assert(i == typeof(i).max); break; } +} + /***************************************/ // 7814 @@ -453,6 +483,24 @@ void test10049() } } +/******************************************/ + +struct T11955(T...) { T field; alias field this; } + +alias X11955 = uint; + +struct S11955 +{ + enum empty = false; + T11955!(uint, uint) front; + void popFront() {} +} + +void test11955() +{ + foreach(X11955 i, v; S11955()) {} +} + /******************************************/ // 6652 @@ -587,7 +635,7 @@ struct Foo9068 struct SimpleCounter9068 { static int destroyedCount; - const(int) limit = 5; + enum int limit = 5; int counter; ~this() { destroyedCount++; } @@ -597,14 +645,14 @@ struct SimpleCounter9068 void popFront() { counter++; } } -// ICE when trying to break outer loop from inside switch statement void test9068() { //---------------------------------------- // There was never a bug in this case (no range). int sum; loop_simple: - foreach (i; [10, 20]) { + foreach (i; [10, 20]) + { sum += i; break loop_simple; } @@ -613,10 +661,14 @@ loop_simple: //---------------------------------------- // There was a bug with loops over ranges. int last = -1; -X: foreach (i; SimpleCounter9068()) { - switch(i) { - case 3: break X; - default: last = i; +X: foreach (i; SimpleCounter9068()) + { + switch(i) + { + case 3: + break X; + default: + last = i; } } assert(last == 2); @@ -626,7 +678,8 @@ X: foreach (i; SimpleCounter9068()) { // Simpler case: the compiler error had nothing to do with the switch. last = -1; loop_with_range: - foreach (i; SimpleCounter9068()) { + foreach (i; SimpleCounter9068()) + { last = i; break loop_with_range; } @@ -637,21 +690,110 @@ loop_with_range: // Test with destructors: the loop is implicitly wrapped into two // try/finally clauses. loop_with_dtors: - for (auto x = Foo9068(4), y = Foo9068(5); x.x != 10; ++x.x) { + for (auto x = Foo9068(4), y = Foo9068(5); x.x != 10; ++x.x) + { if (x.x == 8) break loop_with_dtors; } assert(Foo9068.destroyed == [5, 8]); - Foo9068.destroyed.clear(); + Foo9068.destroyed = null; //---------------------------------------- // Same with an unlabelled break. - for (auto x = Foo9068(4), y = Foo9068(5); x.x != 10; ++x.x) { + for (auto x = Foo9068(4), y = Foo9068(5); x.x != 10; ++x.x) + { if (x.x == 7) break; } assert(Foo9068.destroyed == [5, 7]); - Foo9068.destroyed.clear(); + Foo9068.destroyed = null; +} + +/***************************************/ +// 11885 + +struct Foo11885 +{ + static int[] destroyed; + int x; + ~this() { destroyed ~= x; } +} + +struct SimpleCounter11885 +{ + static int destroyedCount; + enum int limit = 5; + int counter; + ~this() { destroyedCount++; } + + // Range primitives. + @property bool empty() const { return counter >= limit; } + @property int front() { return counter; } + void popFront() { counter++; } +} + +void test11885() +{ + //---------------------------------------- + // There was never a bug in this case (no range). + int sum; +loop_simple: + foreach (i; [10, 20]) + { + sum += i; + continue loop_simple; + } + assert(sum == 30); + + //---------------------------------------- + // There was a bug with loops over ranges. + int last = -1; +X: foreach (i; SimpleCounter11885()) + { + switch(i) + { + case 3: + continue X; + default: + last = i; + } + } + assert(last == 4); + assert(SimpleCounter11885.destroyedCount == 1); + + //---------------------------------------- + // Simpler case: the compiler error had nothing to do with the switch. + last = -1; +loop_with_range: + foreach (i; SimpleCounter11885()) + { + last = i; + continue loop_with_range; + } + assert(last == 4); + assert(SimpleCounter11885.destroyedCount == 2); + + //---------------------------------------- + // Test with destructors: the loop is implicitly wrapped into two + // try/finally clauses. +loop_with_dtors: + for (auto x = Foo11885(4), y = Foo11885(5); x.x != 10; ++x.x) + { + if (x.x == 8) + continue loop_with_dtors; + } + assert(Foo11885.destroyed == [5, 10]); + Foo11885.destroyed = null; + + //---------------------------------------- + // Same with an unlabelled continue. + for (auto x = Foo11885(4), y = Foo11885(5); x.x != 10; ++x.x) + { + if (x.x == 7) + continue; + } + assert(Foo11885.destroyed == [5, 10]); + Foo11885.destroyed = null; } /***************************************/ @@ -761,6 +903,65 @@ void test11291() } } +/***************************************/ +// 12103 + +alias TypeTuple12103(TL...) = TL; + +alias Id12103(alias a) = a; + +void test12103() +{ + alias fs1 = TypeTuple12103!(() => 0, () => 1); + foreach (i, f; fs1) + { + static assert(f() == i); + static assert(Id12103!f() == i); + assert(f() == i); + assert(Id12103!f() == i); + } + + alias fs2 = TypeTuple12103!(x=>x+0, y=>y+1); + foreach (i, f; fs2) + { + static assert(f(0) == i); + static assert(Id12103!f(0) == i); + assert(f(0) == i); + assert(Id12103!f(0) == i); + } +} + +/***************************************/ +// 12739 + +struct S12739 +{ +nothrow: + int opApply(int delegate(ref int) nothrow dg) + { + return 0; + } +} + +void test12739() nothrow +{ + S12739 s; + foreach (e; s) {} +} + +/***************************************/ +// 12932 + +void test12932() @nogc +{ + int sum; + foreach (e; [1,2,3]) + { + sum += e; + } + assert(sum == 6); +} + /***************************************/ int main() @@ -774,6 +975,7 @@ int main() test4090b(); test5605(); test7004(); + test10221(); test7406(); test6659(); test6659a(); @@ -782,9 +984,13 @@ int main() test7814(); test6652(); test9068(); + test11885(); test10475a(); test10475b(); test11291(); + test12103(); + test12739(); + test12932(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/funclit.d b/gcc/testsuite/gdc.test/runnable/funclit.d index aa73de372..ab00029ae 100644 --- a/gcc/testsuite/gdc.test/runnable/funclit.d +++ b/gcc/testsuite/gdc.test/runnable/funclit.d @@ -314,7 +314,7 @@ void test10() void test11() { auto a1 = [x => x, (int x) => x * 2]; - static assert(is(typeof(a1[0]) == int function(int) pure @safe nothrow)); + static assert(is(typeof(a1[0]) == int function(int) pure @safe nothrow @nogc)); assert(a1[0](10) == 10); assert(a1[1](10) == 20); @@ -358,9 +358,7 @@ void test6714() bar6714x((a, b) { return a + b; }); assert(bar6714y((a, b){ return 1.0; }) == 1); - static assert(!__traits(compiles, { - bar6714y((a, b){ return 1.0f; }); - })); + assert(bar6714y((a, b){ return 1.0f; }) == 1); assert(bar6714y((a, b){ return a; }) == 2); } @@ -395,7 +393,7 @@ void test7288() return () => { return x; }; } pragma(msg, typeof(&foo)); - alias int delegate() nothrow @safe delegate() nothrow @safe delegate() Dg; + alias int delegate() nothrow @nogc @safe delegate() nothrow @nogc @safe delegate() Dg; pragma(msg, Dg); static assert(is(typeof(&foo) == Dg)); // should pass } @@ -536,10 +534,10 @@ auto foo7743b() } void test7743() { - static assert(is(typeof(&foo7743a) == int delegate() nothrow @safe function())); + static assert(is(typeof(&foo7743a) == int delegate() nothrow @nogc @safe function())); assert(foo7743a()() == 10); - static assert(is(typeof(&foo7743b) == int delegate() nothrow @safe function())); + static assert(is(typeof(&foo7743b) == int delegate() nothrow @nogc @safe function())); assert(foo7743b()() == 10); } @@ -550,7 +548,7 @@ enum dg7761 = (int a) pure => 2 * a; void test7761() { - static assert(is(typeof(dg7761) == int function(int) pure @safe nothrow)); + static assert(is(typeof(dg7761) == int function(int) pure @safe nothrow @nogc)); assert(dg7761(10) == 20); } @@ -834,6 +832,55 @@ void test10133() func10133b!("x")(); } +/***************************************************/ +// 10219 + +void test10219() +{ + interface I { } + class C : I { } + + void test_dg(I delegate(C) dg) + { + C c = new C; + void* cptr = cast(void*) c; + void* iptr = cast(void*) cast(I) c; + void* xptr = cast(void*) dg(c); + assert(cptr != iptr); + assert(cptr != xptr); // should pass + assert(iptr == xptr); // should pass + } + + C delegate(C c) dg = delegate C(C c) { return c; }; + static assert(!__traits(compiles, { test_dg(dg); })); + static assert(!__traits(compiles, { test_dg(delegate C(C c) { return c; }); })); + static assert(!__traits(compiles, { I delegate(C) dg2 = dg; })); + + // creates I delegate(C) + test_dg(c => c); + test_dg(delegate(C c) => c); + + void test_fp(I function(C) fp) + { + C c = new C; + void* cptr = cast(void*) c; + void* iptr = cast(void*) cast(I) c; + void* xptr = cast(void*) fp(c); + assert(cptr != iptr); + assert(cptr != xptr); // should pass + assert(iptr == xptr); // should pass + } + + C function(C c) fp = function C(C c) { return c; }; + static assert(!__traits(compiles, { test_fp(fp); })); + static assert(!__traits(compiles, { test_fp(function C(C c) { return c; }); })); + static assert(!__traits(compiles, { I function(C) fp2 = fp; })); + + // creates I function(C) + test_fp(c => c); + test_fp(function(C c) => c); +} + /***************************************************/ // 10288 @@ -952,6 +999,25 @@ C11230 visit11230() return null; } +/***************************************************/ +// 10336 + +struct S10336 +{ + template opDispatch(string name) + { + enum opDispatch = function(int x) { + return x; + }; + } +} + +void test10336() +{ + S10336 s; + assert(s.hello(12) == 12); +} + /***************************************************/ // 11661 @@ -961,6 +1027,60 @@ void test11661() void function() fp = {}; // OK <- NG } +/***************************************************/ +// 11774 + +void f11774(T, R)(R delegate(T[]) dg) +{ + T[] src; + dg(src); +} + +void test11774() +{ + int[] delegate(int[]) dg = (int[] a) => a; + f11774!int(dg); + f11774!Object(a => a); + f11774!int(dg); +} + +/***************************************************/ +// 12508 + +interface A12508(T) +{ + T getT(); +} + +class C12508 : A12508!double +{ + double getT() { return 1; } +} + +void f12508(A12508!double delegate() dg) +{ + auto a = dg(); + assert(a !is null); + assert(a.getT() == 1.0); // fails! +} + +void t12508(T)(A12508!T delegate() dg) +{ + auto a = dg(); + assert(a !is null); + assert(a.getT() == 1.0); // fails! +} + +ref alias Dg12508 = A12508!double delegate(); +void t12508(T)(Dg12508 dg) {} + +void test12508() +{ + f12508({ return new C12508(); }); + t12508({ return new C12508(); }); + static assert(!__traits(compiles, x12508({ return new C12508(); }))); +} + /***************************************************/ int main() @@ -1008,8 +1128,12 @@ int main() test9628(); test9928(); test10133(); + test10219(); test10288(); + test10336(); test11661(); + test11774(); + test12508(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/iasm64.d b/gcc/testsuite/gdc.test/runnable/iasm64.d index 9175971f7..4a8f20cf3 100644 --- a/gcc/testsuite/gdc.test/runnable/iasm64.d +++ b/gcc/testsuite/gdc.test/runnable/iasm64.d @@ -6599,6 +6599,55 @@ L1: pop RAX; /****************************************************/ +void test12849() +{ + ulong a = 0xff00ff00ff00ff00L; + ulong result; + ulong expected = 0b10101010; + asm + { + pxor XMM0, XMM0; + movq XMM0, a; + pmovmskb RAX, XMM0; + mov result, RAX; + } + assert (result == expected); +} + +/****************************************************/ + +void test12968() +{ + int x; + ubyte* p; + static ubyte data[] = + [ + 0x48, 0x89, 0xF8, + 0x4C, 0x87, 0xC2, + 0xC3 + ]; + + asm + { + call L1 ; + + mov RAX, RDI; + xchg RDX, R8; + ret; + +L1: pop RAX; + mov p[RBP],RAX; + } + + foreach (ref i, b; data) + { + //printf("data[%d] = 0x%02x, should be 0x%02x\n", i, p[i], b); + assert(p[i] == b); + } +} + +/****************************************************/ + int main() { printf("Testing iasm64.d\n"); @@ -6668,6 +6717,8 @@ int main() test9866(); testxadd(); test9965(); + test12849(); + test12968(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/implicit.d b/gcc/testsuite/gdc.test/runnable/implicit.d index da5304520..7659599b2 100644 --- a/gcc/testsuite/gdc.test/runnable/implicit.d +++ b/gcc/testsuite/gdc.test/runnable/implicit.d @@ -92,6 +92,242 @@ void test5() /***********************************/ +int* pureMaker() pure +{ + return [1,2,3,4].ptr + 1; +} + +void testDIP29_1() +{ + int* p; + static assert(!__traits(compiles, { immutable x = p + 3; })); + immutable x = pureMaker() + 1; + immutable y = pureMaker() - 1; + immutable z = 1 + pureMaker(); +} + +/***********************************/ + +int** pureMaker2() pure +{ + int*[] da = [[11,12,13].ptr, [21,22,23].ptr, [31,32,33].ptr, [41,42,43].ptr]; + return da.ptr + 1; +} + +void testDIP29_2() +{ + immutable x2 = pureMaker2() + 1; + immutable y2 = pureMaker2() - 1; + immutable z2 = 1 + pureMaker2(); +} + +/***********************************/ + +int[] pureMaker3a() pure +{ + return new int[4]; +} + +int* pureMaker3b() pure +{ + return new int[4].ptr; +} + +int[4] pureMaker3c() pure +{ + int[4] buf; + return buf; +} + +void testDIP29_3() +{ + immutable x1 = pureMaker3a()[]; + immutable x2 = pureMaker3a()[0..2]; + + immutable y2 = pureMaker3b()[0..2]; + + // Conversion from *rvalue* of mutable static array to immutable slice + immutable z1 = pureMaker3c()[]; + immutable z2 = pureMaker3c()[0..2]; + + // Issue 12467 - conversion from lvalue of mutable static array to immutable slice + char[3] arr = "foo"; + static assert(!__traits(compiles, { string str = arr[]; })); +} + +/***********************************/ + +import core.vararg; + +int* maker() pure { return null; } +int* maker1(int *) pure { return null; } +int* function(int *) pure makerfp1; +int* maker2(int *, ...) pure { return null; } +int* maker3(int) pure { return null; } +int* maker4(ref int) pure { return null; } +int* maker5(ref immutable int) pure { return null; } + +void testDIP29_4() +{ + { immutable x = maker1(maker()); } + { immutable x = maker1(null); } + static assert(__traits(compiles, { immutable x = (*makerfp1)(maker()); })); + { shared x = maker1(null); } + { immutable x = maker2(null, 3); } + { immutable int g; immutable x = maker2(null, 3, &g); } + static assert(!__traits(compiles, { int g; immutable x = maker2(null, 3, &g); })); + { immutable x = maker3(1); } + static assert(!__traits(compiles, { int g; immutable x = maker4(g); })); + { immutable int g; immutable x = maker5(g); } +} + +/***********************************/ + +int[] test6(int[] a) pure @safe nothrow +{ + return a.dup; +} + +/***********************************/ + +int*[] pureFoo() pure { return null; } + + +void testDIP29_5() pure +{ + { char[] s; immutable x = s.dup; } + { immutable x = (cast(int*[])null).dup; } + { immutable x = pureFoo(); } + { immutable x = pureFoo().dup; } +} + +/***********************************/ + +void testDIP29_6() +{ + /******* structs ************/ + + static assert(__traits(compiles, + { + static struct S { int *p; } + immutable s = new S; // since p is null + })); + + static assert(!__traits(compiles, + { + __gshared int x; + static struct S { int *p = &x; } + immutable s = new S; // x is mutable + })); + + static assert(!__traits(compiles, + { + int y; + struct S { int x; void bar() { y = 3; } } + immutable s = new S; // nested struct + })); + + static assert(!__traits(compiles, + { + static struct S { int x; this(int); } + immutable s = new S(1); + })); + + static assert(__traits(compiles, + { + static struct S { int x; this(int) pure; } + immutable s = new S(1); + })); + + static assert(__traits(compiles, + { + static struct S { int* p = void; this(int) pure; } + immutable s = new S(1); + })); + + static assert(!__traits(compiles, + { + static struct S { int* p = void; this(int*) pure; } + int x; + immutable s = new S(&x); + })); + + static assert(__traits(compiles, + { + static struct S { int* p = void; this(immutable(int)*) pure; } + immutable int x; + immutable s = new S(&x); + })); + + static assert(__traits(compiles, + { + static struct S { int* p = void; this(int*) pure; } + immutable s = new S(null); + })); + + /******* classes ************/ + + static assert(__traits(compiles, + { + static class S { int *p; } + immutable s = new S; // since p is null + })); + + static assert(!__traits(compiles, + { + __gshared int x; + static class S { int *p = &x; } + immutable s = new S; // x is mutable + })); + + static assert(!__traits(compiles, + { + int y; + class S { int x; void bar() { y = 3; } } + immutable s = new S; // nested class + })); + + static assert(!__traits(compiles, + { + static class S { int x; this(int); } + immutable s = new S(1); + })); + + static assert(__traits(compiles, + { + static class S { int x; this(int) pure; } + immutable s = new S(1); + })); + + static assert(__traits(compiles, + { + static class S { int* p = void; this(int) pure; } + immutable s = new S(1); + })); + + static assert(!__traits(compiles, + { + static class S { int* p = void; this(int*) pure; } + int x; + immutable s = new S(&x); + })); + + static assert(__traits(compiles, + { + static class S { int* p = void; this(immutable(int)*) pure; } + immutable int x; + immutable s = new S(&x); + })); + + static assert(__traits(compiles, + { + static class S { int* p = void; this(int*) pure; } + immutable s = new S(null); + })); +} + +/***********************************/ + void main() { test1(); @@ -99,6 +335,12 @@ void main() test3(); test4(); test5(); + testDIP29_1(); + testDIP29_2(); + testDIP29_3(); + testDIP29_4(); + testDIP29_5(); + testDIP29_6(); writefln("Success"); } diff --git a/gcc/testsuite/gdc.test/runnable/imports/a12874.d b/gcc/testsuite/gdc.test/runnable/imports/a12874.d new file mode 100644 index 000000000..fb2e6f2f0 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/imports/a12874.d @@ -0,0 +1,13 @@ +module imports.a12874; + +template foo(alias x) +{ + void check(int[] arr) + { + auto n = arr[0]; + } + void foo() + { + check([]); + } +} diff --git a/gcc/testsuite/gdc.test/runnable/imports/argufile.d b/gcc/testsuite/gdc.test/runnable/imports/argufile.d index eebdef160..fb3ccff49 100644 --- a/gcc/testsuite/gdc.test/runnable/imports/argufile.d +++ b/gcc/testsuite/gdc.test/runnable/imports/argufile.d @@ -7,7 +7,7 @@ import std.stdio; import std.format; import std.utf; -dstring formatstring(TypeInfo[] arguments, va_list argptr) +dstring formatstring(TypeInfo[] arguments, va_list argptr) { dstring message = null; diff --git a/gcc/testsuite/gdc.test/runnable/imports/link13043a.d b/gcc/testsuite/gdc.test/runnable/imports/link13043a.d new file mode 100644 index 000000000..fe5bedb8a --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/imports/link13043a.d @@ -0,0 +1,17 @@ +module imports.lin13043a; + +struct QualifiedNameTests +{ + struct Inner + { + const int opCmp(ref const Inner) { return 0; } + } + + shared(const(Inner[string])[]) data; + + version(bug) + size_t toHash() const + { + return typeid(typeof(data)).getHash(cast(const void*)&data); + } +} diff --git a/gcc/testsuite/gdc.test/runnable/imports/link13394a.d b/gcc/testsuite/gdc.test/runnable/imports/link13394a.d new file mode 100644 index 000000000..57b4422a8 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/imports/link13394a.d @@ -0,0 +1,24 @@ +module imports.link13394a; + +class A +{ + this() { } +} + +class Btpl(T) : T +{ + this()() { } + + invariant() {} +} + +alias B = Btpl!A; + +struct Stpl() +{ + void func()() {} + + invariant() {} +} + +alias S = Stpl!(); diff --git a/gcc/testsuite/gdc.test/runnable/imports/link13400a.d b/gcc/testsuite/gdc.test/runnable/imports/link13400a.d new file mode 100644 index 000000000..2b565dbd3 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/imports/link13400a.d @@ -0,0 +1,16 @@ +module imports.link13400a; + +struct BigInt +{ + this(string s) {} + + // This template opEquals is determined to 'identity opEquals' by + // hasIdentityOpEquals() in clone.c with the speculative instantiation + // with dummy rvalue argument. + // Then BigInt.opEquals!().opEquals(const BigInt y) const pure is stored + // in template instance cache. + bool opEquals()(auto ref const BigInt y) const pure + { + return true; + } +} diff --git a/gcc/testsuite/gdc.test/runnable/imports/link846a.d b/gcc/testsuite/gdc.test/runnable/imports/link846a.d new file mode 100644 index 000000000..1404d009b --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/imports/link846a.d @@ -0,0 +1,38 @@ +module imports.link846a; + +template ElemTypeOf(T) +{ + alias typeof(T.init[0]) ElemTypeOf; +} + +template removeIf_(Elem, Pred) +{ + size_t fn(Elem[] buf, Pred pred) + { + void exch(size_t p1, size_t p2) + { + Elem t = buf[p1]; + buf[p1] = buf[p2]; + buf[p2] = t; + } + + size_t cnt = 0; + + for (size_t pos = 0, len = buf.length; pos < len; ++pos) + { + if (pred(buf[pos])) + ++cnt; + else + exch(pos, pos - cnt); + } + return buf.length - cnt; + } +} + +template removeIf(Buf, Pred) +{ + size_t removeIf(Buf buf, Pred pred) + { + return removeIf_!(ElemTypeOf!Buf, Pred).fn(buf, pred); + } +} diff --git a/gcc/testsuite/gdc.test/runnable/imports/mangle10077.d b/gcc/testsuite/gdc.test/runnable/imports/mangle10077.d new file mode 100644 index 000000000..c25e616ed --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/imports/mangle10077.d @@ -0,0 +1,9 @@ +module imports.mangle10077; + +//UTF-8 chars +__gshared pragma(mangle, "test_эльфийские_письмена_9") ubyte test10077i_v; + +void setTest10077i() +{ + test10077i_v = 42; +} diff --git a/gcc/testsuite/gdc.test/runnable/imports/template13478a.d b/gcc/testsuite/gdc.test/runnable/imports/template13478a.d new file mode 100644 index 000000000..e10ceffe4 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/imports/template13478a.d @@ -0,0 +1,8 @@ +module imports.template13478a; + +bool foo(T)() { + // Make sure this is not inlined so template13478.o actually + // needs to reference it. + asm { nop; } + return false; +} diff --git a/gcc/testsuite/gdc.test/runnable/imports/template13478b.d b/gcc/testsuite/gdc.test/runnable/imports/template13478b.d new file mode 100644 index 000000000..1107e6b7c --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/imports/template13478b.d @@ -0,0 +1,7 @@ +import imports.template13478b; + +import imports.template13478a; + +// Note that foo is only used in the template constraint here. +T barImpl(T)(T t) if (is(typeof({ foo!T(); }))) { return t; } +int bar(int a) { return barImpl(a); } diff --git a/gcc/testsuite/gdc.test/runnable/imports/template_ovs1.d b/gcc/testsuite/gdc.test/runnable/imports/template_ovs1.d index 839532009..a4b2ecf2a 100644 --- a/gcc/testsuite/gdc.test/runnable/imports/template_ovs1.d +++ b/gcc/testsuite/gdc.test/runnable/imports/template_ovs1.d @@ -51,3 +51,8 @@ void Value1900b() {} Range remove8352a(alias pred, Range)(Range range) { return range; } void remove8352b(in char[] name) {} + +/***************************************************/ +// 10658 + +template Val10658(int n) { enum Val10658 = 1; } diff --git a/gcc/testsuite/gdc.test/runnable/imports/template_ovs2.d b/gcc/testsuite/gdc.test/runnable/imports/template_ovs2.d index 348f1cf6f..5b925689e 100644 --- a/gcc/testsuite/gdc.test/runnable/imports/template_ovs2.d +++ b/gcc/testsuite/gdc.test/runnable/imports/template_ovs2.d @@ -51,3 +51,8 @@ void Value1900b() {} void remove8352a(in char[] name) {} Range remove8352b(alias pred, Range)(Range range) { return range; } + +/***************************************************/ +// 10658 + +template Val10658(long n) { enum Val10658 = 2; } diff --git a/gcc/testsuite/gdc.test/runnable/inline.d b/gcc/testsuite/gdc.test/runnable/inline.d index 89312502e..f8ecf0bb5 100644 --- a/gcc/testsuite/gdc.test/runnable/inline.d +++ b/gcc/testsuite/gdc.test/runnable/inline.d @@ -286,6 +286,9 @@ struct Task } } +/************************************/ +// 9356 + void test9356() { static inout(char)[] bar (inout(char)[] a) @@ -308,6 +311,37 @@ void test12079() foo.get("bar", null).get("baz", null); } +/************************************/ +// 12243 + +char f12243() { return 'a'; } + +void test12243() +{ + string s; + s ~= f12243(); +} + +/************************************/ +// 11201 + +struct Foo11201 +{ + int a; + float b; + + Foo11201 func()() const { return this; } +} + +auto f11201()(Foo11201 a) { return a; } + +void test11201() +{ + auto a = Foo11201(0, 1); + + assert(f11201(a.func!()()) == a); +} + /************************************/ // 11223 @@ -330,6 +364,23 @@ void test11223() tmp = Tuple11223!string(); } +/************************************/ + + +void foo3918() +{ + import core.stdc.stdlib : alloca; + void[] mem = alloca(1024)[0..1024]; +} + +void test3918() +{ + foreach(i; 0 .. 10_000_000) + { + foo3918(); + } +} + /************************************/ // 11314 @@ -486,6 +537,7 @@ int main() test1(); test2(); test3(); + test3918(); test4(); test5(); test9356(); @@ -493,6 +545,7 @@ int main() test7(); test8(); test4841(); + test11201(); test11223(); test11314(); //test11224(); // XBUG: NRVO unimplemented. diff --git a/gcc/testsuite/gdc.test/runnable/interpret.d b/gcc/testsuite/gdc.test/runnable/interpret.d index 3a85c62df..1b539db91 100644 --- a/gcc/testsuite/gdc.test/runnable/interpret.d +++ b/gcc/testsuite/gdc.test/runnable/interpret.d @@ -2055,7 +2055,8 @@ int badfoo6() int badfoo7() { S[] b = [S(7), S(15), S(56), S(12), S(67)]; - b[1..4] = [S(17), S(4)]; // slice mismatch in dynamic array + S[] c = [S(17), S(4)]; + b[1..4] = c[]; // slice mismatch in dynamic array return 1; } diff --git a/gcc/testsuite/gdc.test/runnable/lazy.d b/gcc/testsuite/gdc.test/runnable/lazy.d index 18fbb3cc4..334627fcb 100644 --- a/gcc/testsuite/gdc.test/runnable/lazy.d +++ b/gcc/testsuite/gdc.test/runnable/lazy.d @@ -187,6 +187,26 @@ void test6() /*********************************************************/ +void foo7(long delegate()[] dg...) +{ + assert(dg[0]() == 1024); + assert(dg[1]() == 1024); +} + +void bar7(lazy long n) +{ + assert(n == 1024); +} + +void test7() +{ + int n = 1024; + foo7(n, n); + bar7(n); +} + +/*********************************************************/ + struct Bug5750 { int a, b; } pure Bug5750 bug5750(lazy int y) { Bug5750 retval; @@ -257,6 +277,7 @@ int main() test4(); test5(); test6(); + test7(); test5750a(); test5750b(); diff --git a/gcc/testsuite/gdc.test/runnable/lexer.d b/gcc/testsuite/gdc.test/runnable/lexer.d index 97c4f3474..c16d7b3e2 100644 --- a/gcc/testsuite/gdc.test/runnable/lexer.d +++ b/gcc/testsuite/gdc.test/runnable/lexer.d @@ -77,6 +77,10 @@ debug(9223372036854775807){} /*********************************************************/ +enum e13102=184467440737095516153.6L; + +/*********************************************************/ + int main() { test6(); diff --git a/gcc/testsuite/gdc.test/runnable/link13043.d b/gcc/testsuite/gdc.test/runnable/link13043.d new file mode 100644 index 000000000..296907442 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/link13043.d @@ -0,0 +1,5 @@ +// PERMUTE_ARGS: -g -inline -version=bug -release -O + +import imports.link13043a; + +void main() {} diff --git a/gcc/testsuite/gdc.test/runnable/link13394.d b/gcc/testsuite/gdc.test/runnable/link13394.d new file mode 100644 index 000000000..aea86862a --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/link13394.d @@ -0,0 +1,13 @@ +// COMPILE_SEPARATELY +// EXTRA_SOURCES: imports/link13394a.d + +module link13394; +import imports.link13394a; + +void main() +{ + auto b = new B(); + + auto s = S(); + s.func(); +} diff --git a/gcc/testsuite/gdc.test/runnable/link13400.d b/gcc/testsuite/gdc.test/runnable/link13400.d new file mode 100644 index 000000000..fbb9ec757 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/link13400.d @@ -0,0 +1,13 @@ +// COMPILE_SEPARATELY +// EXTRA_SOURCES: imports/link13400a.d + +import imports.link13400a; + +void main() +{ + BigInt r; + + // This comparison will instantiate BigInt.opEquals!().opEquals(const BigInt y) const pure again. + // But here is non-speculative context, so this module compilation should generate its objcode. + bool b = r == BigInt("2"); // comparison with rvalue +} diff --git a/gcc/testsuite/gdc.test/runnable/link846.d b/gcc/testsuite/gdc.test/runnable/link846.d new file mode 100644 index 000000000..2e92f8367 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/link846.d @@ -0,0 +1,6 @@ +import imports.link846a; + +void main() +{ + auto num = removeIf("abcdef".dup, (char c){ return c == 'c'; }); +} diff --git a/gcc/testsuite/gdc.test/runnable/mangle.d b/gcc/testsuite/gdc.test/runnable/mangle.d index 4201b06fb..bce43dab6 100644 --- a/gcc/testsuite/gdc.test/runnable/mangle.d +++ b/gcc/testsuite/gdc.test/runnable/mangle.d @@ -1,57 +1,548 @@ // PERMUTE_ARGS: -// EXTRA_SOURCES: imports/mangle1.d +// EXTRA_SOURCES: imports/mangle10077.d -import mangle1; +/***************************************************/ +// 10077 - pragma(mangle) -pragma(mangle, "_test1_") int test1; +pragma(mangle, "_test10077a_") int test10077a; +static assert(test10077a.mangleof == "_test10077a_"); -static assert(test1.mangleof == "_test1_"); +__gshared pragma(mangle, "_test10077b_") ubyte test10077b; +static assert(test10077b.mangleof == "_test10077b_"); -__gshared pragma(mangle, "_test2_") ubyte test2; +pragma(mangle, "_test10077c_") void test10077c() {} +static assert(test10077c.mangleof == "_test10077c_"); -static assert(test2.mangleof == "_test2_"); +pragma(mangle, "_test10077f_") __gshared char test10077f; +static assert(test10077f.mangleof == "_test10077f_"); -pragma(mangle, "_test3_") void test3() +pragma(mangle, "_test10077g_") @system { void test10077g() {} } +static assert(test10077g.mangleof == "_test10077g_"); + +template getModuleInfo(alias mod) +{ + pragma(mangle, "_D"~mod.mangleof~"12__ModuleInfoZ") static __gshared extern ModuleInfo mi; + enum getModuleInfo = &mi; +} + +void test10077h() +{ + assert(getModuleInfo!(object).name == "object"); +} + +//UTF-8 chars +__gshared extern pragma(mangle, "test_эльфийские_письмена_9") ubyte test10077i_evar; + +void test10077i() +{ + import imports.mangle10077; + + setTest10077i(); + assert(test10077i_evar == 42); +} + +/***************************************************/ +// 13050 + +void func13050(int); +template decl13050(Arg) +{ + void decl13050(Arg); +} +template problem13050(Arg) +{ + pragma(mangle, "foobar") + void problem13050(Arg); +} +template workaround13050(Arg) { + pragma(mangle, "foobar") + void func(Arg); + alias workaround13050 = func; } -static assert(test3.mangleof == "_test3_"); +static assert(is(typeof(&func13050) == void function(int))); +static assert(is(typeof(&decl13050!int) == void function(int))); +static assert(is(typeof(&problem13050!int) == void function(int))); +static assert(is(typeof(&workaround13050!int) == void function(int))); -pragma(mangle, "_test6_") __gshared char test6; +/***************************************************/ +// 2774 -static assert(test6.mangleof == "_test6_"); +int foo2774(int n) { return 0; } +static assert(foo2774.mangleof == "_D6mangle7foo2774FiZi"); -pragma(mangle, "_test7_") @system +class C2774 { - void test7() + int foo2774() { return 0; } +} +static assert(C2774.foo2774.mangleof == "_D6mangle5C27747foo2774MFZi"); + +template TFoo2774(T) {} +static assert(TFoo2774!int.mangleof == "6mangle15__T8TFoo2774TiZ"); + +void test2774() +{ + int foo2774(int n) { return 0; } + static assert(foo2774.mangleof == "_D6mangle8test2774FZ7foo2774MFiZi"); +} + +/*******************************************/ +// 8847 + +auto S8847() +{ + static struct Result { + inout(Result) get() inout { return this; } } + return Result(); } -static assert(test7.mangleof == "_test7_"); +void test8847a() +{ + auto a = S8847(); + auto b = a.get(); + alias typeof(a) A; + alias typeof(b) B; + assert(is(A == B), A.stringof~ " is different from "~B.stringof); +} -template getModuleInfo(alias mod) +// -------- + +enum result8847a = "S6mangle9iota8847aFZ6Result"; +enum result8847b = "S6mangle9iota8847bFZ4iotaMFZ6Result"; +enum result8847c = "C6mangle9iota8847cFZ6Result"; +enum result8847d = "C6mangle9iota8847dFZ4iotaMFZ6Result"; + +auto iota8847a() { - pragma(mangle, "_D"~mod.mangleof~"12__ModuleInfoZ") static __gshared extern ModuleInfo mi; - enum getModuleInfo = &mi; + static struct Result + { + this(int) {} + inout(Result) test() inout { return cast(inout)Result(0); } + } + static assert(Result.mangleof == result8847a); + return Result.init; +} +auto iota8847b() +{ + auto iota() + { + static struct Result + { + this(int) {} + inout(Result) test() inout { return cast(inout)Result(0); } + } + static assert(Result.mangleof == result8847b); + return Result.init; + } + return iota(); } +auto iota8847c() +{ + static class Result + { + this(int) {} + inout(Result) test() inout { return cast(inout)new Result(0); } + } + static assert(Result.mangleof == result8847c); + return Result.init; +} +auto iota8847d() +{ + auto iota() + { + static class Result + { + this(int) {} + inout(Result) test() inout { return cast(inout)new Result(0); } + } + static assert(Result.mangleof == result8847d); + return Result.init; + } + return iota(); +} +void test8847b() +{ + static assert(typeof(iota8847a().test()).mangleof == result8847a); + static assert(typeof(iota8847b().test()).mangleof == result8847b); + static assert(typeof(iota8847c().test()).mangleof == result8847c); + static assert(typeof(iota8847d().test()).mangleof == result8847d); +} + +// -------- -void test8() +struct Test8847 { - assert(getModuleInfo!(object).name == "object"); + enum result1 = "S6mangle8Test88478__T3fooZ3fooMFZ6Result"; + enum result2 = "S6mangle8Test88478__T3fooZ3fooMxFiZ6Result"; + + auto foo()() + { + static struct Result + { + inout(Result) get() inout { return this; } + } + static assert(Result.mangleof == Test8847.result1); + return Result(); + } + auto foo()(int n) const + { + static struct Result + { + inout(Result) get() inout { return this; } + } + static assert(Result.mangleof == Test8847.result2); + return Result(); + } +} +void test8847c() +{ + static assert(typeof(Test8847().foo( ).get()).mangleof == Test8847.result1); + static assert(typeof(Test8847().foo(1).get()).mangleof == Test8847.result2); } -//UTF-8 chars -__gshared extern pragma(mangle, "test_эльфийские_письмена_9") ubyte test9_1_e; +// -------- + +void test8847d() +{ + enum resultS = "S6mangle9test8847dFZ3fooMFZ3barMFZ3bazMFZ1S"; + enum resultX = "S6mangle9test8847dFZ3fooMFZ1X"; + // Return types for test8847d and bar are mangled correctly, + // and return types for foo and baz are not mangled correctly. + + auto foo() + { + struct X { inout(X) get() inout { return inout(X)(); } } + string bar() + { + auto baz() + { + struct S { inout(S) get() inout { return inout(S)(); } } + return S(); + } + static assert(typeof(baz() ).mangleof == resultS); + static assert(typeof(baz().get()).mangleof == resultS); + return ""; + } + return X(); + } + static assert(typeof(foo() ).mangleof == resultX); + static assert(typeof(foo().get()).mangleof == resultX); +} + +// -------- -void test9() +void test8847e() { - setTest91(); - assert(test9_1_e == 42); + enum resultHere = "6mangle"~"9test8847eFZ"~"8__T3fooZ"~"3foo"; + enum resultBar = "S"~resultHere~"MFNaNfNgiZ3Bar"; + enum resultFoo = "_D"~resultHere~"MFNaNbNiNfNgiZNg"~resultBar; // added 'Nb' + + // Make template function to infer 'nothrow' attributes + auto foo()(inout int) pure @safe + { + struct Bar {} + static assert(Bar.mangleof == resultBar); + return inout(Bar)(); + } + + auto bar = foo(0); + static assert(typeof(bar).stringof == "Bar"); + static assert(typeof(bar).mangleof == resultBar); + static assert(foo!().mangleof == resultFoo); } +// -------- + +pure f8847a() +{ + struct S {} + return S(); +} + +pure +{ + auto f8847b() + { + struct S {} + return S(); + } +} + +static assert(typeof(f8847a()).mangleof == "S6mangle6f8847aFNaZ1S"); +static assert(typeof(f8847b()).mangleof == "S6mangle6f8847bFNaZ1S"); + +/*******************************************/ +// 12352 + +auto bar12352() +{ + struct S { int var; void func() {} } + + static assert(!__traits(compiles, bar12352.mangleof)); // forward reference to bar + static assert(S .mangleof == "S6mangle8bar12352FZ1S"); + static assert(S.func.mangleof == "_D6mangle8bar12352FZ1S4funcMFZv"); + + return S(); +} +static assert( bar12352 .mangleof == "_D6mangle8bar12352FZS6mangle8bar12352FZ1S"); +static assert(typeof(bar12352()) .mangleof == "S6mangle8bar12352FZ1S"); +static assert(typeof(bar12352()).func.mangleof == "_D6mangle8bar12352FZ1S4funcMFZv"); + +auto baz12352() +{ + class C { int var; void func() {} } + + static assert(!__traits(compiles, baz12352.mangleof)); // forward reference to baz + static assert(C .mangleof == "C6mangle8baz12352FZ1C"); + static assert(C.func.mangleof == "_D6mangle8baz12352FZ1C4funcMFZv"); + + return new C(); +} +static assert( baz12352 .mangleof == "_D6mangle8baz12352FZC6mangle8baz12352FZ1C"); +static assert(typeof(baz12352()) .mangleof == "C6mangle8baz12352FZ1C"); +static assert(typeof(baz12352()).func.mangleof == "_D6mangle8baz12352FZ1C4funcMFZv"); + +/*******************************************/ +// 9525 + +void f9525(T)(in T*) { } + +void test9525() +{ + enum result1 = "S6mangle8test9525FZ26__T5test1S136mangle5f9525Z5test1MFZ1S"; + enum result2 = "S6mangle8test9525FZ26__T5test2S136mangle5f9525Z5test2MFNaNbZ1S"; + + void test1(alias a)() + { + static struct S {} + static assert(S.mangleof == result1); + S s; + a(&s); // Error: Cannot convert &S to const(S*) at compile time + } + static assert((test1!f9525(), true)); + + void test2(alias a)() pure nothrow + { + static struct S {} + static assert(S.mangleof == result2); + S s; + a(&s); // Error: Cannot convert &S to const(S*) at compile time + } + static assert((test2!f9525(), true)); +} + +/******************************************/ +// 10249 + +template Seq10249(T...) { alias Seq10249 = T; } + +mixin template Func10249(T) +{ + void func10249(T) {} +} +mixin Func10249!long; +mixin Func10249!string; + +void f10249(long) {} + +class C10249 +{ + mixin Func10249!long; + mixin Func10249!string; + static assert(Seq10249!(.func10249)[0].mangleof == "6mangle9func10249"); // <- 9func10249 + static assert(Seq10249!( func10249)[0].mangleof == "6mangle6C102499func10249"); // <- 9func10249 + +static: // necessary to make overloaded symbols accessible via __traits(getOverloads, C10249) + void foo(long) {} + void foo(string) {} + static assert(Seq10249!(foo)[0].mangleof == "6mangle6C102493foo"); // <- _D6mangle6C102493fooFlZv + static assert(Seq10249!(__traits(getOverloads, C10249, "foo"))[0].mangleof == "_D6mangle6C102493fooFlZv"); // <- + static assert(Seq10249!(__traits(getOverloads, C10249, "foo"))[1].mangleof == "_D6mangle6C102493fooFAyaZv"); // <- + + void g(string) {} + alias bar = .f10249; + alias bar = g; + static assert(Seq10249!(bar)[0].mangleof == "6mangle6C102496f10249"); // <- _D6mangle1fFlZv (todo!) + static assert(Seq10249!(__traits(getOverloads, C10249, "bar"))[0].mangleof == "_D6mangle6f10249FlZv"); // <- + static assert(Seq10249!(__traits(getOverloads, C10249, "bar"))[1].mangleof == "_D6mangle6C102491gFAyaZv"); // <- +} + +/*******************************************/ +// 11718 + +struct Ty11718(alias sym) {} + +auto fn11718(T)(T a) { return Ty11718!(a).mangleof; } +auto fn11718(T)() { T a; return Ty11718!(a).mangleof; } + +void test11718() +{ + string TyName(string tail)() + { + enum s = "__T7Ty11718" ~ tail; + enum int len = s.length; + return "S6mangle" ~ len.stringof ~ s; + } + string fnName(string paramPart)() + { + enum s = "_D6mangle35__T7fn11718T"~ + "S6mangle9test11718FZ1AZ7fn11718"~paramPart~"1a"~ + "S6mangle9test11718FZ1A"; + enum int len = s.length; + return len.stringof ~ s; + } + enum result1 = TyName!("S" ~ fnName!("F"~"S6mangle9test11718FZ1A"~"Z") ~ "Z") ~ "7Ty11718"; + enum result2 = TyName!("S" ~ fnName!("F"~"" ~"Z") ~ "Z") ~ "7Ty11718"; + + struct A {} + static assert(fn11718(A.init) == result1); + static assert(fn11718!A() == result2); +} + +/*******************************************/ +// 11776 + +struct S11776(alias fun) { } + +void test11776() +{ + auto g = () + { + if (1) + return; // fill tf->next + if (1) + { + auto s = S11776!(a => 1)(); + static assert(typeof(s).mangleof == + "S"~"6mangle"~"56"~( + "__T"~"6S11776"~"S42"~("6mangle"~"9test11776"~"FZ"~"9__lambda1MFZ"~"9__lambda1")~"Z" + )~"6S11776"); + } + }; +} + +/***************************************************/ +// 12044 + +struct S12044(T) +{ + void f()() + { + new T[1]; + } + + bool opEquals(O)(O) + { + f(); + } +} + +void test12044() +{ + () + { + enum E { e } + auto arr = [E.e]; + S12044!E s; + } + (); +} + +/*******************************************/ +// 12217 + +void test12217(int) +{ + static struct S {} + void bar() {} + int var; + template X(T) {} + + static assert( S.mangleof == "S6mangle9test12217FiZ1S"); + static assert( bar.mangleof == "_D6mangle9test12217FiZ3barMFZv"); + static assert( var.mangleof == "_D6mangle9test12217FiZ3vari"); + static assert(X!int.mangleof == "6mangle9test12217FiZ8__T1XTiZ"); +} + +void test12217() {} + +/***************************************************/ +// 12231 + +void func12231a()() +if (is(typeof({ + class C {} + static assert(C.mangleof == + "C6mangle16__U10func12231aZ10func12231aFZ9__lambda1MFZ1C"); + // ### L # + }))) +{} + +void func12231b()() +if (is(typeof({ + class C {} + static assert(C.mangleof == + "C6mangle16__U10func12231bZ10func12231bFZ9__lambda1MFZ1C"); + // L__L L LL + })) && + is(typeof({ + class C {} + static assert(C.mangleof == + "C6mangle16__U10func12231bZ10func12231bFZ9__lambda2MFZ1C"); + // L__L L LL + }))) +{} + +void func12231c()() +if (is(typeof({ + class C {} + static assert(C.mangleof == + "C6mangle16__U10func12231cZ10func12231cFZ9__lambda1MFZ1C"); + // L__L L LL + }))) +{ + (){ + class C {} + static assert(C.mangleof == + "C6mangle16__T10func12231cZ10func12231cFZ9__lambda1MFZ1C"); + // L__L L LL + }(); +} + +void func12231c(X)() +if (is(typeof({ + class C {} + static assert(C.mangleof == + "C6mangle20__U10func12231cTAyaZ10func12231cFZ9__lambda1MFZ1C"); + // L__L L___L LL + }))) +{ + (){ + class C {} + static assert(C.mangleof == + "C6mangle20__T10func12231cTAyaZ10func12231cFZ9__lambda1MFZ1C"); + // L__L L___L LL + }(); +} + +void test12231() +{ + func12231a(); + + func12231b(); + + func12231c(); + func12231c!string(); +} + +/***************************************************/ + void main() { - test8(); - test9(); + test10077h(); + test10077i(); + test12044(); } diff --git a/gcc/testsuite/gdc.test/runnable/mars1.d b/gcc/testsuite/gdc.test/runnable/mars1.d index 089caaff3..af313e596 100644 --- a/gcc/testsuite/gdc.test/runnable/mars1.d +++ b/gcc/testsuite/gdc.test/runnable/mars1.d @@ -218,6 +218,20 @@ void testarrayinit() /////////////////////// +void test13023(ulong n) +{ + static void func(bool b) {} + + ulong k = 0; + + func(k >= n / 2); + + if (k >= n / 2) + assert(0); +} + +/////////////////////// + struct U { int a; union { char c; int d; } long b; } U f = { b:3, d:2, a:1 }; @@ -877,6 +891,19 @@ void testbt() //////////////////////////////////////////////////////////////////////// +void test13383() +{ + foreach (k; 32..33) + { + if (1L & (1L << k)) + { + assert(0); + } + } +} + +//////////////////////////////////////////////////////////////////////// + int andand1(int c) { return (c > 32 && c != 46 && c != 44 @@ -1034,6 +1061,27 @@ void test10715() //////////////////////////////////////////////////////////////////////// +ptrdiff_t compare12164(A12164* rhsPA, A12164* zis) +{ + if (*rhsPA == *zis) + return 0; + return ptrdiff_t.min; +} + +struct A12164 +{ + int a; +} + +void test12164() +{ + auto a = A12164(3); + auto b = A12164(2); + assert(compare12164(&a, &b)); +} + +//////////////////////////////////////////////////////////////////////// + int foo10678(char[5] txt) { return txt[0] + txt[1] + txt[4]; @@ -1086,6 +1134,86 @@ int bug8525(int[] devt) return devt[$ - 1]; } +//////////////////////////////////////////////////////////////////////// + +void func13190(int) {} + +struct Struct13190 +{ + ulong a; + uint b; +}; + +__gshared Struct13190* table13190 = +[ + Struct13190(1, 1), + Struct13190(0, 2) +]; + +void test13190() +{ + for (int i = 0; table13190[i].a; i++) + { + ulong tbl = table13190[i].a; + func13190(i); + if (1 + tbl) + { + if (tbl == 0x80000) + return; + } + } +} + +//////////////////////////////////////////////////////////////////////// + +void test12833a(int a) +{ + long x = cast(long)a; + + switch (cast(int)(cast(ushort)(x >> 16 & 65535L))) + { + case 1: + { + break; + } + default: + { + assert(0); + } + } +} + +void test12833() +{ + test12833a(0x1_0000); +} + +/***********************************************/ + +struct Point9449 +{ + double f = 3.0; + double g = 4.0; +} + +void test9449() +{ + Point9449[1] arr; + if (arr[0].f != 3.0) assert(0); + if (arr[0].g != 4.0) assert(0); +} + +//////////////////////////////////////////////////////////////////////// +// https://issues.dlang.org/show_bug.cgi?id=12057 + +bool prop12057(real x) { return false; } +double f12057(real) { return double.init; } +void test12057() +{ + real fc = f12057(real.init); + if (fc == 0 || fc.prop12057) {} +} + //////////////////////////////////////////////////////////////////////// @@ -1097,6 +1225,7 @@ int main() testbreak(); teststringswitch(); teststrarg(); + test12164(); testsizes(); testarrayinit(); testU(); @@ -1115,10 +1244,16 @@ int main() testandand(); testor_combine(); testshrshl(); + test13383(); + test13190(); test10639(); test10715(); test10678(); test7565(); + test13023(0x10_0000_0000); + test12833(); + test9449(); + test12057(); printf("Success\n"); return 0; } diff --git a/gcc/testsuite/gdc.test/runnable/mixin1.d b/gcc/testsuite/gdc.test/runnable/mixin1.d index 8c0d8bf95..c2d35b460 100644 --- a/gcc/testsuite/gdc.test/runnable/mixin1.d +++ b/gcc/testsuite/gdc.test/runnable/mixin1.d @@ -1254,8 +1254,8 @@ void test11767() mixin M11767!(); alias S2 = S11767; static assert(!is(S1 == S2)); - static assert(S1.mangleof == "S6mixin19test11767FZv8__mixin16S11767"); - static assert(S2.mangleof == "S6mixin19test11767FZv8__mixin26S11767"); + static assert(S1.mangleof == "S6mixin19test11767FZ8__mixin16S11767"); + static assert(S2.mangleof == "S6mixin19test11767FZ8__mixin26S11767"); } mixin M11767!(); static assert(!__traits(compiles, S11767)); diff --git a/gcc/testsuite/gdc.test/runnable/nested.d b/gcc/testsuite/gdc.test/runnable/nested.d index 24d4e255d..206eabec0 100644 --- a/gcc/testsuite/gdc.test/runnable/nested.d +++ b/gcc/testsuite/gdc.test/runnable/nested.d @@ -1822,6 +1822,33 @@ void test8339c() Pair pair; } +/*******************************************/ +// 8704 + +void check8704(T, int num)() +{ + static if (num == 1) T t0; + static if (num == 2) T t1 = T(); + static if (num == 3) T t2 = T(1); +} + +void test8704() +{ + struct S + { + int n; + void foo(){} + } + + static assert(!is(typeof(check8704!(S, 1)()))); + static assert(!is(typeof(check8704!(S, 2)()))); + static assert(!is(typeof(check8704!(S, 3)()))); + + static assert(!__traits(compiles, check8704!(S, 1)())); + static assert(!__traits(compiles, check8704!(S, 2)())); + static assert(!__traits(compiles, check8704!(S, 3)())); +} + /*******************************************/ // 8923 @@ -2151,256 +2178,6 @@ void test9036() assert(j == 0); // fails, j = 2, postblit called } -/*******************************************/ -// 8847 - -auto S8847() -{ - static struct Result - { - inout(Result) get() inout { return this; } - } - return Result(); -} - -void test8847a() -{ - auto a = S8847(); - auto b = a.get(); - alias typeof(a) A; - alias typeof(b) B; - assert(is(A == B), A.stringof~ " is different from "~B.stringof); -} - -// -------- - -enum result8847a = "S6nested9iota8847aFZ6Result"; -enum result8847b = "S6nested9iota8847bFZ4iotaMFZ6Result"; -enum result8847c = "C6nested9iota8847cFZ6Result"; -enum result8847d = "C6nested9iota8847dFZ4iotaMFZ6Result"; - -auto iota8847a() -{ - static struct Result - { - this(int) {} - inout(Result) test() inout { return cast(inout)Result(0); } - } - static assert(Result.mangleof == result8847a); - return Result.init; -} -auto iota8847b() -{ - auto iota() - { - static struct Result - { - this(int) {} - inout(Result) test() inout { return cast(inout)Result(0); } - } - static assert(Result.mangleof == result8847b); - return Result.init; - } - return iota(); -} -auto iota8847c() -{ - static class Result - { - this(int) {} - inout(Result) test() inout { return cast(inout)new Result(0); } - } - static assert(Result.mangleof == result8847c); - return Result.init; -} -auto iota8847d() -{ - auto iota() - { - static class Result - { - this(int) {} - inout(Result) test() inout { return cast(inout)new Result(0); } - } - static assert(Result.mangleof == result8847d); - return Result.init; - } - return iota(); -} -void test8847b() -{ - static assert(typeof(iota8847a().test()).mangleof == result8847a); - static assert(typeof(iota8847b().test()).mangleof == result8847b); - static assert(typeof(iota8847c().test()).mangleof == result8847c); - static assert(typeof(iota8847d().test()).mangleof == result8847d); -} - -// -------- - -struct Test8847 -{ - enum result1 = "S6nested8Test88478__T3fooZ3fooMFZ6Result"; - enum result2 = "S6nested8Test88478__T3fooZ3fooMxFiZ6Result"; - - auto foo()() - { - static struct Result - { - inout(Result) get() inout { return this; } - } - static assert(Result.mangleof == Test8847.result1); - return Result(); - } - auto foo()(int n) const - { - static struct Result - { - inout(Result) get() inout { return this; } - } - static assert(Result.mangleof == Test8847.result2); - return Result(); - } -} -void test8847c() -{ - static assert(typeof(Test8847().foo( ).get()).mangleof == Test8847.result1); - static assert(typeof(Test8847().foo(1).get()).mangleof == Test8847.result2); -} - -// -------- - -void test8847d() -{ - enum resultS = "S6nested9test8847dFZv3fooMFZ3barMFZAya3bazMFZ1S"; - enum resultX = "S6nested9test8847dFZv3fooMFZ1X"; - // Return types for test8847d and bar are mangled correctly, - // and return types for foo and baz are not mangled correctly. - - auto foo() - { - struct X { inout(X) get() inout { return inout(X)(); } } - string bar() - { - auto baz() - { - struct S { inout(S) get() inout { return inout(S)(); } } - return S(); - } - static assert(typeof(baz() ).mangleof == resultS); - static assert(typeof(baz().get()).mangleof == resultS); - return ""; - } - return X(); - } - static assert(typeof(foo() ).mangleof == resultX); - static assert(typeof(foo().get()).mangleof == resultX); -} - -// -------- - -void test8847e() -{ - enum resultHere = "6nested"~"9test8847eFZv"~"8__T3fooZ"~"3foo"; - enum resultBar = "S"~resultHere~"MFNaNfNgiZ3Bar"; - enum resultFoo = "_D"~resultHere~"MFNaNbNfNgiZNg"~resultBar; // added 'Nb' - - // Make template function to infer 'nothrow' attributes - auto foo()(inout int) pure @safe - { - struct Bar {} - static assert(Bar.mangleof == resultBar); - return inout(Bar)(); - } - - auto bar = foo(0); - static assert(typeof(bar).stringof == "Bar"); - static assert(typeof(bar).mangleof == resultBar); - static assert(foo!().mangleof == resultFoo); -} - -/*******************************************/ -// 9525 - -void f9525(T)(in T*) { } - -void test9525() -{ - enum result1 = "S6nested8test9525FZv26__T5test1S136nested5f9525Z5test1MFZ1S"; - enum result2 = "S6nested8test9525FZv26__T5test2S136nested5f9525Z5test2MFNaNbZ1S"; - - void test1(alias a)() - { - static struct S {} - static assert(S.mangleof == result1); - S s; - a(&s); // Error: Cannot convert &S to const(S*) at compile time - } - static assert((test1!f9525(), true)); - - void test2(alias a)() pure nothrow - { - static struct S {} - static assert(S.mangleof == result2); - S s; - a(&s); // Error: Cannot convert &S to const(S*) at compile time - } - static assert((test2!f9525(), true)); -} - -/*******************************************/ -// 11718 - -struct Ty11718(alias sym) {} - -auto fn11718(T)(T a) { return Ty11718!(a).mangleof; } -auto fn11718(T)() { T a; return Ty11718!(a).mangleof; } - -void test11718() -{ - string TyName(string tail)() - { - enum s = "__T7Ty11718" ~ tail; - enum int len = s.length; - return "S6nested" ~ len.stringof ~ s; - } - string fnName(string paramPart)() - { - enum s = "_D6nested36__T7fn11718T"~ - "S6nested9test11718FZv1AZ7fn11718"~paramPart~"1a"~ - "S6nested9test11718FZv1A"; - enum int len = s.length; - return len.stringof ~ s; - } - enum result1 = TyName!("S" ~ fnName!("F"~"S6nested9test11718FZv1A"~"Z") ~ "Z") ~ "7Ty11718"; - enum result2 = TyName!("S" ~ fnName!("F"~"" ~"Z") ~ "Z") ~ "7Ty11718"; - - struct A {} - static assert(fn11718(A.init) == result1); - static assert(fn11718!A() == result2); -} - -/*******************************************/ -// 11776 - -struct S11776(alias fun) { } - -void test11776() -{ - auto g = () - { - if (1) - return; // fill tf->next - if (1) - { - auto s = S11776!(a => 1)(); - static assert(typeof(s).mangleof == - "S"~"6nested"~"57"~( - "__T"~"6S11776"~"S43"~("6nested"~"9test11776"~"FZv"~"9__lambda1MFZ"~"9__lambda1")~"Z" - )~"6S11776"); - } - }; -} - /*******************************************/ /+ @@ -2574,7 +2351,7 @@ void xmap(alias g)(int t) enum foo11297 = function (int x) { -// int bar(int y) { return x; } xmap!bar(7); + //int bar(int y) { return x; } xmap!bar(7); xmap!(y => x)(7); }; @@ -2587,6 +2364,43 @@ void test11297() { xreduce!foo11297(); } +/*******************************************/ +// 12234 + +void test12234() +{ + class B + { + int a; + this(int aa) { a = aa; } + } + auto foo = { + return new B(1); + }; + static assert(is(typeof(foo) == delegate)); + + auto b = foo(); + assert(b.a == 1); +} + +/*******************************************/ +// 12981 + +template Mix12981(T) +{ + class A + { + alias typeof(this.outer) x; + } +} + +class B12981 +{ + mixin Mix12981!(int); + + static assert(is(A.x == B12981)); +} + /*******************************************/ int main() @@ -2675,6 +2489,7 @@ int main() test9244(); test11385(); test11297(); + test12234(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/nogc.d b/gcc/testsuite/gdc.test/runnable/nogc.d new file mode 100644 index 000000000..bb917376c --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/nogc.d @@ -0,0 +1,56 @@ + +extern(C) int printf(const char*, ...); + +/***********************/ + +@nogc int test1() +{ + return 3; +} + +/***********************/ +// 3032 + +void test3032() @nogc +{ + scope o1 = new Object(); // on stack + scope o2 = new class Object {}; // on stack + + int n = 1; + scope fp = (){ n = 10; }; // no closure + fp(); + assert(n == 10); +} + +/***********************/ +// 12642 + +__gshared int[1] data12642; + +int[1] foo12642() @nogc +{ + int x; + return [x]; +} + +void test12642() @nogc +{ + int x; + data12642 = [x]; + int[1] data2; + data2 = [x]; + + data2 = foo12642(); +} + +/***********************/ + +int main() +{ + test1(); + test3032(); + test12642(); + + printf("Success\n"); + return 0; +} diff --git a/gcc/testsuite/gdc.test/runnable/nulltype.d b/gcc/testsuite/gdc.test/runnable/nulltype.d index db2a62174..32d117468 100644 --- a/gcc/testsuite/gdc.test/runnable/nulltype.d +++ b/gcc/testsuite/gdc.test/runnable/nulltype.d @@ -153,6 +153,21 @@ void test9385() assert(!null); } +/**********************************************/ +// 12203 + +void test12203() +{ + typeof(null) v; + void foo(float) {} + void delegate(float) dg = &foo; + assert(dg !is null); + + dg = v; // Error: e2ir: cannot cast v of type typeof(null) to type void delegate(float) + + assert(dg is null); +} + /**********************************************/ int main() @@ -163,6 +178,7 @@ int main() test8221(); test8589(); test9385(); + test12203(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/opover.d b/gcc/testsuite/gdc.test/runnable/opover.d index e82ca2cea..1c592b124 100644 --- a/gcc/testsuite/gdc.test/runnable/opover.d +++ b/gcc/testsuite/gdc.test/runnable/opover.d @@ -1020,6 +1020,54 @@ void test8522() assert(cp == cp); // doesn't work } +/**************************************/ +// 12778 + +struct Vec12778X +{ + Vec12778X opBinary(string op)(Vec12778X b) const + if (op == "+") + { + mixin("return Vec12778X(this.x " ~ op ~ " b.x, this.y " ~ op ~ " b.y);"); + } + alias opBinaryRight = opBinary; + + float x = 0, y = 0; +} + +struct Vec12778Y +{ + Vec12778Y opAdd()(Vec12778Y b) const + { + enum op = "+"; + mixin("return Vec12778Y(this.x " ~ op ~ " b.x, this.y " ~ op ~ " b.y);"); + } + alias opAdd_r = opAdd; + + float x = 0, y = 0; +} + +void test12778() +{ + struct S + { + void test1() + { + Vec12778X vx = vx1 + vx2; // ok + Vec12778Y vy = vy1 + vy2; // ok + } + + void test2() const + { + Vec12778X vx = vx1 + vx2; // ok <- error + Vec12778Y vy = vy1 + vy2; // ok <- error + } + + Vec12778X vx1, vx2; + Vec12778Y vy1, vy2; + } +} + /**************************************/ int main() diff --git a/gcc/testsuite/gdc.test/runnable/opover2.d b/gcc/testsuite/gdc.test/runnable/opover2.d index bf86c86f8..acf20d896 100644 --- a/gcc/testsuite/gdc.test/runnable/opover2.d +++ b/gcc/testsuite/gdc.test/runnable/opover2.d @@ -822,6 +822,260 @@ void test10037() assert(lhs == rhs); // lowered to: lhs.value == rhs.value } +/**************************************/ +// 5810 + +struct Bug5810 +{ + void opUnary(string op)() {} +} + +struct Foo5810 +{ + Bug5810 x; + void bar() { x++; } +} + +/**************************************/ +// 6798 + +struct Tuple6798(T...) +{ + T field; + alias field this; + + bool opEquals(Tuple6798 rhs) + { + foreach (i, _; T) + { + if (this[i] != rhs[i]) + return false; + } + return true; + } +} +auto tuple6798(T...)(T args) +{ + return Tuple6798!T(args); +} + +int test6798a() +{ + //import std.typecons; + alias tuple6798 tuple; + + static struct S1 + { + auto opDollar(size_t dim)() + { + return 99; + } + auto opSlice(int dim)(int lwr, int upr) + { + return [dim, lwr, upr]; + } + + auto opIndex(A...)(A indices) + { + return tuple(" []", indices); + } + auto opIndexUnary(string op, A...)(A indices) + { + return tuple(op~"[]", indices); + } + auto opIndexAssign(A...)(string s, A indices) + { + return tuple("[] =", s, indices); + } + auto opIndexOpAssign(string op, A...)(string s, A indices) + { + return tuple("[]"~op~"=", s, indices); + } + } + S1 s1; + assert( s1[] == tuple(" []")); + assert( s1[10] == tuple(" []", 10)); + assert( s1[10, 20] == tuple(" []", 10, 20)); + assert( s1[10..20] == tuple(" []", [0, 10, 20])); + assert(+s1[] == tuple("+[]")); + assert(-s1[10] == tuple("-[]", 10)); + assert(*s1[10, 20] == tuple("*[]", 10, 20)); + assert(~s1[10..20] == tuple("~[]", [0, 10, 20])); + assert((s1[] ="x") == tuple("[] =", "x")); + assert((s1[10] ="x") == tuple("[] =", "x", 10)); + assert((s1[10, 20] ="x") == tuple("[] =", "x", 10, 20)); + assert((s1[10..20] ="x") == tuple("[] =", "x", [0, 10, 20])); + assert((s1[] +="x") == tuple("[]+=", "x")); + assert((s1[10] -="x") == tuple("[]-=", "x", 10)); + assert((s1[10, 20]*="x") == tuple("[]*=", "x", 10, 20)); + assert((s1[10..20]~="x") == tuple("[]~=", "x", [0, 10, 20])); + assert( s1[20..30, 10] == tuple(" []", [0, 20, 30], 10)); + assert( s1[10, 10..$, $-4, $..2] == tuple(" []", 10, [1,10,99], 99-4, [3,99,2])); + assert(+s1[20..30, 10] == tuple("+[]", [0, 20, 30], 10)); + assert(-s1[10, 10..$, $-4, $..2] == tuple("-[]", 10, [1,10,99], 99-4, [3,99,2])); + assert((s1[20..30, 10] ="x") == tuple("[] =", "x", [0, 20, 30], 10)); + assert((s1[10, 10..$, $-4, $..2] ="x") == tuple("[] =", "x", 10, [1,10,99], 99-4, [3,99,2])); + assert((s1[20..30, 10] +="x") == tuple("[]+=", "x", [0, 20, 30], 10)); + assert((s1[10, 10..$, $-4, $..2]-="x") == tuple("[]-=", "x", 10, [1,10,99], 99-4, [3,99,2])); + + // opIndex exist, but opSlice for multi-dimensional doesn't. + static struct S2 + { + auto opSlice(size_t dim)() { return [dim]; } + auto opSlice()(size_t lwr, size_t upr) { return [lwr, upr]; } + + auto opIndex(A...)(A indices){ return [[indices]]; } + } + S2 s2; + static assert(!__traits(compiles, s2[] )); + assert(s2[1] == [[1]]); + assert(s2[1, 2] == [[1, 2]]); + assert(s2[1..2] == [1, 2]); + static assert(!__traits(compiles, s2[1, 2..3] )); + static assert(!__traits(compiles, s2[1..2, 2..3] )); + + // opSlice for multi-dimensional exists, but opIndex for that doesn't. + static struct S3 + { + auto opSlice(size_t dim)(size_t lwr, size_t upr) { return [lwr, upr]; } + + auto opIndex(size_t n){ return [[n]]; } + auto opIndex(size_t n, size_t m){ return [[n, m]]; } + } + S3 s3; + static assert(!__traits(compiles, s3[] )); + assert(s3[1] == [[1]]); + assert(s3[1, 2] == [[1, 2]]); + static assert(!__traits(compiles, s3[1..2] )); + static assert(!__traits(compiles, s3[1, 2..3] )); + static assert(!__traits(compiles, s3[1..2, 2..3] )); + + return 0; +} + +int test6798b() +{ + static struct Typedef(T) + { + private T Typedef_payload = T.init; + + alias a = Typedef_payload; + + auto ref opIndex(this X, D...)(auto ref D i) { return a[i]; } + auto ref opSlice(this X )() { return a[]; } + auto ref opSlice(this X, B, E)(auto ref B b, auto ref E e) + { + assert(b == 0 && e == 3); + return a[b..e]; + } + + template opDispatch(string name) + { + // field or property function + @property auto ref opDispatch(this X)() { return mixin("a."~name); } + @property auto ref opDispatch(this X, V)(auto ref V v) { return mixin("a."~name~" = v"); } + } + + static if (is(typeof(a) : E[], E)) + { + auto opDollar() const { return a.length; } + } + } + + Typedef!(int[]) dollar2; + dollar2.length = 3; + assert(dollar2.Typedef_payload.length == 3); + assert(dollar2[0 .. $] is dollar2[0 .. 3]); + + return 0; +} + +int test6798c() +{ + alias T = Tuple6798!(int, int); + auto n = T[].init; + static assert(is(typeof(n[0]) == Tuple6798!(int, int))); + + return 0; +} + +void test6798() +{ + static assert(test6798a() == 0); // CTFE check + test6798a(); + static assert(test6798b() == 0); + test6798b(); + static assert(test6798c() == 0); + test6798c(); +} + +/**************************************/ +// 12382 + +struct S12382 +{ + size_t opDollar() { return 0; } + size_t opIndex(size_t) { return 0; } +} + +S12382 func12382() { return S12382(); } + +static assert(S12382.init[$] == 0); +static assert(func12382()[$] == 0); +enum e12382a = S12382.init[$]; +enum e12382b = func12382()[$]; +static v12382a = S12382.init[$]; +static v12382b = func12382()[$]; + +void test12382() +{ + static assert(S12382.init[$] == 0); + static assert(func12382()[$] == 0); + enum e12382a = S12382.init[$]; + enum e12382b = func12382()[$]; + static v12382a = S12382.init[$]; + static v12382b = func12382()[$]; +} + +/**************************************/ +// 12904 + +struct S12904 +{ + void opIndexAssign(U, A...)(U value, A args) + { + static assert(0); + } + void opSliceAssign(int n) + { + assert(n == 10); + } + + size_t opDollar(size_t dim)() + { + return 7; + } + + int opSlice(size_t dim)(size_t, size_t to) + { + assert(to == 7); + return 1; + } + + int opIndex(int i1, int i2) + { + assert(i1 == 1 && i2 == 1); + return 10; + } +} + +void test12904() +{ + S12904 s; + s[] = s[0..$, 1]; + s[] = s[0..$, 0..$]; +} + /**************************************/ // 7641 @@ -1054,7 +1308,7 @@ void test18() } assert(dollar!q{@property size_t opDollar() { return 8; }}() == 8); assert(dollar!q{template opDollar(size_t dim) { enum opDollar = dim; }}() == 0); - assert(dollar!q{const size_t opDollar = 8;}() == 8); + assert(dollar!q{static const size_t opDollar = 8;}() == 8); assert(dollar!q{enum opDollar = 8;}() == 8); assert(dollar!q{size_t length() { return 8; } alias length opDollar;}() == 8); } @@ -1207,6 +1461,45 @@ void test10064() auto x = S(0)[0 .. $]; } +/**************************************/ +// 12585 + +void test12585() +{ + struct Bar + { + int opIndex(size_t index) + { + return 0; + } + } + + struct Foo + { + Bar opIndex(size_t index) + { + throw new Exception("Fail"); + } + } + + Foo foo() + { + return Foo(); + } + + void catchStuff(E)(lazy E expression) + { + try + expression(); + catch (Exception e) {} + } + + catchStuff(foo()[0][0]); // OK <- NG + catchStuff(foo().opIndex(0)[0]); // OK + catchStuff(foo()[0].opIndex(0)); // OK + Foo f; catchStuff(f[0][0]); // OK +} + /**************************************/ // 10394 @@ -1494,6 +1787,25 @@ void test11311() assert(ctor + cpctor == dtor); } +/**************************************/ +// 12193 + +void test12193() +{ + struct Foo + { + bool bar; + alias bar this; + void opOpAssign(string op)(size_t x) + { + bar = false; + } + } + + Foo foo; + foo <<= 1; +} + /**************************************/ int main() @@ -1518,6 +1830,8 @@ int main() test17(); test3789(); test10037(); + test6798(); + test12904(); test7641(); test8434(); test18(); @@ -1527,6 +1841,7 @@ int main() test9689(); test9694(); test10064(); + test12585(); test10394(); test10567(); test11062(); diff --git a/gcc/testsuite/gdc.test/runnable/overload.d b/gcc/testsuite/gdc.test/runnable/overload.d index 92e77255f..21794c8cb 100644 --- a/gcc/testsuite/gdc.test/runnable/overload.d +++ b/gcc/testsuite/gdc.test/runnable/overload.d @@ -588,8 +588,6 @@ void test1900c() assert(s.a.mixfoob(10) == 2); static assert(!__traits(compiles, s.a.mixfoob())); } -version(none) // yet not implemented -{ alias merge1900 = imports.template_ovs1.merge1900; alias merge1900 = imports.template_ovs2.merge1900; @@ -598,11 +596,6 @@ void test1900d() assert( merge1900!double(100) == 1); assert(.merge1900!double(100) == 1); } -} -else -{ -void test1900d() {} // dummy -} mixin template Foo1900e(T) { @@ -648,8 +641,6 @@ void test1900() static assert(Value1900b!string == 2); } -version(none) // yet not implemented -{ alias imports.template_ovs1.Traits1900 Traits1900X; alias imports.template_ovs2.Traits1900 Traits1900X; alias imports.template_ovs3.Traits1900 Traits1900X; @@ -665,7 +656,6 @@ alias Traits1900Y2 Traits1900Y; static assert(Traits1900Y!(AClass1900).name == "AClass"); static assert(!__traits(compiles, Traits1900Y!(BClass1900))); static assert(!__traits(compiles, Traits1900Y!(int))); -} template Foo1900(T) { @@ -861,8 +851,6 @@ void test9235a() // ---- -version(none) // yet not implemented -{ mixin template mixA9235() { int foo(string s)() if (s == "a") { return 1; } @@ -884,6 +872,88 @@ void test9235b() assert(f.foo!"a"() == 1); assert(f.foo!"b"() == 2); } + +/***************************************************/ +// 10658 + +alias Val10658 = imports.template_ovs1.Val10658; +alias Val10658 = imports.template_ovs2.Val10658; +static assert(Val10658!1 == 1); +static assert(Val10658!1L == 2); + +// ---- + +template Foo10658(T) if (is(T == double)) { enum Foo10658 = 1; } +template Bar10658(T) if (is(T == string)) { enum Bar10658 = 2; } +alias Baz10658 = Foo10658; +alias Baz10658 = Bar10658; + +template Voo10658(T) if (is(T == cfloat)) { enum Voo10658 = 5; } +template Voo10658(T) if (is(T == Object)) { enum Voo10658 = 6; } + +alias Vaz10658 = Baz10658; // OvarDeclaration +alias Vaz10658 = Voo10658; // TemplateDeclaration (overnext != NULL) + +template Merge10658a(alias A) +{ + enum Merge10658a = A!double + A!string; +} +template Merge10658b(alias A) +{ + enum Merge10658b = A!double + A!string + A!cfloat + A!Object; +} + +void test10658a() +{ + static assert(Baz10658!double == 1); + static assert(Baz10658!string == 2); + static assert(Voo10658!cfloat == 5); + static assert(Voo10658!Object == 6); + + // pass OverDeclaration through TemplateAliasParameter + static assert(Merge10658a!Baz10658 == 1 + 2); + static assert(Merge10658b!Vaz10658 == 1 + 2 + 5 + 6); +} + +// ---- + +mixin template mix10658A() +{ + int f10658(string s)() if (s == "a") { return 1; } +} +mixin template mix10658B() +{ + int f10658(string s)() if (s == "b") { return 2; } +} +mixin mix10658A A10658; +mixin mix10658B B10658; +alias A10658.f10658 foo10658; +alias B10658.f10658 foo10658; + +mixin template mix10658C() +{ + int f10658(string s, T)(T arg) if (s == "c") { return 3; } +} +mixin template mix10658D() +{ + int f10658(string s, T)(T arg) if (s == "d") { return 4; } +} +struct S10658 +{ + mixin mix10658C C10658; + mixin mix10658D D10658; + alias C10658.f10658 foo10658; + alias D10658.f10658 foo10658; +} + +void test10658b() +{ + assert( foo10658!"a"() == 1); + assert(.foo10658!"b"() == 2); + + S10658 s; + assert(s.foo10658!"c"(0) == 3); + assert(s.foo10658!"d"(0) == 4); } /***************************************************/ @@ -945,7 +1015,9 @@ int main() test8441b(); test8441c(); test9235a(); - //test9235b(); + test9235b(); + test10658a(); + test10658b(); test11785(); printf("Success\n"); diff --git a/gcc/testsuite/gdc.test/runnable/sdtor.d b/gcc/testsuite/gdc.test/runnable/sdtor.d index 996bb512b..f9151a372 100644 --- a/gcc/testsuite/gdc.test/runnable/sdtor.d +++ b/gcc/testsuite/gdc.test/runnable/sdtor.d @@ -2415,11 +2415,6 @@ struct Test9386 printf("Deleted %.*s\n", name.length, name.ptr); op ~= "c"; } - - const int opCmp(ref const Test9386 t) - { - return op[0] - t.op[0]; - } } void test9386() @@ -2734,7 +2729,7 @@ void test10055a() static struct SG { SX sx; SY sy; nothrow ~this() {} } static struct SH { SX sx; SY sy; pure ~this() {} } static struct SI { SX sx; SY sy; ~this() {} } - static assert(is( typeof(&check!S1) == void function() pure nothrow @safe )); + static assert(is( typeof(&check!S1) == void function() pure nothrow @nogc @safe )); static assert(is( typeof(&check!S2) == void function() )); static assert(is( typeof(&check!SA) == void function() pure nothrow @safe )); static assert(is( typeof(&check!SB) == void function() pure nothrow @safe )); @@ -2783,7 +2778,7 @@ void test10055b() static struct SG { SX sx; SY sy; nothrow this(this) {} } static struct SH { SX sx; SY sy; pure this(this) {} } static struct SI { SX sx; SY sy; this(this) {} } - static assert(is( typeof(&check!S1) == void function() pure nothrow @safe )); + static assert(is( typeof(&check!S1) == void function() pure nothrow @nogc @safe )); static assert(is( typeof(&check!S2) == void function() )); static assert(is( typeof(&check!SA) == void function() pure nothrow @safe )); static assert(is( typeof(&check!SB) == void function() pure nothrow @safe )); @@ -2818,7 +2813,7 @@ void test10055b() struct S10160 { this(this) {} } struct X10160a { S10160 s; const int x; } -struct X10160b { S10160 s; const int x = 1; } +struct X10160b { S10160 s; enum int x = 1; } void test10160() { @@ -3133,6 +3128,208 @@ void test11505() f = Foo11505(); } +/**********************************/ +// 12045 + +bool test12045() +{ + string dtor; + void* ptr; + + struct S12045 + { + string val; + + this(this) { assert(0); } + ~this() { dtor ~= val; } + } + + auto makeS12045(bool thrown) + { + auto s1 = S12045("1"); + auto s2 = S12045("2"); + ptr = &s1; + + if (thrown) + throw new Exception(""); + + return s1; // NRVO + } + + dtor = null, ptr = null; + try + { + S12045 s = makeS12045(true); + assert(0); + } + catch (Exception e) + { + assert(dtor == "21", dtor); + } + + dtor = null, ptr = null; + { + S12045 s = makeS12045(false); + assert(dtor == "2"); + if (!__ctfe) assert(ptr is &s); // NRVO + } + assert(dtor == "21"); + + return true; +} +static assert(test12045()); + +/**********************************/ +// 12591 + +struct S12591(T) +{ + this(this) + {} +} + +struct Tuple12591(Types...) +{ + Types expand; + this(Types values) + { + expand[] = values[]; + } +} + +void test12591() +{ + alias T1 = Tuple12591!(S12591!int); +} + +/**********************************/ +// 12660 + +struct X12660 +{ + this(this) @nogc {} + ~this() @nogc {} + void opAssign(X12660) @nogc {} + @nogc invariant() {} +} +struct Y12660 +{ + X12660 x; + + this(this) @nogc {} + ~this() @nogc {} + @nogc invariant() {} +} +struct Z12660 +{ + Y12660 y; +} + +class C12660 +{ + this() @nogc {} + @nogc invariant() {} +} + +void test12660() @nogc +{ + X12660 x; + x = x; + + Y12660 y = { x }; + y = y; + + Z12660 z = { y }; + z = z; +} + +/**********************************/ +// 12686 + +struct Foo12686 +{ + static int count; + + invariant() { ++count; } + + @disable this(this); + + Foo12686 bar() + { + Foo12686 f; + return f; + } +} + +void test12686() +{ + Foo12686 f; + Foo12686 f2 = f.bar(); + version (unittest) + { } + else + assert(Foo12686.count == 2); +} + +/**********************************/ +// 13089 + +struct S13089 +{ + @disable this(this); // non nothrow +} + +void* p13089; + +S13089[1000] foo13089() nothrow +{ + typeof(return) data; + p13089 = &data; + return data; +} + +void test13089() nothrow +{ + immutable data = foo13089(); + assert(p13089 == &data); +} + +/**********************************/ + +struct NoDtortest11763 {} + +struct HasDtortest11763 +{ + NoDtortest11763 func() + { + return NoDtortest11763(); + } + ~this() {} +} + +void test11763() +{ + HasDtortest11763().func(); +} + +/**********************************/ + +struct Buf { } + +struct Variant +{ + ~this() { } + + Buf get() { Buf b; return b; } +} + +Variant value() { Variant v; return v; } + +void test13303() +{ + value.get(); +} + /**********************************/ int main() @@ -3231,6 +3428,13 @@ int main() test11197(); test7474(); test11505(); + //test12045(); // XBUG: NRVO unimplemented. + test12591(); + test12660(); + test12686(); + //test13089(); // XBUG: NRVO unimplemented. + test11763(); + test13303(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/statictor.d b/gcc/testsuite/gdc.test/runnable/statictor.d index 305e26680..e378966ae 100644 --- a/gcc/testsuite/gdc.test/runnable/statictor.d +++ b/gcc/testsuite/gdc.test/runnable/statictor.d @@ -28,6 +28,28 @@ class Bar static ~this() {printf("Bar static dtor\n");} } +/***********************************************/ +// 6677 + +int global6677; + +static this() nothrow pure @safe +{ + int* p; + static assert(!__traits(compiles, ++p)); + static assert(!__traits(compiles, ++global6677)); + auto throwit = { throw new Exception("sup"); }; + static assert(!__traits(compiles, throwit() )); +} + +shared static this() nothrow pure @safe +{ + int* p; + static assert(!__traits(compiles, ++p)); + static assert(!__traits(compiles, ++global6677)); +} + +/***********************************************/ // 7533 struct Foo7533(int n) { @@ -36,6 +58,8 @@ struct Foo7533(int n) alias Foo7533!5 Bar7533; +/***********************************************/ + void main() { } diff --git a/gcc/testsuite/gdc.test/runnable/structlit.d b/gcc/testsuite/gdc.test/runnable/structlit.d index de1bf19f3..25297ea3d 100644 --- a/gcc/testsuite/gdc.test/runnable/structlit.d +++ b/gcc/testsuite/gdc.test/runnable/structlit.d @@ -618,7 +618,7 @@ void test9993b() } A ma = new A; assert(ma.x == 13); immutable A ia = new immutable A; assert(ia.x == 42); - static assert(!__traits(compiles, { immutable A ia = new A; })); + static assert(__traits(compiles, { immutable A ia = new A; })); static class B { @@ -628,7 +628,7 @@ void test9993b() } const B mb = new B; assert(mb.x == 13); const B cb = new const B; assert(cb.x == 42); - static assert(!__traits(compiles, { immutable B ib = new B; })); + static assert(__traits(compiles, { immutable B ib = new B; })); static class C { @@ -850,6 +850,51 @@ void test6937() assert(py2.x.v == 2); } +/********************************************/ +// 12681 + +struct HasUnion12774 +{ + union + { + int a, b; + } +} + +bool test12681() +{ + immutable int x = 42; + + static struct S1 + { + immutable int *p; + } + immutable s1 = new S1(&x); + assert(s1.p == &x); + + struct S2 + { + immutable int *p; + void foo() {} + } + auto s2 = new S2(&x); + assert(s2.p == &x); + + struct S3 + { + immutable int *p; + int foo() { return x; } + } + auto s3 = new S3(&x); + assert(s3.p == &x); + assert(s3.foo() == 42); + + auto x12774 = new HasUnion12774(); + + return true; +} +static assert(test12681()); + /********************************************/ // 3991 @@ -1011,6 +1056,23 @@ void test7021() }))); } +/********************************************/ +// 8738 + +void test8738() +{ + int[3] a = [1, 2, 3]; + + struct S { int a, b, c; } + S s = S(1, 2, 3); + + a = [4, a[0], 6]; + s = S(4, s.a, 6); + + assert(a == [4, 1, 6]); + assert(s == S(4, 1, 6)); +} + /********************************************/ // 8763 @@ -1259,6 +1321,67 @@ int foo11427() @safe return 0; } +/********************************************/ +// 12011 + +struct S12011a +{ + int f() { return i; } + enum e = this.init.f(); + int i = 1, j = 2; +} + +struct S12011b +{ + int f() { return i; } + enum e = S12011b().f(); + int i = 1, j = 2; +} + +void test12011() +{ + static assert(S12011a.e == 1); + static assert(S12011b.e == 1); +} + +/********************************************/ +// 13021 + +void test13021() +{ + static union U1 + { + float a; + int b; + } + + static union U2 + { + double a; + long b; + } + + static union U3 + { + real a; + struct B { long b1, b2; } // ICE happens only if B.sizeof == real.sizeof + B b; + } + + static union U4 + { + real a; + long[2] b; // ditto + } + + auto f = U1(1.0); auto ok = f.b; + + auto fail1 = U1(1.0).b; // OK <- Internal error: e2ir.c 1162 + auto fail2 = U2(1.0).b; // OK <- Internal error: e2ir.c 1162 + auto fail3 = U3(1.0).b; // OK <- Internal error: e2ir.c 1162 + auto fail4 = U4(1.0).b; // OK <- Internal error: backend/el.c 2904 +} + /********************************************/ int main() @@ -1289,10 +1412,12 @@ int main() test5889(); test4247(); test6937(); + test12681(); test3991(); test7727(); test7929(); test7021(); + test8738(); test8763(); test8902(); test9116(); @@ -1301,6 +1426,7 @@ int main() test11105(); test11147(); test11256(); + test13021(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/template13478.d b/gcc/testsuite/gdc.test/runnable/template13478.d new file mode 100644 index 000000000..7fa7a18aa --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/template13478.d @@ -0,0 +1,10 @@ +/// Tests emission of templates also referenced in speculative contexts. +/// Failure triggered with -inline. +module template13478; + +import imports.template13478a; +import imports.template13478b; + +int main() { + return foo!int(); +} diff --git a/gcc/testsuite/gdc.test/runnable/template4.d b/gcc/testsuite/gdc.test/runnable/template4.d index b6631d05e..357a8dbb2 100644 --- a/gcc/testsuite/gdc.test/runnable/template4.d +++ b/gcc/testsuite/gdc.test/runnable/template4.d @@ -6,21 +6,21 @@ import std.c.stdio; template Foo(T) { static if ( is(T : int) ) - alias T t1; + alias T t1; static if (T.sizeof == 4) - alias T t2; + alias T t2; static if ( is(T AB : int) ) - alias AB t3; + alias AB t3; static if ( is(T* V : V*) ) - alias V t4; + alias V t4; static if ( is(T W) ) - alias W t5; + alias W t5; else - alias char t5; + alias char t5; static if ( is(T* X : X*) ) { @@ -56,21 +56,21 @@ void test2() alias int T; static if ( is(T : int) ) - alias T t1; + alias T t1; static if (T.sizeof == 4) - alias T t2; + alias T t2; static if ( is(T U : int) ) - alias U t3; + alias U t3; static if ( is(T* V : V*) ) - alias V t4; + alias V t4; static if ( is(T W) ) - alias W t5; + alias W t5; else - alias char t5; + alias char t5; static if ( is(T* X : X*) ) { @@ -100,17 +100,19 @@ void test2() void test3() { static if ( is(short : int) ) - { printf("1\n"); + { + printf("1\n"); } else - assert(0); + assert(0); static if ( is(short == int) ) - assert(0); + assert(0); static if ( is(int == int) ) - { printf("3\n"); + { + printf("3\n"); } else - assert(0); + assert(0); } /*********************************************************/ @@ -120,76 +122,76 @@ void test4() alias void Function(int); static if (is(Function Void == function)) - printf("if\n"); + printf("if\n"); else - assert(0); + assert(0); // static if (is(Void == void)) -// printf("if\n"); +// printf("if\n"); // else -// assert(0); +// assert(0); alias byte delegate(int) Delegate; static if (is(Delegate Foo == delegate)) - printf("if\n"); + printf("if\n"); else - assert(0); + assert(0); static if (is(Foo Byte == function)) - printf("if\n"); + printf("if\n"); else - assert(0); + assert(0); // static if (is(Byte == byte)) -// printf("if\n"); +// printf("if\n"); // else -// assert(0); +// assert(0); union Union { } static if (is(Union == union)) - printf("if\n"); + printf("if\n"); else - assert(0); + assert(0); struct Struct { } static if (is(Struct == struct)) - printf("if\n"); + printf("if\n"); else - assert(0); + assert(0); enum Enum : short { EnumMember } static if (is(Enum Short == enum)) - printf("if\n"); + printf("if\n"); else - assert(0); + assert(0); static if (is(Short == short)) - printf("if\n"); + printf("if\n"); else - assert(0); + assert(0); class Class { } static if (is(Class == class)) - printf("if\n"); + printf("if\n"); else - assert(0); + assert(0); interface Interface { } static if (is(Interface == interface)) - printf("if\n"); + printf("if\n"); else - assert(0); + assert(0); } @@ -201,7 +203,7 @@ class Foo5(T) struct Node { - int value; + int value; } } @@ -217,9 +219,9 @@ void test5() template factorial6(int n) { static if (n == 1) - const int factorial6 = 1; + const int factorial6 = 1; else - const int factorial6 = n * .factorial6!(n-1); + const int factorial6 = n * .factorial6!(n-1); } void test6() @@ -235,9 +237,9 @@ void test6() template factorial7(float n, cdouble c, string sss, string ttt) { static if (n == 1) - const float factorial7 = 1; + const float factorial7 = 1; else - const float factorial7 = n * 2; + const float factorial7 = n * 2; } template bar7(wstring abc, dstring def) @@ -310,24 +312,24 @@ template Recurse(string pattern){ template slice(string str, int from, int to) { - const string slice = str[from..to]; + const string slice = str[from..to]; } template Compile(string pattern) { - const string left = slice!(pattern,4,pattern.length); - - const string remaining = slice!(left,1,left.length); - - alias Recurse!(remaining) fn; + const string left = slice!(pattern,4,pattern.length); + + const string remaining = slice!(left,1,left.length); + + alias Recurse!(remaining) fn; } template Match(string pattern) { - alias Compile!(pattern) Match; + alias Compile!(pattern) Match; } void test9() @@ -405,7 +407,7 @@ template Foo13(int i) { const int j = i + 1; static if (j == 3) - const int k = 2; + const int k = 2; } void test13() @@ -418,18 +420,18 @@ void test13() template zebra(string w) { - static if (w.length > 2 && w[1] == 'q') - const bool zebra = true; - else - const bool zebra = false; + static if (w.length > 2 && w[1] == 'q') + const bool zebra = true; + else + const bool zebra = false; } template horse(string w) { - static if (w.length == 1 || w[1] == 'q') - const bool horse = true; - else - const bool horse = false; + static if (w.length == 1 || w[1] == 'q') + const bool horse = true; + else + const bool horse = false; } void test14() @@ -514,8 +516,9 @@ const uint[14] testtable = void test16() { for (int i=0; i a + 1)(3); if (i != 4) - assert(0); + assert(0); } /*********************************************************/ @@ -1061,6 +1063,34 @@ void test6701() assert(foo2_6701!(0u, "+")() == 1); } +/******************************************/ +// 7469 + +struct Foo7469a(int x) { } +struct Foo7469b(int x) { } +struct Foo7469c(alias v) { } +struct Foo7469d(T...) { } +struct Foo7469e(int a, T...) { } +struct Foo7469f(T, int k=1) { } +struct Foo7469g(T, int k=1) { } + +void test7469() +{ + static assert(Foo7469a!(3 ) .mangleof[$-28 .. $] == "17__T8Foo7469aVii3Z8Foo7469a"); + static assert(Foo7469a!(3u) .mangleof[$-28 .. $] == "17__T8Foo7469aVii3Z8Foo7469a"); + static assert(Foo7469b!(3u) .mangleof[$-28 .. $] == "17__T8Foo7469bVii3Z8Foo7469b"); + static assert(Foo7469b!(3 ) .mangleof[$-28 .. $] == "17__T8Foo7469bVii3Z8Foo7469b"); + static assert(Foo7469c!(3 ) .mangleof[$-28 .. $] == "17__T8Foo7469cVii3Z8Foo7469c"); + static assert(Foo7469c!(3u) .mangleof[$-28 .. $] == "17__T8Foo7469cVki3Z8Foo7469c"); + static assert(Foo7469d!(3 ) .mangleof[$-28 .. $] == "17__T8Foo7469dVii3Z8Foo7469d"); + static assert(Foo7469d!(3u) .mangleof[$-28 .. $] == "17__T8Foo7469dVki3Z8Foo7469d"); + static assert(Foo7469e!(3u, 5u).mangleof[$-32 .. $] == "21__T8Foo7469eVii3Vki5Z8Foo7469e"); + static assert(Foo7469f!(int, 1).mangleof[$-30 .. $] == "19__T8Foo7469fTiVii1Z8Foo7469f"); + static assert(Foo7469f!(int) .mangleof[$-30 .. $] == "19__T8Foo7469fTiVii1Z8Foo7469f"); + static assert(Foo7469g!(int) .mangleof[$-30 .. $] == "19__T8Foo7469gTiVii1Z8Foo7469g"); + static assert(Foo7469g!(int, 1).mangleof[$-30 .. $] == "19__T8Foo7469gTiVii1Z8Foo7469g"); +} + /******************************************/ template foo7698a(T, T val : 0) diff --git a/gcc/testsuite/gdc.test/runnable/template9.d b/gcc/testsuite/gdc.test/runnable/template9.d index 14cb06dec..343ffaa50 100644 --- a/gcc/testsuite/gdc.test/runnable/template9.d +++ b/gcc/testsuite/gdc.test/runnable/template9.d @@ -203,6 +203,47 @@ void test1780() static assert(is(SQ1780 == Tuple1780!(bool, short))); } +/**********************************/ +// 1659 + +class Foo1659 { } +class Bar1659 : Foo1659 { } + +void f1659(T : Foo1659)() { } +void f1659(alias T)() { static assert(false); } + +void test1659() +{ + f1659!Bar1659(); +} + +/**********************************/ +// 2025 + +struct S2025 {} +void f2025() {} + +template Foo2025(int i) { enum Foo2025 = 1; } +template Foo2025(TL...) { enum Foo2025 = 2; } +static assert(Foo2025!1 == 1); +static assert(Foo2025!int == 2); +static assert(Foo2025!S2025 == 2); +static assert(Foo2025!f2025 == 2); + +template Bar2025(T) { enum Bar2025 = 1; } +template Bar2025(A...) { enum Bar2025 = 2; } +static assert(Bar2025!1 == 2); +static assert(Bar2025!int == 1); // 2 -> 1 +static assert(Bar2025!S2025 == 1); // 2 -> 1 +static assert(Bar2025!f2025 == 2); + +template Baz2025(T) { enum Baz2025 = 1; } +template Baz2025(alias A) { enum Baz2025 = 2; } +static assert(Baz2025!1 == 2); +static assert(Baz2025!int == 1); +static assert(Baz2025!S2025 == 1); // 2 -> 1 +static assert(Baz2025!f2025 == 2); + /**********************************/ // 3608 @@ -1610,6 +1651,21 @@ void test14() static assert( Params[2] == 1); } +/**********************************/ +// test for evaluateConstraint assertion + +bool canSearchInCodeUnits15(C)(dchar c) +if (is(C == char)) +{ + return true; +} + +void test15() +{ + int needle = 0; + auto b = canSearchInCodeUnits15!char(needle); +} + /**********************************/ // 8129 @@ -2507,6 +2563,16 @@ void test9977() assert(v == 20); } +/******************************************/ + +enum T8848a(int[] a) = a; +enum T8848b(int[int] b) = b; +enum T8848c(void* c) = c; + +static assert(T8848a!([1,2,3]) == [1,2,3]); +static assert(T8848b!([1:2,3:4]) == [1:2,3:4]); +static assert(T8848c!(null) == null); + /******************************************/ // 9990 @@ -2695,42 +2761,6 @@ template b10134() pragma(msg, getResultType10134!(a10134!())); -/******************************************/ -// 10249 - -template Seq10249(T...) { alias Seq10249 = T; } - -mixin template Func10249(T) -{ - void func10249(T) {} -} -mixin Func10249!long; -mixin Func10249!string; - -void f10249(long) {} - -class C10249 -{ - mixin Func10249!long; - mixin Func10249!string; - static assert(Seq10249!(.func10249)[0].mangleof == "7breaker9func10249"); // <- 9func10249 - static assert(Seq10249!( func10249)[0].mangleof == "7breaker6C102499func10249"); // <- 9func10249 - -static: // necessary to make overloaded symbols accessible via __traits(getOverloads, C10249) - void foo(long) {} - void foo(string) {} - static assert(Seq10249!(foo)[0].mangleof == "7breaker6C102493foo"); // <- _D7breaker6C102493fooFlZv - static assert(Seq10249!(__traits(getOverloads, C10249, "foo"))[0].mangleof == "_D7breaker6C102493fooFlZv"); // <- - static assert(Seq10249!(__traits(getOverloads, C10249, "foo"))[1].mangleof == "_D7breaker6C102493fooFAyaZv"); // <- - - void g(string) {} - alias bar = .f10249; - alias bar = g; - static assert(Seq10249!(bar)[0].mangleof == "7breaker6C102496f10249"); // <- _D7breaker1fFlZv (todo!) - static assert(Seq10249!(__traits(getOverloads, C10249, "bar"))[0].mangleof == "_D7breaker6f10249FlZv"); // <- - static assert(Seq10249!(__traits(getOverloads, C10249, "bar"))[1].mangleof == "_D7breaker6C102491gFAyaZv"); // <- -} - /******************************************/ // 10313 @@ -2940,6 +2970,8 @@ void test11271() /******************************************/ // 11533 +version (none) +{ struct S11533 { void put(alias fun)() { fun!int(); } @@ -2972,6 +3004,20 @@ void test11533c() assert(foo.call() == var); } +void test11533() +{ + test11533a(); + test11533b(); + test11533c(); +} +} +else +{ +void test11533() +{ +} +} + /******************************************/ // 11553 @@ -3069,6 +3115,1100 @@ void test11843() static assert(!is(typeof(bar3) == typeof(bar4))); } +/******************************************/ +// 11872 + +class Foo11872 +{ + auto test(int v)() {} + auto test(int v)(string) {} + + template Bar(T) + { + void test(T) {} + } +} + +void test11872() +{ + auto foo = new Foo11872(); + + with (foo) + { + // ScopeExp(ti) -> DotTemplateInstanceExp(wthis, ti) + foo.test!2(); // works + test!2(); // works <- fails + test!2; // works <- fails + + // ScopeExp(ti) -> DotTemplateInstanceExp(wthis, ti) -> DotExp(wthis, ScopeExp) + foo.Bar!int.test(1); // works + Bar!int.test(1); // works <- fails + } +} + +/******************************************/ +// 12042 + +struct S12042 +{ + int[] t; + + void m()() + { + t = null; // CTFE error -> OK + } +} + +int test12042() +{ + S12042 s; + + with (s) + m!()(); + + return 1; +} + +static assert(test12042()); + +/******************************************/ +// 12077 + +struct S12077(A) {} + +alias T12077(alias T : Base!Args, alias Base, Args...) = Base; +static assert(__traits(isSame, T12077!(S12077!int), S12077)); + +alias U12077(alias T : Base!Args, alias Base, Args...) = Base; +alias U12077( T : Base!Args, alias Base, Args...) = Base; +static assert(__traits(isSame, U12077!(S12077!int), S12077)); + +/******************************************/ +// 12262 + +template Inst12262(T) { int x; } + +enum fqnSym12262(alias a) = 1; +enum fqnSym12262(alias a : B!A, alias B, A...) = 2; + +static assert(fqnSym12262!(Inst12262!(Object)) == 2); +static assert(fqnSym12262!(Inst12262!(Object).x) == 1); + +/******************************************/ +// 12264 + +struct S12264(A) {} + +template AX12264(alias A1) { enum AX12264 = 1; } +template AX12264(alias A2 : B!A, alias B, A...) { enum AX12264 = 2; } +template AY12264(alias A1) { enum AY12264 = 1; } +template AY12264(alias A2 : B!int, alias B) { enum AY12264 = 2; } +template AZ12264(alias A1) { enum AZ12264 = 1; } +template AZ12264(alias A2 : S12264!T, T) { enum AZ12264 = 2; } +static assert(AX12264!(S12264!int) == 2); +static assert(AY12264!(S12264!int) == 2); +static assert(AZ12264!(S12264!int) == 2); + +template TX12264(T1) { enum TX12264 = 1; } +template TX12264(T2 : B!A, alias B, A...) { enum TX12264 = 2; } +template TY12264(T1) { enum TY12264 = 1; } +template TY12264(T2 : B!int, alias B) { enum TY12264 = 2; } +template TZ12264(T1) { enum TZ12264 = 1; } +template TZ12264(T2 : S12264!T, T) { enum TZ12264 = 2; } +static assert(TX12264!(S12264!int) == 2); +static assert(TY12264!(S12264!int) == 2); +static assert(TZ12264!(S12264!int) == 2); + +/******************************************/ +// 12122 + +enum N12122 = 1; + +void foo12122(T)(T[N12122]) if(is(T == int)) {} + +void test12122() +{ + int[N12122] data; + foo12122(data); +} + +/******************************************/ +// 12186 + +template map_front12186(fun...) +{ + auto map_front12186(Range)(Range r) + { + return fun[0](r[0]); + } +} + +void test12186() +{ + immutable int[][] mat; + + mat.map_front12186!((in r) => 0); // OK + mat.map_front12186!((const r) => 0); // OK + mat.map_front12186!((immutable int[] r) => 0); // OK + mat.map_front12186!((immutable r) => 0); // OK <- Error +} + +/******************************************/ +// 12207 + +void test12207() +{ + static struct S + { + static void f(T)(T) {} + } + + immutable S s; + + s.f(1); +} + +/******************************************/ +// 12263 + +template A12263(alias a) { int x; } +template B12263(alias a) { int x; } + +template fqnSym12263(alias T : B12263!A, alias B12263, A...) +{ + enum fqnSym12263 = true; +} + +static assert(fqnSym12263!(A12263!(Object))); +static assert(fqnSym12263!(B12263!(Object))); + +/******************************************/ +// 12290 + +void test12290() +{ + short[] arrS; + float[] arrF; + double[] arrD; + real[] arrR; + string cstr; + wstring wstr; + dstring dstr; + short[short] aa; + + auto func1a(E)(E[], E) { return E.init; } + auto func1b(E)(E, E[]) { return E.init; } + + static assert(is(typeof(func1a(arrS, 1)) == short)); + static assert(is(typeof(func1b(1, arrS)) == short)); + static assert(is(typeof(func1a(arrF, 1.0)) == float)); + static assert(is(typeof(func1b(1.0, arrF)) == float)); + static assert(is(typeof(func1a(arrD, 1.0L)) == double)); + static assert(is(typeof(func1b(1.0L, arrD)) == double)); + static assert(is(typeof(func1a(arrR, 1)) == real)); + static assert(is(typeof(func1b(1, arrR)) == real)); + static assert(is(typeof(func1a("str" , 'a')) == immutable char)); + static assert(is(typeof(func1b('a', "str" )) == immutable char)); + static assert(is(typeof(func1a("str"c, 'a')) == immutable char)); + static assert(is(typeof(func1b('a', "str"c)) == immutable char)); + static assert(is(typeof(func1a("str"w, 'a')) == immutable wchar)); + static assert(is(typeof(func1b('a', "str"w)) == immutable wchar)); + static assert(is(typeof(func1a("str"d, 'a')) == immutable dchar)); + static assert(is(typeof(func1b('a', "str"d)) == immutable dchar)); + static assert(is(typeof(func1a([1,2,3], 1L)) == long)); + static assert(is(typeof(func1b(1L, [1,2,3])) == long)); + static assert(is(typeof(func1a([1,2,3], 1.5)) == double)); + static assert(is(typeof(func1b(1.5, [1,2,3])) == double)); + static assert(is(typeof(func1a(["a","b"], "s"c)) == string)); + static assert(is(typeof(func1b("s"c, ["a","b"])) == string)); + static assert(is(typeof(func1a(["a","b"], "s"w)) == wstring)); + static assert(is(typeof(func1b("s"w, ["a","b"])) == wstring)); + static assert(is(typeof(func1a(["a","b"], "s"d)) == dstring)); + static assert(is(typeof(func1b("s"d, ["a","b"])) == dstring)); + + auto func2a(K, V)(V[K], K, V) { return V[K].init; } + auto func2b(K, V)(V, K, V[K]) { return V[K].init; } + + static assert(is(typeof(func2a(aa, 1, 1)) == short[short])); + static assert(is(typeof(func2b(1, 1, aa)) == short[short])); + static assert(is(typeof(func2a([1:10,2:20,3:30], 1L, 10L)) == long[long])); + static assert(is(typeof(func2b(1L, 10L, [1:20,2:20,3:30])) == long[long])); + + auto func3a(T)(T, T) { return T.init; } + auto func3b(T)(T, T) { return T.init; } + + static assert(is(typeof(func3a(arrS, null)) == short[])); + static assert(is(typeof(func3b(null, arrS)) == short[])); + static assert(is(typeof(func3a(arrR, null)) == real[])); + static assert(is(typeof(func3b(null, arrR)) == real[])); + static assert(is(typeof(func3a(cstr, "str")) == string)); + static assert(is(typeof(func3b("str", cstr)) == string)); + static assert(is(typeof(func3a(wstr, "str")) == wstring)); + static assert(is(typeof(func3b("str", wstr)) == wstring)); + static assert(is(typeof(func3a(dstr, "str")) == dstring)); + static assert(is(typeof(func3b("str", dstr)) == dstring)); + static assert(is(typeof(func3a("str1" , "str2"c)) == string)); + static assert(is(typeof(func3b("str1"c, "str2" )) == string)); + static assert(is(typeof(func3a("str1" , "str2"w)) == wstring)); + static assert(is(typeof(func3b("str1"w, "str2" )) == wstring)); + static assert(is(typeof(func3a("str1" , "str2"d)) == dstring)); + static assert(is(typeof(func3b("str1"d, "str2" )) == dstring)); + + inout(V) get(K, V)(inout(V[K]) aa, K key, lazy V defaultValue) { return V.init; } + + short[short] hash12220; + short res12220 = get(hash12220, 1, 1); + + short[short] hash12221; + enum Key12221 : short { a } + get(hash12221, Key12221.a, Key12221.a); + + int[][string] mapping13026; + int[] v = get(mapping13026, "test", []); +} + +/******************************************/ +// 12292 + +void test12292() +{ + void fun(T : string)(T data) {} + + ubyte[3] sa; + static assert(!__traits(compiles, fun(sa))); + static assert(!__traits(compiles, { alias f = fun!(ubyte[3]); })); +} + +/******************************************/ +// 12376 + +static auto encode12376(size_t sz)(dchar ch) if (sz > 1) +{ + undefined; +} + +void test12376() +{ + enum x = __traits(compiles, encode12376!2(x)); +} + +/******************************************/ +// 12651 + +alias TemplateArgsOf12651(alias T : Base!Args, alias Base, Args...) = Args; + +struct S12651(T) { } + +static assert(!__traits(compiles, TemplateArgsOf12651!(S12651!int, S, float))); + +/******************************************/ +// 12719 + +struct A12719 +{ + B12719!int b(); +} + +struct B12719(T) +{ + A12719 a; + void m() + { + auto v = B12719!T.init; + } +} + +// -------- + +enum canDoIt12719(R) = is(typeof(W12719!R)); + +struct W12719(R) +{ + R r; + static if (canDoIt12719!R) {} +} + +W12719!int a12719; + +/******************************************/ +// 12746 + +template foo12746() +{ + void bar() + { + static assert(!__traits(compiles, bar(1))); + } + alias foo12746 = bar; +} + +void foo12746(int) +{ + assert(0); +} + +void test12746() +{ + foo12746(); // instantiate +} + +/******************************************/ +// 9708 + +struct S9708 +{ + void f()(inout(Object)) inout {} +} + +void test9708() +{ + S9708 s; + s.f(new Object); +} + +/******************************************/ +// 12880 + +void f12880(T)(in T value) { static assert(is(T == string)); } +void test12880() { f12880(string.init); } + +/******************************************/ +// 13087 + +struct Vec13087 +{ + int x; + void m() { auto n = component13087!(this, 'x'); } + void c() const { auto n = component13087!(this, 'x'); } + void w() inout { auto n = component13087!(this, 'x'); } + void wc() inout const { auto n = component13087!(this, 'x'); } + void s() shared { auto n = component13087!(this, 'x'); } + void sc() shared const { auto n = component13087!(this, 'x'); } + void sw() shared inout { auto n = component13087!(this, 'x'); } + void swc() shared inout const { auto n = component13087!(this, 'x'); } + void i() immutable { auto n = component13087!(this, 'x'); } +} + +template component13087(alias vec, char c) +{ + alias component13087 = vec.x; +} + +/******************************************/ +// 13127 + +/+void test13127(inout int = 0) +{ + int [] ma1; + const(int)[] ca1; + const(int[]) ca2; + inout( int)[] wma1; + inout( int[]) wma2; + inout(const int)[] wca1; + inout(const int[]) wca2; + immutable(int)[] ia1; + immutable(int[]) ia2; + shared( int)[] sma1; + shared( int[]) sma2; + shared( const int)[] sca1; + shared( const int[]) sca2; + shared(inout int)[] swma1; + shared(inout int[]) swma2; + shared(inout const int)[] swca1; + shared(inout const int[]) swca2; + + /* In all cases, U should be deduced to top-unqualified type. + */ + + /* Parameter is (shared) mutable + */ + U f_m(U)( U) { return null; } + U fsm(U)(shared U) { return null; } + // 9 * 2 - 1 + static assert(is(typeof(f_m( ma1)) == int [])); + static assert(is(typeof(f_m( ca1)) == const(int)[])); + static assert(is(typeof(f_m( ca2)) == const(int)[])); + static assert(is(typeof(f_m( wma1)) == inout( int)[])); + static assert(is(typeof(f_m( wma2)) == inout( int)[])); + static assert(is(typeof(f_m( wca1)) == inout(const int)[])); + static assert(is(typeof(f_m( wca2)) == inout(const int)[])); + static assert(is(typeof(f_m( ia1)) == immutable(int)[])); + static assert(is(typeof(f_m( ia2)) == immutable(int)[])); + static assert(is(typeof(f_m( sma1)) == shared( int)[])); + static assert(is(typeof(f_m( sma2)) == shared( int)[])); // <- shared(int[]) + static assert(is(typeof(f_m( sca1)) == shared( const int)[])); + static assert(is(typeof(f_m( sca2)) == shared( const int)[])); // <- shared(const(int)[]) + static assert(is(typeof(f_m(swma1)) == shared(inout int)[])); + static assert(is(typeof(f_m(swma2)) == shared(inout int)[])); // <- shared(inout(int[]) + static assert(is(typeof(f_m(swca1)) == shared(inout const int)[])); + static assert(is(typeof(f_m(swca2)) == shared(inout const int)[])); // <- shared(inout(const(int))[]) + // 9 * 2 - 1 + static assert(is(typeof(fsm( ma1))) == false); + static assert(is(typeof(fsm( ca1))) == false); + static assert(is(typeof(fsm( ca2))) == false); + static assert(is(typeof(fsm( wma1))) == false); + static assert(is(typeof(fsm( wma2))) == false); + static assert(is(typeof(fsm( wca1))) == false); + static assert(is(typeof(fsm( wca2))) == false); + static assert(is(typeof(fsm( ia1))) == false); + static assert(is(typeof(fsm( ia2))) == false); + static assert(is(typeof(fsm( sma1)) == shared( int)[])); // <- NG + static assert(is(typeof(fsm( sma2)) == shared( int)[])); + static assert(is(typeof(fsm( sca1)) == shared( const int)[])); // <- NG + static assert(is(typeof(fsm( sca2)) == shared( const int)[])); + static assert(is(typeof(fsm(swma1)) == shared(inout int)[])); // <- NG + static assert(is(typeof(fsm(swma2)) == shared(inout int)[])); + static assert(is(typeof(fsm(swca1)) == shared(inout const int)[])); // <- NG + static assert(is(typeof(fsm(swca2)) == shared(inout const int)[])); + + /* Parameter is (shared) const + */ + U f_c(U)( const U) { return null; } + U fsc(U)(shared const U) { return null; } + // 9 * 2 - 1 + static assert(is(typeof(f_c( ma1)) == int [])); + static assert(is(typeof(f_c( ca1)) == const(int)[])); + static assert(is(typeof(f_c( ca2)) == const(int)[])); + static assert(is(typeof(f_c( wma1)) == inout( int)[])); + static assert(is(typeof(f_c( wma2)) == inout( int)[])); + static assert(is(typeof(f_c( wca1)) == inout(const int)[])); + static assert(is(typeof(f_c( wca2)) == inout(const int)[])); + static assert(is(typeof(f_c( ia1)) == immutable(int)[])); + static assert(is(typeof(f_c( ia2)) == immutable(int)[])); + static assert(is(typeof(f_c( sma1)) == shared( int)[])); + static assert(is(typeof(f_c( sma2)) == shared( int)[])); // <- shared(int[]) + static assert(is(typeof(f_c( sca1)) == shared( const int)[])); + static assert(is(typeof(f_c( sca2)) == shared( const int)[])); // <- shared(const(int)[]) + static assert(is(typeof(f_c(swma1)) == shared(inout int)[])); + static assert(is(typeof(f_c(swma2)) == shared(inout int)[])); // shared(inout(int)[]) + static assert(is(typeof(f_c(swca1)) == shared(inout const int)[])); + static assert(is(typeof(f_c(swca2)) == shared(inout const int)[])); // shared(inout(const(int))[]) + // 9 * 2 - 1 + static assert(is(typeof(fsc( ma1))) == false); + static assert(is(typeof(fsc( ca1))) == false); + static assert(is(typeof(fsc( ca2))) == false); + static assert(is(typeof(fsc( wma1))) == false); + static assert(is(typeof(fsc( wma2))) == false); + static assert(is(typeof(fsc( wca1))) == false); + static assert(is(typeof(fsc( wca2))) == false); + static assert(is(typeof(fsc( ia1)) == immutable(int)[])); // <- NG + static assert(is(typeof(fsc( ia2)) == immutable(int)[])); // <- NG + static assert(is(typeof(fsc( sma1)) == shared( int)[])); // <- NG + static assert(is(typeof(fsc( sma2)) == shared( int)[])); + static assert(is(typeof(fsc( sca1)) == shared( const int)[])); // <- NG + static assert(is(typeof(fsc( sca2)) == shared( const int)[])); + static assert(is(typeof(fsc(swma1)) == shared(inout int)[])); // <- NG + static assert(is(typeof(fsc(swma2)) == shared(inout int)[])); + static assert(is(typeof(fsc(swca1)) == shared(inout const int)[])); // <- NG + static assert(is(typeof(fsc(swca2)) == shared(inout const int)[])); + + /* Parameter is immutable + */ + U fi(U)(immutable U) { return null; } + // 9 * 2 - 1 + static assert(is(typeof(fi( ma1))) == false); + static assert(is(typeof(fi( ca1))) == false); + static assert(is(typeof(fi( ca2))) == false); + static assert(is(typeof(fi( wma1))) == false); + static assert(is(typeof(fi( wma2))) == false); + static assert(is(typeof(fi( wca1))) == false); + static assert(is(typeof(fi( wca2))) == false); + static assert(is(typeof(fi( ia1)) == immutable(int)[])); // <- NG + static assert(is(typeof(fi( ia2)) == immutable(int)[])); // <- NG + static assert(is(typeof(fi( sma1))) == false); + static assert(is(typeof(fi( sma2))) == false); + static assert(is(typeof(fi( sca1))) == false); + static assert(is(typeof(fi( sca2))) == false); + static assert(is(typeof(fi(swma1))) == false); + static assert(is(typeof(fi(swma2))) == false); + static assert(is(typeof(fi(swca1))) == false); + static assert(is(typeof(fi(swca2))) == false); + + /* Parameter is (shared) inout + */ + U f_w(U)( inout U) { return null; } + U fsw(U)(shared inout U) { return null; } + // 9 * 2 - 1 + static assert(is(typeof(f_w( ma1)) == int [])); + static assert(is(typeof(f_w( ca1)) == int [])); // <- const(int)[] + static assert(is(typeof(f_w( ca2)) == int [])); // <- const(int)[] + static assert(is(typeof(f_w( wma1)) == int [])); // <- inout(int)[] + static assert(is(typeof(f_w( wma2)) == int [])); // <- inout(int)[] + static assert(is(typeof(f_w( wca1)) == const(int)[])); // <- inout(const(int))[] + static assert(is(typeof(f_w( wca2)) == const(int)[])); // <- inout(const(int))[] + static assert(is(typeof(f_w( ia1)) == int [])); // <- immutable(int)[] + static assert(is(typeof(f_w( ia2)) == int [])); // <- immutable(int)[] + static assert(is(typeof(f_w( sma1)) == shared( int)[])); + static assert(is(typeof(f_w( sma2)) == shared( int)[])); // <- shared(int[]) + static assert(is(typeof(f_w( sca1)) == shared( int)[])); // <- shared(const(int))[] + static assert(is(typeof(f_w( sca2)) == shared( int)[])); // <- shared(const(int)[]) + static assert(is(typeof(f_w(swma1)) == shared( int)[])); // <- shared(inout(int))[] + static assert(is(typeof(f_w(swma2)) == shared( int)[])); // <- shared(inout(int)[]) + static assert(is(typeof(f_w(swca1)) == shared(const int)[])); // <- shared(inout(const(int)))[] + static assert(is(typeof(f_w(swca2)) == shared(const int)[])); // <- shared(inout(const(int))[]) + // 9 * 2 - 1 + static assert(is(typeof(fsw( ma1))) == false); + static assert(is(typeof(fsw( ca1))) == false); + static assert(is(typeof(fsw( ca2))) == false); + static assert(is(typeof(fsw( wma1))) == false); + static assert(is(typeof(fsw( wma2))) == false); + static assert(is(typeof(fsw( wca1))) == false); + static assert(is(typeof(fsw( wca2))) == false); + static assert(is(typeof(fsw( ia1)) == int [])); // <- NG + static assert(is(typeof(fsw( ia2)) == int [])); // <- NG + static assert(is(typeof(fsw( sma1)) == int [])); // <- NG + static assert(is(typeof(fsw( sma2)) == int [])); + static assert(is(typeof(fsw( sca1)) == int [])); // <- NG + static assert(is(typeof(fsw( sca2)) == int [])); // const(int)[] + static assert(is(typeof(fsw(swma1)) == int [])); // <- NG + static assert(is(typeof(fsw(swma2)) == int [])); // inout(int)[] + static assert(is(typeof(fsw(swca1)) == const(int)[])); // <- NG + static assert(is(typeof(fsw(swca2)) == const(int)[])); // <- inout(const(int))[] + + /* Parameter is (shared) inout const + */ + U f_wc(U)( inout const U) { return null; } + U fswc(U)(shared inout const U) { return null; } + // 9 * 2 - 1 + static assert(is(typeof(f_wc( ma1)) == int [])); + static assert(is(typeof(f_wc( ca1)) == int [])); // <- const(int)[] + static assert(is(typeof(f_wc( ca2)) == int [])); // <- const(int)[] + static assert(is(typeof(f_wc( wma1)) == int [])); // <- inout(int)[] + static assert(is(typeof(f_wc( wma2)) == int [])); // <- inout(int)[] + static assert(is(typeof(f_wc( wca1)) == int [])); // <- inout(const(int))[] + static assert(is(typeof(f_wc( wca2)) == int [])); // <- inout(const(int))[] + static assert(is(typeof(f_wc( ia1)) == int [])); // <- immutable(int)[] + static assert(is(typeof(f_wc( ia2)) == int [])); // <- immutable(int)[] + static assert(is(typeof(f_wc( sma1)) == shared(int)[])); + static assert(is(typeof(f_wc( sma2)) == shared(int)[])); // <- shared(int[]) + static assert(is(typeof(f_wc( sca1)) == shared(int)[])); // <- shared(const(int))[] + static assert(is(typeof(f_wc( sca2)) == shared(int)[])); // <- shared(const(int)[]) + static assert(is(typeof(f_wc(swma1)) == shared(int)[])); // <- shared(inout(int))[] + static assert(is(typeof(f_wc(swma2)) == shared(int)[])); // <- shared(inout(int)[]) + static assert(is(typeof(f_wc(swca1)) == shared(int)[])); // <- shared(inout(const(int)))[] + static assert(is(typeof(f_wc(swca2)) == shared(int)[])); // <- shared(inout(const(int))[]) + // 9 * 2 - 1 + static assert(is(typeof(fswc( ma1))) == false); + static assert(is(typeof(fswc( ca1))) == false); + static assert(is(typeof(fswc( ca2))) == false); + static assert(is(typeof(fswc( wma1))) == false); + static assert(is(typeof(fswc( wma2))) == false); + static assert(is(typeof(fswc( wca1))) == false); + static assert(is(typeof(fswc( wca2))) == false); + static assert(is(typeof(fswc( ia1)) == int [])); // <- NG + static assert(is(typeof(fswc( ia2)) == int [])); // <- NG + static assert(is(typeof(fswc( sma1)) == int [])); // <- NG + static assert(is(typeof(fswc( sma2)) == int [])); + static assert(is(typeof(fswc( sca1)) == int [])); // <- NG + static assert(is(typeof(fswc( sca2)) == int [])); // <- const(int)[] + static assert(is(typeof(fswc(swma1)) == int [])); // <- NG + static assert(is(typeof(fswc(swma2)) == int [])); // <- inout(int)[] + static assert(is(typeof(fswc(swca1)) == int [])); // <- NG + static assert(is(typeof(fswc(swca2)) == int [])); // <- inout(const(int))[] +}+/ + +void test13127a() +{ + void foo(T)(in T[] src, T[] dst) { static assert(is(T == int[])); } + + int[][] a; + foo(a, a); +} + +/******************************************/ +// 13159 + +template maxSize13159(T...) +{ + static if (T.length == 1) + { + enum size_t maxSize13159 = T[0].sizeof; + } + else + { + enum size_t maxSize13159 = + T[0].sizeof >= maxSize13159!(T[1 .. $]) + ? T[0].sizeof + : maxSize13159!(T[1 .. $]); + } +} + +struct Node13159 +{ + struct Pair + { + Node13159 value; + } + + //alias Algebraic!(Node[], int) Value; + enum n = maxSize13159!(Node13159[], int); +} + +/******************************************/ +// 13180 + +void test13180() +{ + inout(V) get1a(K, V)(inout(V[K]) aa, lazy inout(V) defaultValue) + { + static assert(is(V == string)); + static assert(is(K == string)); + return defaultValue; + } + inout(V) get1b(K, V)(lazy inout(V) defaultValue, inout(V[K]) aa) + { + static assert(is(V == string)); + static assert(is(K == string)); + return defaultValue; + } + + inout(V) get2a(K, V)(inout(V)[K] aa, lazy inout(V) defaultValue) + { + static assert(is(V == string)); + static assert(is(K == string)); + return defaultValue; + } + inout(V) get2b(K, V)(lazy inout(V) defaultValue, inout(V)[K] aa) + { + static assert(is(V == string)); + static assert(is(K == string)); + return defaultValue; + } + string def; + string[string] aa; + string s1a = get1a(aa, def); + string s1b = get1b(def, aa); + string s2a = get2a(aa, def); + string s2b = get2b(def, aa); +} + +/******************************************/ +// 13204 + +struct A13204(uint v) +{ + alias whatever = A13204y; + static assert(is(whatever == A13204)); +} +alias A13204x = A13204!1; +alias A13204y = A13204x; + +struct B13204(uint v) +{ + alias whatever = B13204z; + static assert(is(whatever == B13204)); +} +alias B13204x = B13204!1; +alias B13204y = B13204x; +alias B13204z = B13204y; + +void test13204() +{ + static assert(is(A13204x == A13204!1)); + static assert(is(A13204x == A13204!1.whatever)); + static assert(is(A13204x == A13204y)); + + static assert(is(B13204x == B13204!1)); + static assert(is(B13204x == B13204!1.whatever)); + static assert(is(B13204x == B13204y)); + static assert(is(B13204x == B13204z)); +} + +/******************************************/ +// 13218 + +template isCallable13218(T...) + if (T.length == 1) +{ + static assert(0); +} + +template ParameterTypeTuple13218(func...) + if (func.length == 1 && isCallable13218!func) +{ + static assert(0); +} + +struct R13218 +{ + private static string mangleFuncPtr(ArgTypes...)() + { + string result = "fnp_"; + foreach (T; ArgTypes) + result ~= T.mangleof; + return result; + } + void function(int) fnp_i; + double delegate(double) fnp_d; + + void opAssign(FnT)(FnT func) + { + mixin(mangleFuncPtr!( ParameterTypeTuple13218!FnT) ~ " = func;"); // parsed as TypeInstance + //mixin(mangleFuncPtr!(.ParameterTypeTuple13218!FnT) ~ " = func;"); // parsed as DotTemplateInstanceExp -> works + } +} + +/******************************************/ +// 13219 + +struct Map13219(V) {} + +void test13219a(alias F, VA, VB)(Map13219!VA a, Map13219!VB b) +if (is(VA : typeof(F(VA.init, VB.init)))) +{} + +void test13219b(alias F)() +{ + test13219a!((a, b) => b)(Map13219!int.init, Map13219!int.init); +} + +void test13219() +{ + int x; + test13219b!x(); +} + +/******************************************/ +// 13223 + +void test13223() +{ + T[] f1(T)(T[] a1, T[] a2) + { + static assert(is(T == int)); + return a1 ~ a2; + } + T[] f2(T)(T[] a1, T[] a2) + { + static assert(is(T == int)); + return a1 ~ a2; + } + int[] a = [1, 2]; + static assert(is(typeof(f1(a, [])) == int[])); + static assert(is(typeof(f2([], a)) == int[])); + static assert(is(typeof(f1(a, null)) == int[])); + static assert(is(typeof(f2(null, a)) == int[])); + + T[] f3(T)(T[] a) { return a; } + static assert(is(typeof(f3([])) == void[])); + static assert(is(typeof(f3(null)) == void[])); + + T f4(T)(T a) { return a; } + static assert(is(typeof(f4([])) == void[])); + static assert(is(typeof(f4(null)) == typeof(null))); + + T[][] f5(T)(T[][] a) { return a; } + static assert(is(typeof(f5([])) == void[][])); + static assert(is(typeof(f5(null)) == void[][])); + + void translate(C = immutable char)(const(C)[] toRemove) + { + static assert(is(C == char)); + } + translate(null); +} + +void test13223a() +{ + T f(T)(T, T) { return T.init; } + + immutable i = 0; + const c = 0; + auto m = 0; + shared s = 0; + + static assert(is(typeof(f(i, i)) == immutable int)); + static assert(is(typeof(f(i, c)) == const int)); + static assert(is(typeof(f(c, i)) == const int)); + static assert(is(typeof(f(i, m)) == int)); + static assert(is(typeof(f(m, i)) == int)); + static assert(is(typeof(f(c, m)) == int)); + static assert(is(typeof(f(m, c)) == int)); + static assert(is(typeof(f(m, m)) == int)); + static assert(is(typeof(f(i, s)) == shared int)); + static assert(is(typeof(f(s, i)) == shared int)); + static assert(is(typeof(f(c, s)) == shared int)); + static assert(is(typeof(f(s, c)) == shared int)); + static assert(is(typeof(f(s, s)) == shared int)); + static assert(is(typeof(f(s, m)) == int)); + static assert(is(typeof(f(m, s)) == int)); +} + +/******************************************/ +// 13235 + +struct Tuple13235(T...) +{ + T expand; + alias expand field; + + this(T values) + { + field = values; + } +} +struct Foo13235 +{ + Tuple13235!(int, Foo13235)* foo; +} + +template Inst13235(T...) +{ + struct Tuple + { + T expand; + alias expand field; + + this(T values) + { + field = values; + } + } + alias Inst13235 = Tuple*; +} +struct Bar13235 +{ + Inst13235!(int, Bar13235) bar; +} + +void test13235() +{ + alias Tup1 = Tuple13235!(int, Foo13235); + assert(Tup1(1, Foo13235()).expand[0] == 1); + + alias Tup2 = typeof(*Inst13235!(int, Bar13235).init); + assert(Tup2(1, Bar13235()).expand[0] == 1); +} + +/******************************************/ +// 13252 + +alias TypeTuple13252(T...) = T; + +static assert(is(typeof(TypeTuple13252!(cast(int )1)[0]) == int )); +static assert(is(typeof(TypeTuple13252!(cast(long)1)[0]) == long)); + +static assert(is(typeof(TypeTuple13252!(cast(float )3.14)[0]) == float )); +static assert(is(typeof(TypeTuple13252!(cast(double)3.14)[0]) == double)); + +static assert(is(typeof(TypeTuple13252!(cast(cfloat )(1 + 2i))[0]) == cfloat )); +static assert(is(typeof(TypeTuple13252!(cast(cdouble)(1 + 2i))[0]) == cdouble)); + +static assert(is(typeof(TypeTuple13252!(cast(string )null)[0]) == string )); +static assert(is(typeof(TypeTuple13252!(cast(string[])null)[0]) == string[])); // OK <- NG + +static assert(is(typeof(TypeTuple13252!(cast(wstring)"abc")[0]) == wstring)); +static assert(is(typeof(TypeTuple13252!(cast(dstring)"abc")[0]) == dstring)); + +static assert(is(typeof(TypeTuple13252!(cast(int[] )[])[0]) == int[] )); +static assert(is(typeof(TypeTuple13252!(cast(long[])[])[0]) == long[])); // OK <- NG + +struct S13252 { } +static assert(is(typeof(TypeTuple13252!(const S13252())[0]) == const(S13252))); +static assert(is(typeof(TypeTuple13252!(immutable S13252())[0]) == immutable(S13252))); // OK <- NG + +/******************************************/ +// 13294 + +void test13294() +{ + void f(T)(const ref T src, ref T dst) + { + pragma(msg, "T = ", T); + static assert(!is(T == const)); + } + { + const byte src; + byte dst; + f(src, dst); + } + { + const char src; + char dst; + f(src, dst); + } + + // 13351 + T add(T)(in T x, in T y) + { + T z; + z = x + y; + return z; + } + const double a = 1.0; + const double b = 2.0; + double c; + c = add(a,b); +} + +/******************************************/ +// 13299 + +struct Foo13299 +{ + Foo13299 opDispatch(string name)(int a, int[] b...) + if (name == "bar") + { + return Foo13299(); + } + + Foo13299 opDispatch(string name)() + if (name != "bar") + { + return Foo13299(); + } +} + +void test13299() +{ + Foo13299() + .bar(0) + .bar(1) + .bar(2); + + Foo13299() + .opDispatch!"bar"(0) + .opDispatch!"bar"(1) + .opDispatch!"bar"(2); +} + +/******************************************/ +// 13333 + +template AliasThisTypeOf13333(T) +{ + static assert(0, T.stringof); // T.stringof is important +} + +template StaticArrayTypeOf13333(T) +{ + static if (is(AliasThisTypeOf13333!T AT)) + alias X = StaticArrayTypeOf13333!AT; + else + alias X = T; + + static if (is(X : E[n], E, size_t n)) + alias StaticArrayTypeOf13333 = X; + else + static assert(0, T.stringof~" is not a static array type"); +} + +enum bool isStaticArray13333(T) = is(StaticArrayTypeOf13333!T); + +struct VaraiantN13333(T) +{ + static if (isStaticArray13333!T) + ~this() { static assert(0); } +} + +struct DummyScope13333 +{ + alias A = VaraiantN13333!C; + + static class C + { + A entity; + } +} + +void test13333() +{ + struct DummyScope + { + alias A = VaraiantN13333!C; + + static class C + { + A entity; + } + } +} + +/******************************************/ +// 13374 + +int f13374(alias a)() { return 1; } +int f13374(string s)() { return 2; } + +void x13374(int i) {} + +void test13374() +{ + assert(f13374!x13374() == 1); +} + +/******************************************/ +// 13378 + +struct Vec13378(size_t n, T, string as) +{ + T[n] data; +} + +void doSome13378(size_t n, T, string as)(Vec13378!(n,T,as) v) {} + +void test13378() +{ + auto v = Vec13378!(3, float, "xyz")([1,2,3]); + doSome13378(v); +} + +/******************************************/ +// 13379 + +void test13379() +{ + match13379(""); +} + +auto match13379(RegEx )(RegEx re) +if (is(RegEx == Regex13379!char)) // #1 Regex!char (speculative && tinst == NULL) +{} +auto match13379(String)(String re) +{} + +struct Regex13379(Char) +{ + ShiftOr13379!Char kickstart; // #2 ShiftOr!char (speculative && tinst == Regex!char) +} +struct ShiftOr13379(Char) +{ + this(ref Regex13379!Char re) // #3 Regex!Char (speculative && tinst == ShiftOr!char) + { + uint n_length; + uint idx; + n_length = min13379(idx, n_length); + } +} + +template MinType13379(T...) +{ + alias MinType13379 = T[0]; +} +MinType13379!T min13379(T...)(T args) // #4 MinType!uint (speculative && thist == ShiftOr!char) +{ + alias a = args[0]; + alias b = args[$-1]; + return cast(typeof(return)) (a < b ? a : b); +} + +/******************************************/ +// 13417 + +struct V13417(size_t N, E, alias string AS) +{ +} + +auto f13417(E)(in V13417!(4, E, "ijka")) +{ + return V13417!(3, E, "xyz")(); +} + +void test13417() +{ + f13417(V13417!(4, float, "ijka")()); +} + /******************************************/ int main() @@ -3161,11 +4301,19 @@ int main() test10811(); test10969(); test11271(); - test11533a(); - test11533b(); - test11533c(); + test11533(); test11818(); test11843(); + test11872(); + test12122(); + test12207(); + test12376(); + test13235(); + test13294(); + test13299(); + test13374(); + test13378(); + test13379(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/test12.d b/gcc/testsuite/gdc.test/runnable/test12.d index 37a349267..a20973971 100644 --- a/gcc/testsuite/gdc.test/runnable/test12.d +++ b/gcc/testsuite/gdc.test/runnable/test12.d @@ -781,7 +781,7 @@ void test36() printf("%d\n", a.d); version(D_LP64) - assert(a.classinfo.init.length == 40); + assert(a.classinfo.init.length == 36); else assert(a.classinfo.init.length == 28); assert(a.s == 1); @@ -914,7 +914,6 @@ struct Property struct Value { int a,b,c,d; - const int opCmp(ref const Value v) { return 0; } } struct PropTable diff --git a/gcc/testsuite/gdc.test/runnable/test12874.d b/gcc/testsuite/gdc.test/runnable/test12874.d new file mode 100644 index 000000000..750c8247e --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test12874.d @@ -0,0 +1,18 @@ +// EXTRA_SOURCES: imports/a12874.d +// PERMUTE_ARGS: -inline -g -O + +import imports.a12874; + +void main() +{ + try + { + int x; + foo!(x)(); + } + catch (Error e) + { + assert(e.file[$-8..$] == "a12874.d"); + assert(e.line == 7); + } +} diff --git a/gcc/testsuite/gdc.test/runnable/test22.d b/gcc/testsuite/gdc.test/runnable/test22.d index 8375518aa..ddd7c504b 100644 --- a/gcc/testsuite/gdc.test/runnable/test22.d +++ b/gcc/testsuite/gdc.test/runnable/test22.d @@ -582,7 +582,7 @@ void test27() printf("%.*s\n", s.length, s.ptr); assert((int*function(int ...)[]).mangleof == "APFiXPi"); assert(typeof(x).mangleof == "i"); - assert(x.mangleof == "_D6test226test27FZv1xi"); + assert(x.mangleof == "_D6test226test27FZ1xi"); } /*************************************/ diff --git a/gcc/testsuite/gdc.test/runnable/test23.d b/gcc/testsuite/gdc.test/runnable/test23.d index 00c987866..b8a287828 100644 --- a/gcc/testsuite/gdc.test/runnable/test23.d +++ b/gcc/testsuite/gdc.test/runnable/test23.d @@ -1487,6 +1487,22 @@ void test71() /*******************************************/ +size_t getLength(int[] arr) { return arr.length; } + +void test13237() +{ + int[] arr = [0]; + immutable size_t len = getLength(arr); + + arr.length--; + + assert(len == 1); // ok + if (len) { auto l = len; } + assert(len == 1); // len cannot be changed, but produces Assertion failure with "-O -inline" +} + +/*******************************************/ + void main() { test1(); @@ -1553,6 +1569,7 @@ void main() test69(); test70(); test71(); + test13237(); printf("Success\n"); } diff --git a/gcc/testsuite/gdc.test/runnable/test28.d b/gcc/testsuite/gdc.test/runnable/test28.d index 023b820a9..f1144c3fe 100644 --- a/gcc/testsuite/gdc.test/runnable/test28.d +++ b/gcc/testsuite/gdc.test/runnable/test28.d @@ -652,21 +652,23 @@ class Confectionary { this(int sugar) { - //if (sugar < 500) - // tastiness = 200; + //if (sugar < 500) + // tastiness = 200; - //for (int i = 0; i < 10; ++i) - // tastiness = 300; + //for (int i = 0; i < 10; ++i) + // tastiness = 300; - //int[] tastinesses_array; + //int[] tastinesses_array; - //foreach (n; tastinesses_array) - // tastiness = n; + //foreach (n; tastinesses_array) + // tastiness = n; - int[int] tastinesses_aa; + //int[int] tastinesses_aa; - foreach (n; tastinesses_aa) - tastiness = n; + //foreach (n; tastinesses_aa) + // tastiness = n; + + tastiness = 1; } const int tastiness; diff --git a/gcc/testsuite/gdc.test/runnable/test4.d b/gcc/testsuite/gdc.test/runnable/test4.d index 0599d20d9..51f5f867a 100644 --- a/gcc/testsuite/gdc.test/runnable/test4.d +++ b/gcc/testsuite/gdc.test/runnable/test4.d @@ -254,6 +254,16 @@ else version(ARM) assert(TRECT6.BottomRight.offsetof == 16); assert(TRECT6.foo2.offsetof == 24); } +else version(ARM) +{ + assert(TRECT6.Left.offsetof == 8); + assert(TRECT6.Top.offsetof == 12); + assert(TRECT6.Right.offsetof == 16); + assert(TRECT6.Bottom.offsetof == 20); + assert(TRECT6.TopLeft.offsetof == 8); + assert(TRECT6.BottomRight.offsetof == 16); + assert(TRECT6.foo2.offsetof == 24); +} else { assert(TRECT6.Left.offsetof == 4); diff --git a/gcc/testsuite/gdc.test/runnable/test42.d b/gcc/testsuite/gdc.test/runnable/test42.d index 2c9486b68..13a28fd21 100644 --- a/gcc/testsuite/gdc.test/runnable/test42.d +++ b/gcc/testsuite/gdc.test/runnable/test42.d @@ -1686,83 +1686,83 @@ void test101() /***************************************************/ -version(X86) +version(GNU) { int x103; -void external(...) +void external(int a, ...) { - printf("external: %d\n", *cast (int *) _argptr); - x103 = *cast (int *) _argptr; + va_list ap; + va_start(ap, a); + auto ext = va_arg!int(ap); + printf("external: %d\n", ext); + x103 = ext; + va_end(ap); } class C103 { void method () { - void internal (...) + void internal (int a, ...) { - printf("internal: %d\n", *cast (int *)_argptr); - x103 = *cast (int *) _argptr; + va_list ap; + va_start(ap, a); + auto internal = va_arg!int(ap); + printf("internal: %d\n", internal); + x103 = internal; + va_end(ap); } - internal (43); + internal (0, 43); assert(x103 == 43); } } void test103() { - external(42); + external(0, 42); assert(x103 == 42); (new C103).method (); } } -else version(X86_64) -{ - pragma(msg, "Not ported to x86-64 compatible varargs, yet."); - void test103() {} -} -else version(GNU) +else version(X86) { int x103; -void external(int a, ...) +void external(...) { - va_list ap; - va_start(ap, a); - auto ext = va_arg!int(ap); - printf("external: %d\n", ext); - x103 = ext; - va_end(ap); + printf("external: %d\n", *cast (int *) _argptr); + x103 = *cast (int *) _argptr; } class C103 { void method () { - void internal (int a, ...) + void internal (...) { - va_list ap; - va_start(ap, a); - auto internal = va_arg!int(ap); - printf("internal: %d\n", internal); - x103 = internal; - va_end(ap); + printf("internal: %d\n", *cast (int *)_argptr); + x103 = *cast (int *) _argptr; } - internal (0, 43); + internal (43); assert(x103 == 43); } } void test103() { - external(0, 42); + external(42); assert(x103 == 42); (new C103).method (); } } +else version(X86_64) +{ + pragma(msg, "Not ported to x86-64 compatible varargs, yet."); + void test103() {} +} else static assert(false, "Unknown platform"); @@ -2141,6 +2141,42 @@ void test129() auto i = begin129(r); } +/***************************************************/ +// 12725 + +struct R12725(R : E[], E) +{ +} + +int begin12725(F)(R12725!(F) range) +{ + return 0; +} + +void test12725() +{ + R12725!(int[], int) r; + auto i = begin12725(r); +} + +/***************************************************/ +// 12728 + +struct Matrix12728(T, uint m, uint n = m, ubyte f = 0) +{ + void foo(uint r)(auto ref in Matrix12728!(T, n, r) b) + { + } +} + +void test12728() +{ + alias Matrix4 = Matrix12728!(float, 4); + + Matrix4 m; + m.foo(m); +} + /***************************************************/ struct S130 @@ -4209,19 +4245,14 @@ int bug3809() { version(GNU) { - version(X86) asm - { - "nop;" : : :; - } - else version(X86_64) asm - { - "nop;" : : :; - } - else version(ARM) asm - { - "nop;" : : :; - } - else static assert(false, "ASM code not implemented for this architecture"); + version(X86) + asm { "nop"; } + else version(X86_64) + asm { "nop"; } + else version(ARM) + asm { "nop"; } + else + static assert(false, "ASM code not implemented for this architecture"); } else { @@ -5916,6 +5947,23 @@ void test7436() assert(b == 16); } +/***************************************************/ +// 12138 + +struct S12138 +{ + int num; + this(int n) { num = n; } + ~this() { num = 0; } +} + +void test12138() +{ +label: + auto s = S12138(10); + assert(s.num == 10); +} + /***************************************************/ int main() @@ -6207,6 +6255,7 @@ int main() test10633(); test10642(); test7436(); + test12138(); writefln("Success"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/test5854.d b/gcc/testsuite/gdc.test/runnable/test5854.d new file mode 100644 index 000000000..c1aac4729 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/test5854.d @@ -0,0 +1,45 @@ +struct S1 +{ + long x1, x2; + + int opCmp(ref const S1 s) const + { + if (x2 - s.x2 != 0) + return x2 < s.x2 ? -1 : 1; + + return x1 < s.x1 ? -1 : x1 == s.x1 ? 0 : 1; + } +} + +struct S2 +{ + long x1, x2; + + int opCmp(in S2 s) const + { + if (x2 - s.x2 != 0) + return x2 < s.x2 ? -1 : 1; + + return x1 < s.x1 ? -1 : x1 == s.x1 ? 0 : 1; + } +} +void test5854() +{ + auto arr1 = [S1(4, 4), S1(1, 3), S1(2, 9), S1(3, 22)].sort; + + foreach (i; 0 .. arr1.length - 1) + { + assert(arr1[i] < arr1[i + 1]); + } + + auto arr2 = [S2(4, 4), S2(1, 3), S2(2, 9), S2(3, 22)].sort; + + foreach (i; 0 .. arr2.length - 1) + { + assert(arr2[i] < arr2[i + 1]); + } +} +void main() +{ + test5854(); +} diff --git a/gcc/testsuite/gdc.test/runnable/test7511.d b/gcc/testsuite/gdc.test/runnable/test7511.d index cdddb60dc..17b8bc8a7 100644 --- a/gcc/testsuite/gdc.test/runnable/test7511.d +++ b/gcc/testsuite/gdc.test/runnable/test7511.d @@ -302,7 +302,7 @@ auto foo10329(T)(T arg) { return arg; } - static assert(is(typeof(&bar) == T delegate() nothrow @safe)); + static assert(is(typeof(&bar) == T delegate() nothrow @nogc @safe)); return bar(); } @@ -313,7 +313,7 @@ auto make10329(T)(T arg) auto front() { return T.init; } } S s; - static assert(is(typeof(&s.front) == T delegate() pure nothrow @safe)); + static assert(is(typeof(&s.front) == T delegate() pure nothrow @nogc @safe)); return s; } @@ -392,6 +392,17 @@ void test11896c() assert(FooBar.foo() !is null); } +/**********************************/ +// 12392 + +void f12392(T)() {} +alias fa12392 = f12392; + +void test12392() pure nothrow @safe +{ + fa12392!int(); +} + /**********************************/ int main() diff --git a/gcc/testsuite/gdc.test/runnable/test9.d b/gcc/testsuite/gdc.test/runnable/test9.d deleted file mode 100644 index c3c46d5aa..000000000 --- a/gcc/testsuite/gdc.test/runnable/test9.d +++ /dev/null @@ -1,360 +0,0 @@ -// REQUIRED_ARGS: -d -// PERMUTE_ARGS: -dw -// The use of typedef in these tests is fundamental. - -public: - -// VECTOR - -alias float[1] vector1; -alias float[2] vector2; -alias float[3] vector3; -alias float[4] vector4; - -// POINT - -typedef vector1 point1; -typedef vector2 point2; -typedef vector3 point3; -typedef vector4 point4; - -// MATRIX - -alias vector1[1] matrix1x1; -alias vector2[2] matrix2x2; -alias vector3[2] matrix2x3; -alias vector2[3] matrix3x2; -alias vector3[3] matrix3x3; -alias vector4[3] matrix3x4; -alias vector3[4] matrix4x3; -alias vector4[4] matrix4x4; - -// QUATERNION - -typedef vector3 normal; -typedef vector4 quaternion; - -// LINE / PLANE - -alias vector2 plane1; // ax = b -alias vector3 plane2; // ax+by = c -alias plane2 line2; -alias vector4 plane3; // ax+by+cz = d -alias plane3 plane; - -// SPHERE - -struct sphere1 -{ - vector1 center; - float radius; -} -alias sphere1 centeredrange; - -struct sphere2 -{ - vector2 center; - float radius; -} -alias sphere2 circle; - -struct sphere3 -{ - vector3 center; - float radius; -} -alias sphere3 sphere; - -struct sphere4 -{ - vector4 center; - float radius; -} - -// RAY - -struct ray1 -{ - point1 from; - vector1 to; -} - -struct ray2 -{ - point2 from; - vector2 to; -} - -struct ray3 -{ - point3 from; - vector3 to; -} - -struct ray4 -{ - point4 from; - vector4 to; -} - -// RECT/BOX - -struct box1 -{ - point1 p1,p2; -} -alias box1 range; - -struct box2 -{ - point2 p1,p2; -} -alias box2 rect; - -struct box3 -{ - point3 p1,p2; -} -alias box3 box; - -struct box4 -{ - point4 p1,p2; -} - - -alias float mvfloat; -typedef mvfloat scalar0; -struct multivector0 -{ - union - { - scalar0 scalar; - mvfloat r; - } -} - -typedef mvfloat scalar1; -typedef mvfloat pseudoscalar1; -struct multivector1 -{ - union - { - struct - { - scalar1 scalar; - union - { - pseudoscalar1 pseudoscalar; - pseudoscalar1 bivector; - } - } - struct - { - mvfloat r; - mvfloat i; - } - mvfloat v[2]; - } -} - -typedef mvfloat scalar2; -//typedef mvfloat[2] vector2; -typedef mvfloat pseudoscalar2; -alias pseudoscalar2 bivector2; -struct multivector2 -{ - union - { - struct - { - scalar2 scalar; - vector2 vector; - union - { - pseudoscalar2 pseudoscalar; - pseudoscalar2 bivector; - } - } - struct - { - mvfloat r; - mvfloat i,j; - mvfloat e; - } - mvfloat v[4]; - } -} - -multivector2 add(multivector2 a,multivector2 b) // a + b -{ - multivector2 c; - c.r = a.r+b.r; - c.i = a.i+b.i; - c.j = a.j+b.j; - c.e = a.e+b.e; - return c; -} - -multivector2 sub(multivector2 a,multivector2 b) // a - b -{ - multivector2 c; - c.r = a.r-b.r; - c.i = a.i-b.i; - c.j = a.j-b.j; - c.e = a.e-b.e; - return c; -} - -multivector2 dual(multivector2 a) // ~a = (a * 1e) -{ - multivector2 c; - c.r = a.r; c.i = -a.i; c.j = a.j; c.e = -a.e; - return c; -} - -multivector2 inner(multivector2 a,multivector2 b) // a . b = 0.5(ab + ba) -{ - multivector2 c; - c.r = a.r*b.r + a.i*b.i + a.j*b.j - a.e*b.e; -//c.r = a.r*b.r + a.i*b.i + a.j*b.j + a.e*b.e; - return c; -} - -multivector2 meet(multivector2 a,multivector2 b) // a v b = ~a . b = meet(a,b) -{ - return inner(dual(a),b); -} - - -multivector2 outer(multivector2 a,multivector2 b) // a ^ b = 0.5(ab - ba) = join(a,b) -{ - multivector2 c; -//c.r = a.r*b.r + a.i*b.i + a.j*b.j - a.e*b.e; - c.i = a.r*b.i + a.i*b.r - a.j*b.e + a.e*b.j; - c.j = a.r*b.j + a.i*b.e + a.j*b.r - a.e*b.i; - c.e = a.r*b.e + a.i*b.j - a.j*b.i + a.e*b.r; - return c; -} - -multivector2 product(multivector2 a,multivector2 b) // ab = a * b + a ^ b -{ - multivector2 c; - c.r = a.r*b.r + a.i*b.i + a.j*b.j - a.e*b.e; - c.i = a.r*b.i + a.i*b.r - a.j*b.e + a.e*b.j; - c.j = a.r*b.j + a.i*b.e + a.j*b.r - a.e*b.i; - c.e = a.r*b.e + a.i*b.j - a.j*b.i + a.e*b.r; - return c; -} - -typedef mvfloat scalar3; -//typedef mvfloat[3] vector3; -typedef mvfloat[3] bivector3; -typedef mvfloat pseudoscalar3; -alias pseudoscalar3 trivector3; -struct multivector3 -{ - union - { - struct - { - scalar3 scalar; - vector3 vector; - bivector3 bivector; - union - { - pseudoscalar3 pseudoscalar; - pseudoscalar3 trivector; - } - } - struct - { - mvfloat r; - mvfloat i,j,k; - mvfloat K,J,I; - mvfloat e; - } - mvfloat v[8]; - } -} - -multivector3 add(multivector3 a,multivector3 b) // a + b -{ - multivector3 c; - c.r = a.r+b.r; - c.i = a.i+b.i; - c.j = a.j+b.j; - c.k = a.k+b.k; - c.K = a.K+b.K; - c.J = a.J+b.J; - c.I = a.I+b.I; - c.e = a.e+b.e; - return c; -} - -multivector3 sub(multivector3 a,multivector3 b) // a - b -{ - multivector3 c; - c.r = a.r-b.r; - c.i = a.i-b.i; - c.j = a.j-b.j; - c.k = a.k-b.k; - c.K = a.K-b.K; - c.J = a.J-b.J; - c.I = a.I-b.I; - c.e = a.e-b.e; - return c; -} - -multivector3 dual(multivector3 a) // ~a = (a * 1e) -{ - multivector3 c; - c.r = -a.r; c.i = -a.i; c.j = a.j; c.k = a.k; c.K = -a.K; c.J = -a.J; c.I = a.I; c.e = a.e; - return c; -} - -multivector3 inner(multivector3 a,multivector3 b) // a . b = 0.5(ab + ba) -{ - multivector3 c; -//c.r = a.r*b.r + a.i*b.i + a.j*b.j + a.k*b.k - a.K*b.K - a.J*b.J - a.I*b.I - a.e*b.e; - c.r = a.r*b.r + a.i*b.i + a.j*b.j + a.k*b.k + a.K*b.K + a.J*b.J + a.I*b.I + a.e*b.e; - return c; -} - -multivector3 meet(multivector3 a,multivector3 b) // a v b = ~a . b = meet(a,b) -{ - return inner(dual(a),b); -} - -multivector3 outer(multivector3 a,multivector3 b) // a ^ b = 0.5(ab - ba) -{ - multivector3 c; -//c.r = a.r*b.r + a.i*b.i + a.j*b.j - a.k*b.k + a.K*b.K - a.J*b.J - a.I*b.I - a.e*b.e; - c.i = a.r*b.i + a.i*b.r - a.j*b.K + a.K*b.j - a.k*b.J + a.J*b.k - a.I*b.e - a.e*b.I; - c.j = a.r*b.j + a.i*b.K + a.j*b.r - a.K*b.i - a.k*b.I + a.J*b.e + a.I*b.k + a.e*b.J; - c.K = a.r*b.K + a.i*b.j - a.j*b.i + a.K*b.r + a.k*b.e - a.J*b.I + a.I*b.J + a.e*b.k; - c.k = a.r*b.k + a.i*b.J + a.j*b.I - a.K*b.e + a.k*b.r - a.J*b.i - a.I*b.j - a.e*b.K; - c.J = a.r*b.J + a.i*b.k - a.j*b.e + a.K*b.I - a.k*b.i + a.J*b.r - a.I*b.K - a.e*b.j; - c.I = a.r*b.I + a.i*b.e + a.j*b.k - a.K*b.J - a.k*b.j + a.J*b.K + a.I*b.r + a.e*b.i; - c.e = a.r*b.e + a.i*b.I - a.j*b.J + a.K*b.k + a.k*b.K - a.J*b.j + a.I*b.i + a.e*b.r; - return c; -} - -multivector3 product(multivector3 a,multivector3 b) // ab = a * b + a ^ b -{ - multivector3 c; - c.r = a.r*b.r + a.i*b.i + a.j*b.j - a.k*b.k + a.K*b.K - a.J*b.J - a.I*b.I - a.e*b.e; - c.i = a.r*b.i + a.i*b.r - a.j*b.K + a.K*b.j - a.k*b.J + a.J*b.k - a.I*b.e - a.e*b.I; - c.j = a.r*b.j + a.i*b.K + a.j*b.r - a.K*b.i - a.k*b.I + a.J*b.e + a.I*b.k + a.e*b.J; - c.K = a.r*b.K + a.i*b.j - a.j*b.i + a.K*b.r + a.k*b.e - a.J*b.I + a.I*b.J + a.e*b.k; - c.k = a.r*b.k + a.i*b.J + a.j*b.I - a.K*b.e + a.k*b.r - a.J*b.i - a.I*b.j - a.e*b.K; - c.J = a.r*b.J + a.i*b.k - a.j*b.e + a.K*b.I - a.k*b.i + a.J*b.r - a.I*b.K - a.e*b.j; - c.I = a.r*b.I + a.i*b.e + a.j*b.k - a.K*b.J - a.k*b.j + a.J*b.K + a.I*b.r + a.e*b.i; - c.e = a.r*b.e + a.i*b.I - a.j*b.J + a.K*b.k + a.k*b.K - a.J*b.j + a.I*b.i + a.e*b.r; - return c; -} - -int main() -{ - return 0; -} diff --git a/gcc/testsuite/gdc.test/runnable/testaa3.d b/gcc/testsuite/gdc.test/runnable/testaa3.d new file mode 100644 index 000000000..0ef053f4a --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/testaa3.d @@ -0,0 +1,323 @@ + +/***************************************************/ + +/* Test all aa properties (length, values, keys, opApply(Key, Value), opApply_r(Value), + * dup, byKey, byValue, rehash, opIndex, opIndexAssign, opIn_r) + * + * With all aas: literal, variable, ref parameter, rvalue + */ + +int testLiteral() +{ + assert([5 : 4].length == 1); + assert([5 : 4].values == [4]); + assert([5 : 4].keys == [5]); + foreach(k, v; [5 : 4]) + assert(k == 5 && v == 4); + foreach(v; [5 : 4]) + assert(v == 4); + assert([5 : 4].dup == [5 : 4]); + assert([5 : 4].dup); + if (!__ctfe) + foreach(k; [5 : 4].byKey) + assert(k == 5); + if (!__ctfe) + foreach(v; [5 : 4].byValue) + assert(v == 4); + assert([5 : 4].rehash == [5 : 4]); + assert([5 : 4][5] == 4); + assert([5 : 4].get(5, 2) == 4); + assert([5 : 4].get(1, 2) == 2); + //assert(([5 : 4][3] = 7) == 7); + assert(*(5 in [5 : 4]) == 4); + return 1; +} + +int testVar() +{ + auto aa = [5 : 4]; + assert(aa.length == 1); + assert(aa.values == [4]); + assert(aa.keys == [5]); + foreach(k, v; aa) + assert(k == 5 && v == 4); + foreach(v; aa) + assert(v == 4); + assert(aa.dup == aa); + assert(aa.dup); + if (!__ctfe) + foreach(k; aa.byKey) + assert(k == 5); + if (!__ctfe) + foreach(v; aa.byValue) + assert(v == 4); + assert(aa.rehash == aa); + assert(aa[5] == 4); + assert(aa.get(5, 2) == 4); + assert(aa.get(1, 2) == 2); + assert(*(5 in aa) == 4); + assert((aa[3] = 7) == 7); + return 1; +} + +int testVarConst() +{ + const aa = [5 : 4]; + assert(aa.length == 1); + assert(aa.values == [4]); + assert(aa.keys == [5]); + foreach(k, v; aa) + assert(k == 5 && v == 4); + foreach(v; aa) + assert(v == 4); + //assert(aa.dup == aa); + assert(aa.dup); + if (!__ctfe) + foreach(k; aa.byKey) + assert(k == 5); + if (!__ctfe) + foreach(v; aa.byValue) + assert(v == 4); + //assert(aa.rehash == aa); + assert(aa[5] == 4); + assert(aa.get(5, 2) == 4); + assert(aa.get(1, 2) == 2); + assert(*(5 in aa) == 4); + //assert((aa[3] = 7) == 7); + return 1; +} + +int testVarImmutable() +{ + immutable aa = [5 : 4]; + assert(aa.length == 1); + assert(aa.values == [4]); + assert(aa.keys == [5]); + foreach(k, v; aa) + assert(k == 5 && v == 4); + foreach(v; aa) + assert(v == 4); + //assert(aa.dup == aa); + assert(aa.dup); + if (!__ctfe) + foreach(k; aa.byKey) + assert(k == 5); + if (!__ctfe) + foreach(v; aa.byValue) + assert(v == 4); + // assert(aa.rehash == aa); + assert(aa[5] == 4); + assert(aa.get(5, 2) == 4); + assert(aa.get(1, 2) == 2); + assert(*(5 in aa) == 4); + //assert((aa[3] = 7) == 7); + return 1; +} + +int testPointer() +{ + auto x = [5 : 4]; + auto aa = &x; + assert(aa.length == 1); + assert(aa.values == [4]); + assert(aa.keys == [5]); + // foreach(k, v; aa) + // assert(k == 5 && v == 4); + // foreach(v; aa) + // assert(v == 4); + assert(aa.dup == *aa); + assert(aa.dup); + if (!__ctfe) + foreach(k; aa.byKey) + assert(k == 5); + if (!__ctfe) + foreach(v; aa.byValue) + assert(v == 4); + if (!__ctfe) + assert(aa.rehash == *aa); + assert((*aa)[5] == 4); + assert(aa.get(5, 2) == 4); + assert(aa.get(1, 2) == 2); + // assert(*(5 in aa) == 4); + if (!__ctfe) + assert(((*aa)[3] = 7) == 7); + return 1; +} + +int testPointerConst() +{ + const x = [5 : 4]; + const aa = &x; + assert(aa.length == 1); + assert(aa.values == [4]); + assert(aa.keys == [5]); + // foreach(k, v; aa) + // assert(k == 5 && v == 4); + // foreach(v; aa) + // assert(v == 4); + // assert(aa.dup == *aa); + assert(aa.dup); + if (!__ctfe) + foreach(k; aa.byKey) + assert(k == 5); + if (!__ctfe) + foreach(v; aa.byValue) + assert(v == 4); + // assert(aa.rehash == aa); + assert((*aa)[5] == 4); + assert(aa.get(5, 2) == 4); + assert(aa.get(1, 2) == 2); + // assert(*(5 in aa) == 4); + // assert(((*aa)[3] = 7) == 7); + return 1; +} + +int testPointerImmutable() +{ + immutable x = [5 : 4]; + auto aa = &x; + assert(aa.length == 1); + assert(aa.values == [4]); + assert(aa.keys == [5]); + // foreach(k, v; aa) + // assert(k == 5 && v == 4); + // foreach(v; aa) + // assert(v == 4); + // assert(aa.dup == *aa); + assert(aa.dup); + if (!__ctfe) + foreach(k; aa.byKey) + assert(k == 5); + if (!__ctfe) + foreach(v; aa.byValue) + assert(v == 4); + // assert(aa.rehash == aa); + assert((*aa)[5] == 4); + assert(aa.get(5, 2) == 4); + assert(aa.get(1, 2) == 2); + // assert(*(5 in aa) == 4); + // assert(((*aa)[3] = 7) == 7); + return 1; +} + +int testRef() +{ + auto aa = [5 : 4]; + return testRefx(aa); +} +int testRefx(ref int[int] aa) +{ + assert(aa.length == 1); + assert(aa.values == [4]); + assert(aa.keys == [5]); + foreach(k, v; aa) + assert(k == 5 && v == 4); + foreach(v; aa) + assert(v == 4); + assert(aa.dup == aa); + assert(aa.dup); + if (!__ctfe) + foreach(k; aa.byKey) + assert(k == 5); + if (!__ctfe) + foreach(v; aa.byValue) + assert(v == 4); + assert(aa.rehash == aa); + assert(aa[5] == 4); + assert(aa.get(5, 2) == 4); + assert(aa.get(1, 2) == 2); + assert((aa[3] = 7) == 7); + assert(*(5 in aa) == 4); + return 1; +} + +int testRet() +{ + assert(testRetx().length == 1); + assert(testRetx().values == [4]); + assert(testRetx().keys == [5]); + foreach(k, v; testRetx()) + assert(k == 5 && v == 4); + foreach(v; testRetx()) + assert(v == 4); + assert(testRetx().dup == testRetx()); + assert(testRetx().dup); + if (!__ctfe) + foreach(k; testRetx().byKey) + assert(k == 5); + if (!__ctfe) + foreach(v; testRetx().byValue) + assert(v == 4); + assert(testRetx().rehash == testRetx()); + assert(testRetx()[5] == 4); + assert(testRetx().get(5, 2) == 4); + assert(testRetx().get(1, 2) == 2); + //assert((testRetx()[3] = 7) == 7); + assert(*(5 in testRetx()) == 4); + return 1; +} +int[int] testRetx() +{ + return [5 : 4]; +} + +void aafunc(int[int] aa) {} + +/***************************************************/ +// 12214 + +void test12214() pure nothrow +{ + int[int] aa; + auto n = aa.length; +} + +/***************************************************/ +// 12220 & 12221 + +void test12220() +{ + short[short] hash; + short k = hash.get(1, 2); + assert(k == 2); + + enum Key : short { a = 10 } + short a = hash.get(Key.a, Key.a); + assert(a == 10); +} + +/***************************************************/ +// 12403 + +void test12403() +{ + const(int)[int] m; + assert(m.get(0, 1) == 1); +} + +/***************************************************/ + +void main() +{ + assert(testLiteral()); + static assert(testLiteral()); + assert(testVar()); + static assert(testVar()); + assert(testVarConst()); + static assert(testVarConst()); + assert(testVarImmutable()); + static assert(testVarImmutable()); + assert(testPointer()); + static assert(testPointer()); + assert(testPointerConst()); + static assert(testPointerConst()); + assert(testPointerImmutable()); + static assert(testPointerImmutable()); + assert(testRef()); + static assert(testRef()); + assert(testRet()); + static assert(testRet()); + + test12220(); + test12403(); +} diff --git a/gcc/testsuite/gdc.test/runnable/testappend.d b/gcc/testsuite/gdc.test/runnable/testappend.d index 7d2fda82e..a92ac2f43 100644 --- a/gcc/testsuite/gdc.test/runnable/testappend.d +++ b/gcc/testsuite/gdc.test/runnable/testappend.d @@ -3,27 +3,39 @@ import std.c.stdio; import std.math; +void test12826() +{ + string s, t; + t = t ~ "1234567"; + s = s ~ "1234567"; + + s ~= s; + assert(s == "12345671234567", s); + assert(t == "1234567", t); +} + + int main() { int[] a; for (int i = 0; i < 1000; i++) { - a.length = a.length + 100; + a.length = a.length + 100; } foreach (v; a) { - assert(v == 0); + assert(v == 0); } float[] b; for (int i = 0; i < 2000; i++) { - b.length = b.length + 100; + b.length = b.length + 100; } foreach (v; b) { - assert(isnan(v)); + assert(isnan(v)); } delete a; delete b; @@ -31,25 +43,26 @@ int main() a = null; for (int i = 0; i < 100000; i++) { - a ~= i; + a ~= i; } foreach (k, v; a) { - assert(v == k); + assert(v == k); } b = null; for (int i = 0; i < 200000; i++) { - b ~= i; + b ~= i; } foreach (k, v; b) { - assert(v == k); + assert(v == k); } delete a; delete b; + test12826(); printf("Success\n"); return 0; } diff --git a/gcc/testsuite/gdc.test/runnable/testbounds2.d b/gcc/testsuite/gdc.test/runnable/testbounds2.d index d8a457422..be807d4d0 100644 --- a/gcc/testsuite/gdc.test/runnable/testbounds2.d +++ b/gcc/testsuite/gdc.test/runnable/testbounds2.d @@ -244,6 +244,19 @@ void test9747() foo9747D("abcd"d); } +/******************************************/ +// 12876 + +void test12876() +{ + void foo(int[4] b) {} + void bar(size_t n)(int[n] c) { static assert(n == 4); } + + int[5] a; + foo(a[1 .. $]); // OK + bar(a[1 .. $]); // OK <- Error +} + /******************************************/ int main() diff --git a/gcc/testsuite/gdc.test/runnable/testbounds_off.d b/gcc/testsuite/gdc.test/runnable/testbounds_off.d new file mode 100644 index 000000000..6821f3e64 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/testbounds_off.d @@ -0,0 +1,27 @@ +// REQUIRED_ARGS: -boundscheck=off +// PERMUTE_ARGS: -inline -g -O + +import core.exception : RangeError; + +// Check for RangeError is thrown +bool thrown(T)(lazy T cond) +{ + import core.exception; + bool f = false; + try { cond(); } catch (RangeError e) { f = true; } + return f; +} + +@safe int safeIndex (int[] arr) { return arr[2]; } +@trusted int trustedIndex(int[] arr) { return arr[2]; } +@system int systemIndex (int[] arr) { return arr[2]; } + +void main() +{ + int[3] data = [1,2,3]; + int[] arr = data[0..2]; + + assert(arr. safeIndex() == 3); + assert(arr.trustedIndex() == 3); + assert(arr. systemIndex() == 3); +} diff --git a/gcc/testsuite/gdc.test/runnable/testbounds_on.d b/gcc/testsuite/gdc.test/runnable/testbounds_on.d new file mode 100644 index 000000000..0506bb112 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/testbounds_on.d @@ -0,0 +1,27 @@ +// REQUIRED_ARGS: -boundscheck=on +// PERMUTE_ARGS: -inline -g -O + +import core.exception : RangeError; + +// Check for RangeError is thrown +bool thrown(T)(lazy T cond) +{ + import core.exception; + bool f = false; + try { cond(); } catch (RangeError e) { f = true; } + return f; +} + +@safe int safeIndex (int[] arr) { return arr[2]; } +@trusted int trustedIndex(int[] arr) { return arr[2]; } +@system int systemIndex (int[] arr) { return arr[2]; } + +void main() +{ + int[3] data = [1,2,3]; + int[] arr = data[0..2]; + + assert(arr. safeIndex().thrown); + assert(arr.trustedIndex().thrown); + assert(arr. systemIndex().thrown); +} diff --git a/gcc/testsuite/gdc.test/runnable/testbounds_safeonly.d b/gcc/testsuite/gdc.test/runnable/testbounds_safeonly.d new file mode 100644 index 000000000..ae8e83137 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/testbounds_safeonly.d @@ -0,0 +1,27 @@ +// REQUIRED_ARGS: -boundscheck=safeonly +// PERMUTE_ARGS: -inline -g -O + +import core.exception : RangeError; + +// Check for RangeError is thrown +bool thrown(T)(lazy T cond) +{ + import core.exception; + bool f = false; + try { cond(); } catch (RangeError e) { f = true; } + return f; +} + +@safe int safeIndex (int[] arr) { return arr[2]; } +@trusted int trustedIndex(int[] arr) { return arr[2]; } +@system int systemIndex (int[] arr) { return arr[2]; } + +void main() +{ + int[3] data = [1,2,3]; + int[] arr = data[0..2]; + + assert(arr. safeIndex().thrown); + assert(arr.trustedIndex() == 3); + assert(arr. systemIndex() == 3); +} diff --git a/gcc/testsuite/gdc.test/runnable/testclass.d b/gcc/testsuite/gdc.test/runnable/testclass.d new file mode 100644 index 000000000..ac0aba317 --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/testclass.d @@ -0,0 +1,65 @@ +extern(C) int printf(const char*, ...); + +/******************************************/ +// 12078 + +class B12078(T) +{ + static assert(is(T : B12078!T), "not related"); +} +class D12078 : B12078!D12078 +{ +} + +interface X12078(T) +{ + static assert(is(T : X12078!T), "not related"); +} +interface Y12078 : X12078!Y12078 +{ +} + +void test12078() +{ + static assert(is(D12078 : B12078!D12078)); + static assert(is(Y12078 : X12078!Y12078)); +} + +/******************************************/ +// 12143 + +class Node12143 +{ + alias typeof(true ? Node12143.init : Class12143.init) V; + static assert(is(V == Node12143)); +} + +class Type12143 : Node12143 {} + +class Class12143 : Type12143 {} + +/***************************************************/ +// 13353 + +interface Base13353(T) +{ + static assert(is(T : Base13353!T)); +} + +interface Derived13353 : Base13353!Derived13353 +{ + void func(); +} + +class Concrete13353 : Derived13353 +{ + void func() {} +} + +/***************************************************/ + +int main() +{ + printf("Success\n"); + return 0; +} diff --git a/gcc/testsuite/gdc.test/runnable/testconst.d b/gcc/testsuite/gdc.test/runnable/testconst.d index 80d075e1c..39efbca3f 100644 --- a/gcc/testsuite/gdc.test/runnable/testconst.d +++ b/gcc/testsuite/gdc.test/runnable/testconst.d @@ -87,7 +87,7 @@ void test8() auto p = &foo8; showf(p.mangleof); assert(typeof(p).mangleof == "PFxAaxC9testconst2C8xiZv"); - assert(p.mangleof == "_D9testconst5test8FZv1pPFxAaxC9testconst2C8xiZv"); + assert(p.mangleof == "_D9testconst5test8FZ1pPFxAaxC9testconst2C8xiZv"); } /************************************/ @@ -1968,46 +1968,7 @@ void test1961b() inout(T) min2(int i, int j, T)(inout(T) a, inout(T) b) { //pragma(msg, "(", i, ", ", j, ") = ", T); - static if (i == 0) - { - static if (j == 0) static assert(is(T == immutable(char)[])); - static if (j == 1) static assert(is(T == immutable(char)[])); - static if (j == 2) static assert(is(T == const(char)[])); - static if (j == 3) static assert(is(T == const(char)[])); - static if (j == 4) static assert(is(T == const(char)[])); - } - static if (i == 1) - { - static if (j == 0) static assert(is(T == immutable(char)[])); - static if (j == 1) static assert(is(T == immutable(char)[])); - static if (j == 2) static assert(is(T == const(char)[])); - static if (j == 3) static assert(is(T == const(char)[])); - static if (j == 4) static assert(is(T == const(char)[])); - } - static if (i == 2) - { - static if (j == 0) static assert(is(T == const(char)[])); - static if (j == 1) static assert(is(T == const(char)[])); - static if (j == 2) static assert(is(T == const(char)[])); - static if (j == 3) static assert(is(T == const(char)[])); - static if (j == 4) static assert(is(T == const(char)[])); - } - static if (i == 3) - { - static if (j == 0) static assert(is(T == const(char)[])); - static if (j == 1) static assert(is(T == const(char)[])); - static if (j == 2) static assert(is(T == const(char)[])); - static if (j == 3) static assert(is(T == const(char)[])); - static if (j == 4) static assert(is(T == const(char)[])); - } - static if (i == 4) - { - static if (j == 0) static assert(is(T == const(char)[])); - static if (j == 1) static assert(is(T == const(char)[])); - static if (j == 2) static assert(is(T == const(char)[])); - static if (j == 3) static assert(is(T == const(char)[])); - static if (j == 4) static assert(is(T == char[])); - } + static assert(is(T == char[])); return a < b ? a : b; } @@ -2026,55 +1987,39 @@ void test1961c() { min2!(i, j)(x, y); //pragma(msg, "x: ",typeof(x), ", y: ",typeof(y), " -> ", typeof(min2(x, y)), " : ", __traits(compiles, min2(x, y))); - /+ - x: immutable(char[]) , y: immutable(char[]) -> immutable(char[]) : true - x: immutable(char[]) , y: immutable(char)[] -> const(immutable(char)[]) : true - x: immutable(char[]) , y: const(char[]) -> const(char[]) : true - x: immutable(char[]) , y: const(char)[] -> const(char[]) : true - x: immutable(char[]) , y: char[] -> const(char[]) : true - - x: immutable(char)[] , y: immutable(char[]) -> const(immutable(char)[]) : true - x: immutable(char)[] , y: immutable(char)[] -> immutable(char)[] : true - x: immutable(char)[] , y: const(char[]) -> const(char[]) : true - x: immutable(char)[] , y: const(char)[] -> const(char)[] : true - x: immutable(char)[] , y: char[] -> const(char)[] : true - - x: const(char[]) , y: immutable(char[]) -> const(char[]) : true - x: const(char[]) , y: immutable(char)[] -> const(char[]) : true - x: const(char[]) , y: const(char[]) -> const(char[]) : true - x: const(char[]) , y: const(char)[] -> const(char[]) : true - x: const(char[]) , y: char[] -> const(char[]) : true - - x: const(char)[] , y: immutable(char[]) -> const(char[]) : true - x: const(char)[] , y: immutable(char)[] -> const(char)[] : true - x: const(char)[] , y: const(char[]) -> const(char[]) : true - x: const(char)[] , y: const(char)[] -> const(char)[] : true - x: const(char)[] , y: char[] -> const(char)[] : true - - x: char[] , y: immutable(char[]) -> const(char[]) : true - x: char[] , y: immutable(char)[] -> const(char)[] : true - x: char[] , y: const(char[]) -> const(char[]) : true - x: char[] , y: const(char)[] -> const(char)[] : true - x: char[] , y: char[] -> char[] : true - +/ } } /************************************/ -inout(int) function(inout(int)) notinoutfun1() { return null; } -inout(int) delegate(inout(int)) notinoutfun2() { return null; } -void notinoutfun1(inout(int) function(inout(int)) fn) {} -void notinoutfun2(inout(int) delegate(inout(int)) dg) {} +inout(int) function(inout(int)) notinoutfun1() { return null; } +inout(int) function(inout(int))[] notinoutfun2() { return null; } +inout(int) delegate(inout(int)) notinoutfun3() { return null; } +inout(int) delegate(inout(int))[] notinoutfun4() { return null; } +void notinoutfun1(inout(int) function(inout(int)) fn) {} +void notinoutfun2(inout(int) function(inout(int))[] fn) {} +void notinoutfun3(inout(int) delegate(inout(int)) dg) {} +void notinoutfun4(inout(int) delegate(inout(int))[] dg) {} void test88() { - inout(int) function(inout(int)) fp; - inout(int) delegate(inout(int)) dg; + inout(int) function(inout int) fp; + inout(int) delegate(inout int) dg; - static assert(!__traits(compiles, { - inout(int)* p; - })); + inout(int) function(inout int)* fp2p; + inout(int) function(inout int)[] fp2a; + inout(int) function(inout int)[3] fp2s; + + inout(int) delegate(inout int)* dg3p; + inout(int) delegate(inout int)[] dg3a; + inout(int) delegate(inout int)[3] dg3s; + + int delegate() inout* dg4p; + int delegate() inout[] dg4a; + int delegate() inout[3] dg4s; + + static assert(!__traits(compiles, { inout(int)* p; })); + static assert(!__traits(compiles, { inout(int delegate()) dg; })); } /************************************/ @@ -2770,6 +2715,23 @@ void validate12089(S)(in S str) void decodeImpl12089(S)(auto ref S str) {} +/************************************/ +// 12524 + +inout(int) dup12524(inout(const(int)) val) +{ + return val; +} + +void test12524(inout(int)) +{ + inout(const(int)) val; + + auto bug = dup12524(val); + + static assert(is(typeof(bug) == inout(int))); +} + /************************************/ // 6941 @@ -2970,6 +2932,8 @@ inout(T) bar7757a(T)(T x, lazy inout(T) def) { return def; } inout(T)[] bar7757b(T)(T x, lazy inout(T)[] def) { return def; } inout(T)[T] bar7757c(T)(T x, lazy inout(T)[T] def) { return def; } +inout(Object) get7757(lazy inout(Object) defVal) { return null; } + void test7757() { int mx1 = foo7757a(1,2); @@ -2985,6 +2949,9 @@ void test7757() const(int)[] ca2 = bar7757b(1,[2]); int [int] maa2 = bar7757c(1,[2:3]); const(int)[int] caa2 = bar7757c(1,[2:3]); + + Object defObj = null; + auto resObj = get7757(defObj); } /************************************/ @@ -3671,6 +3638,45 @@ void test11768(inout int = 0) static assert(is(typeof(k1) == typeof(k2))); // fails } +/************************************/ +// 12403 + +void test12403() +{ + void func(K, V)(inout(V[K]) aa) + { + static assert(is(V == const int)); + static assert(is(K == int)); + } + + const(int)[int] m; + func(m); +} + +/************************************/ +// 13011 + +void test13011() +{ + static size_t hashOf(int delegate() inout val) + { + return 0; + } + + int delegate() inout dg; + auto h = hashOf(dg); +} + +/************************************/ +// 13030 + +void va13030(Args...)(const Args args) {} + +void func13030(int delegate(int n) a) +{ + va13030(a); +} + /************************************/ int main() @@ -3801,6 +3807,7 @@ int main() test9209(); test11226(); test11768(); + test13011(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/testdt.d b/gcc/testsuite/gdc.test/runnable/testdt.d index cbe98e7a1..bc89293be 100644 --- a/gcc/testsuite/gdc.test/runnable/testdt.d +++ b/gcc/testsuite/gdc.test/runnable/testdt.d @@ -1,10 +1,10 @@ // PERMUTE_ARGS: -struct S { uint[0x100000] arr; } // Bugzilla 11233 +/******************************************/ static int bigarray[100][100]; -int main(char[][] args) +void test1() { for (int i = 0; i < 100; i += 1) { @@ -14,5 +14,49 @@ int main(char[][] args) bigarray[i][j] = 0; } } - return 0; +} + +/******************************************/ +// 11233 + +struct S11233 +{ + uint[0x100000] arr; +} + +/******************************************/ +// 11672 + +void test11672() +{ + struct V { float f; } + struct S + { + V[3] v = V(1); + } + + S s; + assert(s.v == [V(1), V(1), V(1)]); /* was [V(1), V(nan), V(nan)] */ +} + +/******************************************/ +// 12509 + +struct A12509 +{ + int member; +} +struct B12509 +{ + A12509[0x10000] array; +} + +/******************************************/ + +int main() +{ + test1(); + test11672(); + + return 0; } diff --git a/gcc/testsuite/gdc.test/runnable/testinvariant.d b/gcc/testsuite/gdc.test/runnable/testinvariant.d index 5eba90972..3f81522cc 100644 --- a/gcc/testsuite/gdc.test/runnable/testinvariant.d +++ b/gcc/testsuite/gdc.test/runnable/testinvariant.d @@ -106,10 +106,44 @@ void test6453() } } +/***************************************************/ +// 13113 + +struct S13113 +{ + static int count; + invariant() // impure, throwable, system, and gc-able + { + ++count; // impure + } + + this(int) pure nothrow @safe @nogc {} + // post invaiant is called directly but doesn't interfere with constructor attributes + + ~this() pure nothrow @safe @nogc {} + // pre invaiant is called directly but doesn't interfere with destructor attributes + + void foo() pure nothrow @safe @nogc {} + // pre & post invariant calls don't interfere with method attributes +} + +void test13113() +{ + assert(S13113.count == 0); + { + auto s = S13113(1); + assert(S13113.count == 1); + s.foo(); + assert(S13113.count == 3); + } + assert(S13113.count == 4); +} + /***************************************************/ void main() { testinvariant(); test6453(); + test13113(); } diff --git a/gcc/testsuite/gdc.test/runnable/testrightthis.d b/gcc/testsuite/gdc.test/runnable/testrightthis.d index d7784f8aa..40675e15b 100644 --- a/gcc/testsuite/gdc.test/runnable/testrightthis.d +++ b/gcc/testsuite/gdc.test/runnable/testrightthis.d @@ -467,6 +467,25 @@ void test7() static assert(!__traits(compiles, S7.bar(1))); } +/********************************************************/ +// 6430 + +auto bug6430(int a) +{ + static struct Result2 {} + return 4; +} +auto bug6430(int a, int b) +{ + static struct Result2 + { + int z; + int y() { return z; } + } + auto t = Result2(1); + return 5; +} + /********************************************************/ // 9619 diff --git a/gcc/testsuite/gdc.test/runnable/testsafe.d b/gcc/testsuite/gdc.test/runnable/testsafe.d index e2694d2ab..30bb6c8f9 100644 --- a/gcc/testsuite/gdc.test/runnable/testsafe.d +++ b/gcc/testsuite/gdc.test/runnable/testsafe.d @@ -210,12 +210,13 @@ void inlineasm() version(GNU) { version(X86) - static assert(!__traits(compiles, { asm { "nop;" :::; } }() )); + static assert(!__traits(compiles, { asm { "nop"; } }() )); else version(X86_64) - static assert(!__traits(compiles, { asm { "nop;" :::; } }() )); + static assert(!__traits(compiles, { asm { "nop"; } }() )); else version(ARM) - static assert(!__traits(compiles, { asm { "nop;" :::; } }() )); - else static assert(false, "ASM code not implemented for this architecture"); + static assert(!__traits(compiles, { asm { "nop"; } }() )); + else + static assert(false, "ASM code not implemented for this architecture"); } else static assert(!__traits(compiles, { asm { int 3; } }() )); diff --git a/gcc/testsuite/gdc.test/runnable/testtypeid.d b/gcc/testsuite/gdc.test/runnable/testtypeid.d index 0b3a75a87..53e09f66a 100644 --- a/gcc/testsuite/gdc.test/runnable/testtypeid.d +++ b/gcc/testsuite/gdc.test/runnable/testtypeid.d @@ -522,6 +522,62 @@ void test11010() assert(ti.toString() == "testtypeid.D11010"); } +/******************************************************/ +// 13045 + +void test13045a() +{ + static struct S + { + int[] a; + } + + auto s1 = S([1,2]); + auto s2 = S([1,2]); + assert(s1 !is s2); + assert(s1 == s2); + assert(typeid(S).getHash(&s1) == typeid(S).getHash(&s2)); // should succeed +} + +void test13045b() +{ + bool thrown(T)(lazy T cond) + { + import core.exception; + try + cond(); + catch (Error e) + return true; + return false; + } + + struct S + { + size_t toHash() const nothrow @safe + { + // all getHash call should reach here + throw new Error(""); + } + } + struct T + { + S s; + } + S s; + assert(thrown(typeid(S).getHash(&s))); // OK + S[1] ssa; + assert(thrown(typeid(S[1]).getHash(&ssa))); // OK + S[] sda = [S(), S()]; + assert(thrown(typeid(S[]).getHash(&sda))); // OK + + T t; + assert(thrown(typeid(T).getHash(&t))); // OK <- NG + T[1] tsa; + assert(thrown(typeid(T[1]).getHash(&tsa))); // OK <- NG + T[] tda = [T(), T()]; + assert(thrown(typeid(T[]).getHash(&tda))); // OK <- NG +} + /******************************************************/ int main() @@ -564,6 +620,8 @@ int main() test9442(); test10451(); test11010(); + test13045a(); + test13045b(); return 0; } diff --git a/gcc/testsuite/gdc.test/runnable/testxmm.d b/gcc/testsuite/gdc.test/runnable/testxmm.d index 0b422a051..669c2f7c2 100644 --- a/gcc/testsuite/gdc.test/runnable/testxmm.d +++ b/gcc/testsuite/gdc.test/runnable/testxmm.d @@ -1161,6 +1161,38 @@ void test9910() /*****************************************/ +bool normalize(double[] range, double sum = 1) +{ + double s = 0; + const length = range.length; + foreach (e; range) + { + s += e; + } + if (s == 0) + { + return false; + } + return true; +} + +void test12852() +{ + double[3] range = [0.0, 0.0, 0.0]; + assert(normalize(range[]) == false); + range[1] = 3.0; + assert(normalize(range[]) == true); +} + +/*****************************************/ + +void test9449() +{ + ubyte16 table[1]; +} + +/*****************************************/ + int main() { test1(); @@ -1188,6 +1220,8 @@ int main() // test9200(); test9304(); test9910(); + test12852(); + test9449(); return 0; } diff --git a/gcc/testsuite/gdc.test/runnable/traits.d b/gcc/testsuite/gdc.test/runnable/traits.d index d70f79bb1..934f68ad7 100644 --- a/gcc/testsuite/gdc.test/runnable/traits.d +++ b/gcc/testsuite/gdc.test/runnable/traits.d @@ -1114,6 +1114,19 @@ void test9237() static assert(__traits(isPOD, POD2_9237)); static assert(__traits(isPOD, POD3_9237)); + // static array of POD/non-POD types + static assert(!__traits(isPOD, NS_9237[2])); + static assert(__traits(isPOD, NonNS_9237[2])); + static assert(__traits(isPOD, StatNS_9237[2])); + static assert(__traits(isPOD, CtorS_9237[2])); + static assert(!__traits(isPOD, DtorS_9237[2])); + static assert(!__traits(isPOD, PostblitS_9237[2])); + static assert(!__traits(isPOD, NonPOD1_9237[2])); + static assert(!__traits(isPOD, NonPOD2_9237[2])); + static assert(__traits(isPOD, POD1_9237[2])); + static assert(__traits(isPOD, POD2_9237[2])); + static assert(__traits(isPOD, POD3_9237[2])); + // non-structs are POD types static assert(__traits(isPOD, C_9273)); static assert(__traits(isPOD, int)); @@ -1123,11 +1136,13 @@ void test9237() } /*************************************************************/ +// 5978 -void test5978() { +void test5978() +{ () { int x; - pragma(msg, __traits(parent, x)); + pragma(msg, __traits(identifier, __traits(parent, x))); } (); } @@ -1288,6 +1303,117 @@ void test_getUnitTests () /********************************************************/ +void test_getFunctionAttributes() +{ + alias tuple(T...) = T; + + struct S + { + int noF() { return 0; } + int constF() const { return 0; } + int immutableF() immutable { return 0; } + int inoutF() inout { return 0; } + int sharedF() shared { return 0; } + + int x; + ref int refF() { return x; } + int propertyF() @property { return 0; } + int nothrowF() nothrow { return 0; } + int nogcF() @nogc { return 0; } + + int systemF() @system { return 0; } + int trustedF() @trusted { return 0; } + int safeF() @safe { return 0; } + + int pureF() pure { return 0; } + } + + static assert(__traits(getFunctionAttributes, S.noF) == tuple!("@system")); + static assert(__traits(getFunctionAttributes, typeof(S.noF)) == tuple!("@system")); + + static assert(__traits(getFunctionAttributes, S.constF) == tuple!("const", "@system")); + static assert(__traits(getFunctionAttributes, typeof(S.constF)) == tuple!("const", "@system")); + + static assert(__traits(getFunctionAttributes, S.immutableF) == tuple!("immutable", "@system")); + static assert(__traits(getFunctionAttributes, typeof(S.immutableF)) == tuple!("immutable", "@system")); + + static assert(__traits(getFunctionAttributes, S.inoutF) == tuple!("inout", "@system")); + static assert(__traits(getFunctionAttributes, typeof(S.inoutF)) == tuple!("inout", "@system")); + + static assert(__traits(getFunctionAttributes, S.sharedF) == tuple!("shared", "@system")); + static assert(__traits(getFunctionAttributes, typeof(S.sharedF)) == tuple!("shared", "@system")); + + static assert(__traits(getFunctionAttributes, S.refF) == tuple!("ref", "@system")); + static assert(__traits(getFunctionAttributes, typeof(S.refF)) == tuple!("ref", "@system")); + + static assert(__traits(getFunctionAttributes, S.propertyF) == tuple!("@property", "@system")); + static assert(__traits(getFunctionAttributes, typeof(&S.propertyF)) == tuple!("@property", "@system")); + + static assert(__traits(getFunctionAttributes, S.nothrowF) == tuple!("nothrow", "@system")); + static assert(__traits(getFunctionAttributes, typeof(S.nothrowF)) == tuple!("nothrow", "@system")); + + static assert(__traits(getFunctionAttributes, S.nogcF) == tuple!("@nogc", "@system")); + static assert(__traits(getFunctionAttributes, typeof(S.nogcF)) == tuple!("@nogc", "@system")); + + static assert(__traits(getFunctionAttributes, S.systemF) == tuple!("@system")); + static assert(__traits(getFunctionAttributes, typeof(S.systemF)) == tuple!("@system")); + + static assert(__traits(getFunctionAttributes, S.trustedF) == tuple!("@trusted")); + static assert(__traits(getFunctionAttributes, typeof(S.trustedF)) == tuple!("@trusted")); + + static assert(__traits(getFunctionAttributes, S.safeF) == tuple!("@safe")); + static assert(__traits(getFunctionAttributes, typeof(S.safeF)) == tuple!("@safe")); + + static assert(__traits(getFunctionAttributes, S.pureF) == tuple!("pure", "@system")); + static assert(__traits(getFunctionAttributes, typeof(S.pureF)) == tuple!("pure", "@system")); + + int pure_nothrow() nothrow pure { return 0; } + static ref int static_ref_property() @property { return *(new int); } + ref int ref_property() @property { return *(new int); } + void safe_nothrow() @safe nothrow { } + + static assert(__traits(getFunctionAttributes, pure_nothrow) == tuple!("pure", "nothrow", "@system")); + static assert(__traits(getFunctionAttributes, typeof(pure_nothrow)) == tuple!("pure", "nothrow", "@system")); + + static assert(__traits(getFunctionAttributes, static_ref_property) == tuple!("@property", "ref", "@system")); + static assert(__traits(getFunctionAttributes, typeof(&static_ref_property)) == tuple!("@property", "ref", "@system")); + + static assert(__traits(getFunctionAttributes, ref_property) == tuple!("@property", "ref", "@system")); + static assert(__traits(getFunctionAttributes, typeof(&ref_property)) == tuple!("@property", "ref", "@system")); + + static assert(__traits(getFunctionAttributes, safe_nothrow) == tuple!("nothrow", "@safe")); + static assert(__traits(getFunctionAttributes, typeof(safe_nothrow)) == tuple!("nothrow", "@safe")); + + struct S2 + { + int pure_const() const pure { return 0; } + int pure_sharedconst() const shared pure { return 0; } + } + + static assert(__traits(getFunctionAttributes, S2.pure_const) == tuple!("const", "pure", "@system")); + static assert(__traits(getFunctionAttributes, typeof(S2.pure_const)) == tuple!("const", "pure", "@system")); + + static assert(__traits(getFunctionAttributes, S2.pure_sharedconst) == tuple!("const", "shared", "pure", "@system")); + static assert(__traits(getFunctionAttributes, typeof(S2.pure_sharedconst)) == tuple!("const", "shared", "pure", "@system")); + + static assert(__traits(getFunctionAttributes, (int a) { }) == tuple!("pure", "nothrow", "@nogc", "@safe")); + static assert(__traits(getFunctionAttributes, typeof((int a) { })) == tuple!("pure", "nothrow", "@nogc", "@safe")); + + auto safeDel = delegate() @safe { }; + static assert(__traits(getFunctionAttributes, safeDel) == tuple!("pure", "nothrow", "@nogc", "@safe")); + static assert(__traits(getFunctionAttributes, typeof(safeDel)) == tuple!("pure", "nothrow", "@nogc", "@safe")); + + auto trustedDel = delegate() @trusted { }; + static assert(__traits(getFunctionAttributes, trustedDel) == tuple!("pure", "nothrow", "@nogc", "@trusted")); + static assert(__traits(getFunctionAttributes, typeof(trustedDel)) == tuple!("pure", "nothrow", "@nogc", "@trusted")); + + auto systemDel = delegate() @system { }; + static assert(__traits(getFunctionAttributes, systemDel) == tuple!("pure", "nothrow", "@nogc", "@system")); + static assert(__traits(getFunctionAttributes, typeof(systemDel)) == tuple!("pure", "nothrow", "@nogc", "@system")); +} + +/********************************************************/ + class TestIsOverrideFunctionBase { void bar () {} @@ -1330,6 +1456,62 @@ void test11711() == TypeTuple!(int, string))); } + +/********************************************************/ +// Issue 12278 + +class Foo12278 +{ + InPlace12278!Bar12278 inside; +} + +class Bar12278 { } + +struct InPlace12278(T) +{ + static assert(__traits(classInstanceSize, T) != 0); +} + +/********************************************************/ +// 12571 + +mixin template getScopeName12571() +{ + enum string scopeName = __traits(identifier, __traits(parent, scopeName)); +} + +void test12571() +{ + mixin getScopeName12571; + static assert(scopeName == "test12571"); +} + +/********************************************************/ +// 12237 + +auto f12237(T)(T a) +{ + static if (is(typeof(a) == int)) + return f12237(""); + else + return 10; +} + +void test12237() +{ + assert(f12237(1) == 10); + + assert((a){ + static if (is(typeof(a) == int)) + { + int x; + return __traits(parent, x)(""); + } + else + return 10; + }(1) == 10); +} + /********************************************************/ int main() @@ -1368,7 +1550,9 @@ int main() test9136(); test10096(); test_getUnitTests(); + test_getFunctionAttributes(); test_isOverrideFunction(); + test12237(); writeln("Success"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/uda.d b/gcc/testsuite/gdc.test/runnable/uda.d index bfd8a5616..ee8920bd2 100644 --- a/gcc/testsuite/gdc.test/runnable/uda.d +++ b/gcc/testsuite/gdc.test/runnable/uda.d @@ -380,6 +380,71 @@ static assert(__traits(getAttributes, x10208_12)[0] == 120); // OK static assert(__traits(getAttributes, x10208_13)[0] == 130); // Error -> OK static assert(__traits(getAttributes, x10208_14)[0] == 140); // Error -> OK +/************************************************/ +// 11677, 11678 + +bool test_uda(alias func)() @safe +{ + alias Tuple!(__traits(getAttributes, func)) udas; + static assert([udas] == [10, 20]); + + func(); // @safe attribute check + + return true; +} + +@(10) @(20) @safe void func11677a() {} // OK +@safe @(10) @(20) void func11677b() {} // OK <- NG +@(10) @safe @(20) void func11677c() {} // OK <- NG + +static assert(test_uda!func11677a()); +static assert(test_uda!func11677b()); +static assert(test_uda!func11677c()); + +void func11678a() @safe @(10) @(20) {} // OK <- NG +void func11678b() @(10) @safe @(20) {} // OK <- NG +void func11678c() @(10) @(20) @safe {} // OK <- NG + +static assert(test_uda!func11678a()); +static assert(test_uda!func11678b()); +static assert(test_uda!func11678c()); + +/************************************************/ +// 11678 + +class C11678 +{ + this() @(10) @(20) {} + ~this() @(10) @(20) {} +} + +//static this() @(10) @(20) {} +static ~this() @(10) @(20) {} + +//shared static this() @(10) @(20) {} +shared static ~this() @(10) @(20) {} + +/************************************************/ +// 11679 + +void test11679() +{ + @(10) @(20) auto var = 1; + static assert([__traits(getAttributes, var)] == [10, 20]); +} + +/************************************************/ +// 11680 + +@(10) gvar11680 = 1; // OK <- NG +@(10) gfun11680() {} // OK <- NG + +void test11680() +{ + @(10) lvar11680 = 1; // OK <- NG + @(10) lfun11680() {} // OK <- NG +} + /************************************************/ // 11844 diff --git a/gcc/testsuite/gdc.test/runnable/ufcs.d b/gcc/testsuite/gdc.test/runnable/ufcs.d index c2a4ea331..43c6b7a63 100644 --- a/gcc/testsuite/gdc.test/runnable/ufcs.d +++ b/gcc/testsuite/gdc.test/runnable/ufcs.d @@ -794,6 +794,21 @@ void test10609() static assert(__traits(compiles, x.foo10609 )); } +/*******************************************/ +// 11312 + +struct S11312; + +S11312* getS11312() { return null; } +int getValue(S11312*) { return 10; } + +void test11312() +{ + S11312* op = getS11312(); + int x = op.getValue(); + assert(x == 10); +} + /*******************************************/ int main() @@ -823,6 +838,7 @@ int main() test10041(); test10047(); test10526(); + test11312(); printf("Success\n"); return 0; diff --git a/gcc/testsuite/gdc.test/runnable/uniformctor.d b/gcc/testsuite/gdc.test/runnable/uniformctor.d new file mode 100644 index 000000000..d56c782bf --- /dev/null +++ b/gcc/testsuite/gdc.test/runnable/uniformctor.d @@ -0,0 +1,157 @@ +extern(C) int printf(const char*, ...); +template TypeTuple(TL...) { alias TypeTuple = TL; } + +import core.stdc.math : isnan; + +/********************************************/ +// 9112 + +void test9112a() // T() and T(v) +{ + void test(T)(T v) + { + foreach (string qual; TypeTuple!("", "const ", "immutable ")) + { + mixin("alias U = "~qual~T.stringof~";"); + //pragma(msg, U); + + mixin("auto x1 = "~qual~T.stringof~"();"); // U() default construction syntax + mixin("auto x2 = "~qual~T.stringof~"(v);"); // U(v) + static assert(!__traits(compiles, mixin(qual~T.stringof~"(v, v)"))); // U(v, v) + static assert(is(typeof(x1) == U)); + static assert(is(typeof(x2) == U)); + static if ( is(typeof(U.nan) : real)) assert( isnan(x1.re) && !isnan(x1.im), U.stringof); + static if ( is(typeof(U.nan) : ireal)) assert(!isnan(x1.re) && isnan(x1.im), U.stringof); + static if ( is(typeof(U.nan) : creal)) assert( isnan(x1.re) && isnan(x1.im), U.stringof); + static if (!is(typeof(U.nan))) assert( x1 == U.init, U.stringof); + assert(x2 == v, U.stringof); + } + } + static assert(!__traits(compiles, { auto x1 = void(); })); + static assert(!__traits(compiles, { auto x2 = void(1); })); + test!( byte )(10); + test!(ubyte )(10); + test!( short )(10); + test!(ushort )(10); + test!( int )(10); + test!(uint )(10); + test!( long )(10); + test!(ulong )(10); + test!( float )(3.14); + test!( double)(3.14); + test!( real )(3.14); + test!(ifloat )(1.4142i); + test!(idouble)(1.4142i); + test!(ireal )(1.4142i); + test!(cfloat )(1.2+3.4i); + test!(cdouble)(1.2+3.4i); + test!(creal )(1.2+3.4i); + test!( char )('A'); + test!(wchar )('A'); + test!(dchar )('A'); + test!(bool )(true); + + static assert(!__traits(compiles, int(1.42))); // in curre,t this is disallowed + static assert(!__traits(compiles, double(3.14i))); + + { + int x; + alias T = int*; + //auto p = int*(&x); // Error: found '*' when expecting '.' following int + //auto p = (int*)(&x); // Error: C style cast illegal, use cast(int*)&x + auto p = T(&x); + assert( p == &x); + assert(*p == x); + } +} + +enum Enum : long { a = 10, b = 20 } + +void test9112b() // new T(v) +{ + void test(T)(T v) + { + foreach (string qual; TypeTuple!("", "const ", "immutable ")) + { + mixin("alias U = "~qual~T.stringof~";"); + //pragma(msg, U); + + mixin("auto p1 = new "~qual~T.stringof~"();"); // U() default construction syntax + mixin("auto p2 = new "~qual~T.stringof~"(v);"); // U(v) + static assert(!__traits(compiles, mixin("new "~qual~T.stringof~"(v, v)"))); // U(v, v) + static assert(is(typeof(p1) == U*)); + static assert(is(typeof(p2) == U*)); + assert( p1 !is null); + assert( p2 !is null); + auto x1 = *p1; + auto x2 = *p2; + static if ( is(typeof(U.nan) : real)) assert( isnan(x1.re) && !isnan(x1.im), U.stringof); + static if ( is(typeof(U.nan) : ireal)) assert(!isnan(x1.re) && isnan(x1.im), U.stringof); + static if ( is(typeof(U.nan) : creal)) assert( isnan(x1.re) && isnan(x1.im), U.stringof); + static if (!is(typeof(U.nan))) assert( x1 == U.init, U.stringof); + assert(x2 == v, U.stringof); + } + } + + static assert(!__traits(compiles, { auto x1 = new void(); })); + static assert(!__traits(compiles, { auto x2 = new void(1); })); + static assert(!__traits(compiles, { auto x2 = new void(1, 2); })); + test!( byte )(10); + test!(ubyte )(10); + test!( short )(10); + test!(ushort )(10); + test!( int )(10); + test!(uint )(10); + test!( long )(10); + test!(ulong )(10); + test!( float )(3.14); + test!( double)(3.14); + test!( real )(3.14); + test!(ifloat )(1.4142i); + test!(idouble)(1.4142i); + test!(ireal )(1.4142i); + test!(cfloat )(1.2+3.4i); + test!(cdouble)(1.2+3.4i); + test!(creal )(1.2+3.4i); + test!( char )('A'); + test!(wchar )('A'); + test!(dchar )('A'); + test!(bool )(true); + test!(Enum )(Enum.a); + + void testPtr(T)(T v) + { + T* pv = &v; + T** ppv = new T*(pv); + assert( *ppv == pv); + assert(**ppv == v); + } + foreach (T; TypeTuple!(int, const long, immutable double)) + { + testPtr!T(10); + } + foreach (T; TypeTuple!(Enum, const Enum, immutable Enum)) + { + testPtr!T(Enum.a); + } + + static assert(!__traits(compiles, new const int(1, 2))); + + static assert(!__traits(compiles, new int(1.42))); // in curre,t this is disallowed + static assert(!__traits(compiles, new double(3.14i))); + + // int(1) in directly on statement scope should be parsed as an expression, but + // would fail to compile because of "has no effect" error. + static assert(!__traits(compiles, { int(1); })); +} + +/********************************************/ + +int main() +{ + test9112a(); + test9112b(); + + printf("Success\n"); + return 0; +} diff --git a/gcc/testsuite/gdc.test/runnable/warning1.d b/gcc/testsuite/gdc.test/runnable/warning1.d index c0735cee3..008c35f53 100644 --- a/gcc/testsuite/gdc.test/runnable/warning1.d +++ b/gcc/testsuite/gdc.test/runnable/warning1.d @@ -153,6 +153,37 @@ struct S9332 } } +/******************************************/ +// 13201 + +class C13201 +{ + void foo() + { + synchronized(this) + { + assert(0); + } + } +} + +void test13201a() +{ + auto c = new C13201(); + synchronized(c) + { + assert(0); + } +} + +void test13201b() +{ + struct S { ~this() {} } + + S s; + assert(0); +} + /******************************************/ void main() diff --git a/gcc/testsuite/gdc.test/runnable/xtest46.d b/gcc/testsuite/gdc.test/runnable/xtest46.d index 323283aac..03e392734 100644 --- a/gcc/testsuite/gdc.test/runnable/xtest46.d +++ b/gcc/testsuite/gdc.test/runnable/xtest46.d @@ -3159,6 +3159,14 @@ void test141() S141 s = S141(10); } +/***************************************************/ + +class test5498_A {} +class test5498_B : test5498_A {} +class test5498_C : test5498_A {} + +static assert(is(typeof([test5498_B.init, test5498_C.init]) == test5498_A[])); + /***************************************************/ // 3688 @@ -3359,6 +3367,17 @@ void test3559() } } +/***************************************************/ + +extern(C++) +class C13182 +{ +} + +void test13182() +{ + scope C13182 c = new C13182(); +} /***************************************************/ // 5897 @@ -3934,26 +3953,6 @@ void test1471() deprecated @disable int bug6389; static assert(!is(typeof(bug6389 = bug6389))); -/***************************************************/ -// 4596 - -class NoGo4596 -{ - void fun() - { - static assert(!__traits(compiles, this = new NoGo4596)); - static assert(!__traits(compiles, (1?this:this) = new NoGo4596)); - static assert(!__traits(compiles, super = new Object)); - static assert(!__traits(compiles, (1?super:super) = new Object)); - } -} - -void test4596() -{ - auto n = new NoGo4596; - n.fun(); -} - /***************************************************/ void test10927() @@ -4111,7 +4110,7 @@ void test6264() S s; static assert(!is(typeof(a[] = s[]))); int*[] b; - static assert(!is(typeof(b[] = [new immutable(int)]))); + static assert(is(typeof(b[] = [new immutable(int)]))); char[] c = new char[](5); c[] = "hello"; } @@ -4236,27 +4235,6 @@ void test6293() { f6293([x]); } -/***************************************************/ -// 2774 - -int foo2774(int n){ return 0; } -static assert(foo2774.mangleof == "_D7xtest467foo2774FiZi"); - -class C2774 -{ - int foo2774(){ return 0; } -} -static assert(C2774.foo2774.mangleof == "_D7xtest465C27747foo2774MFZi"); - -template TFoo2774(T){} -static assert(TFoo2774!int.mangleof == "7xtest4615__T8TFoo2774TiZ"); - -void test2774() -{ - int foo2774(int n){ return 0; } - static assert(foo2774.mangleof == "_D7xtest468test2774FZv7foo2774MFiZi"); -} - /***************************************************/ // 3733 @@ -4951,6 +4929,22 @@ void test6836() /***************************************************/ +string func12864() { return ['a', 'b', 'c']; } + +void test12864(string s) +{ + switch (s) + { + case func12864(): + break; + + default: + break; + } +} + +/***************************************************/ + void test5448() { int[int][] aaa = [[1: 2]]; @@ -5123,6 +5117,33 @@ void test6910() alias Test6910!(i, Bag!(A)).fn func; } +/***************************************************/ + +void fun12503() +{ + string b = "abc"; + try + { + try + { + b = null; + return; + } + catch + { + } + } + finally + { + assert("abc" !is b); + } +} + +void test12503() +{ + fun12503(); +} + /***************************************************/ // 6902 @@ -5451,8 +5472,8 @@ void test7285() void test7321() { - static assert(is(typeof((){})==void function()pure nothrow @safe)); // ok - static assert(is(typeof((){return;})==void function()pure nothrow @safe)); // fail + static assert(is(typeof((){})==void function()pure nothrow @nogc @safe)); // ok + static assert(is(typeof((){return;})==void function()pure nothrow @nogc @safe)); // fail } /***************************************************/ @@ -5601,6 +5622,14 @@ mixin template ProxyOf(alias a) void test2(this Y)(){} } +/***************************************************/ + +import core.stdc.stdlib; + +void test13427(void* buffer = alloca(100)) +{ +} + /***************************************************/ // 7583 @@ -6376,7 +6405,7 @@ void test163() { shared const S* s3 = new S(); shared S* s4; - assert(!__traits(compiles, s4 = new immutable(S)())); + assert(__traits(compiles, s4 = new immutable(S)())); struct T { int x; int y; } immutable T* t; @@ -6734,6 +6763,16 @@ void test10091() auto arr = cast(ubyte[1]) S10091.e; } +/***************************************************/ + +void test12824() +{ +label: + static if (0) + { + } +} + /***************************************************/ // 9130 @@ -6828,7 +6867,8 @@ void test10634() /***************************************************/ -immutable(char)[4] bar7254(int i) { +immutable(char)[4] bar7254(int i) +{ if (i) { immutable(char)[4] r; return r; @@ -6862,6 +6902,19 @@ void test11075() static assert(!is(typeof(I11075!().x))); } +/***************************************************/ +// 11181 + +void test11181() +{ + auto a = ["a", "b"]; + + static assert(!is(typeof([a, "x"]))); + static assert(!is(typeof(true ? a : "x"))); + + static assert(!is(typeof(true ? a[0 .. $] : "x"))); + static assert(!is(typeof([a[0 .. $], "x"]))); +} /***************************************************/ // 11317 @@ -6880,29 +6933,134 @@ void test11317() } /***************************************************/ +// 12153 -struct S12044(T) +void test12153() { - void f()() - { - new T[1]; - } + int[1] i, j; + bool b = true; + (b ? i : j)[] = [4]; + assert(i == [4]); +} - bool opEquals(O)(O) - { - f(); - } +/***************************************************/ +// 12498 + +string a12498() +{ + string b; + while (b) { } + for (; b; ) { } + return ""; } -void test12044() +void test12498() { - () - { - enum E { e } - auto arr = [E.e]; - S12044!E s; - } - (); + enum t = a12498(); + string x = t; +} + +/***************************************************/ +// 12900 + +struct A12900 +{ + char[1] b; +} + +void test12900() +{ + A12900 c; + if (*c.b.ptr) + return; +} + +/***************************************************/ +// 12937 + +void test12937() +{ + void[1] sa2 = cast(void[])[cast(ubyte)1]; // ICE! + assert((cast(ubyte[])sa2[])[0] == 1); +} + +/***************************************************/ +// 13154 + +void test13154() +{ + int[3] ints = [2 , 1 , 0 , 1 ][0..3]; + float[3] floats0 = [2f , 1f , 0f , 1f ][0..3]; + float[3] floats1 = [2.0 , 1.0 , 0.0 , 1.0 ][0..3]; // fails! + float[3] floats2 = [2.0f, 1.0f, 0.0f, 1.0f][0..3]; + assert(ints == [2, 1, 0]); + assert(floats0 == [2, 1, 0]); + assert(floats1 == [2, 1, 0]); // fail! + assert(floats1 != [0, 0, 0]); // fail! + assert(floats2 == [2, 1, 0]); +} + +/***************************************************/ +// 13472 + +class A13472 +{ + int a; +} + +void test13472() +{ + A13472[] test; + test.length = 4; + auto b = test[0..2] ~ null ~ test[2..$]; + assert(b.length == 5); +} + +/***************************************************/ +// 13476 + +template ParameterTypeTuple13476(func...) +{ + static if (is(typeof(*func[0]) P == function)) + alias ParameterTypeTuple13476 = P; + else + static assert(0, "argument has no parameters"); +} + +int flag13476; + +__gshared extern(C) void function(int) nothrow someFunc13476 = &Stub13476!someFunc13476; + +extern(C) auto Stub13476(alias func)(ParameterTypeTuple13476!func args) +{ + ++flag13476; + extern(C) void function(int) nothrow impl = (i) { }; + return (func = impl)(args); +} + +__gshared extern(C) void function(int) nothrow someFunc13476Alt = &Stub13476Alt!someFunc13476AltP; +__gshared extern(C) void function(int) nothrow* someFunc13476AltP = &someFunc13476Alt; + +extern(C) auto Stub13476Alt(alias func)(int args) nothrow +{ + ++flag13476; + extern(C) void function(int) nothrow impl = (i) {}; + return (*func = impl)(args); +} + +void test13476() +{ + assert(flag13476 == 0); + + someFunc13476(42); + assert(flag13476 == 1); + someFunc13476(43); + assert(flag13476 == 1); + + someFunc13476Alt(42); + assert(flag13476 == 2); + someFunc13476Alt(43); + assert(flag13476 == 2); } /***************************************************/ @@ -7091,7 +7249,6 @@ int main() test658(); test4258(); test4539(); - test4596(); test4963(); test4031(); test5437(); @@ -7105,7 +7262,6 @@ int main() test6335(); test1687(); test6228(); - test2774(); test3733(); test4392(); test6220(); @@ -7154,12 +7310,16 @@ int main() test7871(); test7906(); test7907(); + test12503(); test8004(); test8064(); test8105(); test159(); + test12824(); test8283(); + test13182(); test8395(); + test13427(); test5749(); test8396(); test160(); @@ -7189,8 +7349,13 @@ int main() test10634(); test7254(); test11075(); + test11181(); test11317(); - test12044(); + test12153(); + test12937(); + test13154(); + test13472(); + test13476(); printf("Success\n"); return 0; diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am index ad3be20d8..b54107fdd 100644 --- a/libphobos/libdruntime/Makefile.am +++ b/libphobos/libdruntime/Makefile.am @@ -37,6 +37,7 @@ SUFFIXES = .d # Used to generate .di headers, now just copy from source.d to import/source.di $(IMPDIR): mkdir -p $(IMPDIR) + mkdir -p $(IMPDIR)/core/internal mkdir -p $(IMPDIR)/core/stdc mkdir -p $(IMPDIR)/core/sync mkdir -p $(IMPDIR)/core/sys/freebsd/sys @@ -76,15 +77,15 @@ gcc/cbridge_math.o: gcc/cbridge_math.c BASE_OBJS=object_.o RUNTIME_OBJS=rt/aaA.o rt/aApply.o rt/aApplyR.o rt/adi.o rt/arrayassign.o \ - rt/arraybyte.o rt/arraycast.o rt/arraycat.o rt/arraydouble.o \ - rt/arrayfloat.o rt/arrayint.o rt/arrayreal.o rt/arrayshort.o \ - rt/cast_.o rt/critical_.o rt/deh.o rt/dmain2.o rt/minfo.o \ - rt/memory.o rt/invariant.o rt/lifetime.o \ - rt/monitor_.o rt/obj.o rt/qsort.o rt/switch_.o rt/tlsgc.o - -CORE_OBJS=core/atomic.o core/bitop.o core/cpuid.o core/demangle.o \ - core/exception.o core/math.o core/memory.o core/runtime.o \ - core/simd.o core/thread.o core/threadasm.o core/time.o core/vararg.o \ + rt/arraycast.o rt/arraycat.o rt/cast_.o rt/critical_.o \ + rt/deh.o rt/dmain2.o rt/minfo.o rt/memory.o rt/invariant.o \ + rt/lifetime.o rt/monitor_.o rt/obj.o rt/qsort.o rt/switch_.o \ + rt/tlsgc.o + +CORE_OBJS=core/atomic.o core/bitop.o core/checkedint.o core/cpuid.o \ + core/demangle.o core/exception.o core/math.o core/memory.o \ + core/runtime.o core/simd.o core/thread.o core/threadasm.o \ + core/time.o core/vararg.o core/internal/traits.o \ core/sync/barrier.o core/sync/condition.o core/sync/config.o \ core/sync/exception.o core/sync/mutex.o core/sync/rwmutex.o \ core/sync/semaphore.o @@ -92,8 +93,9 @@ CORE_OBJS=core/atomic.o core/bitop.o core/cpuid.o core/demangle.o \ GCC_OBJS=gcc/atomics.o gcc/backtrace.o gcc/builtins.o gcc/deh.o gcc/emutls.o \ gcc/libbacktrace.o gcc/unwind/pe.o -UTIL_OBJS=rt/util/array.o rt/util/container.o rt/util/hash.o \ - rt/util/string.o rt/util/utf.o +UTIL_OBJS=rt/util/array.o rt/util/hash.o rt/util/random.o rt/util/string.o \ + rt/util/utf.o rt/util/container/array.o rt/util/container/common.o \ + rt/util/container/hashtab.o rt/util/container/treap.o TI=ti_AC.o ti_Acdouble.o ti_Acfloat.o ti_Acreal.o ti_Adouble.o ti_Afloat.o \ ti_Ag.o ti_Aint.o ti_Along.o ti_Areal.o ti_Ashort.o ti_byte.o ti_C.o \ @@ -114,6 +116,8 @@ RT_STDC_OBJS=core/stdc/config.o core/stdc/ctype.o core/stdc/errno.o \ core/stdc/stdint.o core/stdc/stddef.o core/stdc/string.o \ core/stdc/time.o core/stdc/wchar_.o +RT_LINUX_OBJS= + RT_FREEBSD_OBJS=core/sys/freebsd/execinfo.o core/sys/freebsd/sys/event.o RT_OSX_OBJS=core/sys/osx/mach/kern_return.o core/sys/osx/mach/port.o \ @@ -122,10 +126,10 @@ RT_OSX_OBJS=core/sys/osx/mach/kern_return.o core/sys/osx/mach/port.o \ RT_POSIX_OBJS=core/sys/posix/dirent.o core/sys/posix/netdb.o \ core/sys/posix/signal.o core/sys/posix/sys/ioctl.o \ - core/sys/posix/sys/select.o core/sys/posix/sys/socket.o \ - core/sys/posix/sys/stat.o core/sys/posix/sys/wait.o \ - core/sys/posix/sys/un.o core/sys/posix/sys/utsname.o \ - core/sys/posix/sys/utsname.o core/sys/posix/netinet/in_.o + core/sys/posix/sys/resource.o core/sys/posix/sys/select.o \ + core/sys/posix/sys/socket.o core/sys/posix/sys/stat.o \ + core/sys/posix/sys/utsname.o core/sys/posix/sys/wait.o \ + core/sys/posix/arpa/inet.o core/sys/posix/netinet/in_.o RT_WINDOWS_OBJS=core/sys/windows/dbghelp.o core/sys/windows/dll.o \ core/sys/windows/stacktrace.o core/sys/windows/threadaux.o \ @@ -134,9 +138,12 @@ RT_WINDOWS_OBJS=core/sys/windows/dbghelp.o core/sys/windows/dll.o \ D_GC_MODULES=@D_GC_MODULES@ # Regardless of OS, all import headers are generated. -CORE_IMPORTS=core/atomic.di core/bitop.di core/cpuid.di core/demangle.di \ - core/exception.di core/math.di core/memory.di core/runtime.di \ - core/simd.di core/time.di core/vararg.di \ +CORE_IMPORTS=core/atomic.di core/bitop.di core/checkedint.di core/cpuid.di \ + core/demangle.di core/exception.di core/math.di core/memory.di \ + core/runtime.di core/simd.di core/thread.di core/time.di \ + core/vararg.di \ + \ + core/internal/traits.di \ \ core/stdc/complex.di core/stdc/config.di core/stdc/ctype.di \ core/stdc/errno.di core/stdc/fenv.di core/stdc/float_.di \ @@ -241,10 +248,11 @@ install-data-local: libgdruntime.a $(INSTALL_HEADER) $$f $(DESTDIR)$(gdc_include_dir)/$$i; \ done; \ done - for i in core core/stdc core/sync core/sys/freebsd \ - core/sys/freebsd/sys core/sys/linux core/sys/linux/sys \ - core/sys/osx core/sys/osx/mach core/sys/posix \ - core/sys/posix/arpa core/sys/posix/net \ + for i in core core/internal core/stdc core/sync \ + core/sys/freebsd core/sys/freebsd/sys \ + core/sys/linux core/sys/linux/sys \ + core/sys/osx core/sys/osx/mach \ + core/sys/posix core/sys/posix/arpa core/sys/posix/net \ core/sys/posix/netinet core/sys/posix/sys \ core/sys/windows; do \ $(mkinstalldirs) $(DESTDIR)$(gdc_include_dir)/$$i; \ diff --git a/libphobos/libdruntime/Makefile.in b/libphobos/libdruntime/Makefile.in index 5c2888be1..2f9c0d1a7 100644 --- a/libphobos/libdruntime/Makefile.in +++ b/libphobos/libdruntime/Makefile.in @@ -228,15 +228,15 @@ toolexeclibdir = $(phobos_toolexeclibdir) SUFFIXES = .d BASE_OBJS = object_.o RUNTIME_OBJS = rt/aaA.o rt/aApply.o rt/aApplyR.o rt/adi.o rt/arrayassign.o \ - rt/arraybyte.o rt/arraycast.o rt/arraycat.o rt/arraydouble.o \ - rt/arrayfloat.o rt/arrayint.o rt/arrayreal.o rt/arrayshort.o \ - rt/cast_.o rt/critical_.o rt/deh.o rt/dmain2.o rt/minfo.o \ - rt/memory.o rt/invariant.o rt/lifetime.o \ - rt/monitor_.o rt/obj.o rt/qsort.o rt/switch_.o rt/tlsgc.o - -CORE_OBJS = core/atomic.o core/bitop.o core/cpuid.o core/demangle.o \ - core/exception.o core/math.o core/memory.o core/runtime.o \ - core/simd.o core/thread.o core/threadasm.o core/time.o core/vararg.o \ + rt/arraycast.o rt/arraycat.o rt/cast_.o rt/critical_.o \ + rt/deh.o rt/dmain2.o rt/minfo.o rt/memory.o rt/invariant.o \ + rt/lifetime.o rt/monitor_.o rt/obj.o rt/qsort.o rt/switch_.o \ + rt/tlsgc.o + +CORE_OBJS = core/atomic.o core/bitop.o core/checkedint.o core/cpuid.o \ + core/demangle.o core/exception.o core/math.o core/memory.o \ + core/runtime.o core/simd.o core/thread.o core/threadasm.o \ + core/time.o core/vararg.o core/internal/traits.o \ core/sync/barrier.o core/sync/condition.o core/sync/config.o \ core/sync/exception.o core/sync/mutex.o core/sync/rwmutex.o \ core/sync/semaphore.o @@ -244,8 +244,9 @@ CORE_OBJS = core/atomic.o core/bitop.o core/cpuid.o core/demangle.o \ GCC_OBJS = gcc/atomics.o gcc/backtrace.o gcc/builtins.o gcc/deh.o gcc/emutls.o \ gcc/libbacktrace.o gcc/unwind/pe.o -UTIL_OBJS = rt/util/array.o rt/util/container.o rt/util/hash.o \ - rt/util/string.o rt/util/utf.o +UTIL_OBJS = rt/util/array.o rt/util/hash.o rt/util/random.o rt/util/string.o \ + rt/util/utf.o rt/util/container/array.o rt/util/container/common.o \ + rt/util/container/hashtab.o rt/util/container/treap.o TI = ti_AC.o ti_Acdouble.o ti_Acfloat.o ti_Acreal.o ti_Adouble.o ti_Afloat.o \ ti_Ag.o ti_Aint.o ti_Along.o ti_Areal.o ti_Ashort.o ti_byte.o ti_C.o \ @@ -262,6 +263,7 @@ RT_STDC_OBJS = core/stdc/config.o core/stdc/ctype.o core/stdc/errno.o \ core/stdc/stdint.o core/stdc/stddef.o core/stdc/string.o \ core/stdc/time.o core/stdc/wchar_.o +RT_LINUX_OBJS = RT_FREEBSD_OBJS = core/sys/freebsd/execinfo.o core/sys/freebsd/sys/event.o RT_OSX_OBJS = core/sys/osx/mach/kern_return.o core/sys/osx/mach/port.o \ core/sys/osx/mach/semaphore.o core/sys/osx/mach/thread_act.o \ @@ -269,10 +271,10 @@ RT_OSX_OBJS = core/sys/osx/mach/kern_return.o core/sys/osx/mach/port.o \ RT_POSIX_OBJS = core/sys/posix/dirent.o core/sys/posix/netdb.o \ core/sys/posix/signal.o core/sys/posix/sys/ioctl.o \ - core/sys/posix/sys/select.o core/sys/posix/sys/socket.o \ - core/sys/posix/sys/stat.o core/sys/posix/sys/wait.o \ - core/sys/posix/sys/un.o core/sys/posix/sys/utsname.o \ - core/sys/posix/sys/utsname.o core/sys/posix/netinet/in_.o + core/sys/posix/sys/resource.o core/sys/posix/sys/select.o \ + core/sys/posix/sys/socket.o core/sys/posix/sys/stat.o \ + core/sys/posix/sys/utsname.o core/sys/posix/sys/wait.o \ + core/sys/posix/arpa/inet.o core/sys/posix/netinet/in_.o RT_WINDOWS_OBJS = core/sys/windows/dbghelp.o core/sys/windows/dll.o \ core/sys/windows/stacktrace.o core/sys/windows/threadaux.o \ @@ -280,9 +282,12 @@ RT_WINDOWS_OBJS = core/sys/windows/dbghelp.o core/sys/windows/dll.o \ # Regardless of OS, all import headers are generated. -CORE_IMPORTS = core/atomic.di core/bitop.di core/cpuid.di core/demangle.di \ - core/exception.di core/math.di core/memory.di core/runtime.di \ - core/simd.di core/time.di core/vararg.di \ +CORE_IMPORTS = core/atomic.di core/bitop.di core/checkedint.di core/cpuid.di \ + core/demangle.di core/exception.di core/math.di core/memory.di \ + core/runtime.di core/simd.di core/thread.di core/time.di \ + core/vararg.di \ + \ + core/internal/traits.di \ \ core/stdc/complex.di core/stdc/config.di core/stdc/ctype.di \ core/stdc/errno.di core/stdc/fenv.di core/stdc/float_.di \ @@ -586,6 +591,7 @@ all-local: libgdruntime.a # Used to generate .di headers, now just copy from source.d to import/source.di $(IMPDIR): mkdir -p $(IMPDIR) + mkdir -p $(IMPDIR)/core/internal mkdir -p $(IMPDIR)/core/stdc mkdir -p $(IMPDIR)/core/sync mkdir -p $(IMPDIR)/core/sys/freebsd/sys @@ -657,10 +663,11 @@ install-data-local: libgdruntime.a $(INSTALL_HEADER) $$f $(DESTDIR)$(gdc_include_dir)/$$i; \ done; \ done - for i in core core/stdc core/sync core/sys/freebsd \ - core/sys/freebsd/sys core/sys/linux core/sys/linux/sys \ - core/sys/osx core/sys/osx/mach core/sys/posix \ - core/sys/posix/arpa core/sys/posix/net \ + for i in core core/internal core/stdc core/sync \ + core/sys/freebsd core/sys/freebsd/sys \ + core/sys/linux core/sys/linux/sys \ + core/sys/osx core/sys/osx/mach \ + core/sys/posix core/sys/posix/arpa core/sys/posix/net \ core/sys/posix/netinet core/sys/posix/sys \ core/sys/windows; do \ $(mkinstalldirs) $(DESTDIR)$(gdc_include_dir)/$$i; \ diff --git a/libphobos/libdruntime/core/atomic.d b/libphobos/libdruntime/core/atomic.d index 2d3b8e7c3..85dd87d67 100644 --- a/libphobos/libdruntime/core/atomic.d +++ b/libphobos/libdruntime/core/atomic.d @@ -13,6 +13,10 @@ * (See accompanying file LICENSE or copy at * http://www.boost.org/LICENSE_1_0.txt) */ + +/* NOTE: This file has been patched from the original DMD distribution to + * work with the GDC compiler. + */ module core.atomic; version( D_InlineAsm_X86 ) @@ -69,7 +73,7 @@ version( CoreDdoc ) * The result of the operation. */ HeadUnshared!(T) atomicOp(string op, T, V1)( ref shared T val, V1 mod ) nothrow - if( __traits( compiles, mixin( "val" ~ op ~ "mod" ) ) ) + if( __traits( compiles, mixin( "*cast(T*)&val" ~ op ~ "mod" ) ) ) { return HeadUnshared!(T).init; } @@ -155,7 +159,7 @@ version( CoreDdoc ) else version( AsmX86_32 ) { HeadUnshared!(T) atomicOp(string op, T, V1)( ref shared T val, V1 mod ) nothrow - if( __traits( compiles, mixin( "val" ~ op ~ "mod" ) ) ) + if( __traits( compiles, mixin( "*cast(T*)&val" ~ op ~ "mod" ) ) ) in { // NOTE: 32 bit x86 systems support 8 byte CAS, which only requires @@ -343,22 +347,12 @@ else version( AsmX86_32 ) } - // NOTE: While x86 loads have acquire semantics for stores, it appears - // that independent loads may be reordered by some processors - // (notably the AMD64). This implies that the hoist-load barrier - // op requires an ordering instruction, which also extends this - // requirement to acquire ops (though hoist-store should not need - // one if support is added for this later). However, since no - // modern architectures will reorder dependent loads to occur - // before the load they depend on (except the Alpha), raw loads - // are actually a possible means of ordering specific sequences - // of loads in some instances. - // - // For reference, the old behavior (acquire semantics for loads) - // required a memory barrier if: ms == MemoryOrder.seq || isSinkOp!(ms) + // NOTE: x86 loads implicitly have acquire semantics so a memory + // barrier is only necessary on releases. template needsLoadBarrier( MemoryOrder ms ) { - enum bool needsLoadBarrier = ms != MemoryOrder.raw; + enum bool needsLoadBarrier = ms == MemoryOrder.seq || + isSinkOp!(ms); } @@ -633,7 +627,7 @@ else version( AsmX86_32 ) else version( AsmX86_64 ) { HeadUnshared!(T) atomicOp(string op, T, V1)( ref shared T val, V1 mod ) nothrow - if( __traits( compiles, mixin( "val" ~ op ~ "mod" ) ) ) + if( __traits( compiles, mixin( "*cast(T*)&val" ~ op ~ "mod" ) ) ) in { // NOTE: 32 bit x86 systems support 8 byte CAS, which only requires @@ -814,22 +808,12 @@ else version( AsmX86_64 ) } - // NOTE: While x86 loads have acquire semantics for stores, it appears - // that independent loads may be reordered by some processors - // (notably the AMD64). This implies that the hoist-load barrier - // op requires an ordering instruction, which also extends this - // requirement to acquire ops (though hoist-store should not need - // one if support is added for this later). However, since no - // modern architectures will reorder dependent loads to occur - // before the load they depend on (except the Alpha), raw loads - // are actually a possible means of ordering specific sequences - // of loads in some instances. - // - // For reference, the old behavior (acquire semantics for loads) - // required a memory barrier if: ms == MemoryOrder.seq || isSinkOp!(ms) + // NOTE: x86 loads implicitly have acquire semantics so a memory + // barrier is only necessary on releases. template needsLoadBarrier( MemoryOrder ms ) { - enum bool needsLoadBarrier = ms != MemoryOrder.raw; + enum bool needsLoadBarrier = ms == MemoryOrder.seq || + isSinkOp!(ms); } @@ -1087,8 +1071,8 @@ else version( GNU ) import gcc.atomics; import gcc.builtins; - HeadUnshared!(T) atomicOp(string op, T, V1)( ref shared T val, V1 mod ) - if( __traits( compiles, mixin( "val" ~ op ~ "mod" ) ) ) + HeadUnshared!(T) atomicOp(string op, T, V1)( ref shared T val, V1 mod ) nothrow + if( __traits( compiles, mixin( "*cast(T*)&val" ~ op ~ "mod" ) ) ) { // binary operators // @@ -1215,22 +1199,12 @@ else version( GNU ) } - // NOTE: While x86 loads have acquire semantics for stores, it appears - // that independent loads may be reordered by some processors - // (notably the AMD64). This implies that the hoist-load barrier - // op requires an ordering instruction, which also extends this - // requirement to acquire ops (though hoist-store should not need - // one if support is added for this later). However, since no - // modern architectures will reorder dependent loads to occur - // before the load they depend on (except the Alpha), raw loads - // are actually a possible means of ordering specific sequences - // of loads in some instances. - // - // For reference, the old behavior (acquire semantics for loads) - // required a memory barrier if: ms == MemoryOrder.seq || isSinkOp!(ms) + // NOTE: x86 loads implicitly have acquire semantics so a memory + // barrier is only necessary on releases. template needsLoadBarrier( MemoryOrder ms ) { - enum bool needsLoadBarrier = ms != MemoryOrder.raw; + enum bool needsLoadBarrier = ms == MemoryOrder.seq || + isSinkOp!(ms); } diff --git a/libphobos/libdruntime/core/bitop.d b/libphobos/libdruntime/core/bitop.d index 38ba8784c..81db59459 100644 --- a/libphobos/libdruntime/core/bitop.d +++ b/libphobos/libdruntime/core/bitop.d @@ -11,6 +11,7 @@ module core.bitop; nothrow: @safe: +@nogc: version( D_InlineAsm_X86_64 ) version = AsmX86; diff --git a/libphobos/libdruntime/core/checkedint.d b/libphobos/libdruntime/core/checkedint.d new file mode 100644 index 000000000..dfa27b726 --- /dev/null +++ b/libphobos/libdruntime/core/checkedint.d @@ -0,0 +1,500 @@ + +/********************************************** + * This module implements integral arithmetic primitives that check + * for out-of-range results. + * + * Integral arithmetic operators operate on fixed width types. + * Results that are not representable in those fixed widths are silently + * truncated to fit. + * This module offers integral arithmetic primitives that produce the + * same results, but set an 'overflow' flag when such truncation occurs. + * The setting is sticky, meaning that numerous operations can be cascaded + * and then the flag need only be checked at the end. + * Whether the operation is signed or unsigned is indicated by an 's' or 'u' + * suffix, respectively. While this could be achieved without such suffixes by + * using overloading on the signedness of the types, the suffix makes it clear + * which is happening without needing to examine the types. + * + * While the generic versions of these functions are computationally expensive + * relative to the cost of the operation itself, compiler implementations are free + * to recognize them and generate equivalent and faster code. + * + * References: $(LINK2 http://blog.regehr.org/archives/1139, Fast Integer Overflow Checks) + * Copyright: Copyright (c) Walter Bright 2014. + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Authors: Walter Bright + * Source: $(DRUNTIMESRC core/_checkedint.d) + */ + +module core.checkedint; + +nothrow: +@safe: +@nogc: +pure: + +/******************************* + * Add two signed integers, checking for overflow. + * + * The overflow is sticky, meaning a sequence of operations can + * be done and overflow need only be checked at the end. + * Params: + * x = left operand + * y = right operand + * overflow = set if an overflow occurs, is not affected otherwise + * Returns: + * the sum + */ + +int adds(int x, int y, ref bool overflow) +{ + long r = cast(long)x + cast(long)y; + if (r < int.min || r > int.max) + overflow = true; + return cast(int)r; +} + +unittest +{ + bool overflow; + assert(adds(2, 3, overflow) == 5); + assert(!overflow); + assert(adds(1, int.max - 1, overflow) == int.max); + assert(!overflow); + assert(adds(int.min + 1, -1, overflow) == int.min); + assert(!overflow); + assert(adds(int.max, 1, overflow) == int.min); + assert(overflow); + overflow = false; + assert(adds(int.min, -1, overflow) == int.max); + assert(overflow); + assert(adds(0, 0, overflow) == 0); + assert(overflow); // sticky +} + +/// ditto +long adds(long x, long y, ref bool overflow) +{ + long r = cast(ulong)x + cast(ulong)y; + if (x < 0 && y < 0 && r >= 0 || + x >= 0 && y >= 0 && r < 0) + overflow = true; + return r; +} + +unittest +{ + bool overflow; + assert(adds(2L, 3L, overflow) == 5); + assert(!overflow); + assert(adds(1L, long.max - 1, overflow) == long.max); + assert(!overflow); + assert(adds(long.min + 1, -1, overflow) == long.min); + assert(!overflow); + assert(adds(long.max, 1, overflow) == long.min); + assert(overflow); + overflow = false; + assert(adds(long.min, -1, overflow) == long.max); + assert(overflow); + assert(adds(0L, 0L, overflow) == 0); + assert(overflow); // sticky +} + + +/******************************* + * Add two unsigned integers, checking for overflow (aka carry). + * + * The overflow is sticky, meaning a sequence of operations can + * be done and overflow need only be checked at the end. + * Params: + * x = left operand + * y = right operand + * overflow = set if an overflow occurs, is not affected otherwise + * Returns: + * the sum + */ + +uint addu(uint x, uint y, ref bool overflow) +{ + uint r = x + y; + if (r < x || r < y) + overflow = true; + return r; +} + +unittest +{ + bool overflow; + assert(addu(2, 3, overflow) == 5); + assert(!overflow); + assert(addu(1, uint.max - 1, overflow) == uint.max); + assert(!overflow); + assert(addu(uint.min, -1, overflow) == uint.max); + assert(!overflow); + assert(addu(uint.max, 1, overflow) == uint.min); + assert(overflow); + overflow = false; + assert(addu(uint.min + 1, -1, overflow) == uint.min); + assert(overflow); + assert(addu(0, 0, overflow) == 0); + assert(overflow); // sticky +} + +/// ditto +ulong addu(ulong x, ulong y, ref bool overflow) +{ + ulong r = x + y; + if (r < x || r < y) + overflow = true; + return r; +} + +unittest +{ + bool overflow; + assert(addu(2L, 3L, overflow) == 5); + assert(!overflow); + assert(addu(1, ulong.max - 1, overflow) == ulong.max); + assert(!overflow); + assert(addu(ulong.min, -1L, overflow) == ulong.max); + assert(!overflow); + assert(addu(ulong.max, 1, overflow) == ulong.min); + assert(overflow); + overflow = false; + assert(addu(ulong.min + 1, -1L, overflow) == ulong.min); + assert(overflow); + assert(addu(0L, 0L, overflow) == 0); + assert(overflow); // sticky +} + + +/******************************* + * Subtract two signed integers, checking for overflow. + * + * The overflow is sticky, meaning a sequence of operations can + * be done and overflow need only be checked at the end. + * Params: + * x = left operand + * y = right operand + * overflow = set if an overflow occurs, is not affected otherwise + * Returns: + * the sum + */ + +int subs(int x, int y, ref bool overflow) +{ + long r = cast(long)x - cast(long)y; + if (r < int.min || r > int.max) + overflow = true; + return cast(int)r; +} + +unittest +{ + bool overflow; + assert(subs(2, -3, overflow) == 5); + assert(!overflow); + assert(subs(1, -int.max + 1, overflow) == int.max); + assert(!overflow); + assert(subs(int.min + 1, 1, overflow) == int.min); + assert(!overflow); + assert(subs(int.max, -1, overflow) == int.min); + assert(overflow); + overflow = false; + assert(subs(int.min, 1, overflow) == int.max); + assert(overflow); + assert(subs(0, 0, overflow) == 0); + assert(overflow); // sticky +} + +/// ditto +long subs(long x, long y, ref bool overflow) +{ + long r = cast(ulong)x - cast(ulong)y; + if (x < 0 && y >= 0 && r >= 0 || + x >= 0 && y < 0 && r < 0 || + y == long.min) + overflow = true; + return r; +} + +unittest +{ + bool overflow; + assert(subs(2L, -3L, overflow) == 5); + assert(!overflow); + assert(subs(1L, -long.max + 1, overflow) == long.max); + assert(!overflow); + assert(subs(long.min + 1, 1, overflow) == long.min); + assert(!overflow); + assert(subs(long.max, -1, overflow) == long.min); + assert(overflow); + overflow = false; + assert(subs(long.min, 1, overflow) == long.max); + assert(overflow); + assert(subs(0L, 0L, overflow) == 0); + assert(overflow); // sticky +} + +/******************************* + * Subtract two unsigned integers, checking for overflow (aka borrow). + * + * The overflow is sticky, meaning a sequence of operations can + * be done and overflow need only be checked at the end. + * Params: + * x = left operand + * y = right operand + * overflow = set if an overflow occurs, is not affected otherwise + * Returns: + * the sum + */ + +uint subu(uint x, uint y, ref bool overflow) +{ + if (x < y) + overflow = true; + return x - y; +} + +unittest +{ + bool overflow; + assert(subu(3, 2, overflow) == 1); + assert(!overflow); + assert(subu(uint.max, 1, overflow) == uint.max - 1); + assert(!overflow); + assert(subu(1, 1, overflow) == uint.min); + assert(!overflow); + assert(subu(0, 1, overflow) == uint.max); + assert(overflow); + overflow = false; + assert(subu(uint.max - 1, uint.max, overflow) == uint.max); + assert(overflow); + assert(subu(0, 0, overflow) == 0); + assert(overflow); // sticky +} + + +/// ditto +ulong subu(ulong x, ulong y, ref bool overflow) +{ + if (x < y) + overflow = true; + return x - y; +} + +unittest +{ + bool overflow; + assert(subu(3UL, 2UL, overflow) == 1); + assert(!overflow); + assert(subu(ulong.max, 1, overflow) == ulong.max - 1); + assert(!overflow); + assert(subu(1UL, 1UL, overflow) == ulong.min); + assert(!overflow); + assert(subu(0UL, 1UL, overflow) == ulong.max); + assert(overflow); + overflow = false; + assert(subu(ulong.max - 1, ulong.max, overflow) == ulong.max); + assert(overflow); + assert(subu(0UL, 0UL, overflow) == 0); + assert(overflow); // sticky +} + + +/*********************************************** + * Negate an integer. + * + * Params: + * x = operand + * overflow = set if x cannot be negated, is not affected otherwise + * Returns: + * the negation of x + */ + +int negs(int x, ref bool overflow) +{ + if (x == int.min) + overflow = true; + return -x; +} + +unittest +{ + bool overflow; + assert(negs(0, overflow) == -0); + assert(!overflow); + assert(negs(1234, overflow) == -1234); + assert(!overflow); + assert(negs(-5678, overflow) == 5678); + assert(!overflow); + assert(negs(int.min, overflow) == -int.min); + assert(overflow); + assert(negs(0, overflow) == -0); + assert(overflow); // sticky +} + +/// ditto +long negs(long x, ref bool overflow) +{ + if (x == long.min) + overflow = true; + return -x; +} + +unittest +{ + bool overflow; + assert(negs(0L, overflow) == -0); + assert(!overflow); + assert(negs(1234L, overflow) == -1234); + assert(!overflow); + assert(negs(-5678L, overflow) == 5678); + assert(!overflow); + assert(negs(long.min, overflow) == -long.min); + assert(overflow); + assert(negs(0L, overflow) == -0); + assert(overflow); // sticky +} + + +/******************************* + * Multiply two signed integers, checking for overflow. + * + * The overflow is sticky, meaning a sequence of operations can + * be done and overflow need only be checked at the end. + * Params: + * x = left operand + * y = right operand + * overflow = set if an overflow occurs, is not affected otherwise + * Returns: + * the sum + */ + +int muls(int x, int y, ref bool overflow) +{ + long r = cast(long)x * cast(long)y; + if (r < int.min || r > int.max) + overflow = true; + return cast(int)r; +} + +unittest +{ + bool overflow; + assert(muls(2, 3, overflow) == 6); + assert(!overflow); + assert(muls(-200, 300, overflow) == -60_000); + assert(!overflow); + assert(muls(1, int.max, overflow) == int.max); + assert(!overflow); + assert(muls(int.min, 1, overflow) == int.min); + assert(!overflow); + assert(muls(int.max, 2, overflow) == (int.max * 2)); + assert(overflow); + overflow = false; + assert(muls(int.min, -1, overflow) == int.min); + assert(overflow); + assert(muls(0, 0, overflow) == 0); + assert(overflow); // sticky +} + +/// ditto +long muls(long x, long y, ref bool overflow) +{ + long r = cast(ulong)x * cast(ulong)y; + if (x && (r / x) != y) + overflow = true; + return r; +} + +unittest +{ + bool overflow; + assert(muls(2L, 3L, overflow) == 6); + assert(!overflow); + assert(muls(-200L, 300L, overflow) == -60_000); + assert(!overflow); + assert(muls(1, long.max, overflow) == long.max); + assert(!overflow); + assert(muls(long.min, 1L, overflow) == long.min); + assert(!overflow); + assert(muls(long.max, 2L, overflow) == (long.max * 2)); + assert(overflow); + overflow = false; + assert(muls(long.min, -1L, overflow) == long.min); + assert(overflow); + assert(muls(0L, 0L, overflow) == 0); + assert(overflow); // sticky +} + + +/******************************* + * Multiply two unsigned integers, checking for overflow (aka carry). + * + * The overflow is sticky, meaning a sequence of operations can + * be done and overflow need only be checked at the end. + * Params: + * x = left operand + * y = right operand + * overflow = set if an overflow occurs, is not affected otherwise + * Returns: + * the sum + */ + +uint mulu(uint x, uint y, ref bool overflow) +{ + ulong r = ulong(x) * ulong(y); + if (r > uint.max) + overflow = true; + return cast(uint)r; +} + +unittest +{ + void test(uint x, uint y, uint r, bool overflow) @nogc nothrow + { + bool o; + assert(mulu(x, y, o) == r); + assert(o == overflow); + } + test(2, 3, 6, false); + test(1, uint.max, uint.max, false); + test(0, 1, 0, false); + test(0, uint.max, 0, false); + test(uint.max, 2, 2 * uint.max, true); + test(1 << 16, 1U << 16, 0, true); + + bool overflow = true; + assert(mulu(0, 0, overflow) == 0); + assert(overflow); // sticky +} + +/// ditto +ulong mulu(ulong x, ulong y, ref bool overflow) +{ + ulong r = x * y; + if (x && (r / x) != y) + overflow = true; + return r; +} + +unittest +{ + void test(ulong x, ulong y, ulong r, bool overflow) @nogc nothrow + { + bool o; + assert(mulu(x, y, o) == r); + assert(o == overflow); + } + test(2, 3, 6, false); + test(1, ulong.max, ulong.max, false); + test(0, 1, 0, false); + test(0, ulong.max, 0, false); + test(ulong.max, 2, 2 * ulong.max, true); + test(1UL << 32, 1UL << 32, 0, true); + + bool overflow = true; + assert(mulu(0UL, 0UL, overflow) == 0); + assert(overflow); // sticky +} diff --git a/libphobos/libdruntime/core/cpuid.d b/libphobos/libdruntime/core/cpuid.d index 76c6b9f98..0ac721ad3 100644 --- a/libphobos/libdruntime/core/cpuid.d +++ b/libphobos/libdruntime/core/cpuid.d @@ -63,6 +63,7 @@ module core.cpuid; @trusted: nothrow: +@nogc: // If optimizing for a particular processor, it is generally better // to identify based on features rather than model. NOTE: Normally @@ -376,7 +377,7 @@ __gshared uint max_cpuid, max_extended_cpuid; void getcacheinfoCPUID2() { // We are only interested in the data caches - void decipherCpuid2(ubyte x) { + void decipherCpuid2(ubyte x) @nogc nothrow { if (x==0) return; // Values from http://www.sandpile.org/ia32/cpuid.htm. // Includes Itanium and non-Intel CPUs. diff --git a/libphobos/libdruntime/core/demangle.d b/libphobos/libdruntime/core/demangle.d index 323beca6f..ecb725698 100644 --- a/libphobos/libdruntime/core/demangle.d +++ b/libphobos/libdruntime/core/demangle.d @@ -2,16 +2,14 @@ * The demangle module converts mangled D symbols to a representation similar * to what would have existed in code. * - * Copyright: Copyright Sean Kelly 2010 - 2010. - * License: Boost License 1.0. + * Copyright: Copyright Sean Kelly 2010 - 2014. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/_demangle.d) */ -/* Copyright Sean Kelly 2010 - 2010. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.demangle; @@ -305,7 +303,7 @@ private struct Demangle void match( const(char)[] val ) { - foreach( e; val ) + foreach(char e; val ) { test( e ); next(); @@ -478,7 +476,7 @@ private struct Demangle error( "LName must be at least 1 character" ); if( '_' != tok() && !isAlpha( tok() ) ) error( "Invalid character in LName" ); - foreach( e; buf[pos + 1 .. pos + n] ) + foreach(char e; buf[pos + 1 .. pos + n] ) { if( '_' != e && !isAlpha( e ) && !isDigit( e ) ) error( "Invalid character in LName" ); @@ -838,6 +836,9 @@ private struct Demangle FuncAttrSafe: Nf + FuncAttrNogc: + Ni + Arguments: Argument Argument Arguments @@ -926,6 +927,10 @@ private struct Demangle // the parameter list. Rewind and break. pos--; break breakFuncAttrs; + case 'i': // FuncAttrNogc + next(); + put( "@nogc " ); + continue; default: error(); } @@ -1796,8 +1801,8 @@ version(unittest) ["_D8demangle21__T2fnVHiiA2i1i2i3i4Z2fnFZv", "void demangle.fn!([1:2, 3:4]).fn()"], ["_D8demangle2fnFNgiZNgi", "inout(int) demangle.fn(inout(int))"], ["_D8demangle29__T2fnVa97Va9Va0Vu257Vw65537Z2fnFZv", "void demangle.fn!('a', '\\t', \\x00, '\\u0101', '\\U00010001').fn()"], - ["_D2gc11gctemplates56__T8mkBitmapTS3std5range13__T4iotaTiTiZ4iotaFiiZ6ResultZ8mkBitmapFNbNfPmmZv", - "nothrow @safe void gc.gctemplates.mkBitmap!(std.range.iota!(int, int).iota(int, int).Result).mkBitmap(ulong*, ulong)"], + ["_D2gc11gctemplates56__T8mkBitmapTS3std5range13__T4iotaTiTiZ4iotaFiiZ6ResultZ8mkBitmapFNbNiNfPmmZv", + "nothrow @nogc @safe void gc.gctemplates.mkBitmap!(std.range.iota!(int, int).iota(int, int).Result).mkBitmap(ulong*, ulong)"], ["_D8serenity9persister6Sqlite70__T15SqlitePersisterTS8serenity9persister6Sqlite11__unittest6FZv4TestZ15SqlitePersister12__T7opIndexZ7opIndexMFmZS8serenity9persister6Sqlite11__unittest6FZv4Test", "serenity.persister.Sqlite.__unittest6().Test serenity.persister.Sqlite.SqlitePersister!(serenity.persister.Sqlite.__unittest6().Test).SqlitePersister.opIndex!().opIndex(ulong)"], ["_D8bug100274mainFZv5localMFZi","int bug10027.main().local()"], diff --git a/libphobos/libdruntime/core/exception.d b/libphobos/libdruntime/core/exception.d index 6647900e7..66bb15345 100644 --- a/libphobos/libdruntime/core/exception.d +++ b/libphobos/libdruntime/core/exception.d @@ -9,6 +9,10 @@ * Authors: Sean Kelly and Jonathan M Davis * Source: $(DRUNTIMESRC core/_exception.d) */ + +/* NOTE: This file has been patched from the original DMD distribution to + * work with the GDC compiler. + */ module core.exception; import core.stdc.stdio; @@ -221,7 +225,7 @@ class OutOfMemoryError : Error @trusted override const string toString() { - return msg.ptr ? (cast()super).toString() : "Memory allocation failed"; + return msg.length ? (cast()super).toString() : "Memory allocation failed"; } } @@ -262,7 +266,7 @@ class InvalidMemoryOperationError : Error @trusted override const string toString() { - return msg.ptr ? (cast()super).toString() : "Invalid memory operation"; + return msg.length ? (cast()super).toString() : "Invalid memory operation"; } } @@ -483,7 +487,7 @@ extern (C) void onRangeError( string file = __FILE__, size_t line = __LINE__ ) @ * Throws: * FinalizeError. */ -extern (C) void onFinalizeError( ClassInfo info, Exception e, string file = __FILE__, size_t line = __LINE__ ) @safe pure nothrow +extern (C) void onFinalizeError( ClassInfo info, Throwable e, string file = __FILE__, size_t line = __LINE__ ) @safe pure nothrow { throw new FinalizeError( info, file, line, e ); } @@ -509,7 +513,7 @@ extern (C) void onHiddenFuncError( Object o ) @safe pure nothrow * Throws: * OutOfMemoryError. */ -extern (C) void onOutOfMemoryError() @trusted pure nothrow +extern (C) void onOutOfMemoryError(void* pretend_sideffect = null) @trusted pure nothrow /* dmd @@@BUG11461@@@ */ { // NOTE: Since an out of memory condition exists, no allocation must occur // while generating this object. @@ -524,7 +528,7 @@ extern (C) void onOutOfMemoryError() @trusted pure nothrow * Throws: * InvalidMemoryOperationError. */ -extern (C) void onInvalidMemoryOperationError() @trusted pure nothrow +extern (C) void onInvalidMemoryOperationError(void* pretend_sideffect = null) @trusted pure nothrow /* dmd @@@BUG11461@@@ */ { // The same restriction applies as for onOutOfMemoryError. The GC is in an // undefined state, thus no allocation must occur while generating this object. @@ -590,7 +594,7 @@ extern (C) /* One of these three is called upon an assert() fail. */ - void _d_assertm(ModuleInfo* m, uint line) + void _d_assertm(immutable(ModuleInfo)* m, uint line) { onAssertError(m.name, line); } @@ -607,7 +611,7 @@ extern (C) /* One of these three is called upon an assert() fail inside of a unittest block */ - void _d_unittestm(ModuleInfo* m, uint line) + void _d_unittestm(immutable(ModuleInfo)* m, uint line) { _d_unittest(m.name, line); } @@ -624,7 +628,7 @@ extern (C) /* Called when an array index is out of bounds */ - void _d_array_bounds(ModuleInfo* m, uint line) + void _d_array_bounds(immutable(ModuleInfo)* m, uint line) { onRangeError(m.name, line); } @@ -636,7 +640,7 @@ extern (C) /* Called when a switch statement has no DefaultStatement, yet none of the cases match */ - void _d_switch_errorm(ModuleInfo* m, uint line) + void _d_switch_errorm(immutable(ModuleInfo)* m, uint line) { onSwitchError(m.name, line); } @@ -675,5 +679,3 @@ extern (C) } } } - - diff --git a/libphobos/libdruntime/core/internal/traits.d b/libphobos/libdruntime/core/internal/traits.d new file mode 100644 index 000000000..13268f3bc --- /dev/null +++ b/libphobos/libdruntime/core/internal/traits.d @@ -0,0 +1,73 @@ +/** + * Contains traits for runtime internal usage. + * + * Copyright: Copyright Digital Mars 2014 -. + * License: Boost License 1.0. + * Authors: Martin Nowak + * Source: $(DRUNTIMESRC core/internal/_traits.d) + */ +module core.internal.traits; + +/// taken from std.typetuple.TypeTuple +template TypeTuple(TList...) +{ + alias TypeTuple = TList; +} + +T trustedCast(T, U)(auto ref U u) @trusted pure nothrow +{ + return cast(T)u; +} + +template Unconst(T) +{ + static if (is(T U == immutable U)) alias Unconst = U; + else static if (is(T U == inout const U)) alias Unconst = U; + else static if (is(T U == inout U)) alias Unconst = U; + else static if (is(T U == const U)) alias Unconst = U; + else alias Unconst = T; +} + +/// taken from std.traits.Unqual +template Unqual(T) +{ + version (none) // Error: recursive alias declaration @@@BUG1308@@@ + { + static if (is(T U == const U)) alias Unqual = Unqual!U; + else static if (is(T U == immutable U)) alias Unqual = Unqual!U; + else static if (is(T U == inout U)) alias Unqual = Unqual!U; + else static if (is(T U == shared U)) alias Unqual = Unqual!U; + else alias Unqual = T; + } + else // workaround + { + static if (is(T U == immutable U)) alias Unqual = U; + else static if (is(T U == shared inout const U)) alias Unqual = U; + else static if (is(T U == shared inout U)) alias Unqual = U; + else static if (is(T U == shared const U)) alias Unqual = U; + else static if (is(T U == shared U)) alias Unqual = U; + else static if (is(T U == inout const U)) alias Unqual = U; + else static if (is(T U == inout U)) alias Unqual = U; + else static if (is(T U == const U)) alias Unqual = U; + else alias Unqual = T; + } +} + +/// used to declare an extern(D) function that is defined in a different module +template externDFunc(string fqn, T:FT*, FT) if(is(FT == function)) +{ + static if (is(FT RT == return) && is(FT Args == function)) + { + import core.demangle : mangleFunc; + enum decl = { + string s = "extern(D) RT bug13050(Args)"; + foreach (attr; __traits(getFunctionAttributes, FT)) + s ~= " " ~ attr; + return s ~ ";"; + }(); + pragma(mangle, mangleFunc!T(fqn)) mixin(decl); + alias externDFunc = bug13050; + } + else + static assert(0); +} diff --git a/libphobos/libdruntime/core/math.d b/libphobos/libdruntime/core/math.d index 693cd761b..38418fcf2 100644 --- a/libphobos/libdruntime/core/math.d +++ b/libphobos/libdruntime/core/math.d @@ -26,6 +26,7 @@ module core.math; public: +@nogc: /*********************************** * Returns cosine of x. x is in radians. diff --git a/libphobos/libdruntime/core/memory.d b/libphobos/libdruntime/core/memory.d index 5ec946d26..667505f86 100644 --- a/libphobos/libdruntime/core/memory.d +++ b/libphobos/libdruntime/core/memory.d @@ -9,7 +9,7 @@ * * Notes_to_implementors: * $(UL - * $(LI On POSIX systems, the signals SIGUSR1 and SIGUSR2 and reserved + * $(LI On POSIX systems, the signals SIGUSR1 and SIGUSR2 are reserved * by this module for use in the garbage collector implementation. * Typically, they will be used to stop and resume other threads * when performing a collection, but an implementation may choose @@ -95,11 +95,11 @@ private extern (C) uint gc_setAttr( void* p, uint a ) pure nothrow; extern (C) uint gc_clrAttr( void* p, uint a ) pure nothrow; - extern (C) void* gc_malloc( size_t sz, uint ba = 0 ) pure nothrow; - extern (C) void* gc_calloc( size_t sz, uint ba = 0 ) pure nothrow; - extern (C) BlkInfo_ gc_qalloc( size_t sz, uint ba = 0 ) pure nothrow; - extern (C) void* gc_realloc( void* p, size_t sz, uint ba = 0 ) pure nothrow; - extern (C) size_t gc_extend( void* p, size_t mx, size_t sz ) pure nothrow; + extern (C) void* gc_malloc( size_t sz, uint ba = 0, const TypeInfo = null ) pure nothrow; + extern (C) void* gc_calloc( size_t sz, uint ba = 0, const TypeInfo = null ) pure nothrow; + extern (C) BlkInfo_ gc_qalloc( size_t sz, uint ba = 0, const TypeInfo = null ) pure nothrow; + extern (C) void* gc_realloc( void* p, size_t sz, uint ba = 0, const TypeInfo = null ) pure nothrow; + extern (C) size_t gc_extend( void* p, size_t mx, size_t sz, const TypeInfo = null ) pure nothrow; extern (C) size_t gc_reserve( size_t sz ) nothrow; extern (C) void gc_free( void* p ) pure nothrow; @@ -116,10 +116,11 @@ private extern (C) BlkInfo_ gc_query( void* p ) pure nothrow; extern (C) void gc_addRoot( in void* p ) nothrow; - extern (C) void gc_addRange( in void* p, size_t sz ) nothrow; + extern (C) void gc_addRange( in void* p, size_t sz, const TypeInfo ti = null ) nothrow; extern (C) void gc_removeRoot( in void* p ) nothrow; extern (C) void gc_removeRange( in void* p ) nothrow; + extern (C) void gc_runFinalizers( in void[] segment ); } @@ -324,6 +325,8 @@ struct GC * Params: * sz = The desired allocation size in bytes. * ba = A bitmask of the attributes to set on this block. + * ti = TypeInfo to describe the memory. The GC might use this information + * to improve scanning for pointers or to call finalizers. * * Returns: * A reference to the allocated memory or null if insufficient memory @@ -332,9 +335,9 @@ struct GC * Throws: * OutOfMemoryError on allocation failure. */ - static void* malloc( size_t sz, uint ba = 0 ) pure nothrow + static void* malloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) pure nothrow { - return gc_malloc( sz, ba ); + return gc_malloc( sz, ba, ti ); } @@ -348,6 +351,8 @@ struct GC * Params: * sz = The desired allocation size in bytes. * ba = A bitmask of the attributes to set on this block. + * ti = TypeInfo to describe the memory. The GC might use this information + * to improve scanning for pointers or to call finalizers. * * Returns: * Information regarding the allocated memory block or BlkInfo.init on @@ -356,9 +361,9 @@ struct GC * Throws: * OutOfMemoryError on allocation failure. */ - static BlkInfo qalloc( size_t sz, uint ba = 0 ) pure nothrow + static BlkInfo qalloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) pure nothrow { - return gc_qalloc( sz, ba ); + return gc_qalloc( sz, ba, ti ); } @@ -373,6 +378,8 @@ struct GC * Params: * sz = The desired allocation size in bytes. * ba = A bitmask of the attributes to set on this block. + * ti = TypeInfo to describe the memory. The GC might use this information + * to improve scanning for pointers or to call finalizers. * * Returns: * A reference to the allocated memory or null if insufficient memory @@ -381,9 +388,9 @@ struct GC * Throws: * OutOfMemoryError on allocation failure. */ - static void* calloc( size_t sz, uint ba = 0 ) pure nothrow + static void* calloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) pure nothrow { - return gc_calloc( sz, ba ); + return gc_calloc( sz, ba, ti ); } @@ -411,6 +418,8 @@ struct GC * p = A pointer to the root of a valid memory block or to null. * sz = The desired allocation size in bytes. * ba = A bitmask of the attributes to set on this block. + * ti = TypeInfo to describe the memory. The GC might use this information + * to improve scanning for pointers or to call finalizers. * * Returns: * A reference to the allocated memory on success or null if sz is @@ -419,9 +428,22 @@ struct GC * Throws: * OutOfMemoryError on allocation failure. */ - static void* realloc( void* p, size_t sz, uint ba = 0 ) pure nothrow + static void* realloc( void* p, size_t sz, uint ba = 0, const TypeInfo ti = null ) pure nothrow { - return gc_realloc( p, sz, ba ); + return gc_realloc( p, sz, ba, ti ); + } + + /// Issue 13111 + unittest + { + enum size1 = 1 << 11 + 1; // page in large object pool + enum size2 = 1 << 22 + 1; // larger than large object pool size + + auto data1 = cast(ubyte*)GC.calloc(size1); + auto data2 = cast(ubyte*)GC.realloc(data1, size2); + + BlkInfo info = query(data2); + assert(info.size >= size2); } @@ -436,6 +458,9 @@ struct GC * p = A pointer to the root of a valid memory block or to null. * mx = The minimum extension size in bytes. * sz = The desired extension size in bytes. + * ti = TypeInfo to describe the full memory block. The GC might use + * this information to improve scanning for pointers or to + * call finalizers. * * Returns: * The size in bytes of the extended memory block referenced by p or zero @@ -447,9 +472,9 @@ struct GC * as an indicator of success. $(LREF capacity) should be used to * retrieve actual useable slice capacity. */ - static size_t extend( void* p, size_t mx, size_t sz ) pure nothrow + static size_t extend( void* p, size_t mx, size_t sz, const TypeInfo ti = null ) pure nothrow { - return gc_extend( p, mx, sz ); + return gc_extend( p, mx, sz, ti ); } /// Standard extending unittest @@ -684,6 +709,8 @@ struct GC * p = A pointer to a valid memory address or to null. * sz = The size in bytes of the block to add. If sz is zero then the * no operation will occur. If p is null then sz must be zero. + * ti = TypeInfo to describe the memory. The GC might use this information + * to improve scanning for pointers or to call finalizers * * Example: * --- @@ -698,9 +725,9 @@ struct GC * // rawMemory will be recognized on collection. * --- */ - static void addRange( in void* p, size_t sz ) nothrow /* FIXME pure */ + static void addRange( in void* p, size_t sz, const TypeInfo ti = null ) nothrow /* FIXME pure */ { - gc_addRange( p, sz ); + gc_addRange( p, sz, ti ); } @@ -717,4 +744,20 @@ struct GC { gc_removeRange( p ); } + + + /** + * Runs any finalizer that is located in address range of the + * given code segment. This is used before unloading shared + * libraries. All matching objects which have a finalizer in this + * code segment are assumed to be dead, using them while or after + * calling this method has undefined behavior. + * + * Params: + * segment = address range of a code segment. + */ + static void runFinalizers( in void[] segment ) + { + gc_runFinalizers( segment ); + } } diff --git a/libphobos/libdruntime/core/runtime.d b/libphobos/libdruntime/core/runtime.d index 46d789017..46b8bf4a2 100644 --- a/libphobos/libdruntime/core/runtime.d +++ b/libphobos/libdruntime/core/runtime.d @@ -12,6 +12,10 @@ * (See accompanying file LICENSE or copy at * http://www.boost.org/LICENSE_1_0.txt) */ + +/* NOTE: This file has been patched from the original DMD distribution to + * work with the GDC compiler. + */ module core.runtime; version (Windows) import core.stdc.wchar_ : wchar_t; @@ -503,8 +507,6 @@ Throwable.TraceInfo defaultTraceHandler( void* ptr = null ) { this() { - static enum MAXFRAMES = 128; - void*[MAXFRAMES] callstack; numframes = 0; //backtrace( callstack, MAXFRAMES ); if (numframes < 2) // backtrace() failed, do it ourselves { @@ -536,12 +538,6 @@ Throwable.TraceInfo defaultTraceHandler( void* ptr = null ) } } } - framelist = backtrace_symbols( callstack.ptr, numframes ); - } - - ~this() - { - free( framelist ); } override int opApply( scope int delegate(ref const(char[])) dg ) const @@ -573,6 +569,9 @@ Throwable.TraceInfo defaultTraceHandler( void* ptr = null ) } int ret = 0; + const framelist = backtrace_symbols( callstack.ptr, numframes ); + scope(exit) free(cast(void*) framelist); + for( int i = FIRSTFRAME; i < numframes; ++i ) { char[4096] fixbuf; @@ -596,7 +595,8 @@ Throwable.TraceInfo defaultTraceHandler( void* ptr = null ) private: int numframes; - char** framelist; + static enum MAXFRAMES = 128; + void*[MAXFRAMES] callstack = void; private: const(char)[] fixline( const(char)[] buf, ref char[4096] fixbuf ) const @@ -658,7 +658,7 @@ Throwable.TraceInfo defaultTraceHandler( void* ptr = null ) } assert(symBeg < buf.length && symEnd < buf.length); - assert(symBeg < symEnd); + assert(symBeg <= symEnd); enum min = (size_t a, size_t b) => a <= b ? a : b; if (symBeg == symEnd || symBeg >= fixbuf.length) diff --git a/libphobos/libdruntime/core/simd.d b/libphobos/libdruntime/core/simd.d index 0b88db3d0..0b27d674c 100644 --- a/libphobos/libdruntime/core/simd.d +++ b/libphobos/libdruntime/core/simd.d @@ -10,11 +10,15 @@ * Authors: $(WEB digitalmars.com, Walter Bright), */ +/* NOTE: This file has been patched from the original DMD distribution to + * work with the GDC compiler. + */ module core.simd; pure: nothrow: @safe: +@nogc: /******************************* * Create a vector type. @@ -37,38 +41,38 @@ template Vector(T) /* Handy aliases */ -alias Vector!(void[8]) void8; /// -alias Vector!(float[2]) float2; /// -alias Vector!(byte[8]) byte8; /// -alias Vector!(ubyte[8]) ubyte8; /// -alias Vector!(short[4]) short4; /// -alias Vector!(ushort[4]) ushort4; /// -alias Vector!(int[2]) int2; /// -alias Vector!(uint[2]) uint2; /// - -alias Vector!(void[16]) void16; /// -alias Vector!(double[2]) double2; /// -alias Vector!(float[4]) float4; /// -alias Vector!(byte[16]) byte16; /// -alias Vector!(ubyte[16]) ubyte16; /// -alias Vector!(short[8]) short8; /// -alias Vector!(ushort[8]) ushort8; /// -alias Vector!(int[4]) int4; /// -alias Vector!(uint[4]) uint4; /// -alias Vector!(long[2]) long2; /// -alias Vector!(ulong[2]) ulong2; /// - -alias Vector!(void[32]) void32; /// -alias Vector!(double[4]) double4; /// -alias Vector!(float[8]) float8; /// -alias Vector!(byte[32]) byte32; /// -alias Vector!(ubyte[32]) ubyte32; /// -alias Vector!(short[16]) short16; /// -alias Vector!(ushort[16]) ushort16; /// -alias Vector!(int[8]) int8; /// -alias Vector!(uint[8]) uint8; /// -alias Vector!(long[4]) long4; /// -alias Vector!(ulong[4]) ulong4; /// +static if (is(Vector!(void[8]))) alias Vector!(void[8]) void8; /// +static if (is(Vector!(float[2]))) alias Vector!(float[2]) float2; /// +static if (is(Vector!(byte[8]))) alias Vector!(byte[8]) byte8; /// +static if (is(Vector!(ubyte[8]))) alias Vector!(ubyte[8]) ubyte8; /// +static if (is(Vector!(short[4]))) alias Vector!(short[4]) short4; /// +static if (is(Vector!(ushort[4]))) alias Vector!(ushort[4]) ushort4; /// +static if (is(Vector!(int[2]))) alias Vector!(int[2]) int2; /// +static if (is(Vector!(uint[2]))) alias Vector!(uint[2]) uint2; /// + +static if (is(Vector!(void[16]))) alias Vector!(void[16]) void16; /// +static if (is(Vector!(double[2]))) alias Vector!(double[2]) double2; /// +static if (is(Vector!(float[4]))) alias Vector!(float[4]) float4; /// +static if (is(Vector!(byte[16]))) alias Vector!(byte[16]) byte16; /// +static if (is(Vector!(ubyte[16]))) alias Vector!(ubyte[16]) ubyte16; /// +static if (is(Vector!(short[8]))) alias Vector!(short[8]) short8; /// +static if (is(Vector!(ushort[8]))) alias Vector!(ushort[8]) ushort8; /// +static if (is(Vector!(int[4]))) alias Vector!(int[4]) int4; /// +static if (is(Vector!(uint[4]))) alias Vector!(uint[4]) uint4; /// +static if (is(Vector!(long[2]))) alias Vector!(long[2]) long2; /// +static if (is(Vector!(ulong[2]))) alias Vector!(ulong[2]) ulong2; /// + +static if (is(Vector!(void[32]))) alias Vector!(void[32]) void32; /// +static if (is(Vector!(double[4]))) alias Vector!(double[4]) double4; /// +static if (is(Vector!(float[8]))) alias Vector!(float[8]) float8; /// +static if (is(Vector!(byte[32]))) alias Vector!(byte[32]) byte32; /// +static if (is(Vector!(ubyte[32]))) alias Vector!(ubyte[32]) ubyte32; /// +static if (is(Vector!(short[16]))) alias Vector!(short[16]) short16; /// +static if (is(Vector!(ushort[16]))) alias Vector!(ushort[16]) ushort16; /// +static if (is(Vector!(int[8]))) alias Vector!(int[8]) int8; /// +static if (is(Vector!(uint[8]))) alias Vector!(uint[8]) uint8; /// +static if (is(Vector!(long[4]))) alias Vector!(long[4]) long4; /// +static if (is(Vector!(ulong[4]))) alias Vector!(ulong[4]) ulong4; /// version ( D_SIMD ) { diff --git a/libphobos/libdruntime/core/stdc/complex.d b/libphobos/libdruntime/core/stdc/complex.d index f99c7ce1d..fedf29554 100644 --- a/libphobos/libdruntime/core/stdc/complex.d +++ b/libphobos/libdruntime/core/stdc/complex.d @@ -2,21 +2,20 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/stdc/_complex.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.stdc.complex; extern (C): @trusted: // All of these operate on floating point values only. nothrow: +@nogc: alias creal complex; alias ireal imaginary; diff --git a/libphobos/libdruntime/core/stdc/config.d b/libphobos/libdruntime/core/stdc/config.d index 5ac5c4ea8..a7492695c 100644 --- a/libphobos/libdruntime/core/stdc/config.d +++ b/libphobos/libdruntime/core/stdc/config.d @@ -2,21 +2,24 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) + * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/stdc/_config.d) * Authors: Sean Kelly * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) +/* NOTE: This file has been patched from the original DMD distribution to + * work with the GDC compiler. */ module core.stdc.config; extern (C): @trusted: // Types only. nothrow: +@nogc: version( GNU ) { diff --git a/libphobos/libdruntime/core/stdc/ctype.d b/libphobos/libdruntime/core/stdc/ctype.d index 6aa77ae22..6cb80a96c 100644 --- a/libphobos/libdruntime/core/stdc/ctype.d +++ b/libphobos/libdruntime/core/stdc/ctype.d @@ -2,21 +2,20 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/stdc/_ctype.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.stdc.ctype; extern (C): @trusted: // All of these operate on integers only. nothrow: +@nogc: pure int isalnum(int c); pure int isalpha(int c); diff --git a/libphobos/libdruntime/core/stdc/errno.d b/libphobos/libdruntime/core/stdc/errno.d index b3bb2139c..9b99d81eb 100644 --- a/libphobos/libdruntime/core/stdc/errno.d +++ b/libphobos/libdruntime/core/stdc/errno.d @@ -2,20 +2,19 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly, Alex Rønne Petersen + * Source: $(DRUNTIMESRC core/stdc/_errno.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.stdc.errno; @trusted: // Only manipulates errno. nothrow: +@nogc: @property int errno() { return getErrno(); } @property int errno(int n) { return setErrno(n); } @@ -164,6 +163,7 @@ else version( linux ) enum EPROTONOSUPPORT = 93; // Protocol not supported enum ESOCKTNOSUPPORT = 94; // Socket type not supported enum EOPNOTSUPP = 95; // Operation not supported on transport endpoint + enum ENOTSUP = EOPNOTSUPP; enum EPFNOSUPPORT = 96; // Protocol family not supported enum EAFNOSUPPORT = 97; // Address family not supported by protocol enum EADDRINUSE = 98; // Address already in use @@ -504,6 +504,145 @@ else version (Solaris) enum EINPROGRESS = 150 /* operation now in progress */; enum ESTALE = 151 /* Stale NFS file handle */; } +else version( Android ) +{ + version(X86) + { + enum EPERM = 1; + enum ENOENT = 2; + enum ESRCH = 3; + enum EINTR = 4; + enum EIO = 5; + enum ENXIO = 6; + enum E2BIG = 7; + enum ENOEXEC = 8; + enum EBADF = 9; + enum ECHILD = 10; + enum EAGAIN = 11; + enum ENOMEM = 12; + enum EACCES = 13; + enum EFAULT = 14; + enum ENOTBLK = 15; + enum EBUSY = 16; + enum EEXIST = 17; + enum EXDEV = 18; + enum ENODEV = 19; + enum ENOTDIR = 20; + enum EISDIR = 21; + enum EINVAL = 22; + enum ENFILE = 23; + enum EMFILE = 24; + enum ENOTTY = 25; + enum ETXTBSY = 26; + enum EFBIG = 27; + enum ENOSPC = 28; + enum ESPIPE = 29; + enum EROFS = 30; + enum EMLINK = 31; + enum EPIPE = 32; + enum EDOM = 33; + enum ERANGE = 34; + enum EDEADLK = 35; + enum ENAMETOOLONG = 36; + enum ENOLCK = 37; + enum ENOSYS = 38; + enum ENOTEMPTY = 39; + enum ELOOP = 40; + enum ENOMSG = 42; + enum EIDRM = 43; + enum ECHRNG = 44; + enum EL2NSYNC = 45; + enum EL3HLT = 46; + enum EL3RST = 47; + enum ELNRNG = 48; + enum EUNATCH = 49; + enum ENOCSI = 50; + enum EL2HLT = 51; + enum EBADE = 52; + enum EBADR = 53; + enum EXFULL = 54; + enum ENOANO = 55; + enum EBADRQC = 56; + enum EBADSLT = 57; + enum EBFONT = 59; + enum ENOSTR = 60; + enum ENODATA = 61; + enum ETIME = 62; + enum ENOSR = 63; + enum ENONET = 64; + enum ENOPKG = 65; + enum EREMOTE = 66; + enum ENOLINK = 67; + enum EADV = 68; + enum ESRMNT = 69; + enum ECOMM = 70; + enum EPROTO = 71; + enum EMULTIHOP = 72; + enum EDOTDOT = 73; + enum EBADMSG = 74; + enum EOVERFLOW = 75; + enum ENOTUNIQ = 76; + enum EBADFD = 77; + enum EREMCHG = 78; + enum ELIBACC = 79; + enum ELIBBAD = 80; + enum ELIBSCN = 81; + enum ELIBMAX = 82; + enum ELIBEXEC = 83; + enum EILSEQ = 84; + enum ERESTART = 85; + enum ESTRPIPE = 86; + enum EUSERS = 87; + enum ENOTSOCK = 88; + enum EDESTADDRREQ = 89; + enum EMSGSIZE = 90; + enum EPROTOTYPE = 91; + enum ENOPROTOOPT = 92; + enum EPROTONOSUPPORT = 93; + enum ESOCKTNOSUPPORT = 94; + enum EOPNOTSUPP = 95; + enum EPFNOSUPPORT = 96; + enum EAFNOSUPPORT = 97; + enum EADDRINUSE = 98; + enum EADDRNOTAVAIL = 99; + enum ENETDOWN = 100; + enum ENETUNREACH = 101; + enum ENETRESET = 102; + enum ECONNABORTED = 103; + enum ECONNRESET = 104; + enum ENOBUFS = 105; + enum EISCONN = 106; + enum ENOTCONN = 107; + enum ESHUTDOWN = 108; + enum ETOOMANYREFS = 109; + enum ETIMEDOUT = 110; + enum ECONNREFUSED = 111; + enum EHOSTDOWN = 112; + enum EHOSTUNREACH = 113; + enum EALREADY = 114; + enum EINPROGRESS = 115; + enum ESTALE = 116; + enum EUCLEAN = 117; + enum ENOTNAM = 118; + enum ENAVAIL = 119; + enum EISNAM = 120; + enum EREMOTEIO = 121; + enum EDQUOT = 122; + enum ENOMEDIUM = 123; + enum EMEDIUMTYPE = 124; + enum ECANCELED = 125; + enum ENOKEY = 126; + enum EKEYEXPIRED = 127; + enum EKEYREVOKED = 128; + enum EKEYREJECTED = 129; + enum EOWNERDEAD = 130; + enum ENOTRECOVERABLE = 131; + } + else + { + static assert(false, "Architecture not supported."); + } +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/stdc/errno_.c b/libphobos/libdruntime/core/stdc/errno_.c index 5443762aa..49e9ed436 100644 --- a/libphobos/libdruntime/core/stdc/errno_.c +++ b/libphobos/libdruntime/core/stdc/errno_.c @@ -1,16 +1,14 @@ /** - * This file contains wrapper functions for macro-defined C rouines. + * This file contains wrapper functions for macro-defined C routines. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/stdc/errno.c) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ #include diff --git a/libphobos/libdruntime/core/stdc/fenv.d b/libphobos/libdruntime/core/stdc/fenv.d index ad87e1ceb..b49f4ec13 100644 --- a/libphobos/libdruntime/core/stdc/fenv.d +++ b/libphobos/libdruntime/core/stdc/fenv.d @@ -2,21 +2,20 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/stdc/_fenv.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.stdc.fenv; extern (C): @system: nothrow: +@nogc: version( Windows ) { @@ -75,7 +74,27 @@ else version( linux ) alias fexcept_t = ushort; } - // https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/arm/bits/fenv.h;hb=HEAD + // https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/mips/bits/fenv.h + else version (MIPS32) + { + struct fenv_t + { + uint __fp_control_register; + } + + alias fexcept_t = ushort; + } + // https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/mips/bits/fenv.h + else version (MIPS64) + { + struct fenv_t + { + uint __fp_control_register; + } + + alias fexcept_t = ushort; + } + // https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/arm/bits/fenv.h else version (ARM) { struct fenv_t @@ -85,6 +104,12 @@ else version( linux ) alias fexcept_t = uint; } + // https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/powerpc/bits/fenv.h + else version (PPC64) + { + alias fenv_t = double; + alias fexcept_t = uint; + } else { static assert(0, "Unimplemented architecture"); @@ -124,6 +149,27 @@ else version ( FreeBSD ) alias ushort fexcept_t; } +else version( Android ) +{ + version(X86) + { + struct fenv_t + { + ushort __control; + ushort __mxcsr_hi; + ushort __status; + ushort __mxcsr_lo; + uint __tag; + byte[16] __other; + } + + alias ushort fexcept_t; + } + else + { + static assert(false, "Architecture not supported."); + } +} else { static assert( false, "Unsupported platform" ); @@ -163,6 +209,11 @@ else version( FreeBSD ) private extern const fenv_t __fe_dfl_env; const fenv_t* FE_DFL_ENV = &__fe_dfl_env; } +else version( Android ) +{ + private extern const fenv_t __fe_dfl_env; + const fenv_t* FE_DFL_ENV = &__fe_dfl_env; +} else { static assert( false, "Unsupported platform" ); diff --git a/libphobos/libdruntime/core/stdc/float_.d b/libphobos/libdruntime/core/stdc/float_.d index c82c605d8..a79b31c29 100644 --- a/libphobos/libdruntime/core/stdc/float_.d +++ b/libphobos/libdruntime/core/stdc/float_.d @@ -2,21 +2,20 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/stdc/_float_.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.stdc.float_; extern (C): @trusted: // Constants only. nothrow: +@nogc: enum FLT_ROUNDS = 1; enum FLT_EVAL_METHOD = 2; diff --git a/libphobos/libdruntime/core/stdc/inttypes.d b/libphobos/libdruntime/core/stdc/inttypes.d index 5d4dd880d..9b3625990 100644 --- a/libphobos/libdruntime/core/stdc/inttypes.d +++ b/libphobos/libdruntime/core/stdc/inttypes.d @@ -2,16 +2,14 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/stdc/_inttypes.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.stdc.inttypes; public import core.stdc.stddef; // for wchar_t @@ -20,6 +18,7 @@ public import core.stdc.stdint; // required by spec extern (C): @trusted: // Types and constants only. nothrow: +@nogc: struct imaxdiv_t { diff --git a/libphobos/libdruntime/core/stdc/limits.d b/libphobos/libdruntime/core/stdc/limits.d index 3f8f8cab2..9aaa48dc7 100644 --- a/libphobos/libdruntime/core/stdc/limits.d +++ b/libphobos/libdruntime/core/stdc/limits.d @@ -2,16 +2,14 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/stdc/_limits.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.stdc.limits; private import core.stdc.config; @@ -19,6 +17,7 @@ private import core.stdc.config; extern (C): @trusted: // Constants only. nothrow: +@nogc: enum CHAR_BIT = 8; enum SCHAR_MIN = byte.min; diff --git a/libphobos/libdruntime/core/stdc/locale.d b/libphobos/libdruntime/core/stdc/locale.d index 80fa97ccc..9c9809821 100644 --- a/libphobos/libdruntime/core/stdc/locale.d +++ b/libphobos/libdruntime/core/stdc/locale.d @@ -2,21 +2,20 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/stdc/_locale.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.stdc.locale; extern (C): @trusted: // Only setlocale operates on C strings. nothrow: +@nogc: struct lconv { @@ -91,6 +90,25 @@ else version(FreeBSD) enum LC_TIME = 5; enum LC_MESSAGES = 6; } +else version(Android) +{ + enum + { + LC_CTYPE = 0, + LC_NUMERIC = 1, + LC_TIME = 2, + LC_COLLATE = 3, + LC_MONETARY = 4, + LC_MESSAGES = 5, + LC_ALL = 6, + LC_PAPER = 7, + LC_NAME = 8, + LC_ADDRESS = 9, + LC_TELEPHONE = 10, + LC_MEASUREMENT = 11, + LC_IDENTIFICATION = 12, + } +} else { static assert(false); diff --git a/libphobos/libdruntime/core/stdc/math.d b/libphobos/libdruntime/core/stdc/math.d index 505ad2bd4..62a62bcbf 100644 --- a/libphobos/libdruntime/core/stdc/math.d +++ b/libphobos/libdruntime/core/stdc/math.d @@ -16,6 +16,7 @@ private import core.stdc.config; extern (C): @trusted: // All functions here operate on floating point and integer values only. nothrow: +@nogc: alias float float_t; alias double double_t; @@ -104,12 +105,12 @@ version( none ) version( DigitalMars ) { version( Win32 ) - version = DigitalMarsWin32; + version = DMC_RUNTIME; version( Win64 ) - version = DigitalMarsWin64; // just to get it to compile for the moment - fix later + version = MSVC_RUNTIME; // just to get it to compile for the moment - fix later } -version( DigitalMarsWin32 ) +version( DMC_RUNTIME ) { enum { @@ -178,7 +179,7 @@ version( DigitalMarsWin32 ) } } } -else version( DigitalMarsWin64 ) +else version( MSVC_RUNTIME ) { enum { @@ -544,6 +545,94 @@ else version( FreeBSD ) int signbit(real x) { return __signbit(x); } } } +else version( Solaris ) +{ + int __isnanf(float x); + int __isnan(double x); + int __isnanl(real x); + + extern (D) + { + //int isnan(real-floating x); + int isnan(float x) { return __isnanf(x); } + int isnan(double x) { return __isnan(x); } + int isnan(real x) + { + return (real.sizeof == double.sizeof) + ? __isnan(x) + : __isnanl(x); + } + } +} +else version( Android ) +{ + enum + { + FP_INFINITE = 0x01, + FP_NAN = 0x02, + FP_NORMAL = 0x04, + FP_SUBNORMAL = 0x08, + FP_ZERO = 0x10, + } + + enum FP_FAST_FMAF; + + int __fpclassifyd(double); + int __fpclassifyf(float); + int __fpclassifyl(real); + + int __isfinitef(float); + int __isfinite(double); + int __isfinitel(real); + + int __isinff(float); + int __isinf(double); + int __isinfl(real); + + int isnanf(float); + int isnan(double); + int __isnanl(real); + + int __isnormalf(float); + int __isnormal(double); + int __isnormall(real); + + int __signbit(double); + int __signbitf(float); + int __signbitl(real); + + extern (D) + { + //int fpclassify(real-floating x); + int fpclassify(float x) { return __fpclassifyf(x); } + int fpclassify(double x) { return __fpclassifyd(x); } + int fpclassify(real x) { return __fpclassifyl(x); } + + //int isfinite(real-floating x); + int isfinite(float x) { return __isfinitef(x); } + int isfinite(double x) { return __isfinite(x); } + int isfinite(real x) { return __isfinitel(x); } + + //int isinf(real-floating x); + int isinf(float x) { return __isinff(x); } + int isinf(double x) { return __isinf(x); } + int isinf(real x) { return __isinfl(x); } + + //int isnan(real-floating x); + int isnan(float x) { return isnanf(x); } + int isnan(real x) { return __isnanl(x); } + + //int isnormal(real-floating x); + int isnormal(float x) { return __isnormalf(x); } + int isnormal(double x) { return __isnormal(x); } + int isnormal(real x) { return __isnormall(x); } + + //int signbit(real-floating x); + int signbit(float x) { return __signbitf(x); } + int signbit(double x) { return __signbit(x); } + int signbit(real x) { return __signbitl(x); } + } +} extern (D) { diff --git a/libphobos/libdruntime/core/stdc/signal.d b/libphobos/libdruntime/core/stdc/signal.d index a869e9cc3..0312b3729 100644 --- a/libphobos/libdruntime/core/stdc/signal.d +++ b/libphobos/libdruntime/core/stdc/signal.d @@ -2,21 +2,20 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/stdc/_signal.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.stdc.signal; extern (C): @system: nothrow: +@nogc: // this should be volatile alias int sig_atomic_t; diff --git a/libphobos/libdruntime/core/stdc/stdarg.d b/libphobos/libdruntime/core/stdc/stdarg.d index 881e7b015..55a560293 100644 --- a/libphobos/libdruntime/core/stdc/stdarg.d +++ b/libphobos/libdruntime/core/stdc/stdarg.d @@ -2,23 +2,21 @@ * D header file for C99. * * Copyright: Copyright Digital Mars 2000 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * Boost Software License 1.0. + * (See accompanying file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) * Authors: Walter Bright, Hauke Duden * Standards: ISO/IEC 9899:1999 (E) - */ - -/* Copyright Digital Mars 2000 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) + * Source: $(DRUNTIMESRC core/stdc/_stdarg.d) */ /* NOTE: This file has been patched from the original DMD distribution to - work with the GDC compiler. + * work with the GDC compiler. */ module core.stdc.stdarg; @system: +//@nogc: // Not yet, need to make TypeInfo's member functions @nogc first version( GNU ) { @@ -72,10 +70,9 @@ version( GNU ) TypeInfo arg1, arg2; if (!ti.argTypes(arg1, arg2)) { - bool inXMMregister(TypeInfo arg) + bool inXMMregister(TypeInfo arg) pure nothrow @safe { - auto s = arg.toString(); - return (s == "double" || s == "float" || s == "idouble" || s == "ifloat"); + return (arg.flags & 2) != 0; } TypeInfo_Vector v1 = arg1 ? cast(TypeInfo_Vector)arg1 : null; @@ -215,7 +212,7 @@ else version( X86 ) /********************* * The argument pointer type. */ - alias void* va_list; + alias char* va_list; /********** * Initialize ap. @@ -284,7 +281,7 @@ else version (Windows) // Win64 /********************* * The argument pointer type. */ - alias void* va_list; + alias char* va_list; /********** * Initialize ap. @@ -332,7 +329,7 @@ else version (Windows) // Win64 //auto p = cast(void*)(cast(size_t)ap + talign - 1) & ~(talign - 1); auto p = ap; auto tsize = ti.tsize; - ap = cast(void*)(cast(size_t)p + ((size_t.sizeof + size_t.sizeof - 1) & ~(size_t.sizeof - 1))); + ap = cast(va_list)(cast(size_t)p + ((size_t.sizeof + size_t.sizeof - 1) & ~(size_t.sizeof - 1))); void* q = (tsize > size_t.sizeof) ? *cast(void**)p : p; parmn[0..tsize] = q[0..tsize]; } @@ -363,13 +360,14 @@ else version (X86_64) } // Layout of this struct must match __gnuc_va_list for C ABI compatibility - struct __va_list + struct __va_list_tag { uint offset_regs = 6 * 8; // no regs uint offset_fpregs = 6 * 8 + 8 * 16; // no fp regs void* stack_args; void* reg_args; } + alias __va_list = __va_list_tag; align(16) struct __va_argsave_t { @@ -382,7 +380,7 @@ else version (X86_64) * Making it an array of 1 causes va_list to be passed as a pointer in * function argument lists */ - alias void* va_list; + alias va_list = __va_list*; void va_start(T)(out va_list ap, ref T parmn) { @@ -542,10 +540,9 @@ else version (X86_64) TypeInfo arg1, arg2; if (!ti.argTypes(arg1, arg2)) { - bool inXMMregister(TypeInfo arg) + bool inXMMregister(TypeInfo arg) pure nothrow @safe { - auto s = arg.toString(); - return (s == "double" || s == "float" || s == "idouble" || s == "ifloat"); + return (arg.flags & 2) != 0; } TypeInfo_Vector v1 = arg1 ? cast(TypeInfo_Vector)arg1 : null; diff --git a/libphobos/libdruntime/core/stdc/stddef.d b/libphobos/libdruntime/core/stdc/stddef.d index 032815605..57f893e51 100644 --- a/libphobos/libdruntime/core/stdc/stddef.d +++ b/libphobos/libdruntime/core/stdc/stddef.d @@ -2,21 +2,20 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/stdc/_stddef.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.stdc.stddef; extern (C): @trusted: // Types only. nothrow: +@nogc: // size_t and ptrdiff_t are defined in the object module. diff --git a/libphobos/libdruntime/core/stdc/stdint.d b/libphobos/libdruntime/core/stdc/stdint.d index 16cde640d..1f3ac4030 100644 --- a/libphobos/libdruntime/core/stdc/stdint.d +++ b/libphobos/libdruntime/core/stdc/stdint.d @@ -2,21 +2,17 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/stdc/_stdint.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ - /* NOTE: This file has been patched from the original DMD distribution to - work with the GDC compiler. -*/ - + * work with the GDC compiler. + */ module core.stdc.stdint; version(GNU) import gcc.builtins; @@ -32,6 +28,7 @@ T _typify(T)(T val) @safe pure nothrow { return val; } extern (C): @trusted: // Types and constants only. nothrow: +@nogc: alias int8_t = byte ; alias int16_t = short; diff --git a/libphobos/libdruntime/core/stdc/stdio.d b/libphobos/libdruntime/core/stdc/stdio.d index b9325f4b0..d13105c4f 100644 --- a/libphobos/libdruntime/core/stdc/stdio.d +++ b/libphobos/libdruntime/core/stdc/stdio.d @@ -2,16 +2,17 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly, - Alex Rønne Petersen + * Alex Rønne Petersen + * Source: $(DRUNTIMESRC core/stdc/_stdio.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) +/* NOTE: This file has been patched from the original DMD distribution to + * work with the GDC compiler. */ module core.stdc.stdio; @@ -26,11 +27,16 @@ private { import core.sys.posix.sys.types; } + else version (Android) + { + import core.sys.posix.sys.types: off_t; + } } extern (C): @system: nothrow: +@nogc: version( Win32 ) { @@ -121,7 +127,6 @@ else version ( FreeBSD ) ubyte *_base; int _size; } - alias _iobuf __sFILE; union __mbstate_t // { @@ -146,6 +151,24 @@ else version (Solaris) else enum int _NFILE = 20; } +else version( Android ) +{ + enum + { + BUFSIZ = 1024, + EOF = -1, + FOPEN_MAX = 20, + FILENAME_MAX = 1024, + TMP_MAX = 308915776, + L_tmpnam = 1024 + } + + struct __sbuf + { + ubyte* _base; + int _size; + } +} else { static assert( false, "Unsupported platform" ); @@ -160,6 +183,8 @@ enum version( Win32 ) { + alias int fpos_t; //check this + struct _iobuf { char* _ptr; @@ -171,9 +196,13 @@ version( Win32 ) int _bufsiz; char* __tmpnum; } + + alias shared(_iobuf) FILE; } else version( Win64 ) { + alias int fpos_t; //check this + struct _iobuf { char* _ptr; @@ -185,12 +214,14 @@ else version( Win64 ) int _bufsiz; char* _tmpfname; } + + alias shared(_iobuf) FILE; } else version( linux ) { - alias _iobuf = _IO_FILE; + alias int fpos_t; //this is probably wrong, fix this - align(1) struct _IO_FILE + struct _IO_FILE { int _flags; char* _read_ptr; @@ -205,7 +236,7 @@ else version( linux ) char* _backup_base; char* _save_end; void* _markers; - _iobuf* _chain; + _IO_FILE* _chain; int _fileno; int _blksize; int _old_offset; @@ -214,10 +245,15 @@ else version( linux ) char[1] _shortbuf; void* _lock; } + + alias _IO_FILE _iobuf; //remove later + alias shared(_IO_FILE) FILE; } else version( OSX ) { - align (1) struct _iobuf + alias int fpos_t; //check this + + struct __sFILE { ubyte* _p; int _r; @@ -227,10 +263,11 @@ else version( OSX ) __sbuf _bf; int _lbfsize; - int* function(void*) _close; - int* function(void*, char*, int) _read; - fpos_t* function(void*, fpos_t, int) _seek; - int* function(void*, char *, int) _write; + void* _cookie; + int function(void*) _close; + int function(void*, char*, int) _read; + fpos_t function(void*, fpos_t, int) _seek; + int function(void*, char *, int) _write; __sbuf _ub; __sFILEX* _extra; @@ -244,10 +281,15 @@ else version( OSX ) int _blksize; fpos_t _offset; } + + alias __sFILE _iobuf; //remove later + alias shared(__sFILE) FILE; } else version( FreeBSD ) { - align (1) struct _iobuf + alias int fpos_t; //check this + + struct __sFILE { ubyte* _p; int _r; @@ -281,10 +323,15 @@ else version( FreeBSD ) int _orientation; __mbstate_t _mbstate; } + + alias __sFILE _iobuf; //remove later + alias shared(__sFILE) FILE; } else version (Solaris) { - align (1) struct _iobuf + alias int fpos_t; //check this + + struct _iobuf { char* _ptr; int _cnt; @@ -298,15 +345,50 @@ else version (Solaris) // __xf_nocheck:1 // __filler:10 } + + alias shared(_iobuf) FILE; +} +else version( Android ) +{ + alias off_t fpos_t; + + struct __sFILE + { + ubyte* _p; + int _r; + int _w; + short _flags; + short _file; + __sbuf _bf; + int _lbfsize; + + void* _cookie; + int function(void*) _close; + int function(void*, char*, int) _read; + fpos_t function(void*, fpos_t, int) _seek; + int function(void*, in char*, int) _write; + + __sbuf _ext; + ubyte* _up; + int _ur; + + ubyte[3] _ubuf; + ubyte[1] _nbuf; + + __sbuf _lb; + + int _blksize; + fpos_t _offset; + } + + alias __sFILE _iobuf; //remove later + alias shared(__sFILE) FILE; } else { static assert( false, "Unsupported platform" ); } - -alias shared(_iobuf) FILE; - enum { _F_RDWR = 0x0003, // non-standard @@ -467,6 +549,21 @@ else version (Solaris) shared stdout = &__iob[1]; shared stderr = &__iob[2]; } +else version( Android ) +{ + enum + { + _IOFBF = 0, + _IOLBF = 1, + _IONBF = 2, + } + + private extern shared FILE[3] __sF; + + shared stdin = &__sF[0]; + shared stdout = &__sF[1]; + shared stderr = &__sF[2]; +} else version( GNU_CBridge_Stdio ) { extern FILE * _d_gnu_cbridge_stdin; @@ -489,8 +586,6 @@ else static assert( false, "Unsupported platform" ); } -alias int fpos_t; - int remove(in char* filename); int rename(in char* from, in char* to); @@ -608,8 +703,8 @@ version( MinGW ) // No unsafe pointer manipulation. extern (D) @trusted { - void rewind(FILE* stream) { fseek(stream,0L,SEEK_SET); stream._flag&=~_IOERR; } - pure void clearerr(FILE* stream) { stream._flag &= ~(_IOERR|_IOEOF); } + void rewind(FILE* stream) { fseek(stream,0L,SEEK_SET); stream._flag = stream._flag & ~_IOERR; } + pure void clearerr(FILE* stream) { stream._flag = stream._flag & ~(_IOERR|_IOEOF); } pure int feof(FILE* stream) { return stream._flag&_IOEOF; } pure int ferror(FILE* stream) { return stream._flag&_IOERR; } } @@ -626,8 +721,8 @@ else version( Win32 ) // No unsafe pointer manipulation. extern (D) @trusted { - void rewind(FILE* stream) { fseek(stream,0L,SEEK_SET); stream._flag&=~_IOERR; } - pure void clearerr(FILE* stream) { stream._flag &= ~(_IOERR|_IOEOF); } + void rewind(FILE* stream) { fseek(stream,0L,SEEK_SET); stream._flag= stream._flag & ~_IOERR; } + pure void clearerr(FILE* stream) { stream._flag = stream._flag & ~(_IOERR|_IOEOF); } pure int feof(FILE* stream) { return stream._flag&_IOEOF; } pure int ferror(FILE* stream) { return stream._flag&_IOERR; } } @@ -642,8 +737,8 @@ else version( Win64 ) // No unsafe pointer manipulation. extern (D) @trusted { - void rewind(FILE* stream) { fseek(stream,0L,SEEK_SET); stream._flag&=~_IOERR; } - pure void clearerr(FILE* stream) { stream._flag &= ~(_IOERR|_IOEOF); } + void rewind(FILE* stream) { fseek(stream,0L,SEEK_SET); stream._flag = stream._flag & ~_IOERR; } + pure void clearerr(FILE* stream) { stream._flag = stream._flag & ~(_IOERR|_IOEOF); } pure int feof(FILE* stream) { return stream._flag&_IOEOF; } pure int ferror(FILE* stream) { return stream._flag&_IOERR; } pure int fileno(FILE* stream) { return stream._file; } @@ -662,16 +757,26 @@ else version( Win64 ) int _fputc_nolock(int c, FILE *fp) { - if (--fp._cnt >= 0) - return *fp._ptr++ = cast(char)c; + fp._cnt = fp._cnt - 1; + if (fp._cnt >= 0) + { + *fp._ptr = cast(char)c; + fp._ptr = fp._ptr + 1; + return cast(char)c; + } else return _flsbuf(c, fp); } int _fgetc_nolock(FILE *fp) { - if (--fp._cnt >= 0) - return *fp._ptr++; + fp._cnt = fp._cnt - 1; + if (fp._cnt >= 0) + { + char c = *fp._ptr; + fp._ptr = fp._ptr + 1; + return c; + } else return _filbuf(fp); } @@ -742,6 +847,21 @@ else version (Solaris) int snprintf(char* s, size_t n, in char* format, ...); int vsnprintf(char* s, size_t n, in char* format, va_list arg); } +else version( Android ) +{ + // No unsafe pointer manipulation. + @trusted + { + void rewind(FILE*); + pure void clearerr(FILE*); + pure int feof(FILE*); + pure int ferror(FILE*); + int fileno(FILE*); + } + + int snprintf(char* s, size_t n, in char* format, ...); + int vsnprintf(char* s, size_t n, in char* format, va_list arg); +} else version( GNU ) { // No unsafe pointer manipulation. diff --git a/libphobos/libdruntime/core/stdc/stdlib.d b/libphobos/libdruntime/core/stdc/stdlib.d index a88c0b411..489323af4 100644 --- a/libphobos/libdruntime/core/stdc/stdlib.d +++ b/libphobos/libdruntime/core/stdc/stdlib.d @@ -1,7 +1,7 @@ /** * D header file for C99. * - * Copyright: Copyright Sean Kelly 2005 - 2012. + * Copyright: Copyright Sean Kelly 2005 - 2014. * License: Distributed under the * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) @@ -17,7 +17,16 @@ public import core.stdc.stddef; // for size_t, wchar_t extern (C): @system: + +/* Placed outside @nogc in order to not constrain what the callback does. + */ +alias int function(in void*, in void*) _compare_fp_t; +void* bsearch(in void* key, in void* base, size_t nmemb, size_t size, _compare_fp_t compar); +void qsort(void* base, size_t nmemb, size_t size, _compare_fp_t compar); + + nothrow: +@nogc: struct div_t { @@ -46,6 +55,7 @@ else version(linux) enum RAND_MAX = 0x7fffffff; else version(OSX) enum RAND_MAX = 0x7fffffff; else version(FreeBSD) enum RAND_MAX = 0x7fffffff; else version(Solaris) enum RAND_MAX = 0x7fff; +else version(Android) enum RAND_MAX = 0x7fffffff; else static assert( false, "Unsupported platform" ); double atof(in char* nptr); @@ -72,6 +82,13 @@ else version (MinGW) real __mingw_strtold(in char* nptr, char** endptr); alias __mingw_strtold strtold; } +else version (Android) +{ + real strtold(in char* nptr, char** endptr) + { // Fake it again till we make it + return strtod(nptr, endptr); + } +} else { real strtold(in char* nptr, char** endptr); @@ -101,9 +118,6 @@ void _Exit(int status); char* getenv(in char* name); int system(in char* string); -void* bsearch(in void* key, in void* base, size_t nmemb, size_t size, int function(in void*, in void*) compar); -void qsort(void* base, size_t nmemb, size_t size, int function(in void*, in void*) compar); - // These only operate on integer values. @trusted { diff --git a/libphobos/libdruntime/core/stdc/string.d b/libphobos/libdruntime/core/stdc/string.d index b621683bb..35b905a0f 100644 --- a/libphobos/libdruntime/core/stdc/string.d +++ b/libphobos/libdruntime/core/stdc/string.d @@ -2,16 +2,14 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/stdc/_string.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.stdc.string; private import core.stdc.stddef; // for size_t @@ -19,6 +17,7 @@ private import core.stdc.stddef; // for size_t extern (C): @system: nothrow: +@nogc: pure void* memchr(in void* s, int c, size_t n); pure int memcmp(in void* s1, in void* s2, size_t n); diff --git a/libphobos/libdruntime/core/stdc/tgmath.d b/libphobos/libdruntime/core/stdc/tgmath.d index 8589a2abd..99462a393 100644 --- a/libphobos/libdruntime/core/stdc/tgmath.d +++ b/libphobos/libdruntime/core/stdc/tgmath.d @@ -2,16 +2,14 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/stdc/_tgmath.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.stdc.tgmath; private import core.stdc.config; @@ -21,6 +19,7 @@ private static import core.stdc.complex; extern (C): @trusted: // Everything here operates on floating point and integer values. nothrow: +@nogc: version( FreeBSD ) { diff --git a/libphobos/libdruntime/core/stdc/time.d b/libphobos/libdruntime/core/stdc/time.d index 4eac80f06..0c47c6453 100644 --- a/libphobos/libdruntime/core/stdc/time.d +++ b/libphobos/libdruntime/core/stdc/time.d @@ -2,17 +2,15 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly, - Alex Rønne Petersen + * Alex Rønne Petersen + * Source: $(DRUNTIMESRC core/stdc/_time.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.stdc.time; private import core.stdc.config; @@ -21,6 +19,7 @@ private import core.stdc.stddef; // for size_t extern (C): @trusted: // There are only a few functions here that use unsafe C strings. nothrow: +@nogc: version( Windows ) { @@ -79,7 +78,11 @@ else version( FreeBSD ) } else version (linux) { - enum clock_t CLOCKS_PER_SEC = 1000000; + enum clock_t CLOCKS_PER_SEC = 1_000_000; +} +else version (Android) +{ + enum clock_t CLOCKS_PER_SEC = 1_000_000; } clock_t clock(); @@ -121,6 +124,11 @@ else version (Solaris) void tzset(); extern __gshared const(char)*[2] tzname; } +else version( Android ) +{ + void tzset(); + extern __gshared const(char)*[2] tzname; +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/stdc/wchar_.d b/libphobos/libdruntime/core/stdc/wchar_.d index 2f2d00914..1e3213837 100644 --- a/libphobos/libdruntime/core/stdc/wchar_.d +++ b/libphobos/libdruntime/core/stdc/wchar_.d @@ -2,15 +2,16 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/stdc/_wchar_.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) +/* NOTE: This file has been patched from the original DMD distribution to + * work with the GDC compiler. */ module core.stdc.wchar_; @@ -24,6 +25,7 @@ public import core.stdc.stdint; // for WCHAR_MIN, WCHAR_MAX extern (C): @system: nothrow: +@nogc: alias int mbstate_t; alias wchar_t wint_t; diff --git a/libphobos/libdruntime/core/stdc/wctype.d b/libphobos/libdruntime/core/stdc/wctype.d index 33b720202..d431b74eb 100644 --- a/libphobos/libdruntime/core/stdc/wctype.d +++ b/libphobos/libdruntime/core/stdc/wctype.d @@ -2,16 +2,14 @@ * D header file for C99. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Sean Kelly + * Source: $(DRUNTIMESRC core/stdc/_wctype.d) * Standards: ISO/IEC 9899:1999 (E) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.stdc.wctype; public import core.stdc.wchar_; // for wint_t, WEOF @@ -19,6 +17,7 @@ public import core.stdc.wchar_; // for wint_t, WEOF extern (C): @trusted: // Only a couple of functions below operate on unsafe C strings. nothrow: +@nogc: alias wchar_t wctrans_t; alias wchar_t wctype_t; diff --git a/libphobos/libdruntime/core/sync/config.d b/libphobos/libdruntime/core/sync/config.d index d07671faf..d5a030540 100644 --- a/libphobos/libdruntime/core/sync/config.d +++ b/libphobos/libdruntime/core/sync/config.d @@ -51,21 +51,18 @@ version( Posix ) void mvtspec( ref timespec t, Duration delta ) { auto val = delta; - val += dur!("seconds")( t.tv_sec ); - val += dur!("nsecs")( t.tv_nsec ); + val += dur!"seconds"( t.tv_sec ); + val += dur!"nsecs"( t.tv_nsec ); - //auto val = delta + dur!("seconds")( t.tv_sec ) + - // + dur!("nsecs")( t.tv_nsec ); + //auto val = delta + dur!"seconds"( t.tv_sec ) + + // + dur!"nsecs"( t.tv_nsec ); if( val.total!"seconds" > t.tv_sec.max ) { t.tv_sec = t.tv_sec.max; - t.tv_nsec = cast(typeof(t.tv_nsec)) val.fracSec.nsecs; + t.tv_nsec = cast(typeof(t.tv_nsec)) val.split!("seconds", "nsecs")().nsecs; } else - { - t.tv_sec = cast(typeof(t.tv_sec)) val.total!"seconds"; - t.tv_nsec = cast(typeof(t.tv_nsec)) val.fracSec.nsecs; - } + val.split!("seconds", "nsecs")(t.tv_sec, t.tv_nsec); } } diff --git a/libphobos/libdruntime/core/sync/mutex.d b/libphobos/libdruntime/core/sync/mutex.d index 085984876..8665662e9 100644 --- a/libphobos/libdruntime/core/sync/mutex.d +++ b/libphobos/libdruntime/core/sync/mutex.d @@ -129,6 +129,15 @@ class Mutex : * SyncException on error. */ @trusted void lock() + { + lock_impl!SyncException(); + } + + @trusted void lock_nothrow() nothrow + { + lock_impl!Error(); + } + private @trusted void lock_impl(Exc)() { version( Windows ) { @@ -138,11 +147,10 @@ class Mutex : { int rc = pthread_mutex_lock( &m_hndl ); if( rc ) - throw new SyncException( "Unable to lock mutex" ); + throw new Exc( "Unable to lock mutex" ); } } - /** * Decrements the internal lock count by one. If this brings the count to * zero, the lock is released. @@ -151,6 +159,16 @@ class Mutex : * SyncException on error. */ @trusted void unlock() + { + unlock_impl!SyncException(); + } + + @trusted void unlock_nothrow() nothrow + { + unlock_impl!Error(); + } + + private @trusted void unlock_impl(Exc)() { version( Windows ) { @@ -160,11 +178,10 @@ class Mutex : { int rc = pthread_mutex_unlock( &m_hndl ); if( rc ) - throw new SyncException( "Unable to unlock mutex" ); + throw new Exc( "Unable to unlock mutex" ); } } - /** * If the lock is held by another caller, the method returns. Otherwise, * the lock is acquired if it is not already held, and then the internal diff --git a/libphobos/libdruntime/core/sync/semaphore.d b/libphobos/libdruntime/core/sync/semaphore.d index 78ab455c9..27154b195 100644 --- a/libphobos/libdruntime/core/sync/semaphore.d +++ b/libphobos/libdruntime/core/sync/semaphore.d @@ -223,13 +223,10 @@ class Semaphore if( period.total!"seconds" > t.tv_sec.max ) { t.tv_sec = t.tv_sec.max; - t.tv_nsec = cast(typeof(t.tv_nsec)) period.fracSec.nsecs; + t.tv_nsec = cast(typeof(t.tv_nsec)) period.split!("seconds", "nsecs")().nsecs; } else - { - t.tv_sec = cast(typeof(t.tv_sec)) period.total!"seconds"; - t.tv_nsec = cast(typeof(t.tv_nsec)) period.fracSec.nsecs; - } + period.split!("seconds", "nsecs")(t.tv_sec, t.tv_nsec); while( true ) { auto rc = semaphore_timedwait( m_hndl, t ); diff --git a/libphobos/libdruntime/core/sys/freebsd/dlfcn.d b/libphobos/libdruntime/core/sys/freebsd/dlfcn.d index 41293debc..ad5962522 100644 --- a/libphobos/libdruntime/core/sys/freebsd/dlfcn.d +++ b/libphobos/libdruntime/core/sys/freebsd/dlfcn.d @@ -11,6 +11,7 @@ public import core.sys.posix.dlfcn; version (FreeBSD): extern (C): +nothrow: enum __BSD_VISIBLE = true; @@ -86,7 +87,7 @@ static if (__BSD_VISIBLE) private template __externC(RT, P...) { - alias extern(C) RT function(P) __externC; + alias extern(C) RT function(P) nothrow @nogc __externC; } /* XSI functions first. */ diff --git a/libphobos/libdruntime/core/sys/freebsd/execinfo.d b/libphobos/libdruntime/core/sys/freebsd/execinfo.d index e9ef6fdfa..2c8b470db 100644 --- a/libphobos/libdruntime/core/sys/freebsd/execinfo.d +++ b/libphobos/libdruntime/core/sys/freebsd/execinfo.d @@ -10,6 +10,7 @@ module core.sys.freebsd.execinfo; version (FreeBSD): extern (C): +nothrow: import core.sys.freebsd.dlfcn; @@ -41,7 +42,7 @@ extern (D) int backtrace(void** buffer, int size) extern (D) char** backtrace_symbols(const(void*)* buffer, int size) { - static void* realloc(void* p, size_t len) + static void* realloc(void* p, size_t len) nothrow { static import cstdlib=core.stdc.stdlib; auto res = cstdlib.realloc(p, len); diff --git a/libphobos/libdruntime/core/sys/linux/dlfcn.d b/libphobos/libdruntime/core/sys/linux/dlfcn.d index adcceedee..4cb45293a 100644 --- a/libphobos/libdruntime/core/sys/linux/dlfcn.d +++ b/libphobos/libdruntime/core/sys/linux/dlfcn.d @@ -85,6 +85,30 @@ else version (MIPS32) void _dl_mcount_wrapper_check(void* __selfpc); } } +else version (MIPS64) +{ + // http://sourceware.org/git/?p=glibc.git;a=blob;f=ports/sysdeps/mips/bits/dlfcn.h + // enum RTLD_LAZY = 0x0001; // POSIX + // enum RTLD_NOW = 0x0002; // POSIX + enum RTLD_BINDING_MASK = 0x3; + enum RTLD_NOLOAD = 0x00008; + enum RTLD_DEEPBIND = 0x00010; + + // enum RTLD_GLOBAL = 0x0004; // POSIX + // enum RTLD_LOCAL = 0; // POSIX + enum RTLD_NODELETE = 0x01000; + + static if (__USE_GNU) + { + RT DL_CALL_FCT(RT, Args...)(RT function(Args) fctp, auto ref Args args) + { + _dl_mcount_wrapper_check(cast(void*)fctp); + return fctp(args); + } + + void _dl_mcount_wrapper_check(void* __selfpc); + } +} else version (PPC) { // http://sourceware.org/git/?p=glibc.git;a=blob;f=bits/dlfcn.h diff --git a/libphobos/libdruntime/core/sys/linux/epoll.d b/libphobos/libdruntime/core/sys/linux/epoll.d index 3ac50c638..3fb140f64 100644 --- a/libphobos/libdruntime/core/sys/linux/epoll.d +++ b/libphobos/libdruntime/core/sys/linux/epoll.d @@ -46,9 +46,10 @@ enum } struct epoll_event -{ - uint events; - epoll_data_t data; +{ + align(1): + uint events; + epoll_data_t data; }; union epoll_data_t diff --git a/libphobos/libdruntime/core/sys/linux/execinfo.d b/libphobos/libdruntime/core/sys/linux/execinfo.d index 6807afca7..3259443fe 100644 --- a/libphobos/libdruntime/core/sys/linux/execinfo.d +++ b/libphobos/libdruntime/core/sys/linux/execinfo.d @@ -9,6 +9,7 @@ module core.sys.linux.execinfo; version (linux): extern (C): +nothrow: int backtrace(void** buffer, int size); char** backtrace_symbols(const(void*)* buffer, int size); diff --git a/libphobos/libdruntime/core/sys/linux/link.d b/libphobos/libdruntime/core/sys/linux/link.d index a2cb7e2b6..2a384070c 100644 --- a/libphobos/libdruntime/core/sys/linux/link.d +++ b/libphobos/libdruntime/core/sys/linux/link.d @@ -33,6 +33,12 @@ else version (MIPS32) alias __WORDSIZE __ELF_NATIVE_CLASS; alias uint32_t Elf_Symndx; } +else version (MIPS64) +{ + // http://sourceware.org/git/?p=glibc.git;a=blob;f=bits/elfclass.h + alias __WORDSIZE __ELF_NATIVE_CLASS; + alias uint32_t Elf_Symndx; +} else version (PPC) { // http://sourceware.org/git/?p=glibc.git;a=blob;f=bits/elfclass.h diff --git a/libphobos/libdruntime/core/sys/linux/sys/mman.d b/libphobos/libdruntime/core/sys/linux/sys/mman.d index 9f0c87df5..1010e5ea8 100644 --- a/libphobos/libdruntime/core/sys/linux/sys/mman.d +++ b/libphobos/libdruntime/core/sys/linux/sys/mman.d @@ -589,7 +589,7 @@ else version (MIPS32) private enum __MAP_ANONYMOUS = 0x0800; - static if (__USE_MISC) enum MAP_RENAME MAP_ANONYMOUS; + static if (__USE_MISC) enum MAP_RENAME = MAP_ANONYMOUS; } // http://sourceware.org/git/?p=glibc.git;a=blob;f=ports/sysdeps/unix/sysv/linux/mips/bits/mman.h else version (MIPS64) @@ -609,7 +609,7 @@ else version (MIPS64) private enum __MAP_ANONYMOUS = 0x0800; - static if (__USE_MISC) enum MAP_RENAME MAP_ANONYMOUS; + static if (__USE_MISC) enum MAP_RENAME = MAP_ANONYMOUS; } else { diff --git a/libphobos/libdruntime/core/sys/osx/execinfo.d b/libphobos/libdruntime/core/sys/osx/execinfo.d index 20ef17bd5..9cb45ff73 100644 --- a/libphobos/libdruntime/core/sys/osx/execinfo.d +++ b/libphobos/libdruntime/core/sys/osx/execinfo.d @@ -9,6 +9,7 @@ module core.sys.osx.execinfo; version (OSX): extern (C): +nothrow: int backtrace(void** buffer, int size); char** backtrace_symbols(const(void*)* buffer, int size); diff --git a/libphobos/libdruntime/core/sys/osx/mach/thread_act.d b/libphobos/libdruntime/core/sys/osx/mach/thread_act.d index b16207d30..ed10ddb9a 100644 --- a/libphobos/libdruntime/core/sys/osx/mach/thread_act.d +++ b/libphobos/libdruntime/core/sys/osx/mach/thread_act.d @@ -15,6 +15,7 @@ module core.sys.osx.mach.thread_act; version (OSX): extern (C): +nothrow: public import core.sys.osx.mach.kern_return; public import core.sys.osx.mach.port; diff --git a/libphobos/libdruntime/core/sys/osx/pthread.d b/libphobos/libdruntime/core/sys/osx/pthread.d index 73ed48159..f3e8587f3 100644 --- a/libphobos/libdruntime/core/sys/osx/pthread.d +++ b/libphobos/libdruntime/core/sys/osx/pthread.d @@ -15,6 +15,7 @@ module core.sys.osx.pthread; version (OSX): extern (C): +nothrow: public import core.sys.posix.pthread; public import core.sys.osx.mach.port; diff --git a/libphobos/libdruntime/core/sys/posix/arpa/inet.d b/libphobos/libdruntime/core/sys/posix/arpa/inet.d index bdee36a0e..2ce7e223d 100644 --- a/libphobos/libdruntime/core/sys/posix/arpa/inet.d +++ b/libphobos/libdruntime/core/sys/posix/arpa/inet.d @@ -115,6 +115,49 @@ else version( FreeBSD ) const(char)* inet_ntop(int, in void*, char*, socklen_t); int inet_pton(int, in char*, void*); } +else version( Android ) +{ + alias uint32_t in_addr_t; + + struct in_addr + { + in_addr_t s_addr; + } + + enum INET_ADDRSTRLEN = 16; + + private + { + extern (D) + { + uint32_t __swap32( uint32_t x ) + { + uint32_t byte32_swap = (x & 0xff) << 24 | (x &0xff00) << 8 | + (x & 0xff0000) >> 8 | (x & 0xff000000) >> 24; + return byte32_swap; + } + + uint16_t __swap16( uint16_t x ) + { + uint16_t byte16_swap = (x & 0xff) << 8 | (x & 0xff00) >> 8; + return byte16_swap; + } + } + } + + extern (D) + { + uint32_t htonl(uint32_t x) { return __swap32(x); } + uint16_t htons(uint16_t x) { return __swap16(x); } + uint32_t ntohl(uint32_t x) { return __swap32(x); } + uint16_t ntohs(uint16_t x) { return __swap16(x); } + } + + in_addr_t inet_addr(in char*); + char* inet_ntoa(in_addr); + const(char)* inet_ntop(int, in void*, char*, size_t); + int inet_pton(int, in char*, void*); +} // // IPV6 (IP6) @@ -138,3 +181,7 @@ else version( FreeBSD ) { enum INET6_ADDRSTRLEN = 46; } +else version( Android ) +{ + enum INET6_ADDRSTRLEN = 46; +} diff --git a/libphobos/libdruntime/core/sys/posix/dirent.d b/libphobos/libdruntime/core/sys/posix/dirent.d index e57c03ad9..af08e46a2 100644 --- a/libphobos/libdruntime/core/sys/posix/dirent.d +++ b/libphobos/libdruntime/core/sys/posix/dirent.d @@ -20,6 +20,8 @@ public import core.sys.posix.sys.types; // for ino_t version (Posix): extern (C): +nothrow: +@nogc: // // Required @@ -168,6 +170,43 @@ else version (Solaris) dirent* readdir(DIR*); } } +else version( Android ) +{ + enum + { + DT_UNKNOWN = 0, + DT_FIFO = 1, + DT_CHR = 2, + DT_DIR = 4, + DT_BLK = 6, + DT_REG = 8, + DT_LNK = 10, + DT_SOCK = 12, + DT_WHT = 14 + } + + version (X86) + { + struct dirent + { + ulong d_ino; + long d_off; + ushort d_reclen; + ubyte d_type; + char[256] d_name; + } + } + else + { + static assert(false, "Architecture not supported."); + } + + struct DIR + { + } + + dirent* readdir(DIR*); +} else { static assert(false, "Unsupported platform"); @@ -217,6 +256,10 @@ else version (Solaris) int readdir_r(DIR*, dirent*, dirent**); } } +else version( Android ) +{ + int readdir_r(DIR*, dirent*, dirent**); +} else { static assert(false, "Unsupported platform"); @@ -248,6 +291,9 @@ else version (Solaris) c_long telldir(DIR*); void seekdir(DIR*, c_long); } +else version (Android) +{ +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/dlfcn.d b/libphobos/libdruntime/core/sys/posix/dlfcn.d index 400b01768..4f838c8ce 100644 --- a/libphobos/libdruntime/core/sys/posix/dlfcn.d +++ b/libphobos/libdruntime/core/sys/posix/dlfcn.d @@ -18,6 +18,8 @@ private import core.sys.posix.config; version (Posix): extern (C): +nothrow: +@nogc: // // XOpen (XSI) @@ -57,6 +59,13 @@ version( linux ) enum RTLD_GLOBAL = 0x0004; enum RTLD_LOCAL = 0; } + else version (MIPS64) + { + enum RTLD_LAZY = 0x0001; + enum RTLD_NOW = 0x0002; + enum RTLD_GLOBAL = 0x0004; + enum RTLD_LOCAL = 0; + } else version (PPC) { enum RTLD_LAZY = 0x00001; @@ -149,3 +158,27 @@ else version( FreeBSD ) void* dli_saddr; } } +else version( Android ) +{ + enum + { + RTLD_NOW = 0, + RTLD_LAZY = 1, + RTLD_LOCAL = 0, + RTLD_GLOBAL = 2 + } + + int dladdr(in void*, Dl_info*); + int dlclose(void*); + const(char)* dlerror(); + void* dlopen(in char*, int); + void* dlsym(void*, in char*); + + struct Dl_info + { + const(char)* dli_fname; + void* dli_fbase; + const(char)* dli_sname; + void* dli_saddr; + } +} diff --git a/libphobos/libdruntime/core/sys/posix/fcntl.d b/libphobos/libdruntime/core/sys/posix/fcntl.d index 3f9f3419c..fcce8b4dc 100644 --- a/libphobos/libdruntime/core/sys/posix/fcntl.d +++ b/libphobos/libdruntime/core/sys/posix/fcntl.d @@ -23,6 +23,9 @@ public import core.sys.posix.sys.stat; // for S_IFMT, etc. version (Posix): extern (C): +nothrow: +@nogc: + // // Required // @@ -148,6 +151,19 @@ version( linux ) enum O_RSYNC = O_SYNC; enum O_SYNC = 0x0010; } + else version (MIPS64) + { + enum O_CREAT = 0x0100; + enum O_EXCL = 0x0400; + enum O_NOCTTY = 0x0800; + enum O_TRUNC = 0x0200; + + enum O_APPEND = 0x0008; + enum O_DSYNC = 0x0010; + enum O_NONBLOCK = 0x0080; + enum O_RSYNC = O_SYNC; + enum O_SYNC = 0x4010; + } else version (PPC) { enum O_CREAT = 0x40; // octal 0100 @@ -347,7 +363,7 @@ else version (Solaris) enum F_GETFL = 3; enum F_SETFL = 4; - static if (__USE_FILE_OFFSET64) + version (D_LP64) { enum F_GETLK = 14; enum F_SETLK = 6; @@ -355,9 +371,18 @@ else version (Solaris) } else { - enum F_GETLK = 33; - enum F_SETLK = 34; - enum F_SETLKW = 35; + static if (__USE_FILE_OFFSET64) + { + enum F_GETLK = 14; + enum F_SETLK = 6; + enum F_SETLKW = 7; + } + else + { + enum F_GETLK = 33; + enum F_SETLK = 34; + enum F_SETLKW = 35; + } } enum F_GETOWN = 23; @@ -401,17 +426,97 @@ else version (Solaris) static if (__USE_LARGEFILE64) { - int creat64(in char*, mode_t); - alias creat64 creat; - - int open64(in char*, int, ...); - alias open64 open; + struct flock64 + { + short l_type; + short l_whence; + off64_t l_start; + off64_t l_len; + int l_sysid; + pid_t l_pid; + c_long[4] l_pad; + } } - else + + version (D_LP64) { int creat(in char*, mode_t); int open(in char*, int, ...); + + static if (__USE_LARGEFILE64) + { + alias creat creat64; + alias open open64; + } } + else + { + static if (__USE_LARGEFILE64) + { + int creat64(in char*, mode_t); + alias creat64 creat; + + int open64(in char*, int, ...); + alias open64 open; + } + else + { + int creat(in char*, mode_t); + int open(in char*, int, ...); + } + } +} +else version( Android ) +{ + enum F_DUPFD = 0; + enum F_GETFD = 1; + enum F_SETFD = 2; + enum F_GETFL = 3; + enum F_SETFL = 4; + enum F_GETLK = 5; + enum F_SETLK = 6; + enum F_SETLKW = 7; + enum F_SETOWN = 8; + enum F_GETOWN = 9; + + enum FD_CLOEXEC = 1; + + enum F_RDLCK = 0; + enum F_WRLCK = 1; + enum F_UNLCK = 2; + + version (X86) + { + enum O_CREAT = 0x40; // octal 0100 + enum O_EXCL = 0x80; // octal 0200 + enum O_NOCTTY = 0x100; // octal 0400 + enum O_TRUNC = 0x200; // octal 01000 + + enum O_APPEND = 0x400; // octal 02000 + enum O_NONBLOCK = 0x800; // octal 04000 + enum O_SYNC = 0x1000; // octal 010000 + } + else + { + static assert(false, "Architecture not supported."); + } + + enum O_ACCMODE = 0x3; + enum O_RDONLY = 0x0; + enum O_WRONLY = 0x1; + enum O_RDWR = 0x2; + + struct flock + { + short l_type; + short l_whence; + off_t l_start; + off_t l_len; + pid_t l_pid; + } + + int creat(in char*, mode_t); + int open(in char*, int, ...); } else { diff --git a/libphobos/libdruntime/core/sys/posix/grp.d b/libphobos/libdruntime/core/sys/posix/grp.d index 8518988e1..37cfdd869 100644 --- a/libphobos/libdruntime/core/sys/posix/grp.d +++ b/libphobos/libdruntime/core/sys/posix/grp.d @@ -20,6 +20,7 @@ public import core.sys.posix.sys.types; // for gid_t, uid_t version (Posix): extern (C): nothrow: +@nogc: // // Required @@ -67,6 +68,16 @@ else version( FreeBSD ) char** gr_mem; } } +else version( Android ) +{ + struct group + { + char* gr_name; + char* gr_passwd; + gid_t gr_gid; + char** gr_mem; + } +} else { static assert(false, "Unsupported platform"); @@ -98,6 +109,9 @@ else version( FreeBSD ) int getgrnam_r(in char*, group*, char*, size_t, group**); int getgruid_r(gid_t, group*, char*, size_t, group**); } +else version( Android ) +{ +} else { static assert(false, "Unsupported platform"); @@ -130,6 +144,9 @@ else version( FreeBSD ) @trusted void endgrent(); @trusted void setgrent(); } +else version( Android ) +{ +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/net/if_.d b/libphobos/libdruntime/core/sys/posix/net/if_.d index e02e27e6c..f9fee41cc 100644 --- a/libphobos/libdruntime/core/sys/posix/net/if_.d +++ b/libphobos/libdruntime/core/sys/posix/net/if_.d @@ -82,3 +82,10 @@ else version( FreeBSD ) if_nameindex_t* if_nameindex(); void if_freenameindex(if_nameindex_t*); } +else version( Android ) +{ + enum IF_NAMESIZE = 16; + + uint if_nametoindex(in char*); + char* if_indextoname(uint, char*); +} diff --git a/libphobos/libdruntime/core/sys/posix/netdb.d b/libphobos/libdruntime/core/sys/posix/netdb.d index 5519cb452..339abb215 100644 --- a/libphobos/libdruntime/core/sys/posix/netdb.d +++ b/libphobos/libdruntime/core/sys/posix/netdb.d @@ -22,6 +22,8 @@ public import core.sys.posix.sys.socket; // for socklen_t version (Posix): extern (C): +nothrow: +@nogc: // // Required @@ -475,6 +477,87 @@ else version (Solaris) enum EAI_PROTOCOL = 13; enum EAI_MAX = 14; } +else version( Android ) +{ + struct hostent + { + char* h_name; + char** h_aliases; + int h_addrtype; + int h_length; + char** h_addr_list; + extern (D) char* h_addr() @property { return h_addr_list[0]; } // non-standard + } + + struct netent + { + char* n_name; + char** n_aliases; + int n_addrtype; + uint32_t n_net; + } + + struct protoent + { + char* p_name; + char** p_aliases; + int p_proto; + } + + struct servent + { + char* s_name; + char** s_aliases; + int s_port; + char* s_proto; + } + + enum IPPORT_RESERVED = 1024; + + enum HOST_NOT_FOUND = 1; + enum NO_DATA = 4; + enum NO_RECOVERY = 3; + enum TRY_AGAIN = 2; + + struct addrinfo + { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + socklen_t ai_addrlen; + char* ai_canonname; + sockaddr* ai_addr; + addrinfo* ai_next; + } + + enum AI_PASSIVE = 0x1; + enum AI_CANONNAME = 0x2; + enum AI_NUMERICHOST = 0x4; + enum AI_NUMERICSERV = 0x8; + enum AI_V4MAPPED = 0x800; + enum AI_ALL = 0x100; + enum AI_ADDRCONFIG = 0x400; + + enum NI_NOFQDN = 0x1; + enum NI_NUMERICHOST = 0x2; + enum NI_NAMEREQD = 0x4; + enum NI_NUMERICSERV = 0x8; + enum NI_DGRAM = 0x10; + enum NI_MAXHOST = 1025; // non-standard + enum NI_MAXSERV = 32; // non-standard + + enum EAI_AGAIN = 2; + enum EAI_BADFLAGS = 3; + enum EAI_FAIL = 4; + enum EAI_FAMILY = 5; + enum EAI_MEMORY = 6; + enum EAI_NONAME = 8; + enum EAI_SERVICE = 9; + enum EAI_SOCKTYPE = 10; + enum EAI_SYSTEM = 11; + enum EAI_OVERFLOW = 14; +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/netinet/in_.d b/libphobos/libdruntime/core/sys/posix/netinet/in_.d index c3806abdc..688791ac4 100644 --- a/libphobos/libdruntime/core/sys/posix/netinet/in_.d +++ b/libphobos/libdruntime/core/sys/posix/netinet/in_.d @@ -166,6 +166,32 @@ else version( FreeBSD ) //enum INET_ADDRSTRLEN = 16; } +else version( Android ) +{ + private enum __SOCK_SIZE__ = 16; + + struct sockaddr_in + { + sa_family_t sin_family; + ushort sin_port; + in_addr sin_addr; + + /* Pad to size of `struct sockaddr'. */ + ubyte[__SOCK_SIZE__ - sa_family_t.sizeof - + ushort.sizeof - in_addr.sizeof] __pad; + } + + enum + { + IPPROTO_IP = 0, + IPPROTO_ICMP = 1, + IPPROTO_TCP = 6, + IPPROTO_UDP = 17 + } + + enum c_ulong INADDR_ANY = 0x00000000; + enum c_ulong INADDR_BROADCAST = 0xffffffff; +} // @@ -610,6 +636,149 @@ else version( FreeBSD ) __IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_GLOBAL; } } +else version( Android ) +{ + struct in6_addr + { + union + { + uint8_t[16] s6_addr; + uint16_t[8] s6_addr16; + uint32_t[4] s6_addr32; + } + } + + struct sockaddr_in6 + { + ushort sin6_family; + uint16_t sin6_port; + uint32_t sin6_flowinfo; + in6_addr sin6_addr; + uint32_t sin6_scope_id; + } + + __gshared immutable in6_addr in6addr_any = {[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}; + __gshared immutable in6_addr in6addr_loopback = {[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]}; + + struct ipv6_mreq + { + in6_addr ipv6mr_multiaddr; + int ipv6mr_ifindex; + } + + enum : uint + { + IPPROTO_IPV6 = 41, + + IPV6_JOIN_GROUP = 20, + IPV6_LEAVE_GROUP = 21, + IPV6_MULTICAST_HOPS = 18, + IPV6_MULTICAST_IF = 17, + IPV6_MULTICAST_LOOP = 19, + IPV6_UNICAST_HOPS = 16, + IPV6_V6ONLY = 26 + } + + private enum + { + IPV6_ADDR_SCOPE_NODELOCAL = 0x01, + IPV6_ADDR_SCOPE_INTFACELOCAL = 0x01, + IPV6_ADDR_SCOPE_LINKLOCAL = 0x02, + IPV6_ADDR_SCOPE_SITELOCAL = 0x05, + IPV6_ADDR_SCOPE_ORGLOCAL = 0x08, + IPV6_ADDR_SCOPE_GLOBAL = 0x0e, + } + + extern (D) + { + bool IN6_IS_ADDR_UNSPECIFIED( in in6_addr* a ) + { + return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) && + (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) && + (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == 0) && + (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) == 0); + } + + bool IN6_IS_ADDR_LOOPBACK( in in6_addr* a ) + { + return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) && + (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) && + (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == 0) && + (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) == ntohl(1)); + } + + bool IN6_IS_ADDR_V4COMPAT( in in6_addr* a ) + { + return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) && + (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) && + (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == 0) && + (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) != 0) && + (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[12]) != ntohl(1)); + } + + bool IN6_IS_ADDR_V4MAPPED( in in6_addr* a ) + { + return (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[0]) == 0) && + (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[4]) == 0) && + (*cast(const uint32_t*) cast(const void*) (&a.s6_addr[8]) == ntohl(0x0000ffff)); + } + + bool IN6_IS_ADDR_LINKLOCAL( in in6_addr* a ) + { + return a.s6_addr[0] == 0xfe && (a.s6_addr[1] & 0xc0) == 0x80; + } + + bool IN6_IS_ADDR_SITELOCAL( in in6_addr* a ) + { + return a.s6_addr[0] == 0xfe && (a.s6_addr[1] & 0xc0) == 0xc0; + } + + bool IN6_IS_ADDR_ULA( in in6_addr* a ) + { + return (a.s6_addr[0] & 0xfe) == 0xfc; + } + + bool IN6_IS_ADDR_MULTICAST( in in6_addr* a ) + { + return a.s6_addr[0] == 0xff; + } + + uint8_t IPV6_ADDR_MC_SCOPE( in in6_addr* a ) + { + return a.s6_addr[1] & 0x0f; + } + + bool IN6_IS_ADDR_MC_NODELOCAL( in in6_addr* a ) + { + return IN6_IS_ADDR_MULTICAST(a) && + IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_NODELOCAL; + } + + bool IN6_IS_ADDR_MC_LINKLOCAL( in in6_addr* a ) + { + return IN6_IS_ADDR_MULTICAST(a) && + IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_LINKLOCAL; + } + + bool IN6_IS_ADDR_MC_SITELOCAL( in in6_addr* a ) + { + return IN6_IS_ADDR_MULTICAST(a) && + IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_SITELOCAL; + } + + bool IN6_IS_ADDR_MC_ORGLOCAL( in in6_addr* a ) + { + return IN6_IS_ADDR_MULTICAST(a) && + IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_ORGLOCAL; + } + + bool IN6_IS_ADDR_MC_GLOBAL( in in6_addr* a ) + { + return IN6_IS_ADDR_MULTICAST(a) && + IPV6_ADDR_MC_SCOPE(a) == IPV6_ADDR_SCOPE_GLOBAL; + } + } +} // @@ -631,4 +800,7 @@ else version( FreeBSD ) { enum uint IPPROTO_RAW = 255; } - +else version( Android ) +{ + enum uint IPPROTO_RAW = 255; +} diff --git a/libphobos/libdruntime/core/sys/posix/netinet/tcp.d b/libphobos/libdruntime/core/sys/posix/netinet/tcp.d index 04a7376c0..20bcf31fb 100644 --- a/libphobos/libdruntime/core/sys/posix/netinet/tcp.d +++ b/libphobos/libdruntime/core/sys/posix/netinet/tcp.d @@ -38,3 +38,7 @@ else version( FreeBSD ) { enum TCP_NODELAY = 1; } +else version( Android ) +{ + enum TCP_NODELAY = 1; +} diff --git a/libphobos/libdruntime/core/sys/posix/poll.d b/libphobos/libdruntime/core/sys/posix/poll.d index d8b99b443..7f4fb6d3f 100644 --- a/libphobos/libdruntime/core/sys/posix/poll.d +++ b/libphobos/libdruntime/core/sys/posix/poll.d @@ -18,6 +18,8 @@ private import core.sys.posix.config; version (Posix): extern (C): +nothrow: +@nogc: // // XOpen (XSI) @@ -141,3 +143,30 @@ else version( FreeBSD ) int poll(pollfd*, nfds_t, int); } +else version( Android ) +{ + struct pollfd + { + int fd; + short events; + short revents; + } + + alias uint nfds_t; + + enum + { + POLLIN = 0x001, + POLLRDNORM = 0x040, + POLLRDBAND = 0x080, + POLLPRI = 0x002, + POLLOUT = 0x004, + POLLWRNORM = 0x100, + POLLWRBAND = 0x200, + POLLERR = 0x008, + POLLHUP = 0x010, + POLLNVAL = 0x020, + } + + int poll(pollfd*, nfds_t, c_long); +} diff --git a/libphobos/libdruntime/core/sys/posix/pthread.d b/libphobos/libdruntime/core/sys/posix/pthread.d index 88ba2fc74..76efbed25 100644 --- a/libphobos/libdruntime/core/sys/posix/pthread.d +++ b/libphobos/libdruntime/core/sys/posix/pthread.d @@ -239,6 +239,23 @@ else version (Solaris) enum PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t.init; enum PTHREAD_ONCE_INIT = pthread_once_t.init; } +else version( Android ) +{ + enum + { + PTHREAD_CREATE_JOINABLE, + PTHREAD_CREATE_DETACHED + } + + enum PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t.init; + enum PTHREAD_ONCE_INIT = pthread_once_t.init; + + enum + { + PTHREAD_PROCESS_PRIVATE, + PTHREAD_PROCESS_SHARED + } +} else { static assert(false, "Unsupported platform"); @@ -377,6 +394,36 @@ else version (Solaris) void __pthread_cleanup_push(_pthread_cleanup_routine, void*, caddr_t, _pthread_cleanup_info*); void __pthread_cleanup_pop(int, _pthread_cleanup_info*); } +else version( Android ) +{ + alias void function(void*) __pthread_cleanup_func_t; + + struct __pthread_cleanup_t + { + __pthread_cleanup_t* __cleanup_prev; + __pthread_cleanup_func_t __cleanup_routine; + void* __cleanup_arg; + } + + void __pthread_cleanup_push(__pthread_cleanup_t*, __pthread_cleanup_func_t, + void*); + void __pthread_cleanup_pop(__pthread_cleanup_t*, int); + + struct pthread_cleanup + { + __pthread_cleanup_t __cleanup = void; + + extern (D) void push()( __pthread_cleanup_func_t routine, void* arg ) + { + __pthread_cleanup_push( &__cleanup, routine, arg ); + } + + extern (D) void pop()( int execute ) + { + __pthread_cleanup_pop( &__cleanup, execute ); + } + } +} else version( Posix ) { void pthread_cleanup_push(void function(void*), void*); @@ -479,6 +526,9 @@ else version (Solaris) int pthread_barrierattr_init(pthread_barrierattr_t*); int pthread_barrierattr_setpshared(pthread_barrierattr_t*, int); } +else version (Android) +{ +} else { static assert(false, "Unsupported platform"); @@ -530,6 +580,9 @@ else version (Solaris) int pthread_spin_trylock(pthread_spinlock_t*); int pthread_spin_unlock(pthread_spinlock_t*); } +else version (Android) +{ +} else { static assert(false, "Unsupported platform"); @@ -617,6 +670,18 @@ else version (Solaris) int pthread_mutexattr_settype(pthread_mutexattr_t*, int); int pthread_setconcurrency(int); } +else version (Android) +{ + enum PTHREAD_MUTEX_NORMAL = 0; + enum PTHREAD_MUTEX_RECURSIVE = 1; + enum PTHREAD_MUTEX_ERRORCHECK = 2; + enum PTHREAD_MUTEX_DEFAULT = PTHREAD_MUTEX_NORMAL; + + int pthread_attr_getguardsize(in pthread_attr_t*, size_t*); + int pthread_attr_setguardsize(pthread_attr_t*, size_t); + int pthread_mutexattr_gettype(in pthread_mutexattr_t*, int*); + int pthread_mutexattr_settype(pthread_mutexattr_t*, int); +} else { static assert(false, "Unsupported platform"); @@ -643,6 +708,10 @@ else version (OSX) else version (Solaris) { } +else version( Android ) +{ + int pthread_getcpuclockid(pthread_t, clockid_t*); +} else { static assert(false, "Unsupported platform"); @@ -681,6 +750,11 @@ else version (Solaris) int pthread_rwlock_timedrdlock(pthread_rwlock_t*, in timespec*); int pthread_rwlock_timedwrlock(pthread_rwlock_t*, in timespec*); } +else version( Android ) +{ + int pthread_rwlock_timedrdlock(pthread_rwlock_t*, in timespec*); + int pthread_rwlock_timedwrlock(pthread_rwlock_t*, in timespec*); +} else { static assert(false, "Unsupported platform"); @@ -808,6 +882,21 @@ else version (Solaris) int pthread_setschedparam(pthread_t, int, sched_param*); int pthread_setschedprio(pthread_t, int); } +else version (Android) +{ + enum + { + PTHREAD_SCOPE_SYSTEM, + PTHREAD_SCOPE_PROCESS + } + + int pthread_attr_getschedpolicy(in pthread_attr_t*, int*); + int pthread_attr_getscope(in pthread_attr_t*); + int pthread_attr_setschedpolicy(pthread_attr_t*, int); + int pthread_attr_setscope(pthread_attr_t*, int); + int pthread_getschedparam(pthread_t, int*, sched_param*); + int pthread_setschedparam(pthread_t, int, in sched_param*); +} else { static assert(false, "Unsupported platform"); @@ -861,6 +950,15 @@ else version (Solaris) int pthread_attr_setstackaddr(pthread_attr_t*, void*); int pthread_attr_setstacksize(pthread_attr_t*, size_t); } +else version (Android) +{ + int pthread_attr_getstack(in pthread_attr_t*, void**, size_t*); + int pthread_attr_getstackaddr(in pthread_attr_t*, void**); + int pthread_attr_getstacksize(in pthread_attr_t*, size_t*); + int pthread_attr_setstack(pthread_attr_t*, void*, size_t); + int pthread_attr_setstackaddr(pthread_attr_t*, void*); + int pthread_attr_setstacksize(pthread_attr_t*, size_t); +} else { static assert(false, "Unsupported platform"); @@ -914,6 +1012,15 @@ else version (Solaris) int pthread_rwlockattr_getpshared(in pthread_rwlockattr_t*, int*); int pthread_rwlockattr_setpshared(pthread_rwlockattr_t*, int); } +else version (Android) +{ + int pthread_condattr_getpshared(pthread_condattr_t*, int*); + int pthread_condattr_setpshared(pthread_condattr_t*, int); + int pthread_mutexattr_getpshared(pthread_mutexattr_t*, int*); + int pthread_mutexattr_setpshared(pthread_mutexattr_t*, int); + int pthread_rwlockattr_getpshared(pthread_rwlockattr_t*, int*); + int pthread_rwlockattr_setpshared(pthread_rwlockattr_t*, int); +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/pwd.d b/libphobos/libdruntime/core/sys/posix/pwd.d index 3118fa481..54c27d36f 100644 --- a/libphobos/libdruntime/core/sys/posix/pwd.d +++ b/libphobos/libdruntime/core/sys/posix/pwd.d @@ -19,6 +19,8 @@ public import core.sys.posix.sys.types; // for gid_t, uid_t version (Posix): extern (C): +nothrow: +@nogc: // // Required @@ -98,16 +100,25 @@ else version (Solaris) char* pw_shell; } } +else version( Android ) +{ + struct passwd + { + char* pw_name; + char* pw_passwd; + uid_t pw_uid; + gid_t pw_gid; + char* pw_dir; + char* pw_shell; + } +} else { static assert(false, "Unsupported platform"); } -version( Posix ) -{ - passwd* getpwnam(in char*); - passwd* getpwuid(uid_t); -} +passwd* getpwnam(in char*); +passwd* getpwuid(uid_t); // // Thread-Safe Functions (TSF) @@ -137,6 +148,10 @@ else version (Solaris) int getpwnam_r(in char*, passwd*, char*, size_t, passwd**); int getpwuid_r(uid_t, passwd*, char*, size_t, passwd**); } +else version( Android ) +{ + // Missing from bionic +} else { static assert(false, "Unsupported platform"); @@ -175,6 +190,10 @@ else version (Solaris) passwd* getpwent(); void setpwent(); } +else version ( Android ) +{ + void endpwent(); +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/sched.d b/libphobos/libdruntime/core/sys/posix/sched.d index e0aeec935..250a369de 100644 --- a/libphobos/libdruntime/core/sys/posix/sched.d +++ b/libphobos/libdruntime/core/sys/posix/sched.d @@ -21,6 +21,8 @@ public import core.sys.posix.sys.types; version (Posix): extern (C): +nothrow: +@nogc: // // Required @@ -101,6 +103,18 @@ else version (Solaris) enum SCHED_FX = 6; enum _SCHED_NEXT = 7; } +else version( Android ) +{ + struct sched_param + { + int sched_priority; + } + + enum SCHED_NORMAL = 0; + enum SCHED_OTHER = 0; + enum SCHED_FIFO = 1; + enum SCHED_RR = 2; +} else { static assert(false, "Unsupported platform"); @@ -137,6 +151,10 @@ else version (Solaris) { int sched_yield(); } +else version (Android) +{ + int sched_yield(); +} else { static assert(false, "Unsupported platform"); @@ -175,6 +193,12 @@ else version (Solaris) int sched_get_priority_min(int); int sched_rr_get_interval(pid_t, timespec*); } +else version (Android) +{ + int sched_get_priority_max(int); + int sched_get_priority_min(int); + int sched_rr_get_interval(pid_t, timespec*); +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/semaphore.d b/libphobos/libdruntime/core/sys/posix/semaphore.d index 4b031cc8a..d312ae617 100644 --- a/libphobos/libdruntime/core/sys/posix/semaphore.d +++ b/libphobos/libdruntime/core/sys/posix/semaphore.d @@ -19,6 +19,8 @@ private import core.sys.posix.time; version (Posix): extern (C): +nothrow: +@nogc: // // Required @@ -92,6 +94,15 @@ else version (Solaris) enum SEM_FAILED = cast(sem_t*)-1; } +else version( Android ) +{ + struct sem_t + { + uint count; //volatile + } + + enum SEM_FAILED = null; +} else { static assert(false, "Unsupported platform"); @@ -133,6 +144,10 @@ else version (Solaris) { int sem_timedwait(sem_t*, in timespec*); } +else version( Android ) +{ + int sem_timedwait(sem_t*, in timespec*); +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/setjmp.d b/libphobos/libdruntime/core/sys/posix/setjmp.d index 163ab6695..2d0f25cd2 100644 --- a/libphobos/libdruntime/core/sys/posix/setjmp.d +++ b/libphobos/libdruntime/core/sys/posix/setjmp.d @@ -19,6 +19,7 @@ private import core.sys.posix.signal; // for sigset_t version (Posix): extern (C): +@nogc: // // Required @@ -101,6 +102,22 @@ version( linux ) double __fpregs[6]; } } + else version (MIPS64) + { + struct __jmp_buf + { + long __pc; + long __sp; + long __regs[8]; + long __fp; + long __gp; + int __fpc_csr; + version (MIPS_N64) + double __fpregs[8]; + else + double __fpregs[6]; + } + } else static assert(0, "unimplemented"); @@ -141,6 +158,23 @@ else version( FreeBSD ) int setjmp(ref jmp_buf); void longjmp(ref jmp_buf, int); } +else version( Android ) +{ + // + version( X86 ) + { + enum _JBLEN = 10; + } + else + { + static assert(false, "Architecture not supported."); + } + + alias c_long[_JBLEN] jmp_buf; + + int setjmp(ref jmp_buf); + void longjmp(ref jmp_buf, int); +} // // C Extension (CX) @@ -188,6 +222,13 @@ else version( FreeBSD ) int sigsetjmp(ref sigjmp_buf); void siglongjmp(ref sigjmp_buf, int); } +else version( Android ) +{ + alias c_long[_JBLEN + 1] sigjmp_buf; + + int sigsetjmp(ref sigjmp_buf, int); + void siglongjmp(ref sigjmp_buf, int); +} // // XOpen (XSI) @@ -207,3 +248,8 @@ else version( FreeBSD ) int _setjmp(ref jmp_buf); void _longjmp(ref jmp_buf, int); } +else version( Android ) +{ + int _setjmp(ref jmp_buf); + void _longjmp(ref jmp_buf, int); +} diff --git a/libphobos/libdruntime/core/sys/posix/signal.d b/libphobos/libdruntime/core/sys/posix/signal.d index c3623e6fb..b21a75348 100644 --- a/libphobos/libdruntime/core/sys/posix/signal.d +++ b/libphobos/libdruntime/core/sys/posix/signal.d @@ -2,17 +2,13 @@ * D header file for POSIX. * * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: Boost License 1.0. + * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * Authors: Sean Kelly, Alex Rønne Petersen * Standards: The Open Group Base Specifications Issue 6, IEEE Std 1003.1, 2004 Edition + * Source: $(DRUNTIMESRC core/sys/posix/_signal.d) */ -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.sys.posix.signal; private import core.sys.posix.config; @@ -23,6 +19,7 @@ public import core.sys.posix.sys.types; // for pid_t version (Posix): extern (C): +//nothrow: // this causes Issue 12738 // // Required @@ -91,6 +88,13 @@ version( Posix ) private alias void function(int) sigfn_t; private alias void function(int, siginfo_t*, void*) sigactfn_t; + // nothrow versions + nothrow @nogc + { + private alias void function(int) sigfn_t2; + private alias void function(int, siginfo_t*, void*) sigactfn_t2; + } + enum { SIGEV_SIGNAL, @@ -104,11 +108,26 @@ version( Posix ) void* sival_ptr; } - private extern (C) int __libc_current_sigrtmin(); - private extern (C) int __libc_current_sigrtmax(); + version( Solaris ) + { + import core.sys.posix.unistd; + private int _sigrtmin() { return cast(int) sysconf(_SC_SIGRT_MIN); } + private int _sigrtmax() { return cast(int) sysconf(_SC_SIGRT_MAX); } - alias __libc_current_sigrtmin SIGRTMIN; - alias __libc_current_sigrtmax SIGRTMAX; + alias _sigrtmin SIGRTMIN; + alias _sigrtmax SIGRTMAX; + } + else + { + private extern (C) nothrow @nogc + { + int __libc_current_sigrtmin(); + int __libc_current_sigrtmax(); + } + + alias __libc_current_sigrtmin SIGRTMIN; + alias __libc_current_sigrtmax SIGRTMAX; + } } version( linux ) @@ -185,6 +204,30 @@ version( linux ) enum SIGUSR2 = 17; enum SIGURG = 21; } + else version (MIPS64) + { + //SIGABRT (defined in core.stdc.signal) + enum SIGALRM = 14; + enum SIGBUS = 10; + enum SIGCHLD = 18; + enum SIGCONT = 25; + //SIGFPE (defined in core.stdc.signal) + enum SIGHUP = 1; + //SIGILL (defined in core.stdc.signal) + //SIGINT (defined in core.stdc.signal) + enum SIGKILL = 9; + enum SIGPIPE = 13; + enum SIGQUIT = 3; + //SIGSEGV (defined in core.stdc.signal) + enum SIGSTOP = 23; + //SIGTERM (defined in core.stdc.signal) + enum SIGTSTP = 24; + enum SIGTTIN = 26; + enum SIGTTOU = 27; + enum SIGUSR1 = 16; + enum SIGUSR2 = 17; + enum SIGURG = 21; + } else version (PPC) { //SIGABRT (defined in core.stdc.signal) @@ -350,6 +393,31 @@ else version (Solaris) enum SIGUSR2 = 17; enum SIGURG = 21; } +else version (Android) +{ + version (X86) + { + enum SIGALRM = 14; + enum SIGBUS = 7; + enum SIGCHLD = 17; + enum SIGCONT = 18; + enum SIGHUP = 1; + enum SIGKILL = 9; + enum SIGPIPE = 13; + enum SIGQUIT = 3; + enum SIGSTOP = 19; + enum SIGTSTP = 20; + enum SIGTTIN = 21; + enum SIGTTOU = 22; + enum SIGUSR1 = 10; + enum SIGUSR2 = 12; + enum SIGURG = 23; + } + else + { + static assert(false, "Architecture not supported."); + } +} else { static assert(false, "Unsupported platform"); @@ -381,6 +449,31 @@ else version (Solaris) } sigset_t sa_mask; + version (D_LP64) {} + else + int[2] sa_resv; + } +} +else version (Android) +{ + version (X86) + { + struct sigaction_t + { + union + { + sigfn_t sa_handler; + sigactfn_t sa_sigaction; + } + + sigset_t sa_mask; + c_ulong sa_flags; + void function() sa_restorer; + } + } + else + { + static assert(false, "Architecture not supported."); } } else version( Posix ) @@ -472,9 +565,12 @@ int sigsuspend(in sigset_t*); int sigwait(in sigset_t*, int*); */ +nothrow @nogc +{ + version( linux ) { - enum SIG_HOLD = cast(sigfn_t) 1; + enum SIG_HOLD = cast(sigfn_t2) 1; private enum _SIGSET_NWORDS = 1024 / (8 * c_ulong.sizeof); @@ -567,6 +663,7 @@ version( linux ) } _sigpoll_t _sigpoll; } _sifields_t _sifields; + nothrow @nogc: @property ref pid_t si_pid() { return _sifields._kill.si_pid; } @property ref uid_t si_uid() { return _sifields._kill.si_uid; } @property ref void* si_addr() { return _sifields._sigfault.si_addr; } @@ -602,7 +699,7 @@ version( linux ) } else version( OSX ) { - //SIG_HOLD + enum SIG_HOLD = cast(sigfn_t2) 5; alias uint sigset_t; // pid_t (defined in core.sys.types) @@ -614,11 +711,11 @@ else version( OSX ) //SIGSEGV (defined in core.stdc.signal) //SIGTERM (defined in core.stdc.signal) - //SA_NOCLDSTOP (CX|XSI) + enum SA_NOCLDSTOP = 8; // (CX|XSI) - //SIG_BLOCK - //SIG_UNBLOCK - //SIG_SETMASK + enum SIG_BLOCK = 1; + enum SIG_UNBLOCK = 2; + enum SIG_SETMASK = 3; struct siginfo_t { @@ -634,11 +731,11 @@ else version( OSX ) uint pad[7]; } - //SI_USER - //SI_QUEUE - //SI_TIMER - //SI_ASYNCIO - //SI_MESGQ + enum SI_USER = 0x10001; + enum SI_QUEUE = 0x10002; + enum SI_TIMER = 0x10003; + enum SI_ASYNCIO = 0x10004; + enum SI_MESGQ = 0x10005; int kill(pid_t, int); int sigaction(int, in sigaction_t*, sigaction_t*); @@ -718,7 +815,7 @@ else version( FreeBSD ) } else version (Solaris) { - enum SIG_HOLD = cast(sigfn_t)2; + enum SIG_HOLD = cast(sigfn_t2)2; struct sigset_t { @@ -825,11 +922,134 @@ else version (Solaris) int sigsuspend(in sigset_t*); int sigwait(in sigset_t*, int*); } +else version( Android ) +{ + public import core.sys.posix.time: timer_t; + private import core.stdc.string : memset; + + version (X86) + { + alias c_ulong sigset_t; + enum int LONG_BIT = 32; + } + else + { + static assert(false, "Architecture not supported."); + } + + enum SIG_BLOCK = 0; + enum SIG_UNBLOCK = 1; + enum SIG_SETMASK = 2; + + private enum SI_MAX_SIZE = 128; + private enum SI_PAD_SIZE = ((SI_MAX_SIZE / int.sizeof) - 3); + + struct siginfo_t + { + int si_signo; + int si_errno; + int si_code; + + union _sifields_t + { + int[SI_PAD_SIZE] _pad; + + struct _kill_t + { + pid_t _pid; + uid_t _uid; + } _kill_t _kill; + + struct _timer_t + { + timer_t _tid; + int _overrun; + sigval _sigval; + int _sys_private; + } _timer_t _timer; + + struct _rt_t + { + pid_t _pid; + uid_t _uid; + sigval _sigval; + } _rt_t _rt; + + struct _sigchild_t + { + pid_t _pid; + uid_t _uid; + int _status; + clock_t _utime; + clock_t _stime; + } _sigchild_t _sigchld; + + struct _sigfault_t + { + void* _addr; + } _sigfault_t _sigfault; + + struct _sigpoll_t + { + c_long _band; + int _fd; + } _sigpoll_t _sigpoll; + } _sifields_t _sifields; + } + + enum + { + SI_TKILL = -6, + SI_SIGIO, + SI_ASYNCIO, + SI_MESGQ, + SI_TIMER, + SI_QUEUE, + SI_USER, + SI_KERNEL = 0x80 + } + + int kill(pid_t, int); + int sigaction(int, in sigaction_t*, sigaction_t*); + + // These functions are defined inline in bionic. + int sigaddset(sigset_t* set, int signum) + { + c_ulong* local_set = cast(c_ulong*) set; + signum--; + local_set[signum/LONG_BIT] |= 1UL << (signum%LONG_BIT); + return 0; + } + + int sigdelset(sigset_t* set, int signum) + { + c_ulong* local_set = cast(c_ulong*) set; + signum--; + local_set[signum/LONG_BIT] &= ~(1UL << (signum%LONG_BIT)); + return 0; + } + + int sigemptyset(sigset_t* set) { memset(set, 0, (*set).sizeof); return 0; } + + int sigfillset(sigset_t* set) { memset(set, ~0, (*set).sizeof); return 0; } + + int sigismember(sigset_t* set, int signum) + { + c_ulong* local_set = cast(c_ulong*) set; + signum--; + return cast(int) ((local_set[signum/LONG_BIT] >> (signum%LONG_BIT)) & 1); + } + + int sigpending(sigset_t*); + int sigprocmask(int, in sigset_t*, sigset_t*); + int sigsuspend(in sigset_t*); + int sigwait(in sigset_t*, int*); +} else { static assert(false, "Unsupported platform"); } - +} // // XOpen (XSI) @@ -956,6 +1176,16 @@ version( linux ) enum SIGXCPU = 30; enum SIGXFSZ = 31; } + else version (MIPS64) + { + enum SIGPOLL = 22; + enum SIGPROF = 29; + enum SIGSYS = 12; + enum SIGTRAP = 5; + enum SIGVTALRM = 28; + enum SIGXCPU = 30; + enum SIGXFSZ = 31; + } else version (PPC) { enum SIGPOLL = 29; @@ -1092,6 +1322,11 @@ version( linux ) sigfn_t bsd_signal(int sig, sigfn_t func); sigfn_t sigset(int sig, sigfn_t func); + nothrow: + @nogc: + sigfn_t2 bsd_signal(int sig, sigfn_t2 func); + sigfn_t2 sigset(int sig, sigfn_t2 func); + int killpg(pid_t, int); int sigaltstack(in stack_t*, stack_t*); int sighold(int); @@ -1197,6 +1432,11 @@ else version( OSX ) sigfn_t bsd_signal(int sig, sigfn_t func); sigfn_t sigset(int sig, sigfn_t func); + nothrow: + @nogc: + sigfn_t2 bsd_signal(int sig, sigfn_t2 func); + sigfn_t2 sigset(int sig, sigfn_t2 func); + int killpg(pid_t, int); int sigaltstack(in stack_t*, stack_t*); int sighold(int); @@ -1316,6 +1556,11 @@ else version( FreeBSD ) //sigfn_t bsd_signal(int sig, sigfn_t func); sigfn_t sigset(int sig, sigfn_t func); + nothrow: + @nogc: + //sigfn_t2 bsd_signal(int sig, sigfn_t2 func); + sigfn_t2 sigset(int sig, sigfn_t2 func); + int killpg(pid_t, int); int sigaltstack(in stack_t*, stack_t*); int sighold(int); @@ -1436,6 +1681,10 @@ else version (Solaris) sigfn_t sigset(int sig, sigfn_t func); + nothrow: + @nogc: + sigfn_t2 sigset(int sig, sigfn_t2 func); + int killpg(pid_t, int); int sigaltstack(in stack_t*, stack_t*); int sighold(int); @@ -1444,6 +1693,114 @@ else version (Solaris) int sigpause(int); int sigrelse(int); } +else version (Android) +{ + version (X86) + { + enum SIGPOLL = 29; + enum SIGPROF = 27; + enum SIGSYS = 31; + enum SIGTRAP = 5; + enum SIGVTALRM = 26; + enum SIGXCPU = 24; + enum SIGXFSZ = 25; + + enum SA_ONSTACK = 0x08000000; + enum SA_RESETHAND = 0x80000000; + enum SA_RESTART = 0x10000000; + enum SA_SIGINFO = 4; + enum SA_NOCLDWAIT = 2; + enum SA_NODEFER = 0x40000000; + enum SS_ONSTACK = 1; + enum SS_DISABLE = 2; + enum MINSIGSTKSZ = 2048; + enum SIGSTKSZ = 8192; + + struct stack_t + { + void* ss_sp; + int ss_flags; + size_t ss_size; + } + } + else + { + static assert(false, "Architecture not supported."); + } + + enum + { + ILL_ILLOPC = 1, + ILL_ILLOPN, + ILL_ILLADR, + ILL_ILLTRP, + ILL_PRVOPC, + ILL_PRVREG, + ILL_COPROC, + ILL_BADSTK + } + + enum + { + FPE_INTDIV = 1, + FPE_INTOVF, + FPE_FLTDIV, + FPE_FLTOVF, + FPE_FLTUND, + FPE_FLTRES, + FPE_FLTINV, + FPE_FLTSUB + } + + enum + { + SEGV_MAPERR = 1, + SEGV_ACCERR + } + + enum + { + BUS_ADRALN = 1, + BUS_ADRERR, + BUS_OBJERR + } + + enum + { + TRAP_BRKPT = 1, + TRAP_TRACE + } + + enum + { + CLD_EXITED = 1, + CLD_KILLED, + CLD_DUMPED, + CLD_TRAPPED, + CLD_STOPPED, + CLD_CONTINUED + } + + enum + { + POLL_IN = 1, + POLL_OUT, + POLL_MSG, + POLL_ERR, + POLL_PRI, + POLL_HUP + } + + sigfn_t bsd_signal(int, sigfn_t); + + nothrow: + @nogc: + sigfn_t2 bsd_signal(int, sigfn_t2); + + int killpg(int, int); + int sigaltstack(in stack_t*, stack_t*); + int siginterrupt(int, int); +} else { static assert(false, "Unsupported platform"); @@ -1497,6 +1854,14 @@ else version (Solaris) alias timespec timestruc_t; } +else version( Android ) +{ + struct timespec + { + time_t tv_sec; + c_long tv_nsec; + } +} else { static assert(false, "Unsupported platform"); @@ -1520,6 +1885,9 @@ int sigtimedwait(in sigset_t*, siginfo_t*, in timespec*); int sigwaitinfo(in sigset_t*, siginfo_t*); */ +nothrow: +@nogc: + version( linux ) { private enum __SIGEV_MAX_SIZE = 64; @@ -1598,6 +1966,32 @@ else version (Solaris) int sigtimedwait(in sigset_t*, siginfo_t*, in timespec*); int sigwaitinfo(in sigset_t*, siginfo_t*); } +else version( Android ) +{ + private enum __ARCH_SIGEV_PREAMBLE_SIZE = (int.sizeof * 2) + sigval.sizeof; + private enum SIGEV_MAX_SIZE = 64; + private enum SIGEV_PAD_SIZE = (SIGEV_MAX_SIZE - __ARCH_SIGEV_PREAMBLE_SIZE) + / int.sizeof; + + struct sigevent + { + sigval sigev_value; + int sigev_signo; + int sigev_notify; + + union _sigev_un_t + { + int[SIGEV_PAD_SIZE] _pad; + int _tid; + + struct _sigev_thread_t + { + void function(sigval) _function; + void* _attribute; + } _sigev_thread_t _sigev_thread; + } _sigev_un_t _sigev_un; + } +} else { static assert(false, "Unsupported platform"); @@ -1631,6 +2025,11 @@ else version (Solaris) int pthread_kill(pthread_t, int); int pthread_sigmask(int, in sigset_t*, sigset_t*); } +else version( Android ) +{ + int pthread_kill(pthread_t, int); + int pthread_sigmask(int, in sigset_t*, sigset_t*); +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/stdio.d b/libphobos/libdruntime/core/sys/posix/stdio.d index 24503fd8b..8e2026b95 100644 --- a/libphobos/libdruntime/core/sys/posix/stdio.d +++ b/libphobos/libdruntime/core/sys/posix/stdio.d @@ -21,6 +21,9 @@ public import core.sys.posix.sys.types; // for off_t version (Posix): extern (C): +nothrow: +@nogc: + // // Required (defined in core.stdc.stdio) // @@ -129,6 +132,14 @@ version( linux ) FILE* tmpfile(); } } +else version( Android ) +{ + int fgetpos(FILE*, fpos_t *); + FILE* fopen(in char*, in char*); + FILE* freopen(in char*, in char*, FILE*); + int fseek(FILE*, c_long, int); + int fsetpos(FILE*, in fpos_t*); +} // // C Extension (CX) @@ -170,23 +181,20 @@ version( linux ) off_t ftello(FILE*); } } -else version( Posix ) +else { int fseeko(FILE*, off_t, int); off_t ftello(FILE*); } -version( Posix ) -{ - char* ctermid(char*); - FILE* fdopen(int, in char*); - int fileno(FILE*); - //int fseeko(FILE*, off_t, int); - //off_t ftello(FILE*); - char* gets(char*); - int pclose(FILE*); - FILE* popen(in char*, in char*); -} +char* ctermid(char*); +FILE* fdopen(int, in char*); +int fileno(FILE*); +//int fseeko(FILE*, off_t, int); +//off_t ftello(FILE*); +char* gets(char*); +int pclose(FILE*); +FILE* popen(in char*, in char*); // // Thread-Safe Functions (TSF) @@ -222,9 +230,14 @@ va_list (defined in core.stdc.stdarg) char* tempnam(in char*, in char*); */ +char* tempnam(in char*, in char*); + version( linux ) { enum P_tmpdir = "/tmp"; - - char* tempnam(in char*, in char*); } +version( OSX ) +{ + enum P_tmpdir = "/var/tmp"; +} + diff --git a/libphobos/libdruntime/core/sys/posix/stdlib.d b/libphobos/libdruntime/core/sys/posix/stdlib.d index 8db62837b..c880f3c2c 100644 --- a/libphobos/libdruntime/core/sys/posix/stdlib.d +++ b/libphobos/libdruntime/core/sys/posix/stdlib.d @@ -20,6 +20,8 @@ public import core.sys.posix.sys.wait; version (Posix): extern (C): +nothrow: +@nogc: // // Required (defined in core.stdc.stdlib) @@ -119,6 +121,13 @@ else version( FreeBSD ) void* valloc(size_t); // LEGACY non-standard } +else version( Android ) +{ + int setenv(in char*, in char*, int); + int unsetenv(in char*); + + void* valloc(size_t); +} // // Thread-Safe Functions (TSF) @@ -314,3 +323,23 @@ else version( FreeBSD ) void srandom(uint); int unlockpt(int); } +else version( Android ) +{ + double drand48(); + double erand48(ref ushort[3]); + //int grantpt(int); defined inline, but seems to do nothing in bionic + c_long jrand48(ref ushort[3]); + c_long lrand48(); + char* mktemp(char*); // LEGACY + int mkstemp(char*); + c_long mrand48(); + c_long nrand48(ref ushort[3]); + char* ptsname(int); + int putenv(in char*); + c_long random() { return lrand48(); } + char* realpath(in char*, char*); + ushort* seed48(ref ushort[3]); + void srand48(c_long); + void srandom(uint s) { srand48(s); } + int unlockpt(int); +} diff --git a/libphobos/libdruntime/core/sys/posix/sys/ioctl.d b/libphobos/libdruntime/core/sys/posix/sys/ioctl.d index aefa8a87a..b98e0aa53 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/ioctl.d +++ b/libphobos/libdruntime/core/sys/posix/sys/ioctl.d @@ -365,6 +365,10 @@ else version (Solaris) { int ioctl(int fildes, int request, ...); } +else version (Android) +{ + int ioctl(int, int, ...); +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/sys/ipc.d b/libphobos/libdruntime/core/sys/posix/sys/ipc.d index a0d63ff15..5c9d56087 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/ipc.d +++ b/libphobos/libdruntime/core/sys/posix/sys/ipc.d @@ -115,3 +115,35 @@ else version( FreeBSD ) key_t ftok(in char*, int); } +else version( Android ) +{ + version (X86) + { + struct ipc_perm + { + key_t key; + ushort uid; + ushort gid; + ushort cuid; + ushort cgid; + mode_t mode; + ushort seq; + } + } + else + { + static assert(false, "Architecture not supported."); + } + + enum IPC_CREAT = 0x0200; // 01000 + enum IPC_EXCL = 0x0400; // 02000 + enum IPC_NOWAIT = 0x0800; // 04000 + + enum key_t IPC_PRIVATE = 0; + + enum IPC_RMID = 0; + enum IPC_SET = 1; + enum IPC_STAT = 2; + + key_t ftok(in char*, int); +} diff --git a/libphobos/libdruntime/core/sys/posix/sys/mman.d b/libphobos/libdruntime/core/sys/posix/sys/mman.d index 64c8aab3b..3fb6b65d2 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/mman.d +++ b/libphobos/libdruntime/core/sys/posix/sys/mman.d @@ -81,6 +81,9 @@ else version( FreeBSD ) else version (Solaris) { } +else version (Android) +{ +} else { static assert(false, "Unsupported platform"); @@ -124,6 +127,13 @@ else version (Solaris) enum PROT_WRITE = 0x02; enum PROT_EXEC = 0x04; } +else version (Android) +{ + enum PROT_NONE = 0x00; + enum PROT_READ = 0x01; + enum PROT_WRITE = 0x02; + enum PROT_EXEC = 0x04; +} else { static assert(false, "Unsupported platform"); @@ -161,6 +171,11 @@ else version (Solaris) void* mmap(void*, size_t, int, int, int, off_t); int munmap(void*, size_t); } +else version (Android) +{ + void* mmap(void*, size_t, int, int, int, off_t); + int munmap(void*, size_t); +} else { static assert(false, "Unsupported platform"); @@ -266,6 +281,29 @@ else version (Solaris) int msync(void*, size_t, int); } +else version (Android) +{ + enum MAP_SHARED = 0x0001; + enum MAP_PRIVATE = 0x0002; + enum MAP_FIXED = 0x0010; + + version (X86) + { + enum MAP_ANON = 0x0020; + } + else + { + static assert(false, "Architecture not supported."); + } + + enum MAP_FAILED = cast(void*)-1; + + enum MS_SYNC = 4; + enum MS_ASYNC = 1; + enum MS_INVALIDATE = 2; + + int msync(in void*, size_t, int); +} else { static assert(false, "Unsupported platform"); @@ -343,6 +381,14 @@ else version (Solaris) int mlockall(int); int munlockall(); } +else version (Android) +{ + enum MCL_CURRENT = 1; + enum MCL_FUTURE = 2; + + int mlockall(int); + int munlockall(); +} else { static assert(false, "Unsupported platform"); @@ -376,6 +422,11 @@ else version (Solaris) int mlock(in void*, size_t); int munlock(in void*, size_t); } +else version (Android) +{ + int mlock(in void*, size_t); + int munlock(in void*, size_t); +} else { static assert(false, "Unsupported platform"); @@ -404,6 +455,10 @@ else version (Solaris) { int mprotect(void*, size_t, int); } +else version (Android) +{ + int mprotect(in void*, size_t, int); +} else { static assert(false, "Unsupported platform"); @@ -437,6 +492,9 @@ else version (Solaris) int shm_open(in char*, int, mode_t); int shm_unlink(in char*); } +else version (Android) +{ +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/sys/resource.d b/libphobos/libdruntime/core/sys/posix/sys/resource.d index 720864a34..abefeff91 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/resource.d +++ b/libphobos/libdruntime/core/sys/posix/sys/resource.d @@ -288,6 +288,55 @@ else version (Solaris) RLIMIT_AS = 6, } } +else version (Android) +{ + enum + { + PRIO_PROCESS = 0, + PRIO_PGRP = 1, + PRIO_USER = 2, + } + + alias c_ulong rlim_t; + enum RLIM_INFINITY = cast(c_ulong)(~0UL); + + enum + { + RUSAGE_SELF = 0, + RUSAGE_CHILDREN = -1, + } + + struct rusage + { + timeval ru_utime; + timeval ru_stime; + c_long ru_maxrss; + c_long ru_ixrss; + c_long ru_idrss; + c_long ru_isrss; + c_long ru_minflt; + c_long ru_majflt; + c_long ru_nswap; + c_long ru_inblock; + c_long ru_oublock; + c_long ru_msgsnd; + c_long ru_msgrcv; + c_long ru_nsignals; + c_long ru_nvcsw; + c_long ru_nivcsw; + } + + enum + { + RLIMIT_CORE = 4, + RLIMIT_CPU = 0, + RLIMIT_DATA = 2, + RLIMIT_FSIZE = 1, + RLIMIT_NOFILE = 7, + RLIMIT_STACK = 3, + RLIMIT_AS = 9, + } +} else static assert (false, "Unsupported platform"); struct rlimit @@ -301,6 +350,11 @@ version (FreeBSD) int getpriority(int, int); int setpriority(int, int, int); } +else version (Android) +{ + int getpriority(int, int); + int setpriority(int, int, int); +} else { int getpriority(int, id_t); @@ -309,4 +363,4 @@ else int getrlimit(int, rlimit*); int getrusage(int, rusage*); -int setrlimit(int, const rlimit*); +int setrlimit(int, in rlimit*); diff --git a/libphobos/libdruntime/core/sys/posix/sys/select.d b/libphobos/libdruntime/core/sys/posix/sys/select.d index 5f6850542..e5ae22b9b 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/select.d +++ b/libphobos/libdruntime/core/sys/posix/sys/select.d @@ -261,6 +261,55 @@ else version (Solaris) int select(int, fd_set*, fd_set*, fd_set*, timeval*); int pselect(int, fd_set*, fd_set*, fd_set*, in timespec*, in sigset_t*); } +else version( Android ) +{ + private + { + alias c_ulong __fd_mask; + enum uint __NFDBITS = 8 * __fd_mask.sizeof; + + extern (D) auto __FDELT( int d ) + { + return d / __NFDBITS; + } + + extern (D) auto __FDMASK( int d ) + { + return cast(__fd_mask) 1 << ( d % __NFDBITS ); + } + } + + enum FD_SETSIZE = 1024; + + struct fd_set + { + __fd_mask[FD_SETSIZE / __NFDBITS] fds_bits; + } + + // These functions are generated in assembly in bionic. + extern (D) void FD_CLR( int fd, fd_set* fdset ) + { + fdset.fds_bits[__FDELT( fd )] &= ~__FDMASK( fd ); + } + + extern (D) bool FD_ISSET( int fd, const(fd_set)* fdset ) + { + return (fdset.fds_bits[__FDELT( fd )] & __FDMASK( fd )) != 0; + } + + extern (D) void FD_SET( int fd, fd_set* fdset ) + { + fdset.fds_bits[__FDELT( fd )] |= __FDMASK( fd ); + } + + extern (D) void FD_ZERO( fd_set* fdset ) + { + fdset.fds_bits[0 .. $] = 0; + } + + int pselect(int, fd_set*, fd_set*, fd_set*, in timespec*, in sigset_t*); + int select(int, fd_set*, fd_set*, fd_set*, timeval*); +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/sys/socket.d b/libphobos/libdruntime/core/sys/posix/sys/socket.d index 5808c0320..88f4e5e05 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/socket.d +++ b/libphobos/libdruntime/core/sys/posix/sys/socket.d @@ -310,6 +310,40 @@ version( linux ) SO_TYPE = 0x1008, } } + else version (MIPS64) + { + enum + { + SOCK_DGRAM = 1, + SOCK_SEQPACKET = 5, + SOCK_STREAM = 2, + } + + enum + { + SOL_SOCKET = 0xffff + } + + enum + { + SO_ACCEPTCONN = 0x1009, + SO_BROADCAST = 0x0020, + SO_DEBUG = 0x0001, + SO_DONTROUTE = 0x0010, + SO_ERROR = 0x1007, + SO_KEEPALIVE = 0x0008, + SO_LINGER = 0x0080, + SO_OOBINLINE = 0x0100, + SO_RCVBUF = 0x1002, + SO_RCVLOWAT = 0x1004, + SO_RCVTIMEO = 0x1006, + SO_REUSEADDR = 0x0004, + SO_SNDBUF = 0x1001, + SO_SNDLOWAT = 0x1003, + SO_SNDTIMEO = 0x1005, + SO_TYPE = 0x1008, + } + } else version (PPC) { enum @@ -921,6 +955,167 @@ else version (Solaris) int sockatmark(int); int socketpair(int, int, int, ref int[2]); } +else version( Android ) +{ + alias int socklen_t; + alias ushort sa_family_t; + + struct sockaddr + { + sa_family_t sa_family; + byte[14] sa_data; + } + + private enum size_t _K_SS_MAXSIZE = 128; + + struct sockaddr_storage + { + ushort ss_family; + byte[_K_SS_MAXSIZE - ushort.sizeof] __data; + } + + enum : uint + { + SCM_RIGHTS = 0x01 + } + + private enum _ALIGNBYTES = c_long.sizeof - 1; + + extern (D) + { + size_t CMSG_ALIGN( size_t len ) + { + return (len + _ALIGNBYTES) & ~_ALIGNBYTES; + } + + void* CMSG_DATA( cmsghdr* cmsg ) + { + return cast(void*) (cast(char*) cmsg + CMSG_ALIGN( cmsghdr.sizeof )); + } + + cmsghdr* CMSG_NXTHDR( msghdr* mhdr, cmsghdr* cmsg ) + { + cmsghdr* __ptr = cast(cmsghdr*) ((cast(ubyte*) cmsg) + CMSG_ALIGN(cmsg.cmsg_len)); + return cast(c_ulong)( cast(char*)(__ptr+1) - cast(char*) mhdr.msg_control) > mhdr.msg_controllen ? null : __ptr; + } + + cmsghdr* CMSG_FIRSTHDR( msghdr* mhdr ) + { + return mhdr.msg_controllen >= cmsghdr.sizeof ? cast(cmsghdr*) mhdr.msg_control : null; + } + } + + struct linger + { + int l_onoff; + int l_linger; + } + + version (X86) + { + struct msghdr + { + void* msg_name; + int msg_namelen; + iovec* msg_iov; + uint msg_iovlen; + void* msg_control; + uint msg_controllen; + uint msg_flags; + } + + struct cmsghdr + { + uint cmsg_len; + int cmsg_level; + int cmsg_type; + } + + enum + { + SOCK_DGRAM = 2, + SOCK_SEQPACKET = 5, + SOCK_STREAM = 1 + } + + enum + { + SOL_SOCKET = 1 + } + + enum + { + SO_ACCEPTCONN = 30, + SO_BROADCAST = 6, + SO_DEBUG = 1, + SO_DONTROUTE = 5, + SO_ERROR = 4, + SO_KEEPALIVE = 9, + SO_LINGER = 13, + SO_OOBINLINE = 10, + SO_RCVBUF = 8, + SO_RCVLOWAT = 18, + SO_RCVTIMEO = 20, + SO_REUSEADDR = 2, + SO_SNDBUF = 7, + SO_SNDLOWAT = 19, + SO_SNDTIMEO = 21, + SO_TYPE = 3 + } + } + else + { + static assert(false, "Architecture not supported."); + } + + enum + { + SOMAXCONN = 128 + } + + enum : uint + { + MSG_CTRUNC = 0x08, + MSG_DONTROUTE = 0x04, + MSG_EOR = 0x80, + MSG_OOB = 0x01, + MSG_PEEK = 0x02, + MSG_TRUNC = 0x20, + MSG_WAITALL = 0x100 + } + + enum + { + AF_INET = 2, + AF_UNIX = 1, + AF_UNSPEC = 0 + } + + enum + { + SHUT_RD, + SHUT_WR, + SHUT_RDWR + } + + int accept(int, sockaddr*, socklen_t*); + int bind(int, in sockaddr*, int); + int connect(int, in sockaddr*, socklen_t); + int getpeername(int, sockaddr*, socklen_t*); + int getsockname(int, sockaddr*, socklen_t*); + int getsockopt(int, int, int, void*, socklen_t*); + int listen(int, int); + ssize_t recv(int, void*, size_t, uint); + ssize_t recvfrom(int, void*, size_t, uint, in sockaddr*, socklen_t*); + int recvmsg(int, msghdr*, uint); + ssize_t send(int, in void*, size_t, uint); + int sendmsg(int, in msghdr*, uint); + ssize_t sendto(int, in void*, size_t, int, in sockaddr*, socklen_t); + int setsockopt(int, int, int, in void*, socklen_t); + int shutdown(int, int); + int socket(int, int, int); + int socketpair(int, int, int, ref int[2]); +} else { static assert(false, "Unsupported platform"); @@ -961,6 +1156,13 @@ else version (Solaris) AF_INET6 = 26, } } +else version( Android ) +{ + enum + { + AF_INET6 = 10 + } +} else { static assert(false, "Unsupported platform"); @@ -1001,6 +1203,13 @@ else version (Solaris) SOCK_RAW = 4, } } +else version( Android ) +{ + enum + { + SOCK_RAW = 3 + } +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/sys/stat.d b/libphobos/libdruntime/core/sys/posix/sys/stat.d index ad478d9a8..c599cd222 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/stat.d +++ b/libphobos/libdruntime/core/sys/posix/sys/stat.d @@ -22,6 +22,7 @@ public import core.sys.posix.sys.types; // for off_t, mode_t version (Posix): extern (C): +nothrow: // // Required @@ -232,6 +233,70 @@ version( linux ) c_long[14] st_pad5; } } + else version (MIPS64) + { + struct stat_t + { + c_ulong st_dev; + int[3] st_pad1; + static if (!__USE_FILE_OFFSET64) + { + ino_t st_ino; + } + else + { + c_ulong st_ino; + } + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid; + gid_t st_gid; + c_ulong st_rdev; + static if (!__USE_FILE_OFFSET64) + { + uint[2] st_pad2; + off_t st_size; + int st_pad3; + } + else + { + c_long[3] st_pad2; + c_long st_size; + } + static if (__USE_MISC || __USE_XOPEN2K8) + { + timespec st_atim; + timespec st_mtim; + timespec st_ctim; + extern(D) + { + @property ref time_t st_atime() { return st_atim.tv_sec; } + @property ref time_t st_mtime() { return st_mtim.tv_sec; } + @property ref time_t st_ctime() { return st_ctim.tv_sec; } + } + } + else + { + time_t st_atime; + c_ulong st_atimensec; + time_t st_mtime; + c_ulong st_mtimensec; + time_t st_ctime; + c_ulong st_ctimensec; + } + blksize_t st_blksize; + uint st_pad4; + static if (!__USE_FILE_OFFSET64) + { + blkcnt_t st_blocks; + } + else + { + c_long st_blocks; + } + c_long[14] st_pad5; + } + } else version (PPC) { struct stat_t @@ -511,10 +576,20 @@ else version( OSX ) { struct stat_t { + version ( DARWIN_USE_64_BIT_INODE ) + { + dev_t st_dev; + mode_t st_mode; + nlink_t st_nlink; + ino_t st_ino; + } + else + { dev_t st_dev; ino_t st_ino; mode_t st_mode; nlink_t st_nlink; + } uid_t st_uid; gid_t st_gid; dev_t st_rdev; @@ -650,7 +725,7 @@ else version (Solaris) version (D_LP64) { - struct stat64_t + struct stat_t { dev_t st_dev; ino_t st_ino; @@ -668,7 +743,7 @@ else version (Solaris) char[_ST_FSTYPSZ] st_fstype; } - alias stat64_t stat32_t; + static if (__USE_LARGEFILE64) alias stat_t stat64_t; } else { @@ -715,12 +790,13 @@ else version (Solaris) char[_ST_FSTYPSZ] st_fstype; c_long[8] st_pad4; } - } - static if (__USE_FILE_OFFSET64) - alias stat64_t stat_t; - else - alias stat32_t stat_t; + static if (__USE_FILE_OFFSET64) + alias stat64_t stat_t; + else + alias stat32_t stat_t; + + } enum S_IRUSR = 0x100; enum S_IWUSR = 0x080; @@ -756,23 +832,90 @@ else version (Solaris) extern (D) bool S_ISREG(mode_t mode) { return S_ISTYPE(mode, S_IFREG); } extern (D) bool S_ISLNK(mode_t mode) { return S_ISTYPE(mode, S_IFLNK); } extern (D) bool S_ISSOCK(mode_t mode) { return S_ISTYPE(mode, S_IFSOCK); } + extern (D) bool S_ISDOOR(mode_t mode) { return S_ISTYPE(mode, S_IFDOOR); } + extern (D) bool S_ISPORT(mode_t mode) { return S_ISTYPE(mode, S_IFPORT); } +} +else version( Android ) +{ + version (X86) + { + struct stat_t + { + ulong st_dev; + ubyte[4] __pad0; + c_ulong __st_ino; + uint st_mode; + uint st_nlink; + c_ulong st_uid; + c_ulong st_gid; + ulong st_rdev; + ubyte[4] __pad3; + + long st_size; + c_ulong st_blksize; + ulong st_blocks; + c_ulong st_atime; + c_ulong st_atime_nsec; + c_ulong st_mtime; + c_ulong st_mtime_nsec; + c_ulong st_ctime; + c_ulong st_ctime_nsec; + ulong st_ino; + } + } + else + { + static assert(false, "Architecture not supported."); + } + + enum S_IRUSR = 0x100; // octal 0000400 + enum S_IWUSR = 0x080; // octal 0000200 + enum S_IXUSR = 0x040; // octal 0000100 + enum S_IRWXU = 0x1C0; // octal 0000700 + + enum S_IRGRP = 0x020; // octal 0000040 + enum S_IWGRP = 0x010; // octal 0000020 + enum S_IXGRP = 0x008; // octal 0000010 + enum S_IRWXG = 0x038; // octal 0000070 + + enum S_IROTH = 0x4; // 0000004 + enum S_IWOTH = 0x2; // 0000002 + enum S_IXOTH = 0x1; // 0000001 + enum S_IRWXO = 0x7; // 0000007 + + enum S_ISUID = 0x800; // octal 0004000 + enum S_ISGID = 0x400; // octal 0002000 + enum S_ISVTX = 0x200; // octal 0001000 + + private + { + extern (D) bool S_ISTYPE( mode_t mode, uint mask ) + { + return ( mode & S_IFMT ) == mask; + } + } + + extern (D) bool S_ISBLK( mode_t mode ) { return S_ISTYPE( mode, S_IFBLK ); } + extern (D) bool S_ISCHR( mode_t mode ) { return S_ISTYPE( mode, S_IFCHR ); } + extern (D) bool S_ISDIR( mode_t mode ) { return S_ISTYPE( mode, S_IFDIR ); } + extern (D) bool S_ISFIFO( mode_t mode ) { return S_ISTYPE( mode, S_IFIFO ); } + extern (D) bool S_ISREG( mode_t mode ) { return S_ISTYPE( mode, S_IFREG ); } + extern (D) bool S_ISLNK( mode_t mode ) { return S_ISTYPE( mode, S_IFLNK ); } + extern (D) bool S_ISSOCK( mode_t mode ) { return S_ISTYPE( mode, S_IFSOCK ); } } else { static assert(false, "Unsupported platform"); } -version( Posix ) -{ - int chmod(in char*, mode_t); - int fchmod(int, mode_t); - //int fstat(int, stat_t*); - //int lstat(in char*, stat_t*); - int mkdir(in char*, mode_t); - int mkfifo(in char*, mode_t); - //int stat(in char*, stat_t*); - mode_t umask(mode_t); -} +int chmod(in char*, mode_t); +int fchmod(int, mode_t); +//int fstat(int, stat_t*); +//int lstat(in char*, stat_t*); +int mkdir(in char*, mode_t); +int mkfifo(in char*, mode_t); +//int stat(in char*, stat_t*); +mode_t umask(mode_t); version( linux ) { @@ -796,22 +939,38 @@ version( linux ) } else version (Solaris) { - static if (__USE_LARGEFILE64) + version (D_LP64) { - int fstat64(int, stat_t*); - alias fstat64 fstat; - - int lstat64(in char*, stat_t*); - alias lstat64 lstat; + int fstat(int, stat_t*); + int lstat(in char*, stat_t*); + int stat(in char*, stat_t*); - int stat64(in char*, stat_t*); - alias stat64 stat; + static if (__USE_LARGEFILE64) + { + alias fstat fstat64; + alias lstat lstat64; + alias stat stat64; + } } else { - int fstat(int, stat_t*); - int lstat(in char*, stat_t*); - int stat(in char*, stat_t*); + static if (__USE_LARGEFILE64) + { + int fstat64(int, stat_t*); + alias fstat64 fstat; + + int lstat64(in char*, stat_t*); + alias lstat64 lstat; + + int stat64(in char*, stat_t*); + alias stat64 stat; + } + else + { + int fstat(int, stat_t*); + int lstat(in char*, stat_t*); + int stat(in char*, stat_t*); + } } } else version( Posix ) @@ -893,6 +1052,21 @@ else version (Solaris) enum S_IFDIR = 0x4000; enum S_IFLNK = 0xA000; enum S_IFSOCK = 0xC000; + enum S_IFDOOR = 0xD000; + enum S_IFPORT = 0xE000; + + int mknod(in char*, mode_t, dev_t); +} +else version( Android ) +{ + enum S_IFMT = 0xF000; // octal 0170000 + enum S_IFBLK = 0x6000; // octal 0060000 + enum S_IFCHR = 0x2000; // octal 0020000 + enum S_IFIFO = 0x1000; // octal 0010000 + enum S_IFREG = 0x8000; // octal 0100000 + enum S_IFDIR = 0x4000; // octal 0040000 + enum S_IFLNK = 0xA000; // octal 0120000 + enum S_IFSOCK = 0xC000; // octal 0140000 int mknod(in char*, mode_t, dev_t); } diff --git a/libphobos/libdruntime/core/sys/posix/sys/time.d b/libphobos/libdruntime/core/sys/posix/sys/time.d index 8953ffea4..c40ba6730 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/time.d +++ b/libphobos/libdruntime/core/sys/posix/sys/time.d @@ -142,6 +142,35 @@ else version (Solaris) int setitimer(int, in itimerval*, itimerval*); int utimes(in char*, ref const(timeval)[2]); } +else version( Android ) +{ + struct timeval + { + time_t tv_sec; + suseconds_t tv_usec; + } + + struct itimerval + { + timeval it_interval; + timeval it_value; + } + + struct timezone_t + { + int tz_minuteswest; + int tz_dsttime; + } + + enum ITIMER_REAL = 0; + enum ITIMER_VIRTUAL = 1; + enum ITIMER_PROF = 2; + + int getitimer(int, itimerval*); + int gettimeofday(timeval*, timezone_t*); + int setitimer(int, in itimerval*, itimerval*); + int utimes(in char*, ref const(timeval)[2]); +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/sys/types.d b/libphobos/libdruntime/core/sys/posix/sys/types.d index 0b89c4778..74edaec0a 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/types.d +++ b/libphobos/libdruntime/core/sys/posix/sys/types.d @@ -107,7 +107,11 @@ else version( OSX ) alias int blksize_t; alias int dev_t; alias uint gid_t; - alias uint ino_t; + version( DARWIN_USE_64_BIT_INODE ) { + alias ulong ino_t; + } else { + alias uint ino_t; + } alias ushort mode_t; alias ushort nlink_t; alias long off_t; @@ -167,7 +171,7 @@ else version (Solaris) } alias uint blksize_t; - alias ulong dev_t; + alias c_ulong dev_t; alias uid_t gid_t; alias uint mode_t; alias uint nlink_t; @@ -176,6 +180,28 @@ else version (Solaris) alias c_long time_t; alias uint uid_t; } +else version( Android ) +{ + version(X86) + { + alias c_ulong blkcnt_t; + alias c_ulong blksize_t; + alias uint dev_t; + alias uint gid_t; + alias c_ulong ino_t; + alias ushort mode_t; + alias ushort nlink_t; + alias c_long off_t; + alias int pid_t; + alias c_long ssize_t; + alias c_long time_t; + alias uint uid_t; + } + else + { + static assert(false, "Architecture not supported."); + } +} else { static assert(false, "Unsupported platform"); @@ -257,6 +283,23 @@ else version (Solaris) alias id_t zoneid_t; alias id_t ctid_t; } +else version( Android ) +{ + version(X86) + { + alias c_ulong fsblkcnt_t; + alias c_ulong fsfilcnt_t; + alias c_long clock_t; + alias uint id_t; + alias int key_t; + alias c_long suseconds_t; + alias c_long useconds_t; + } + else + { + static assert(false, "Architecture not supported."); + } +} else { static assert(false, "Unsupported platform"); @@ -688,6 +731,55 @@ else version (Solaris) alias uint pthread_key_t; } +else version( Android ) +{ + version(X86) + { + struct pthread_attr_t + { + uint flags; + void* stack_base; + size_t stack_size; + size_t guard_size; + int sched_policy; + int sched_priority; + } + } + else + { + static assert(false, "Architecture not supported."); + } + + struct pthread_cond_t + { + int value; //volatile + } + + alias c_long pthread_condattr_t; + alias int pthread_key_t; + + struct pthread_mutex_t + { + int value; //volatile + } + + alias c_long pthread_mutexattr_t; + alias int pthread_once_t; //volatile + + struct pthread_rwlock_t + { + pthread_mutex_t lock; + pthread_cond_t cond; + int numLocks; + int writerThreadId; + int pendingReaders; + int pendingWriters; + void*[4] reserved; + } + + alias int pthread_rwlockattr_t; + alias c_long pthread_t; +} else { static assert(false, "Unsupported platform"); @@ -740,6 +832,9 @@ else version (Solaris) void* __pthread_barrierattrp; } } +else version( Android ) +{ +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/sys/uio.d b/libphobos/libdruntime/core/sys/posix/sys/uio.d index ace55af19..faa370d4c 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/uio.d +++ b/libphobos/libdruntime/core/sys/posix/sys/uio.d @@ -81,6 +81,24 @@ else version (Solaris) ssize_t readv(int, in iovec*, int); ssize_t writev(int, in iovec*, int); } +else version( Android ) +{ + version (X86) + { + struct iovec + { + void* iov_base; + uint iov_len; + } + } + else + { + static assert(false, "Architecture not supported."); + } + + int readv(int, in iovec*, int); + int writev(int, in iovec*, int); +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/sys/un.d b/libphobos/libdruntime/core/sys/posix/sys/un.d index 6b54a9b2f..71ad9e845 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/un.d +++ b/libphobos/libdruntime/core/sys/posix/sys/un.d @@ -58,3 +58,13 @@ else version( FreeBSD ) byte[104] sun_path; } } +else version( Android ) +{ + enum UNIX_PATH_MAX = 108; + + struct sockaddr_un + { + sa_family_t sun_family; + byte[UNIX_PATH_MAX] sun_path; + } +} diff --git a/libphobos/libdruntime/core/sys/posix/sys/utsname.d b/libphobos/libdruntime/core/sys/posix/sys/utsname.d index 35fc6fba2..2f13b21df 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/utsname.d +++ b/libphobos/libdruntime/core/sys/posix/sys/utsname.d @@ -54,4 +54,21 @@ extern (C) int uname(utsname* __name); } + else version(Android) + { + private enum SYS_NMLN = 65; + + struct utsname + { + char[SYS_NMLN] sysname; + char[SYS_NMLN] nodename; + char[SYS_NMLN] release; + // The field name is version but version is a keyword in D. + char[SYS_NMLN] _version; + char[SYS_NMLN] machine; + char[SYS_NMLN] domainname; + } + + int uname(utsname*); + } } diff --git a/libphobos/libdruntime/core/sys/posix/sys/wait.d b/libphobos/libdruntime/core/sys/posix/sys/wait.d index b21eef408..0aa4608ee 100644 --- a/libphobos/libdruntime/core/sys/posix/sys/wait.d +++ b/libphobos/libdruntime/core/sys/posix/sys/wait.d @@ -126,16 +126,25 @@ else version (Solaris) extern (D) int WSTOPSIG(int status) { return (status >> 8) & 0x7f; } extern (D) int WTERMSIG(int status) { return (status & 0x7f); } } +else version( Android ) +{ + enum WNOHANG = 1; + enum WUNTRACED = 2; + + extern (D) int WEXITSTATUS( int status ) { return ( status & 0xFF00 ) >> 8; } + extern (D) bool WIFEXITED( int status ) { return WTERMSIG(status) == 0; } + extern (D) bool WIFSIGNALED( int status ) { return WTERMSIG(status + 1) >= 2; } + extern (D) bool WIFSTOPPED( int status ) { return WTERMSIG(status) == 0x7F; } + extern (D) int WSTOPSIG( int status ) { return WEXITSTATUS(status); } + extern (D) int WTERMSIG( int status ) { return status & 0x7F; } +} else { static assert(false, "Unsupported platform"); } -version( Posix ) -{ - pid_t wait(int*); - pid_t waitpid(pid_t, int*, int); -} +pid_t wait(int*); +pid_t waitpid(pid_t, int*, int); // // XOpen (XSI) @@ -227,6 +236,17 @@ else version (Solaris) int waitid(idtype_t, id_t, siginfo_t*, int); } +else version( Android ) +{ + enum WEXITED = 4; + enum WSTOPPED = 2; + enum WCONTINUED = 8; + enum WNOWAIT = 0x01000000; + + alias int idtype_t; + + int waitid(idtype_t, id_t, siginfo_t*, int); +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/termios.d b/libphobos/libdruntime/core/sys/posix/termios.d index 959111bbb..96843ce59 100644 --- a/libphobos/libdruntime/core/sys/posix/termios.d +++ b/libphobos/libdruntime/core/sys/posix/termios.d @@ -20,6 +20,9 @@ public import core.sys.posix.sys.types; // for pid_t version (Posix): extern (C): +nothrow: +@nogc: + // // Required // diff --git a/libphobos/libdruntime/core/sys/posix/time.d b/libphobos/libdruntime/core/sys/posix/time.d index e40f37ab1..fe559f1da 100644 --- a/libphobos/libdruntime/core/sys/posix/time.d +++ b/libphobos/libdruntime/core/sys/posix/time.d @@ -22,6 +22,8 @@ public import core.sys.posix.signal; // for sigevent version (Posix): extern (C): +nothrow: +@nogc: // // Required (defined in core.stdc.time) @@ -54,6 +56,10 @@ else version (Solaris) { // Not supported. } +else version (Android) +{ + // Not supported. +} else { static assert(false, "Unsupported platform"); @@ -113,6 +119,11 @@ else version (Windows) { pragma(msg, "no Windows support for CLOCK_MONOTONIC"); } +else version (Android) +{ + enum CLOCK_MONOTONIC = 1; + enum CLOCK_MONOTONIC_HR = 5; +} else { static assert(0); @@ -258,6 +269,40 @@ else version (Solaris) int timer_gettime(timer_t, itimerspec*); int timer_settime(timer_t, int, in itimerspec*, itimerspec*); } +else version( Android ) +{ + enum CLOCK_PROCESS_CPUTIME_ID = 2; + enum CLOCK_THREAD_CPUTIME_ID = 3; + + struct itimerspec + { + timespec it_interval; + timespec it_value; + } + + enum CLOCK_REALTIME = 0; + enum CLOCK_REALTIME_HR = 4; + enum TIMER_ABSTIME = 0x01; + + version(X86) + { + alias int clockid_t; + alias int timer_t; + } + else + { + static assert(false, "Architecture not supported."); + } + + int clock_getres(int, timespec*); + int clock_gettime(int, timespec*); + int nanosleep(in timespec*, timespec*); + int timer_create(int, sigevent*, timer_t*); + int timer_delete(timer_t); + int timer_gettime(timer_t, itimerspec*); + int timer_getoverrun(timer_t); + int timer_settime(timer_t, int, in itimerspec*, itimerspec*); +} else { static assert(false, "Unsupported platform"); @@ -301,6 +346,13 @@ else version (Solaris) tm* gmtime_r(in time_t*, tm*); tm* localtime_r(in time_t*, tm*); } +else version (Android) +{ + char* asctime_r(in tm*, char*); + char* ctime_r(in time_t*, char*); + tm* gmtime_r(in time_t*, tm*); + tm* localtime_r(in time_t*, tm*); +} else { static assert(false, "Unsupported platform"); @@ -349,6 +401,13 @@ else version (Solaris) char* __strptime_dontzero(in char*, in char*, tm*); alias __strptime_dontzero strptime; } +else version( Android ) +{ + extern __gshared int daylight; + extern __gshared c_long timezone; + + char* strptime(in char*, in char*, tm*); +} else { static assert(false, "Unsupported platform"); diff --git a/libphobos/libdruntime/core/sys/posix/ucontext.d b/libphobos/libdruntime/core/sys/posix/ucontext.d index 924a1280b..5edc14d53 100644 --- a/libphobos/libdruntime/core/sys/posix/ucontext.d +++ b/libphobos/libdruntime/core/sys/posix/ucontext.d @@ -19,6 +19,8 @@ public import core.sys.posix.signal; // for sigset_t, stack_t version (Posix): extern (C): +nothrow: +@nogc: // // XOpen (XSI) @@ -191,7 +193,7 @@ version( linux ) _libc_fpstate __fpregs_mem; } } - else version (MIPS) + else version (MIPS32) { private { @@ -270,6 +272,58 @@ version( linux ) sigset_t uc_sigmask; } } + else version (MIPS64) + { + private + { + enum NGREG = 32; + enum NFPREG = 32; + + alias ulong greg_t; + alias greg_t[NGREG] gregset_t; + + struct fpregset_t + { + union fp_r_t + { + double[NFPREG] fp_dregs; + static struct fp_fregs_t + { + float _fp_fregs; + uint _fp_pad; + } fp_fregs_t[NFPREG] fp_fregs; + } fp_r_t fp_r; + } + } + + struct mcontext_t + { + gregset_t gregs; + fpregset_t fpregs; + greg_t mdhi; + greg_t hi1; + greg_t hi2; + greg_t hi3; + greg_t mdlo; + greg_t lo1; + greg_t lo2; + greg_t lo3; + greg_t pc; + uint fpc_csr; + uint used_math; + uint dsp; + uint reserved; + } + + struct ucontext_t + { + c_ulong uc_flags; + ucontext_t* uc_link; + stack_t uc_stack; + mcontext_t uc_mcontext; + sigset_t uc_sigmask; + } + } else version (PPC) { private diff --git a/libphobos/libdruntime/core/sys/posix/unistd.d b/libphobos/libdruntime/core/sys/posix/unistd.d index b47a8a096..c26299f1a 100644 --- a/libphobos/libdruntime/core/sys/posix/unistd.d +++ b/libphobos/libdruntime/core/sys/posix/unistd.d @@ -21,6 +21,8 @@ public import core.sys.posix.sys.types; // for size_t, ssize_t, uid_t, gid_t, of version (Posix): extern (C): +nothrow: +@nogc: version( Posix ) { @@ -28,64 +30,64 @@ version( Posix ) enum STDOUT_FILENO = 1; enum STDERR_FILENO = 2; - char* optarg; - int optind; - int opterr; - int optopt; + extern __gshared char* optarg; + extern __gshared int optind; + extern __gshared int opterr; + extern __gshared int optopt; int access(in char*, int); - uint alarm(uint); + uint alarm(uint) @trusted; int chdir(in char*); int chown(in char*, uid_t, gid_t); - int close(int); + int close(int) @trusted; size_t confstr(int, char*, size_t); - int dup(int); - int dup2(int, int); + int dup(int) @trusted; + int dup2(int, int) @trusted; int execl(in char*, in char*, ...); int execle(in char*, in char*, ...); int execlp(in char*, in char*, ...); int execv(in char*, in char**); int execve(in char*, in char**, in char**); int execvp(in char*, in char**); - void _exit(int); - int fchown(int, uid_t, gid_t); - pid_t fork(); - c_long fpathconf(int, int); + void _exit(int) @trusted; + int fchown(int, uid_t, gid_t) @trusted; + pid_t fork() @trusted; + c_long fpathconf(int, int) @trusted; //int ftruncate(int, off_t); char* getcwd(char*, size_t); - gid_t getegid(); - uid_t geteuid(); - gid_t getgid(); + gid_t getegid() @trusted; + uid_t geteuid() @trusted; + gid_t getgid() @trusted; int getgroups(int, gid_t *); int gethostname(char*, size_t); - char* getlogin(); + char* getlogin() @trusted; int getlogin_r(char*, size_t); int getopt(int, in char**, in char*); - pid_t getpgrp(); - pid_t getpid(); - pid_t getppid(); - uid_t getuid(); - int isatty(int); + pid_t getpgrp() @trusted; + pid_t getpid() @trusted; + pid_t getppid() @trusted; + uid_t getuid() @trusted; + int isatty(int) @trusted; int link(in char*, in char*); //off_t lseek(int, off_t, int); c_long pathconf(in char*, int); - int pause(); - int pipe(ref int[2]); + int pause() @trusted; + int pipe(ref int[2]) @trusted; ssize_t read(int, void*, size_t); ssize_t readlink(in char*, char*, size_t); int rmdir(in char*); - int setegid(gid_t); - int seteuid(uid_t); - int setgid(gid_t); - int setpgid(pid_t, pid_t); - pid_t setsid(); - int setuid(uid_t); - uint sleep(uint); + int setegid(gid_t) @trusted; + int seteuid(uid_t) @trusted; + int setgid(gid_t) @trusted; + int setpgid(pid_t, pid_t) @trusted; + pid_t setsid() @trusted; + int setuid(uid_t) @trusted; + uint sleep(uint) @trusted; int symlink(in char*, in char*); - c_long sysconf(int); - pid_t tcgetpgrp(int); - int tcsetpgrp(int, pid_t); - char* ttyname(int); + c_long sysconf(int) @trusted; + pid_t tcgetpgrp(int) @trusted; + int tcsetpgrp(int, pid_t) @trusted; + char* ttyname(int) @trusted; int ttyname_r(int, char*, size_t); int unlink(in char*); ssize_t write(int, in void*, size_t); @@ -95,32 +97,59 @@ version( linux ) { static if( __USE_FILE_OFFSET64 ) { - off_t lseek64(int, off_t, int); + off_t lseek64(int, off_t, int) @trusted; alias lseek64 lseek; } else { - off_t lseek(int, off_t, int); + off_t lseek(int, off_t, int) @trusted; } static if( __USE_LARGEFILE64 ) { - int ftruncate64(int, off_t); + int ftruncate64(int, off_t) @trusted; alias ftruncate64 ftruncate; } else { - int ftruncate(int, off_t); + int ftruncate(int, off_t) @trusted; } } else version( FreeBSD ) { - off_t lseek(int, off_t, int); - int ftruncate(int, off_t); + off_t lseek(int, off_t, int) @trusted; + int ftruncate(int, off_t) @trusted; +} +else version( Solaris ) +{ + version ( D_LP64 ) + { + off_t lseek(int, off_t, int) @trusted; + alias lseek lseek64; + + int ftruncate(int, off_t) @trusted; + alias ftruncate ftruncate64; + } + else + { + static if( __USE_LARGEFILE64 ) + { + off64_t lseek64(int, off64_t, int) @trusted; + alias lseek64 lseek; + + int ftruncate64(int, off64) @trusted; + alias ftruncate64 ftruncate; + } + else + { + off_t lseek(int, off_t, int) @trusted; + int ftruncate(int, off_t) @trusted; + } + } } else version( Posix ) { - off_t lseek(int, off_t, int); - int ftruncate(int, off_t); + off_t lseek(int, off_t, int) @trusted; + int ftruncate(int, off_t) @trusted; } version( linux ) @@ -449,6 +478,178 @@ else version( OSX ) enum F_LOCK = 1; enum F_TLOCK = 2; enum F_TEST = 3; + + enum + { + _SC_ARG_MAX = 1, + _SC_CHILD_MAX = 2, + _SC_CLK_TCK = 3, + _SC_NGROUPS_MAX = 4, + _SC_OPEN_MAX = 5, + _SC_JOB_CONTROL = 6, + _SC_SAVED_IDS = 7, + _SC_VERSION = 8, + _SC_BC_BASE_MAX = 9, + _SC_BC_DIM_MAX = 10, + _SC_BC_SCALE_MAX = 11, + _SC_BC_STRING_MAX = 12, + _SC_COLL_WEIGHTS_MAX = 13, + _SC_EXPR_NEST_MAX = 14, + _SC_LINE_MAX = 15, + _SC_RE_DUP_MAX = 16, + _SC_2_VERSION = 17, + _SC_2_C_BIND = 18, + _SC_2_C_DEV = 19, + _SC_2_CHAR_TERM = 20, + _SC_2_FORT_DEV = 21, + _SC_2_FORT_RUN = 22, + _SC_2_LOCALEDEF = 23, + _SC_2_SW_DEV = 24, + _SC_2_UPE = 25, + _SC_STREAM_MAX = 26, + _SC_TZNAME_MAX = 27, + _SC_ASYNCHRONOUS_IO = 28, + _SC_PAGESIZE = 29, + _SC_MEMLOCK = 30, + _SC_MEMLOCK_RANGE = 31, + _SC_MEMORY_PROTECTION = 32, + _SC_MESSAGE_PASSING = 33, + _SC_PRIORITIZED_IO = 34, + _SC_PRIORITY_SCHEDULING = 35, + _SC_REALTIME_SIGNALS = 36, + _SC_SEMAPHORES = 37, + _SC_FSYNC = 38, + _SC_SHARED_MEMORY_OBJECTS = 39, + _SC_SYNCHRONIZED_IO = 40, + _SC_TIMERS = 41, + _SC_AIO_LISTIO_MAX = 42, + _SC_AIO_MAX = 43, + _SC_AIO_PRIO_DELTA_MAX = 44, + _SC_DELAYTIMER_MAX = 45, + _SC_MQ_OPEN_MAX = 46, + _SC_MAPPED_FILES = 47, + _SC_RTSIG_MAX = 48, + _SC_SEM_NSEMS_MAX = 49, + _SC_SEM_VALUE_MAX = 50, + _SC_SIGQUEUE_MAX = 51, + _SC_TIMER_MAX = 52, + _SC_IOV_MAX = 56, + _SC_NPROCESSORS_CONF = 57, + _SC_NPROCESSORS_ONLN = 58, + _SC_2_PBS = 59, + _SC_2_PBS_ACCOUNTING = 60, + _SC_2_PBS_CHECKPOINT = 61, + _SC_2_PBS_LOCATE = 62, + _SC_2_PBS_MESSAGE = 63, + _SC_2_PBS_TRACK = 64, + _SC_ADVISORY_INFO = 65, + _SC_BARRIERS = 66, + _SC_CLOCK_SELECTION = 67, + _SC_CPUTIME = 68, + _SC_FILE_LOCKING = 69, + _SC_GETGR_R_SIZE_MAX = 70, + _SC_GETPW_R_SIZE_MAX = 71, + _SC_HOST_NAME_MAX = 72, + _SC_LOGIN_NAME_MAX = 73, + _SC_MONOTONIC_CLOCK = 74, + _SC_MQ_PRIO_MAX = 75, + _SC_READER_WRITER_LOCKS = 76, + _SC_REGEXP = 77, + _SC_SHELL = 78, + _SC_SPAWN = 79, + _SC_SPIN_LOCKS = 80, + _SC_SPORADIC_SERVER = 81, + _SC_THREAD_ATTR_STACKADDR = 82, + _SC_THREAD_ATTR_STACKSIZE = 83, + _SC_THREAD_CPUTIME = 84, + _SC_THREAD_DESTRUCTOR_ITERATIONS = 85, + _SC_THREAD_KEYS_MAX = 86, + _SC_THREAD_PRIO_INHERIT = 87, + _SC_THREAD_PRIO_PROTECT = 88, + _SC_THREAD_PRIORITY_SCHEDULING = 89, + _SC_THREAD_PROCESS_SHARED = 90, + _SC_THREAD_SAFE_FUNCTIONS = 91, + _SC_THREAD_SPORADIC_SERVER = 92, + _SC_THREAD_STACK_MIN = 93, + _SC_THREAD_THREADS_MAX = 94, + _SC_TIMEOUTS = 95, + _SC_THREADS = 96, + _SC_TRACE = 97, + _SC_TRACE_EVENT_FILTER = 98, + _SC_TRACE_INHERIT = 99, + _SC_TRACE_LOG = 100, + _SC_TTY_NAME_MAX = 101, + _SC_TYPED_MEMORY_OBJECTS = 102, + _SC_V6_ILP32_OFF32 = 103, + _SC_V6_ILP32_OFFBIG = 104, + _SC_V6_LP64_OFF64 = 105, + _SC_V6_LPBIG_OFFBIG = 106, + _SC_ATEXIT_MAX = 107, + _SC_XOPEN_CRYPT = 108, + _SC_XOPEN_ENH_I18N = 109, + _SC_XOPEN_LEGACY = 110, + _SC_XOPEN_REALTIME = 111, + _SC_XOPEN_REALTIME_THREADS = 112, + _SC_XOPEN_SHM = 113, + _SC_XOPEN_STREAMS = 114, + _SC_XOPEN_UNIX = 115, + _SC_XOPEN_VERSION = 116, + _SC_IPV6 = 118, + _SC_RAW_SOCKETS = 119, + _SC_SYMLOOP_MAX = 120, + _SC_XOPEN_XCU_VERSION = 121, + _SC_XBS5_ILP32_OFF32 = 122, + _SC_XBS5_ILP32_OFFBIG = 123, + _SC_XBS5_LP64_OFF64 = 124, + _SC_XBS5_LPBIG_OFFBIG = 125, + _SC_SS_REPL_MAX = 126, + _SC_TRACE_EVENT_NAME_MAX = 127, + _SC_TRACE_NAME_MAX = 128, + _SC_TRACE_SYS_MAX = 129, + _SC_TRACE_USER_EVENT_MAX = 130, + _SC_PASS_MAX = 131, + } + + enum _SC_PAGE_SIZE = _SC_PAGESIZE; + + enum + { + _CS_PATH = 1, + _CS_POSIX_V6_ILP32_OFF32_CFLAGS = 2, + _CS_POSIX_V6_ILP32_OFF32_LDFLAGS = 3, + _CS_POSIX_V6_ILP32_OFF32_LIBS = 4, + _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS = 5, + _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS = 6, + _CS_POSIX_V6_ILP32_OFFBIG_LIBS = 7, + _CS_POSIX_V6_LP64_OFF64_CFLAGS = 8, + _CS_POSIX_V6_LP64_OFF64_LDFLAGS = 9, + _CS_POSIX_V6_LP64_OFF64_LIBS = 10, + _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS = 11, + _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS = 12, + _CS_POSIX_V6_LPBIG_OFFBIG_LIBS = 13, + _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS = 14, + + _CS_XBS5_ILP32_OFF32_CFLAGS = 20, + _CS_XBS5_ILP32_OFF32_LDFLAGS = 21, + _CS_XBS5_ILP32_OFF32_LIBS = 22, + _CS_XBS5_ILP32_OFF32_LINTFLAGS = 23, + _CS_XBS5_ILP32_OFFBIG_CFLAGS = 24, + _CS_XBS5_ILP32_OFFBIG_LDFLAGS = 25, + _CS_XBS5_ILP32_OFFBIG_LIBS = 26, + _CS_XBS5_ILP32_OFFBIG_LINTFLAGS = 27, + _CS_XBS5_LP64_OFF64_CFLAGS = 28, + _CS_XBS5_LP64_OFF64_LDFLAGS = 29, + _CS_XBS5_LP64_OFF64_LIBS = 30, + _CS_XBS5_LP64_OFF64_LINTFLAGS = 31, + _CS_XBS5_LPBIG_OFFBIG_CFLAGS = 32, + _CS_XBS5_LPBIG_OFFBIG_LDFLAGS = 33, + _CS_XBS5_LPBIG_OFFBIG_LIBS = 34, + _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS = 35, + + _CS_DARWIN_USER_DIR = 65536, + _CS_DARWIN_USER_TEMP_DIR = 65537, + _CS_DARWIN_USER_CACHE_DIR = 65538, + } } else version( FreeBSD ) { @@ -462,6 +663,278 @@ else version( FreeBSD ) enum F_TLOCK = 2; enum F_TEST = 3; } +else version( Android ) +{ + enum F_OK = 0; + enum R_OK = 4; + enum W_OK = 2; + enum X_OK = 1; + + enum _SC_PAGESIZE = 0x0027; + enum _SC_NPROCESSORS_ONLN = 0x0061; +} +else version( Solaris ) +{ + enum F_OK = 0; + enum R_OK = 4; + enum W_OK = 2; + enum X_OK = 1; + + enum + { + // large file compilation environment configuration + _CS_LFS_CFLAGS = 68, + _CS_LFS_LDFLAGS = 69, + _CS_LFS_LIBS = 70, + _CS_LFS_LINTFLAGS = 71, + // transitional large file interface configuration + _CS_LFS64_CFLAGS = 72, + _CS_LFS64_LDFLAGS = 73, + _CS_LFS64_LIBS = 74, + _CS_LFS64_LINTFLAGS = 75, + + // UNIX 98 + _CS_XBS5_ILP32_OFF32_CFLAGS = 700, + _CS_XBS5_ILP32_OFF32_LDFLAGS = 701, + _CS_XBS5_ILP32_OFF32_LIBS = 702, + _CS_XBS5_ILP32_OFF32_LINTFLAGS = 703, + _CS_XBS5_ILP32_OFFBIG_CFLAGS = 705, + _CS_XBS5_ILP32_OFFBIG_LDFLAGS = 706, + _CS_XBS5_ILP32_OFFBIG_LIBS = 707, + _CS_XBS5_ILP32_OFFBIG_LINTFLAGS = 708, + _CS_XBS5_LP64_OFF64_CFLAGS = 709, + _CS_XBS5_LP64_OFF64_LDFLAGS = 710, + _CS_XBS5_LP64_OFF64_LIBS = 711, + _CS_XBS5_LP64_OFF64_LINTFLAGS = 712, + _CS_XBS5_LPBIG_OFFBIG_CFLAGS = 713, + _CS_XBS5_LPBIG_OFFBIG_LDFLAGS = 714, + _CS_XBS5_LPBIG_OFFBIG_LIBS = 715, + _CS_XBS5_LPBIG_OFFBIG_LINTFLAGS = 716, + + // UNIX 03 + _CS_POSIX_V6_ILP32_OFF32_CFLAGS = 800, + _CS_POSIX_V6_ILP32_OFF32_LDFLAGS = 801, + _CS_POSIX_V6_ILP32_OFF32_LIBS = 802, + _CS_POSIX_V6_ILP32_OFF32_LINTFLAGS = 803, + _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS = 804, + _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS = 805, + _CS_POSIX_V6_ILP32_OFFBIG_LIBS = 806, + _CS_POSIX_V6_ILP32_OFFBIG_LINTFLAGS = 807, + _CS_POSIX_V6_LP64_OFF64_CFLAGS = 808, + _CS_POSIX_V6_LP64_OFF64_LDFLAGS = 809, + _CS_POSIX_V6_LP64_OFF64_LIBS = 810, + _CS_POSIX_V6_LP64_OFF64_LINTFLAGS = 811, + _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS = 812, + _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS = 813, + _CS_POSIX_V6_LPBIG_OFFBIG_LIBS = 814, + _CS_POSIX_V6_LPBIG_OFFBIG_LINTFLAGS = 815, + _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS = 816 + } + + enum { + _SC_ARG_MAX = 1, + _SC_CHILD_MAX = 2, + _SC_CLK_TCK = 3, + _SC_NGROUPS_MAX = 4, + _SC_OPEN_MAX = 5, + _SC_JOB_CONTROL = 6, + _SC_SAVED_IDS = 7, + _SC_VERSION = 8, + + _SC_PASS_MAX = 9, + _SC_LOGNAME_MAX = 10, + _SC_PAGESIZE = 11, + _SC_XOPEN_VERSION = 12, + // 13 reserved for SVr4-ES/MP _SC_NACLS_MAX + _SC_NPROCESSORS_CONF = 14, + _SC_NPROCESSORS_ONLN = 15, + _SC_STREAM_MAX = 16, + _SC_TZNAME_MAX = 17, + + _SC_AIO_LISTIO_MAX = 18, + _SC_AIO_MAX = 19, + _SC_AIO_PRIO_DELTA_MAX = 20, + _SC_ASYNCHRONOUS_IO = 21, + _SC_DELAYTIMER_MAX = 22, + _SC_FSYNC = 23, + _SC_MAPPED_FILES = 24, + _SC_MEMLOCK = 25, + _SC_MEMLOCK_RANGE = 26, + _SC_MEMORY_PROTECTION = 27, + _SC_MESSAGE_PASSING = 28, + _SC_MQ_OPEN_MAX = 29, + _SC_MQ_PRIO_MAX = 30, + _SC_PRIORITIZED_IO = 31, + _SC_PRIORITY_SCHEDULING = 32, + _SC_REALTIME_SIGNALS = 33, + _SC_RTSIG_MAX = 34, + _SC_SEMAPHORES = 35, + _SC_SEM_NSEMS_MAX = 36, + _SC_SEM_VALUE_MAX = 37, + _SC_SHARED_MEMORY_OBJECTS = 38, + _SC_SIGQUEUE_MAX = 39, + _SC_SIGRT_MIN = 40, + _SC_SIGRT_MAX = 41, + _SC_SYNCHRONIZED_IO = 42, + _SC_TIMERS = 43, + _SC_TIMER_MAX = 44, + + _SC_2_C_BIND = 45, + _SC_2_C_DEV = 46, + _SC_2_C_VERSION = 47, + _SC_2_FORT_DEV = 48, + _SC_2_FORT_RUN = 49, + _SC_2_LOCALEDEF = 50, + _SC_2_SW_DEV = 51, + _SC_2_UPE = 52, + _SC_2_VERSION = 53, + _SC_BC_BASE_MAX = 54, + _SC_BC_DIM_MAX = 55, + _SC_BC_SCALE_MAX = 56, + _SC_BC_STRING_MAX = 57, + _SC_COLL_WEIGHTS_MAX = 58, + _SC_EXPR_NEST_MAX = 59, + _SC_LINE_MAX = 60, + _SC_RE_DUP_MAX = 61, + _SC_XOPEN_CRYPT = 62, + _SC_XOPEN_ENH_I18N = 63, + _SC_XOPEN_SHM = 64, + _SC_2_CHAR_TERM = 66, + _SC_XOPEN_XCU_VERSION = 67, + + _SC_ATEXIT_MAX = 76, + _SC_IOV_MAX = 77, + _SC_XOPEN_UNIX = 78, + + _SC_T_IOV_MAX = 79, + + _SC_PHYS_PAGES = 500, + _SC_AVPHYS_PAGES = 501, + + _SC_COHER_BLKSZ = 503, + _SC_SPLIT_CACHE = 504, + _SC_ICACHE_SZ = 505, + _SC_DCACHE_SZ = 506, + _SC_ICACHE_LINESZ = 507, + _SC_DCACHE_LINESZ = 508, + _SC_ICACHE_BLKSZ = 509, + _SC_DCACHE_BLKSZ = 510, + _SC_DCACHE_TBLKSZ = 511, + _SC_ICACHE_ASSOC = 512, + _SC_DCACHE_ASSOC = 513, + + _SC_MAXPID = 514, + _SC_STACK_PROT = 515, + _SC_NPROCESSORS_MAX = 516, + _SC_CPUID_MAX = 517, + _SC_EPHID_MAX = 518, + + _SC_THREAD_DESTRUCTOR_ITERATIONS = 568, + _SC_GETGR_R_SIZE_MAX = 569, + _SC_GETPW_R_SIZE_MAX = 570, + _SC_LOGIN_NAME_MAX = 571, + _SC_THREAD_KEYS_MAX = 572, + _SC_THREAD_STACK_MIN = 573, + _SC_THREAD_THREADS_MAX = 574, + _SC_TTY_NAME_MAX = 575, + _SC_THREADS = 576, + _SC_THREAD_ATTR_STACKADDR = 577, + _SC_THREAD_ATTR_STACKSIZE = 578, + _SC_THREAD_PRIORITY_SCHEDULING = 579, + _SC_THREAD_PRIO_INHERIT = 580, + _SC_THREAD_PRIO_PROTECT = 581, + _SC_THREAD_PROCESS_SHARED = 582, + _SC_THREAD_SAFE_FUNCTIONS = 583, + + _SC_XOPEN_LEGACY = 717, + _SC_XOPEN_REALTIME = 718, + _SC_XOPEN_REALTIME_THREADS = 719, + _SC_XBS5_ILP32_OFF32 = 720, + _SC_XBS5_ILP32_OFFBIG = 721, + _SC_XBS5_LP64_OFF64 = 722, + _SC_XBS5_LPBIG_OFFBIG = 723, + + _SC_2_PBS = 724, + _SC_2_PBS_ACCOUNTING = 725, + _SC_2_PBS_CHECKPOINT = 726, + _SC_2_PBS_LOCATE = 728, + _SC_2_PBS_MESSAGE = 729, + _SC_2_PBS_TRACK = 730, + _SC_ADVISORY_INFO = 731, + _SC_BARRIERS = 732, + _SC_CLOCK_SELECTION = 733, + _SC_CPUTIME = 734, + _SC_HOST_NAME_MAX = 735, + _SC_MONOTONIC_CLOCK = 736, + _SC_READER_WRITER_LOCKS = 737, + _SC_REGEXP = 738, + _SC_SHELL = 739, + _SC_SPAWN = 740, + _SC_SPIN_LOCKS = 741, + _SC_SPORADIC_SERVER = 742, + _SC_SS_REPL_MAX = 743, + _SC_SYMLOOP_MAX = 744, + _SC_THREAD_CPUTIME = 745, + _SC_THREAD_SPORADIC_SERVER = 746, + _SC_TIMEOUTS = 747, + _SC_TRACE = 748, + _SC_TRACE_EVENT_FILTER = 749, + _SC_TRACE_EVENT_NAME_MAX = 750, + _SC_TRACE_INHERIT = 751, + _SC_TRACE_LOG = 752, + _SC_TRACE_NAME_MAX = 753, + _SC_TRACE_SYS_MAX = 754, + _SC_TRACE_USER_EVENT_MAX = 755, + _SC_TYPED_MEMORY_OBJECTS = 756, + _SC_V6_ILP32_OFF32 = 757, + _SC_V6_ILP32_OFFBIG = 758, + _SC_V6_LP64_OFF64 = 759, + _SC_V6_LPBIG_OFFBIG = 760, + _SC_XOPEN_STREAMS = 761, + _SC_IPV6 = 762, + _SC_RAW_SOCKETS = 763, + } + enum _SC_PAGE_SIZE = _SC_PAGESIZE; + + enum { + _PC_LINK_MAX = 1, + _PC_MAX_CANON = 2, + _PC_MAX_INPUT = 3, + _PC_NAME_MAX = 4, + _PC_PATH_MAX = 5, + _PC_PIPE_BUF = 6, + _PC_NO_TRUNC = 7, + _PC_VDISABLE = 8, + _PC_CHOWN_RESTRICTED = 9, + + _PC_ASYNC_IO = 10, + _PC_PRIO_IO = 11, + _PC_SYNC_IO = 12, + + _PC_ALLOC_SIZE_MIN = 13, + _PC_REC_INCR_XFER_SIZE = 14, + _PC_REC_MAX_XFER_SIZE = 15, + _PC_REC_MIN_XFER_SIZE = 16, + _PC_REC_XFER_ALIGN = 17, + _PC_SYMLINK_MAX = 18, + _PC_2_SYMLINKS = 19, + _PC_ACL_ENABLED = 20, + _PC_MIN_HOLE_SIZE = 21, + _PC_CASE_BEHAVIOR = 22, + _PC_SATTR_ENABLED = 23, + _PC_SATTR_EXISTS = 24, + _PC_ACCESS_FILTERING = 25, + + _PC_TIMESTAMP_RESOLUTION = 26, + + _PC_FILESIZEBITS = 67, + + _PC_XATTR_ENABLED = 100, + _PC_XATTR_EXISTS = 101 + } + + enum _PC_LAST = 101; +} // // File Synchronization (FSC) @@ -472,15 +945,23 @@ int fsync(int); version( linux ) { - int fsync(int); + int fsync(int) @trusted; } else version( OSX ) { - int fsync(int); + int fsync(int) @trusted; } else version( FreeBSD ) { - int fsync(int); + int fsync(int) @trusted; +} +else version( Android ) +{ + int fsync(int) @trusted; +} +else version( Solaris ) +{ + int fsync(int) @trusted; } // @@ -492,7 +973,11 @@ int fdatasync(int); version( linux ) { - int fdatasync(int); + int fdatasync(int) @trusted; +} +else version( Android ) +{ + int fdatasync(int) @trusted; } // @@ -527,30 +1012,30 @@ version( linux ) { char* crypt(in char*, in char*); char* ctermid(char*); - void encrypt(ref char[64], int); - int fchdir(int); - c_long gethostid(); - pid_t getpgid(pid_t); - pid_t getsid(pid_t); + void encrypt(ref char[64], int) @trusted; + int fchdir(int) @trusted; + c_long gethostid() @trusted; + pid_t getpgid(pid_t) @trusted; + pid_t getsid(pid_t) @trusted; char* getwd(char*); // LEGACY int lchown(in char*, uid_t, gid_t); //int lockf(int, int, off_t); - int nice(int); + int nice(int) @trusted; //ssize_t pread(int, void*, size_t, off_t); //ssize_t pwrite(int, in void*, size_t, off_t); - pid_t setpgrp(); - int setregid(gid_t, gid_t); - int setreuid(uid_t, uid_t); + pid_t setpgrp() @trusted; + int setregid(gid_t, gid_t) @trusted; + int setreuid(uid_t, uid_t) @trusted; void swab(in void*, void*, ssize_t); - void sync(); + void sync() @trusted; //int truncate(in char*, off_t); - useconds_t ualarm(useconds_t, useconds_t); - int usleep(useconds_t); + useconds_t ualarm(useconds_t, useconds_t) @trusted; + int usleep(useconds_t) @trusted; pid_t vfork(); static if( __USE_FILE_OFFSET64 ) { - int lockf64(int, int, off_t); + int lockf64(int, int, off_t) @trusted; alias lockf64 lockf; ssize_t pread64(int, void*, size_t, off_t); @@ -564,7 +1049,7 @@ version( linux ) } else { - int lockf(int, int, off_t); + int lockf(int, int, off_t) @trusted; ssize_t pread(int, void*, size_t, off_t); ssize_t pwrite(int, in void*, size_t, off_t); int truncate(in char*, off_t); @@ -574,43 +1059,84 @@ else version( OSX ) { char* crypt(in char*, in char*); char* ctermid(char*); - void encrypt(ref char[64], int); - int fchdir(int); - c_long gethostid(); - pid_t getpgid(pid_t); - pid_t getsid(pid_t); + void encrypt(ref char[64], int) @trusted; + int fchdir(int) @trusted; + c_long gethostid() @trusted; + pid_t getpgid(pid_t) @trusted; + pid_t getsid(pid_t) @trusted; char* getwd(char*); // LEGACY int lchown(in char*, uid_t, gid_t); - int lockf(int, int, off_t); - int nice(int); + int lockf(int, int, off_t) @trusted; + int nice(int) @trusted; ssize_t pread(int, void*, size_t, off_t); ssize_t pwrite(int, in void*, size_t, off_t); - pid_t setpgrp(); - int setregid(gid_t, gid_t); - int setreuid(uid_t, uid_t); + pid_t setpgrp() @trusted; + int setregid(gid_t, gid_t) @trusted; + int setreuid(uid_t, uid_t) @trusted; void swab(in void*, void*, ssize_t); - void sync(); + void sync() @trusted; int truncate(in char*, off_t); - useconds_t ualarm(useconds_t, useconds_t); - int usleep(useconds_t); + useconds_t ualarm(useconds_t, useconds_t) @trusted; + int usleep(useconds_t) @trusted; pid_t vfork(); } else version( FreeBSD ) { char* crypt(in char*, in char*); //char* ctermid(char*); + void encrypt(ref char[64], int) @trusted; + int fchdir(int) @trusted; + c_long gethostid() @trusted; + int getpgid(pid_t) @trusted; + int getsid(pid_t) @trusted; + char* getwd(char*); // LEGACY + int lchown(in char*, uid_t, gid_t); + int lockf(int, int, off_t) @trusted; + int nice(int) @trusted; + ssize_t pread(int, void*, size_t, off_t); + ssize_t pwrite(int, in void*, size_t, off_t); + int setpgrp(pid_t, pid_t) @trusted; + int setregid(gid_t, gid_t) @trusted; + int setreuid(uid_t, uid_t) @trusted; + void swab(in void*, void*, ssize_t); + void sync() @trusted; + int truncate(in char*, off_t); + useconds_t ualarm(useconds_t, useconds_t) @trusted; + int usleep(useconds_t) @trusted; + pid_t vfork(); +} +else version( Android ) +{ + int fchdir(int) @trusted; + pid_t getpgid(pid_t) @trusted; + int lchown(in char*, uid_t, gid_t); + int nice(int) @trusted; + ssize_t pread(int, void*, size_t, off_t); + ssize_t pwrite(int, in void*, size_t, off_t); + int setpgrp() @trusted; + int setregid(gid_t, gid_t) @trusted; + int setreuid(uid_t, uid_t) @trusted; + int sync() @trusted; + int truncate(in char*, off_t); + int usleep(c_ulong) @trusted; + pid_t vfork(); +} +else version( Solaris ) +{ + char* crypt(in char*, in char*); + char* ctermid(char*); void encrypt(ref char[64], int); int fchdir(int); c_long gethostid(); - int getpgid(pid_t); - int getsid(pid_t); + pid_t getpgid(pid_t); + pid_t getsid(pid_t); char* getwd(char*); // LEGACY int lchown(in char*, uid_t, gid_t); int lockf(int, int, off_t); int nice(int); ssize_t pread(int, void*, size_t, off_t); ssize_t pwrite(int, in void*, size_t, off_t); - int setpgrp(pid_t, pid_t); + pid_t setpgrp(); int setregid(gid_t, gid_t); int setreuid(uid_t, uid_t); void swab(in void*, void*, ssize_t); @@ -619,4 +1145,43 @@ else version( FreeBSD ) useconds_t ualarm(useconds_t, useconds_t); int usleep(useconds_t); pid_t vfork(); + + version (D_LP64) + { + int lockf(int, int, off_t); + alias lockf lockf64; + + ssize_t pread(int, void*, size_t, off_t); + alias pread pread64; + + ssize_t pwrite(int, in void*, size_t, off_t); + alias pwrite pwrite64; + + int truncate(in char*, off_t); + alias truncate truncate64; + } + else + { + static if( __USE_FILE_OFFSET64 ) + { + int lockf64(int, int, off64_t); + alias lockf64 lockf; + + ssize_t pread64(int, void*, size_t, off64_t); + alias pread64 pread; + + ssize_t pwrite64(int, in void*, size_t, off_t); + alias pwrite64 pwrite; + + int truncate64(in char*, off_t); + alias truncate64 truncate; + } + else + { + int lockf(int, int, off_t); + ssize_t pread(int, void*, size_t, off_t); + ssize_t pwrite(int, in void*, size_t, off_t); + int truncate(in char*, off_t); + } + } } diff --git a/libphobos/libdruntime/core/sys/posix/utime.d b/libphobos/libdruntime/core/sys/posix/utime.d index 25b8d8446..fa230ff1a 100644 --- a/libphobos/libdruntime/core/sys/posix/utime.d +++ b/libphobos/libdruntime/core/sys/posix/utime.d @@ -19,6 +19,8 @@ public import core.sys.posix.sys.types; // for time_t version (Posix): extern (C): +nothrow: +@nogc: // // Required @@ -63,3 +65,13 @@ else version( FreeBSD ) int utime(in char*, in utimbuf*); } +else version( Android ) +{ + struct utimbuf + { + time_t actime; + time_t modtime; + } + + int utime(in char*, in utimbuf*); +} diff --git a/libphobos/libdruntime/core/sys/windows/dbghelp.d b/libphobos/libdruntime/core/sys/windows/dbghelp.d index 3ccb662e5..6c55c3c8c 100644 --- a/libphobos/libdruntime/core/sys/windows/dbghelp.d +++ b/libphobos/libdruntime/core/sys/windows/dbghelp.d @@ -2,16 +2,13 @@ * ... * * Copyright: Copyright Benjamin Thaut 2010 - 2011. - * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Benjamin Thaut, Sean Kelly * Source: $(DRUNTIMESRC core/sys/windows/_stacktrace.d) */ -/* Copyright Benjamin Thaut 2010 - 2011. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.sys.windows.dbghelp; version (Windows): @@ -171,7 +168,7 @@ struct IMAGEHLP_CBA_READ_MEMORY DWORD *bytesread; }; -struct API_VERSION +struct API_VERSION { USHORT MajorVersion; USHORT MinorVersion; diff --git a/libphobos/libdruntime/core/sys/windows/dll.d b/libphobos/libdruntime/core/sys/windows/dll.d index f0a0a2ecb..022bfe1b0 100644 --- a/libphobos/libdruntime/core/sys/windows/dll.d +++ b/libphobos/libdruntime/core/sys/windows/dll.d @@ -9,8 +9,7 @@ * Source: $(DRUNTIMESRC src/core/sys/windows/_dll.d) */ -/* - * NOTE: This file has been patched from the original DMD distribution to +/* NOTE: This file has been patched from the original DMD distribution to * work with the GDC compiler. */ module core.sys.windows.dll; @@ -311,7 +310,7 @@ public: * * _tls_index is initialized by the compiler to 0, so we can use this as a test. */ - bool dll_fixTLS( HINSTANCE hInstance, void* tlsstart, void* tlsend, void* tls_callbacks_a, int* tlsindex ) + bool dll_fixTLS( HINSTANCE hInstance, void* tlsstart, void* tlsend, void* tls_callbacks_a, int* tlsindex ) nothrow { version (Win64) return true; // fixed @@ -349,6 +348,8 @@ public: if( !entry ) return false; + scope (failure) assert(0); // enforce nothrow, Bugzilla 13561 + if( !enumProcessThreads( function (uint id, void* context) nothrow { dll_aux.LdrpTlsListEntry* entry = cast(dll_aux.LdrpTlsListEntry*) context; diff --git a/libphobos/libdruntime/core/sys/windows/stacktrace.d b/libphobos/libdruntime/core/sys/windows/stacktrace.d index c0e5ce9a7..9f9a0e7b1 100644 --- a/libphobos/libdruntime/core/sys/windows/stacktrace.d +++ b/libphobos/libdruntime/core/sys/windows/stacktrace.d @@ -2,16 +2,13 @@ * ... * * Copyright: Copyright Benjamin Thaut 2010 - 2013. - * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Benjamin Thaut, Sean Kelly * Source: $(DRUNTIMESRC core/sys/windows/_stacktrace.d) */ -/* Copyright Benjamin Thaut 2010 - 2012. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ module core.sys.windows.stacktrace; version(Windows): @@ -52,7 +49,7 @@ public: static enum INTERNALFRAMES = 3; else static enum INTERNALFRAMES = 2; - + skip += INTERNALFRAMES; //skip the stack frames within the StackTrace class } else @@ -62,7 +59,7 @@ public: static enum INTERNALFRAMES = 1; else static enum INTERNALFRAMES = 1; - + skip += INTERNALFRAMES; } if( initialized ) @@ -141,12 +138,12 @@ private: auto dbghelp = DbgHelp.get(); if(dbghelp is null) return []; // dbghelp.dll not available - + if(RtlCaptureStackBackTrace !is null && context is null) { size_t[63] buffer = void; // On windows xp the sum of "frames to skip" and "frames to capture" can't be greater then 63 auto backtraceLength = RtlCaptureStackBackTrace(cast(ULONG)skip, cast(ULONG)(buffer.length - skip), cast(void**)buffer.ptr, null); - + // If we get a backtrace and it does not have the maximum length use it. // Otherwise rely on tracing through StackWalk64 which is slower but works when no frame pointers are available. if(backtraceLength > 1 && backtraceLength < buffer.length - skip) @@ -186,7 +183,7 @@ private: STACKFRAME64 stackframe; with (stackframe) { - version(X86) + version(X86) { enum Flat = ADDRESS_MODE.AddrModeFlat; AddrPC.Offset = ctxt.Eip; @@ -214,9 +211,9 @@ private: ulong[] result; size_t frameNum = 0; - + // do ... while so that we don't skip the first stackframe - do + do { if( stackframe.AddrPC.Offset == stackframe.AddrReturn.Offset ) { @@ -383,19 +380,19 @@ shared static this() if( dbghelp is null ) return; // dbghelp.dll not available - + auto kernel32Handle = LoadLibraryA( "kernel32.dll" ); if(kernel32Handle !is null) { RtlCaptureStackBackTrace = cast(RtlCaptureStackBackTraceFunc) GetProcAddress(kernel32Handle, "RtlCaptureStackBackTrace"); - debug(PRINTF) + debug(PRINTF) { if(RtlCaptureStackBackTrace !is null) printf("Found RtlCaptureStackBackTrace\n"); } } - debug(PRINTF) + debug(PRINTF) { API_VERSION* dbghelpVersion = dbghelp.ImagehlpApiVersion(); printf("DbgHelp Version %d.%d.%d\n", dbghelpVersion.MajorVersion, dbghelpVersion.MinorVersion, dbghelpVersion.Revision); diff --git a/libphobos/libdruntime/core/sys/windows/threadaux.d b/libphobos/libdruntime/core/sys/windows/threadaux.d index 086cf8dea..d073e3b65 100644 --- a/libphobos/libdruntime/core/sys/windows/threadaux.d +++ b/libphobos/libdruntime/core/sys/windows/threadaux.d @@ -2,16 +2,16 @@ * This module provides OS specific helper function for threads support * * Copyright: Copyright Digital Mars 2010 - 2010. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) + * Source: $(DRUNTIMESRC core/sys/windows/_threadaux.d) * Authors: Rainer Schuetze */ -/* Copyright Digital Mars 2010 - 2010. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) +/* NOTE: This file has been patched from the original DMD distribution to + * work with the GDC compiler. */ - module core.sys.windows.threadaux; version( Windows ) @@ -64,7 +64,7 @@ private: long CreateTime; long UserTime; long KernelTime; - UNICODE_STRING ImageName; + UNICODE_STRING ImageName; int BasePriority; PTID /*Unique*/ProcessId; PTID InheritedFromUniqueProcessId; diff --git a/libphobos/libdruntime/core/sys/windows/windows.d b/libphobos/libdruntime/core/sys/windows/windows.d index 3b959f52c..7a5ed5ae0 100644 --- a/libphobos/libdruntime/core/sys/windows/windows.d +++ b/libphobos/libdruntime/core/sys/windows/windows.d @@ -3,18 +3,14 @@ * States and other countries. * * Copyright: Copyright Digital Mars 2000 - 2009. - * License: Boost License 1.0. + * License: Distributed under the + * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). + * (See accompanying file LICENSE) * Authors: Walter Bright, Sean Kelly, Alex Rønne Petersen + * Source: $(DRUNTIMESRC core/sys/windows/_windows.d) */ -/* Copyright Digital Mars 2000 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ - -/* - * NOTE: This file has been patched from the original DMD distribution to +/* NOTE: This file has been patched from the original DMD distribution to * work with the GDC compiler. */ module core.sys.windows.windows; @@ -22,6 +18,7 @@ module core.sys.windows.windows; version (Windows): extern (Windows): nothrow: +//@nogc: alias uint ULONG; alias ULONG *PULONG; @@ -151,7 +148,8 @@ else // Win32 alias WORD ATOM; version (all) -{ // Properly prototyped versions +{ + // Properly prototyped versions alias INT_PTR function(HWND, UINT, WPARAM, LPARAM) DLGPROC; alias VOID function(HWND, UINT, UINT_PTR, DWORD) TIMERPROC; alias BOOL function(HDC, LPARAM, int) GRAYSTRINGPROC; @@ -186,7 +184,7 @@ else alias FARPROC DRAWSTATEPROC; } -extern (D) pure +extern (D) pure @nogc { WORD HIWORD(long x) { return cast(WORD)((x >> 16) & 0xFFFF); } WORD LOWORD(long x) { return cast(WORD)x; } @@ -471,7 +469,7 @@ struct WIN32_FILE_ATTRIBUTE_DATA } alias WIN32_FILE_ATTRIBUTE_DATA* LPWIN32_FILE_ATTRIBUTE_DATA; -export +export @nogc { BOOL SetCurrentDirectoryA(LPCSTR lpPathName); BOOL SetCurrentDirectoryW(LPCWSTR lpPathName); @@ -505,8 +503,7 @@ BOOL FindNextFileA(HANDLE hFindFile, WIN32_FIND_DATA* lpFindFileData); BOOL FindNextFileW(HANDLE hFindFile, WIN32_FIND_DATAW* lpFindFileData); BOOL GetExitCodeThread(HANDLE hThread, DWORD *lpExitCode); BOOL GetExitCodeProcess(HANDLE hProcess, DWORD *lpExitCode); -DWORD GetLastError(); -void SetLastError(DWORD dwErrCode); +DWORD GetLastError() @trusted; DWORD GetFileAttributesA(in char *lpFileName); DWORD GetFileAttributesW(in wchar *lpFileName); BOOL GetFileAttributesExA(LPCSTR, GET_FILEEX_INFO_LEVELS, PVOID); @@ -543,6 +540,8 @@ struct MEMORYSTATUS { }; alias MEMORYSTATUS *LPMEMORYSTATUS; +@nogc +{ HMODULE LoadLibraryA(LPCSTR lpLibFileName); HMODULE LoadLibraryW(LPCWSTR lpLibFileName); FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName); @@ -550,6 +549,7 @@ DWORD GetVersion(); BOOL FreeLibrary(HMODULE hLibModule); void FreeLibraryAndExitThread(HMODULE hLibModule, DWORD dwExitCode); BOOL DisableThreadLibraryCalls(HMODULE hLibModule); +} // // Registry Specific Access Rights. @@ -661,12 +661,13 @@ enum MB_MISCMASK = 0x0000C000, } - +@nogc +{ int MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType); int MessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType); int MessageBoxExA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType, WORD wLanguageId); int MessageBoxExW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType, WORD wLanguageId); - +} enum : HKEY { @@ -704,6 +705,8 @@ enum REG_LEGAL_OPTION = (REG_OPTION_RESERVED | REG_OPTION_NON_VOLATILE | REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK | REG_OPTION_BACKUP_RESTORE | REG_OPTION_OPEN_LINK), } +@nogc +{ export LONG RegDeleteKeyA(in HKEY hKey, LPCSTR lpSubKey); export LONG RegDeleteKeyW(in HKEY hKey, LPCWSTR lpSubKey); export LONG RegDeleteValueA(in HKEY hKey, LPCSTR lpValueName); @@ -752,6 +755,7 @@ export LONG RegOpenCurrentUser(REGSAM samDesired, PHKEY phkResult); export LONG RegConnectRegistryA(LPCSTR lpMachineName, HKEY hKey, PHKEY phkResult); export LONG RegConnectRegistryW(LPCWSTR lpMachineName, HKEY hKey, PHKEY phkResult); +} struct MEMORY_BASIC_INFORMATION { PVOID BaseAddress; @@ -855,7 +859,7 @@ enum FILE_GENERIC_EXECUTE = cast(int)(STANDARD_RIGHTS_EXECUTE | FILE_READ_ATTRIBUTES | FILE_EXECUTE | SYNCHRONIZE), } -export +export @nogc { BOOL FreeResource(HGLOBAL hResData); LPVOID LockResource(HGLOBAL hResData); @@ -926,6 +930,8 @@ enum TIME_ZONE_ID_DAYLIGHT = 2, } +@nogc +{ export void GetSystemTime(SYSTEMTIME* lpSystemTime); export BOOL GetFileTime(HANDLE hFile, FILETIME *lpCreationTime, FILETIME *lpLastAccessTime, FILETIME *lpLastWriteTime); export void GetSystemTimeAsFileTime(FILETIME* lpSystemTimeAsFileTime); @@ -950,6 +956,7 @@ export BOOL SetSystemTimeAdjustment(DWORD dwTimeAdjustment, BOOL bTimeAdjustment export BOOL GetSystemTimeAdjustment(DWORD* lpTimeAdjustment, DWORD* lpTimeIncrement, BOOL* lpTimeAdjustmentDisabled); export DWORD FormatMessageA(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPSTR lpBuffer, DWORD nSize, void* *Arguments); export DWORD FormatMessageW(DWORD dwFlags, LPCVOID lpSource, DWORD dwMessageId, DWORD dwLanguageId, LPWSTR lpBuffer, DWORD nSize, void* *Arguments); +} enum { @@ -1160,10 +1167,12 @@ enum // SUBLANGID - extract sublanguage id from a language id. // +pure @nogc +{ int MAKELANGID(int p, int s) { return ((cast(WORD)s) << 10) | cast(WORD)p; } WORD PRIMARYLANGID(int lgid) { return cast(WORD)(lgid & 0x3ff); } WORD SUBLANGID(int lgid) { return cast(WORD)(lgid >> 10); } - +} version (Win64) { @@ -1553,14 +1562,19 @@ struct SYSTEM_INFO alias SYSTEM_INFO* LPSYSTEM_INFO; +@nogc +{ export void GetSystemInfo(LPSYSTEM_INFO lpSystemInfo); export void GetNativeSystemInfo(LPSYSTEM_INFO lpSystemInfo); +} enum : DWORD { MAX_COMPUTERNAME_LENGTH = 15, } +@nogc +{ export BOOL GetComputerNameA(LPSTR lpBuffer, LPDWORD nSize); export BOOL GetComputerNameW(LPWSTR lpBuffer, LPDWORD nSize); export BOOL SetComputerNameA(LPCSTR lpComputerName); @@ -1588,12 +1602,13 @@ export DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); export DWORD WaitForMultipleObjects(DWORD nCount, HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds); export void Sleep(DWORD dwMilliseconds); export BOOL SwitchToThread(); +} // Synchronization version (MinGW) { - extern(C) + extern(C) @nogc { LONG _InterlockedIncrement(LPLONG lpAddend); LONG _InterlockedDecrement(LPLONG lpAddend); @@ -1616,7 +1631,7 @@ version (MinGW) } else { - export + export @nogc { LONG InterlockedIncrement(LPLONG lpAddend); LONG InterlockedDecrement(LPLONG lpAddend); @@ -1633,9 +1648,11 @@ else } - +@nogc +{ export BOOL QueryPerformanceCounter(long* lpPerformanceCount); export BOOL QueryPerformanceFrequency(long* lpFrequency); +} enum { @@ -2104,7 +2121,7 @@ enum DCX_VALIDATE = 0x00200000, } -export +export @nogc { BOOL UpdateWindow(HWND hWnd); HWND SetActiveWindow(HWND hWnd); @@ -2147,7 +2164,7 @@ enum RDW_NOFRAME = 0x0800, } -export +export @nogc { BOOL GetClientRect(HWND hWnd, LPRECT lpRect); BOOL GetWindowRect(HWND hWnd, LPRECT lpRect); @@ -2394,7 +2411,7 @@ struct PIXELFORMATDESCRIPTOR } alias PIXELFORMATDESCRIPTOR* PPIXELFORMATDESCRIPTOR, LPPIXELFORMATDESCRIPTOR; -export +export @nogc { BOOL RoundRect(HDC, int, int, int, int, int, int); BOOL ResizePalette(HPALETTE, UINT); @@ -2462,18 +2479,21 @@ struct POINT alias POINT* PPOINT, NPPOINT, LPPOINT; -export +export @nogc { BOOL MoveToEx(HDC, int, int, LPPOINT); BOOL TextOutA(HDC, int, int, LPCSTR, int); BOOL TextOutW(HDC, int, int, LPCWSTR, int); } +@nogc +{ export void PostQuitMessage(int nExitCode); export LRESULT DefWindowProcA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); export LRESULT DefWindowProcW(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); export HMODULE GetModuleHandleA(LPCSTR lpModuleName); export HMODULE GetModuleHandleW(LPCWSTR lpModuleName); +} alias LRESULT function (HWND, UINT, WPARAM, LPARAM) WNDPROC; @@ -2638,7 +2658,7 @@ enum CS_IME = 0x00010000, } -export +export @nogc { HICON LoadIconA(HINSTANCE hInstance, LPCSTR lpIconName); HICON LoadIconW(HINSTANCE hInstance, LPCWSTR lpIconName); @@ -2719,6 +2739,8 @@ enum : HWND HWND_DESKTOP = cast(HWND)0, } +@nogc +{ export ATOM RegisterClassA(in WNDCLASSA *lpWndClass); export ATOM RegisterClassExA(in WNDCLASSEXA *lpWndClass); export ATOM RegisterClassW(in WNDCLASSW *lpWndClass); @@ -2787,6 +2809,28 @@ HWND CreateWindowW( return CreateWindowExW(0, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam); } +export BOOL DestroyWindow(HWND hWnd); +export BOOL SetWindowPos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags); +} + +enum : uint +{ + SWP_ASYNCWINDOWPOS = 0x4000, + SWP_DEFERERASE = 0x2000, + SWP_DRAWFRAME = 0x0020, + SWP_FRAMECHANGED = 0x0020, + SWP_HIDEWINDOW = 0x0080, + SWP_NOACTIVATE = 0x0010, + SWP_NOCOPYBITS = 0x0100, + SWP_NOMOVE = 0x0002, + SWP_NOOWNERZORDER = 0x0200, + SWP_NOREDRAW = 0x0008, + SWP_NOREPOSITION = 0x0200, + SWP_NOSENDCHANGING = 0x0400, + SWP_NOSIZE = 0x0001, + SWP_NOZORDER = 0x0004, + SWP_SHOWWINDOW = 0x0040, +} /* * Message structure @@ -2801,7 +2845,7 @@ struct MSG { } alias MSG* PMSG, NPMSG, LPMSG; -export +export @nogc { BOOL GetMessageA(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax); BOOL GetMessageW(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax); @@ -2819,14 +2863,17 @@ According to MSDN about a value returned by GetEnvironmentString: So return type of GetEnvironmentStrings is changed from LPWCH (as in *.h file) to LPCWCH. FreeEnvironmentStrings's argument type is changed correspondingly. */ +@nogc +{ export LPCWCH GetEnvironmentStringsW(); export BOOL FreeEnvironmentStringsW(LPCWCH lpszEnvironmentBlock); export DWORD GetEnvironmentVariableW(LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize); export BOOL SetEnvironmentVariableW(LPCWSTR lpName, LPCWSTR lpValue); export DWORD ExpandEnvironmentStringsA(LPCSTR lpSrc, LPSTR lpDst, DWORD nSize); export DWORD ExpandEnvironmentStringsW(LPCWSTR lpSrc, LPWSTR lpDst, DWORD nSize); +} -export +export @nogc { BOOL IsValidCodePage(UINT CodePage); UINT GetACP(); @@ -2844,6 +2891,8 @@ enum : UINT CP_UTF8 = 65001 } +@nogc +{ export HANDLE CreateFileMappingA(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCSTR lpName); export HANDLE CreateFileMappingW(HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCWSTR lpName); @@ -2856,10 +2905,12 @@ export BOOL UnmapViewOfFile(LPCVOID lpBaseAddress); export HGDIOBJ GetStockObject(int); export BOOL ShowWindow(HWND hWnd, int nCmdShow); +} /* Stock Logical Objects */ enum -{ WHITE_BRUSH = 0, +{ + WHITE_BRUSH = 0, LTGRAY_BRUSH = 1, GRAY_BRUSH = 2, DKGRAY_BRUSH = 3, @@ -2884,7 +2935,8 @@ enum * ShowWindow() Commands */ enum -{ SW_HIDE = 0, +{ + SW_HIDE = 0, SW_SHOWNORMAL = 1, SW_NORMAL = 1, SW_SHOWMINIMIZED = 2, @@ -2924,13 +2976,14 @@ struct TEXTMETRICA BYTE tmCharSet; } -export BOOL GetTextMetricsA(HDC, TEXTMETRICA*); +export @nogc BOOL GetTextMetricsA(HDC, TEXTMETRICA*); /* * Scroll Bar Constants */ enum -{ SB_HORZ = 0, +{ + SB_HORZ = 0, SB_VERT = 1, SB_CTL = 2, SB_BOTH = 3, @@ -2940,7 +2993,8 @@ enum * Scroll Bar Commands */ enum -{ SB_LINEUP = 0, +{ + SB_LINEUP = 0, SB_LINELEFT = 0, SB_LINEDOWN = 1, SB_LINERIGHT = 1, @@ -2957,27 +3011,47 @@ enum SB_ENDSCROLL = 8, } +@nogc +{ export int SetScrollPos(HWND hWnd, int nBar, int nPos, BOOL bRedraw); export int GetScrollPos(HWND hWnd, int nBar); export BOOL SetScrollRange(HWND hWnd, int nBar, int nMinPos, int nMaxPos, BOOL bRedraw); export BOOL GetScrollRange(HWND hWnd, int nBar, LPINT lpMinPos, LPINT lpMaxPos); export BOOL ShowScrollBar(HWND hWnd, int wBar, BOOL bShow); export BOOL EnableScrollBar(HWND hWnd, UINT wSBflags, UINT wArrows); +} /* * LockWindowUpdate API */ +@nogc +{ export BOOL LockWindowUpdate(HWND hWndLock); export BOOL ScrollWindow(HWND hWnd, int XAmount, int YAmount, RECT* lpRect, RECT* lpClipRect); export BOOL ScrollDC(HDC hDC, int dx, int dy, RECT* lprcScroll, RECT* lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate); export int ScrollWindowEx(HWND hWnd, int dx, int dy, RECT* prcScroll, RECT* prcClip, HRGN hrgnUpdate, LPRECT prcUpdate, UINT flags); +} + +/* + * Key State API + */ + +@nogc +{ +export SHORT GetKeyState(int vKey); +export SHORT GetAsyncKeyState(int vKey); +export BOOL GetKeyboardState(PBYTE lpKeyState); +export BOOL SetKeyboardState(LPBYTE lpKeyState); +export UINT MapVirtualKey(UINT uCode, UINT uMapType); +} /* * Virtual Keys, Standard Set */ enum -{ VK_LBUTTON = 0x01, +{ + VK_LBUTTON = 0x01, VK_RBUTTON = 0x02, VK_CANCEL = 0x03, VK_MBUTTON = 0x04, /* NOT contiguous with L & RBUTTON */ @@ -3092,7 +3166,7 @@ enum VK_OEM_CLEAR = 0xFE, } -export LRESULT SendMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +export @nogc LRESULT SendMessageA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); alias UINT function (HWND, UINT, WPARAM, LPARAM) LPOFNHOOKPROC; @@ -3144,6 +3218,8 @@ struct OPENFILENAMEW { } alias OPENFILENAMEW *LPOPENFILENAMEW; +@nogc +{ BOOL GetOpenFileNameA(LPOPENFILENAMEA); BOOL GetOpenFileNameW(LPOPENFILENAMEW); @@ -3152,6 +3228,7 @@ BOOL GetSaveFileNameW(LPOPENFILENAMEW); short GetFileTitleA(LPCSTR, LPSTR, WORD); short GetFileTitleW(LPCWSTR, LPWSTR, WORD); +} enum { @@ -3173,12 +3250,14 @@ struct BITMAP } alias BITMAP* PBITMAP, NPBITMAP, LPBITMAP; - +@nogc +{ export HDC CreateCompatibleDC(HDC); export int GetObjectA(HGDIOBJ, int, LPVOID); export int GetObjectW(HGDIOBJ, int, LPVOID); export BOOL DeleteDC(HDC); +} struct LOGFONTA { @@ -3199,6 +3278,8 @@ struct LOGFONTA } alias LOGFONTA* PLOGFONTA, NPLOGFONTA, LPLOGFONTA; +@nogc +{ export HMENU LoadMenuA(HINSTANCE hInstance, LPCSTR lpMenuName); export HMENU LoadMenuW(HINSTANCE hInstance, LPCWSTR lpMenuName); @@ -3236,6 +3317,7 @@ export HWND ChildWindowFromPoint(HWND hWndParent, POINT Point); export BOOL TrackPopupMenu(HMENU hMenu, UINT uFlags, int x, int y, int nReserved, HWND hWnd, RECT *prcRect); +} align (2) struct DLGTEMPLATE { DWORD style; @@ -3259,9 +3341,9 @@ alias DLGTEMPLATE *LPCDLGTEMPLATEW; alias LPCDLGTEMPLATEA LPCDLGTEMPLATE; -export int DialogBoxParamA(HINSTANCE hInstance, LPCSTR lpTemplateName, +export @nogc int DialogBoxParamA(HINSTANCE hInstance, LPCSTR lpTemplateName, HWND hWndParent, DLGPROC lpDialogFunc, LPARAM dwInitParam); -export int DialogBoxIndirectParamA(HINSTANCE hInstance, +export @nogc int DialogBoxIndirectParamA(HINSTANCE hInstance, LPCDLGTEMPLATEA hDialogTemplate, HWND hWndParent, DLGPROC lpDialogFunc, LPARAM dwInitParam); @@ -3306,6 +3388,8 @@ enum SND_ALIAS_START = 0, /* alias base */ } +@nogc +{ export BOOL PlaySoundA(LPCSTR pszSound, HMODULE hmod, DWORD fdwSound); export BOOL PlaySoundW(LPCWSTR pszSound, HMODULE hmod, DWORD fdwSound); @@ -3315,9 +3399,10 @@ export int GetMetaRgn(HDC, HRGN); export HGDIOBJ GetCurrentObject(HDC, UINT); export BOOL GetCurrentPositionEx(HDC, LPPOINT); export int GetDeviceCaps(HDC, int); +} struct LOGPEN - { +{ UINT lopnStyle; POINT lopnWidth; COLORREF lopnColor; @@ -3352,6 +3437,8 @@ enum PS_TYPE_MASK = 0x000F0000, } +@nogc +{ export HPALETTE CreatePalette(LOGPALETTE *); export HPEN CreatePen(int, int, COLORREF); export HPEN CreatePenIndirect(LOGPEN *); @@ -3394,8 +3481,9 @@ export BOOL CheckRadioButton(HWND hDlg, int nIDFirstButton, int nIDLastButton, export UINT IsDlgButtonChecked(HWND hDlg, int nIDButton); export HWND SetFocus(HWND hWnd); +} -extern (C) +extern (C) @nogc { export int wsprintfA(LPSTR, LPCSTR, ...); export int wsprintfW(LPWSTR, LPCWSTR, ...); @@ -3412,9 +3500,12 @@ enum : uint WAIT_FAILED = uint.max, } +@nogc +{ export HANDLE CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName); export HANDLE OpenSemaphoreA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName); export BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount); +} struct COORD { SHORT X; @@ -3585,6 +3676,8 @@ enum ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002, } +@nogc +{ BOOL PeekConsoleInputA(HANDLE hConsoleInput, PINPUT_RECORD lpBuffer, DWORD nLength, LPDWORD lpNumberOfEventsRead); BOOL PeekConsoleInputW(HANDLE hConsoleInput, PINPUT_RECORD lpBuffer, DWORD nLength, LPDWORD lpNumberOfEventsRead); BOOL ReadConsoleInputA(HANDLE hConsoleInput, PINPUT_RECORD lpBuffer, DWORD nLength, LPDWORD lpNumberOfEventsRead); @@ -3620,7 +3713,6 @@ BOOL ScrollConsoleScreenBufferA(HANDLE hConsoleOutput, in SMALL_RECT *lpScrollRe BOOL ScrollConsoleScreenBufferW(HANDLE hConsoleOutput, in SMALL_RECT *lpScrollRectangle, in SMALL_RECT *lpClipRectangle, COORD dwDestinationOrigin, in CHAR_INFO *lpFill); BOOL SetConsoleWindowInfo(HANDLE hConsoleOutput, BOOL bAbsolute, in SMALL_RECT *lpConsoleWindow); BOOL SetConsoleTextAttribute(HANDLE hConsoleOutput, WORD wAttributes); -alias BOOL function(DWORD CtrlType) PHANDLER_ROUTINE; BOOL SetConsoleCtrlHandler(PHANDLER_ROUTINE HandlerRoutine, BOOL Add); BOOL GenerateConsoleCtrlEvent( DWORD dwCtrlEvent, DWORD dwProcessGroupId); BOOL AllocConsole(); @@ -3638,6 +3730,9 @@ UINT GetConsoleCP(); BOOL SetConsoleCP( UINT wCodePageID); UINT GetConsoleOutputCP(); BOOL SetConsoleOutputCP(UINT wCodePageID); +} + +alias BOOL function(DWORD CtrlType) PHANDLER_ROUTINE; enum { @@ -3728,6 +3823,7 @@ enum SM_CMETRICS = 75, } +@nogc int GetSystemMetrics(int nIndex); enum : DWORD @@ -3735,10 +3831,13 @@ enum : DWORD STILL_ACTIVE = (0x103), } +@nogc +{ DWORD TlsAlloc(); LPVOID TlsGetValue(DWORD); BOOL TlsSetValue(DWORD, LPVOID); BOOL TlsFree(DWORD); +} struct STARTUPINFO { @@ -3797,7 +3896,7 @@ struct PROCESS_INFORMATION alias PROCESS_INFORMATION *LPPROCESS_INFORMATION; -export +export @nogc { BOOL CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, @@ -3826,6 +3925,9 @@ enum } // shellapi.h + +@nogc +{ HINSTANCE ShellExecuteA(HWND hwnd, LPCSTR lpOperation, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT nShowCmd); HINSTANCE ShellExecuteW(HWND hwnd, LPCWSTR lpOperation, LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, INT nShowCmd); @@ -3837,6 +3939,7 @@ BOOL SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags); BOOL TerminateProcess(HANDLE hProcess, UINT uExitCode); LPWSTR* CommandLineToArgvW(LPCWSTR lpCmdLine, int* pNumArgs); LPWSTR GetCommandLineW(); +} enum { @@ -3846,23 +3949,29 @@ enum enum CREATE_UNICODE_ENVIRONMENT = 0x400; +@nogc +{ BOOL LockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh); BOOL UnlockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh); BOOL LockFileEx(HANDLE hFile, DWORD dwFlags, DWORD dwReserved, DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh, LPOVERLAPPED lpOverlapped); BOOL UnlockFileEx(HANDLE hFile, DWORD dwReserved, DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh, LPOVERLAPPED lpOverlapped); +} + enum LOCKFILE_FAIL_IMMEDIATELY = 1; enum LOCKFILE_EXCLUSIVE_LOCK = 2; +@nogc +{ BOOL IsDebuggerPresent(); LPSTR lstrcatA(LPSTR lpString1, LPCSTR lpString2); LPWSTR lstrcatW(LPWSTR lpString1, LPCWSTR lpString2); -int lstrcmp(LPCSTR lpString1, LPCSTR lpString2); -int lstrcmp(LPCWSTR lpString1,LPCWSTR lpString2); +int lstrcmpA(LPCSTR lpString1, LPCSTR lpString2); +int lstrcmpW(LPCWSTR lpString1,LPCWSTR lpString2); -int lstrcmpi(LPCSTR lpString1, LPCSTR lpString2); -int lstrcmpi(LPCWSTR lpString1,LPCWSTR lpString2); +int lstrcmpiA(LPCSTR lpString1, LPCSTR lpString2); +int lstrcmpiW(LPCWSTR lpString1,LPCWSTR lpString2); LPSTR lstrcpyA(LPSTR lpString1, LPCSTR lpString2); LPWSTR lstrcpyW(LPWSTR lpString1, LPCWSTR lpString2); @@ -3872,3 +3981,4 @@ LPWSTR lstrcpynW(LPWSTR lpString1, LPCWSTR lpString2, int iMaxLength); int lstrlenA(LPCSTR lpString); int lstrlenW(LPCWSTR lpString); +} diff --git a/libphobos/libdruntime/core/thread.d b/libphobos/libdruntime/core/thread.d index ed4ee8491..86d6a6b52 100644 --- a/libphobos/libdruntime/core/thread.d +++ b/libphobos/libdruntime/core/thread.d @@ -9,12 +9,31 @@ * Source: $(DRUNTIMESRC core/_thread.d) */ +/* NOTE: This file has been patched from the original DMD distribution to + * work with the GDC compiler. + */ module core.thread; public import core.time; // for Duration import core.exception : onOutOfMemoryError; -static import rt.tlsgc; + + +private +{ + // interface to rt.tlsgc + import core.internal.traits : externDFunc; + + alias rt_tlsgc_init = externDFunc!("rt.tlsgc.init", void* function()); + alias rt_tlsgc_destroy = externDFunc!("rt.tlsgc.destroy", void function(void*)); + + alias ScanDg = void delegate(void* pstart, void* pend) nothrow; + alias ScanFunc = void function(void*, scope ScanDg) nothrow; // Bug 13049 + alias rt_tlsgc_scan = externDFunc!("rt.tlsgc.scan", ScanFunc); + + alias ProcessFunc = void function(void*, scope IsMarkedDg) nothrow; // Bug 13049 + alias rt_tlsgc_processGCMarks = externDFunc!("rt.tlsgc.processGCMarks", ProcessFunc); +} // this should be true for most architectures version( GNU_StackGrowsDown ) @@ -61,6 +80,23 @@ class ThreadException : Exception } +/** +* Base class for thread errors to be used for function inside GC when allocations are unavailable. +*/ +class ThreadError : Error +{ + @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) + { + super(msg, file, line, next); + } + + @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) + { + super(msg, file, line, next); + } +} + + /** * Base class for fiber exceptions. */ @@ -154,7 +190,7 @@ version( Windows ) assert( obj.m_curr is &obj.m_main ); obj.m_main.bstack = getStackBottom(); obj.m_main.tstack = obj.m_main.bstack; - obj.m_tlsgcdata = rt.tlsgc.init(); + obj.m_tlsgcdata = rt_tlsgc_init(); Thread.setThis( obj ); //Thread.add( obj ); @@ -275,7 +311,7 @@ else version( Posix ) assert( obj.m_curr is &obj.m_main ); obj.m_main.bstack = getStackBottom(); obj.m_main.tstack = obj.m_main.bstack; - obj.m_tlsgcdata = rt.tlsgc.init(); + obj.m_tlsgcdata = rt_tlsgc_init(); version (GNU) { auto pstart = cast(void*) &_tlsstart; @@ -393,14 +429,14 @@ else version( Posix ) __gshared sem_t suspendCount; - extern (C) void thread_suspendHandler( int sig ) + extern (C) void thread_suspendHandler( int sig ) nothrow in { assert( sig == SIGUSR1 ); } body { - void op(void* sp) + void op(void* sp) nothrow { // NOTE: Since registers are being pushed and popped from the // stack, any other stack data used by this function should @@ -442,7 +478,7 @@ else version( Posix ) } - extern (C) void thread_resumeHandler( int sig ) + extern (C) void thread_resumeHandler( int sig ) nothrow in { assert( sig == SIGUSR2 ); @@ -592,7 +628,7 @@ class Thread { m_tmach = m_tmach.init; } - rt.tlsgc.destroy( m_tlsgcdata ); + rt_tlsgc_destroy( m_tlsgcdata ); m_tlsgcdata = null; } @@ -849,7 +885,7 @@ class Thread * Returns: * true if the thread is running, false if not. */ - final @property bool isRunning() + final @property bool isRunning() nothrow { if( m_addr == m_addr.init ) { @@ -1044,16 +1080,9 @@ class Thread timespec tin = void; timespec tout = void; + val.split!("seconds", "nsecs")(tin.tv_sec, tin.tv_nsec); if( val.total!"seconds" > tin.tv_sec.max ) - { tin.tv_sec = tin.tv_sec.max; - tin.tv_nsec = cast(typeof(tin.tv_nsec)) val.fracSec.nsecs; - } - else - { - tin.tv_sec = cast(typeof(tin.tv_sec)) val.total!"seconds"; - tin.tv_nsec = cast(typeof(tin.tv_nsec)) val.fracSec.nsecs; - } while( true ) { if( !nanosleep( &tin, &tout ) ) @@ -1082,7 +1111,6 @@ class Thread // Thread Accessors /////////////////////////////////////////////////////////////////////////// - /** * Provides a reference to the calling thread. * @@ -1091,12 +1119,24 @@ class Thread * deleting this object is undefined. If the current thread is not * attached to the runtime, a null reference is returned. */ - static Thread getThis() + static Thread getThis() nothrow { // NOTE: This function may not be called until thread_init has // completed. See thread_suspendAll for more information // on why this might occur. - return sm_this; + version( OSX ) + { + return sm_this; + } + else version( Posix ) + { + auto t = cast(Thread) pthread_getspecific( sm_this ); + return t; + } + else + { + return sm_this; + } } @@ -1262,7 +1302,22 @@ private: // // Local storage // - static Thread sm_this; + version( OSX ) + { + static Thread sm_this; + } + else version( Posix ) + { + // On Posix (excluding OSX), pthread_key_t is explicitly used to + // store and access thread reference. This is needed + // to avoid TLS access in signal handlers (malloc deadlock) + // when using shared libraries, see issue 11981. + __gshared pthread_key_t sm_this; + } + else + { + static Thread sm_this; + } // @@ -1311,7 +1366,18 @@ private: // static void setThis( Thread t ) { - sm_this = t; + version( OSX ) + { + sm_this = t; + } + else version( Posix ) + { + pthread_setspecific( sm_this, cast(void*) t ); + } + else + { + sm_this = t; + } } @@ -1321,7 +1387,7 @@ private: /////////////////////////////////////////////////////////////////////////// - final void pushContext( Context* c ) + final void pushContext( Context* c ) nothrow in { assert( !c.within ); @@ -1333,7 +1399,7 @@ private: } - final void popContext() + final void popContext() nothrow in { assert( m_curr && m_curr.within ); @@ -1346,7 +1412,7 @@ private: } - final Context* topContext() + final Context* topContext() nothrow in { assert( m_curr ); @@ -1374,7 +1440,7 @@ private: { void[] m_tls; // spans implicit thread local storage } - rt.tlsgc.Data* m_tlsgcdata; + void* m_tlsgcdata; version( Windows ) { @@ -1449,12 +1515,12 @@ private: // // All use of the global lists should synchronize on this lock. // - @property static Mutex slock() + @property static Mutex slock() nothrow { return cast(Mutex)_locks[0].ptr; } - @property static Mutex criticalRegionLock() + @property static Mutex criticalRegionLock() nothrow { return cast(Mutex)_locks[1].ptr; } @@ -1477,7 +1543,6 @@ private: } __gshared Context* sm_cbeg; - __gshared size_t sm_clen; __gshared Thread sm_tbeg; __gshared size_t sm_tlen; @@ -1524,7 +1589,6 @@ private: sm_cbeg.prev = c; } sm_cbeg = c; - ++sm_clen; return; } } @@ -1536,7 +1600,9 @@ private: // // Remove a context from the global context list. // - static void remove( Context* c ) + // This assumes slock being acquired. This isn't done here to + // avoid double locking when called from remove(Thread) + static void remove( Context* c ) nothrow in { assert( c ); @@ -1544,16 +1610,12 @@ private: } body { - synchronized( slock ) - { - if( c.prev ) - c.prev.next = c.next; - if( c.next ) - c.next.prev = c.prev; - if( sm_cbeg == c ) - sm_cbeg = c.next; - --sm_clen; - } + if( c.prev ) + c.prev.next = c.next; + if( c.next ) + c.next.prev = c.prev; + if( sm_cbeg == c ) + sm_cbeg = c.next; // NOTE: Don't null out c.next or c.prev because opApply currently // follows c.next after removing a node. This could be easily // addressed by simply returning the next node from this @@ -1629,7 +1691,7 @@ private: // // Remove a thread from the global thread list. // - static void remove( Thread t ) + static void remove( Thread t ) nothrow in { assert( t ); @@ -1637,7 +1699,7 @@ private: } body { - synchronized( slock ) + slock.lock_nothrow(); // this is called from within the GC, so it cannot allocate an exception { // NOTE: When a thread is removed from the global thread list its // main context is invalid and should be removed as well. @@ -1654,7 +1716,7 @@ private: t.prev.next = t.next; if( t.next ) t.next.prev = t.prev; - if( sm_tbeg == t ) + if( sm_tbeg is t ) sm_tbeg = t.next; --sm_tlen; } @@ -1664,68 +1726,10 @@ private: // function, however, a thread should never be re-added to the // list anyway and having next and prev be non-null is a good way // to ensure that. + slock.unlock_nothrow(); } } -// These must be kept in sync with core/thread.di -version (GNU) -{ - version (D_LP64) - { - version (Windows) - static assert(__traits(classInstanceSize, Thread) == 312); - else version (OSX) - static assert(__traits(classInstanceSize, Thread) == 320); - else version (Solaris) - static assert(__traits(classInstanceSize, Thread) == 176); - else version (Posix) - static assert(__traits(classInstanceSize, Thread) == 184); - else - static assert(0, "Platform not supported."); - } - else - { - static assert((void*).sizeof == 4); // 32-bit - - version (Windows) - static assert(__traits(classInstanceSize, Thread) == 128); - else version (OSX) - static assert(__traits(classInstanceSize, Thread) == 128); - else version (Posix) - static assert(__traits(classInstanceSize, Thread) == 92); - else - static assert(0, "Platform not supported."); - - } -} -else -version (D_LP64) -{ - version (Windows) - static assert(__traits(classInstanceSize, Thread) == 296); - else version (OSX) - static assert(__traits(classInstanceSize, Thread) == 304); - else version (Solaris) - static assert(__traits(classInstanceSize, Thread) == 160); - else version (Posix) - static assert(__traits(classInstanceSize, Thread) == 168); - else - static assert(0, "Platform not supported."); -} -else -{ - static assert((void*).sizeof == 4); // 32-bit - - version (Windows) - static assert(__traits(classInstanceSize, Thread) == 120); - else version (OSX) - static assert(__traits(classInstanceSize, Thread) == 120); - else version (Posix) - static assert(__traits(classInstanceSize, Thread) == 84); - else - static assert(0, "Platform not supported."); -} - unittest { @@ -1826,6 +1830,9 @@ extern (C) void thread_init() status = sem_init( &suspendCount, 0, 0 ); assert( status == 0 ); + + status = pthread_key_create( &Thread.sm_this, null ); + assert( status == 0 ); } Thread.sm_main = thread_attachThis(); } @@ -1838,6 +1845,14 @@ extern (C) void thread_init() extern (C) void thread_term() { Thread.termLocks(); + + version( OSX ) + { + } + else version( Posix ) + { + pthread_key_delete( Thread.sm_this ); + } } @@ -1881,7 +1896,7 @@ extern (C) Thread thread_attachThis() thisThread.m_isRunning = true; } thisThread.m_isDaemon = true; - thisThread.m_tlsgcdata = rt.tlsgc.init(); + thisThread.m_tlsgcdata = rt_tlsgc_init(); Thread.setThis( thisThread ); version( OSX ) @@ -1945,7 +1960,7 @@ version( Windows ) if( addr == GetCurrentThreadId() ) { thisThread.m_hndl = GetCurrentThreadHandle(); - thisThread.m_tlsgcdata = rt.tlsgc.init(); + thisThread.m_tlsgcdata = rt_tlsgc_init(); version (GNU) { auto pstart = cast(void*) &_tlsstart; @@ -1959,7 +1974,7 @@ version( Windows ) thisThread.m_hndl = OpenThreadHandle( addr ); impersonate_thread(addr, { - thisThread.m_tlsgcdata = rt.tlsgc.init(); + thisThread.m_tlsgcdata = rt_tlsgc_init(); version (GNU) { auto pstart = cast(void*) &_tlsstart; @@ -1985,7 +2000,7 @@ version( Windows ) /** * Deregisters the calling thread from use with the runtime. If this routine - * is called for a thread which is not registered, the result is undefined. + * is called for a thread which is not registered, no action is performed. */ extern (C) void thread_detachThis() { @@ -2096,12 +2111,12 @@ version (PPC64) version = ExternStackShell; version (ExternStackShell) { - extern(D) public void callWithStackShell(scope void delegate(void* sp) fn); + extern(D) public void callWithStackShell(scope void delegate(void* sp) nothrow fn) nothrow; } else { // Calls the given delegate, passing the current thread's stack pointer to it. - private void callWithStackShell(scope void delegate(void* sp) fn) + private void callWithStackShell(scope void delegate(void* sp) nothrow fn) nothrow in { assert(fn); @@ -2196,9 +2211,9 @@ private __gshared uint suspendDepth = 0; * t = The thread to suspend. * * Throws: - * ThreadException if the suspend operation fails for a running thread. + * ThreadError if the suspend operation fails for a running thread. */ -private void suspend( Thread t ) +private void suspend( Thread t ) nothrow { version( Windows ) { @@ -2209,14 +2224,14 @@ private void suspend( Thread t ) Thread.remove( t ); return; } - throw new ThreadException( "Unable to suspend thread" ); + onThreadError( "Unable to suspend thread" ); } CONTEXT context = void; context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; if( !GetThreadContext( t.m_hndl, &context ) ) - throw new ThreadException( "Unable to load thread context" ); + onThreadError( "Unable to load thread context" ); version( X86 ) { if( !t.m_lock ) @@ -2268,7 +2283,7 @@ private void suspend( Thread t ) Thread.remove( t ); return; } - throw new ThreadException( "Unable to suspend thread" ); + onThreadError( "Unable to suspend thread" ); } version( X86 ) @@ -2277,7 +2292,7 @@ private void suspend( Thread t ) mach_msg_type_number_t count = x86_THREAD_STATE32_COUNT; if( thread_get_state( t.m_tmach, x86_THREAD_STATE32, &state, &count ) != KERN_SUCCESS ) - throw new ThreadException( "Unable to load thread state" ); + onThreadError( "Unable to load thread state" ); if( !t.m_lock ) t.m_curr.tstack = cast(void*) state.esp; // eax,ebx,ecx,edx,edi,esi,ebp,esp @@ -2296,7 +2311,7 @@ private void suspend( Thread t ) mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT; if( thread_get_state( t.m_tmach, x86_THREAD_STATE64, &state, &count ) != KERN_SUCCESS ) - throw new ThreadException( "Unable to load thread state" ); + onThreadError( "Unable to load thread state" ); if( !t.m_lock ) t.m_curr.tstack = cast(void*) state.rsp; // rax,rbx,rcx,rdx,rdi,rsi,rbp,rsp @@ -2334,12 +2349,12 @@ private void suspend( Thread t ) Thread.remove( t ); return; } - throw new ThreadException( "Unable to suspend thread" ); + onThreadError( "Unable to suspend thread" ); } while (sem_wait(&suspendCount) != 0) { if (errno != EINTR) - throw new ThreadException( "Unable to wait for semaphore" ); + onThreadError( "Unable to wait for semaphore" ); errno = 0; } } @@ -2357,9 +2372,9 @@ private void suspend( Thread t ) * processing is resumed. * * Throws: - * ThreadException if the suspend operation fails for a running thread. + * ThreadError if the suspend operation fails for a running thread. */ -extern (C) void thread_suspendAll() +extern (C) void thread_suspendAll() nothrow { // NOTE: We've got an odd chicken & egg problem here, because while the GC // is required to call thread_init before calling any other thread @@ -2382,7 +2397,7 @@ extern (C) void thread_suspendAll() return; } - Thread.slock.lock(); + Thread.slock.lock_nothrow(); { if( ++suspendDepth > 1 ) return; @@ -2395,7 +2410,7 @@ extern (C) void thread_suspendAll() // cause the second suspend to fail, the garbage collection to // abort, and Bad Things to occur. - Thread.criticalRegionLock.lock(); + Thread.criticalRegionLock.lock_nothrow(); for (Thread t = Thread.sm_tbeg; t !is null; t = t.next) { Duration waittime = dur!"usecs"(10); @@ -2406,10 +2421,19 @@ extern (C) void thread_suspendAll() } else if (t.m_isInCriticalRegion) { - Thread.criticalRegionLock.unlock(); - Thread.sleep(waittime); + Thread.criticalRegionLock.unlock_nothrow(); + try + { + Thread.sleep(waittime); + } + catch(Exception) + { + // if sleep actually fails, it tries to allocate the new exception + // which fails the GC recursion check, so we don't expect to ever + // reach this point, but we have to convince the compiler, too + } if (waittime < dur!"msecs"(10)) waittime *= 2; - Thread.criticalRegionLock.lock(); + Thread.criticalRegionLock.lock_nothrow(); goto Lagain; } else @@ -2417,7 +2441,7 @@ extern (C) void thread_suspendAll() suspend(t); } } - Thread.criticalRegionLock.unlock(); + Thread.criticalRegionLock.unlock_nothrow(); } } @@ -2433,9 +2457,9 @@ extern (C) void thread_suspendAll() * t = The thread to resume. * * Throws: - * ThreadException if the resume fails for a running thread. + * ThreadError if the resume fails for a running thread. */ -private void resume( Thread t ) +private void resume( Thread t ) nothrow { version( Windows ) { @@ -2446,7 +2470,7 @@ private void resume( Thread t ) Thread.remove( t ); return; } - throw new ThreadException( "Unable to resume thread" ); + onThreadError( "Unable to resume thread" ); } if( !t.m_lock ) @@ -2462,7 +2486,7 @@ private void resume( Thread t ) Thread.remove( t ); return; } - throw new ThreadException( "Unable to resume thread" ); + onThreadError( "Unable to resume thread" ); } if( !t.m_lock ) @@ -2480,7 +2504,7 @@ private void resume( Thread t ) Thread.remove( t ); return; } - throw new ThreadException( "Unable to resume thread" ); + onThreadError( "Unable to resume thread" ); } } else if( !t.m_lock ) @@ -2499,9 +2523,9 @@ private void resume( Thread t ) * This routine must be preceded by a call to thread_suspendAll. * * Throws: - * ThreadException if the resume operation fails for a running thread. + * ThreadError if the resume operation fails for a running thread. */ -extern (C) void thread_resumeAll() +extern (C) void thread_resumeAll() nothrow in { assert( suspendDepth > 0 ); @@ -2516,7 +2540,7 @@ body return; } - scope(exit) Thread.slock.unlock(); + scope(exit) Thread.slock.unlock_nothrow(); { if( --suspendDepth > 0 ) return; @@ -2530,16 +2554,29 @@ body } } +/** + * Indicates the kind of scan being performed by $(D thread_scanAllType). + */ enum ScanType { - stack, - tls, + stack, /// The stack and/or registers are being scanned. + tls, /// TLS data is being scanned. } -alias void delegate(void*, void*) ScanAllThreadsFn; -alias void delegate(ScanType, void*, void*) ScanAllThreadsTypeFn; +alias void delegate(void*, void*) nothrow ScanAllThreadsFn; /// The scanning function. +alias void delegate(ScanType, void*, void*) nothrow ScanAllThreadsTypeFn; /// ditto -extern (C) void thread_scanAllType( scope ScanAllThreadsTypeFn scan ) +/** + * The main entry point for garbage collection. The supplied delegate + * will be passed ranges representing both stack and register values. + * + * Params: + * scan = The scanner function. It should scan from p1 through p2 - 1. + * + * In: + * This routine must be preceded by a call to thread_suspendAll. + */ +extern (C) void thread_scanAllType( scope ScanAllThreadsTypeFn scan ) nothrow in { assert( suspendDepth > 0 ); @@ -2550,7 +2587,7 @@ body } -private void scanAllTypeImpl( scope ScanAllThreadsTypeFn scan, void* curStackTop ) +private void scanAllTypeImpl( scope ScanAllThreadsTypeFn scan, void* curStackTop ) nothrow { Thread thisThread = null; void* oldStackTop = null; @@ -2607,16 +2644,47 @@ private void scanAllTypeImpl( scope ScanAllThreadsTypeFn scan, void* curStackTop scan( ScanType.tls, t.m_tls.ptr, t.m_tls.ptr + t.m_tls.length ); if (t.m_tlsgcdata !is null) - rt.tlsgc.scan(t.m_tlsgcdata, (p1, p2) => scan(ScanType.tls, p1, p2)); + rt_tlsgc_scan(t.m_tlsgcdata, (p1, p2) => scan(ScanType.tls, p1, p2)); } } - -extern (C) void thread_scanAll( scope ScanAllThreadsFn scan ) +/** + * The main entry point for garbage collection. The supplied delegate + * will be passed ranges representing both stack and register values. + * + * Params: + * scan = The scanner function. It should scan from p1 through p2 - 1. + * + * In: + * This routine must be preceded by a call to thread_suspendAll. + */ +extern (C) void thread_scanAll( scope ScanAllThreadsFn scan ) nothrow { thread_scanAllType((type, p1, p2) => scan(p1, p2)); } + +/** + * Signals that the code following this call is a critical region. Any code in + * this region must finish running before the calling thread can be suspended + * by a call to thread_suspendAll. + * + * This function is, in particular, meant to help maintain garbage collector + * invariants when a lock is not used. + * + * A critical region is exited with thread_exitCriticalRegion. + * + * $(RED Warning): + * Using critical regions is extremely error-prone. For instance, using locks + * inside a critical region can easily result in a deadlock when another thread + * holding the lock already got suspended. + * + * The term and concept of a 'critical region' comes from + * $(LINK2 https://github.com/mono/mono/blob/521f4a198e442573c400835ef19bbb36b60b0ebb/mono/metadata/sgen-gc.h#L925 Mono's SGen garbage collector). + * + * In: + * The calling thread must be attached to the runtime. + */ extern (C) void thread_enterCriticalRegion() in { @@ -2628,6 +2696,14 @@ body Thread.getThis().m_isInCriticalRegion = true; } + +/** + * Signals that the calling thread is no longer in a critical region. Following + * a call to this function, the thread can once again be suspended. + * + * In: + * The calling thread must be attached to the runtime. + */ extern (C) void thread_exitCriticalRegion() in { @@ -2639,6 +2715,13 @@ body Thread.getThis().m_isInCriticalRegion = false; } + +/** + * Returns true if the current thread is in a critical region; otherwise, false. + * + * In: + * The calling thread must be attached to the runtime. + */ extern (C) bool thread_inCriticalRegion() in { @@ -2650,6 +2733,23 @@ body return Thread.getThis().m_isInCriticalRegion; } + +/** +* A callback for thread errors in D during collections. Since an allocation is not possible +* a preallocated ThreadError will be used as the Error instance +* +* Throws: +* ThreadError. +*/ +private void onThreadError(string msg = null, Throwable next = null) nothrow +{ + __gshared ThreadError error = new ThreadError(null); + error.msg = msg; + error.next = next; + throw error; +} + + unittest { assert(!thread_inCriticalRegion()); @@ -2717,26 +2817,43 @@ unittest import core.sync.semaphore; shared bool inCriticalRegion; - auto sem = new Semaphore(); + auto sema = new Semaphore(), + semb = new Semaphore(); auto thr = new Thread( { thread_enterCriticalRegion(); inCriticalRegion = true; - sem.notify(); + sema.notify(); + semb.wait(); + Thread.sleep(dur!"msecs"(1)); inCriticalRegion = false; thread_exitCriticalRegion(); }); thr.start(); - sem.wait(); + sema.wait(); assert(inCriticalRegion); + semb.notify(); + thread_suspendAll(); assert(!inCriticalRegion); thread_resumeAll(); } +/** + * Indicates whether an address has been marked by the GC. + */ +enum IsMarked : int +{ + no, /// Address is not marked. + yes, /// Address is marked. + unknown, /// Address is not managed by the GC. +} + +alias int delegate( void* addr ) nothrow IsMarkedDg; /// The isMarked callback function. + /** * This routine allows the runtime to process any special per-thread handling * for the GC. This is needed for taking into account any memory that is @@ -2744,33 +2861,35 @@ unittest * means the array append cache. * * Params: - * hasMarks = The probe function. It should return true for pointers into marked memory blocks. + * isMarked = The function used to check if $(D addr) is marked. * * In: * This routine must be called just prior to resuming all threads. */ -extern(C) void thread_processGCMarks(scope rt.tlsgc.IsMarkedDg dg) +extern(C) void thread_processGCMarks( scope IsMarkedDg isMarked ) nothrow { for( Thread t = Thread.sm_tbeg; t; t = t.next ) { /* Can be null if collection was triggered between adding a - * thread and calling rt.tlsgc.init. + * thread and calling rt_tlsgc_init. */ if (t.m_tlsgcdata !is null) - rt.tlsgc.processGCMarks(t.m_tlsgcdata, dg); + rt_tlsgc_processGCMarks(t.m_tlsgcdata, isMarked); } } extern (C) { +nothrow: version (linux) int pthread_getattr_np(pthread_t thread, pthread_attr_t* attr); version (FreeBSD) int pthread_attr_get_np(pthread_t thread, pthread_attr_t* attr); version (Solaris) int thr_stksegment(stack_t* stk); + version (Android) int pthread_getattr_np(pthread_t thid, pthread_attr_t* attr); } -private void* getStackTop() +private void* getStackTop() nothrow { version (D_InlineAsm_X86) asm { naked; mov EAX, ESP; ret; } @@ -2783,7 +2902,7 @@ private void* getStackTop() } -private void* getStackBottom() +private void* getStackBottom() nothrow { version (Windows) { @@ -2845,12 +2964,32 @@ private void* getStackBottom() thr_stksegment(&stk); return stk.ss_sp; } + else version (Android) + { + pthread_attr_t attr; + void* addr; size_t size; + + pthread_getattr_np(pthread_self(), &attr); + pthread_attr_getstack(&attr, &addr, &size); + pthread_attr_destroy(&attr); + return addr + size; + } else static assert(false, "Platform not supported."); } -extern (C) void* thread_stackTop() +/** + * Returns the stack top of the currently active stack within the calling + * thread. + * + * In: + * The calling thread must be attached to the runtime. + * + * Returns: + * The address of the stack top. + */ +extern (C) void* thread_stackTop() nothrow in { // Not strictly required, but it gives us more flexibility. @@ -2862,7 +3001,17 @@ body } -extern (C) void* thread_stackBottom() +/** + * Returns the stack bottom of the currently active stack within the calling + * thread. + * + * In: + * The calling thread must be attached to the runtime. + * + * Returns: + * The address of the stack bottom. + */ +extern (C) void* thread_stackBottom() nothrow in { assert(Thread.getThis()); @@ -3027,17 +3176,6 @@ private: Thread[Thread] m_all; } -// These must be kept in sync with core/thread.di -version (D_LP64) -{ - static assert(__traits(classInstanceSize, ThreadGroup) == 24); -} -else -{ - static assert((void*).sizeof == 4); // 32-bit - static assert(__traits(classInstanceSize, ThreadGroup) == 12); -} - /////////////////////////////////////////////////////////////////////////////// // Fiber Platform Detection and Memory Allocation @@ -3254,32 +3392,60 @@ private naked; // save current stack state + // NOTE: When changing the layout of registers on the stack, + // make sure that the XMM registers are still aligned. + // On function entry, the stack is guaranteed to not + // be aligned to 16 bytes because of the return address + // on the stack. push RBP; mov RBP, RSP; - push RBX; push R12; push R13; push R14; push R15; - xor RCX,RCX; - push qword ptr GS:[RCX]; - push qword ptr GS:8[RCX]; - push qword ptr GS:16[RCX]; + // Five registers = 40 bytes; stack is now aligned to 16 bytes + sub RSP, 160; + movdqa [RSP + 144], XMM6; + movdqa [RSP + 128], XMM7; + movdqa [RSP + 112], XMM8; + movdqa [RSP + 96], XMM9; + movdqa [RSP + 80], XMM10; + movdqa [RSP + 64], XMM11; + movdqa [RSP + 48], XMM12; + movdqa [RSP + 32], XMM13; + movdqa [RSP + 16], XMM14; + movdqa [RSP], XMM15; + push RBX; + xor RAX,RAX; + push qword ptr GS:[RAX]; + push qword ptr GS:8[RAX]; + push qword ptr GS:16[RAX]; // store oldp - mov [RDI], RSP; + mov [RCX], RSP; // load newp to begin context switch - mov RSP, RSI; + mov RSP, RDX; // load saved state from new stack - pop qword ptr GS:16[RCX]; - pop qword ptr GS:8[RCX]; - pop qword ptr GS:[RCX]; + pop qword ptr GS:16[RAX]; + pop qword ptr GS:8[RAX]; + pop qword ptr GS:[RAX]; + pop RBX; + movdqa XMM15, [RSP]; + movdqa XMM14, [RSP + 16]; + movdqa XMM13, [RSP + 32]; + movdqa XMM12, [RSP + 48]; + movdqa XMM11, [RSP + 64]; + movdqa XMM10, [RSP + 80]; + movdqa XMM9, [RSP + 96]; + movdqa XMM8, [RSP + 112]; + movdqa XMM7, [RSP + 128]; + movdqa XMM6, [RSP + 144]; + add RSP, 160; pop R15; pop R14; pop R13; pop R12; - pop RBX; pop RBP; // 'return' to complete switch @@ -3376,9 +3542,6 @@ private * The main routines to implement when porting Fibers to new architectures are * fiber_switchContext and initStack. Some version constants have to be defined * for the new platform as well, search for "Fiber Platform Detection and Memory Allocation". - * These must be kept in sync with thread.di as well! You might also want to verify - * the Fiber size for the new platform in thread.d and thread.di. Search for - * "enum FiberSize" * * Fibers are based on a concept called 'Context'. A Context describes the execution * state of a Fiber or main thread which is fully described by the stack, some @@ -3420,7 +3583,7 @@ private * Such registers are usually floating point registers and the return address. In order to * implement this, we return a modified stack pointer from fiber_switchContext. However, * we have to remember that when we restore the registers from the stack! - * + * * --------------------------- <= Stack Base * | Frame | <= Many other stack frames * | Frame | @@ -3489,7 +3652,7 @@ private * * The ARM implementation is meant to be used as a kind of documented example implementation. * Look there for a concrete example. - * + * * FIXME: fiber_entrypoint might benefit from a @noreturn attribute, but D doesn't have one. */ @@ -3657,7 +3820,7 @@ class Fiber * Any exception not handled by this fiber if rethrow = false, null * otherwise. */ - final Object call( bool rethrow = true ) + final Throwable call( bool rethrow = true ) in { assert( m_state == State.HOLD ); @@ -4059,7 +4222,8 @@ private: { // NOTE: m_ctxt is guaranteed to be alive because it is held in the // global context list. - Thread.remove( m_ctxt ); + synchronized( Thread.slock ) + Thread.remove( m_ctxt ); static if( __traits( compiles, VirtualAlloc ) ) { @@ -4202,14 +4366,51 @@ private: } else version( AsmX86_64_Windows ) { - push( 0x00000000_00000000 ); // Return address of fiber_entryPoint call - push( cast(size_t) &fiber_entryPoint ); // RIP + // Using this trampoline instead of the raw fiber_entryPoint + // ensures that during context switches, source and destination + // stacks have the same alignment. Otherwise, the stack would need + // to be shifted by 8 bytes for the first call, as fiber_entryPoint + // is an actual function expecting a stack which is not aligned + // to 16 bytes. + static void trampoline() + { + asm + { + naked; + sub RSP, 32; // Shadow space (Win64 calling convention) + call fiber_entryPoint; + xor RCX, RCX; // This should never be reached, as + jmp RCX; // fiber_entryPoint must never return. + } + } + + push( cast(size_t) &trampoline ); // RIP push( 0x00000000_00000000 ); // RBP - push( 0x00000000_00000000 ); // RBX push( 0x00000000_00000000 ); // R12 push( 0x00000000_00000000 ); // R13 push( 0x00000000_00000000 ); // R14 push( 0x00000000_00000000 ); // R15 + push( 0x00000000_00000000 ); // XMM6 (high) + push( 0x00000000_00000000 ); // XMM6 (low) + push( 0x00000000_00000000 ); // XMM7 (high) + push( 0x00000000_00000000 ); // XMM7 (low) + push( 0x00000000_00000000 ); // XMM8 (high) + push( 0x00000000_00000000 ); // XMM8 (low) + push( 0x00000000_00000000 ); // XMM9 (high) + push( 0x00000000_00000000 ); // XMM9 (low) + push( 0x00000000_00000000 ); // XMM10 (high) + push( 0x00000000_00000000 ); // XMM10 (low) + push( 0x00000000_00000000 ); // XMM11 (high) + push( 0x00000000_00000000 ); // XMM11 (low) + push( 0x00000000_00000000 ); // XMM12 (high) + push( 0x00000000_00000000 ); // XMM12 (low) + push( 0x00000000_00000000 ); // XMM13 (high) + push( 0x00000000_00000000 ); // XMM13 (low) + push( 0x00000000_00000000 ); // XMM14 (high) + push( 0x00000000_00000000 ); // XMM14 (low) + push( 0x00000000_00000000 ); // XMM15 (high) + push( 0x00000000_00000000 ); // XMM15 (low) + push( 0x00000000_00000000 ); // RBX push( 0xFFFFFFFF_FFFFFFFF ); // GS:[0] version( StackGrowsDown ) { @@ -4505,54 +4706,6 @@ private: } } -// These must be kept in sync with core/thread.di -version (GNU) {} else -version (D_LP64) -{ - version (Windows) - static assert(__traits(classInstanceSize, Fiber) == 88); - else version (OSX) - static assert(__traits(classInstanceSize, Fiber) == 88); - else version (Posix) - { - static if( __traits( compiles, ucontext_t ) ) - static assert(__traits(classInstanceSize, Fiber) == 88 + ucontext_t.sizeof + 8); - else - static assert(__traits(classInstanceSize, Fiber) == 88); - } - else - static assert(0, "Platform not supported."); -} -else -{ - static assert((void*).sizeof == 4); // 32-bit - - version (Windows) - static assert(__traits(classInstanceSize, Fiber) == 44); - else version (OSX) - static assert(__traits(classInstanceSize, Fiber) == 44); - else version (Posix) - { - static if( __traits( compiles, ucontext_t ) ) - { - // ucontext_t might have an alignment larger than 4. - static roundUp()(size_t n) - { - return (n + (ucontext_t.alignof - 1)) & ~(ucontext_t.alignof - 1); - } - static assert(__traits(classInstanceSize, Fiber) == - roundUp(roundUp(44) + ucontext_t.sizeof + 4)); - } - else - static assert(__traits(classInstanceSize, Fiber) == 44); - } - else - static assert(0, "Platform not supported."); -} - - -version(Win64) {} -else { version( unittest ) { @@ -4839,9 +4992,63 @@ unittest fiber.call(); } + +version( AsmX86_64_Windows ) +{ + // Test Windows x64 calling convention + unittest + { + void testNonvolatileRegister(alias REG)() + { + auto zeroRegister = new Fiber(() { + mixin("asm { xor "~REG~", "~REG~"; }"); + }); + long after; + + mixin("asm { mov "~REG~", 0xFFFFFFFFFFFFFFFF; }"); + zeroRegister.call(); + mixin("asm { mov after, "~REG~"; }"); + + assert(after == -1); + } + + void testNonvolatileRegisterSSE(alias REG)() + { + auto zeroRegister = new Fiber(() { + mixin("asm { xorpd "~REG~", "~REG~"; }"); + }); + long[2] before = [0xFFFFFFFF_FFFFFFFF, 0xFFFFFFFF_FFFFFFFF], after; + + mixin("asm { movdqu "~REG~", before; }"); + zeroRegister.call(); + mixin("asm { movdqu after, "~REG~"; }"); + + assert(before == after); + } + + testNonvolatileRegister!("R12")(); + testNonvolatileRegister!("R13")(); + testNonvolatileRegister!("R14")(); + testNonvolatileRegister!("R15")(); + testNonvolatileRegister!("RDI")(); + testNonvolatileRegister!("RSI")(); + testNonvolatileRegister!("RBX")(); + + testNonvolatileRegisterSSE!("XMM6")(); + testNonvolatileRegisterSSE!("XMM7")(); + testNonvolatileRegisterSSE!("XMM8")(); + testNonvolatileRegisterSSE!("XMM9")(); + testNonvolatileRegisterSSE!("XMM10")(); + testNonvolatileRegisterSSE!("XMM11")(); + testNonvolatileRegisterSSE!("XMM12")(); + testNonvolatileRegisterSSE!("XMM13")(); + testNonvolatileRegisterSSE!("XMM14")(); + testNonvolatileRegisterSSE!("XMM15")(); + } } -version( AsmX86_64_Posix ) + +version( D_InlineAsm_X86_64 ) { unittest { diff --git a/libphobos/libdruntime/core/thread.di b/libphobos/libdruntime/core/thread.di deleted file mode 100644 index 84ddf9458..000000000 --- a/libphobos/libdruntime/core/thread.di +++ /dev/null @@ -1,1134 +0,0 @@ -/** - * The thread module provides support for thread creation and management. - * - * Copyright: Copyright Sean Kelly 2005 - 2009. - * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) - * Authors: Sean Kelly, Walter Bright, Alex Rønne Petersen - * Source: $(DRUNTIMESRC core/_thread.d) - */ - -/* Copyright Sean Kelly 2005 - 2009. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - * Source: $(LINK http://www.dsource.org/projects/druntime/browser/trunk/src/core/thread.d) - */ -module core.thread; - - -public import core.time; // for Duration - - -// this should be true for most architectures -version = StackGrowsDown; - -/** - * Returns the process ID of the calling process, which is guaranteed to be - * unique on the system. This call is always successful. - * - * Example: - * --- - * writefln("Current process id: %s", getpid()); - * --- - */ -version(Posix) -{ - import core.sys.posix.unistd; - alias core.sys.posix.unistd.getpid getpid; -} -else version (Windows) -{ - import core.sys.windows.windows; - alias core.sys.windows.windows.GetCurrentProcessId getpid; -} - - -/////////////////////////////////////////////////////////////////////////////// -// Thread and Fiber Exceptions -/////////////////////////////////////////////////////////////////////////////// - - -/** - * Base class for thread exceptions. - */ -class ThreadException : Exception -{ - @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null); - @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__); -} - - -/** - * Base class for fiber exceptions. - */ -class FiberException : Exception -{ - @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null); - @safe pure nothrow this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__); -} - - -/////////////////////////////////////////////////////////////////////////////// -// Thread -/////////////////////////////////////////////////////////////////////////////// - - -/** - * This class encapsulates all threading functionality for the D - * programming language. As thread manipulation is a required facility - * for garbage collection, all user threads should derive from this - * class, and instances of this class should never be explicitly deleted. - * A new thread may be created using either derivation or composition, as - * in the following example. - * - * Example: - * ---------------------------------------------------------------------------- - * - * class DerivedThread : Thread - * { - * this() - * { - * super( &run ); - * } - * - * private : - * void run() - * { - * printf( "Derived thread running.\n" ); - * } - * } - * - * void threadFunc() - * { - * printf( "Composed thread running.\n" ); - * } - * - * // create instances of each type - * Thread derived = new DerivedThread(); - * Thread composed = new Thread( &threadFunc ); - * - * // start both threads - * derived.start(); - * composed.start(); - * - * ---------------------------------------------------------------------------- - */ -class Thread -{ - /////////////////////////////////////////////////////////////////////////// - // Initialization - /////////////////////////////////////////////////////////////////////////// - - - /** - * Initializes a thread object which is associated with a static - * D function. - * - * Params: - * fn = The thread function. - * sz = The stack size for this thread. - * - * In: - * fn must not be null. - */ - this( void function() fn, size_t sz = 0 ); - - - /** - * Initializes a thread object which is associated with a dynamic - * D function. - * - * Params: - * dg = The thread function. - * sz = The stack size for this thread. - * - * In: - * dg must not be null. - */ - this( void delegate() dg, size_t sz = 0 ); - - - ~this(); - - - /////////////////////////////////////////////////////////////////////////// - // General Actions - /////////////////////////////////////////////////////////////////////////// - - - /** - * Starts the thread and invokes the function or delegate passed upon - * construction. - * - * In: - * This routine may only be called once per thread instance. - * - * Throws: - * ThreadException if the thread fails to start. - */ - final void start(); - - - /** - * Waits for this thread to complete. If the thread terminated as the - * result of an unhandled exception, this exception will be rethrown. - * - * Params: - * rethrow = Rethrow any unhandled exception which may have caused this - * thread to terminate. - * - * Throws: - * ThreadException if the operation fails. - * Any exception not handled by the joined thread. - * - * Returns: - * Any exception not handled by this thread if rethrow = false, null - * otherwise. - */ - final Throwable join( bool rethrow = true ); - - - /////////////////////////////////////////////////////////////////////////// - // General Properties - /////////////////////////////////////////////////////////////////////////// - - - /** - * Gets the user-readable label for this thread. - * - * Returns: - * The name of this thread. - */ - final @property string name(); - - - /** - * Sets the user-readable label for this thread. - * - * Params: - * val = The new name of this thread. - */ - final @property void name( string val ); - - - /** - * Gets the daemon status for this thread. While the runtime will wait for - * all normal threads to complete before tearing down the process, daemon - * threads are effectively ignored and thus will not prevent the process - * from terminating. In effect, daemon threads will be terminated - * automatically by the OS when the process exits. - * - * Returns: - * true if this is a daemon thread. - */ - final @property bool isDaemon(); - - - /** - * Sets the daemon status for this thread. While the runtime will wait for - * all normal threads to complete before tearing down the process, daemon - * threads are effectively ignored and thus will not prevent the process - * from terminating. In effect, daemon threads will be terminated - * automatically by the OS when the process exits. - * - * Params: - * val = The new daemon status for this thread. - */ - final @property void isDaemon( bool val ); - - - /** - * Tests whether this thread is running. - * - * Returns: - * true if the thread is running, false if not. - */ - final @property bool isRunning(); - - - /////////////////////////////////////////////////////////////////////////// - // Thread Priority Actions - /////////////////////////////////////////////////////////////////////////// - - - /** - * The minimum scheduling priority that may be set for a thread. On - * systems where multiple scheduling policies are defined, this value - * represents the minimum valid priority for the scheduling policy of - * the process. - */ - __gshared const int PRIORITY_MIN; - - - /** - * The maximum scheduling priority that may be set for a thread. On - * systems where multiple scheduling policies are defined, this value - * represents the maximum valid priority for the scheduling policy of - * the process. - */ - __gshared const int PRIORITY_MAX; - - - /** - * The default scheduling priority that is set for a thread. On - * systems where multiple scheduling policies are defined, this value - * represents the default priority for the scheduling policy of - * the process. - */ - __gshared const int PRIORITY_DEFAULT; - - - /** - * Gets the scheduling priority for the associated thread. - * - * Returns: - * The scheduling priority of this thread. - */ - final @property int priority(); - - - /** - * Sets the scheduling priority for the associated thread. - * - * Params: - * val = The new scheduling priority of this thread. - */ - final @property void priority( int val ); - - - /////////////////////////////////////////////////////////////////////////// - // Actions on Calling Thread - /////////////////////////////////////////////////////////////////////////// - - - /** - * Suspends the calling thread for at least the supplied period. This may - * result in multiple OS calls if period is greater than the maximum sleep - * duration supported by the operating system. - * - * Params: - * val = The minimum duration the calling thread should be suspended. - * - * In: - * period must be non-negative. - * - * Example: - * ------------------------------------------------------------------------ - * - * Thread.sleep( dur!("msecs")( 50 ) ); // sleep for 50 milliseconds - * Thread.sleep( dur!("seconds")( 5 ) ); // sleep for 5 seconds - * - * ------------------------------------------------------------------------ - */ - static void sleep( Duration val ); - - - /** - * Forces a context switch to occur away from the calling thread. - */ - static void yield(); - - - /////////////////////////////////////////////////////////////////////////// - // Thread Accessors - /////////////////////////////////////////////////////////////////////////// - - - /** - * Provides a reference to the calling thread. - * - * Returns: - * The thread object representing the calling thread. The result of - * deleting this object is undefined. If the current thread is not - * attached to the runtime, a null reference is returned. - */ - static Thread getThis(); - - - /** - * Provides a list of all threads currently being tracked by the system. - * - * Returns: - * An array containing references to all threads currently being - * tracked by the system. The result of deleting any contained - * objects is undefined. - */ - static Thread[] getAll(); - - - /** - * Operates on all threads currently being tracked by the system. The - * result of deleting any Thread object is undefined. - * - * Params: - * dg = The supplied code as a delegate. - * - * Returns: - * Zero if all elemented are visited, nonzero if not. - */ - static int opApply( scope int delegate( ref Thread ) dg ); - - - /////////////////////////////////////////////////////////////////////////// - // Static Initalizer - /////////////////////////////////////////////////////////////////////////// - - - // This initializer is used to set thread constants. All functional - // initialization occurs within thread_init(). - shared static this(); - - - /////////////////////////////////////////////////////////////////////////// - // Stuff That Should Go Away - /////////////////////////////////////////////////////////////////////////// - - -private: - // - // Standard types - // - version( Windows ) - { - alias uint TLSKey; - alias uint ThreadAddr; - } - else version( Posix ) - { - import core.sys.posix.pthread; - alias pthread_key_t TLSKey; - alias pthread_t ThreadAddr; - } - - // These must be kept in sync with core/thread.d - version (GNU) - { - version (D_LP64) - { - version (Windows) enum ThreadSize = 312; - else version (OSX) enum ThreadSize = 320; - else version (Solaris) enum ThreadSize = 176; - else version (Posix) enum ThreadSize = 184; - else static assert(0, "Platform not supported."); - } - else - { - static assert((void*).sizeof == 4); // 32-bit - - version (Windows) enum ThreadSize = 128; - else version (OSX) enum ThreadSize = 128; - else version (Posix) enum ThreadSize = 92; - else static assert(0, "Platform not supported."); - } - } - else - version (D_LP64) - { - version (Windows) enum ThreadSize = 296; - else version (OSX) enum ThreadSize = 304; - else version (Solaris) enum ThreadSize = 160; - else version (Posix) enum ThreadSize = 168; - else static assert(0, "Platform not supported."); - } - else - { - static assert((void*).sizeof == 4); // 32-bit - - version (Windows) enum ThreadSize = 120; - else version (OSX) enum ThreadSize = 120; - else version (Posix) enum ThreadSize = 84; - else static assert(0, "Platform not supported."); - } - - void data[ThreadSize - __traits(classInstanceSize, Object)] = void; -} - - -/////////////////////////////////////////////////////////////////////////////// -// GC Support Routines -/////////////////////////////////////////////////////////////////////////////// - - -/** - * Initializes the thread module. This function must be called by the - * garbage collector on startup and before any other thread routines - * are called. - */ -extern (C) void thread_init(); - - -/** - * Terminates the thread module. No other thread routine may be called - * afterwards. - */ -extern (C) void thread_term(); - - -/** - * - */ -extern (C) bool thread_isMainThread(); - - -/** - * Registers the calling thread for use with the D Runtime. If this routine - * is called for a thread which is already registered, no action is performed. - */ -extern (C) Thread thread_attachThis(); - - -version( Windows ) -{ - // NOTE: These calls are not safe on Posix systems that use signals to - // perform garbage collection. The suspendHandler uses getThis() - // to get the thread handle so getThis() must be a simple call. - // Mutexes can't safely be acquired inside signal handlers, and - // even if they could, the mutex needed (Thread.slock) is held by - // thread_suspendAll(). So in short, these routines will remain - // Windows-specific. If they are truly needed elsewhere, the - // suspendHandler will need a way to call a version of getThis() - // that only does the TLS lookup without the fancy fallback stuff. - - /// ditto - extern (C) Thread thread_attachByAddr( Thread.ThreadAddr addr ); - - /// ditto - extern (C) Thread thread_attachByAddrB( Thread.ThreadAddr addr, void* bstack ); -} - - -/** - * Deregisters the calling thread from use with the runtime. If this routine - * is called for a thread which is not registered, no action is performed. - */ -extern (C) void thread_detachThis(); - - -/// ditto -extern (C) void thread_detachByAddr( Thread.ThreadAddr addr ); - - -/** - * Search the list of all threads for a thread with the given thread identifier. - * - * Params: - * addr = The thread identifier to search for. - * Returns: - * The thread object associated with the thread identifier, null if not found. - */ -static Thread thread_findByAddr( Thread.ThreadAddr addr ); - - -/** - * Sets the current thread to a specific reference. Only to be used - * when dealing with externally-created threads (in e.g. C code). - * The primary use of this function is when Thread.getThis() must - * return a sensible value in, for example, TLS destructors. In - * other words, don't touch this unless you know what you're doing. - * - * Params: - * t = A reference to the current thread. May be null. - */ -extern (C) void thread_setThis(Thread t); - - -/** - * Joins all non-daemon threads that are currently running. This is done by - * performing successive scans through the thread list until a scan consists - * of only daemon threads. - */ -extern (C) void thread_joinAll(); - - -// Performs intermediate shutdown of the thread module. -shared static ~this(); - - -/** - * Suspend all threads but the calling thread for "stop the world" garbage - * collection runs. This function may be called multiple times, and must - * be followed by a matching number of calls to thread_resumeAll before - * processing is resumed. - * - * Throws: - * ThreadException if the suspend operation fails for a running thread. - */ -extern (C) void thread_suspendAll(); - - -/** - * Resume all threads but the calling thread for "stop the world" garbage - * collection runs. This function must be called once for each preceding - * call to thread_suspendAll before the threads are actually resumed. - * - * In: - * This routine must be preceded by a call to thread_suspendAll. - * - * Throws: - * ThreadException if the resume operation fails for a running thread. - */ -extern (C) void thread_resumeAll(); - - -/** - * Indicates the kind of scan being performed by $(D thread_scanAllType). - */ -enum ScanType -{ - stack, /// The stack and/or registers are being scanned. - tls, /// TLS data is being scanned. -} - -alias void delegate(void*, void*) ScanAllThreadsFn; /// The scanning function. -alias void delegate(ScanType, void*, void*) ScanAllThreadsTypeFn; /// ditto - -/** - * The main entry point for garbage collection. The supplied delegate - * will be passed ranges representing both stack and register values. - * - * Params: - * scan = The scanner function. It should scan from p1 through p2 - 1. - * - * In: - * This routine must be preceded by a call to thread_suspendAll. - */ -extern (C) void thread_scanAllType( scope ScanAllThreadsTypeFn scan ); - - -/** - * The main entry point for garbage collection. The supplied delegate - * will be passed ranges representing both stack and register values. - * - * Params: - * scan = The scanner function. It should scan from p1 through p2 - 1. - * - * In: - * This routine must be preceded by a call to thread_suspendAll. - */ -extern (C) void thread_scanAll( scope ScanAllThreadsFn scan ); - - -/** - * Signals that the code following this call is a critical region. Any code in - * this region must finish running before the calling thread can be suspended - * by a call to thread_suspendAll. - * - * This function is, in particular, meant to help maintain garbage collector - * invariants when a lock is not used. - * - * A critical region is exited with thread_exitCriticalRegion. - * - * $(RED Warning): - * Using critical regions is extremely error-prone. For instance, using locks - * inside a critical region can easily result in a deadlock when another thread - * holding the lock already got suspended. - * - * The term and concept of a 'critical region' comes from - * $(LINK2 https://github.com/mono/mono/blob/521f4a198e442573c400835ef19bbb36b60b0ebb/mono/metadata/sgen-gc.h#L925 Mono's SGen garbage collector). - * - * In: - * The calling thread must be attached to the runtime. - */ -extern (C) void thread_enterCriticalRegion(); - - -/** - * Signals that the calling thread is no longer in a critical region. Following - * a call to this function, the thread can once again be suspended. - * - * In: - * The calling thread must be attached to the runtime. - */ -extern (C) void thread_exitCriticalRegion(); - - -/** - * Returns true if the current thread is in a critical region; otherwise, false. - * - * In: - * The calling thread must be attached to the runtime. - */ -extern (C) bool thread_inCriticalRegion(); - -/** - * Indicates whether an address has been marked by the GC. - */ -enum IsMarked : int -{ - no, /// Address is not marked. - yes, /// Address is marked. - unknown, /// Address is not managed by the GC. -} - -alias IsMarked delegate( void* addr ) IsMarkedDg; - -/** - * This routine allows the runtime to process any special per-thread handling - * for the GC. This is needed for taking into account any memory that is - * referenced by non-scanned pointers but is about to be freed. That currently - * means the array append cache. - * - * Params: - * isMarked = The function used to check if $(D addr) is marked. - * - * In: - * This routine must be called just prior to resuming all threads. - */ -extern(C) void thread_processGCMarks( scope IsMarkedDg isMarked ); - - -/** - * Returns the stack top of the currently active stack within the calling - * thread. - * - * In: - * The calling thread must be attached to the runtime. - * - * Returns: - * The address of the stack top. - */ -extern (C) void* thread_stackTop(); - - -/** - * Returns the stack bottom of the currently active stack within the calling - * thread. - * - * In: - * The calling thread must be attached to the runtime. - * - * Returns: - * The address of the stack bottom. - */ -extern (C) void* thread_stackBottom(); - - -/////////////////////////////////////////////////////////////////////////////// -// Thread Group -/////////////////////////////////////////////////////////////////////////////// - - -/** - * This class is intended to simplify certain common programming techniques. - */ -class ThreadGroup -{ - /** - * Creates and starts a new Thread object that executes fn and adds it to - * the list of tracked threads. - * - * Params: - * fn = The thread function. - * - * Returns: - * A reference to the newly created thread. - */ - final Thread create( void function() fn ); - - - /** - * Creates and starts a new Thread object that executes dg and adds it to - * the list of tracked threads. - * - * Params: - * dg = The thread function. - * - * Returns: - * A reference to the newly created thread. - */ - final Thread create( void delegate() dg ); - - - /** - * Add t to the list of tracked threads if it is not already being tracked. - * - * Params: - * t = The thread to add. - * - * In: - * t must not be null. - */ - final void add( Thread t ); - - - /** - * Removes t from the list of tracked threads. No operation will be - * performed if t is not currently being tracked by this object. - * - * Params: - * t = The thread to remove. - * - * In: - * t must not be null. - */ - final void remove( Thread t ); - - - /** - * Operates on all threads currently tracked by this object. - */ - final int opApply( scope int delegate( ref Thread ) dg ); - - - /** - * Iteratively joins all tracked threads. This function will block add, - * remove, and opApply until it completes. - * - * Params: - * rethrow = Rethrow any unhandled exception which may have caused the - * current thread to terminate. - * - * Throws: - * Any exception not handled by the joined threads. - */ - final void joinAll( bool rethrow = true ); - - -private: - - // These must be kept in sync with core/thread.d - version (D_LP64) - { - enum ThreadGroupSize = 24; - } - else - { - static assert((void*).sizeof == 4); // 32-bit - enum ThreadGroupSize = 12; - } - - void data[ThreadGroupSize - __traits(classInstanceSize, Object)] = void; -} - - -/////////////////////////////////////////////////////////////////////////////// -// Fiber Platform Detection and Memory Allocation -/////////////////////////////////////////////////////////////////////////////// - -private -{ - // These must be kept in sync with core/thread.d - version( D_InlineAsm_X86 ) - { - version( Windows ) - version = NoUcontext; - else version( Posix ) - version = NoUcontext; - } - else version( D_InlineAsm_X86_64 ) - { - version( Windows ) - version = NoUcontext; - else version( Posix ) - version = NoUcontext; - } - else version( PPC ) - { - version( Posix ) - version = NoUcontext; - } - else version( PPC64 ) - { - version( Posix ) - { - // uses ucontext_t. - } - } - else version( MIPS_O32 ) - { - version( Posix ) - version = NoUcontext; - } - else version( ARM ) - { - version( Posix ) - version = NoUcontext; - } - - version( Posix ) - { - version( NoUcontext ) {} else - { - import core.sys.posix.ucontext; - } - } -} - -private extern __gshared const size_t PAGESIZE; - -shared static this(); - - -/////////////////////////////////////////////////////////////////////////////// -// Fiber -/////////////////////////////////////////////////////////////////////////////// - -/** - * This class provides a cooperative concurrency mechanism integrated with the - * threading and garbage collection functionality. Calling a fiber may be - * considered a blocking operation that returns when the fiber yields (via - * Fiber.yield()). Execution occurs within the context of the calling thread - * so synchronization is not necessary to guarantee memory visibility so long - * as the same thread calls the fiber each time. Please note that there is no - * requirement that a fiber be bound to one specific thread. Rather, fibers - * may be freely passed between threads so long as they are not currently - * executing. Like threads, a new fiber thread may be created using either - * derivation or composition, as in the following example. - * - * Example: - * ---------------------------------------------------------------------- - * - * class DerivedFiber : Fiber - * { - * this() - * { - * super( &run ); - * } - * - * private : - * void run() - * { - * printf( "Derived fiber running.\n" ); - * } - * } - * - * void fiberFunc() - * { - * printf( "Composed fiber running.\n" ); - * Fiber.yield(); - * printf( "Composed fiber running.\n" ); - * } - * - * // create instances of each type - * Fiber derived = new DerivedFiber(); - * Fiber composed = new Fiber( &fiberFunc ); - * - * // call both fibers once - * derived.call(); - * composed.call(); - * printf( "Execution returned to calling context.\n" ); - * composed.call(); - * - * // since each fiber has run to completion, each should have state TERM - * assert( derived.state == Fiber.State.TERM ); - * assert( composed.state == Fiber.State.TERM ); - * - * ---------------------------------------------------------------------- - * - * Authors: Based on a design by Mikola Lysenko. - */ -class Fiber -{ - /////////////////////////////////////////////////////////////////////////// - // Initialization - /////////////////////////////////////////////////////////////////////////// - - - /** - * Initializes a fiber object which is associated with a static - * D function. - * - * Params: - * fn = The fiber function. - * sz = The stack size for this fiber. - * - * In: - * fn must not be null. - */ - this( void function() fn, size_t sz = PAGESIZE*4 ); - - - /** - * Initializes a fiber object which is associated with a dynamic - * D function. - * - * Params: - * dg = The fiber function. - * sz = The stack size for this fiber. - * - * In: - * dg must not be null. - */ - this( void delegate() dg, size_t sz = PAGESIZE*4 ); - - - ~this(); - - - /////////////////////////////////////////////////////////////////////////// - // General Actions - /////////////////////////////////////////////////////////////////////////// - - - /** - * Transfers execution to this fiber object. The calling context will be - * suspended until the fiber calls Fiber.yield() or until it terminates - * via an unhandled exception. - * - * Params: - * rethrow = Rethrow any unhandled exception which may have caused this - * fiber to terminate. - * - * In: - * This fiber must be in state HOLD. - * - * Throws: - * Any exception not handled by the joined thread. - * - * Returns: - * Any exception not handled by this fiber if rethrow = false, null - * otherwise. - */ - final Object call( bool rethrow = true ); - - - /** - * Resets this fiber so that it may be re-used, optionally with a - * new function/delegate. This routine may only be called for - * fibers that have terminated, as doing otherwise could result in - * scope-dependent functionality that is not executed. - * Stack-based classes, for example, may not be cleaned up - * properly if a fiber is reset before it has terminated. - * - * In: - * This fiber must be in state TERM. - */ - final void reset(); - - /// ditto - final void reset( void function() fn ); - - /// ditto - final void reset( void delegate() dg ); - - /////////////////////////////////////////////////////////////////////////// - // General Properties - /////////////////////////////////////////////////////////////////////////// - - - /** - * A fiber may occupy one of three states: HOLD, EXEC, and TERM. The HOLD - * state applies to any fiber that is suspended and ready to be called. - * The EXEC state will be set for any fiber that is currently executing. - * And the TERM state is set when a fiber terminates. Once a fiber - * terminates, it must be reset before it may be called again. - */ - enum State - { - HOLD, /// - EXEC, /// - TERM /// - } - - - /** - * Gets the current state of this fiber. - * - * Returns: - * The state of this fiber as an enumerated value. - */ - final @property State state() const; - - - /////////////////////////////////////////////////////////////////////////// - // Actions on Calling Fiber - /////////////////////////////////////////////////////////////////////////// - - - /** - * Forces a context switch to occur away from the calling fiber. - */ - static void yield(); - - - /** - * Forces a context switch to occur away from the calling fiber and then - * throws obj in the calling fiber. - * - * Params: - * t = The object to throw. - * - * In: - * t must not be null. - */ - static void yieldAndThrow( Throwable t ); - - - /////////////////////////////////////////////////////////////////////////// - // Fiber Accessors - /////////////////////////////////////////////////////////////////////////// - - - /** - * Provides a reference to the calling fiber or null if no fiber is - * currently active. - * - * Returns: - * The fiber object representing the calling fiber or null if no fiber - * is currently active within this thread. The result of deleting this object is undefined. - */ - static Fiber getThis(); - - - /////////////////////////////////////////////////////////////////////////// - // Static Initialization - /////////////////////////////////////////////////////////////////////////// - - - version( Posix ) - { - static this(); - } - -private: - // These must be kept in sync with core/thread.d - version (D_LP64) - { - version (Windows) enum FiberSize = 88; - else version (OSX) enum FiberSize = 88; - else version (Posix) - { - static if( __traits( compiles, ucontext_t ) ) - enum FiberSize = 88 + ucontext_t.sizeof + 8; - else - enum FiberSize = 88; - } - else static assert(0, "Platform not supported."); - } - else - { - static assert((void*).sizeof == 4); // 32-bit - - version (Windows) enum FiberSize = 44; - else version (OSX) enum FiberSize = 44; - else version (Posix) - { - static if( __traits( compiles, ucontext_t ) ) - { - // ucontext_t might have an alignment larger than 4. - static roundUp()(size_t n) - { - return (n + (ucontext_t.alignof - 1)) & ~(ucontext_t.alignof - 1); - } - enum FiberSize = roundUp(roundUp(44) + ucontext_t.sizeof + 4); - } - else - enum FiberSize = 44; - } - else static assert(0, "Platform not supported."); - } - - void data[FiberSize - __traits(classInstanceSize, Object)] = void; -} - -version( OSX ) -{ - // NOTE: The Mach-O object file format does not allow for thread local - // storage declarations. So instead we roll our own by putting tls - // into the sections bracketed by _tls_beg and _tls_end. - // - // This function is called by the code emitted by the compiler. It - // is expected to translate an address into the TLS static data to - // the corresponding address in the TLS dynamic per-thread data. - extern (D) void* ___tls_get_addr( void* p ); -} diff --git a/libphobos/libdruntime/core/threadasm.S b/libphobos/libdruntime/core/threadasm.S index cd4094438..5128a951a 100644 --- a/libphobos/libdruntime/core/threadasm.S +++ b/libphobos/libdruntime/core/threadasm.S @@ -13,6 +13,20 @@ * http://www.boost.org/LICENSE_1_0.txt) */ +/* NOTE: This file has been patched from the original DMD distribution to + * work with the GDC compiler. + */ +#if defined(__linux__) +/* + * Mark the resulting object file as not requiring execution permissions on + * stack memory. The absence of this section would mark the whole resulting + * library as requiring an executable stack, making it impossible to + * dynamically load druntime on several Linux platforms where this is + * forbidden due to security policies. + */ +.section .note.GNU-stack,"",@progbits +#endif + /************************************************************************************ * POWER PC ASM BITS ************************************************************************************/ diff --git a/libphobos/libdruntime/core/time.d b/libphobos/libdruntime/core/time.d index 8b940189d..9a94882d5 100644 --- a/libphobos/libdruntime/core/time.d +++ b/libphobos/libdruntime/core/time.d @@ -12,16 +12,73 @@ are a few functions that also allow "nsecs", but very little actually has precision greater than hnsecs. + $(BOOKTABLE Cheat Sheet, + $(TR $(TH Symbol) $(TH Description)) + $(LEADINGROW Types) + $(TR $(TDNW $(LREF Duration)) $(TD Represents a duration of time of weeks + or less (kept internally as hnsecs). (e.g. 22 days or 700 seconds).)) + $(TR $(TDNW $(LREF TickDuration)) $(TD Represents a duration of time in + system clock ticks, using the highest precision that the system provides.)) + $(TR $(TDNW $(LREF FracSec)) $(TD Represents fractional seconds + (portions of time smaller than a second).)) + $(LEADINGROW Functions) + $(TR $(TDNW $(LREF convert)) $(TD Generic way of converting between two + time units.)) + $(TR $(TDNW $(LREF dur)) $(TD Allow constructing a $(LREF Duration) from + the given time units with the given length.)) + $(TR $(TDNW $(LREF weeks)$(NBSP)$(LREF days)$(NBSP)$(LREF hours)$(BR) + $(LREF minutes)$(NBSP)$(LREF seconds)$(NBSP)$(LREF msecs)$(BR) + $(LREF usecs)$(NBSP)$(LREF hnsecs)$(NBSP)$(LREF nsecs)) + $(TD Short-hands for $(D dur).)) + $(TR $(TDNW $(LREF abs)) $(TD Returns the absolute value of a duration.)) + ) + + $(BOOKTABLE Conversions, + $(TR $(TH ) + $(TH From $(LREF Duration)) + $(TH From $(LREF TickDuration)) + $(TH From $(LREF FracSec)) + $(TH From units) + ) + $(TR $(TD $(B To $(LREF Duration))) + $(TD -) + $(TD $(D tickDuration.)$(SXREF conv, to)$(D !Duration())) + $(TD -) + $(TD $(D dur!"msecs"(5)) or $(D 5.msecs())) + ) + $(TR $(TD $(B To $(LREF TickDuration))) + $(TD $(D duration.)$(SXREF conv, to)$(D !TickDuration())) + $(TD -) + $(TD -) + $(TD $(D TickDuration.from!"msecs"(msecs))) + ) + $(TR $(TD $(B To $(LREF FracSec))) + $(TD $(D duration.fracSec)) + $(TD -) + $(TD -) + $(TD $(D FracSec.from!"msecs"(msecs))) + ) + $(TR $(TD $(B To units)) + $(TD $(D duration.total!"days")) + $(TD $(D tickDuration.msecs)) + $(TD $(D fracSec.msecs)) + $(TD $(D convert!("days", "msecs")(msecs))) + )) + Copyright: Copyright 2010 - 2012 License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: Jonathan M Davis and Kato Shoichi Source: $(DRUNTIMESRC core/_time.d) + Macros: + NBSP=  + SXREF=$(D $2) +/ module core.time; import core.exception; import core.stdc.time; import core.stdc.stdio; +import core.internal.traits : _Unqual = Unqual; version(Windows) { @@ -40,7 +97,7 @@ version(OSX) public import core.sys.osx.mach.kern_return; -extern(C) +extern(C) nothrow { struct mach_timebase_info_data_t @@ -82,19 +139,6 @@ ulong mach_absolute_time(); years and months rather than creating a Duration of years or months and adding that to a $(XREF datetime, Date). But Duration is used when dealing with weeks or smaller. - - Examples: --------------------- -assert(dur!"days"(12) == Duration(10_368_000_000_000L)); -assert(dur!"hnsecs"(27) == Duration(27)); -assert(std.datetime.Date(2010, 9, 7) + dur!"days"(5) == - std.datetime.Date(2010, 9, 12)); - -assert(days(-12) == Duration(-10_368_000_000_000L)); -assert(hnsecs(-27) == Duration(-27)); -assert(std.datetime.Date(2010, 9, 7) - std.datetime.Date(2010, 10, 3) == - days(-26)); --------------------- +/ struct Duration { @@ -715,6 +759,9 @@ public: /++ Returns a $(LREF TickDuration) with the same number of hnsecs as this $(D Duration). + Note that the conventional way to convert between $(D Duration) and + $(D TickDuration) is using $(XREF conv, to), e.g.: + $(D duration.to!TickDuration()) +/ TickDuration opCast(T)() @safe const pure nothrow if(is(_Unqual!T == TickDuration)) @@ -762,21 +809,343 @@ public: /++ - Returns the number of the given units in this $(D Duration) - (minus the larger units). + Splits out the Duration into the given units. + + split takes the list of time units to split out as template arguments. + The time unit strings must be given in decreasing order. How it returns + the values for those units depends on the overload used. + + The overload which accepts function arguments takes integral types in + the order that the time unit strings were given, and those integers are + passed by $(D ref). split assigns the values for the units to each + corresponding integer. Any integral type may be used, but no attempt is + made to prevent integer overflow, so don't use small integral types in + circumstances where the values for those units aren't likely to fit in + an integral type that small. + + The overload with no arguments returns the values for the units in a + struct with members whose names are the same as the given time unit + strings. The members are all $(D long)s. This overload will also work + with no time strings being given, in which case $(I all) of the time + units from weeks through hnsecs will be provided (but no nsecs, since it + would always be $(D 0)). + + For both overloads, the entire value of the Duration is split among the + units (rather than splitting the Duration across all units and then only + providing the values for the requested units), so if only one unit is + given, the result is equivalent to $(LREF total). + + $(D "nsecs") is accepted by split, but $(D "years") and $(D "months") + are not. + + For negative durations, all of the split values will be negative. + +/ + template split(units...) + if(allAreAcceptedUnits!("weeks", "days", "hours", "minutes", "seconds", + "msecs", "usecs", "hnsecs", "nsecs")(units) && + unitsAreInDescendingOrder(units)) + { + /++ Ditto +/ + void split(Args...)(out Args args) @safe const pure nothrow + if(units.length != 0 && args.length == units.length && allAreMutableIntegralTypes!Args) + { + long hnsecs = _hnsecs; + foreach(i, unit; units) + { + static if(unit == "nsecs") + args[i] = cast(typeof(args[i]))convert!("hnsecs", "nsecs")(hnsecs); + else + args[i] = cast(typeof(args[i]))splitUnitsFromHNSecs!unit(hnsecs); + } + } - Examples: --------------------- -assert(dur!"weeks"(12).get!"weeks"() == 12); -assert(dur!"weeks"(12).get!"days"() == 0); + /++ Ditto +/ + auto split() @safe const pure nothrow + { + static if(units.length == 0) + return split!("weeks", "days", "hours", "minutes", "seconds", "msecs", "usecs", "hnsecs")(); + else + { + static string genMemberDecls() + { + string retval; + foreach(unit; units) + { + retval ~= "long "; + retval ~= unit; + retval ~= "; "; + } + return retval; + } -assert(dur!"days"(13).get!"weeks"() == 1); -assert(dur!"days"(13).get!"days"() == 6); + static struct SplitUnits + { + mixin(genMemberDecls); + } -assert(dur!"hours"(49).get!"days"() == 2); -assert(dur!"hours"(49).get!"hours"() == 1); --------------------- + static string genSplitCall() + { + auto retval = "split("; + foreach(i, unit; units) + { + retval ~= "su."; + retval ~= unit; + if(i < units.length - 1) + retval ~= ", "; + else + retval ~= ");"; + } + return retval; + } + + SplitUnits su = void; + mixin(genSplitCall()); + return su; + } + } + + /+ + Whether all of the given arguments are integral types. + +/ + private template allAreMutableIntegralTypes(Args...) + { + static if(Args.length == 0) + enum allAreMutableIntegralTypes = true; + else static if(!is(Args[0] == long) && + !is(Args[0] == int) && + !is(Args[0] == short) && + !is(Args[0] == byte) && + !is(Args[0] == ulong) && + !is(Args[0] == uint) && + !is(Args[0] == ushort) && + !is(Args[0] == ubyte)) + { + enum allAreMutableIntegralTypes = false; + } + else + enum allAreMutableIntegralTypes = allAreMutableIntegralTypes!(Args[1 .. $]); + } + + unittest + { + foreach(T; _TypeTuple!(long, int, short, byte, ulong, uint, ushort, ubyte)) + static assert(allAreMutableIntegralTypes!T); + foreach(T; _TypeTuple!(long, int, short, byte, ulong, uint, ushort, ubyte)) + static assert(!allAreMutableIntegralTypes!(const T)); + foreach(T; _TypeTuple!(char, wchar, dchar, float, double, real, string)) + static assert(!allAreMutableIntegralTypes!T); + static assert(allAreMutableIntegralTypes!(long, int, short, byte)); + static assert(!allAreMutableIntegralTypes!(long, int, short, char, byte)); + static assert(!allAreMutableIntegralTypes!(long, int*, short)); + } + } + + /// + unittest + { + { + auto d = dur!"days"(12) + dur!"minutes"(7) + dur!"usecs"(501223); + long days; + int seconds; + short msecs; + d.split!("days", "seconds", "msecs")(days, seconds, msecs); + assert(days == 12); + assert(seconds == 7 * 60); + assert(msecs == 501); + + auto splitStruct = d.split!("days", "seconds", "msecs")(); + assert(splitStruct.days == 12); + assert(splitStruct.seconds == 7 * 60); + assert(splitStruct.msecs == 501); + + auto fullSplitStruct = d.split(); + assert(fullSplitStruct.weeks == 1); + assert(fullSplitStruct.days == 5); + assert(fullSplitStruct.hours == 0); + assert(fullSplitStruct.minutes == 7); + assert(fullSplitStruct.seconds == 0); + assert(fullSplitStruct.msecs == 501); + assert(fullSplitStruct.usecs == 223); + assert(fullSplitStruct.hnsecs == 0); + + assert(d.split!"minutes"().minutes == d.total!"minutes"); + } + + { + auto d = dur!"days"(12); + assert(d.split!"weeks"().weeks == 1); + assert(d.split!"days"().days == 12); + + assert(d.split().weeks == 1); + assert(d.split().days == 5); + } + + { + auto d = dur!"days"(7) + dur!"hnsecs"(42); + assert(d.split!("seconds", "nsecs")().nsecs == 4200); + } + + { + auto d = dur!"days"(-7) + dur!"hours"(-9); + auto result = d.split!("days", "hours")(); + assert(result.days == -7); + assert(result.hours == -9); + } + } + + pure nothrow unittest + { + foreach(D; _TypeTuple!(const Duration, immutable Duration)) + { + D d = dur!"weeks"(3) + dur!"days"(5) + dur!"hours"(19) + dur!"minutes"(7) + + dur!"seconds"(2) + dur!"hnsecs"(1234567); + byte weeks; + ubyte days; + short hours; + ushort minutes; + int seconds; + uint msecs; + long usecs; + ulong hnsecs; + long nsecs; + + d.split!("weeks", "days", "hours", "minutes", "seconds", "msecs", "usecs", "hnsecs", "nsecs") + (weeks, days, hours, minutes, seconds, msecs, usecs, hnsecs, nsecs); + assert(weeks == 3); + assert(days == 5); + assert(hours == 19); + assert(minutes == 7); + assert(seconds == 2); + assert(msecs == 123); + assert(usecs == 456); + assert(hnsecs == 7); + assert(nsecs == 0); + + d.split!("weeks", "days", "hours", "seconds", "usecs")(weeks, days, hours, seconds, usecs); + assert(weeks == 3); + assert(days == 5); + assert(hours == 19); + assert(seconds == 422); + assert(usecs == 123456); + + d.split!("days", "minutes", "seconds", "nsecs")(days, minutes, seconds, nsecs); + assert(days == 26); + assert(minutes == 1147); + assert(seconds == 2); + assert(nsecs == 123456700); + + d.split!("minutes", "msecs", "usecs", "hnsecs")(minutes, msecs, usecs, hnsecs); + assert(minutes == 38587); + assert(msecs == 2123); + assert(usecs == 456); + assert(hnsecs == 7); + + { + auto result = d.split!("weeks", "days", "hours", "minutes", "seconds", + "msecs", "usecs", "hnsecs", "nsecs"); + assert(result.weeks == 3); + assert(result.days == 5); + assert(result.hours == 19); + assert(result.minutes == 7); + assert(result.seconds == 2); + assert(result.msecs == 123); + assert(result.usecs == 456); + assert(result.hnsecs == 7); + assert(result.nsecs == 0); + } + + { + auto result = d.split!("weeks", "days", "hours", "seconds", "usecs"); + assert(result.weeks == 3); + assert(result.days == 5); + assert(result.hours == 19); + assert(result.seconds == 422); + assert(result.usecs == 123456); + } + + { + auto result = d.split!("days", "minutes", "seconds", "nsecs")(); + assert(result.days == 26); + assert(result.minutes == 1147); + assert(result.seconds == 2); + assert(result.nsecs == 123456700); + } + + { + auto result = d.split!("minutes", "msecs", "usecs", "hnsecs")(); + assert(result.minutes == 38587); + assert(result.msecs == 2123); + assert(result.usecs == 456); + assert(result.hnsecs == 7); + } + + { + auto result = d.split(); + assert(result.weeks == 3); + assert(result.days == 5); + assert(result.hours == 19); + assert(result.minutes == 7); + assert(result.seconds == 2); + assert(result.msecs == 123); + assert(result.usecs == 456); + assert(result.hnsecs == 7); + static assert(!is(typeof(result.nsecs))); + } + + static assert(!is(typeof(d.split("seconds", "hnsecs")(seconds)))); + static assert(!is(typeof(d.split("hnsecs", "seconds", "minutes")(hnsecs, seconds, minutes)))); + static assert(!is(typeof(d.split("hnsecs", "seconds", "msecs")(hnsecs, seconds, msecs)))); + static assert(!is(typeof(d.split("seconds", "hnecs", "msecs")(seconds, hnsecs, msecs)))); + static assert(!is(typeof(d.split("seconds", "msecs", "msecs")(seconds, msecs, msecs)))); + static assert(!is(typeof(d.split("hnsecs", "seconds", "minutes")()))); + static assert(!is(typeof(d.split("hnsecs", "seconds", "msecs")()))); + static assert(!is(typeof(d.split("seconds", "hnecs", "msecs")()))); + static assert(!is(typeof(d.split("seconds", "msecs", "msecs")()))); + alias _TypeTuple!("nsecs", "hnsecs", "usecs", "msecs", "seconds", + "minutes", "hours", "days", "weeks") timeStrs; + foreach(i, str; timeStrs[1 .. $]) + static assert(!is(typeof(d.split!(timeStrs[i - 1], str)()))); + + D nd = -d; + + { + auto result = nd.split(); + assert(result.weeks == -3); + assert(result.days == -5); + assert(result.hours == -19); + assert(result.minutes == -7); + assert(result.seconds == -2); + assert(result.msecs == -123); + assert(result.usecs == -456); + assert(result.hnsecs == -7); + } + + { + auto result = nd.split!("weeks", "days", "hours", "minutes", "seconds", "nsecs")(); + assert(result.weeks == -3); + assert(result.days == -5); + assert(result.hours == -19); + assert(result.minutes == -7); + assert(result.seconds == -2); + assert(result.nsecs == -123456700); + } + } + } + + + /++ + $(RED Deprecated. Please use $(LREF split) instead. Too frequently, + get or one of the individual unit getters is used when the + function that gave the desired behavior was $(LREF total). This + should make it more explicit and help prevent bugs. This function + will be removed in June 2015.) + + Returns the number of the given units in this $(D Duration) + (minus the larger units). + + $(D d.get!"minutes"()) is equivalent to $(D d.split().minutes). +/ + deprecated("Please use split instead. get was too frequently confused for total.") long get(string units)() @safe const pure nothrow if(units == "weeks" || units == "days" || @@ -789,63 +1158,63 @@ assert(dur!"hours"(49).get!"hours"() == 1); else { immutable hnsecs = removeUnitsFromHNSecs!(nextLargerTimeUnits!units)(_hnsecs); - return getUnitsFromHNSecs!units(hnsecs); } } - //Verify Examples - unittest + /// + deprecated unittest { - assert(dur!"weeks"(12).get!"weeks"() == 12); - assert(dur!"weeks"(12).get!"days"() == 0); + assert(dur!"weeks"(12).get!"weeks" == 12); + assert(dur!"weeks"(12).get!"days" == 0); - assert(dur!"days"(13).get!"weeks"() == 1); - assert(dur!"days"(13).get!"days"() == 6); + assert(dur!"days"(13).get!"weeks" == 1); + assert(dur!"days"(13).get!"days" == 6); - assert(dur!"hours"(49).get!"days"() == 2); - assert(dur!"hours"(49).get!"hours"() == 1); + assert(dur!"hours"(49).get!"days" == 2); + assert(dur!"hours"(49).get!"hours" == 1); } - unittest + deprecated unittest { foreach(D; _TypeTuple!(const Duration, immutable Duration)) { - assert((cast(D)dur!"weeks"(12)).get!"weeks"() == 12); - assert((cast(D)dur!"weeks"(12)).get!"days"() == 0); + assert((cast(D)dur!"weeks"(12)).get!"weeks" == 12); + assert((cast(D)dur!"weeks"(12)).get!"days" == 0); - assert((cast(D)dur!"days"(13)).get!"weeks"() == 1); - assert((cast(D)dur!"days"(13)).get!"days"() == 6); + assert((cast(D)dur!"days"(13)).get!"weeks" == 1); + assert((cast(D)dur!"days"(13)).get!"days" == 6); - assert((cast(D)dur!"hours"(49)).get!"days"() == 2); - assert((cast(D)dur!"hours"(49)).get!"hours"() == 1); + assert((cast(D)dur!"hours"(49)).get!"days" == 2); + assert((cast(D)dur!"hours"(49)).get!"hours" == 1); } } /++ + $(RED Deprecated. Please use $(LREF split) instead. Too frequently, + $(LREF get) or one of the individual unit getters is used when the + function that gave the desired behavior was $(LREF total). This + should make it more explicit and help prevent bugs. This function + will be removed in June 2015.) + Returns the number of weeks in this $(D Duration) (minus the larger units). - - Examples: --------------------- -assert(dur!"weeks"(12).weeks == 12); -assert(dur!"days"(13).weeks == 1); --------------------- +/ + deprecated(`Please use split instead. The functions which wrapped get were too frequently confused with total.`) @property long weeks() @safe const pure nothrow { return get!"weeks"(); } - //Verify Examples - unittest + /// + deprecated unittest { assert(dur!"weeks"(12).weeks == 12); assert(dur!"days"(13).weeks == 1); } - unittest + deprecated unittest { foreach(D; _TypeTuple!(const Duration, immutable Duration)) { @@ -856,30 +1225,30 @@ assert(dur!"days"(13).weeks == 1); /++ + $(RED Deprecated. Please use $(LREF split) instead. Too frequently, + $(LREF get) or one of the individual unit getters is used when the + function that gave the desired behavior was $(LREF total). This + should make it more explicit and help prevent bugs. This function + will be removed in June 2015.) + Returns the number of days in this $(D Duration) (minus the larger units). - - Examples: --------------------- -assert(dur!"weeks"(12).days == 0); -assert(dur!"days"(13).days == 6); -assert(dur!"hours"(49).days == 2); --------------------- +/ + deprecated(`Please use split instead. days was too frequently confused for total!"days".`) @property long days() @safe const pure nothrow { return get!"days"(); } - //Verify Examples. - unittest + /// + deprecated unittest { assert(dur!"weeks"(12).days == 0); assert(dur!"days"(13).days == 6); assert(dur!"hours"(49).days == 2); } - unittest + deprecated unittest { foreach(D; _TypeTuple!(const Duration, immutable Duration)) { @@ -891,30 +1260,30 @@ assert(dur!"hours"(49).days == 2); /++ + $(RED Deprecated. Please use $(LREF split) instead. Too frequently, + $(LREF get) or one of the individual unit getters is used when the + function that gave the desired behavior was $(LREF total). This + should make it more explicit and help prevent bugs. This function + will be removed in June 2015.) + Returns the number of hours in this $(D Duration) (minus the larger units). - - Examples: --------------------- -assert(dur!"days"(8).hours == 0); -assert(dur!"hours"(49).hours == 1); -assert(dur!"minutes"(121).hours == 2); --------------------- +/ + deprecated(`Please use split instead. hours was too frequently confused for total!"hours".`) @property long hours() @safe const pure nothrow { return get!"hours"(); } - //Verify Examples. - unittest + /// + deprecated unittest { assert(dur!"days"(8).hours == 0); assert(dur!"hours"(49).hours == 1); assert(dur!"minutes"(121).hours == 2); } - unittest + deprecated unittest { foreach(D; _TypeTuple!(const Duration, immutable Duration)) { @@ -926,30 +1295,30 @@ assert(dur!"minutes"(121).hours == 2); /++ + $(RED Deprecated. Please use $(LREF split) instead. Too frequently, + $(LREF get) or one of the individual unit getters is used when the + function that gave the desired behavior was $(LREF total). This + should make it more explicit and help prevent bugs. This function + will be removed in June 2015.) + Returns the number of minutes in this $(D Duration) (minus the larger units). - - Examples: --------------------- -assert(dur!"hours"(47).minutes == 0); -assert(dur!"minutes"(127).minutes == 7); -assert(dur!"seconds"(121).minutes == 2); --------------------- +/ + deprecated(`Please use split instead. minutes was too frequently confused for total!"minutes".`) @property long minutes() @safe const pure nothrow { return get!"minutes"(); } - //Verify Examples. - unittest + /// + deprecated unittest { assert(dur!"hours"(47).minutes == 0); assert(dur!"minutes"(127).minutes == 7); assert(dur!"seconds"(121).minutes == 2); } - unittest + deprecated unittest { foreach(D; _TypeTuple!(const Duration, immutable Duration)) { @@ -961,30 +1330,30 @@ assert(dur!"seconds"(121).minutes == 2); /++ + $(RED Deprecated. Please use $(LREF split) instead. Too frequently, + $(LREF get) or one of the individual unit getters is used when the + function that gave the desired behavior was $(LREF total). This + should make it more explicit and help prevent bugs. This function + will be removed in June 2015.) + Returns the number of seconds in this $(D Duration) (minus the larger units). - - Examples: --------------------- -assert(dur!"minutes"(47).seconds == 0); -assert(dur!"seconds"(127).seconds == 7); -assert(dur!"msecs"(1217).seconds == 1); --------------------- +/ + deprecated(`Please use split instead. seconds was too frequently confused for total!"seconds".`) @property long seconds() @safe const pure nothrow { return get!"seconds"(); } - //Verify Examples. - unittest + /// + deprecated unittest { assert(dur!"minutes"(47).seconds == 0); assert(dur!"seconds"(127).seconds == 7); assert(dur!"msecs"(1217).seconds == 1); } - unittest + deprecated unittest { foreach(D; _TypeTuple!(const Duration, immutable Duration)) { @@ -996,23 +1365,15 @@ assert(dur!"msecs"(1217).seconds == 1); /++ - Returns the fractional seconds passed the second in this $(D Duration). + $(RED Deprecated. Please use $(LREF split) instead. Too frequently, + $(LREF get) or one of the individual unit getters is used when the + function that gave the desired behavior was $(LREF total). This + should make it more explicit and help prevent bugs. This function + will be removed in June 2015.) - Examples: --------------------- -assert(dur!"msecs"(1000).fracSec == FracSec.from!"msecs"(0)); -assert(dur!"msecs"(1217).fracSec == FracSec.from!"msecs"(217)); -assert(dur!"usecs"(43).fracSec == FracSec.from!"usecs"(43)); -assert(dur!"hnsecs"(50_007).fracSec == FracSec.from!"hnsecs"(50_007)); -assert(dur!"nsecs"(62_127).fracSec == FracSec.from!"nsecs"(62_100)); - -assert(dur!"msecs"(-1000).fracSec == FracSec.from!"msecs"(-0)); -assert(dur!"msecs"(-1217).fracSec == FracSec.from!"msecs"(-217)); -assert(dur!"usecs"(-43).fracSec == FracSec.from!"usecs"(-43)); -assert(dur!"hnsecs"(-50_007).fracSec == FracSec.from!"hnsecs"(-50_007)); -assert(dur!"nsecs"(-62_127).fracSec == FracSec.from!"nsecs"(-62_100)); --------------------- + Returns the fractional seconds past the second in this $(D Duration). +/ + deprecated(`Please use split instead.`) @property FracSec fracSec() @safe const pure nothrow { try @@ -1025,8 +1386,8 @@ assert(dur!"nsecs"(-62_127).fracSec == FracSec.from!"nsecs"(-62_100)); assert(0, "FracSec.from!\"hnsecs\"() threw."); } - //Verify Examples. - unittest + /// + deprecated unittest { assert(dur!"msecs"(1000).fracSec == FracSec.from!"msecs"(0)); assert(dur!"msecs"(1217).fracSec == FracSec.from!"msecs"(217)); @@ -1041,7 +1402,7 @@ assert(dur!"nsecs"(-62_127).fracSec == FracSec.from!"nsecs"(-62_100)); assert(dur!"nsecs"(-62_127).fracSec == FracSec.from!"nsecs"(-62_100)); } - unittest + deprecated unittest { foreach(D; _TypeTuple!(const Duration, immutable Duration)) { @@ -1062,22 +1423,7 @@ assert(dur!"nsecs"(-62_127).fracSec == FracSec.from!"nsecs"(-62_100)); /++ Returns the total number of the given units in this $(D Duration). - So, unlike $(D get), it does not strip out the larger units. - - Examples: --------------------- -assert(dur!"weeks"(12).total!"weeks" == 12); -assert(dur!"weeks"(12).total!"days" == 84); - -assert(dur!"days"(13).total!"weeks" == 1); -assert(dur!"days"(13).total!"days" == 13); - -assert(dur!"hours"(49).total!"days" == 2); -assert(dur!"hours"(49).total!"hours" == 49); - -assert(dur!"nsecs"(2007).total!"hnsecs" == 20); -assert(dur!"nsecs"(2007).total!"nsecs" == 2000); --------------------- + So, unlike $(D split), it does not strip out the larger units. +/ @property long total(string units)() @safe const pure nothrow if(units == "weeks" || @@ -1096,7 +1442,7 @@ assert(dur!"nsecs"(2007).total!"nsecs" == 2000); return getUnitsFromHNSecs!units(_hnsecs); } - //Verify Examples. + /// unittest { assert(dur!"weeks"(12).total!"weeks" == 12); @@ -1295,6 +1641,23 @@ private: long _hnsecs; } +/// +unittest +{ + import core.time; + + // using the dur template + auto numDays = dur!"days"(12); + + // using the days function + numDays = days(12); + + // alternatively using UFCS syntax + numDays = 12.days; + + auto myTime = 100.msecs + 20_000.usecs + 30_000.hnsecs; + assert(myTime == 123.msecs); +} /++ These allow you to construct a $(D Duration) from the given time units @@ -1673,6 +2036,9 @@ struct TickDuration /++ Returns a $(LREF Duration) with the same number of hnsecs as this $(D TickDuration). + Note that the conventional way to convert between $(D TickDuration) + and $(D Duration) is using $(XREF conv, to), e.g.: + $(D tickDuration.to!Duration()) +/ Duration opCast(T)() @safe const pure nothrow if(is(_Unqual!T == Duration)) @@ -2075,15 +2441,13 @@ struct TickDuration Throws: $(D TimeException) if it fails to get the time. +/ - static @property TickDuration currSystemTick() @trusted + static @property TickDuration currSystemTick() @trusted nothrow { version(Windows) { ulong ticks; - if(QueryPerformanceCounter(cast(long*)&ticks) == 0) - // This probably cannot happen on Windows 95 or later - throw new TimeException("Failed in QueryPerformanceCounter()."); + assert(0, "Failed in QueryPerformanceCounter()."); return TickDuration(ticks); } @@ -2095,7 +2459,7 @@ struct TickDuration { timeval tv; if(gettimeofday(&tv, null) != 0) - throw new TimeException("Failed in gettimeofday()."); + assert(0, "Failed in gettimeofday()."); return TickDuration(tv.tv_sec * TickDuration.ticksPerSec + tv.tv_usec * TickDuration.ticksPerSec / 1000 / 1000); @@ -2106,9 +2470,8 @@ struct TickDuration static if(is(typeof(clock_gettime))) { timespec ts; - if(clock_gettime(CLOCK_MONOTONIC, &ts) != 0) - throw new TimeException("Failed in clock_gettime()."); + assert(0, "Failed in clock_gettime()."); return TickDuration(ts.tv_sec * TickDuration.ticksPerSec + ts.tv_nsec * TickDuration.ticksPerSec / 1000 / 1000 / 1000); @@ -2117,7 +2480,7 @@ struct TickDuration { timeval tv; if(gettimeofday(&tv, null) != 0) - throw new TimeException("Failed in gettimeofday()."); + assert(0, "Failed in gettimeofday()."); return TickDuration(tv.tv_sec * TickDuration.ticksPerSec + tv.tv_usec * TickDuration.ticksPerSec / 1000 / 1000); @@ -2125,7 +2488,7 @@ struct TickDuration } } - unittest + @safe nothrow unittest { assert(TickDuration.currSystemTick.length > 0); } @@ -2907,24 +3270,56 @@ class TimeException : Exception line = The line number where the exception occurred. next = The previous exception in the chain of exceptions, if any. +/ - @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) + this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) @safe pure nothrow + { + super(msg, file, line, next); + } + + /++ + Params: + msg = The message for the exception. + next = The previous exception in the chain of exceptions. + file = The file where the exception occurred. + line = The line number where the exception occurred. + +/ + this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) @safe pure nothrow { super(msg, file, line, next); } } +unittest +{ + { + auto e = new TimeException("hello"); + assert(e.msg == "hello"); + assert(e.file == __FILE__); + assert(e.line == __LINE__ - 3); + assert(e.next is null); + } + + { + auto next = new Exception("foo"); + auto e = new TimeException("goodbye", next); + assert(e.msg == "goodbye"); + assert(e.file == __FILE__); + assert(e.line == __LINE__ - 3); + assert(e.next is next); + } +} + /++ Returns the absolute value of a duration. +/ -Duration abs(Duration duration) +Duration abs(Duration duration) @safe pure nothrow { return Duration(_abs(duration._hnsecs)); } /++ Ditto +/ -TickDuration abs(TickDuration duration) +TickDuration abs(TickDuration duration) @safe pure nothrow { return TickDuration(_abs(duration.length)); } @@ -3125,23 +3520,90 @@ unittest } -/++ - Whether all of the given strings are valid units of time. +/+ + Whether all of the given strings are among the accepted strings. + +/ +bool allAreAcceptedUnits(acceptedUnits...)(string[] units...) +{ + foreach(unit; units) + { + bool found = false; + foreach(acceptedUnit; acceptedUnits) + { + if(unit == acceptedUnit) + { + found = true; + break; + } + } + if(!found) + return false; + } + return true; +} + +unittest +{ + assert(allAreAcceptedUnits!("hours", "seconds")("seconds", "hours")); + assert(!allAreAcceptedUnits!("hours", "seconds")("minutes", "hours")); + assert(!allAreAcceptedUnits!("hours", "seconds")("seconds", "minutes")); + assert(allAreAcceptedUnits!("days", "hours", "minutes", "seconds", "msecs")("minutes")); + assert(!allAreAcceptedUnits!("days", "hours", "minutes", "seconds", "msecs")("usecs")); + assert(!allAreAcceptedUnits!("days", "hours", "minutes", "seconds", "msecs")("secs")); +} + + +/+ + Whether the given time unit strings are arranged in order from largest to + smallest. +/ -bool validTimeUnits(string[] units...) +bool unitsAreInDescendingOrder(string[] units...) { - foreach(str; units) + if(units.length <= 1) + return true; + + immutable string[] timeStrings = ["nsecs", "hnsecs", "usecs", "msecs", "seconds", + "minutes", "hours", "days", "weeks", "months", "years"]; + size_t currIndex = 42; + foreach(i, timeStr; timeStrings) { - switch(str) + if(units[0] == timeStr) { - case "years", "months", "weeks", "days", "hours", "minutes", "seconds", "msecs", "usecs", "hnsecs": - return true; - default: - return false; + currIndex = i; + break; + } + } + assert(currIndex != 42); + + foreach(unit; units[1 .. $]) + { + size_t nextIndex = 42; + foreach(i, timeStr; timeStrings) + { + if(unit == timeStr) + { + nextIndex = i; + break; + } } + assert(nextIndex != 42); + + if(currIndex <= nextIndex) + return false; + currIndex = nextIndex; } + return true; +} - return false; +unittest +{ + assert(unitsAreInDescendingOrder("years", "months", "weeks", "days", "hours", "minutes", + "seconds", "msecs", "usecs", "hnsecs", "nsecs")); + assert(unitsAreInDescendingOrder("weeks", "hours", "msecs")); + assert(unitsAreInDescendingOrder("days", "hours", "minutes")); + assert(unitsAreInDescendingOrder("hnsecs")); + assert(!unitsAreInDescendingOrder("days", "hours", "hours")); + assert(!unitsAreInDescendingOrder("days", "hours", "days")); } @@ -3208,10 +3670,10 @@ unittest } -/++ +/+ Local version of abs, since std.math.abs is in Phobos, not druntime. +/ -long _abs(long val) +long _abs(long val) @safe pure nothrow { return val >= 0 ? val : -val; } @@ -3254,38 +3716,6 @@ string numToString(long value) @safe pure nothrow } -/+ A copy of std.traits.Unqual. +/ -private template _Unqual(T) -{ - version (none) // Error: recursive alias declaration @@@BUG1308@@@ - { - static if (is(T U == const U)) alias _Unqual!U _Unqual; - else static if (is(T U == immutable U)) alias _Unqual!U _Unqual; - else static if (is(T U == shared U)) alias _Unqual!U _Unqual; - else alias T _Unqual; - } - else // workaround - { - static if (is(T U == shared(const U))) alias U _Unqual; - else static if (is(T U == const U )) alias U _Unqual; - else static if (is(T U == immutable U )) alias U _Unqual; - else static if (is(T U == shared U )) alias U _Unqual; - else alias T _Unqual; - } -} - -unittest -{ - static assert(is(_Unqual!(int) == int)); - static assert(is(_Unqual!(const int) == int)); - static assert(is(_Unqual!(immutable int) == int)); - static assert(is(_Unqual!(shared int) == int)); - static assert(is(_Unqual!(shared(const int)) == int)); - alias immutable(int[]) ImmIntArr; - static assert(is(_Unqual!(ImmIntArr) == immutable(int)[])); -} - - /+ A copy of std.typecons.TypeTuple. +/ private template _TypeTuple(TList...) { diff --git a/libphobos/libdruntime/core/vararg.d b/libphobos/libdruntime/core/vararg.d index b5aaf683e..732960526 100644 --- a/libphobos/libdruntime/core/vararg.d +++ b/libphobos/libdruntime/core/vararg.d @@ -16,10 +16,8 @@ */ /* NOTE: This file has been patched from the original DMD distribution to - work with the GDC compiler. - - Modified by Iain Buclaw, February 2011 -*/ + * work with the GDC compiler. + */ module core.vararg; version( GNU ) @@ -31,7 +29,7 @@ else version( X86 ) /** * The base vararg list type. */ - alias void* va_list; + alias char* va_list; /** * This function initializes the supplied argument pointer for subsequent @@ -39,7 +37,7 @@ else version( X86 ) * * Params: * ap = The argument pointer to initialize. - * paramn = The identifier of the rightmost parameter in the function + * parmn = The identifier of the rightmost parameter in the function * parameter list. */ void va_start(T)( out va_list ap, ref T parmn ) diff --git a/libphobos/libdruntime/etc/linux/memoryerror.d b/libphobos/libdruntime/etc/linux/memoryerror.d index 9240adcae..25d6714ec 100644 --- a/libphobos/libdruntime/etc/linux/memoryerror.d +++ b/libphobos/libdruntime/etc/linux/memoryerror.d @@ -131,7 +131,7 @@ version (X86_64) static RegType savedRDI, savedRSI; extern(C) - void handleSignal(int signum, siginfo_t* info, void* contextPtr) + void handleSignal(int signum, siginfo_t* info, void* contextPtr) nothrow { auto context = cast(ucontext_t*)contextPtr; @@ -228,7 +228,7 @@ else version (X86) static RegType savedEAX, savedEDX; extern(C) - void handleSignal(int signum, siginfo_t* info, void* contextPtr) + void handleSignal(int signum, siginfo_t* info, void* contextPtr) nothrow { auto context = cast(ucontext_t*)contextPtr; diff --git a/libphobos/libdruntime/gc/bits.d b/libphobos/libdruntime/gc/bits.d index 3b7e6c753..701628269 100644 --- a/libphobos/libdruntime/gc/bits.d +++ b/libphobos/libdruntime/gc/bits.d @@ -17,9 +17,7 @@ module gc.bits; import core.bitop; import core.stdc.string; import core.stdc.stdlib; - - -private extern (C) void onOutOfMemoryError() @trusted /* pure dmd @@@BUG11461@@@ */ nothrow; +import core.exception : onOutOfMemoryError; version (DigitalMars) @@ -48,7 +46,7 @@ struct GCBits size_t nwords = 0; // allocated words in data[] excluding sentinals size_t nbits = 0; // number of bits in data[] excluding sentinals - void Dtor() + void Dtor() nothrow { if (data) { @@ -65,7 +63,7 @@ struct GCBits } } - void alloc(size_t nbits) + void alloc(size_t nbits) nothrow { this.nbits = nbits; nwords = (nbits + (BITS_PER_WORD - 1)) >> BITS_SHIFT; @@ -74,7 +72,7 @@ struct GCBits onOutOfMemoryError(); } - wordtype test(size_t i) + wordtype test(size_t i) nothrow in { assert(i < nbits); @@ -92,7 +90,7 @@ struct GCBits } } - void set(size_t i) + void set(size_t i) nothrow in { assert(i < nbits); @@ -103,7 +101,7 @@ struct GCBits data[1 + (i >> BITS_SHIFT)] |= (BITS_1 << (i & BITS_MASK)); } - void clear(size_t i) + void clear(size_t i) nothrow in { assert(i < nbits); @@ -114,7 +112,7 @@ struct GCBits data[1 + (i >> BITS_SHIFT)] &= ~(BITS_1 << (i & BITS_MASK)); } - wordtype testClear(size_t i) + wordtype testClear(size_t i) nothrow { version (bitops) { @@ -145,7 +143,7 @@ struct GCBits } } - wordtype testSet(size_t i) + wordtype testSet(size_t i) nothrow { version (bitops) { @@ -176,12 +174,12 @@ struct GCBits } } - void zero() + void zero() nothrow { memset(data + 1, 0, nwords * wordtype.sizeof); } - void copy(GCBits *f) + void copy(GCBits *f) nothrow in { assert(nwords == f.nwords); @@ -191,7 +189,7 @@ struct GCBits memcpy(data + 1, f.data + 1, nwords * wordtype.sizeof); } - wordtype* base() + wordtype* base() nothrow in { assert(data); diff --git a/libphobos/libdruntime/gc/gc.d b/libphobos/libdruntime/gc/gc.d index c3ddf402d..2ce82d418 100644 --- a/libphobos/libdruntime/gc/gc.d +++ b/libphobos/libdruntime/gc/gc.d @@ -19,12 +19,15 @@ module gc.gc; //debug = PRINTF; // turn on printf's //debug = COLLECT_PRINTF; // turn on printf's +//debug = PRINTF_TO_FILE; // redirect printf's ouptut to file "gcx.log" //debug = LOGGING; // log allocations / frees //debug = MEMSTOMP; // stomp on memory //debug = SENTINEL; // add underrun/overrrun protection //debug = PTRCHECK; // more pointer checking //debug = PTRCHECK2; // thorough but slow pointer checking //debug = PROFILING; // measure performance of various steps. +//debug = INVARIANT; // enable invariants +//debug = CACHE_HITRATE; // enable hit rate measure /*************** Configuration *********************/ @@ -38,20 +41,47 @@ import gc.bits; import gc.stats; import gc.os; +import rt.util.container.treap; + import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc; -import core.stdc.string; +import core.stdc.string : memcpy, memset, memmove; import core.bitop; import core.sync.mutex; +import core.exception : onOutOfMemoryError, onInvalidMemoryOperationError; +import core.thread; static import core.memory; private alias BlkAttr = core.memory.GC.BlkAttr; +private alias BlkInfo = core.memory.GC.BlkInfo; version (GNU) import gcc.builtins; -debug (PRINTF) import core.stdc.stdio : printf; -debug (COLLECT_PRINTF) import core.stdc.stdio : printf; + debug (PRINTF_TO_FILE) import core.stdc.stdio : fprintf, fopen, fflush, FILE; +else debug (PRINTF) import core.stdc.stdio : printf; +else debug (CACHE_HITRATE) import core.stdc.stdio : printf; +else debug (COLLECT_PRINTF) import core.stdc.stdio : printf; debug private import core.stdc.stdio; -debug(PRINTF) void printFreeInfo(Pool* pool) +debug(PRINTF_TO_FILE) +{ + import core.time; + + private __gshared TickDuration gcStartTick; + private __gshared FILE* gcx_fh; + + private int printf(ARGS...)(const char* fmt, ARGS args) nothrow + { + if (gcStartTick == TickDuration.zero) + gcStartTick = TickDuration.currSystemTick; + if (!gcx_fh) + gcx_fh = fopen("gcx.log", "w"); + int len = fprintf(gcx_fh, "%10.6lf: ", (TickDuration.currSystemTick - gcStartTick).to!("seconds", double)); + len += fprintf(gcx_fh, fmt, args); + fflush(gcx_fh); + return len; + } +} + +debug(PRINTF) void printFreeInfo(Pool* pool) nothrow { uint nReallyFree; foreach(i; 0..pool.npages) { @@ -81,34 +111,13 @@ private // D is the depth of the heap graph. enum MAX_MARK_RECURSIONS = 64; } - struct BlkInfo - { - void* base; - size_t size; - uint attr; - } + private { - extern (C) void rt_finalize2(void* p, bool det, bool resetMemory); - - extern (C) void thread_suspendAll(); - extern (C) void thread_resumeAll(); - - // core.thread - enum IsMarked : int - { - no, - yes, - unknown, // memory is not managed by GC - } - alias IsMarked delegate(void*) IsMarkedDg; - extern (C) void thread_processGCMarks(scope IsMarkedDg isMarked); - - alias void delegate(void*, void*) scanFn; - extern (C) void thread_scanAll(scope scanFn fn); - - extern (C) void onOutOfMemoryError() @trusted /* pure dmd @@@BUG11461@@@ */ nothrow; - extern (C) void onInvalidMemoryOperationError() @trusted /* pure dmd @@@BUG11461@@@ */ nothrow; + // to allow compilation of this module without access to the rt package, + // make these functions available from rt.lifetime + extern (C) void rt_finalize2(void* p, bool det, bool resetMemory) nothrow; + extern (C) int rt_hasFinalizerInSegment(void* p, in void[] segment) nothrow; enum { @@ -133,7 +142,7 @@ debug (LOGGING) char* file; void* parent; - void print() + void print() nothrow { printf(" p = %p, size = %zd, parent = %p ", p, size, parent); if (file) @@ -151,14 +160,14 @@ debug (LOGGING) size_t allocdim; Log *data; - void Dtor() + void Dtor() nothrow { if (data) cstdlib.free(data); data = null; } - void reserve(size_t nentries) + void reserve(size_t nentries) nothrow { assert(dim <= allocdim); if (allocdim - dim < nentries) @@ -185,20 +194,20 @@ debug (LOGGING) } - void push(Log log) + void push(Log log) nothrow { reserve(1); data[dim++] = log; } - void remove(size_t i) + void remove(size_t i) nothrow { memmove(data + i, data + i + 1, (dim - i) * Log.sizeof); dim--; } - size_t find(void *p) + size_t find(void *p) nothrow { for (size_t i = 0; i < dim; i++) { @@ -209,7 +218,7 @@ debug (LOGGING) } - void copy(LogArray *from) + void copy(LogArray *from) nothrow { reserve(from.dim - dim); assert(from.dim <= allocdim); @@ -227,7 +236,14 @@ const uint GCVERSION = 1; // increment every time we change interface // to GC. // This just makes Mutex final to de-virtualize member function calls. -final class GCMutex : Mutex {} +final class GCMutex : Mutex +{ + // it doesn't make sense to use Exception throwing functions here, because allocating + // the exception will try to lock the mutex again, probably resulting in stack overflow + // instead we use the Error throwing functions + alias lock = lock_nothrow; + alias unlock = unlock_nothrow; +} class GC { @@ -299,14 +315,14 @@ class GC /** * */ - uint getAttr(void* p) + uint getAttr(void* p) nothrow { if (!p) { return 0; } - uint go() + uint go() nothrow { Pool* pool = gcx.findPool(p); uint oldb = 0; @@ -321,22 +337,23 @@ class GC } gcLock.lock(); - scope(exit) gcLock.unlock(); - return go(); + auto rc = go(); + gcLock.unlock(); + return rc; } /** * */ - uint setAttr(void* p, uint mask) + uint setAttr(void* p, uint mask) nothrow { if (!p) { return 0; } - uint go() + uint go() nothrow { Pool* pool = gcx.findPool(p); uint oldb = 0; @@ -352,22 +369,23 @@ class GC } gcLock.lock(); - scope(exit) gcLock.unlock(); - return go(); + auto rc = go(); + gcLock.unlock(); + return rc; } /** * */ - uint clrAttr(void* p, uint mask) + uint clrAttr(void* p, uint mask) nothrow { if (!p) { return 0; } - uint go() + uint go() nothrow { Pool* pool = gcx.findPool(p); uint oldb = 0; @@ -383,15 +401,16 @@ class GC } gcLock.lock(); - scope(exit) gcLock.unlock(); - return go(); + auto rc = go(); + gcLock.unlock(); + return rc; } /** * */ - void *malloc(size_t size, uint bits = 0, size_t *alloc_size = null) + void *malloc(size_t size, uint bits = 0, size_t *alloc_size = null, const TypeInfo ti = null) nothrow { if (!size) { @@ -409,8 +428,8 @@ class GC // when allocating. { gcLock.lock(); - scope(exit) gcLock.unlock(); - p = mallocNoSync(size, bits, alloc_size); + p = mallocNoSync(size, bits, *alloc_size, ti); + gcLock.unlock(); } if (!(bits & BlkAttr.NO_SCAN)) @@ -425,7 +444,7 @@ class GC // // // - private void *mallocNoSync(size_t size, uint bits = 0, size_t *alloc_size = null) + private void *mallocNoSync(size_t size, uint bits, ref size_t alloc_size, const TypeInfo ti = null) nothrow { assert(size != 0); @@ -445,8 +464,7 @@ class GC if (bin < B_PAGE) { - if(alloc_size) - *alloc_size = binsize[bin]; + alloc_size = binsize[bin]; int state = gcx.disabled ? 1 : 0; bool collected = false; @@ -496,9 +514,13 @@ class GC if (!p) onOutOfMemoryError(); } - size -= SENTINEL_EXTRA; - p = sentinel_add(p); - sentinel_init(p, size); + debug (SENTINEL) + { + size -= SENTINEL_EXTRA; + p = sentinel_add(p); + sentinel_init(p, size); + alloc_size = size; + } gcx.log_malloc(p, size); if (bits) @@ -512,7 +534,7 @@ class GC /** * */ - void *calloc(size_t size, uint bits = 0, size_t *alloc_size = null) + void *calloc(size_t size, uint bits = 0, size_t *alloc_size = null, const TypeInfo ti = null) nothrow { if (!size) { @@ -530,8 +552,8 @@ class GC // when allocating. { gcLock.lock(); - scope(exit) gcLock.unlock(); - p = mallocNoSync(size, bits, alloc_size); + p = mallocNoSync(size, bits, *alloc_size, ti); + gcLock.unlock(); } memset(p, 0, size); @@ -546,7 +568,7 @@ class GC /** * */ - void *realloc(void *p, size_t size, uint bits = 0, size_t *alloc_size = null) + void *realloc(void *p, size_t size, uint bits = 0, size_t *alloc_size = null, const TypeInfo ti = null) nothrow { size_t localAllocSize = void; auto oldp = p; @@ -557,8 +579,8 @@ class GC // when allocating. { gcLock.lock(); - scope(exit) gcLock.unlock(); - p = reallocNoSync(p, size, bits, alloc_size); + p = reallocNoSync(p, size, bits, *alloc_size, ti); + gcLock.unlock(); } if (p !is oldp && !(bits & BlkAttr.NO_SCAN)) @@ -573,7 +595,7 @@ class GC // // // - private void *reallocNoSync(void *p, size_t size, uint bits = 0, size_t *alloc_size = null) + private void *reallocNoSync(void *p, size_t size, uint bits, ref size_t alloc_size, const TypeInfo ti = null) nothrow { if (gcx.running) onInvalidMemoryOperationError(); @@ -583,19 +605,18 @@ class GC { freeNoSync(p); p = null; } - if(alloc_size) - *alloc_size = 0; + alloc_size = 0; } else if (!p) { - p = mallocNoSync(size, bits, alloc_size); + p = mallocNoSync(size, bits, alloc_size, ti); } else { void *p2; size_t psize; //debug(PRINTF) printf("GC::realloc(p = %p, size = %zu)\n", p, size); - version (SENTINEL) + debug (SENTINEL) { sentinel_Invariant(p); psize = *sentinel_size(p); @@ -620,7 +641,7 @@ class GC } } } - p2 = mallocNoSync(size, bits, alloc_size); + p2 = mallocNoSync(size, bits, alloc_size, ti); if (psize < size) size = psize; //debug(PRINTF) printf("\tcopying %d bytes\n",size); @@ -630,7 +651,8 @@ class GC } else { - psize = gcx.findSize(p); // find allocated size + auto pool = gcx.findPool(p); + psize = pool.getSize(p); // get allocated size if (psize >= PAGESIZE && size >= PAGESIZE) { auto psz = psize / PAGESIZE; @@ -638,8 +660,7 @@ class GC if (newsz == psz) return p; - auto pool = gcx.findPool(p); - auto pagenum = (p - pool.baseAddr) / PAGESIZE; + auto pagenum = pool.pagenumOf(p); if (newsz < psz) { // Shrink in place @@ -657,6 +678,8 @@ class GC pool.freepages -= (newsz - psz); debug(PRINTF) printFreeInfo(pool); } + else + goto Lfallthrough; // does not fit into current pool pool.updateOffsets(pagenum); if (bits) { @@ -664,9 +687,7 @@ class GC gcx.clrBits(pool, biti, ~BlkAttr.NONE); gcx.setBits(pool, biti, bits); } - if(alloc_size) - *alloc_size = newsz * PAGESIZE; - gcx.updateCaches(p, newsz * PAGESIZE); + alloc_size = newsz * PAGESIZE; return p; Lfallthrough: {} @@ -674,34 +695,29 @@ class GC if (psize < size || // if new size is bigger psize > size * 2) // or less than half { - if (psize) + if (psize && pool) { - Pool *pool = gcx.findPool(p); + auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy; - if (pool) + if (bits) { - auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy; - - if (bits) - { - gcx.clrBits(pool, biti, ~BlkAttr.NONE); - gcx.setBits(pool, biti, bits); - } - else - { - bits = gcx.getBits(pool, biti); - } + gcx.clrBits(pool, biti, ~BlkAttr.NONE); + gcx.setBits(pool, biti, bits); + } + else + { + bits = gcx.getBits(pool, biti); } } - p2 = mallocNoSync(size, bits, alloc_size); + p2 = mallocNoSync(size, bits, alloc_size, ti); if (psize < size) size = psize; //debug(PRINTF) printf("\tcopying %d bytes\n",size); memcpy(p2, p, size); p = p2; } - else if(alloc_size) - *alloc_size = psize; + else + alloc_size = psize; } } return p; @@ -717,18 +733,19 @@ class GC * 0 if could not extend p, * total size of entire memory block if successful. */ - size_t extend(void* p, size_t minsize, size_t maxsize) + size_t extend(void* p, size_t minsize, size_t maxsize, const TypeInfo ti = null) nothrow { gcLock.lock(); - scope(exit) gcLock.unlock(); - return extendNoSync(p, minsize, maxsize); + auto rc = extendNoSync(p, minsize, maxsize, ti); + gcLock.unlock(); + return rc; } // // // - private size_t extendNoSync(void* p, size_t minsize, size_t maxsize) + private size_t extendNoSync(void* p, size_t minsize, size_t maxsize, const TypeInfo ti = null) nothrow in { assert(minsize <= maxsize); @@ -739,48 +756,52 @@ class GC onInvalidMemoryOperationError(); //debug(PRINTF) printf("GC::extend(p = %p, minsize = %zu, maxsize = %zu)\n", p, minsize, maxsize); - version (SENTINEL) + debug (SENTINEL) { return 0; } - auto psize = gcx.findSize(p); // find allocated size - if (psize < PAGESIZE) - return 0; // cannot extend buckets + else + { + auto pool = gcx.findPool(p); + if (!pool) + return 0; + auto psize = pool.getSize(p); // get allocated size + if (psize < PAGESIZE) + return 0; // cannot extend buckets - auto psz = psize / PAGESIZE; - auto minsz = (minsize + PAGESIZE - 1) / PAGESIZE; - auto maxsz = (maxsize + PAGESIZE - 1) / PAGESIZE; + auto psz = psize / PAGESIZE; + auto minsz = (minsize + PAGESIZE - 1) / PAGESIZE; + auto maxsz = (maxsize + PAGESIZE - 1) / PAGESIZE; - auto pool = gcx.findPool(p); - auto pagenum = (p - pool.baseAddr) / PAGESIZE; + auto pagenum = pool.pagenumOf(p); - size_t sz; - for (sz = 0; sz < maxsz; sz++) - { - auto i = pagenum + psz + sz; - if (i == pool.npages) - break; - if (pool.pagetable[i] != B_FREE) - { if (sz < minsz) - return 0; - break; + size_t sz; + for (sz = 0; sz < maxsz; sz++) + { + auto i = pagenum + psz + sz; + if (i == pool.npages) + break; + if (pool.pagetable[i] != B_FREE) + { if (sz < minsz) + return 0; + break; + } } + if (sz < minsz) + return 0; + debug (MEMSTOMP) memset(pool.baseAddr + (pagenum + psz) * PAGESIZE, 0xF0, sz * PAGESIZE); + memset(pool.pagetable + pagenum + psz, B_PAGEPLUS, sz); + pool.updateOffsets(pagenum); + pool.freepages -= sz; + return (psz + sz) * PAGESIZE; } - if (sz < minsz) - return 0; - debug (MEMSTOMP) memset(p + psize, 0xF0, (psz + sz) * PAGESIZE - psize); - memset(pool.pagetable + pagenum + psz, B_PAGEPLUS, sz); - pool.updateOffsets(pagenum); - pool.freepages -= sz; - gcx.updateCaches(p, (psz + sz) * PAGESIZE); - return (psz + sz) * PAGESIZE; } /** * */ - size_t reserve(size_t size) + size_t reserve(size_t size) nothrow { if (!size) { @@ -788,15 +809,16 @@ class GC } gcLock.lock(); - scope(exit) gcLock.unlock(); - return reserveNoSync(size); + auto rc = reserveNoSync(size); + gcLock.unlock(); + return rc; } // // // - private size_t reserveNoSync(size_t size) + private size_t reserveNoSync(size_t size) nothrow { assert(size != 0); assert(gcx); @@ -811,7 +833,7 @@ class GC /** * */ - void free(void *p) + void free(void *p) nothrow { if (!p) { @@ -819,15 +841,15 @@ class GC } gcLock.lock(); - scope(exit) gcLock.unlock(); - return freeNoSync(p); + freeNoSync(p); + gcLock.unlock(); } // // // - private void freeNoSync(void *p) + private void freeNoSync(void *p) nothrow { debug(PRINTF) printf("Freeing %p\n", cast(size_t) p); assert (p); @@ -846,7 +868,7 @@ class GC return; // ignore sentinel_Invariant(p); p = sentinel_sub(p); - pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE; + pagenum = pool.pagenumOf(p); debug(PRINTF) printf("pool base = %p, PAGENUM = %d of %d, bin = %d\n", pool.baseAddr, pagenum, pool.npages, pool.pagetable[pagenum]); debug(PRINTF) if(pool.isLargeObject) printf("Block size = %d\n", pool.bPageOffsets[pagenum]); @@ -873,6 +895,7 @@ class GC list.pool = pool; gcx.bucket[bin] = list; } + gcx.log_free(sentinel_add(p)); } @@ -881,7 +904,7 @@ class GC * Determine the base address of the block containing p. If p is not a gc * allocated pointer, return null. */ - void* addrOf(void *p) + void* addrOf(void *p) nothrow { if (!p) { @@ -889,15 +912,16 @@ class GC } gcLock.lock(); - scope(exit) gcLock.unlock(); - return addrOfNoSync(p); + auto rc = addrOfNoSync(p); + gcLock.unlock(); + return rc; } // // // - void* addrOfNoSync(void *p) + void* addrOfNoSync(void *p) nothrow { if (!p) { @@ -912,7 +936,7 @@ class GC * Determine the allocated size of pointer p. If p is an interior pointer * or not a gc allocated pointer, return 0. */ - size_t sizeOf(void *p) + size_t sizeOf(void *p) nothrow { if (!p) { @@ -920,19 +944,20 @@ class GC } gcLock.lock(); - scope(exit) gcLock.unlock(); - return sizeOfNoSync(p); + auto rc = sizeOfNoSync(p); + gcLock.unlock(); + return rc; } // // // - private size_t sizeOfNoSync(void *p) + private size_t sizeOfNoSync(void *p) nothrow { assert (p); - version (SENTINEL) + debug (SENTINEL) { p = sentinel_sub(p); size_t size = gcx.findSize(p); @@ -964,7 +989,7 @@ class GC * Determine the base address of the block containing p. If p is not a gc * allocated pointer, return null. */ - BlkInfo query(void *p) + BlkInfo query(void *p) nothrow { if (!p) { @@ -973,15 +998,16 @@ class GC } gcLock.lock(); - scope(exit) gcLock.unlock(); - return queryNoSync(p); + auto rc = queryNoSync(p); + gcLock.unlock(); + return rc; } // // // - BlkInfo queryNoSync(void *p) + BlkInfo queryNoSync(void *p) nothrow { assert(p); @@ -995,7 +1021,7 @@ class GC * 2) points to the start of an allocated piece of memory * 3) is not on a free list */ - void check(void *p) + void check(void *p) nothrow { if (!p) { @@ -1003,15 +1029,15 @@ class GC } gcLock.lock(); - scope(exit) gcLock.unlock(); checkNoSync(p); + gcLock.unlock(); } // // // - private void checkNoSync(void *p) + private void checkNoSync(void *p) nothrow { assert(p); @@ -1026,7 +1052,7 @@ class GC p = sentinel_sub(p); pool = gcx.findPool(p); assert(pool); - pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE; + pagenum = pool.pagenumOf(p); bin = cast(Bins)pool.pagetable[pagenum]; assert(bin <= B_PAGE); size = binsize[bin]; @@ -1052,7 +1078,7 @@ class GC /** * add p to list of roots */ - void addRoot(void *p) + void addRoot(void *p) nothrow { if (!p) { @@ -1060,15 +1086,15 @@ class GC } gcLock.lock(); - scope(exit) gcLock.unlock(); gcx.addRoot(p); + gcLock.unlock(); } /** * remove p from list of roots */ - void removeRoot(void *p) + void removeRoot(void *p) nothrow { if (!p) { @@ -1076,26 +1102,31 @@ class GC } gcLock.lock(); - scope(exit) gcLock.unlock(); gcx.removeRoot(p); + gcLock.unlock(); } /** * */ - @property int delegate(int delegate(ref void*)) rootIter() + @property auto rootIter() { - gcLock.lock(); - scope(exit) gcLock.unlock(); - return &gcx.rootIter; + auto iter(scope int delegate(ref Root) nothrow dg) + { + gcLock.lock(); + auto res = gcx.roots.opApply(dg); + gcLock.unlock(); + return res; + } + return &iter; } /** * add range to scan for roots */ - void addRange(void *p, size_t sz) + void addRange(void *p, size_t sz, const TypeInfo ti = null) nothrow { if (!p || !sz) { @@ -1105,8 +1136,8 @@ class GC //debug(PRINTF) printf("+GC.addRange(p = %p, sz = 0x%zx), p + sz = %p\n", p, sz, p + sz); gcLock.lock(); - scope(exit) gcLock.unlock(); - gcx.addRange(p, p + sz); + gcx.addRange(p, p + sz, ti); + gcLock.unlock(); //debug(PRINTF) printf("-GC.addRange()\n"); } @@ -1115,7 +1146,7 @@ class GC /** * remove range */ - void removeRange(void *p) + void removeRange(void *p) nothrow { if (!p) { @@ -1123,19 +1154,33 @@ class GC } gcLock.lock(); - scope(exit) gcLock.unlock(); gcx.removeRange(p); + gcLock.unlock(); } + /** + * run finalizers + */ + void runFinalizers(in void[] segment) nothrow + { + gcLock.lock(); + gcx.runFinalizers(segment); + gcLock.unlock(); + } /** * */ - @property int delegate(int delegate(ref Range)) rangeIter() + @property auto rangeIter() { - gcLock.lock(); - scope(exit) gcLock.unlock(); - return &gcx.rangeIter; + auto iter(scope int delegate(ref Range) nothrow dg) + { + gcLock.lock(); + auto res = gcx.ranges.opApply(dg); + gcLock.unlock(); + return res; + } + return &iter; } @@ -1143,7 +1188,7 @@ class GC * Do full garbage collection. * Return number of pages free'd. */ - size_t fullCollect() + size_t fullCollect() nothrow { debug(PRINTF) printf("GC.fullCollect()\n"); size_t result; @@ -1152,8 +1197,8 @@ class GC // when collecting. { gcLock.lock(); - scope(exit) gcLock.unlock(); result = gcx.fullcollect(); + gcLock.unlock(); } version (none) @@ -1173,16 +1218,16 @@ class GC /** * do full garbage collection ignoring roots */ - void fullCollectNoStack() + void fullCollectNoStack() nothrow { // Since a finalizer could launch a new thread, we always need to lock // when collecting. { gcLock.lock(); - scope(exit) gcLock.unlock(); gcx.noStack++; gcx.fullcollect(); gcx.noStack--; + gcLock.unlock(); } } @@ -1190,11 +1235,11 @@ class GC /** * minimize free space usage */ - void minimize() + void minimize() nothrow { gcLock.lock(); - scope(exit) gcLock.unlock(); gcx.minimize(); + gcLock.unlock(); } @@ -1202,18 +1247,18 @@ class GC * Retrieve statistics about garbage collection. * Useful for debugging and tuning. */ - void getStats(out GCStats stats) + void getStats(out GCStats stats) nothrow { gcLock.lock(); - scope(exit) gcLock.unlock(); getStatsNoSync(stats); + gcLock.unlock(); } // // // - private void getStatsNoSync(out GCStats stats) + private void getStatsNoSync(out GCStats stats) nothrow { size_t psize = 0; size_t usize = 0; @@ -1299,6 +1344,13 @@ struct Range { void *pbot; void *ptop; + alias pbot this; // only consider pbot for relative ordering (opCmp) +} + +struct Root +{ + void *proot; + alias proot this; } @@ -1310,19 +1362,18 @@ immutable size_t notbinsize[B_MAX] = [ ~(16-1),~(32-1),~(64-1),~(128-1),~(256-1) struct Gcx { - void *cached_size_key; - size_t cached_size_val; - - void *cached_info_key; - BlkInfo cached_info_val; - - size_t nroots; - size_t rootdim; - void **roots; - - size_t nranges; - size_t rangedim; - Range *ranges; + static if (USE_CACHE){ + byte *cached_pool_topAddr; + byte *cached_pool_baseAddr; + Pool *cached_pool; + debug (CACHE_HITRATE) + { + ulong cached_pool_queries; + ulong cached_pool_hits; + } + } + Treap!Root roots; + Treap!Range ranges; uint noStack; // !=0 means don't scan stack uint log; // turn on logging @@ -1345,6 +1396,8 @@ struct Gcx (cast(byte*)&this)[0 .. Gcx.sizeof] = 0; log_init(); + roots.initialize(); + ranges.initialize(); //printf("gcx = %p, self = %x\n", &this, self); inited = 1; } @@ -1367,6 +1420,11 @@ struct Gcx / CLOCKS_PER_SEC); } + debug(CACHE_HITRATE) + { + printf("\tGcx.Pool Cache hits: %llu\tqueries: %llu\n",cached_pool_hits,cached_pool_queries); + } + inited = 0; for (size_t i = 0; i < npools; i++) @@ -1381,17 +1439,14 @@ struct Gcx pooltable = null; } - if (roots) - cstdlib.free(roots); - - if (ranges) - cstdlib.free(ranges); + roots.removeAll(); + ranges.removeAll(); } void Invariant() const { } - + debug(INVARIANT) invariant() { if (inited) @@ -1416,23 +1471,11 @@ struct Gcx } } - if (roots) - { - assert(rootdim != 0); - assert(nroots <= rootdim); - } - - if (ranges) + foreach (range; ranges) { - assert(rangedim != 0); - assert(nranges <= rangedim); - - for (size_t i = 0; i < nranges; i++) - { - assert(ranges[i].pbot); - assert(ranges[i].ptop); - assert(ranges[i].pbot <= ranges[i].ptop); - } + assert(range.pbot); + assert(range.ptop); + assert(range.pbot <= range.ptop); } for (size_t i = 0; i < B_PAGE; i++) @@ -1448,108 +1491,42 @@ struct Gcx /** * */ - void addRoot(void *p) - { - if (nroots == rootdim) - { - size_t newdim = rootdim * 2 + 16; - void** newroots; - - newroots = cast(void**)cstdlib.malloc(newdim * newroots[0].sizeof); - if (!newroots) - onOutOfMemoryError(); - if (roots) - { memcpy(newroots, roots, nroots * newroots[0].sizeof); - cstdlib.free(roots); - } - roots = newroots; - rootdim = newdim; - } - roots[nroots] = p; - nroots++; - } - - - /** - * - */ - void removeRoot(void *p) + void addRoot(void *p) nothrow { - for (size_t i = nroots; i--;) - { - if (roots[i] == p) - { - nroots--; - memmove(roots + i, roots + i + 1, (nroots - i) * roots[0].sizeof); - return; - } - } - assert(0); + roots.insert(Root(p)); } /** * */ - int rootIter(int delegate(ref void*) dg) + void removeRoot(void *p) nothrow { - int result = 0; - for (size_t i = 0; i < nroots; ++i) - { - result = dg(roots[i]); - if (result) - break; - } - return result; + roots.remove(Root(p)); } /** * */ - void addRange(void *pbot, void *ptop) + void addRange(void *pbot, void *ptop, const TypeInfo ti) nothrow { //debug(PRINTF) printf("Thread %x ", pthread_self()); - debug(PRINTF) printf("%p.Gcx::addRange(%p, %p), nranges = %d\n", &this, pbot, ptop, nranges); - if (nranges == rangedim) - { - size_t newdim = rangedim * 2 + 16; - Range *newranges; - - newranges = cast(Range*)cstdlib.malloc(newdim * newranges[0].sizeof); - if (!newranges) - onOutOfMemoryError(); - if (ranges) - { memcpy(newranges, ranges, nranges * newranges[0].sizeof); - cstdlib.free(ranges); - } - ranges = newranges; - rangedim = newdim; - } - ranges[nranges].pbot = pbot; - ranges[nranges].ptop = ptop; - nranges++; + debug(PRINTF) printf("%p.Gcx::addRange(%p, %p)\n", &this, pbot, ptop); + ranges.insert(Range(pbot, ptop)); } /** * */ - void removeRange(void *pbot) + void removeRange(void *pbot) nothrow { //debug(PRINTF) printf("Thread %x ", pthread_self()); - debug(PRINTF) printf("Gcx.removeRange(%p), nranges = %d\n", pbot, nranges); - for (size_t i = nranges; i--;) - { - if (ranges[i].pbot == pbot) - { - nranges--; - memmove(ranges + i, ranges + i + 1, (nranges - i) * ranges[0].sizeof); - return; - } - } - debug(PRINTF) printf("Wrong thread\n"); + debug(PRINTF) printf("Gcx.removeRange(%p)\n", pbot); + ranges.remove(Range(pbot, pbot)); // only pbot is used, see Range.opCmp + // debug(PRINTF) printf("Wrong thread\n"); // This is a fatal error, but ignore it. // The problem is that we can get a Close() call on a thread // other than the one the range was allocated on. @@ -1560,16 +1537,94 @@ struct Gcx /** * */ - int rangeIter(int delegate(ref Range) dg) + void runFinalizers(in void[] segment) nothrow { - int result = 0; - for (size_t i = 0; i < nranges; ++i) + foreach (pool; pooltable[0 .. npools]) { - result = dg(ranges[i]); - if (result) - break; + if (!pool.finals.nbits) continue; + + if (pool.isLargeObject) + { + foreach (ref pn; 0 .. pool.npages) + { + Bins bin = cast(Bins)pool.pagetable[pn]; + if (bin > B_PAGE) continue; + size_t biti = pn; + + auto p = pool.baseAddr + pn * PAGESIZE; + + if (!pool.finals.test(biti) || + !rt_hasFinalizerInSegment(sentinel_add(p), segment)) + continue; + + rt_finalize2(sentinel_add(p), false, false); + clrBits(pool, biti, ~BlkAttr.NONE); + + if (pn < pool.searchStart) pool.searchStart = pn; + + debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p); + log_free(sentinel_add(p)); + + size_t n = 1; + for (; pn + n < pool.npages; ++n) + if (pool.pagetable[pn + n] != B_PAGEPLUS) break; + debug (MEMSTOMP) memset(pool.baseAddr + pn * PAGESIZE, 0xF3, n * PAGESIZE); + pool.freePages(pn, n); + } + } + else + { + foreach (ref pn; 0 .. pool.npages) + { + Bins bin = cast(Bins)pool.pagetable[pn]; + + if (bin >= B_PAGE) continue; + + immutable size = binsize[bin]; + auto p = pool.baseAddr + pn * PAGESIZE; + const ptop = p + PAGESIZE; + auto biti = pn * (PAGESIZE/16); + immutable bitstride = size / 16; + + GCBits.wordtype toClear; + size_t clearStart = (biti >> GCBits.BITS_SHIFT) + 1; + size_t clearIndex; + + for (; p < ptop; p += size, biti += bitstride, clearIndex += bitstride) + { + if (clearIndex > GCBits.BITS_PER_WORD - 1) + { + if (toClear) + { + Gcx.clrBitsSmallSweep(pool, clearStart, toClear); + toClear = 0; + } + + clearStart = (biti >> GCBits.BITS_SHIFT) + 1; + clearIndex = biti & GCBits.BITS_MASK; + } + + if (!pool.finals.test(biti) || + !rt_hasFinalizerInSegment(sentinel_add(p), segment)) + continue; + + rt_finalize2(sentinel_add(p), false, false); + toClear |= GCBits.BITS_1 << clearIndex; + + debug(COLLECT_PRINTF) printf("\tcollecting %p\n", p); + log_free(sentinel_add(p)); + + debug (MEMSTOMP) memset(p, 0xF3, size); + pool.freebits.set(biti); + } + + if (toClear) + { + Gcx.clrBitsSmallSweep(pool, clearStart, toClear); + } + } + } } - return result; } @@ -1578,8 +1633,18 @@ struct Gcx * Return null if not in a Pool. * Assume pooltable[] is sorted. */ - Pool *findPool(void *p) + Pool *findPool(bool bypassCache = !USE_CACHE)(void *p) nothrow { + static if (!bypassCache && USE_CACHE) + { + debug (CACHE_HITRATE) cached_pool_queries++; + if (p < cached_pool_topAddr + && p >= cached_pool_baseAddr) + { + debug (CACHE_HITRATE) cached_pool_hits++; + return cached_pool; + } + } if (p >= minAddr && p < maxAddr) { if (npools <= 1) @@ -1601,7 +1666,15 @@ struct Gcx else if (p >= pool.topAddr) low = mid + 1; else + { + static if (!bypassCache && USE_CACHE) + { + cached_pool_topAddr = pool.topAddr; + cached_pool_baseAddr = pool.baseAddr; + cached_pool = pool; + } return pool; + } } } return null; @@ -1612,7 +1685,7 @@ struct Gcx * Find base address of block containing pointer p. * Returns null if not a gc'd pointer */ - void* findBase(void *p) + void* findBase(void *p) nothrow { Pool *pool; @@ -1651,45 +1724,22 @@ struct Gcx * Find size of pointer p. * Returns 0 if not a gc'd pointer */ - size_t findSize(void *p) + size_t findSize(void *p) nothrow { - Pool* pool; - size_t size = 0; - - if (USE_CACHE && p == cached_size_key) - return cached_size_val; - - pool = findPool(p); + Pool* pool = findPool(p); if (pool) - { - size_t pagenum; - Bins bin; - - pagenum = cast(size_t)(p - pool.baseAddr) / PAGESIZE; - bin = cast(Bins)pool.pagetable[pagenum]; - size = binsize[bin]; - if (bin == B_PAGE) - { - size = pool.bPageOffsets[pagenum] * PAGESIZE; - } - cached_size_key = p; - cached_size_val = size; - } - return size; + return pool.getSize(p); + return 0; } - /** * */ - BlkInfo getInfo(void* p) + BlkInfo getInfo(void* p) nothrow { Pool* pool; BlkInfo info; - if (USE_CACHE && p == cached_info_key) - return cached_info_val; - pool = findPool(p); if (pool) { @@ -1734,25 +1784,23 @@ struct Gcx // are the bits for the pointer, which may be garbage offset = cast(size_t)(info.base - pool.baseAddr); info.attr = getBits(pool, cast(size_t)(offset >> pool.shiftBy)); - - cached_info_key = p; - cached_info_val = info; } return info; } - void updateCaches(void*p, size_t size) + void resetPoolCache() nothrow { - if (USE_CACHE && p == cached_size_key) - cached_size_val = size; - if (p == cached_info_key) - cached_info_val.size = size; + static if (USE_CACHE){ + cached_pool_topAddr = cached_pool_topAddr.init; + cached_pool_baseAddr = cached_pool_baseAddr.init; + cached_pool = cached_pool.init; + } } /** * Compute bin for size. */ - static Bins findBin(size_t size) + static Bins findBin(size_t size) nothrow { static const byte[2049] binTable = ctfeBins(); @@ -1761,7 +1809,7 @@ struct Gcx B_PAGE; } - static Bins findBinImpl(size_t size) + static Bins findBinImpl(size_t size) nothrow { Bins bin; if (size <= 256) @@ -1806,7 +1854,7 @@ struct Gcx /** * Computes the bin table using CTFE. */ - static byte[2049] ctfeBins() + static byte[2049] ctfeBins() nothrow { byte[2049] ret; for(size_t i = 0; i < 2049; i++) @@ -1824,7 +1872,7 @@ struct Gcx * Mark all memory in the pool as B_FREE. * Return the actual number of bytes reserved or 0 on error. */ - size_t reserve(size_t size) + size_t reserve(size_t size) nothrow { size_t npages = (size + PAGESIZE - 1) / PAGESIZE; @@ -1840,11 +1888,11 @@ struct Gcx /** * Minimizes physical memory usage by returning free pools to the OS. */ - void minimize() + void minimize() nothrow { debug(PRINTF) printf("Minimizing.\n"); - static bool isUsed(Pool *pool) + static bool isUsed(Pool *pool) nothrow { return pool.freepages < pool.npages; } @@ -1891,6 +1939,10 @@ struct Gcx minAddr = maxAddr = null; } + static if (USE_CACHE){ + resetPoolCache(); + } + debug(PRINTF) printf("Done minimizing.\n"); } @@ -2025,7 +2077,7 @@ struct Gcx * Allocate a chunk of memory that is larger than a page. * Return null if out of memory. */ - void *bigAlloc(size_t size, Pool **poolPtr, size_t *alloc_size = null) + void *bigAlloc(size_t size, Pool **poolPtr, ref size_t alloc_size) nothrow { debug(PRINTF) printf("In bigAlloc. Size: %d\n", size); @@ -2111,8 +2163,7 @@ struct Gcx p = pool.baseAddr + pn * PAGESIZE; debug(PRINTF) printf("Got large alloc: %p, pt = %d, np = %d\n", p, pool.pagetable[pn], npages); debug (MEMSTOMP) memset(p, 0xF1, size); - if(alloc_size) - *alloc_size = npages * PAGESIZE; + alloc_size = npages * PAGESIZE; //debug(PRINTF) printf("\tp = %p\n", p); *poolPtr = pool; @@ -2128,7 +2179,7 @@ struct Gcx * Sort it into pooltable[]. * Return null if failed. */ - Pool *newPool(size_t npages, bool isLargeObject) + Pool *newPool(size_t npages, bool isLargeObject) nothrow { Pool* pool; Pool** newpooltable; @@ -2204,7 +2255,7 @@ struct Gcx * Returns: * 0 failed */ - int allocPage(Bins bin) + int allocPage(Bins bin) nothrow { Pool* pool; size_t n; @@ -2245,14 +2296,15 @@ struct Gcx /** * Mark overload for initial mark() call. */ - void mark(void *pbot, void *ptop) { + void mark(void *pbot, void *ptop) nothrow + { mark(pbot, ptop, MAX_MARK_RECURSIONS); } /** * Search a range of memory values and mark any pointers into the GC pool. */ - void mark(void *pbot, void *ptop, int nRecurse) + void mark(void *pbot, void *ptop, int nRecurse) nothrow { //import core.stdc.stdio;printf("nRecurse = %d\n", nRecurse); void **p1 = cast(void **)pbot; @@ -2271,7 +2323,7 @@ struct Gcx if ((cast(size_t)p & ~cast(size_t)(PAGESIZE-1)) == pcache) continue; - auto pool = findPool(p); + auto pool = findPool!true(p); if (pool) { size_t offset = cast(size_t)(p - pool.baseAddr); @@ -2368,7 +2420,7 @@ struct Gcx /** * Return number of full pages free'd. */ - size_t fullcollect() + size_t fullcollect() nothrow { size_t n; Pool* pool; @@ -2388,11 +2440,6 @@ struct Gcx thread_suspendAll(); - cached_size_key = cached_size_key.init; - cached_size_val = cached_size_val.init; - cached_info_key = cached_info_key.init; - cached_info_val = cached_info_val.init; - anychanges = 0; for (n = 0; n < npools; n++) { @@ -2443,15 +2490,18 @@ struct Gcx // Scan roots[] debug(COLLECT_PRINTF) printf("\tscan roots[]\n"); - mark(roots, roots + nroots); + foreach (root; roots) + { + mark(cast(void*)&root.proot, cast(void*)(&root.proot + 1)); + } // Scan ranges[] debug(COLLECT_PRINTF) printf("\tscan ranges[]\n"); //log++; - for (n = 0; n < nranges; n++) + foreach (range; ranges) { - debug(COLLECT_PRINTF) printf("\t\t%p .. %p\n", ranges[n].pbot, ranges[n].ptop); - mark(ranges[n].pbot, ranges[n].ptop); + debug(COLLECT_PRINTF) printf("\t\t%p .. %p\n", range.pbot, range.ptop); + mark(range.pbot, range.ptop); } //log--; @@ -2721,11 +2771,11 @@ struct Gcx * Warning! This should only be called while the world is stopped inside * the fullcollect function. */ - IsMarked isMarked(void *addr) + int isMarked(void *addr) nothrow { // first, we find the Pool this block is in, then check to see if the // mark bit is clear. - auto pool = findPool(addr); + auto pool = findPool!true(addr); if(pool) { auto offset = cast(size_t)(addr - pool.baseAddr); @@ -2755,7 +2805,7 @@ struct Gcx /** * */ - uint getBits(Pool* pool, size_t biti) + uint getBits(Pool* pool, size_t biti) nothrow in { assert(pool); @@ -2783,7 +2833,7 @@ struct Gcx /** * */ - void setBits(Pool* pool, size_t biti, uint mask) + void setBits(Pool* pool, size_t biti, uint mask) nothrow in { assert(pool); @@ -2829,7 +2879,7 @@ struct Gcx /** * */ - void clrBits(Pool* pool, size_t biti, uint mask) + void clrBits(Pool* pool, size_t biti, uint mask) nothrow in { assert(pool); @@ -2852,7 +2902,7 @@ struct Gcx pool.nointerior.data[dataIndex] &= keep; } - void clrBitsSmallSweep(Pool* pool, size_t dataIndex, GCBits.wordtype toClear) + void clrBitsSmallSweep(Pool* pool, size_t dataIndex, GCBits.wordtype toClear) nothrow in { assert(pool); @@ -2892,7 +2942,7 @@ struct Gcx } - void log_malloc(void *p, size_t size) + void log_malloc(void *p, size_t size) nothrow { //debug(PRINTF) printf("+log_malloc(p = %p, size = %zd)\n", p, size); Log log; @@ -2911,7 +2961,7 @@ struct Gcx } - void log_free(void *p) + void log_free(void *p) nothrow { //debug(PRINTF) printf("+log_free(%p)\n", p); auto i = current.find(p); @@ -2925,7 +2975,7 @@ struct Gcx } - void log_collect() + void log_collect() nothrow { //debug(PRINTF) printf("+log_collect()\n"); // Print everything in current that is not in prev @@ -2945,7 +2995,7 @@ struct Gcx for (size_t i = 0; i < current.dim; i++) { void* p = current.data[i].p; - if (!findPool(current.data[i].parent)) + if (!findPool!true(current.data[i].parent)) { auto j = prev.find(current.data[i].p); debug(PRINTF) printf(j == OPFAIL ? "N" : " "); @@ -2960,7 +3010,7 @@ struct Gcx } - void log_parent(void *p, void *parent) + void log_parent(void *p, void *parent) nothrow { //debug(PRINTF) printf("+log_parent()\n"); auto i = current.find(p); @@ -2968,7 +3018,7 @@ struct Gcx { debug(PRINTF) printf("parent'ing unallocated memory %p, parent = %p\n", p, parent); Pool *pool; - pool = findPool(p); + pool = findPool!true(p); assert(pool); size_t offset = cast(size_t)(p - pool.baseAddr); size_t biti; @@ -2987,11 +3037,11 @@ struct Gcx } else { - void log_init() { } - void log_malloc(void *p, size_t size) { } - void log_free(void *p) { } - void log_collect() { } - void log_parent(void *p, void *parent) { } + void log_init() nothrow { } + void log_malloc(void *p, size_t size) nothrow { } + void log_free(void *p) nothrow { } + void log_collect() nothrow { } + void log_parent(void *p, void *parent) nothrow { } } } @@ -3031,7 +3081,7 @@ struct Pool // are occupied, we can bypass them in O(1). size_t searchStart; - void initialize(size_t npages, bool isLargeObject) + void initialize(size_t npages, bool isLargeObject) nothrow { this.isLargeObject = isLargeObject; size_t poolsize; @@ -3088,7 +3138,7 @@ struct Pool } - void Dtor() + void Dtor() nothrow { if (baseAddr) { @@ -3132,6 +3182,7 @@ struct Pool void Invariant() const {} + debug(INVARIANT) invariant() { //mark.Invariant(); @@ -3160,7 +3211,7 @@ struct Pool } // The divisor used for determining bit indices. - @property private size_t divisor() + @property private size_t divisor() nothrow { // NOTE: Since this is called by initialize it must be private or // invariant() will be called and fail. @@ -3168,12 +3219,12 @@ struct Pool } // Bit shift for fast division by divisor. - @property uint shiftBy() + @property uint shiftBy() nothrow { return isLargeObject ? 12 : 4; } - void updateOffsets(size_t fromWhere) + void updateOffsets(size_t fromWhere) nothrow { assert(pagetable[fromWhere] == B_PAGE); size_t pn = fromWhere + 1; @@ -3191,7 +3242,7 @@ struct Pool * Allocate n pages from Pool. * Returns OPFAIL on failure. */ - size_t allocPages(size_t n) + size_t allocPages(size_t n) nothrow { if(freepages < n) return OPFAIL; size_t i; @@ -3236,7 +3287,7 @@ struct Pool /** * Free npages pages starting with pagenum. */ - void freePages(size_t pagenum, size_t npages) + void freePages(size_t pagenum, size_t npages) nothrow { //memset(&pagetable[pagenum], B_FREE, npages); if(pagenum < searchStart) searchStart = pagenum; @@ -3252,11 +3303,45 @@ struct Pool } } + /** + * Given a pointer p in the p, return the pagenum. + */ + size_t pagenumOf(void *p) const nothrow + in + { + assert(p >= baseAddr); + assert(p < topAddr); + } + body + { + return cast(size_t)(p - baseAddr) / PAGESIZE; + } + + /** + * Get size of pointer p in pool. + */ + size_t getSize(void *p) const nothrow + in + { + assert(p >= baseAddr); + assert(p < topAddr); + } + body + { + size_t pagenum = pagenumOf(p); + Bins bin = cast(Bins)pagetable[pagenum]; + size_t size = binsize[bin]; + if (bin == B_PAGE) + { + size = bPageOffsets[pagenum] * PAGESIZE; + } + return size; + } /** * Used for sorting pooltable[] */ - int opCmp(const Pool *p2) const + int opCmp(const Pool *p2) const nothrow { if (baseAddr < p2.baseAddr) return -1; @@ -3269,19 +3354,19 @@ struct Pool /* ============================ SENTINEL =============================== */ -version (SENTINEL) +debug (SENTINEL) { const size_t SENTINEL_PRE = cast(size_t) 0xF4F4F4F4F4F4F4F4UL; // 32 or 64 bits const ubyte SENTINEL_POST = 0xF5; // 8 bits const uint SENTINEL_EXTRA = 2 * size_t.sizeof + 1; - size_t* sentinel_size(void *p) { return &(cast(size_t *)p)[-2]; } - size_t* sentinel_pre(void *p) { return &(cast(size_t *)p)[-1]; } - ubyte* sentinel_post(void *p) { return &(cast(ubyte *)p)[*sentinel_size(p)]; } + inout(size_t*) sentinel_size(inout void *p) nothrow { return &(cast(inout size_t *)p)[-2]; } + inout(size_t*) sentinel_pre(inout void *p) nothrow { return &(cast(inout size_t *)p)[-1]; } + inout(ubyte*) sentinel_post(inout void *p) nothrow { return &(cast(inout ubyte *)p)[*sentinel_size(p)]; } - void sentinel_init(void *p, size_t size) + void sentinel_init(void *p, size_t size) nothrow { *sentinel_size(p) = size; *sentinel_pre(p) = SENTINEL_PRE; @@ -3289,20 +3374,20 @@ version (SENTINEL) } - void sentinel_Invariant(const void *p) + void sentinel_Invariant(const void *p) nothrow { assert(*sentinel_pre(p) == SENTINEL_PRE); assert(*sentinel_post(p) == SENTINEL_POST); } - void *sentinel_add(void *p) + void *sentinel_add(void *p) nothrow { return p + 2 * size_t.sizeof; } - void *sentinel_sub(void *p) + void *sentinel_sub(void *p) nothrow { return p - 2 * size_t.sizeof; } @@ -3312,23 +3397,23 @@ else const uint SENTINEL_EXTRA = 0; - void sentinel_init(void *p, size_t size) + void sentinel_init(void *p, size_t size) nothrow { } - void sentinel_Invariant(const void *p) + void sentinel_Invariant(const void *p) nothrow { } - void *sentinel_add(void *p) + void *sentinel_add(void *p) nothrow { return p; } - void *sentinel_sub(void *p) + void *sentinel_sub(void *p) nothrow { return p; } diff --git a/libphobos/libdruntime/gc/os.d b/libphobos/libdruntime/gc/os.d index 344775ad9..7f4fe27db 100644 --- a/libphobos/libdruntime/gc/os.d +++ b/libphobos/libdruntime/gc/os.d @@ -20,7 +20,7 @@ version (Windows) alias int pthread_t; - pthread_t pthread_self() + pthread_t pthread_self() nothrow { return cast(pthread_t) GetCurrentThreadId(); } @@ -59,7 +59,7 @@ static if (is(typeof(VirtualAlloc))) // version (GC_Use_Alloc_Win32) /** * Map memory. */ - void *os_mem_map(size_t nbytes) + void *os_mem_map(size_t nbytes) nothrow { return VirtualAlloc(null, nbytes, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); @@ -72,14 +72,14 @@ static if (is(typeof(VirtualAlloc))) // version (GC_Use_Alloc_Win32) * 0 success * !=0 failure */ - int os_mem_unmap(void *base, size_t nbytes) + int os_mem_unmap(void *base, size_t nbytes) nothrow { return cast(int)(VirtualFree(base, 0, MEM_RELEASE) == 0); } } else static if (is(typeof(mmap))) // else version (GC_Use_Alloc_MMap) { - void *os_mem_map(size_t nbytes) + void *os_mem_map(size_t nbytes) nothrow { void *p; p = mmap(null, nbytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); @@ -87,20 +87,20 @@ else static if (is(typeof(mmap))) // else version (GC_Use_Alloc_MMap) } - int os_mem_unmap(void *base, size_t nbytes) + int os_mem_unmap(void *base, size_t nbytes) nothrow { return munmap(base, nbytes); } } else static if (is(typeof(valloc))) // else version (GC_Use_Alloc_Valloc) { - void *os_mem_map(size_t nbytes) + void *os_mem_map(size_t nbytes) nothrow { return valloc(nbytes); } - int os_mem_unmap(void *base, size_t nbytes) + int os_mem_unmap(void *base, size_t nbytes) nothrow { free(base); return 0; @@ -120,7 +120,7 @@ else static if (is(typeof(malloc))) // else version (GC_Use_Alloc_Malloc) const size_t PAGE_MASK = PAGESIZE - 1; - void *os_mem_map(size_t nbytes) + void *os_mem_map(size_t nbytes) nothrow { byte *p, q; p = cast(byte *) malloc(nbytes + PAGESIZE); q = p + ((PAGESIZE - ((cast(size_t) p & PAGE_MASK))) & PAGE_MASK); @@ -129,7 +129,7 @@ else static if (is(typeof(malloc))) // else version (GC_Use_Alloc_Malloc) } - int os_mem_unmap(void *base, size_t nbytes) + int os_mem_unmap(void *base, size_t nbytes) nothrow { free( *cast(void**)( cast(byte*) base + nbytes ) ); return 0; diff --git a/libphobos/libdruntime/gc/proxy.d b/libphobos/libdruntime/gc/proxy.d index 087773ab9..f7919a83c 100644 --- a/libphobos/libdruntime/gc/proxy.d +++ b/libphobos/libdruntime/gc/proxy.d @@ -28,6 +28,9 @@ private __gshared gc_t _gc; + static import core.memory; + alias BlkInfo = core.memory.GC.BlkInfo; + extern (C) void thread_init(); extern (C) void thread_term(); @@ -37,6 +40,7 @@ private { void function() gc_enable; void function() gc_disable; + nothrow: void function() gc_collect; void function() gc_minimize; @@ -44,11 +48,11 @@ private uint function(void*, uint) gc_setAttr; uint function(void*, uint) gc_clrAttr; - void* function(size_t, uint) gc_malloc; - BlkInfo function(size_t, uint) gc_qalloc; - void* function(size_t, uint) gc_calloc; - void* function(void*, size_t, uint ba) gc_realloc; - size_t function(void*, size_t, size_t) gc_extend; + void* function(size_t, uint, const TypeInfo) gc_malloc; + BlkInfo function(size_t, uint, const TypeInfo) gc_qalloc; + void* function(size_t, uint, const TypeInfo) gc_calloc; + void* function(void*, size_t, uint ba, const TypeInfo) gc_realloc; + size_t function(void*, size_t, size_t, const TypeInfo) gc_extend; size_t function(size_t) gc_reserve; void function(void*) gc_free; @@ -58,10 +62,11 @@ private BlkInfo function(void*) gc_query; void function(void*) gc_addRoot; - void function(void*, size_t) gc_addRange; + void function(void*, size_t, const TypeInfo ti) gc_addRange; void function(void*) gc_removeRoot; void function(void*) gc_removeRange; + void function(in void[]) gc_runFinalizers; } } @@ -97,6 +102,7 @@ private pthis.gc_removeRoot = &gc_removeRoot; pthis.gc_removeRange = &gc_removeRange; + pthis.gc_runFinalizers = &gc_runFinalizers; } } @@ -158,7 +164,7 @@ extern (C) return proxy.gc_disable(); } - void gc_collect() + void gc_collect() nothrow { if( proxy is null ) { @@ -168,103 +174,103 @@ extern (C) return proxy.gc_collect(); } - void gc_minimize() + void gc_minimize() nothrow { if( proxy is null ) return _gc.minimize(); return proxy.gc_minimize(); } - uint gc_getAttr( void* p ) + uint gc_getAttr( void* p ) nothrow { if( proxy is null ) return _gc.getAttr( p ); return proxy.gc_getAttr( p ); } - uint gc_setAttr( void* p, uint a ) + uint gc_setAttr( void* p, uint a ) nothrow { if( proxy is null ) return _gc.setAttr( p, a ); return proxy.gc_setAttr( p, a ); } - uint gc_clrAttr( void* p, uint a ) + uint gc_clrAttr( void* p, uint a ) nothrow { if( proxy is null ) return _gc.clrAttr( p, a ); return proxy.gc_clrAttr( p, a ); } - void* gc_malloc( size_t sz, uint ba = 0 ) + void* gc_malloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow { if( proxy is null ) - return _gc.malloc( sz, ba ); - return proxy.gc_malloc( sz, ba ); + return _gc.malloc( sz, ba, null, ti ); + return proxy.gc_malloc( sz, ba, ti ); } - BlkInfo gc_qalloc( size_t sz, uint ba = 0 ) + BlkInfo gc_qalloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow { if( proxy is null ) { BlkInfo retval; - retval.base = _gc.malloc( sz, ba, &retval.size ); + retval.base = _gc.malloc( sz, ba, &retval.size, ti ); retval.attr = ba; return retval; } - return proxy.gc_qalloc( sz, ba ); + return proxy.gc_qalloc( sz, ba, ti ); } - void* gc_calloc( size_t sz, uint ba = 0 ) + void* gc_calloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow { if( proxy is null ) - return _gc.calloc( sz, ba ); - return proxy.gc_calloc( sz, ba ); + return _gc.calloc( sz, ba, null, ti ); + return proxy.gc_calloc( sz, ba, ti ); } - void* gc_realloc( void* p, size_t sz, uint ba = 0 ) + void* gc_realloc( void* p, size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow { if( proxy is null ) - return _gc.realloc( p, sz, ba ); - return proxy.gc_realloc( p, sz, ba ); + return _gc.realloc( p, sz, ba, null, ti ); + return proxy.gc_realloc( p, sz, ba, ti ); } - size_t gc_extend( void* p, size_t mx, size_t sz ) + size_t gc_extend( void* p, size_t mx, size_t sz, const TypeInfo ti = null ) nothrow { if( proxy is null ) - return _gc.extend( p, mx, sz ); - return proxy.gc_extend( p, mx, sz ); + return _gc.extend( p, mx, sz, ti ); + return proxy.gc_extend( p, mx, sz,ti ); } - size_t gc_reserve( size_t sz ) + size_t gc_reserve( size_t sz ) nothrow { if( proxy is null ) return _gc.reserve( sz ); return proxy.gc_reserve( sz ); } - void gc_free( void* p ) + void gc_free( void* p ) nothrow { if( proxy is null ) return _gc.free( p ); return proxy.gc_free( p ); } - void* gc_addrOf( void* p ) + void* gc_addrOf( void* p ) nothrow { if( proxy is null ) return _gc.addrOf( p ); return proxy.gc_addrOf( p ); } - size_t gc_sizeOf( void* p ) + size_t gc_sizeOf( void* p ) nothrow { if( proxy is null ) return _gc.sizeOf( p ); return proxy.gc_sizeOf( p ); } - BlkInfo gc_query( void* p ) + BlkInfo gc_query( void* p ) nothrow { if( proxy is null ) return _gc.query( p ); @@ -273,7 +279,7 @@ extern (C) // NOTE: This routine is experimental. The stats or function name may change // before it is made officially available. - GCStats gc_stats() + GCStats gc_stats() nothrow { if( proxy is null ) { @@ -287,35 +293,42 @@ extern (C) return GCStats.init; } - void gc_addRoot( void* p ) + void gc_addRoot( void* p ) nothrow { if( proxy is null ) return _gc.addRoot( p ); return proxy.gc_addRoot( p ); } - void gc_addRange( void* p, size_t sz ) + void gc_addRange( void* p, size_t sz, const TypeInfo ti = null ) nothrow { if( proxy is null ) - return _gc.addRange( p, sz ); - return proxy.gc_addRange( p, sz ); + return _gc.addRange( p, sz, ti ); + return proxy.gc_addRange( p, sz, ti ); } - void gc_removeRoot( void* p ) + void gc_removeRoot( void* p ) nothrow { if( proxy is null ) return _gc.removeRoot( p ); return proxy.gc_removeRoot( p ); } - void gc_removeRange( void* p ) + void gc_removeRange( void* p ) nothrow { if( proxy is null ) return _gc.removeRange( p ); return proxy.gc_removeRange( p ); } - Proxy* gc_getProxy() + void gc_runFinalizers( in void[] segment ) nothrow + { + if( proxy is null ) + return _gc.runFinalizers( segment ); + return proxy.gc_runFinalizers( segment ); + } + + Proxy* gc_getProxy() nothrow { return &pthis; } @@ -329,18 +342,21 @@ extern (C) // TODO: Decide if this is an error condition. } proxy = p; - foreach( r; _gc.rootIter ) + foreach (r; _gc.rootIter) proxy.gc_addRoot( r ); - foreach( r; _gc.rangeIter ) - proxy.gc_addRange( r.pbot, r.ptop - r.pbot ); + + foreach (r; _gc.rangeIter) + proxy.gc_addRange( r.pbot, r.ptop - r.pbot, null ); } void gc_clrProxy() { - foreach( r; _gc.rangeIter ) + foreach (r; _gc.rangeIter) proxy.gc_removeRange( r.pbot ); - foreach( r; _gc.rootIter ) + + foreach (r; _gc.rootIter) proxy.gc_removeRoot( r ); + proxy = null; } } diff --git a/libphobos/libdruntime/gcstub/gc.d b/libphobos/libdruntime/gcstub/gc.d index 0103c6845..a4d52a4fc 100644 --- a/libphobos/libdruntime/gcstub/gc.d +++ b/libphobos/libdruntime/gcstub/gc.d @@ -47,7 +47,7 @@ private } extern (C) void thread_init(); - extern (C) void onOutOfMemoryError() @trusted /* pure dmd @@@BUG11461@@@ */ nothrow; + extern (C) void onOutOfMemoryError(void* pretend_sideffect = null) @trusted pure nothrow; /* dmd @@@BUG11461@@@ */ struct Proxy { @@ -60,11 +60,11 @@ private extern (C) uint function(void*, uint) gc_setAttr; extern (C) uint function(void*, uint) gc_clrAttr; - extern (C) void* function(size_t, uint) gc_malloc; - extern (C) BlkInfo function(size_t, uint) gc_qalloc; - extern (C) void* function(size_t, uint) gc_calloc; - extern (C) void* function(void*, size_t, uint ba) gc_realloc; - extern (C) size_t function(void*, size_t, size_t) gc_extend; + extern (C) void* function(size_t, uint, const TypeInfo) gc_malloc; + extern (C) BlkInfo function(size_t, uint, const TypeInfo) gc_qalloc; + extern (C) void* function(size_t, uint, const TypeInfo) gc_calloc; + extern (C) void* function(void*, size_t, uint ba, const TypeInfo) gc_realloc; + extern (C) size_t function(void*, size_t, size_t, const TypeInfo) gc_extend; extern (C) size_t function(size_t) gc_reserve; extern (C) void function(void*) gc_free; @@ -74,7 +74,7 @@ private extern (C) BlkInfo function(void*) gc_query; extern (C) void function(void*) gc_addRoot; - extern (C) void function(void*, size_t) gc_addRange; + extern (C) void function(void*, size_t, const TypeInfo ti) gc_addRange; extern (C) void function(void*) gc_removeRoot; extern (C) void function(void*) gc_removeRange; @@ -121,6 +121,7 @@ private { void* pos; size_t len; + TypeInfo ti; // should be tail const, but doesn't exist for references } __gshared Range* ranges = null; @@ -190,7 +191,7 @@ extern (C) uint gc_clrAttr( void* p, uint a ) return proxy.gc_clrAttr( p, a ); } -extern (C) void* gc_malloc( size_t sz, uint ba = 0 ) +extern (C) void* gc_malloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) { if( proxy is null ) { @@ -200,10 +201,10 @@ extern (C) void* gc_malloc( size_t sz, uint ba = 0 ) onOutOfMemoryError(); return p; } - return proxy.gc_malloc( sz, ba ); + return proxy.gc_malloc( sz, ba, ti ); } -extern (C) BlkInfo gc_qalloc( size_t sz, uint ba = 0 ) +extern (C) BlkInfo gc_qalloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) { if( proxy is null ) { @@ -213,10 +214,10 @@ extern (C) BlkInfo gc_qalloc( size_t sz, uint ba = 0 ) retval.attr = ba; return retval; } - return proxy.gc_qalloc( sz, ba ); + return proxy.gc_qalloc( sz, ba, ti ); } -extern (C) void* gc_calloc( size_t sz, uint ba = 0 ) +extern (C) void* gc_calloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) { if( proxy is null ) { @@ -226,10 +227,10 @@ extern (C) void* gc_calloc( size_t sz, uint ba = 0 ) onOutOfMemoryError(); return p; } - return proxy.gc_calloc( sz, ba ); + return proxy.gc_calloc( sz, ba, ti ); } -extern (C) void* gc_realloc( void* p, size_t sz, uint ba = 0 ) +extern (C) void* gc_realloc( void* p, size_t sz, uint ba = 0, const TypeInfo ti = null ) { if( proxy is null ) { @@ -239,14 +240,14 @@ extern (C) void* gc_realloc( void* p, size_t sz, uint ba = 0 ) onOutOfMemoryError(); return p; } - return proxy.gc_realloc( p, sz, ba ); + return proxy.gc_realloc( p, sz, ba, ti ); } -extern (C) size_t gc_extend( void* p, size_t mx, size_t sz ) +extern (C) size_t gc_extend( void* p, size_t mx, size_t sz, const TypeInfo ti = null ) { if( proxy is null ) return 0; - return proxy.gc_extend( p, mx, sz ); + return proxy.gc_extend( p, mx, sz, ti ); } extern (C) size_t gc_reserve( size_t sz ) @@ -299,7 +300,7 @@ extern (C) void gc_addRoot( void* p ) return proxy.gc_addRoot( p ); } -extern (C) void gc_addRange( void* p, size_t sz ) +extern (C) void gc_addRange( void* p, size_t sz, const TypeInfo ti = null ) { //printf("gcstub::gc_addRange() proxy = %p\n", proxy); if( proxy is null ) @@ -310,11 +311,12 @@ extern (C) void gc_addRange( void* p, size_t sz ) onOutOfMemoryError(); r[nranges].pos = p; r[nranges].len = sz; + r[nranges].ti = cast()ti; ranges = r; ++nranges; return; } - return proxy.gc_addRange( p, sz ); + return proxy.gc_addRange( p, sz, ti ); } extern (C) void gc_removeRoot( void *p ) @@ -366,7 +368,7 @@ export extern (C) void gc_setProxy( Proxy* p ) foreach( r; roots[0 .. nroots] ) proxy.gc_addRoot( r ); foreach( r; ranges[0 .. nranges] ) - proxy.gc_addRange( r.pos, r.len ); + proxy.gc_addRange( r.pos, r.len, r.ti ); } export extern (C) void gc_clrProxy() diff --git a/libphobos/libdruntime/object.di b/libphobos/libdruntime/object.di index 8b5044600..2d92e873c 100644 --- a/libphobos/libdruntime/object.di +++ b/libphobos/libdruntime/object.di @@ -65,25 +65,25 @@ struct OffsetTypeInfo class TypeInfo { - override string toString() const; + override string toString() const pure @safe nothrow; override size_t toHash() @trusted const; override int opCmp(Object o); override bool opEquals(Object o); size_t getHash(in void* p) @trusted nothrow const; bool equals(in void* p1, in void* p2) const; int compare(in void* p1, in void* p2) const; - @property size_t tsize() nothrow pure const @safe; + @property size_t tsize() nothrow pure const @safe @nogc; void swap(void* p1, void* p2) const; - @property inout(TypeInfo) next() nothrow pure inout; - const(void)[] init() nothrow pure const @safe; // TODO: make this a property, but may need to be renamed to diambiguate with T.init... - @property uint flags() nothrow pure const @safe; + @property inout(TypeInfo) next() nothrow pure inout @nogc; + const(void)[] init() nothrow pure const @safe @nogc; // TODO: make this a property, but may need to be renamed to diambiguate with T.init... + @property uint flags() nothrow pure const @safe @nogc; // 1: // has possible pointers into GC memory const(OffsetTypeInfo)[] offTi() const; void destroy(void* p) const; void postblit(void* p) const; - @property size_t talign() nothrow pure const @safe; + @property size_t talign() nothrow pure const @safe @nogc; version (X86_64) int argTypes(out TypeInfo arg1, out TypeInfo arg2) @safe nothrow; - @property immutable(void)* rtInfo() nothrow pure const @safe; + @property immutable(void)* rtInfo() nothrow pure const @safe @nogc; } class TypeInfo_Typedef : TypeInfo @@ -130,7 +130,6 @@ class TypeInfo_AssociativeArray : TypeInfo { TypeInfo value; TypeInfo key; - TypeInfo impl; } class TypeInfo_Vector : TypeInfo @@ -280,10 +279,20 @@ struct ModuleInfo uint _flags; uint _index; + version (all) + { + deprecated("ModuleInfo cannot be copy-assigned because it is a variable-sized struct.") + void opAssign(in ModuleInfo m) { _flags = m._flags; _index = m._index; } + } + else + { + @disable this(); + @disable this(this) const; + } + +const: @property uint index() nothrow pure; - @property void index(uint i) nothrow pure; @property uint flags() nothrow pure; - @property void flags(uint f) nothrow pure; @property void function() tlsctor() nothrow pure; @property void function() tlsdtor() nothrow pure; @property void* xgetMembers() nothrow pure; @@ -291,11 +300,11 @@ struct ModuleInfo @property void function() dtor() nothrow pure; @property void function() ictor() nothrow pure; @property void function() unitTest() nothrow pure; - @property ModuleInfo*[] importedModules() nothrow pure; + @property immutable(ModuleInfo*)[] importedModules() nothrow pure; @property TypeInfo_Class[] localClasses() nothrow pure; @property string name() nothrow pure; - static int opApply(scope int delegate(ref ModuleInfo*) dg); + static int opApply(scope int delegate(ModuleInfo*) dg); } class Throwable : Object @@ -354,138 +363,160 @@ extern (C) { // from druntime/src/rt/aaA.d - size_t _aaLen(in void* p) pure nothrow; - void* _aaGetX(void** pp, const TypeInfo keyti, in size_t valuesize, in void* pkey); - inout(void)* _aaGetRvalueX(inout void* p, in TypeInfo keyti, in size_t valuesize, in void* pkey); + // size_t _aaLen(in void* p) pure nothrow @nogc; + // void* _aaGetX(void** pp, const TypeInfo keyti, in size_t valuesize, in void* pkey); + // inout(void)* _aaGetRvalueX(inout void* p, in TypeInfo keyti, in size_t valuesize, in void* pkey); inout(void)[] _aaValues(inout void* p, in size_t keysize, in size_t valuesize) pure nothrow; inout(void)[] _aaKeys(inout void* p, in size_t keysize) pure nothrow; void* _aaRehash(void** pp, in TypeInfo keyti) pure nothrow; - extern (D) alias scope int delegate(void *) _dg_t; - int _aaApply(void* aa, size_t keysize, _dg_t dg); + // extern (D) alias scope int delegate(void *) _dg_t; + // int _aaApply(void* aa, size_t keysize, _dg_t dg); - extern (D) alias scope int delegate(void *, void *) _dg2_t; - int _aaApply2(void* aa, size_t keysize, _dg2_t dg); + // extern (D) alias scope int delegate(void *, void *) _dg2_t; + // int _aaApply2(void* aa, size_t keysize, _dg2_t dg); private struct AARange { void* impl, current; } - AARange _aaRange(void* aa); - bool _aaRangeEmpty(AARange r); - void* _aaRangeFrontKey(AARange r); - void* _aaRangeFrontValue(AARange r); - void _aaRangePopFront(ref AARange r); + AARange _aaRange(void* aa) pure nothrow @nogc; + bool _aaRangeEmpty(AARange r) pure nothrow @nogc; + void* _aaRangeFrontKey(AARange r) pure nothrow @nogc; + void* _aaRangeFrontValue(AARange r) pure nothrow @nogc; + void _aaRangePopFront(ref AARange r) pure nothrow @nogc; } -struct AssociativeArray(Key, Value) -{ -private: - void* p; +alias AssociativeArray(Key, Value) = Value[Key]; -public: - @property size_t length() const { return _aaLen(p); } +T rehash(T : Value[Key], Value, Key)(T aa) +{ + _aaRehash(cast(void**)&aa, typeid(Value[Key])); + return aa; +} - Value[Key] rehash() - { - auto p = _aaRehash(cast(void**) &p, typeid(Value[Key])); - return *cast(Value[Key]*)(&p); - } +T rehash(T : Value[Key], Value, Key)(T* aa) +{ + _aaRehash(cast(void**)aa, typeid(Value[Key])); + return *aa; +} - // Note: can't make `values` and `keys` inout as it is used - // e.g. in Phobos like `ReturnType!(aa.keys)` instead of `typeof(aa.keys)` - // which will result in `inout` propagation. +T rehash(T : shared Value[Key], Value, Key)(T aa) +{ + _aaRehash(cast(void**)&aa, typeid(Value[Key])); + return aa; +} - inout(Value)[] inout_values() inout @property - { - auto a = _aaValues(p, Key.sizeof, Value.sizeof); - return *cast(inout Value[]*) &a; - } +T rehash(T : shared Value[Key], Value, Key)(T* aa) +{ + _aaRehash(cast(void**)aa, typeid(Value[Key])); + return *aa; +} - inout(Key)[] inout_keys() inout @property +Value[Key] dup(T : Value[Key], Value, Key)(T aa) if (is(typeof({ + ref Value get(); // pseudo lvalue of Value + Value[Key] r; r[Key.init] = get(); + // bug 10720 - check whether Value is copyable + }))) +{ + Value[Key] result; + foreach (k, v; aa) { - auto a = _aaKeys(p, Key.sizeof); - return *cast(inout Key[]*) &a; + result[k] = v; } + return result; +} - Value[] values() @property - { return inout_values; } - - Key[] keys() @property - { return inout_keys; } +Value[Key] dup(T : Value[Key], Value, Key)(T* aa) if (is(typeof((*aa).dup))) +{ + return (*aa).dup; +} - const(Value)[] values() const @property - { return inout_values; } +@disable Value[Key] dup(T : Value[Key], Value, Key)(T aa) if (!is(typeof({ + ref Value get(); // pseudo lvalue of Value + Value[Key] r; r[Key.init] = get(); + // bug 10720 - check whether Value is copyable + }))); - const(Key)[] keys() const @property - { return inout_keys; } +Value[Key] dup(T : Value[Key], Value, Key)(T* aa) if (!is(typeof((*aa).dup))); - int opApply(scope int delegate(ref Key, ref Value) dg) +auto byKey(T : Value[Key], Value, Key)(T aa) pure nothrow @nogc +{ + static struct Result { - return _aaApply2(p, Key.sizeof, cast(_dg2_t)dg); - } + AARange r; - int opApply(scope int delegate(ref Value) dg) - { - return _aaApply(p, Key.sizeof, cast(_dg_t)dg); + pure nothrow @nogc: + @property bool empty() { return _aaRangeEmpty(r); } + @property ref Key front() { return *cast(Key*)_aaRangeFrontKey(r); } + void popFront() { _aaRangePopFront(r); } + Result save() { return this; } } - Value get(Key key, lazy Value defaultValue) - { - auto p = key in *cast(Value[Key]*)(&p); - return p ? *p : defaultValue; - } + return Result(_aaRange(cast(void*)aa)); +} - static if (is(typeof({ - ref Value get(); // pseudo lvalue of Value - Value[Key] r; r[Key.init] = get(); - // bug 10720 - check whether Value is copyable - }))) +auto byKey(T : Value[Key], Value, Key)(T *aa) pure nothrow @nogc +{ + return (*aa).byKey(); +} + +auto byValue(T : Value[Key], Value, Key)(T aa) pure nothrow @nogc +{ + static struct Result { - Value[Key] dup() - { - Value[Key] result; - foreach (k, v; this) - { - result[k] = v; - } - return result; - } + AARange r; + + pure nothrow @nogc: + @property bool empty() { return _aaRangeEmpty(r); } + @property ref Value front() { return *cast(Value*)_aaRangeFrontValue(r); } + void popFront() { _aaRangePopFront(r); } + Result save() { return this; } } - else - @disable Value[Key] dup(); // for better error message - auto byKey() - { - static struct Result - { - AARange r; + return Result(_aaRange(cast(void*)aa)); +} - @property bool empty() { return _aaRangeEmpty(r); } - @property ref Key front() { return *cast(Key*)_aaRangeFrontKey(r); } - void popFront() { _aaRangePopFront(r); } - Result save() { return this; } - } +auto byValue(T : Value[Key], Value, Key)(T *aa) pure nothrow @nogc +{ + return (*aa).byValue(); +} - return Result(_aaRange(p)); - } +Key[] keys(T : Value[Key], Value, Key)(T aa) @property +{ + auto a = cast(void[])_aaKeys(cast(inout(void)*)aa, Key.sizeof); + return *cast(Key[]*)&a; +} - auto byValue() - { - static struct Result - { - AARange r; +Key[] keys(T : Value[Key], Value, Key)(T *aa) @property +{ + return (*aa).keys; +} - @property bool empty() { return _aaRangeEmpty(r); } - @property ref Value front() { return *cast(Value*)_aaRangeFrontValue(r); } - void popFront() { _aaRangePopFront(r); } - Result save() { return this; } - } +Value[] values(T : Value[Key], Value, Key)(T aa) @property +{ + auto a = cast(void[])_aaValues(cast(inout(void)*)aa, Key.sizeof, Value.sizeof); + return *cast(Value[]*)&a; +} - return Result(_aaRange(p)); - } +Value[] values(T : Value[Key], Value, Key)(T *aa) @property +{ + return (*aa).values; +} + +inout(V) get(K, V)(inout(V[K]) aa, K key, lazy inout(V) defaultValue) +{ + auto p = key in aa; + return p ? *p : defaultValue; +} + +inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue) +{ + return (*aa).get(key, defaultValue); } -// Scheduled for deprecation in December 2012. +// Originally scheduled for deprecation in December 2012. +// Marked 'deprecated' in April 2014. Rescheduled for final deprecation in October 2014. // Please use destroy instead of clear. -alias destroy clear; +deprecated("Please use destroy instead.") +alias clear = destroy; void destroy(T)(T obj) if (is(T == class)) { @@ -531,7 +562,7 @@ template _isStaticArray(T) private { - extern (C) void _d_arrayshrinkfit(TypeInfo ti, void[] arr); + extern (C) void _d_arrayshrinkfit(TypeInfo ti, void[] arr) nothrow; extern (C) size_t _d_arraysetcapacity(TypeInfo ti, size_t newcapacity, void *arrptr) pure nothrow; } @@ -545,7 +576,7 @@ size_t reserve(T)(ref T[] arr, size_t newcapacity) pure nothrow @trusted return _d_arraysetcapacity(typeid(T[]), newcapacity, cast(void *)&arr); } -auto ref inout(T[]) assumeSafeAppend(T)(auto ref inout(T[]) arr) +auto ref inout(T[]) assumeSafeAppend(T)(auto ref inout(T[]) arr) nothrow { _d_arrayshrinkfit(typeid(T[]), *(cast(void[]*)&arr)); return arr; @@ -573,52 +604,113 @@ template RTInfo(T) enum RTInfo = cast(void*)0x12345678; } -version (unittest) +/// Provide the .dup array property. +@property auto dup(T)(T[] a) + if (!is(const(T) : T)) +{ + import core.internal.traits : Unconst; + static assert(is(T : Unconst!T), "Cannot implicitly convert type "~T.stringof~ + " to "~Unconst!T.stringof~" in dup."); + + // wrap unsafe _dup in @trusted to preserve @safe postblit + static if (__traits(compiles, (T b) @safe { T a = b; })) + return _trustedDup!(T, Unconst!T)(a); + else + return _dup!(T, Unconst!T)(a); +} + +/// ditto +// const overload to support implicit conversion to immutable (unique result, see DIP29) +@property T[] dup(T)(const(T)[] a) + if (is(const(T) : T)) +{ + // wrap unsafe _dup in @trusted to preserve @safe postblit + static if (__traits(compiles, (T b) @safe { T a = b; })) + return _trustedDup!(const(T), T)(a); + else + return _dup!(const(T), T)(a); +} + +/// ditto +@property T[] dup(T:void)(const(T)[] a) @trusted +{ + if (__ctfe) assert(0, "Cannot dup a void[] array at compile time."); + return cast(T[])_rawDup(a); +} + +/// Provide the .idup array property. +@property immutable(T)[] idup(T)(T[] a) +{ + static assert(is(T : immutable(T)), "Cannot implicitly convert type "~T.stringof~ + " to immutable in idup."); + + // wrap unsafe _dup in @trusted to preserve @safe postblit + static if (__traits(compiles, (T b) @safe { T a = b; })) + return _trustedDup!(T, immutable(T))(a); + else + return _dup!(T, immutable(T))(a); +} + +/// ditto +@property immutable(T)[] idup(T:void)(const(T)[] a) +{ + return .dup(a); +} + +private U[] _trustedDup(T, U)(T[] a) @trusted +{ + return _dup!(T, U)(a); +} + +private U[] _dup(T, U)(T[] a) // pure nothrow depends on postblit +{ + if (__ctfe) + { + U[] res; + foreach (ref e; a) + res ~= e; + return res; + } + + a = _rawDup(a); + auto res = *cast(typeof(return)*)&a; + _doPostblit(res); + return res; +} + +private extern (C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow; + +private inout(T)[] _rawDup(T)(inout(T)[] a) { - string __unittest_toString(T)(ref T value) pure nothrow @trusted + import core.stdc.string : memcpy; + + void[] arr = _d_newarrayU(typeid(T[]), a.length); + memcpy(arr.ptr, cast(void*)a.ptr, T.sizeof * a.length); + return *cast(inout(T)[]*)&arr; +} + +private void _doPostblit(T)(T[] ary) +{ + // infer static postblit type, run postblit if any + static if (is(T == struct)) { - static if (is(T == string)) - return `"` ~ value ~ `"`; // TODO: Escape internal double-quotes. - else + import core.internal.traits : Unqual; + + alias PostBlitT = typeof(function(void*){T a = T.init, b = a;}); + // use typeid(Unqual!T) here to skip TypeInfo_Const/Shared/... + auto postBlit = cast(PostBlitT)typeid(Unqual!T).xpostblit; + if (postBlit !is null) { - version (druntime_unittest) - { - return T.stringof; - } - else - { - enum phobos_impl = q{ - import std.traits; - alias Unqual!T U; - static if (isFloatingPoint!U) - { - import std.string; - enum format_string = is(U == float) ? "%.7g" : - is(U == double) ? "%.16g" : "%.20g"; - return (cast(string function(...) pure nothrow @safe)&format)(format_string, value); - } - else - { - import std.conv; - alias to!string toString; - alias toString!T f; - return (cast(string function(T) pure nothrow @safe)&f)(value); - } - }; - enum tango_impl = q{ - import tango.util.Convert; - alias to!(string, T) f; - return (cast(string function(T) pure nothrow @safe)&f)(value); - }; - - static if (__traits(compiles, { mixin(phobos_impl); })) - mixin(phobos_impl); - else static if (__traits(compiles, { mixin(tango_impl); })) - mixin(tango_impl); - else - return T.stringof; - } + foreach (ref el; ary) + postBlit(cast(void*)&el); } } -} + else if ((&typeid(T).postblit).funcptr !is &TypeInfo.postblit) + { + alias PostBlitT = typeof(delegate(void*){T a = T.init, b = a;}); + auto postBlit = cast(PostBlitT)&typeid(T).postblit; + foreach (ref el; ary) + postBlit(cast(void*)&el); + } +} diff --git a/libphobos/libdruntime/object_.d b/libphobos/libdruntime/object_.d index 037e10605..aeee5e043 100644 --- a/libphobos/libdruntime/object_.d +++ b/libphobos/libdruntime/object_.d @@ -27,21 +27,15 @@ private import core.memory; import rt.util.hash; import rt.util.string; - import rt.minfo; debug(PRINTF) import core.stdc.stdio; - extern (C) void onOutOfMemoryError() @trusted /* pure dmd @@@BUG11461@@@ */ nothrow; + extern (C) void onOutOfMemoryError(void* pretend_sideffect = null) @trusted pure nothrow; /* dmd @@@BUG11461@@@ */ extern (C) Object _d_newclass(const TypeInfo_Class ci); - extern (C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr); + extern (C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow; extern (C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void *arrptr) pure nothrow; extern (C) void rt_finalize(void *data, bool det=true); } -version (druntime_unittest) -{ - string __unittest_toString(T)(T) { return T.stringof; } -} - // NOTE: For some reason, this declaration method doesn't work // in this particular file (and this file only). It must // be a DMD thing. @@ -123,11 +117,28 @@ class Object } /** - * Create instance of class specified by classname. + * Create instance of class specified by the fully qualified name + * classname. * The class must either have no constructors or have * a default constructor. * Returns: * null if failed + * Example: + * --- + * module foo.bar; + * + * class C + * { + * this() { x = 10; } + * int x; + * } + * + * void main() + * { + * auto c = cast(C)Object.factory("foo.bar.C"); + * assert(c !is null && c.x == 10); + * } + * --- */ static Object factory(string classname) { @@ -201,10 +212,9 @@ struct OffsetTypeInfo */ class TypeInfo { - override string toString() const + override string toString() const pure @safe nothrow { - // hack to keep const qualifiers for TypeInfo member functions - return (cast()super).toString(); + return typeid(this).name; } override size_t toHash() @trusted const @@ -252,10 +262,10 @@ class TypeInfo bool equals(in void* p1, in void* p2) const { return p1 == p2; } /// Compares two instances for <, ==, or >. - int compare(in void* p1, in void* p2) const { return 0; } + int compare(in void* p1, in void* p2) const { return _xopCmp(p1, p2); } /// Returns size of the type. - @property size_t tsize() nothrow pure const @safe { return 0; } + @property size_t tsize() nothrow pure const @safe @nogc { return 0; } /// Swaps two instances of the type. void swap(void* p1, void* p2) const @@ -271,15 +281,16 @@ class TypeInfo /// Get TypeInfo for 'next' type, as defined by what kind of type this is, /// null if none. - @property inout(TypeInfo) next() nothrow pure inout { return null; } + @property inout(TypeInfo) next() nothrow pure inout @nogc { return null; } /// Return default initializer. If the type should be initialized to all zeros, /// an array with a null ptr and a length equal to the type size will be returned. // TODO: make this a property, but may need to be renamed to diambiguate with T.init... - const(void)[] init() nothrow pure const @safe { return null; } + const(void)[] init() nothrow pure const @safe @nogc { return null; } - /// Get flags for type: 1 means GC should scan for pointers - @property uint flags() nothrow pure const @safe { return 0; } + /// Get flags for type: 1 means GC should scan for pointers, + /// 2 means arg of this type is passed in XMM register + @property uint flags() nothrow pure const @safe @nogc { return 0; } /// Get type information on the contents of the type; null if not available const(OffsetTypeInfo)[] offTi() const { return null; } @@ -290,7 +301,7 @@ class TypeInfo /// Return alignment of type - @property size_t talign() nothrow pure const @safe { return tsize; } + @property size_t talign() nothrow pure const @safe @nogc { return tsize; } /** Return internal info on arguments fitting into 8byte. * See X86-64 ABI 3.2.3 @@ -303,7 +314,7 @@ class TypeInfo /** Return info used by the garbage collector to do precise collection. */ - @property immutable(void)* rtInfo() nothrow pure const @safe { return null; } + @property immutable(void)* rtInfo() nothrow pure const @safe @nogc { return null; } } class TypeInfo_Typedef : TypeInfo @@ -413,7 +424,7 @@ class TypeInfo_Array : TypeInfo override size_t getHash(in void* p) @trusted const { void[] a = *cast(void[]*)p; - return hashOf(a.ptr, a.length * value.tsize); + return getArrayHash(value, a.ptr, a.length); } override bool equals(in void* p1, in void* p2) const @@ -488,7 +499,7 @@ class TypeInfo_StaticArray : TypeInfo override string toString() const { SizeStringBuff tmpBuff = void; - return cast(string)(value.toString() ~ "[" ~ len.sizeToTempString(tmpBuff) ~ "]"); + return value.toString() ~ "[" ~ len.sizeToTempString(tmpBuff) ~ "]"; } override bool opEquals(Object o) @@ -502,11 +513,7 @@ class TypeInfo_StaticArray : TypeInfo override size_t getHash(in void* p) @trusted const { - size_t sz = value.tsize; - size_t hash = 0; - for (size_t i = 0; i < len; i++) - hash += value.getHash(p + i * sz); - return hash; + return getArrayHash(value, p, len); } override bool equals(in void* p1, in void* p2) const @@ -606,7 +613,7 @@ class TypeInfo_AssociativeArray : TypeInfo { override string toString() const { - return cast(string)(next.toString() ~ "[" ~ key.toString() ~ "]"); + return value.toString() ~ "[" ~ key.toString() ~ "]"; } override bool opEquals(Object o) @@ -641,8 +648,6 @@ class TypeInfo_AssociativeArray : TypeInfo TypeInfo value; TypeInfo key; - TypeInfo impl; - override @property size_t talign() nothrow pure const { return (char[int]).alignof; @@ -1379,15 +1384,12 @@ class Throwable : Object SizeStringBuff tmpBuff = void; sink(typeid(this).name); - if (file.ptr) - { - sink("@"); sink(file); - sink("("); sink(line.sizeToTempString(tmpBuff)); sink(")"); - } + sink("@"); sink(file); + sink("("); sink(line.sizeToTempString(tmpBuff)); sink(")"); - if (msg.ptr) + if (msg.length) { - sink(": "), sink(msg); + sink(": "); sink(msg); } if (info) { @@ -1466,7 +1468,7 @@ class Exception : Throwable /** * Creates a new instance of Exception. The next parameter is used - * internally and should be always be $(D null) when passed by user code. + * internally and should always be $(D null) when passed by user code. * This constructor does not automatically throw the newly-created * Exception; the $(D throw) statement should be used for that purpose. */ @@ -1509,8 +1511,23 @@ unittest } +/** + * The base class of all unrecoverable runtime errors. + * + * This represents the category of $(D Throwable) objects that are $(B not) + * safe to catch and handle. In principle, one should not catch Error + * objects, as they represent unrecoverable runtime errors. + * Certain runtime guarantees may fail to hold when these errors are + * thrown, making it unsafe to continue execution after catching them. + */ class Error : Throwable { + /** + * Creates a new instance of Error. The next parameter is used + * internally and should always be $(D null) when passed by user code. + * This constructor does not automatically throw the newly-created + * Error; the $(D throw) statement should be used for that purpose. + */ @safe pure nothrow this(string msg, Throwable next = null) { super(msg, next); @@ -1523,8 +1540,8 @@ class Error : Throwable bypassedException = null; } - /// The first Exception which was bypassed when this Error was thrown, - /// or null if no Exceptions were pending. + /// The first $(D Exception) which was bypassed when this Error was thrown, + /// or $(D null) if no $(D Exception)s were pending. Throwable bypassedException; } @@ -1588,6 +1605,18 @@ struct ModuleInfo uint _flags; uint _index; // index into _moduleinfo_array[] + version (all) + { + deprecated("ModuleInfo cannot be copy-assigned because it is a variable-sized struct.") + void opAssign(in ModuleInfo m) { _flags = m._flags; _index = m._index; } + } + else + { + @disable this(); + @disable this(this) const; + } + +const: private void* addrOf(int flag) nothrow pure in { @@ -1652,10 +1681,8 @@ struct ModuleInfo } @property uint index() nothrow pure { return _index; } - @property void index(uint i) nothrow pure { _index = i; } @property uint flags() nothrow pure { return _flags; } - @property void flags(uint f) nothrow pure { _flags = f; } @property void function() tlsctor() nothrow pure { @@ -1692,12 +1719,12 @@ struct ModuleInfo return flags & MIunitTest ? *cast(typeof(return)*)addrOf(MIunitTest) : null; } - @property ModuleInfo*[] importedModules() nothrow pure + @property immutable(ModuleInfo*)[] importedModules() nothrow pure { if (flags & MIimportedModules) { auto p = cast(size_t*)addrOf(MIimportedModules); - return (cast(ModuleInfo**)(p + 1))[0 .. *p]; + return (cast(immutable(ModuleInfo*)*)(p + 1))[0 .. *p]; } return null; } @@ -1722,14 +1749,23 @@ struct ModuleInfo // return null; } - alias int delegate(ref ModuleInfo*) ApplyDg; - - static int opApply(scope ApplyDg dg) + static int opApply(scope int delegate(ModuleInfo*) dg) { - return rt.minfo.moduleinfos_apply(dg); + import rt.minfo; + // Bugzilla 13084 - enforcing immutable ModuleInfo would break client code + return rt.minfo.moduleinfos_apply( + (immutable(ModuleInfo*)m) => dg(cast(ModuleInfo*)m)); } } +unittest +{ + ModuleInfo* m1; + foreach (m; ModuleInfo) + { + m1 = m; + } +} /////////////////////////////////////////////////////////////////////////////// // Monitor @@ -1934,139 +1970,159 @@ extern (C) { // from druntime/src/rt/aaA.d - size_t _aaLen(in void* p) pure nothrow; - void* _aaGetX(void** pp, const TypeInfo keyti, in size_t valuesize, in void* pkey); - inout(void)* _aaGetRvalueX(inout void* p, in TypeInfo keyti, in size_t valuesize, in void* pkey); + // size_t _aaLen(in void* p) pure nothrow @nogc; + // void* _aaGetX(void** pp, const TypeInfo keyti, in size_t valuesize, in void* pkey); + // inout(void)* _aaGetRvalueX(inout void* p, in TypeInfo keyti, in size_t valuesize, in void* pkey); inout(void)[] _aaValues(inout void* p, in size_t keysize, in size_t valuesize) pure nothrow; inout(void)[] _aaKeys(inout void* p, in size_t keysize) pure nothrow; void* _aaRehash(void** pp, in TypeInfo keyti) pure nothrow; - extern (D) alias scope int delegate(void *) _dg_t; - int _aaApply(void* aa, size_t keysize, _dg_t dg); + // extern (D) alias scope int delegate(void *) _dg_t; + // int _aaApply(void* aa, size_t keysize, _dg_t dg); - extern (D) alias scope int delegate(void *, void *) _dg2_t; - int _aaApply2(void* aa, size_t keysize, _dg2_t dg); + // extern (D) alias scope int delegate(void *, void *) _dg2_t; + // int _aaApply2(void* aa, size_t keysize, _dg2_t dg); private struct AARange { void* impl, current; } - AARange _aaRange(void* aa); - bool _aaRangeEmpty(AARange r); - void* _aaRangeFrontKey(AARange r); - void* _aaRangeFrontValue(AARange r); - void _aaRangePopFront(ref AARange r); + AARange _aaRange(void* aa) pure nothrow @nogc; + bool _aaRangeEmpty(AARange r) pure nothrow @nogc; + void* _aaRangeFrontKey(AARange r) pure nothrow @nogc; + void* _aaRangeFrontValue(AARange r) pure nothrow @nogc; + void _aaRangePopFront(ref AARange r) pure nothrow @nogc; int _aaEqual(in TypeInfo tiRaw, in void* e1, in void* e2); hash_t _aaGetHash(in void* aa, in TypeInfo tiRaw) nothrow; } -struct AssociativeArray(Key, Value) -{ -private: - void* p; +alias AssociativeArray(Key, Value) = Value[Key]; -public: - @property size_t length() const { return _aaLen(p); } +T rehash(T : Value[Key], Value, Key)(T aa) +{ + _aaRehash(cast(void**)&aa, typeid(Value[Key])); + return aa; +} - Value[Key] rehash() - { - auto p = _aaRehash(cast(void**) &p, typeid(Value[Key])); - return *cast(Value[Key]*)(&p); - } +T rehash(T : Value[Key], Value, Key)(T* aa) +{ + _aaRehash(cast(void**)aa, typeid(Value[Key])); + return *aa; +} - // Note: can't make `values` and `keys` inout as it is used - // e.g. in Phobos like `ReturnType!(aa.keys)` instead of `typeof(aa.keys)` - // which will result in `inout` propagation. +T rehash(T : shared Value[Key], Value, Key)(T aa) +{ + _aaRehash(cast(void**)&aa, typeid(Value[Key])); + return aa; +} - inout(Value)[] inout_values() inout @property - { - auto a = _aaValues(p, Key.sizeof, Value.sizeof); - return *cast(inout Value[]*) &a; - } +T rehash(T : shared Value[Key], Value, Key)(T* aa) +{ + _aaRehash(cast(void**)aa, typeid(Value[Key])); + return *aa; +} - inout(Key)[] inout_keys() inout @property +Value[Key] dup(T : Value[Key], Value, Key)(T aa) if (is(typeof({ + ref Value get(); // pseudo lvalue of Value + Value[Key] r; r[Key.init] = get(); + // bug 10720 - check whether Value is copyable + }))) +{ + Value[Key] result; + foreach (k, v; aa) { - auto a = _aaKeys(p, Key.sizeof); - return *cast(inout Key[]*) &a; + result[k] = v; } + return result; +} - Value[] values() @property - { return inout_values; } - - Key[] keys() @property - { return inout_keys; } +Value[Key] dup(T : Value[Key], Value, Key)(T* aa) if (is(typeof((*aa).dup))) +{ + return (*aa).dup; +} - const(Value)[] values() const @property - { return inout_values; } +@disable Value[Key] dup(T : Value[Key], Value, Key)(T aa) if (!is(typeof({ + ref Value get(); // pseudo lvalue of Value + Value[Key] r; r[Key.init] = get(); + // bug 10720 - check whether Value is copyable + }))); - const(Key)[] keys() const @property - { return inout_keys; } +Value[Key] dup(T : Value[Key], Value, Key)(T* aa) if (!is(typeof((*aa).dup))); - int opApply(scope int delegate(ref Key, ref Value) dg) +auto byKey(T : Value[Key], Value, Key)(T aa) pure nothrow @nogc +{ + static struct Result { - return _aaApply2(p, Key.sizeof, cast(_dg2_t)dg); - } + AARange r; - int opApply(scope int delegate(ref Value) dg) - { - return _aaApply(p, Key.sizeof, cast(_dg_t)dg); + pure nothrow @nogc: + @property bool empty() { return _aaRangeEmpty(r); } + @property ref Key front() { return *cast(Key*)_aaRangeFrontKey(r); } + void popFront() { _aaRangePopFront(r); } + Result save() { return this; } } - Value get(Key key, lazy Value defaultValue) - { - auto p = key in *cast(Value[Key]*)(&p); - return p ? *p : defaultValue; - } + return Result(_aaRange(cast(void*)aa)); +} - static if (is(typeof({ - ref Value get(); // pseudo lvalue of Value - Value[Key] r; r[Key.init] = get(); - // bug 10720 - check whether Value is copyable - }))) +auto byKey(T : Value[Key], Value, Key)(T *aa) pure nothrow @nogc +{ + return (*aa).byKey(); +} + +auto byValue(T : Value[Key], Value, Key)(T aa) pure nothrow @nogc +{ + static struct Result { - Value[Key] dup() - { - Value[Key] result; - foreach (k, v; this) - { - result[k] = v; - } - return result; - } + AARange r; + + pure nothrow @nogc: + @property bool empty() { return _aaRangeEmpty(r); } + @property ref Value front() { return *cast(Value*)_aaRangeFrontValue(r); } + void popFront() { _aaRangePopFront(r); } + Result save() { return this; } } - else - @disable Value[Key] dup(); // for better error message - auto byKey() - { - static struct Result - { - AARange r; + return Result(_aaRange(cast(void*)aa)); +} - @property bool empty() { return _aaRangeEmpty(r); } - @property ref Key front() { return *cast(Key*)_aaRangeFrontKey(r); } - void popFront() { _aaRangePopFront(r); } - Result save() { return this; } - } +auto byValue(T : Value[Key], Value, Key)(T *aa) pure nothrow @nogc +{ + return (*aa).byValue(); +} - return Result(_aaRange(p)); - } +Key[] keys(T : Value[Key], Value, Key)(T aa) @property +{ + auto a = cast(void[])_aaKeys(cast(inout(void)*)aa, Key.sizeof); + return *cast(Key[]*)&a; +} - auto byValue() - { - static struct Result - { - AARange r; +Key[] keys(T : Value[Key], Value, Key)(T *aa) @property +{ + return (*aa).keys; +} - @property bool empty() { return _aaRangeEmpty(r); } - @property ref Value front() { return *cast(Value*)_aaRangeFrontValue(r); } - void popFront() { _aaRangePopFront(r); } - Result save() { return this; } - } +Value[] values(T : Value[Key], Value, Key)(T aa) @property +{ + auto a = cast(void[])_aaValues(cast(inout(void)*)aa, Key.sizeof, Value.sizeof); + return *cast(Value[]*)&a; +} - return Result(_aaRange(p)); - } +Value[] values(T : Value[Key], Value, Key)(T *aa) @property +{ + return (*aa).values; } -unittest +inout(V) get(K, V)(inout(V[K]) aa, K key, lazy inout(V) defaultValue) +{ + auto p = key in aa; + return p ? *p : defaultValue; +} + +inout(V) get(K, V)(inout(V[K])* aa, K key, lazy inout(V) defaultValue) +{ + return (*aa).get(key, defaultValue); +} + +pure nothrow unittest { int[int] a; foreach (i; a.byKey) @@ -2079,7 +2135,7 @@ unittest } } -unittest +pure /*nothrow @@@BUG5555@@@*/ unittest { auto a = [ 1:"one", 2:"two", 3:"three" ]; auto b = a.dup; @@ -2097,7 +2153,8 @@ unittest assert(c[1] == 2); assert(c[2] == 3); } -unittest + +pure nothrow unittest { // test for bug 5925 const a = [4:0]; @@ -2105,7 +2162,7 @@ unittest assert(a == b); } -unittest +pure nothrow unittest { // test for bug 9052 static struct Json { @@ -2119,7 +2176,7 @@ unittest } } -unittest +pure nothrow unittest { // test for bug 8583: ensure Slot and aaA are on the same page wrt value alignment string[byte] aa0 = [0: "zero"]; @@ -2135,7 +2192,7 @@ unittest assert(aa4.byValue.front == "onetwothreefourfive"); } -unittest +pure nothrow unittest { // test for bug 10720 static struct NC @@ -2147,7 +2204,7 @@ unittest static assert(!is(aa.nonExistingField)); } -unittest +pure nothrow unittest { // bug 5842 string[string] test = null; @@ -2157,7 +2214,7 @@ unittest test["test3"] = "test3"; // causes divide by zero if rehash broke the AA } -unittest +pure nothrow unittest { string[] keys = ["a", "b", "c", "d", "e", "f"]; @@ -2238,7 +2295,7 @@ unittest } } -unittest +pure nothrow unittest { // expanded test for 5842: increase AA size past the point where the AA // stops using binit, in order to test another code path in rehash. @@ -2251,6 +2308,13 @@ unittest aa[1] = 1; } +pure nothrow unittest +{ + // bug 13078 + shared string[][string] map; + map.rehash; +} + deprecated("Please use destroy instead of clear.") alias destroy clear; @@ -2529,7 +2593,7 @@ unittest * Returns: * The input is returned. */ -auto ref inout(T[]) assumeSafeAppend(T)(auto ref inout(T[]) arr) +auto ref inout(T[]) assumeSafeAppend(T)(auto ref inout(T[]) arr) nothrow { _d_arrayshrinkfit(typeid(T[]), *(cast(void[]*)&arr)); return arr; @@ -2664,3 +2728,328 @@ template RTInfo(T) { enum RTInfo = null; } + + +// Helper functions + +private: + +inout(TypeInfo) getElement(inout TypeInfo value) @trusted pure nothrow +{ + TypeInfo element = cast() value; + for(;;) + { + if(auto qualified = cast(TypeInfo_Const) element) + element = qualified.base; + else if(auto redefined = cast(TypeInfo_Typedef) element) // typedef & enum + element = redefined.base; + else if(auto staticArray = cast(TypeInfo_StaticArray) element) + element = staticArray.value; + else if(auto vector = cast(TypeInfo_Vector) element) + element = vector.base; + else + break; + } + return cast(inout) element; +} + +size_t getArrayHash(in TypeInfo element, in void* ptr, in size_t count) @trusted nothrow +{ + if(!count) + return 0; + + const size_t elementSize = element.tsize; + if(!elementSize) + return 0; + + static bool hasCustomToHash(in TypeInfo value) @trusted pure nothrow + { + const element = getElement(value); + + if(const struct_ = cast(const TypeInfo_Struct) element) + return !!struct_.xtoHash; + + return cast(const TypeInfo_Array) element + || cast(const TypeInfo_AssociativeArray) element + || cast(const ClassInfo) element + || cast(const TypeInfo_Interface) element; + } + + if(!hasCustomToHash(element)) + return hashOf(ptr, elementSize * count); + + size_t hash = 0; + foreach(size_t i; 0 .. count) + hash += element.getHash(ptr + i * elementSize); + return hash; +} + + +// @@@BUG5835@@@ tests: + +unittest +{ + class C + { + int i; + this(in int i) { this.i = i; } + override hash_t toHash() { return 0; } + } + C[] a1 = [new C(11)], a2 = [new C(12)]; + assert(typeid(C[]).getHash(&a1) == typeid(C[]).getHash(&a2)); // fails +} + +unittest +{ + struct S + { + int i; + hash_t toHash() const @safe nothrow { return 0; } + } + S[] a1 = [S(11)], a2 = [S(12)]; + assert(typeid(S[]).getHash(&a1) == typeid(S[]).getHash(&a2)); // fails +} + +@safe unittest +{ + struct S + { + int i; + const @safe nothrow: + hash_t toHash() { return 0; } + bool opEquals(const S) { return true; } + int opCmp(const S) { return 0; } + } + + int[S[]] aa = [[S(11)] : 13]; + assert(aa[[S(12)]] == 13); // fails +} + +public: + +/// Provide the .dup array property. +@property auto dup(T)(T[] a) + if (!is(const(T) : T)) +{ + import core.internal.traits : Unconst; + static assert(is(T : Unconst!T), "Cannot implicitly convert type "~T.stringof~ + " to "~Unconst!T.stringof~" in dup."); + + // wrap unsafe _dup in @trusted to preserve @safe postblit + static if (__traits(compiles, (T b) @safe { T a = b; })) + return _trustedDup!(T, Unconst!T)(a); + else + return _dup!(T, Unconst!T)(a); +} + +/// ditto +// const overload to support implicit conversion to immutable (unique result, see DIP29) +@property T[] dup(T)(const(T)[] a) + if (is(const(T) : T)) +{ + // wrap unsafe _dup in @trusted to preserve @safe postblit + static if (__traits(compiles, (T b) @safe { T a = b; })) + return _trustedDup!(const(T), T)(a); + else + return _dup!(const(T), T)(a); +} + +/// ditto +@property T[] dup(T:void)(const(T)[] a) @trusted +{ + if (__ctfe) assert(0, "Cannot dup a void[] array at compile time."); + return cast(T[])_rawDup(a); +} + +/// Provide the .idup array property. +@property immutable(T)[] idup(T)(T[] a) +{ + static assert(is(T : immutable(T)), "Cannot implicitly convert type "~T.stringof~ + " to immutable in idup."); + + // wrap unsafe _dup in @trusted to preserve @safe postblit + static if (__traits(compiles, (T b) @safe { T a = b; })) + return _trustedDup!(T, immutable(T))(a); + else + return _dup!(T, immutable(T))(a); +} + +/// ditto +@property immutable(T)[] idup(T:void)(const(T)[] a) +{ + return a.dup; +} + +private U[] _trustedDup(T, U)(T[] a) @trusted +{ + return _dup!(T, U)(a); +} + +private U[] _dup(T, U)(T[] a) // pure nothrow depends on postblit +{ + if (__ctfe) + { + U[] res; + foreach (ref e; a) + res ~= e; + return res; + } + + a = _rawDup(a); + auto res = *cast(typeof(return)*)&a; + _doPostblit(res); + return res; +} + +private extern (C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow; + +private inout(T)[] _rawDup(T)(inout(T)[] a) +{ + import core.stdc.string : memcpy; + + void[] arr = _d_newarrayU(typeid(T[]), a.length); + memcpy(arr.ptr, cast(void*)a.ptr, T.sizeof * a.length); + return *cast(inout(T)[]*)&arr; +} + +private void _doPostblit(T)(T[] ary) +{ + // infer static postblit type, run postblit if any + static if (is(T == struct)) + { + import core.internal.traits : Unqual; + + alias PostBlitT = typeof(function(void*){T a = T.init, b = a;}); + // use typeid(Unqual!T) here to skip TypeInfo_Const/Shared/... + auto postBlit = cast(PostBlitT)typeid(Unqual!T).xpostblit; + if (postBlit !is null) + { + foreach (ref el; ary) + postBlit(cast(void*)&el); + } + } + else if ((&typeid(T).postblit).funcptr !is &TypeInfo.postblit) + { + alias PostBlitT = typeof(delegate(void*){T a = T.init, b = a;}); + auto postBlit = cast(PostBlitT)&typeid(T).postblit; + + foreach (ref el; ary) + postBlit(cast(void*)&el); + } +} + +unittest +{ + static struct S1 { int* p; } + static struct S2 { @disable this(); } + static struct S3 { @disable this(this); } + + int dg1() pure nothrow @safe + { + { + char[] m; + string i; + m = m.dup; + i = i.idup; + m = i.dup; + i = m.idup; + } + { + S1[] m; + immutable(S1)[] i; + m = m.dup; + i = i.idup; + static assert(!is(typeof(m.idup))); + static assert(!is(typeof(i.dup))); + } + { + S3[] m; + immutable(S3)[] i; + static assert(!is(typeof(m.dup))); + static assert(!is(typeof(i.idup))); + } + { + shared(S1)[] m; + m = m.dup; + static assert(!is(typeof(m.idup))); + } + { + int[] a = (inout(int)) { inout(const(int))[] a; return a.dup; }(0); + } + return 1; + } + + int dg2() pure nothrow @safe + { + { + S2[] m = [S2.init, S2.init]; + immutable(S2)[] i = [S2.init, S2.init]; + m = m.dup; + m = i.dup; + i = m.idup; + i = i.idup; + } + return 2; + } + + enum a = dg1(); + enum b = dg2(); + assert(dg1() == a); + assert(dg2() == b); +} + +unittest +{ + static struct Sunpure { this(this) @safe nothrow {} } + static struct Sthrow { this(this) @safe pure {} } + static struct Sunsafe { this(this) @system pure nothrow {} } + + static assert( __traits(compiles, () { [].dup!Sunpure; })); + static assert(!__traits(compiles, () pure { [].dup!Sunpure; })); + static assert( __traits(compiles, () { [].dup!Sthrow; })); + static assert(!__traits(compiles, () nothrow { [].dup!Sthrow; })); + static assert( __traits(compiles, () { [].dup!Sunsafe; })); + static assert(!__traits(compiles, () @safe { [].dup!Sunsafe; })); + + static assert( __traits(compiles, () { [].idup!Sunpure; })); + static assert(!__traits(compiles, () pure { [].idup!Sunpure; })); + static assert( __traits(compiles, () { [].idup!Sthrow; })); + static assert(!__traits(compiles, () nothrow { [].idup!Sthrow; })); + static assert( __traits(compiles, () { [].idup!Sunsafe; })); + static assert(!__traits(compiles, () @safe { [].idup!Sunsafe; })); +} + +unittest +{ + static int*[] pureFoo() pure { return null; } + { char[] s; immutable x = s.dup; } + { immutable x = (cast(int*[])null).dup; } + { immutable x = pureFoo(); } + { immutable x = pureFoo().dup; } +} + +unittest +{ + auto a = [1, 2, 3]; + auto b = a.dup; + assert(b.capacity >= 3); +} + +unittest +{ + // Bugzilla 12580 + void[] m = [0]; + shared(void)[] s = [cast(shared)1]; + immutable(void)[] i = [cast(immutable)2]; + + s = s.dup; + static assert(is(typeof(s.dup) == shared(void)[])); + + m = i.dup; + i = m.dup; + i = i.idup; + i = m.idup; + i = s.idup; + i = s.dup; + static assert(!__traits(compiles, m = s.dup)); +} diff --git a/libphobos/libdruntime/rt/aApply.d b/libphobos/libdruntime/rt/aApply.d index 87dda162a..72d7ad649 100644 --- a/libphobos/libdruntime/rt/aApply.d +++ b/libphobos/libdruntime/rt/aApply.d @@ -17,13 +17,13 @@ module rt.aApply; private import rt.util.utf; -/********************************************** - */ +/**********************************************/ +/* 1 argument versions */ // dg is D, but _aApplycd() is C extern (D) alias int delegate(void *) dg_t; -extern (C) int _aApplycd1(char[] aa, dg_t dg) +extern (C) int _aApplycd1(in char[] aa, dg_t dg) { int result; size_t i; size_t len = aa.length; @@ -44,7 +44,49 @@ extern (C) int _aApplycd1(char[] aa, dg_t dg) return result; } -extern (C) int _aApplywd1(wchar[] aa, dg_t dg) +unittest +{ + debug(apply) printf("_aApplycd1.unittest\n"); + + auto s = "hello"c[]; + int i; + + foreach(dchar d; s) + { + switch (i) + { + case 0: assert(d == 'h'); break; + case 1: assert(d == 'e'); break; + case 2: assert(d == 'l'); break; + case 3: assert(d == 'l'); break; + case 4: assert(d == 'o'); break; + default: assert(0); + } + i++; + } + assert(i == 5); + + s = "a\u1234\U000A0456b"; + i = 0; + foreach(dchar d; s) + { + //printf("i = %d, d = %x\n", i, d); + switch (i) + { + case 0: assert(d == 'a'); break; + case 1: assert(d == '\u1234'); break; + case 2: assert(d == '\U000A0456'); break; + case 3: assert(d == 'b'); break; + default: assert(0); + } + i++; + } + assert(i == 4); +} + +/*****************************/ + +extern (C) int _aApplywd1(in wchar[] aa, dg_t dg) { int result; size_t i; size_t len = aa.length; @@ -65,7 +107,49 @@ extern (C) int _aApplywd1(wchar[] aa, dg_t dg) return result; } -extern (C) int _aApplycw1(char[] aa, dg_t dg) +unittest +{ + debug(apply) printf("_aApplywd1.unittest\n"); + + auto s = "hello"w[]; + int i; + + foreach(dchar d; s) + { + switch (i) + { + case 0: assert(d == 'h'); break; + case 1: assert(d == 'e'); break; + case 2: assert(d == 'l'); break; + case 3: assert(d == 'l'); break; + case 4: assert(d == 'o'); break; + default: assert(0); + } + i++; + } + assert(i == 5); + + s = "a\u1234\U000A0456b"; + i = 0; + foreach(dchar d; s) + { + //printf("i = %d, d = %x\n", i, d); + switch (i) + { + case 0: assert(d == 'a'); break; + case 1: assert(d == '\u1234'); break; + case 2: assert(d == '\U000A0456'); break; + case 3: assert(d == 'b'); break; + default: assert(0); + } + i++; + } + assert(i == 4); +} + +/*****************************/ + +extern (C) int _aApplycw1(in char[] aa, dg_t dg) { int result; size_t i; size_t len = aa.length; @@ -98,7 +182,50 @@ extern (C) int _aApplycw1(char[] aa, dg_t dg) return result; } -extern (C) int _aApplywc1(wchar[] aa, dg_t dg) +unittest +{ + debug(apply) printf("_aApplycw1.unittest\n"); + + auto s = "hello"c[]; + int i; + + foreach(wchar d; s) + { + switch (i) + { + case 0: assert(d == 'h'); break; + case 1: assert(d == 'e'); break; + case 2: assert(d == 'l'); break; + case 3: assert(d == 'l'); break; + case 4: assert(d == 'o'); break; + default: assert(0); + } + i++; + } + assert(i == 5); + + s = "a\u1234\U000A0456b"; + i = 0; + foreach(wchar d; s) + { + //printf("i = %d, d = %x\n", i, d); + switch (i) + { + case 0: assert(d == 'a'); break; + case 1: assert(d == 0x1234); break; + case 2: assert(d == 0xDA41); break; + case 3: assert(d == 0xDC56); break; + case 4: assert(d == 'b'); break; + default: assert(0); + } + i++; + } + assert(i == 5); +} + +/*****************************/ + +extern (C) int _aApplywc1(in wchar[] aa, dg_t dg) { int result; size_t i; size_t len = aa.length; @@ -135,7 +262,54 @@ extern (C) int _aApplywc1(wchar[] aa, dg_t dg) return result; } -extern (C) int _aApplydc1(dchar[] aa, dg_t dg) +unittest +{ + debug(apply) printf("_aApplywc1.unittest\n"); + + auto s = "hello"w[]; + int i; + + foreach(char d; s) + { + switch (i) + { + case 0: assert(d == 'h'); break; + case 1: assert(d == 'e'); break; + case 2: assert(d == 'l'); break; + case 3: assert(d == 'l'); break; + case 4: assert(d == 'o'); break; + default: assert(0); + } + i++; + } + assert(i == 5); + + s = "a\u1234\U000A0456b"; + i = 0; + foreach(char d; s) + { + //printf("i = %d, d = %x\n", i, d); + switch (i) + { + case 0: assert(d == 'a'); break; + case 1: assert(d == 0xE1); break; + case 2: assert(d == 0x88); break; + case 3: assert(d == 0xB4); break; + case 4: assert(d == 0xF2); break; + case 5: assert(d == 0xA0); break; + case 6: assert(d == 0x91); break; + case 7: assert(d == 0x96); break; + case 8: assert(d == 'b'); break; + default: assert(0); + } + i++; + } + assert(i == 9); +} + +/*****************************/ + +extern (C) int _aApplydc1(in dchar[] aa, dg_t dg) { int result; debug(apply) printf("_aApplydc1(), len = %d\n", aa.length); @@ -167,7 +341,54 @@ extern (C) int _aApplydc1(dchar[] aa, dg_t dg) return result; } -extern (C) int _aApplydw1(dchar[] aa, dg_t dg) +unittest +{ + debug(apply) printf("_aApplyRdc1.unittest\n"); + + auto s = "hello"d[]; + int i; + + foreach(char d; s) + { + switch (i) + { + case 0: assert(d == 'h'); break; + case 1: assert(d == 'e'); break; + case 2: assert(d == 'l'); break; + case 3: assert(d == 'l'); break; + case 4: assert(d == 'o'); break; + default: assert(0); + } + i++; + } + assert(i == 5); + + s = "a\u1234\U000A0456b"; + i = 0; + foreach(char d; s) + { + //printf("i = %d, d = %x\n", i, d); + switch (i) + { + case 0: assert(d == 'a'); break; + case 1: assert(d == 0xE1); break; + case 2: assert(d == 0x88); break; + case 3: assert(d == 0xB4); break; + case 4: assert(d == 0xF2); break; + case 5: assert(d == 0xA0); break; + case 6: assert(d == 0x91); break; + case 7: assert(d == 0x96); break; + case 8: assert(d == 'b'); break; + default: assert(0); + } + i++; + } + assert(i == 9); +} + +/*****************************/ + +extern (C) int _aApplydw1(in dchar[] aa, dg_t dg) { int result; debug(apply) printf("_aApplydw1(), len = %d\n", aa.length); @@ -192,13 +413,55 @@ extern (C) int _aApplydw1(dchar[] aa, dg_t dg) return result; } +unittest +{ + debug(apply) printf("_aApplydw1.unittest\n"); + + auto s = "hello"d[]; + int i; + + foreach(wchar d; s) + { + switch (i) + { + case 0: assert(d == 'h'); break; + case 1: assert(d == 'e'); break; + case 2: assert(d == 'l'); break; + case 3: assert(d == 'l'); break; + case 4: assert(d == 'o'); break; + default: assert(0); + } + i++; + } + assert(i == 5); + + s = "a\u1234\U000A0456b"; + i = 0; + foreach(wchar d; s) + { + //printf("i = %d, d = %x\n", i, d); + switch (i) + { + case 0: assert(d == 'a'); break; + case 1: assert(d == 0x1234); break; + case 2: assert(d == 0xDA41); break; + case 3: assert(d == 0xDC56); break; + case 4: assert(d == 'b'); break; + default: assert(0); + } + i++; + } + assert(i == 5); +} + /****************************************************************************/ +/* 2 argument versions */ // dg is D, but _aApplycd2() is C extern (D) alias int delegate(void *, void *) dg2_t; -extern (C) int _aApplycd2(char[] aa, dg2_t dg) +extern (C) int _aApplycd2(in char[] aa, dg2_t dg) { int result; size_t i; size_t n; @@ -224,7 +487,51 @@ extern (C) int _aApplycd2(char[] aa, dg2_t dg) return result; } -extern (C) int _aApplywd2(wchar[] aa, dg2_t dg) +unittest +{ + debug(apply) printf("_aApplycd2.unittest\n"); + + auto s = "hello"c[]; + int i; + + foreach(k, dchar d; s) + { + //printf("i = %d, k = %d, d = %x\n", i, k, d); + assert(k == i); + switch (i) + { + case 0: assert(d == 'h'); break; + case 1: assert(d == 'e'); break; + case 2: assert(d == 'l'); break; + case 3: assert(d == 'l'); break; + case 4: assert(d == 'o'); break; + default: assert(0); + } + i++; + } + assert(i == 5); + + s = "a\u1234\U000A0456b"; + i = 0; + foreach(k, dchar d; s) + { + //printf("i = %d, k = %d, d = %x\n", i, k, d); + switch (i) + { + case 0: assert(d == 'a'); assert(k == 0); break; + case 1: assert(d == '\u1234'); assert(k == 1); break; + case 2: assert(d == '\U000A0456'); assert(k == 4); break; + case 3: assert(d == 'b'); assert(k == 8); break; + default: assert(0); + } + i++; + } + assert(i == 4); +} + +/*****************************/ + +extern (C) int _aApplywd2(in wchar[] aa, dg2_t dg) { int result; size_t i; size_t n; @@ -250,7 +557,51 @@ extern (C) int _aApplywd2(wchar[] aa, dg2_t dg) return result; } -extern (C) int _aApplycw2(char[] aa, dg2_t dg) +unittest +{ + debug(apply) printf("_aApplywd2.unittest\n"); + + auto s = "hello"w[]; + int i; + + foreach(k, dchar d; s) + { + //printf("i = %d, k = %d, d = %x\n", i, k, d); + assert(k == i); + switch (i) + { + case 0: assert(d == 'h'); break; + case 1: assert(d == 'e'); break; + case 2: assert(d == 'l'); break; + case 3: assert(d == 'l'); break; + case 4: assert(d == 'o'); break; + default: assert(0); + } + i++; + } + assert(i == 5); + + s = "a\u1234\U000A0456b"; + i = 0; + foreach(k, dchar d; s) + { + //printf("i = %d, k = %d, d = %x\n", i, k, d); + switch (i) + { + case 0: assert(k == 0); assert(d == 'a'); break; + case 1: assert(k == 1); assert(d == '\u1234'); break; + case 2: assert(k == 2); assert(d == '\U000A0456'); break; + case 3: assert(k == 4); assert(d == 'b'); break; + default: assert(0); + } + i++; + } + assert(i == 4); +} + +/*****************************/ + +extern (C) int _aApplycw2(in char[] aa, dg2_t dg) { int result; size_t i; size_t n; @@ -286,7 +637,52 @@ extern (C) int _aApplycw2(char[] aa, dg2_t dg) return result; } -extern (C) int _aApplywc2(wchar[] aa, dg2_t dg) +unittest +{ + debug(apply) printf("_aApplycw2.unittest\n"); + + auto s = "hello"c[]; + int i; + + foreach(k, wchar d; s) + { + //printf("i = %d, k = %d, d = %x\n", i, k, d); + assert(k == i); + switch (i) + { + case 0: assert(d == 'h'); break; + case 1: assert(d == 'e'); break; + case 2: assert(d == 'l'); break; + case 3: assert(d == 'l'); break; + case 4: assert(d == 'o'); break; + default: assert(0); + } + i++; + } + assert(i == 5); + + s = "a\u1234\U000A0456b"; + i = 0; + foreach(k, wchar d; s) + { + //printf("i = %d, k = %d, d = %x\n", i, k, d); + switch (i) + { + case 0: assert(k == 0); assert(d == 'a'); break; + case 1: assert(k == 1); assert(d == 0x1234); break; + case 2: assert(k == 4); assert(d == 0xDA41); break; + case 3: assert(k == 4); assert(d == 0xDC56); break; + case 4: assert(k == 8); assert(d == 'b'); break; + default: assert(0); + } + i++; + } + assert(i == 5); +} + +/*****************************/ + +extern (C) int _aApplywc2(in wchar[] aa, dg2_t dg) { int result; size_t i; size_t n; @@ -326,7 +722,56 @@ extern (C) int _aApplywc2(wchar[] aa, dg2_t dg) return result; } -extern (C) int _aApplydc2(dchar[] aa, dg2_t dg) +unittest +{ + debug(apply) printf("_aApplywc2.unittest\n"); + + auto s = "hello"w[]; + int i; + + foreach(k, char d; s) + { + //printf("i = %d, k = %d, d = %x\n", i, k, d); + assert(k == i); + switch (i) + { + case 0: assert(d == 'h'); break; + case 1: assert(d == 'e'); break; + case 2: assert(d == 'l'); break; + case 3: assert(d == 'l'); break; + case 4: assert(d == 'o'); break; + default: assert(0); + } + i++; + } + assert(i == 5); + + s = "a\u1234\U000A0456b"; + i = 0; + foreach(k, char d; s) + { + //printf("i = %d, k = %d, d = %x\n", i, k, d); + switch (i) + { + case 0: assert(k == 0); assert(d == 'a'); break; + case 1: assert(k == 1); assert(d == 0xE1); break; + case 2: assert(k == 1); assert(d == 0x88); break; + case 3: assert(k == 1); assert(d == 0xB4); break; + case 4: assert(k == 2); assert(d == 0xF2); break; + case 5: assert(k == 2); assert(d == 0xA0); break; + case 6: assert(k == 2); assert(d == 0x91); break; + case 7: assert(k == 2); assert(d == 0x96); break; + case 8: assert(k == 4); assert(d == 'b'); break; + default: assert(0); + } + i++; + } + assert(i == 9); +} + +/*****************************/ + +extern (C) int _aApplydc2(in dchar[] aa, dg2_t dg) { int result; size_t i; size_t len = aa.length; @@ -360,7 +805,56 @@ extern (C) int _aApplydc2(dchar[] aa, dg2_t dg) return result; } -extern (C) int _aApplydw2(dchar[] aa, dg2_t dg) +unittest +{ + debug(apply) printf("_aApplydc2.unittest\n"); + + auto s = "hello"d[]; + int i; + + foreach(k, char d; s) + { + //printf("i = %d, k = %d, d = %x\n", i, k, d); + assert(k == i); + switch (i) + { + case 0: assert(d == 'h'); break; + case 1: assert(d == 'e'); break; + case 2: assert(d == 'l'); break; + case 3: assert(d == 'l'); break; + case 4: assert(d == 'o'); break; + default: assert(0); + } + i++; + } + assert(i == 5); + + s = "a\u1234\U000A0456b"; + i = 0; + foreach(k, char d; s) + { + //printf("i = %d, k = %d, d = %x\n", i, k, d); + switch (i) + { + case 0: assert(k == 0); assert(d == 'a'); break; + case 1: assert(k == 1); assert(d == 0xE1); break; + case 2: assert(k == 1); assert(d == 0x88); break; + case 3: assert(k == 1); assert(d == 0xB4); break; + case 4: assert(k == 2); assert(d == 0xF2); break; + case 5: assert(k == 2); assert(d == 0xA0); break; + case 6: assert(k == 2); assert(d == 0x91); break; + case 7: assert(k == 2); assert(d == 0x96); break; + case 8: assert(k == 3); assert(d == 'b'); break; + default: assert(0); + } + i++; + } + assert(i == 9); +} + +/*****************************/ + +extern (C) int _aApplydw2(in dchar[] aa, dg2_t dg) { int result; debug(apply) printf("_aApplydw2(), len = %d\n", aa.length); @@ -385,3 +879,46 @@ extern (C) int _aApplydw2(dchar[] aa, dg2_t dg) } return result; } + +unittest +{ + debug(apply) printf("_aApplydw2.unittest\n"); + + auto s = "hello"d[]; + int i; + + foreach(k, wchar d; s) + { + //printf("i = %d, k = %d, d = %x\n", i, k, d); + assert(k == i); + switch (i) + { + case 0: assert(d == 'h'); break; + case 1: assert(d == 'e'); break; + case 2: assert(d == 'l'); break; + case 3: assert(d == 'l'); break; + case 4: assert(d == 'o'); break; + default: assert(0); + } + i++; + } + assert(i == 5); + + s = "a\u1234\U000A0456b"; + i = 0; + foreach(k, wchar d; s) + { + //printf("i = %d, k = %d, d = %x\n", i, k, d); + switch (i) + { + case 0: assert(k == 0); assert(d == 'a'); break; + case 1: assert(k == 1); assert(d == 0x1234); break; + case 2: assert(k == 2); assert(d == 0xDA41); break; + case 3: assert(k == 2); assert(d == 0xDC56); break; + case 4: assert(k == 3); assert(d == 'b'); break; + default: assert(0); + } + i++; + } + assert(i == 5); +} diff --git a/libphobos/libdruntime/rt/aaA.d b/libphobos/libdruntime/rt/aaA.d index 5f251b741..f7f2e2829 100644 --- a/libphobos/libdruntime/rt/aaA.d +++ b/libphobos/libdruntime/rt/aaA.d @@ -72,7 +72,7 @@ struct Impl TypeInfo _keyti; Entry*[4] binit; // initial value of buckets[] - @property const(TypeInfo) keyti() const @safe pure nothrow + @property const(TypeInfo) keyti() const @safe pure nothrow @nogc { return _keyti; } } @@ -89,7 +89,7 @@ struct AA * GC won't be faced with misaligned pointers * in value. */ -size_t aligntsize(in size_t tsize) @safe pure nothrow +size_t aligntsize(in size_t tsize) @safe pure nothrow @nogc { version (D_LP64) { // align to 16 bytes on 64-bit @@ -105,7 +105,7 @@ extern (C): /**************************************************** * Determine number of entries in associative array. */ -size_t _aaLen(in AA aa) pure nothrow +size_t _aaLen(in AA aa) pure nothrow @nogc in { //printf("_aaLen()+\n"); @@ -175,8 +175,7 @@ body { if (key_hash == e.hash) { - auto c = keyti.compare(pkey, e + 1); - if (c == 0) + if (keyti.equals(pkey, e + 1)) goto Lret; } pe = &e.next; @@ -231,8 +230,7 @@ inout(void)* _aaGetRvalueX(inout AA aa, in TypeInfo keyti, in size_t valuesize, { if (key_hash == e.hash) { - auto c = keyti.compare(pkey, e + 1); - if (c == 0) + if (keyti.equals(pkey, e + 1)) return cast(inout void *)(e + 1) + keysize; } e = e.next; @@ -273,8 +271,7 @@ body { if (key_hash == e.hash) { - auto c = keyti.compare(pkey, e + 1); - if (c == 0) + if (keyti.equals(pkey, e + 1)) return cast(inout void *)(e + 1) + aligntsize(keyti.tsize); } e = e.next; @@ -304,8 +301,7 @@ bool _aaDelX(AA aa, in TypeInfo keyti, in void* pkey) { if (key_hash == e.hash) { - auto c = keyti.compare(pkey, e + 1); - if (c == 0) + if (keyti.equals(pkey, e + 1)) { *pe = e.next; aa.impl.nodes--; @@ -445,7 +441,7 @@ inout(ArrayRet_t) _aaKeys(inout AA aa, in size_t keysize) pure nothrow return *cast(inout ArrayRet_t*)(&a); } -unittest +pure nothrow unittest { int[string] aa; @@ -618,8 +614,7 @@ Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, vo } if (key_hash == e.hash) { - auto c = keyti.compare(pkey, e + 1); - if (c == 0) + if (keyti.equals(pkey, e + 1)) break; } pe = &e.next; @@ -631,7 +626,7 @@ Impl* _d_assocarrayliteralTX(const TypeInfo_AssociativeArray ti, void[] keys, vo } -const(TypeInfo_AssociativeArray) _aaUnwrapTypeInfo(const(TypeInfo) tiRaw) pure nothrow +const(TypeInfo_AssociativeArray) _aaUnwrapTypeInfo(const(TypeInfo) tiRaw) pure nothrow @nogc { const(TypeInfo)* p = &tiRaw; TypeInfo_AssociativeArray ti; @@ -723,8 +718,7 @@ int _aaEqual(in TypeInfo tiRaw, in AA e1, in AA e2) if (key_hash == f.hash) { //printf("hash equals\n"); - auto c = keyti.compare(pkey, f + 1); - if (c == 0) + if (keyti.equals(pkey, f + 1)) { // Found key in e2. Compare values //printf("key equals\n"); auto pvalue2 = cast(void *)(f + 1) + keysize; @@ -804,7 +798,7 @@ hash_t _aaGetHash(in AA* aa, in TypeInfo tiRaw) nothrow return h; } -unittest +pure nothrow unittest { string[int] key1 = [1: "true", 2: "false"]; string[int] key2 = [1: "false", 2: "true"]; @@ -831,7 +825,7 @@ unittest } // Issue 9852 -unittest +pure nothrow unittest { // Original test case (revised, original assert was wrong) int[string] a; @@ -863,7 +857,7 @@ struct Range } -Range _aaRange(AA aa) +Range _aaRange(AA aa) pure nothrow @nogc { typeof(return) res; if (aa.impl is null) @@ -882,13 +876,13 @@ Range _aaRange(AA aa) } -bool _aaRangeEmpty(Range r) +bool _aaRangeEmpty(Range r) pure nothrow @nogc { return r.current is null; } -void* _aaRangeFrontKey(Range r) +void* _aaRangeFrontKey(Range r) pure nothrow @nogc in { assert(r.current !is null); @@ -899,7 +893,7 @@ body } -void* _aaRangeFrontValue(Range r) +void* _aaRangeFrontValue(Range r) pure nothrow @nogc in { assert(r.current !is null); @@ -911,7 +905,7 @@ body } -void _aaRangePopFront(ref Range r) +void _aaRangePopFront(ref Range r) pure nothrow @nogc { if (r.current.next !is null) { diff --git a/libphobos/libdruntime/rt/adi.d b/libphobos/libdruntime/rt/adi.d index 11d732243..464d89161 100644 --- a/libphobos/libdruntime/rt/adi.d +++ b/libphobos/libdruntime/rt/adi.d @@ -22,19 +22,6 @@ private import core.stdc.stdlib; import core.memory; import rt.util.utf; - - enum BlkAttr : uint - { - FINALIZE = 0b0000_0001, - NO_SCAN = 0b0000_0010, - NO_MOVE = 0b0000_0100, - APPENDABLE = 0b0000_1000, - ALL_BITS = 0b1111_1111 - } - - extern (C) void* gc_malloc( size_t sz, uint ba = 0 ); - extern (C) void* gc_calloc( size_t sz, uint ba = 0 ); - extern (C) void gc_free( void* p ); } @@ -254,7 +241,7 @@ body //version (Windows) tmp = cast(byte*) alloca(szelem); //else - //tmp = gc_malloc(szelem); + //tmp = GC.malloc(szelem); } for (; lo < hi; lo += szelem, hi -= szelem) @@ -272,7 +259,7 @@ body //if (szelem > 16) // BUG: bad code is generate for delete pointer, tries // to call delclass. - //gc_free(tmp); + //GC.free(tmp); } } return a; @@ -314,6 +301,23 @@ unittest } } +private dchar[] mallocUTF32(C)(in C[] s) +{ + size_t j = 0; + auto p = cast(dchar*)malloc(dchar.sizeof * s.length); + auto r = p[0..s.length]; // r[] will never be longer than s[] + for (size_t i = 0; i < s.length; ) + { + dchar c = s[i]; + if (c >= 0x80) + c = decode(s, i); + else + i++; // c is ascii, no need for decode + r[j++] = c; + } + return r[0 .. j]; +} + /********************************************** * Sort array of chars. */ @@ -322,7 +326,7 @@ extern (C) char[] _adSortChar(char[] a) { if (a.length > 1) { - dchar[] da = cast(dchar[])toUTF32(a); + auto da = mallocUTF32(a); da.sort; size_t i = 0; foreach (dchar d; da) @@ -331,7 +335,7 @@ extern (C) char[] _adSortChar(char[] a) a[i .. i + t.length] = t[]; i += t.length; } - GC.free(da.ptr); + free(da.ptr); } return a; } @@ -344,7 +348,7 @@ extern (C) wchar[] _adSortWchar(wchar[] a) { if (a.length > 1) { - dchar[] da = cast(dchar[])toUTF32(a); + auto da = mallocUTF32(a); da.sort; size_t i = 0; foreach (dchar d; da) @@ -353,7 +357,7 @@ extern (C) wchar[] _adSortWchar(wchar[] a) a[i .. i + t.length] = t[]; i += t.length; } - GC.free(da.ptr); + free(da.ptr); } return a; } diff --git a/libphobos/libdruntime/rt/arraybyte.d b/libphobos/libdruntime/rt/arraybyte.d deleted file mode 100644 index 986e1ba6b..000000000 --- a/libphobos/libdruntime/rt/arraybyte.d +++ /dev/null @@ -1,1847 +0,0 @@ -/** - * Contains SSE2 and MMX versions of certain operations for char, byte, and - * ubyte ('a', 'g' and 'h' suffixes). - * - * Copyright: Copyright Digital Mars 2008 - 2010. - * License: Boost License 1.0. - * Authors: Walter Bright, based on code originally written by Burton Radons - */ - -/* Copyright Digital Mars 2008 - 2010. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -module rt.arraybyte; - -import core.cpuid; -import rt.util.array; - -// debug=PRINTF - -version (unittest) -{ - private import core.stdc.stdio : printf; - /* This is so unit tests will test every CPU variant - */ - int cpuid; - const int CPUID_MAX = 4; - @property bool mmx() { return cpuid == 1 && core.cpuid.mmx; } - @property bool sse() { return cpuid == 2 && core.cpuid.sse; } - @property bool sse2() { return cpuid == 3 && core.cpuid.sse2; } - @property bool amd3dnow() { return cpuid == 4 && core.cpuid.amd3dnow; } -} -else -{ - alias core.cpuid.mmx mmx; - alias core.cpuid.sse sse; - alias core.cpuid.sse2 sse2; - alias core.cpuid.amd3dnow amd3dnow; -} - -//version = log; - -alias byte T; - -extern (C) @trusted nothrow: - -/* ======================================================================== */ - - -/*********************** - * Computes: - * a[] = b[] + value - */ - -T[] _arraySliceExpAddSliceAssign_a(T[] a, T value, T[] b) -{ - return _arraySliceExpAddSliceAssign_g(a, value, b); -} - -T[] _arraySliceExpAddSliceAssign_h(T[] a, T value, T[] b) -{ - return _arraySliceExpAddSliceAssign_g(a, value, b); -} - -T[] _arraySliceExpAddSliceAssign_g(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceExpAddSliceAssign_g()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 1088% faster - if (sse2 && a.length >= 64) - { - auto n = aptr + (a.length & ~63); - - uint l = cast(ubyte)value * 0x01010101; - - if (((cast(uint) aptr | cast(uint) bptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM4, l; - pshufd XMM4, XMM4, 0; - - align 8; - startaddsse2u: - add ESI, 64; - movdqu XMM0, [EAX]; - movdqu XMM1, [EAX+16]; - movdqu XMM2, [EAX+32]; - movdqu XMM3, [EAX+48]; - add EAX, 64; - paddb XMM0, XMM4; - paddb XMM1, XMM4; - paddb XMM2, XMM4; - paddb XMM3, XMM4; - movdqu [ESI -64], XMM0; - movdqu [ESI+16-64], XMM1; - movdqu [ESI+32-64], XMM2; - movdqu [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startaddsse2u; - - mov aptr, ESI; - mov bptr, EAX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM4, l; - pshufd XMM4, XMM4, 0; - - align 8; - startaddsse2a: - add ESI, 64; - movdqa XMM0, [EAX]; - movdqa XMM1, [EAX+16]; - movdqa XMM2, [EAX+32]; - movdqa XMM3, [EAX+48]; - add EAX, 64; - paddb XMM0, XMM4; - paddb XMM1, XMM4; - paddb XMM2, XMM4; - paddb XMM3, XMM4; - movdqa [ESI -64], XMM0; - movdqa [ESI+16-64], XMM1; - movdqa [ESI+32-64], XMM2; - movdqa [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startaddsse2a; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - else - // MMX version is 1000% faster - if (mmx && a.length >= 32) - { - auto n = aptr + (a.length & ~31); - - uint l = cast(ubyte)value * 0x0101; - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd MM4, l; - pshufw MM4, MM4, 0; - - align 4; - startaddmmx: - add ESI, 32; - movq MM0, [EAX]; - movq MM1, [EAX+8]; - movq MM2, [EAX+16]; - movq MM3, [EAX+24]; - add EAX, 32; - paddb MM0, MM4; - paddb MM1, MM4; - paddb MM2, MM4; - paddb MM3, MM4; - movq [ESI -32], MM0; - movq [ESI+8 -32], MM1; - movq [ESI+16-32], MM2; - movq [ESI+24-32], MM3; - cmp ESI, EDI; - jb startaddmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - } - } - /* trying to be fair and treat normal 32-bit cpu the same way as we do - * the SIMD units, with unrolled asm. There's not enough registers, - * really. - */ - else - if (a.length >= 4) - { - - auto n = aptr + (a.length & ~3); - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov CL, value; - - align 4; - startadd386: - add ESI, 4; - mov DX, [EAX]; - mov BX, [EAX+2]; - add EAX, 4; - add BL, CL; - add BH, CL; - add DL, CL; - add DH, CL; - mov [ESI -4], DX; - mov [ESI+2 -4], BX; - cmp ESI, EDI; - jb startadd386; - - mov aptr, ESI; - mov bptr, EAX; - } - - } - } - - while (aptr < aend) - *aptr++ = cast(T)(*bptr++ + value); - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpAddSliceAssign_g unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] + 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + 6)) - { - printf("[%d]: %d != %d + 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] + c[] - */ - -T[] _arraySliceSliceAddSliceAssign_a(T[] a, T[] c, T[] b) -{ - return _arraySliceSliceAddSliceAssign_g(a, c, b); -} - -T[] _arraySliceSliceAddSliceAssign_h(T[] a, T[] c, T[] b) -{ - return _arraySliceSliceAddSliceAssign_g(a, c, b); -} - -T[] _arraySliceSliceAddSliceAssign_g(T[] a, T[] c, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - enforceTypedArraysConformable("vector operation", a, c); - - //printf("_arraySliceSliceAddSliceAssign_g()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - auto cptr = c.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 5739% faster - if (sse2 && a.length >= 64) - { - auto n = aptr + (a.length & ~63); - - if (((cast(uint) aptr | cast(uint) bptr | cast(uint) cptr) & 15) != 0) - { - version (log) printf("\tsse2 unaligned\n"); - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 8; - startaddlsse2u: - add ESI, 64; - movdqu XMM0, [EAX]; - movdqu XMM1, [EAX+16]; - movdqu XMM2, [EAX+32]; - movdqu XMM3, [EAX+48]; - add EAX, 64; - movdqu XMM4, [ECX]; - movdqu XMM5, [ECX+16]; - movdqu XMM6, [ECX+32]; - movdqu XMM7, [ECX+48]; - add ECX, 64; - paddb XMM0, XMM4; - paddb XMM1, XMM5; - paddb XMM2, XMM6; - paddb XMM3, XMM7; - movdqu [ESI -64], XMM0; - movdqu [ESI+16-64], XMM1; - movdqu [ESI+32-64], XMM2; - movdqu [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startaddlsse2u; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - else - { - version (log) printf("\tsse2 aligned\n"); - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 8; - startaddlsse2a: - add ESI, 64; - movdqa XMM0, [EAX]; - movdqa XMM1, [EAX+16]; - movdqa XMM2, [EAX+32]; - movdqa XMM3, [EAX+48]; - add EAX, 64; - movdqa XMM4, [ECX]; - movdqa XMM5, [ECX+16]; - movdqa XMM6, [ECX+32]; - movdqa XMM7, [ECX+48]; - add ECX, 64; - paddb XMM0, XMM4; - paddb XMM1, XMM5; - paddb XMM2, XMM6; - paddb XMM3, XMM7; - movdqa [ESI -64], XMM0; - movdqa [ESI+16-64], XMM1; - movdqa [ESI+32-64], XMM2; - movdqa [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startaddlsse2a; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - else - // MMX version is 4428% faster - if (mmx && a.length >= 32) - { - version (log) printf("\tmmx\n"); - auto n = aptr + (a.length & ~31); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startaddlmmx: - add ESI, 32; - movq MM0, [EAX]; - movq MM1, [EAX+8]; - movq MM2, [EAX+16]; - movq MM3, [EAX+24]; - add EAX, 32; - movq MM4, [ECX]; - movq MM5, [ECX+8]; - movq MM6, [ECX+16]; - movq MM7, [ECX+24]; - add ECX, 32; - paddb MM0, MM4; - paddb MM1, MM5; - paddb MM2, MM6; - paddb MM3, MM7; - movq [ESI -32], MM0; - movq [ESI+8 -32], MM1; - movq [ESI+16-32], MM2; - movq [ESI+24-32], MM3; - cmp ESI, EDI; - jb startaddlmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - - version (log) if (aptr < aend) printf("\tbase\n"); - while (aptr < aend) - *aptr++ = cast(T)(*bptr++ + *cptr++); - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceAddSliceAssign_g unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] + b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + b[i])) - { - printf("[%d]: %d != %d + %d\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] += value - */ - -T[] _arrayExpSliceAddass_a(T[] a, T value) -{ - return _arrayExpSliceAddass_g(a, value); -} - -T[] _arrayExpSliceAddass_h(T[] a, T value) -{ - return _arrayExpSliceAddass_g(a, value); -} - -T[] _arrayExpSliceAddass_g(T[] a, T value) -{ - //printf("_arrayExpSliceAddass_g(a.length = %d, value = %Lg)\n", a.length, cast(real)value); - auto aptr = a.ptr; - auto aend = aptr + a.length; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 1578% faster - if (sse2 && a.length >= 64) - { - auto n = aptr + (a.length & ~63); - - uint l = cast(ubyte)value * 0x01010101; - - if (((cast(uint) aptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - movd XMM4, l; - pshufd XMM4, XMM4, 0; - - align 8; - startaddasssse2u: - movdqu XMM0, [ESI]; - movdqu XMM1, [ESI+16]; - movdqu XMM2, [ESI+32]; - movdqu XMM3, [ESI+48]; - add ESI, 64; - paddb XMM0, XMM4; - paddb XMM1, XMM4; - paddb XMM2, XMM4; - paddb XMM3, XMM4; - movdqu [ESI -64], XMM0; - movdqu [ESI+16-64], XMM1; - movdqu [ESI+32-64], XMM2; - movdqu [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startaddasssse2u; - - mov aptr, ESI; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - movd XMM4, l; - pshufd XMM4, XMM4, 0; - - align 8; - startaddasssse2a: - movdqa XMM0, [ESI]; - movdqa XMM1, [ESI+16]; - movdqa XMM2, [ESI+32]; - movdqa XMM3, [ESI+48]; - add ESI, 64; - paddb XMM0, XMM4; - paddb XMM1, XMM4; - paddb XMM2, XMM4; - paddb XMM3, XMM4; - movdqa [ESI -64], XMM0; - movdqa [ESI+16-64], XMM1; - movdqa [ESI+32-64], XMM2; - movdqa [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startaddasssse2a; - - mov aptr, ESI; - } - } - } - else - // MMX version is 1721% faster - if (mmx && a.length >= 32) - { - - auto n = aptr + (a.length & ~31); - - uint l = cast(ubyte)value * 0x0101; - - asm - { - mov ESI, aptr; - mov EDI, n; - movd MM4, l; - pshufw MM4, MM4, 0; - - align 8; - startaddassmmx: - movq MM0, [ESI]; - movq MM1, [ESI+8]; - movq MM2, [ESI+16]; - movq MM3, [ESI+24]; - add ESI, 32; - paddb MM0, MM4; - paddb MM1, MM4; - paddb MM2, MM4; - paddb MM3, MM4; - movq [ESI -32], MM0; - movq [ESI+8 -32], MM1; - movq [ESI+16-32], MM2; - movq [ESI+24-32], MM3; - cmp ESI, EDI; - jb startaddassmmx; - - emms; - mov aptr, ESI; - } - } - } - - while (aptr < aend) - *aptr++ += value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceAddass_g unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] += 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + 6)) - { - printf("[%d]: %d != %d + 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] += b[] - */ - -T[] _arraySliceSliceAddass_a(T[] a, T[] b) -{ - return _arraySliceSliceAddass_g(a, b); -} - -T[] _arraySliceSliceAddass_h(T[] a, T[] b) -{ - return _arraySliceSliceAddass_g(a, b); -} - -T[] _arraySliceSliceAddass_g(T[] a, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceSliceAddass_g()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 4727% faster - if (sse2 && a.length >= 64) - { - auto n = aptr + (a.length & ~63); - - if (((cast(uint) aptr | cast(uint) bptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 8; - startaddasslsse2u: - movdqu XMM0, [ESI]; - movdqu XMM1, [ESI+16]; - movdqu XMM2, [ESI+32]; - movdqu XMM3, [ESI+48]; - add ESI, 64; - movdqu XMM4, [ECX]; - movdqu XMM5, [ECX+16]; - movdqu XMM6, [ECX+32]; - movdqu XMM7, [ECX+48]; - add ECX, 64; - paddb XMM0, XMM4; - paddb XMM1, XMM5; - paddb XMM2, XMM6; - paddb XMM3, XMM7; - movdqu [ESI -64], XMM0; - movdqu [ESI+16-64], XMM1; - movdqu [ESI+32-64], XMM2; - movdqu [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startaddasslsse2u; - - mov aptr, ESI; - mov bptr, ECX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 8; - startaddasslsse2a: - movdqa XMM0, [ESI]; - movdqa XMM1, [ESI+16]; - movdqa XMM2, [ESI+32]; - movdqa XMM3, [ESI+48]; - add ESI, 64; - movdqa XMM4, [ECX]; - movdqa XMM5, [ECX+16]; - movdqa XMM6, [ECX+32]; - movdqa XMM7, [ECX+48]; - add ECX, 64; - paddb XMM0, XMM4; - paddb XMM1, XMM5; - paddb XMM2, XMM6; - paddb XMM3, XMM7; - movdqa [ESI -64], XMM0; - movdqa [ESI+16-64], XMM1; - movdqa [ESI+32-64], XMM2; - movdqa [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startaddasslsse2a; - - mov aptr, ESI; - mov bptr, ECX; - } - } - } - else - // MMX version is 3059% faster - if (mmx && a.length >= 32) - { - - auto n = aptr + (a.length & ~31); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 8; - startaddasslmmx: - movq MM0, [ESI]; - movq MM1, [ESI+8]; - movq MM2, [ESI+16]; - movq MM3, [ESI+24]; - add ESI, 32; - movq MM4, [ECX]; - movq MM5, [ECX+8]; - movq MM6, [ECX+16]; - movq MM7, [ECX+24]; - add ECX, 32; - paddb MM0, MM4; - paddb MM1, MM5; - paddb MM2, MM6; - paddb MM3, MM7; - movq [ESI -32], MM0; - movq [ESI+8 -32], MM1; - movq [ESI+16-32], MM2; - movq [ESI+24-32], MM3; - cmp ESI, EDI; - jb startaddasslmmx; - - emms; - mov aptr, ESI; - mov bptr, ECX; - } - } - } - - while (aptr < aend) - *aptr++ += *bptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceAddass_g unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] += b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + b[i])) - { - printf("[%d]: %d != %d + %d\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - - -/*********************** - * Computes: - * a[] = b[] - value - */ - -T[] _arraySliceExpMinSliceAssign_a(T[] a, T value, T[] b) -{ - return _arraySliceExpMinSliceAssign_g(a, value, b); -} - -T[] _arraySliceExpMinSliceAssign_h(T[] a, T value, T[] b) -{ - return _arraySliceExpMinSliceAssign_g(a, value, b); -} - -T[] _arraySliceExpMinSliceAssign_g(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceExpMinSliceAssign_g()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 1189% faster - if (sse2 && a.length >= 64) - { - auto n = aptr + (a.length & ~63); - - uint l = cast(ubyte)value * 0x01010101; - - if (((cast(uint) aptr | cast(uint) bptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM4, l; - pshufd XMM4, XMM4, 0; - - align 8; - startsubsse2u: - add ESI, 64; - movdqu XMM0, [EAX]; - movdqu XMM1, [EAX+16]; - movdqu XMM2, [EAX+32]; - movdqu XMM3, [EAX+48]; - add EAX, 64; - psubb XMM0, XMM4; - psubb XMM1, XMM4; - psubb XMM2, XMM4; - psubb XMM3, XMM4; - movdqu [ESI -64], XMM0; - movdqu [ESI+16-64], XMM1; - movdqu [ESI+32-64], XMM2; - movdqu [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsubsse2u; - - mov aptr, ESI; - mov bptr, EAX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM4, l; - pshufd XMM4, XMM4, 0; - - align 8; - startsubsse2a: - add ESI, 64; - movdqa XMM0, [EAX]; - movdqa XMM1, [EAX+16]; - movdqa XMM2, [EAX+32]; - movdqa XMM3, [EAX+48]; - add EAX, 64; - psubb XMM0, XMM4; - psubb XMM1, XMM4; - psubb XMM2, XMM4; - psubb XMM3, XMM4; - movdqa [ESI -64], XMM0; - movdqa [ESI+16-64], XMM1; - movdqa [ESI+32-64], XMM2; - movdqa [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsubsse2a; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - else - // MMX version is 1079% faster - if (mmx && a.length >= 32) - { - auto n = aptr + (a.length & ~31); - - uint l = cast(ubyte)value * 0x0101; - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd MM4, l; - pshufw MM4, MM4, 0; - - align 4; - startsubmmx: - add ESI, 32; - movq MM0, [EAX]; - movq MM1, [EAX+8]; - movq MM2, [EAX+16]; - movq MM3, [EAX+24]; - add EAX, 32; - psubb MM0, MM4; - psubb MM1, MM4; - psubb MM2, MM4; - psubb MM3, MM4; - movq [ESI -32], MM0; - movq [ESI+8 -32], MM1; - movq [ESI+16-32], MM2; - movq [ESI+24-32], MM3; - cmp ESI, EDI; - jb startsubmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - } - } - // trying to be fair and treat normal 32-bit cpu the same way as we do the SIMD units, with unrolled asm. There's not enough registers, really. - else - if (a.length >= 4) - { - auto n = aptr + (a.length & ~3); - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov CL, value; - - align 4; - startsub386: - add ESI, 4; - mov DX, [EAX]; - mov BX, [EAX+2]; - add EAX, 4; - sub BL, CL; - sub BH, CL; - sub DL, CL; - sub DH, CL; - mov [ESI -4], DX; - mov [ESI+2 -4], BX; - cmp ESI, EDI; - jb startsub386; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - - while (aptr < aend) - *aptr++ = cast(T)(*bptr++ - value); - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpMinSliceAssign_g unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] = b[] - 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(b[i] - 6)) - { - printf("[%d]: %d != %d - 6\n", i, c[i], b[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = value - b[] - */ - -T[] _arrayExpSliceMinSliceAssign_a(T[] a, T[] b, T value) -{ - return _arrayExpSliceMinSliceAssign_g(a, b, value); -} - -T[] _arrayExpSliceMinSliceAssign_h(T[] a, T[] b, T value) -{ - return _arrayExpSliceMinSliceAssign_g(a, b, value); -} - -T[] _arrayExpSliceMinSliceAssign_g(T[] a, T[] b, T value) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arrayExpSliceMinSliceAssign_g()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 8748% faster - if (sse2 && a.length >= 64) - { - auto n = aptr + (a.length & ~63); - - uint l = cast(ubyte)value * 0x01010101; - - if (((cast(uint) aptr | cast(uint) bptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM4, l; - pshufd XMM4, XMM4, 0; - - align 8; - startsubrsse2u: - add ESI, 64; - movdqa XMM5, XMM4; - movdqa XMM6, XMM4; - movdqu XMM0, [EAX]; - movdqu XMM1, [EAX+16]; - psubb XMM5, XMM0; - psubb XMM6, XMM1; - movdqu [ESI -64], XMM5; - movdqu [ESI+16-64], XMM6; - movdqa XMM5, XMM4; - movdqa XMM6, XMM4; - movdqu XMM2, [EAX+32]; - movdqu XMM3, [EAX+48]; - add EAX, 64; - psubb XMM5, XMM2; - psubb XMM6, XMM3; - movdqu [ESI+32-64], XMM5; - movdqu [ESI+48-64], XMM6; - cmp ESI, EDI; - jb startsubrsse2u; - - mov aptr, ESI; - mov bptr, EAX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM4, l; - pshufd XMM4, XMM4, 0; - - align 8; - startsubrsse2a: - add ESI, 64; - movdqa XMM5, XMM4; - movdqa XMM6, XMM4; - movdqa XMM0, [EAX]; - movdqa XMM1, [EAX+16]; - psubb XMM5, XMM0; - psubb XMM6, XMM1; - movdqa [ESI -64], XMM5; - movdqa [ESI+16-64], XMM6; - movdqa XMM5, XMM4; - movdqa XMM6, XMM4; - movdqa XMM2, [EAX+32]; - movdqa XMM3, [EAX+48]; - add EAX, 64; - psubb XMM5, XMM2; - psubb XMM6, XMM3; - movdqa [ESI+32-64], XMM5; - movdqa [ESI+48-64], XMM6; - cmp ESI, EDI; - jb startsubrsse2a; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - else - // MMX version is 7397% faster - if (mmx && a.length >= 32) - { - auto n = aptr + (a.length & ~31); - - uint l = cast(ubyte)value * 0x0101; - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd MM4, l; - pshufw MM4, MM4, 0; - - align 4; - startsubrmmx: - add ESI, 32; - movq MM5, MM4; - movq MM6, MM4; - movq MM0, [EAX]; - movq MM1, [EAX+8]; - psubb MM5, MM0; - psubb MM6, MM1; - movq [ESI -32], MM5; - movq [ESI+8 -32], MM6; - movq MM5, MM4; - movq MM6, MM4; - movq MM2, [EAX+16]; - movq MM3, [EAX+24]; - add EAX, 32; - psubb MM5, MM2; - psubb MM6, MM3; - movq [ESI+16-32], MM5; - movq [ESI+24-32], MM6; - cmp ESI, EDI; - jb startsubrmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - } - } - - } - - while (aptr < aend) - *aptr++ = cast(T)(value - *bptr++); - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMinSliceAssign_g unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] = 6 - b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(6 - b[i])) - { - printf("[%d]: %d != 6 - %d\n", i, c[i], b[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] - c[] - */ - -T[] _arraySliceSliceMinSliceAssign_a(T[] a, T[] c, T[] b) -{ - return _arraySliceSliceMinSliceAssign_g(a, c, b); -} - -T[] _arraySliceSliceMinSliceAssign_h(T[] a, T[] c, T[] b) -{ - return _arraySliceSliceMinSliceAssign_g(a, c, b); -} - -T[] _arraySliceSliceMinSliceAssign_g(T[] a, T[] c, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - enforceTypedArraysConformable("vector operation", a, c); - - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - auto cptr = c.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 5756% faster - if (sse2 && a.length >= 64) - { - auto n = aptr + (a.length & ~63); - - if (((cast(uint) aptr | cast(uint) bptr | cast(uint) cptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 8; - startsublsse2u: - add ESI, 64; - movdqu XMM0, [EAX]; - movdqu XMM1, [EAX+16]; - movdqu XMM2, [EAX+32]; - movdqu XMM3, [EAX+48]; - add EAX, 64; - movdqu XMM4, [ECX]; - movdqu XMM5, [ECX+16]; - movdqu XMM6, [ECX+32]; - movdqu XMM7, [ECX+48]; - add ECX, 64; - psubb XMM0, XMM4; - psubb XMM1, XMM5; - psubb XMM2, XMM6; - psubb XMM3, XMM7; - movdqu [ESI -64], XMM0; - movdqu [ESI+16-64], XMM1; - movdqu [ESI+32-64], XMM2; - movdqu [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsublsse2u; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 8; - startsublsse2a: - add ESI, 64; - movdqa XMM0, [EAX]; - movdqa XMM1, [EAX+16]; - movdqa XMM2, [EAX+32]; - movdqa XMM3, [EAX+48]; - add EAX, 64; - movdqa XMM4, [ECX]; - movdqa XMM5, [ECX+16]; - movdqa XMM6, [ECX+32]; - movdqa XMM7, [ECX+48]; - add ECX, 64; - psubb XMM0, XMM4; - psubb XMM1, XMM5; - psubb XMM2, XMM6; - psubb XMM3, XMM7; - movdqa [ESI -64], XMM0; - movdqa [ESI+16-64], XMM1; - movdqa [ESI+32-64], XMM2; - movdqa [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsublsse2a; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - else - // MMX version is 4428% faster - if (mmx && a.length >= 32) - { - auto n = aptr + (a.length & ~31); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 8; - startsublmmx: - add ESI, 32; - movq MM0, [EAX]; - movq MM1, [EAX+8]; - movq MM2, [EAX+16]; - movq MM3, [EAX+24]; - add EAX, 32; - movq MM4, [ECX]; - movq MM5, [ECX+8]; - movq MM6, [ECX+16]; - movq MM7, [ECX+24]; - add ECX, 32; - psubb MM0, MM4; - psubb MM1, MM5; - psubb MM2, MM6; - psubb MM3, MM7; - movq [ESI -32], MM0; - movq [ESI+8 -32], MM1; - movq [ESI+16-32], MM2; - movq [ESI+24-32], MM3; - cmp ESI, EDI; - jb startsublmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - - while (aptr < aend) - *aptr++ = cast(T)(*bptr++ - *cptr++); - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceMinSliceAssign_g unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] - b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] - b[i])) - { - printf("[%d]: %d != %d - %d\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] -= value - */ - -T[] _arrayExpSliceMinass_a(T[] a, T value) -{ - return _arrayExpSliceMinass_g(a, value); -} - -T[] _arrayExpSliceMinass_h(T[] a, T value) -{ - return _arrayExpSliceMinass_g(a, value); -} - -T[] _arrayExpSliceMinass_g(T[] a, T value) -{ - //printf("_arrayExpSliceMinass_g(a.length = %d, value = %Lg)\n", a.length, cast(real)value); - auto aptr = a.ptr; - auto aend = aptr + a.length; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 1577% faster - if (sse2 && a.length >= 64) - { - auto n = aptr + (a.length & ~63); - - uint l = cast(ubyte)value * 0x01010101; - - if (((cast(uint) aptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - movd XMM4, l; - pshufd XMM4, XMM4, 0; - - align 8; - startsubasssse2u: - movdqu XMM0, [ESI]; - movdqu XMM1, [ESI+16]; - movdqu XMM2, [ESI+32]; - movdqu XMM3, [ESI+48]; - add ESI, 64; - psubb XMM0, XMM4; - psubb XMM1, XMM4; - psubb XMM2, XMM4; - psubb XMM3, XMM4; - movdqu [ESI -64], XMM0; - movdqu [ESI+16-64], XMM1; - movdqu [ESI+32-64], XMM2; - movdqu [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsubasssse2u; - - mov aptr, ESI; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - movd XMM4, l; - pshufd XMM4, XMM4, 0; - - align 8; - startsubasssse2a: - movdqa XMM0, [ESI]; - movdqa XMM1, [ESI+16]; - movdqa XMM2, [ESI+32]; - movdqa XMM3, [ESI+48]; - add ESI, 64; - psubb XMM0, XMM4; - psubb XMM1, XMM4; - psubb XMM2, XMM4; - psubb XMM3, XMM4; - movdqa [ESI -64], XMM0; - movdqa [ESI+16-64], XMM1; - movdqa [ESI+32-64], XMM2; - movdqa [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsubasssse2a; - - mov aptr, ESI; - } - } - } - else - // MMX version is 1577% faster - if (mmx && a.length >= 32) - { - - auto n = aptr + (a.length & ~31); - - uint l = cast(ubyte)value * 0x0101; - - asm - { - mov ESI, aptr; - mov EDI, n; - movd MM4, l; - pshufw MM4, MM4, 0; - - align 8; - startsubassmmx: - movq MM0, [ESI]; - movq MM1, [ESI+8]; - movq MM2, [ESI+16]; - movq MM3, [ESI+24]; - add ESI, 32; - psubb MM0, MM4; - psubb MM1, MM4; - psubb MM2, MM4; - psubb MM3, MM4; - movq [ESI -32], MM0; - movq [ESI+8 -32], MM1; - movq [ESI+16-32], MM2; - movq [ESI+24-32], MM3; - cmp ESI, EDI; - jb startsubassmmx; - - emms; - mov aptr, ESI; - } - } - } - - while (aptr < aend) - *aptr++ -= value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMinass_g unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] -= 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] - 6)) - { - printf("[%d]: %d != %d - 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] -= b[] - */ - -T[] _arraySliceSliceMinass_a(T[] a, T[] b) -{ - return _arraySliceSliceMinass_g(a, b); -} - -T[] _arraySliceSliceMinass_h(T[] a, T[] b) -{ - return _arraySliceSliceMinass_g(a, b); -} - -T[] _arraySliceSliceMinass_g(T[] a, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceSliceMinass_g()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 4800% faster - if (sse2 && a.length >= 64) - { - auto n = aptr + (a.length & ~63); - - if (((cast(uint) aptr | cast(uint) bptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 8; - startsubasslsse2u: - movdqu XMM0, [ESI]; - movdqu XMM1, [ESI+16]; - movdqu XMM2, [ESI+32]; - movdqu XMM3, [ESI+48]; - add ESI, 64; - movdqu XMM4, [ECX]; - movdqu XMM5, [ECX+16]; - movdqu XMM6, [ECX+32]; - movdqu XMM7, [ECX+48]; - add ECX, 64; - psubb XMM0, XMM4; - psubb XMM1, XMM5; - psubb XMM2, XMM6; - psubb XMM3, XMM7; - movdqu [ESI -64], XMM0; - movdqu [ESI+16-64], XMM1; - movdqu [ESI+32-64], XMM2; - movdqu [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsubasslsse2u; - - mov aptr, ESI; - mov bptr, ECX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 8; - startsubasslsse2a: - movdqa XMM0, [ESI]; - movdqa XMM1, [ESI+16]; - movdqa XMM2, [ESI+32]; - movdqa XMM3, [ESI+48]; - add ESI, 64; - movdqa XMM4, [ECX]; - movdqa XMM5, [ECX+16]; - movdqa XMM6, [ECX+32]; - movdqa XMM7, [ECX+48]; - add ECX, 64; - psubb XMM0, XMM4; - psubb XMM1, XMM5; - psubb XMM2, XMM6; - psubb XMM3, XMM7; - movdqa [ESI -64], XMM0; - movdqa [ESI+16-64], XMM1; - movdqa [ESI+32-64], XMM2; - movdqa [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsubasslsse2a; - - mov aptr, ESI; - mov bptr, ECX; - } - } - } - else - // MMX version is 3107% faster - if (mmx && a.length >= 32) - { - - auto n = aptr + (a.length & ~31); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 8; - startsubasslmmx: - movq MM0, [ESI]; - movq MM1, [ESI+8]; - movq MM2, [ESI+16]; - movq MM3, [ESI+24]; - add ESI, 32; - movq MM4, [ECX]; - movq MM5, [ECX+8]; - movq MM6, [ECX+16]; - movq MM7, [ECX+24]; - add ECX, 32; - psubb MM0, MM4; - psubb MM1, MM5; - psubb MM2, MM6; - psubb MM3, MM7; - movq [ESI -32], MM0; - movq [ESI+8 -32], MM1; - movq [ESI+16-32], MM2; - movq [ESI+24-32], MM3; - cmp ESI, EDI; - jb startsubasslmmx; - - emms; - mov aptr, ESI; - mov bptr, ECX; - } - } - } - - while (aptr < aend) - *aptr++ -= *bptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceMinass_g unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] -= b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] - b[i])) - { - printf("[%d]: %d != %d - %d\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} diff --git a/libphobos/libdruntime/rt/arraydouble.d b/libphobos/libdruntime/rt/arraydouble.d deleted file mode 100644 index f8461d2f9..000000000 --- a/libphobos/libdruntime/rt/arraydouble.d +++ /dev/null @@ -1,1668 +0,0 @@ -/** - * Contains SSE2 and MMX versions of certain operations for double. - * - * Copyright: Copyright Digital Mars 2008 - 2010. - * License: Boost License 1.0. - * Authors: Walter Bright, based on code originally written by Burton Radons - */ - -/* Copyright Digital Mars 2008 - 2010. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -module rt.arraydouble; - -// debug=PRINTF - -private import core.cpuid; -import rt.util.array; - -version (unittest) -{ - private import core.stdc.stdio : printf; - /* This is so unit tests will test every CPU variant - */ - int cpuid; - const int CPUID_MAX = 5; - @property bool mmx() { return cpuid == 1 && core.cpuid.mmx; } - @property bool sse() { return cpuid == 2 && core.cpuid.sse; } - @property bool sse2() { return cpuid == 3 && core.cpuid.sse2; } - @property bool amd3dnow() { return cpuid == 4 && core.cpuid.amd3dnow; } -} -else -{ - alias core.cpuid.mmx mmx; - alias core.cpuid.sse sse; - alias core.cpuid.sse2 sse2; - alias core.cpuid.amd3dnow amd3dnow; -} - -//version = log; - -/* Performance figures measured by Burton Radons - */ - -alias double T; - -extern (C) @trusted nothrow: - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] + c[] - */ - -T[] _arraySliceSliceAddSliceAssign_d(T[] a, T[] c, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - enforceTypedArraysConformable("vector operation", a, c); - - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - auto cptr = c.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 version is 333% faster - if (sse2 && b.length >= 16) - { - auto n = aptr + (b.length & ~15); - - // Unaligned case - asm - { - mov EAX, bptr; // left operand - mov ECX, cptr; // right operand - mov ESI, aptr; // destination operand - mov EDI, n; // end comparison - - align 8; - startsseloopb: - movupd XMM0, [EAX]; - movupd XMM1, [EAX+16]; - movupd XMM2, [EAX+32]; - movupd XMM3, [EAX+48]; - add EAX, 64; - movupd XMM4, [ECX]; - movupd XMM5, [ECX+16]; - movupd XMM6, [ECX+32]; - movupd XMM7, [ECX+48]; - add ESI, 64; - addpd XMM0, XMM4; - addpd XMM1, XMM5; - addpd XMM2, XMM6; - addpd XMM3, XMM7; - add ECX, 64; - movupd [ESI+ 0-64], XMM0; - movupd [ESI+16-64], XMM1; - movupd [ESI+32-64], XMM2; - movupd [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloopb; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - - // Handle remainder - while (aptr < aend) - *aptr++ = *bptr++ + *cptr++; - - return a; -} - - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceAddSliceAssign_d unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] + b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + b[i])) - { - printf("[%d]: %g != %g + %g\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] - c[] - */ - -T[] _arraySliceSliceMinSliceAssign_d(T[] a, T[] c, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - enforceTypedArraysConformable("vector operation", a, c); - - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - auto cptr = c.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 version is 324% faster - if (sse2 && b.length >= 8) - { - auto n = aptr + (b.length & ~7); - - // Unaligned case - asm - { - mov EAX, bptr; // left operand - mov ECX, cptr; // right operand - mov ESI, aptr; // destination operand - mov EDI, n; // end comparison - - align 8; - startsseloopb: - movupd XMM0, [EAX]; - movupd XMM1, [EAX+16]; - movupd XMM2, [EAX+32]; - movupd XMM3, [EAX+48]; - add EAX, 64; - movupd XMM4, [ECX]; - movupd XMM5, [ECX+16]; - movupd XMM6, [ECX+32]; - movupd XMM7, [ECX+48]; - add ESI, 64; - subpd XMM0, XMM4; - subpd XMM1, XMM5; - subpd XMM2, XMM6; - subpd XMM3, XMM7; - add ECX, 64; - movupd [ESI+ 0-64], XMM0; - movupd [ESI+16-64], XMM1; - movupd [ESI+32-64], XMM2; - movupd [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloopb; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - - // Handle remainder - while (aptr < aend) - *aptr++ = *bptr++ - *cptr++; - - return a; -} - - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceMinSliceAssign_d unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] - b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] - b[i])) - { - printf("[%d]: %g != %g - %g\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] + value - */ - -T[] _arraySliceExpAddSliceAssign_d(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceExpAddSliceAssign_d()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 version is 305% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - // Unaligned case - asm - { - mov EAX, bptr; - mov ESI, aptr; - mov EDI, n; - movsd XMM4, value; - shufpd XMM4, XMM4, 0; - - align 8; - startsseloop: - add ESI, 64; - movupd XMM0, [EAX]; - movupd XMM1, [EAX+16]; - movupd XMM2, [EAX+32]; - movupd XMM3, [EAX+48]; - add EAX, 64; - addpd XMM0, XMM4; - addpd XMM1, XMM4; - addpd XMM2, XMM4; - addpd XMM3, XMM4; - movupd [ESI+ 0-64], XMM0; - movupd [ESI+16-64], XMM1; - movupd [ESI+32-64], XMM2; - movupd [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloop; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - - while (aptr < aend) - *aptr++ = *bptr++ + value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpAddSliceAssign_d unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] + 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + 6)) - { - printf("[%d]: %g != %g + 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] += value - */ - -T[] _arrayExpSliceAddass_d(T[] a, T value) -{ - //printf("_arrayExpSliceAddass_d(a.length = %d, value = %Lg)\n", a.length, cast(real)value); - auto aptr = a.ptr; - auto aend = aptr + a.length; - - version (D_InlineAsm_X86) - { - // SSE2 version is 114% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - if (aptr < n) - - // Unaligned case - asm - { - mov ESI, aptr; - mov EDI, n; - movsd XMM4, value; - shufpd XMM4, XMM4, 0; - - align 8; - startsseloopa: - movupd XMM0, [ESI]; - movupd XMM1, [ESI+16]; - movupd XMM2, [ESI+32]; - movupd XMM3, [ESI+48]; - add ESI, 64; - addpd XMM0, XMM4; - addpd XMM1, XMM4; - addpd XMM2, XMM4; - addpd XMM3, XMM4; - movupd [ESI+ 0-64], XMM0; - movupd [ESI+16-64], XMM1; - movupd [ESI+32-64], XMM2; - movupd [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloopa; - - mov aptr, ESI; - } - } - } - - while (aptr < aend) - *aptr++ += value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceAddass_d unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] += 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + 6)) - { - printf("[%d]: %g != %g + 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] += b[] - */ - -T[] _arraySliceSliceAddass_d(T[] a, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceSliceAddass_d()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 version is 183% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - // Unaligned case - asm - { - mov ECX, bptr; // right operand - mov ESI, aptr; // destination operand - mov EDI, n; // end comparison - - align 8; - startsseloopb: - movupd XMM0, [ESI]; - movupd XMM1, [ESI+16]; - movupd XMM2, [ESI+32]; - movupd XMM3, [ESI+48]; - add ESI, 64; - movupd XMM4, [ECX]; - movupd XMM5, [ECX+16]; - movupd XMM6, [ECX+32]; - movupd XMM7, [ECX+48]; - add ECX, 64; - addpd XMM0, XMM4; - addpd XMM1, XMM5; - addpd XMM2, XMM6; - addpd XMM3, XMM7; - movupd [ESI+ 0-64], XMM0; - movupd [ESI+16-64], XMM1; - movupd [ESI+32-64], XMM2; - movupd [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloopb; - - mov aptr, ESI; - mov bptr, ECX; - } - } - } - - while (aptr < aend) - *aptr++ += *bptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceAddass_d unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] += b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + b[i])) - { - printf("[%d]: %g != %g + %g\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] - value - */ - -T[] _arraySliceExpMinSliceAssign_d(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceExpMinSliceAssign_d()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 version is 305% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - // Unaligned case - asm - { - mov EAX, bptr; - mov ESI, aptr; - mov EDI, n; - movsd XMM4, value; - shufpd XMM4, XMM4, 0; - - align 8; - startsseloop: - add ESI, 64; - movupd XMM0, [EAX]; - movupd XMM1, [EAX+16]; - movupd XMM2, [EAX+32]; - movupd XMM3, [EAX+48]; - add EAX, 64; - subpd XMM0, XMM4; - subpd XMM1, XMM4; - subpd XMM2, XMM4; - subpd XMM3, XMM4; - movupd [ESI+ 0-64], XMM0; - movupd [ESI+16-64], XMM1; - movupd [ESI+32-64], XMM2; - movupd [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloop; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - - while (aptr < aend) - *aptr++ = *bptr++ - value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpMinSliceAssign_d unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] - 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] - 6)) - { - printf("[%d]: %g != %g - 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = value - b[] - */ - -T[] _arrayExpSliceMinSliceAssign_d(T[] a, T[] b, T value) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arrayExpSliceMinSliceAssign_d()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 version is 66% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - // Unaligned case - asm - { - mov EAX, bptr; - mov ESI, aptr; - mov EDI, n; - movsd XMM4, value; - shufpd XMM4, XMM4, 0; - - align 8; - startsseloop: - add ESI, 64; - movapd XMM5, XMM4; - movapd XMM6, XMM4; - movupd XMM0, [EAX]; - movupd XMM1, [EAX+16]; - movupd XMM2, [EAX+32]; - movupd XMM3, [EAX+48]; - add EAX, 64; - subpd XMM5, XMM0; - subpd XMM6, XMM1; - movupd [ESI+ 0-64], XMM5; - movupd [ESI+16-64], XMM6; - movapd XMM5, XMM4; - movapd XMM6, XMM4; - subpd XMM5, XMM2; - subpd XMM6, XMM3; - movupd [ESI+32-64], XMM5; - movupd [ESI+48-64], XMM6; - cmp ESI, EDI; - jb startsseloop; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - - while (aptr < aend) - *aptr++ = value - *bptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMinSliceAssign_d unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = 6 - a[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(6 - a[i])) - { - printf("[%d]: %g != 6 - %g\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] -= value - */ - -T[] _arrayExpSliceMinass_d(T[] a, T value) -{ - //printf("_arrayExpSliceMinass_d(a.length = %d, value = %Lg)\n", a.length, cast(real)value); - auto aptr = a.ptr; - auto aend = aptr + a.length; - - version (D_InlineAsm_X86) - { - // SSE2 version is 115% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - if (aptr < n) - - // Unaligned case - asm - { - mov ESI, aptr; - mov EDI, n; - movsd XMM4, value; - shufpd XMM4, XMM4, 0; - - align 8; - startsseloopa: - movupd XMM0, [ESI]; - movupd XMM1, [ESI+16]; - movupd XMM2, [ESI+32]; - movupd XMM3, [ESI+48]; - add ESI, 64; - subpd XMM0, XMM4; - subpd XMM1, XMM4; - subpd XMM2, XMM4; - subpd XMM3, XMM4; - movupd [ESI+ 0-64], XMM0; - movupd [ESI+16-64], XMM1; - movupd [ESI+32-64], XMM2; - movupd [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloopa; - - mov aptr, ESI; - } - } - } - - while (aptr < aend) - *aptr++ -= value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMinass_d unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] -= 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] - 6)) - { - printf("[%d]: %g != %g - 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] -= b[] - */ - -T[] _arraySliceSliceMinass_d(T[] a, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceSliceMinass_d()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 version is 183% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - // Unaligned case - asm - { - mov ECX, bptr; // right operand - mov ESI, aptr; // destination operand - mov EDI, n; // end comparison - - align 8; - startsseloopb: - movupd XMM0, [ESI]; - movupd XMM1, [ESI+16]; - movupd XMM2, [ESI+32]; - movupd XMM3, [ESI+48]; - add ESI, 64; - movupd XMM4, [ECX]; - movupd XMM5, [ECX+16]; - movupd XMM6, [ECX+32]; - movupd XMM7, [ECX+48]; - add ECX, 64; - subpd XMM0, XMM4; - subpd XMM1, XMM5; - subpd XMM2, XMM6; - subpd XMM3, XMM7; - movupd [ESI+ 0-64], XMM0; - movupd [ESI+16-64], XMM1; - movupd [ESI+32-64], XMM2; - movupd [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloopb; - - mov aptr, ESI; - mov bptr, ECX; - } - } - } - - while (aptr < aend) - *aptr++ -= *bptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMinass_d unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] -= 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] - 6)) - { - printf("[%d]: %g != %g - 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] * value - */ - -T[] _arraySliceExpMulSliceAssign_d(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceExpMulSliceAssign_d()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 version is 304% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - // Unaligned case - asm - { - mov EAX, bptr; - mov ESI, aptr; - mov EDI, n; - movsd XMM4, value; - shufpd XMM4, XMM4, 0; - - align 8; - startsseloop: - add ESI, 64; - movupd XMM0, [EAX]; - movupd XMM1, [EAX+16]; - movupd XMM2, [EAX+32]; - movupd XMM3, [EAX+48]; - add EAX, 64; - mulpd XMM0, XMM4; - mulpd XMM1, XMM4; - mulpd XMM2, XMM4; - mulpd XMM3, XMM4; - movupd [ESI+ 0-64], XMM0; - movupd [ESI+16-64], XMM1; - movupd [ESI+32-64], XMM2; - movupd [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloop; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - - while (aptr < aend) - *aptr++ = *bptr++ * value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpMulSliceAssign_d unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] * 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] * 6)) - { - printf("[%d]: %g != %g * 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] * c[] - */ - -T[] _arraySliceSliceMulSliceAssign_d(T[] a, T[] c, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - enforceTypedArraysConformable("vector operation", a, c); - - //printf("_arraySliceSliceMulSliceAssign_d()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - auto cptr = c.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 version is 329% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - // Unaligned case - asm - { - mov EAX, bptr; // left operand - mov ECX, cptr; // right operand - mov ESI, aptr; // destination operand - mov EDI, n; // end comparison - - align 8; - startsseloopb: - movupd XMM0, [EAX]; - movupd XMM1, [EAX+16]; - movupd XMM2, [EAX+32]; - movupd XMM3, [EAX+48]; - add ESI, 64; - movupd XMM4, [ECX]; - movupd XMM5, [ECX+16]; - movupd XMM6, [ECX+32]; - movupd XMM7, [ECX+48]; - add EAX, 64; - mulpd XMM0, XMM4; - mulpd XMM1, XMM5; - mulpd XMM2, XMM6; - mulpd XMM3, XMM7; - add ECX, 64; - movupd [ESI+ 0-64], XMM0; - movupd [ESI+16-64], XMM1; - movupd [ESI+32-64], XMM2; - movupd [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloopb; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - - while (aptr < aend) - *aptr++ = *bptr++ * *cptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceMulSliceAssign_d unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] * b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] * b[i])) - { - printf("[%d]: %g != %g * %g\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] *= value - */ - -T[] _arrayExpSliceMulass_d(T[] a, T value) -{ - //printf("_arrayExpSliceMulass_d(a.length = %d, value = %Lg)\n", a.length, cast(real)value); - auto aptr = a.ptr; - auto aend = aptr + a.length; - - version (D_InlineAsm_X86) - { - // SSE2 version is 109% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - if (aptr < n) - - // Unaligned case - asm - { - mov ESI, aptr; - mov EDI, n; - movsd XMM4, value; - shufpd XMM4, XMM4, 0; - - align 8; - startsseloopa: - movupd XMM0, [ESI]; - movupd XMM1, [ESI+16]; - movupd XMM2, [ESI+32]; - movupd XMM3, [ESI+48]; - add ESI, 64; - mulpd XMM0, XMM4; - mulpd XMM1, XMM4; - mulpd XMM2, XMM4; - mulpd XMM3, XMM4; - movupd [ESI+ 0-64], XMM0; - movupd [ESI+16-64], XMM1; - movupd [ESI+32-64], XMM2; - movupd [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloopa; - - mov aptr, ESI; - } - } - } - - while (aptr < aend) - *aptr++ *= value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMulass_d unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] *= 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] * 6)) - { - printf("[%d]: %g != %g * 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] *= b[] - */ - -T[] _arraySliceSliceMulass_d(T[] a, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceSliceMulass_d()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 version is 205% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - // Unaligned case - asm - { - mov ECX, bptr; // right operand - mov ESI, aptr; // destination operand - mov EDI, n; // end comparison - - align 8; - startsseloopb: - movupd XMM0, [ESI]; - movupd XMM1, [ESI+16]; - movupd XMM2, [ESI+32]; - movupd XMM3, [ESI+48]; - add ESI, 64; - movupd XMM4, [ECX]; - movupd XMM5, [ECX+16]; - movupd XMM6, [ECX+32]; - movupd XMM7, [ECX+48]; - add ECX, 64; - mulpd XMM0, XMM4; - mulpd XMM1, XMM5; - mulpd XMM2, XMM6; - mulpd XMM3, XMM7; - movupd [ESI+ 0-64], XMM0; - movupd [ESI+16-64], XMM1; - movupd [ESI+32-64], XMM2; - movupd [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloopb; - - mov aptr, ESI; - mov bptr, ECX; - } - } - } - - while (aptr < aend) - *aptr++ *= *bptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMulass_d unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] *= 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] * 6)) - { - printf("[%d]: %g != %g * 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] / value - */ - -T[] _arraySliceExpDivSliceAssign_d(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceExpDivSliceAssign_d()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - /* Multiplying by the reciprocal is faster, but does - * not produce as accurate an answer. - */ - T recip = cast(T)1 / value; - - version (D_InlineAsm_X86) - { - // SSE2 version is 299% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - // Unaligned case - asm - { - mov EAX, bptr; - mov ESI, aptr; - mov EDI, n; - movsd XMM4, recip; - //movsd XMM4, value - //rcpsd XMM4, XMM4 - shufpd XMM4, XMM4, 0; - - align 8; - startsseloop: - add ESI, 64; - movupd XMM0, [EAX]; - movupd XMM1, [EAX+16]; - movupd XMM2, [EAX+32]; - movupd XMM3, [EAX+48]; - add EAX, 64; - mulpd XMM0, XMM4; - mulpd XMM1, XMM4; - mulpd XMM2, XMM4; - mulpd XMM3, XMM4; - //divpd XMM0, XMM4; - //divpd XMM1, XMM4; - //divpd XMM2, XMM4; - //divpd XMM3, XMM4; - movupd [ESI+ 0-64], XMM0; - movupd [ESI+16-64], XMM1; - movupd [ESI+32-64], XMM2; - movupd [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloop; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - - while (aptr < aend) - { - *aptr++ = *bptr++ / value; - //*aptr++ = *bptr++ * recip; - } - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpDivSliceAssign_d unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] / 8; - - for (int i = 0; i < dim; i++) - { - //printf("[%d]: %g ?= %g / 8\n", i, c[i], a[i]); - if (c[i] != cast(T)(a[i] / 8)) - { - printf("[%d]: %g != %g / 8\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] /= value - */ - -T[] _arrayExpSliceDivass_d(T[] a, T value) -{ - //printf("_arrayExpSliceDivass_d(a.length = %d, value = %Lg)\n", a.length, cast(real)value); - auto aptr = a.ptr; - auto aend = aptr + a.length; - - /* Multiplying by the reciprocal is faster, but does - * not produce as accurate an answer. - */ - T recip = cast(T)1 / value; - - version (D_InlineAsm_X86) - { - // SSE2 version is 65% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - // Unaligned case - asm - { - mov ESI, aptr; - mov EDI, n; - movsd XMM4, recip; - //movsd XMM4, value - //rcpsd XMM4, XMM4 - shufpd XMM4, XMM4, 0; - - align 8; - startsseloopa: - movupd XMM0, [ESI]; - movupd XMM1, [ESI+16]; - movupd XMM2, [ESI+32]; - movupd XMM3, [ESI+48]; - add ESI, 64; - mulpd XMM0, XMM4; - mulpd XMM1, XMM4; - mulpd XMM2, XMM4; - mulpd XMM3, XMM4; - //divpd XMM0, XMM4; - //divpd XMM1, XMM4; - //divpd XMM2, XMM4; - //divpd XMM3, XMM4; - movupd [ESI+ 0-64], XMM0; - movupd [ESI+16-64], XMM1; - movupd [ESI+32-64], XMM2; - movupd [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloopa; - - mov aptr, ESI; - } - } - } - - while (aptr < aend) - *aptr++ *= recip; - - return a; -} - - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceDivass_d unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] /= 8; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] / 8)) - { - printf("[%d]: %g != %g / 8\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] -= b[] * value - */ - -T[] _arraySliceExpMulSliceMinass_d(T[] a, T value, T[] b) -{ - return _arraySliceExpMulSliceAddass_d(a, -value, b); -} - -/*********************** - * Computes: - * a[] += b[] * value - */ - -T[] _arraySliceExpMulSliceAddass_d(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - // Handle remainder - while (aptr < aend) - *aptr++ += *bptr++ * value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpMulSliceAddass_d unittest\n"); - - cpuid = 1; - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 1; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - b[] = c[]; - c[] += a[] * 6; - - for (int i = 0; i < dim; i++) - { - //printf("[%d]: %g ?= %g + %g * 6\n", i, c[i], b[i], a[i]); - if (c[i] != cast(T)(b[i] + a[i] * 6)) - { - printf("[%d]: %g ?= %g + %g * 6\n", i, c[i], b[i], a[i]); - assert(0); - } - } - } - } -} diff --git a/libphobos/libdruntime/rt/arrayfloat.d b/libphobos/libdruntime/rt/arrayfloat.d deleted file mode 100644 index dfbfe92f1..000000000 --- a/libphobos/libdruntime/rt/arrayfloat.d +++ /dev/null @@ -1,1387 +0,0 @@ -/** - * Contains SSE2 and MMX versions of certain operations for float. - * - * Copyright: Copyright Digital Mars 2008 - 2010. - * License: Boost License 1.0. - * Authors: Walter Bright, based on code originally written by Burton Radons - */ - -/* Copyright Digital Mars 2008 - 2010. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -module rt.arrayfloat; - -// debug=PRINTF - -private import core.cpuid; -import rt.util.array; - -version (unittest) -{ - private import core.stdc.stdio : printf; - /* This is so unit tests will test every CPU variant - */ - int cpuid; - const int CPUID_MAX = 5; - @property bool mmx() { return cpuid == 1 && core.cpuid.mmx; } - @property bool sse() { return cpuid == 2 && core.cpuid.sse; } - @property bool sse2() { return cpuid == 3 && core.cpuid.sse2; } - @property bool amd3dnow() { return cpuid == 4 && core.cpuid.amd3dnow; } -} -else -{ - alias core.cpuid.mmx mmx; - alias core.cpuid.sse sse; - alias core.cpuid.sse2 sse2; - alias core.cpuid.amd3dnow amd3dnow; -} - -//version = log; - -alias float T; - -extern (C) @trusted nothrow: - -/* ======================================================================== */ -/* ======================================================================== */ - -/* template for the case - * a[] = b[] ? c[] - * with some binary operator ? - */ -private template CodeGenSliceSliceOp(string opD, string opSSE, string op3DNow) -{ - const CodeGenSliceSliceOp = ` - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - auto cptr = c.ptr; - - version (D_InlineAsm_X86) - { - // SSE version is 834% faster - if (sse && b.length >= 16) - { - auto n = aptr + (b.length & ~15); - - // Unaligned case - asm - { - mov EAX, bptr; // left operand - mov ECX, cptr; // right operand - mov ESI, aptr; // destination operand - mov EDI, n; // end comparison - - align 8; - startsseloopb: - movups XMM0, [EAX]; - movups XMM1, [EAX+16]; - movups XMM2, [EAX+32]; - movups XMM3, [EAX+48]; - add EAX, 64; - movups XMM4, [ECX]; - movups XMM5, [ECX+16]; - movups XMM6, [ECX+32]; - movups XMM7, [ECX+48]; - add ESI, 64; - ` ~ opSSE ~ ` XMM0, XMM4; - ` ~ opSSE ~ ` XMM1, XMM5; - ` ~ opSSE ~ ` XMM2, XMM6; - ` ~ opSSE ~ ` XMM3, XMM7; - add ECX, 64; - movups [ESI+ 0-64], XMM0; - movups [ESI+16-64], XMM1; - movups [ESI+32-64], XMM2; - movups [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloopb; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - else - // 3DNow! version is only 13% faster - if (amd3dnow && b.length >= 8) - { - auto n = aptr + (b.length & ~7); - - asm - { - mov ESI, aptr; // destination operand - mov EDI, n; // end comparison - mov EAX, bptr; // left operand - mov ECX, cptr; // right operand - - align 4; - start3dnow: - movq MM0, [EAX]; - movq MM1, [EAX+8]; - movq MM2, [EAX+16]; - movq MM3, [EAX+24]; - ` ~ op3DNow ~ ` MM0, [ECX]; - ` ~ op3DNow ~ ` MM1, [ECX+8]; - ` ~ op3DNow ~ ` MM2, [ECX+16]; - ` ~ op3DNow ~ ` MM3, [ECX+24]; - movq [ESI], MM0; - movq [ESI+8], MM1; - movq [ESI+16], MM2; - movq [ESI+24], MM3; - add ECX, 32; - add ESI, 32; - add EAX, 32; - cmp ESI, EDI; - jb start3dnow; - - emms; - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - - // Handle remainder - while (aptr < aend) - *aptr++ = *bptr++ ` ~ opD ~ ` *cptr++; - - return a;`; -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] + c[] - */ - -T[] _arraySliceSliceAddSliceAssign_f(T[] a, T[] c, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - enforceTypedArraysConformable("vector operation", a, c); - - mixin(CodeGenSliceSliceOp!("+", "addps", "pfadd")); -} - - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceAddSliceAssign_f unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] + b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + b[i])) - { - printf("[%d]: %g != %g + %g\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] - c[] - */ - -T[] _arraySliceSliceMinSliceAssign_f(T[] a, T[] c, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - enforceTypedArraysConformable("vector operation", a, c); - - mixin(CodeGenSliceSliceOp!("-", "subps", "pfsub")); -} - - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceMinSliceAssign_f unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] - b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] - b[i])) - { - printf("[%d]: %g != %gd - %g\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] * c[] - */ - -T[] _arraySliceSliceMulSliceAssign_f(T[] a, T[] c, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - enforceTypedArraysConformable("vector operation", a, c); - - mixin(CodeGenSliceSliceOp!("*", "mulps", "pfmul")); -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceMulSliceAssign_f unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] * b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] * b[i])) - { - printf("[%d]: %g != %g * %g\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/* template for the case - * a[] ?= value - * with some binary operator ? - */ -private template CodeGenExpSliceOpAssign(string opD, string opSSE, string op3DNow) -{ - const CodeGenExpSliceOpAssign = ` - auto aptr = a.ptr; - auto aend = aptr + a.length; - - version (D_InlineAsm_X86) - { - if (sse && a.length >= 16) - { - auto aabeg = cast(T*)((cast(uint)aptr + 15) & ~15); // beginning of paragraph-aligned slice of a - auto aaend = cast(T*)((cast(uint)aend) & ~15); // end of paragraph-aligned slice of a - - int numAligned = cast(int)(aaend - aabeg); // how many floats are in the aligned slice? - - // are there at least 16 floats in the paragraph-aligned slice? - // otherwise we can't do anything with SSE. - if (numAligned >= 16) - { - aaend = aabeg + (numAligned & ~15); // make sure the slice is actually a multiple of 16 floats long - - // process values up to aligned slice one by one - while (aptr < aabeg) - *aptr++ ` ~ opD ~ ` value; - - // process aligned slice with fast SSE operations - asm - { - mov ESI, aabeg; - mov EDI, aaend; - movss XMM4, value; - shufps XMM4, XMM4, 0; - - align 8; - startsseloopa: - movaps XMM0, [ESI]; - movaps XMM1, [ESI+16]; - movaps XMM2, [ESI+32]; - movaps XMM3, [ESI+48]; - add ESI, 64; - ` ~ opSSE ~ ` XMM0, XMM4; - ` ~ opSSE ~ ` XMM1, XMM4; - ` ~ opSSE ~ ` XMM2, XMM4; - ` ~ opSSE ~ ` XMM3, XMM4; - movaps [ESI+ 0-64], XMM0; - movaps [ESI+16-64], XMM1; - movaps [ESI+32-64], XMM2; - movaps [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloopa; - } - aptr = aaend; - } - } - else - // 3DNow! version is 63% faster - if (amd3dnow && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - ulong w = *cast(uint *) &value; - ulong v = w | (w << 32L); - - asm - { - mov ESI, dword ptr [aptr]; - mov EDI, dword ptr [n]; - movq MM4, qword ptr [v]; - - align 8; - start: - movq MM0, [ESI]; - movq MM1, [ESI+8]; - movq MM2, [ESI+16]; - movq MM3, [ESI+24]; - ` ~ op3DNow ~ ` MM0, MM4; - ` ~ op3DNow ~ ` MM1, MM4; - ` ~ op3DNow ~ ` MM2, MM4; - ` ~ op3DNow ~ ` MM3, MM4; - movq [ESI], MM0; - movq [ESI+8], MM1; - movq [ESI+16], MM2; - movq [ESI+24], MM3; - add ESI, 32; - cmp ESI, EDI; - jb start; - - emms; - mov dword ptr [aptr], ESI; - } - } - } - - while (aptr < aend) - *aptr++ ` ~ opD ~ ` value; - - return a;`; -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] += value - */ - -T[] _arrayExpSliceAddass_f(T[] a, T value) -{ - mixin(CodeGenExpSliceOpAssign!("+=", "addps", "pfadd")); -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceAddass_f unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] += 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + 6)) - { - printf("[%d]: %g != %g + 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] -= value - */ - -T[] _arrayExpSliceMinass_f(T[] a, T value) -{ - mixin(CodeGenExpSliceOpAssign!("-=", "subps", "pfsub")); -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceminass_f unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] -= 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] - 6)) - { - printf("[%d]: %g != %g - 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] *= value - */ - -T[] _arrayExpSliceMulass_f(T[] a, T value) -{ - mixin(CodeGenExpSliceOpAssign!("*=", "mulps", "pfmul")); -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMulass_f unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] *= 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] * 6)) - { - printf("[%d]: %g != %g * 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] /= value - */ - -T[] _arrayExpSliceDivass_f(T[] a, T value) -{ - return _arrayExpSliceMulass_f(a, 1f / value); -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceDivass_f unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] /= 8; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] / 8)) - { - printf("[%d]: %g != %g / 8\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ -/* ======================================================================== */ - -/* template for the case - * a[] = b[] ? value - * with some binary operator ? - */ -private template CodeGenSliceExpOp(string opD, string opSSE, string op3DNow) -{ - const CodeGenSliceExpOp = ` - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE version is 665% faster - if (sse && a.length >= 16) - { - auto n = aptr + (a.length & ~15); - - // Unaligned case - asm - { - mov EAX, bptr; - mov ESI, aptr; - mov EDI, n; - movss XMM4, value; - shufps XMM4, XMM4, 0; - - align 8; - startsseloop: - add ESI, 64; - movups XMM0, [EAX]; - movups XMM1, [EAX+16]; - movups XMM2, [EAX+32]; - movups XMM3, [EAX+48]; - add EAX, 64; - ` ~ opSSE ~ ` XMM0, XMM4; - ` ~ opSSE ~ ` XMM1, XMM4; - ` ~ opSSE ~ ` XMM2, XMM4; - ` ~ opSSE ~ ` XMM3, XMM4; - movups [ESI+ 0-64], XMM0; - movups [ESI+16-64], XMM1; - movups [ESI+32-64], XMM2; - movups [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloop; - - mov aptr, ESI; - mov bptr, EAX; - } - } - else - // 3DNow! version is 69% faster - if (amd3dnow && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - ulong w = *cast(uint *) &value; - ulong v = w | (w << 32L); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movq MM4, qword ptr [v]; - - align 8; - start3dnow: - movq MM0, [EAX]; - movq MM1, [EAX+8]; - movq MM2, [EAX+16]; - movq MM3, [EAX+24]; - ` ~ op3DNow ~ ` MM0, MM4; - ` ~ op3DNow ~ ` MM1, MM4; - ` ~ op3DNow ~ ` MM2, MM4; - ` ~ op3DNow ~ ` MM3, MM4; - movq [ESI], MM0; - movq [ESI+8], MM1; - movq [ESI+16], MM2; - movq [ESI+24], MM3; - add ESI, 32; - add EAX, 32; - cmp ESI, EDI; - jb start3dnow; - - emms; - mov aptr, ESI; - mov bptr, EAX; - } - } - } - - while (aptr < aend) - *aptr++ = *bptr++ ` ~ opD ~ ` value; - - return a;`; -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] + value - */ - -T[] _arraySliceExpAddSliceAssign_f(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - mixin(CodeGenSliceExpOp!("+", "addps", "pfadd")); -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpAddSliceAssign_f unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] + 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + 6)) - { - printf("[%d]: %g != %g + 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] - value - */ - -T[] _arraySliceExpMinSliceAssign_f(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - mixin(CodeGenSliceExpOp!("-", "subps", "pfsub")); -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpMinSliceAssign_f unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] - 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] - 6)) - { - printf("[%d]: %g != %g - 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] * value - */ - -T[] _arraySliceExpMulSliceAssign_f(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - mixin(CodeGenSliceExpOp!("*", "mulps", "pfmul")); -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpMulSliceAssign_f unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] * 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] * 6)) - { - printf("[%d]: %g != %g * 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] / value - */ - -T[] _arraySliceExpDivSliceAssign_f(T[] a, T value, T[] b) -{ - return _arraySliceExpMulSliceAssign_f(a, 1f/value, b); -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpDivSliceAssign_f unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] / 8; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] / 8)) - { - printf("[%d]: %g != %g / 8\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ -/* ======================================================================== */ - -private template CodeGenSliceOpAssign(string opD, string opSSE, string op3DNow) -{ - const CodeGenSliceOpAssign = ` - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE version is 468% faster - if (sse && a.length >= 16) - { - auto n = aptr + (a.length & ~15); - - // Unaligned case - asm - { - mov ECX, bptr; // right operand - mov ESI, aptr; // destination operand - mov EDI, n; // end comparison - - align 8; - startsseloopb: - movups XMM0, [ESI]; - movups XMM1, [ESI+16]; - movups XMM2, [ESI+32]; - movups XMM3, [ESI+48]; - add ESI, 64; - movups XMM4, [ECX]; - movups XMM5, [ECX+16]; - movups XMM6, [ECX+32]; - movups XMM7, [ECX+48]; - add ECX, 64; - ` ~ opSSE ~ ` XMM0, XMM4; - ` ~ opSSE ~ ` XMM1, XMM5; - ` ~ opSSE ~ ` XMM2, XMM6; - ` ~ opSSE ~ ` XMM3, XMM7; - movups [ESI+ 0-64], XMM0; - movups [ESI+16-64], XMM1; - movups [ESI+32-64], XMM2; - movups [ESI+48-64], XMM3; - cmp ESI, EDI; - jb startsseloopb; - - mov aptr, ESI; - mov bptr, ECX; - } - } - else - // 3DNow! version is 57% faster - if (amd3dnow && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - asm - { - mov ESI, dword ptr [aptr]; // destination operand - mov EDI, dword ptr [n]; // end comparison - mov ECX, dword ptr [bptr]; // right operand - - align 4; - start3dnow: - movq MM0, [ESI]; - movq MM1, [ESI+8]; - movq MM2, [ESI+16]; - movq MM3, [ESI+24]; - ` ~ op3DNow ~ ` MM0, [ECX]; - ` ~ op3DNow ~ ` MM1, [ECX+8]; - ` ~ op3DNow ~ ` MM2, [ECX+16]; - ` ~ op3DNow ~ ` MM3, [ECX+24]; - movq [ESI], MM0; - movq [ESI+8], MM1; - movq [ESI+16], MM2; - movq [ESI+24], MM3; - add ESI, 32; - add ECX, 32; - cmp ESI, EDI; - jb start3dnow; - - emms; - mov dword ptr [aptr], ESI; - mov dword ptr [bptr], ECX; - } - } - } - - while (aptr < aend) - *aptr++ ` ~ opD ~ ` *bptr++; - - return a;`; -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] += b[] - */ - -T[] _arraySliceSliceAddass_f(T[] a, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - mixin(CodeGenSliceOpAssign!("+=", "addps", "pfadd")); -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceAddass_f unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] += b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + b[i])) - { - printf("[%d]: %g != %g + %g\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] -= b[] - */ - -T[] _arraySliceSliceMinass_f(T[] a, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - mixin(CodeGenSliceOpAssign!("-=", "subps", "pfsub")); -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMinass_f unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] -= 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] - 6)) - { - printf("[%d]: %g != %g - 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] *= b[] - */ - -T[] _arraySliceSliceMulass_f(T[] a, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - mixin(CodeGenSliceOpAssign!("*=", "mulps", "pfmul")); -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMulass_f unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - c[] *= 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] * 6)) - { - printf("[%d]: %g != %g * 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = value - b[] - */ - -T[] _arrayExpSliceMinSliceAssign_f(T[] a, T[] b, T value) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arrayExpSliceMinSliceAssign_f()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE version is 690% faster - if (sse && a.length >= 16) - { - auto n = aptr + (a.length & ~15); - - // Unaligned case - asm - { - mov EAX, bptr; - mov ESI, aptr; - mov EDI, n; - movss XMM4, value; - shufps XMM4, XMM4, 0; - - align 8; - startsseloop: - add ESI, 64; - movaps XMM5, XMM4; - movaps XMM6, XMM4; - movups XMM0, [EAX]; - movups XMM1, [EAX+16]; - movups XMM2, [EAX+32]; - movups XMM3, [EAX+48]; - add EAX, 64; - subps XMM5, XMM0; - subps XMM6, XMM1; - movups [ESI+ 0-64], XMM5; - movups [ESI+16-64], XMM6; - movaps XMM5, XMM4; - movaps XMM6, XMM4; - subps XMM5, XMM2; - subps XMM6, XMM3; - movups [ESI+32-64], XMM5; - movups [ESI+48-64], XMM6; - cmp ESI, EDI; - jb startsseloop; - - mov aptr, ESI; - mov bptr, EAX; - } - } - else - // 3DNow! version is 67% faster - if (amd3dnow && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - ulong w = *cast(uint *) &value; - ulong v = w | (w << 32L); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movq MM4, qword ptr [v]; - - align 8; - start3dnow: - movq MM0, [EAX]; - movq MM1, [EAX+8]; - movq MM2, [EAX+16]; - movq MM3, [EAX+24]; - pfsubr MM0, MM4; - pfsubr MM1, MM4; - pfsubr MM2, MM4; - pfsubr MM3, MM4; - movq [ESI], MM0; - movq [ESI+8], MM1; - movq [ESI+16], MM2; - movq [ESI+24], MM3; - add ESI, 32; - add EAX, 32; - cmp ESI, EDI; - jb start3dnow; - - emms; - mov aptr, ESI; - mov bptr, EAX; - } - } - } - - while (aptr < aend) - *aptr++ = value - *bptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMinSliceAssign_f unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = 6 - a[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(6 - a[i])) - { - printf("[%d]: %g != 6 - %g\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] -= b[] * value - */ - -T[] _arraySliceExpMulSliceMinass_f(T[] a, T value, T[] b) -{ - return _arraySliceExpMulSliceAddass_f(a, -value, b); -} - -/*********************** - * Computes: - * a[] += b[] * value - */ - -T[] _arraySliceExpMulSliceAddass_f(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - // Handle remainder - while (aptr < aend) - *aptr++ += *bptr++ * value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpMulSliceAddass_f unittest\n"); - - cpuid = 1; - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 1; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - b[] = c[]; - c[] += a[] * 6; - - for (int i = 0; i < dim; i++) - { - //printf("[%d]: %g ?= %g + %g * 6\n", i, c[i], b[i], a[i]); - if (c[i] != cast(T)(b[i] + a[i] * 6)) - { - printf("[%d]: %g ?= %g + %g * 6\n", i, c[i], b[i], a[i]); - assert(0); - } - } - } - } -} diff --git a/libphobos/libdruntime/rt/arrayint.d b/libphobos/libdruntime/rt/arrayint.d deleted file mode 100644 index 2db66de09..000000000 --- a/libphobos/libdruntime/rt/arrayint.d +++ /dev/null @@ -1,3575 +0,0 @@ -/** - * Contains SSE/MMX versions of certain operations for dchar, int, and uint ('w', - * 'i' and 'k' suffixes). - * - * Copyright: Copyright Digital Mars 2008 - 2010. - * License: Boost License 1.0. - * Authors: Walter Bright, based on code originally written by Burton Radons - */ - -/* Copyright Digital Mars 2008 - 2010. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -module rt.arrayint; - -// debug=PRINTF - -private import core.cpuid; -import rt.util.array; - -version (unittest) -{ - private import core.stdc.stdio : printf; - /* This is so unit tests will test every CPU variant - */ - uint cpuid; - enum CPUID_MAX = 14; - @property bool mmx() { return cpuid == 1 && core.cpuid.mmx; } - @property bool sse() { return cpuid == 2 && core.cpuid.sse; } - @property bool sse2() { return cpuid == 3 && core.cpuid.sse2; } - @property bool sse3() { return cpuid == 4 && core.cpuid.sse3; } - @property bool sse41() { return cpuid == 5 && core.cpuid.sse41; } - @property bool sse42() { return cpuid == 6 && core.cpuid.sse42; } - @property bool sse4a() { return cpuid == 7 && core.cpuid.sse4a; } - @property bool avx() { return cpuid == 8 && core.cpuid.avx; } - @property bool avx2() { return cpuid == 9 && core.cpuid.avx2; } - @property bool amd3dnow() { return cpuid == 10 && core.cpuid.amd3dnow; } - @property bool and3dnowExt() { return cpuid == 11 && core.cpuid.amd3dnowExt; } - @property bool amdMmx() { return cpuid == 12 && core.cpuid.amdMmx; } - @property bool has3dnowPrefetch() { return cpuid == 13 && core.cpuid.has3dnowPrefetch; } -} -else -{ - version(X86_64) //guaranteed on x86_64 - { - enum mmx = true; - enum sse = true; - enum sse2 = true; - } - else - { - alias core.cpuid.mmx mmx; - alias core.cpuid.sse sse; - alias core.cpuid.sse2 sse2; - } - alias core.cpuid.sse3 sse3; - alias core.cpuid.sse41 sse41; - alias core.cpuid.sse42 sse42; - alias core.cpuid.sse4a sse4a; - alias core.cpuid.avx avx; - alias core.cpuid.avx2 avx2; - alias core.cpuid.amd3dnow amd3dnow; - alias core.cpuid.amd3dnowExt and3dnowExt; - alias core.cpuid.amdMmx amdMmx; - alias core.cpuid.has3dnowPrefetch has3dnowPrefetch; -} - -//version = log; - -alias int T; - -extern (C) @trusted nothrow: - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] + value - */ - -T[] _arraySliceExpAddSliceAssign_w(T[] a, T value, T[] b) -{ - return _arraySliceExpAddSliceAssign_i(a, value, b); -} - -T[] _arraySliceExpAddSliceAssign_k(T[] a, T value, T[] b) -{ - return _arraySliceExpAddSliceAssign_i(a, value, b); -} - -T[] _arraySliceExpAddSliceAssign_i(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceExpAddSliceAssign_i()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 380% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr | cast(size_t) bptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM2,value; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2u: - add ESI, 32; - movdqu XMM0, [EAX]; - movdqu XMM1, [EAX+16]; - add EAX, 32; - paddd XMM0, XMM2; - paddd XMM1, XMM2; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2u; - - mov aptr, ESI; - mov bptr, EAX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2a: - add ESI, 32; - movdqa XMM0, [EAX]; - movdqa XMM1, [EAX+16]; - add EAX, 32; - paddd XMM0, XMM2; - paddd XMM1, XMM2; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2a; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - // MMX version is 298% faster - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - ulong l = cast(uint) value | ((cast(ulong)cast(uint) value) << 32); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movq MM2, l; - - align 4; - startmmx: - add ESI, 16; - movq MM0, [EAX]; - movq MM1, [EAX+8]; - add EAX, 16; - paddd MM0, MM2; - paddd MM1, MM2; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - } - } - } - version (D_InlineAsm_X86_64) - { - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr | cast(size_t) bptr) & 15) != 0) - { - asm // unaligned case - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2u: - add RSI, 32; - movdqu XMM0, [RAX]; - movdqu XMM1, [RAX+16]; - add RAX, 32; - paddd XMM0, XMM2; - paddd XMM1, XMM2; - movdqu [RSI -32], XMM0; - movdqu [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startaddsse2u; - - mov aptr, RSI; - mov bptr, RAX; - } - } - else - { - asm // aligned case - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - movd XMM2,value; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2a: - add RSI, 32; - movdqa XMM0, [RAX]; - movdqa XMM1, [RAX+16]; - add RAX, 32; - paddd XMM0, XMM2; - paddd XMM1, XMM2; - movdqa [RSI -32], XMM0; - movdqa [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startaddsse2a; - - mov aptr, RSI; - mov bptr, RAX; - } - } - } - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - ulong l = cast(uint) value | ((cast(ulong)cast(uint) value) << 32); - - asm - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - movq MM2, l; - - align 4; - startmmx: - add RSI, 16; - movq MM0, [RAX]; - movq MM1, [RAX+8]; - add RAX, 16; - paddd MM0, MM2; - paddd MM1, MM2; - movq [RSI -16], MM0; - movq [RSI+8-16], MM1; - cmp RSI, RDI; - jb startmmx; - - emms; - mov aptr, RSI; - mov bptr, RAX; - } - } - } - - while (aptr < aend) - *aptr++ = *bptr++ + value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpAddSliceAssign_i unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { - a[i] = cast(T)(i-10); - c[i] = cast(T)((i-10) * 2); - } - - c[] = a[] + 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + 6)) - { - printf("[%d]: %d != %d + 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] + c[] - */ - -T[] _arraySliceSliceAddSliceAssign_w(T[] a, T[] c, T[] b) -{ - return _arraySliceSliceAddSliceAssign_i(a, c, b); -} - -T[] _arraySliceSliceAddSliceAssign_k(T[] a, T[] c, T[] b) -{ - return _arraySliceSliceAddSliceAssign_i(a, c, b); -} - -T[] _arraySliceSliceAddSliceAssign_i(T[] a, T[] c, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - enforceTypedArraysConformable("vector operation", a, c); - - //printf("_arraySliceSliceAddSliceAssign_i()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - auto cptr = c.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 1710% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr | cast(size_t) bptr | cast(size_t) cptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startsse2u: - add ESI, 32; - movdqu XMM0, [EAX]; - movdqu XMM2, [ECX]; - movdqu XMM1, [EAX+16]; - movdqu XMM3, [ECX+16]; - add EAX, 32; - add ECX, 32; - paddd XMM0, XMM2; - paddd XMM1, XMM3; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2u; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startsse2a: - add ESI, 32; - movdqa XMM0, [EAX]; - movdqa XMM2, [ECX]; - movdqa XMM1, [EAX+16]; - movdqa XMM3, [ECX+16]; - add EAX, 32; - add ECX, 32; - paddd XMM0, XMM2; - paddd XMM1, XMM3; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2a; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - // MMX version is 995% faster - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startmmx: - add ESI, 16; - movq MM0, [EAX]; - movq MM2, [ECX]; - movq MM1, [EAX+8]; - movq MM3, [ECX+8]; - add EAX, 16; - add ECX, 16; - paddd MM0, MM2; - paddd MM1, MM3; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - version (D_InlineAsm_X86_64) - { - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr | cast(size_t) bptr | cast(size_t) cptr) & 15) != 0) - { - asm // unaligned case - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - mov RCX, cptr; - - align 4; - startsse2u: - add RSI, 32; - movdqu XMM0, [RAX]; - movdqu XMM2, [RCX]; - movdqu XMM1, [RAX+16]; - movdqu XMM3, [RCX+16]; - add RAX, 32; - add RCX, 32; - paddd XMM0, XMM2; - paddd XMM1, XMM3; - movdqu [RSI -32], XMM0; - movdqu [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startsse2u; - - mov aptr, RSI; - mov bptr, RAX; - mov cptr, RCX; - } - } - else - { - asm // aligned case - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - mov RCX, cptr; - - align 4; - startsse2a: - add RSI, 32; - movdqa XMM0, [RAX]; - movdqa XMM2, [RCX]; - movdqa XMM1, [RAX+16]; - movdqa XMM3, [RCX+16]; - add RAX, 32; - add RCX, 32; - paddd XMM0, XMM2; - paddd XMM1, XMM3; - movdqa [RSI -32], XMM0; - movdqa [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startsse2a; - - mov aptr, RSI; - mov bptr, RAX; - mov cptr, RCX; - } - } - } - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - asm - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - mov RCX, cptr; - - align 4; - startmmx: - add RSI, 16; - movq MM0, [RAX]; - movq MM2, [RCX]; - movq MM1, [RAX+8]; - movq MM3, [RCX+8]; - add RAX, 16; - add RCX, 16; - paddd MM0, MM2; - paddd MM1, MM3; - movq [RSI -16], MM0; - movq [RSI+8-16], MM1; - cmp RSI, RDI; - jb startmmx; - - emms; - mov aptr, RSI; - mov bptr, RAX; - mov cptr, RCX; - } - } - } - - while (aptr < aend) - *aptr++ = *bptr++ + *cptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceAddSliceAssign_i unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { - a[i] = cast(T)(i-10); - b[i] = cast(T)(i-3); - c[i] = cast(T)((i-10) * 2); - } - - c[] = a[] + b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + b[i])) - { - printf("[%d]: %d != %d + %d\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] += value - */ - -T[] _arrayExpSliceAddass_w(T[] a, T value) -{ - return _arrayExpSliceAddass_i(a, value); -} - -T[] _arrayExpSliceAddass_k(T[] a, T value) -{ - return _arrayExpSliceAddass_i(a, value); -} - -T[] _arrayExpSliceAddass_i(T[] a, T value) -{ - //printf("_arrayExpSliceAddass_i(a.length = %d, value = %Lg)\n", a.length, cast(real)value); - auto aptr = a.ptr; - auto aend = aptr + a.length; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 83% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2u: - movdqu XMM0, [ESI]; - movdqu XMM1, [ESI+16]; - add ESI, 32; - paddd XMM0, XMM2; - paddd XMM1, XMM2; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2u; - - mov aptr, ESI; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2a: - movdqa XMM0, [ESI]; - movdqa XMM1, [ESI+16]; - add ESI, 32; - paddd XMM0, XMM2; - paddd XMM1, XMM2; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2a; - - mov aptr, ESI; - } - } - } - // MMX version is 81% faster - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - ulong l = cast(uint) value | (cast(ulong)cast(uint) value << 32); - - asm - { - mov ESI, aptr; - mov EDI, n; - movq MM2, l; - - align 4; - startmmx: - movq MM0, [ESI]; - movq MM1, [ESI+8]; - add ESI, 16; - paddd MM0, MM2; - paddd MM1, MM2; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - } - } - } - version (D_InlineAsm_X86_64) - { - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr) & 15) != 0) - { - asm // unaligned case - { - mov RSI, aptr; - mov RDI, n; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2u: - movdqu XMM0, [RSI]; - movdqu XMM1, [RSI+16]; - add RSI, 32; - paddd XMM0, XMM2; - paddd XMM1, XMM2; - movdqu [RSI -32], XMM0; - movdqu [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startaddsse2u; - - mov aptr, RSI; - } - } - else - { - asm // aligned case - { - mov RSI, aptr; - mov RDI, n; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2a: - movdqa XMM0, [RSI]; - movdqa XMM1, [RSI+16]; - add RSI, 32; - paddd XMM0, XMM2; - paddd XMM1, XMM2; - movdqa [RSI -32], XMM0; - movdqa [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startaddsse2a; - - mov aptr, RSI; - } - } - } - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - ulong l = cast(uint) value | (cast(ulong)cast(uint) value << 32); - - asm - { - mov RSI, aptr; - mov RDI, n; - movq MM2, l; - - align 4; - startmmx: - movq MM0, [RSI]; - movq MM1, [RSI+8]; - add RSI, 16; - paddd MM0, MM2; - paddd MM1, MM2; - movq [RSI -16], MM0; - movq [RSI+8-16], MM1; - cmp RSI, RDI; - jb startmmx; - - emms; - mov aptr, RSI; - } - } - } - - while (aptr < aend) - *aptr++ += value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceAddass_i unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { - a[i] = cast(T)(i-10); - c[i] = cast(T)((i-10) * 2); - } - - a[] = c[]; - a[] += 6; - - for (int i = 0; i < dim; i++) - { - if (a[i] != cast(T)(c[i] + 6)) - { - printf("[%d]: %d != %d + 6\n", i, a[i], c[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] += b[] - */ - -T[] _arraySliceSliceAddass_w(T[] a, T[] b) -{ - return _arraySliceSliceAddass_i(a, b); -} - -T[] _arraySliceSliceAddass_k(T[] a, T[] b) -{ - return _arraySliceSliceAddass_i(a, b); -} - -T[] _arraySliceSliceAddass_i(T[] a, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceSliceAddass_i()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 695% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr | cast(size_t) bptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - startsse2u: - movdqu XMM0, [ESI]; - movdqu XMM2, [ECX]; - movdqu XMM1, [ESI+16]; - movdqu XMM3, [ECX+16]; - add ESI, 32; - add ECX, 32; - paddd XMM0, XMM2; - paddd XMM1, XMM3; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2u; - - mov aptr, ESI; - mov bptr, ECX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - startsse2a: - movdqa XMM0, [ESI]; - movdqa XMM2, [ECX]; - movdqa XMM1, [ESI+16]; - movdqa XMM3, [ECX+16]; - add ESI, 32; - add ECX, 32; - paddd XMM0, XMM2; - paddd XMM1, XMM3; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2a; - - mov aptr, ESI; - mov bptr, ECX; - } - } - } - // MMX version is 471% faster - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - startmmx: - movq MM0, [ESI]; - movq MM2, [ECX]; - movq MM1, [ESI+8]; - movq MM3, [ECX+8]; - add ESI, 16; - add ECX, 16; - paddd MM0, MM2; - paddd MM1, MM3; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - mov bptr, ECX; - } - } - } - version (D_InlineAsm_X86_64) - { - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr | cast(size_t) bptr) & 15) != 0) - { - asm // unaligned case - { - mov RSI, aptr; - mov RDI, n; - mov RCX, bptr; - - align 4; - startsse2u: - movdqu XMM0, [RSI]; - movdqu XMM2, [RCX]; - movdqu XMM1, [RSI+16]; - movdqu XMM3, [RCX+16]; - add RSI, 32; - add RCX, 32; - paddd XMM0, XMM2; - paddd XMM1, XMM3; - movdqu [RSI -32], XMM0; - movdqu [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startsse2u; - - mov aptr, RSI; - mov bptr, RCX; - } - } - else - { - asm // aligned case - { - mov RSI, aptr; - mov RDI, n; - mov RCX, bptr; - - align 4; - startsse2a: - movdqa XMM0, [RSI]; - movdqa XMM2, [RCX]; - movdqa XMM1, [RSI+16]; - movdqa XMM3, [RCX+16]; - add RSI, 32; - add RCX, 32; - paddd XMM0, XMM2; - paddd XMM1, XMM3; - movdqa [RSI-32], XMM0; - movdqa [RSI-16], XMM1; - - cmp RSI, RDI; - jb startsse2a; - - mov aptr, RSI; - mov bptr, RCX; - } - } - } - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - asm - { - mov RSI, aptr; - mov RDI, n; - mov RCX, bptr; - - align 4; - startmmx: - movq MM0, [RSI]; - movq MM2, [RCX]; - movq MM1, [RSI+8]; - movq MM3, [RCX+8]; - add RSI, 16; - add RCX, 16; - paddd MM0, MM2; - paddd MM1, MM3; - movq [RSI -16], MM0; - movq [RSI+8-16], MM1; - cmp RSI, RDI; - jb startmmx; - - emms; - mov aptr, RSI; - mov bptr, RCX; - } - } - } - - while (aptr < aend) - *aptr++ += *bptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceAddass_i unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { - a[i] = cast(T)(i-10); - b[i] = cast(T)(i-3); - c[i] = cast(T)((i-10) * 2); - } - - b[] = c[]; - c[] += a[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(b[i] + a[i])) - { - printf("[%d]: %d != %d + %d\n", i, c[i], b[i], a[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] - value - */ - -T[] _arraySliceExpMinSliceAssign_w(T[] a, T value, T[] b) -{ - return _arraySliceExpMinSliceAssign_i(a, value, b); -} - -T[] _arraySliceExpMinSliceAssign_k(T[] a, T value, T[] b) -{ - return _arraySliceExpMinSliceAssign_i(a, value, b); -} - -T[] _arraySliceExpMinSliceAssign_i(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceExpMinSliceAssign_i()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 400% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr | cast(size_t) bptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2u: - add ESI, 32; - movdqu XMM0, [EAX]; - movdqu XMM1, [EAX+16]; - add EAX, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM2; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2u; - - mov aptr, ESI; - mov bptr, EAX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2a: - add ESI, 32; - movdqa XMM0, [EAX]; - movdqa XMM1, [EAX+16]; - add EAX, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM2; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2a; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - // MMX version is 315% faster - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - ulong l = cast(uint) value | (cast(ulong)cast(uint) value << 32); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movq MM2, l; - - align 4; - startmmx: - add ESI, 16; - movq MM0, [EAX]; - movq MM1, [EAX+8]; - add EAX, 16; - psubd MM0, MM2; - psubd MM1, MM2; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - } - } - } - version (D_InlineAsm_X86_64) - { - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr | cast(size_t) bptr) & 15) != 0) - { - asm // unaligned case - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2u: - add RSI, 32; - movdqu XMM0, [EAX]; - movdqu XMM1, [EAX+16]; - add RAX, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM2; - movdqu [RSI -32], XMM0; - movdqu [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startaddsse2u; - - mov aptr, RSI; - mov bptr, RAX; - } - } - else - { - asm // aligned case - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2a: - add RSI, 32; - movdqa XMM0, [EAX]; - movdqa XMM1, [EAX+16]; - add RAX, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM2; - movdqa [RSI -32], XMM0; - movdqa [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startaddsse2a; - - mov aptr, RSI; - mov bptr, RAX; - } - } - } - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - ulong l = cast(uint) value | (cast(ulong)cast(uint) value << 32); - - asm - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - movq MM2, l; - - align 4; - startmmx: - add RSI, 16; - movq MM0, [EAX]; - movq MM1, [EAX+8]; - add RAX, 16; - psubd MM0, MM2; - psubd MM1, MM2; - movq [RSI -16], MM0; - movq [RSI+8-16], MM1; - cmp RSI, RDI; - jb startmmx; - - emms; - mov aptr, RSI; - mov bptr, RAX; - } - } - } - - while (aptr < aend) - *aptr++ = *bptr++ - value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpMinSliceAssign_i unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { - a[i] = cast(T)(i-10); - c[i] = cast(T)((i-10) * 2); - } - - c[] = a[] - 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] - 6)) - { - printf("[%d]: %d != %d - 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = value - b[] - */ - -T[] _arrayExpSliceMinSliceAssign_w(T[] a, T[] b, T value) -{ - return _arrayExpSliceMinSliceAssign_i(a, b, value); -} - -T[] _arrayExpSliceMinSliceAssign_k(T[] a, T[] b, T value) -{ - return _arrayExpSliceMinSliceAssign_i(a, b, value); -} - -T[] _arrayExpSliceMinSliceAssign_i(T[] a, T[] b, T value) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arrayExpSliceMinSliceAssign_i()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 1812% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr | cast(size_t) bptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM4, value; - pshufd XMM4, XMM4, 0; - - align 4; - startaddsse2u: - add ESI, 32; - movdqu XMM2, [EAX]; - movdqu XMM3, [EAX+16]; - movdqa XMM0, XMM4; - movdqa XMM1, XMM4; - add EAX, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM3; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2u; - - mov aptr, ESI; - mov bptr, EAX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM4, value; - pshufd XMM4, XMM4, 0; - - align 4; - startaddsse2a: - add ESI, 32; - movdqa XMM2, [EAX]; - movdqa XMM3, [EAX+16]; - movdqa XMM0, XMM4; - movdqa XMM1, XMM4; - add EAX, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM3; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2a; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - // MMX version is 1077% faster - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - ulong l = cast(uint) value | (cast(ulong)cast(uint) value << 32); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movq MM4, l; - - align 4; - startmmx: - add ESI, 16; - movq MM2, [EAX]; - movq MM3, [EAX+8]; - movq MM0, MM4; - movq MM1, MM4; - add EAX, 16; - psubd MM0, MM2; - psubd MM1, MM3; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - } - } - } - version (D_InlineAsm_X86_64) - { - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr | cast(size_t) bptr) & 15) != 0) - { - asm // unaligned case - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - movd XMM4, value; - pshufd XMM4, XMM4, 0; - - align 4; - startaddsse2u: - add RSI, 32; - movdqu XMM2, [RAX]; - movdqu XMM3, [RAX+16]; - movdqa XMM0, XMM4; - movdqa XMM1, XMM4; - add RAX, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM3; - movdqu [RSI -32], XMM0; - movdqu [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startaddsse2u; - - mov aptr, RSI; - mov bptr, RAX; - } - } - else - { - asm // aligned case - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - movd XMM4, value; - pshufd XMM4, XMM4, 0; - - align 4; - startaddsse2a: - add RSI, 32; - movdqa XMM2, [EAX]; - movdqa XMM3, [EAX+16]; - movdqa XMM0, XMM4; - movdqa XMM1, XMM4; - add RAX, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM3; - movdqa [RSI -32], XMM0; - movdqa [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startaddsse2a; - - mov aptr, RSI; - mov bptr, RAX; - } - } - } - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - ulong l = cast(uint) value | (cast(ulong)cast(uint) value << 32); - - asm - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - movq MM4, l; - - align 4; - startmmx: - add RSI, 16; - movq MM2, [EAX]; - movq MM3, [EAX+8]; - movq MM0, MM4; - movq MM1, MM4; - add RAX, 16; - psubd MM0, MM2; - psubd MM1, MM3; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp RSI, RDI; - jb startmmx; - - emms; - mov aptr, RSI; - mov bptr, RAX; - } - } - } - - while (aptr < aend) - *aptr++ = value - *bptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMinSliceAssign_i unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { - a[i] = cast(T)(i-10); - c[i] = cast(T)((i-10) * 2); - } - - c[] = 6 - a[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(6 - a[i])) - { - printf("[%d]: %d != 6 - %d\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] - c[] - */ - -T[] _arraySliceSliceMinSliceAssign_w(T[] a, T[] c, T[] b) -{ - return _arraySliceSliceMinSliceAssign_i(a, c, b); -} - -T[] _arraySliceSliceMinSliceAssign_k(T[] a, T[] c, T[] b) -{ - return _arraySliceSliceMinSliceAssign_i(a, c, b); -} - -T[] _arraySliceSliceMinSliceAssign_i(T[] a, T[] c, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - enforceTypedArraysConformable("vector operation", a, c); - - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - auto cptr = c.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 1721% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr | cast(size_t) bptr | cast(size_t) cptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startsse2u: - add ESI, 32; - movdqu XMM0, [EAX]; - movdqu XMM2, [ECX]; - movdqu XMM1, [EAX+16]; - movdqu XMM3, [ECX+16]; - add EAX, 32; - add ECX, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM3; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2u; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startsse2a: - add ESI, 32; - movdqa XMM0, [EAX]; - movdqa XMM2, [ECX]; - movdqa XMM1, [EAX+16]; - movdqa XMM3, [ECX+16]; - add EAX, 32; - add ECX, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM3; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2a; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - // MMX version is 1002% faster - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startmmx: - add ESI, 16; - movq MM0, [EAX]; - movq MM2, [ECX]; - movq MM1, [EAX+8]; - movq MM3, [ECX+8]; - add EAX, 16; - add ECX, 16; - psubd MM0, MM2; - psubd MM1, MM3; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - version (D_InlineAsm_X86_64) - { - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr | cast(size_t) bptr | cast(size_t) cptr) & 15) != 0) - { - asm // unaligned case - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - mov RCX, cptr; - - align 4; - startsse2u: - add RSI, 32; - movdqu XMM0, [RAX]; - movdqu XMM2, [RCX]; - movdqu XMM1, [RAX+16]; - movdqu XMM3, [RCX+16]; - add RAX, 32; - add RCX, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM3; - movdqu [RSI -32], XMM0; - movdqu [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startsse2u; - - mov aptr, RSI; - mov bptr, RAX; - mov cptr, RCX; - } - } - else - { - asm // aligned case - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - mov RCX, cptr; - - align 4; - startsse2a: - add RSI, 32; - movdqa XMM0, [RAX]; - movdqa XMM2, [RCX]; - movdqa XMM1, [RAX+16]; - movdqa XMM3, [RCX+16]; - add RAX, 32; - add RCX, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM3; - movdqa [RSI -32], XMM0; - movdqa [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startsse2a; - - mov aptr, RSI; - mov bptr, RAX; - mov cptr, RCX; - } - } - } - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - asm - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - mov RCX, cptr; - - align 4; - startmmx: - add RSI, 16; - movq MM0, [RAX]; - movq MM2, [RCX]; - movq MM1, [RAX+8]; - movq MM3, [RCX+8]; - add RAX, 16; - add RCX, 16; - psubd MM0, MM2; - psubd MM1, MM3; - movq [RSI -16], MM0; - movq [RSI+8-16], MM1; - cmp RSI, RDI; - jb startmmx; - - emms; - mov aptr, RSI; - mov bptr, RAX; - mov cptr, RCX; - } - } - } - - while (aptr < aend) - *aptr++ = *bptr++ - *cptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceMinSliceAssign_i unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { - a[i] = cast(T)(i-10); - b[i] = cast(T)(i-3); - c[i] = cast(T)((i-10) * 2); - } - - c[] = a[] - b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] - b[i])) - { - printf("[%d]: %d != %d - %d\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] -= value - */ - -T[] _arrayExpSliceMinass_w(T[] a, T value) -{ - return _arrayExpSliceMinass_i(a, value); -} - -T[] _arrayExpSliceMinass_k(T[] a, T value) -{ - return _arrayExpSliceMinass_i(a, value); -} - -T[] _arrayExpSliceMinass_i(T[] a, T value) -{ - //printf("_arrayExpSliceMinass_i(a.length = %d, value = %Lg)\n", a.length, cast(real)value); - auto aptr = a.ptr; - auto aend = aptr + a.length; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 81% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2u: - movdqu XMM0, [ESI]; - movdqu XMM1, [ESI+16]; - add ESI, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM2; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2u; - - mov aptr, ESI; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2a: - movdqa XMM0, [ESI]; - movdqa XMM1, [ESI+16]; - add ESI, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM2; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2a; - - mov aptr, ESI; - } - } - } - // MMX version is 81% faster - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - ulong l = cast(uint) value | (cast(ulong)cast(uint) value << 32); - - asm - { - mov ESI, aptr; - mov EDI, n; - movq MM2, l; - - align 4; - startmmx: - movq MM0, [ESI]; - movq MM1, [ESI+8]; - add ESI, 16; - psubd MM0, MM2; - psubd MM1, MM2; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - } - } - } - version (D_InlineAsm_X86_64) - { - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr) & 15) != 0) - { - asm // unaligned case - { - mov RSI, aptr; - mov RDI, n; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2u: - movdqu XMM0, [RSI]; - movdqu XMM1, [RSI+16]; - add RSI, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM2; - movdqu [RSI -32], XMM0; - movdqu [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startaddsse2u; - - mov aptr, RSI; - } - } - else - { - asm // aligned case - { - mov RSI, aptr; - mov RDI, n; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2a: - movdqa XMM0, [RSI]; - movdqa XMM1, [RSI+16]; - add RSI, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM2; - movdqa [RSI -32], XMM0; - movdqa [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startaddsse2a; - - mov aptr, RSI; - } - } - } - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - ulong l = cast(uint) value | (cast(ulong)cast(uint) value << 32); - - asm - { - mov RSI, aptr; - mov RDI, n; - movq MM2, l; - - align 4; - startmmx: - movq MM0, [RSI]; - movq MM1, [RSI+8]; - add RSI, 16; - psubd MM0, MM2; - psubd MM1, MM2; - movq [RSI -16], MM0; - movq [RSI+8-16], MM1; - cmp RSI, RDI; - jb startmmx; - - emms; - mov aptr, RSI; - } - } - } - - while (aptr < aend) - *aptr++ -= value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMinass_i unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { - a[i] = cast(T)(i-10); - c[i] = cast(T)((i-10) * 2); - } - - a[] = c[]; - a[] -= 6; - - for (int i = 0; i < dim; i++) - { - if (a[i] != cast(T)(c[i] - 6)) - { - printf("[%d]: %d != %d - 6\n", i, a[i], c[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] -= b[] - */ - -T[] _arraySliceSliceMinass_w(T[] a, T[] b) -{ - return _arraySliceSliceMinass_i(a, b); -} - -T[] _arraySliceSliceMinass_k(T[] a, T[] b) -{ - return _arraySliceSliceMinass_i(a, b); -} - -T[] _arraySliceSliceMinass_i(T[] a, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceSliceMinass_i()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 731% faster - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr | cast(size_t) bptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - startsse2u: - movdqu XMM0, [ESI]; - movdqu XMM2, [ECX]; - movdqu XMM1, [ESI+16]; - movdqu XMM3, [ECX+16]; - add ESI, 32; - add ECX, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM3; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2u; - - mov aptr, ESI; - mov bptr, ECX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - startsse2a: - movdqa XMM0, [ESI]; - movdqa XMM2, [ECX]; - movdqa XMM1, [ESI+16]; - movdqa XMM3, [ECX+16]; - add ESI, 32; - add ECX, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM3; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2a; - - mov aptr, ESI; - mov bptr, ECX; - } - } - } - // MMX version is 441% faster - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - startmmx: - movq MM0, [ESI]; - movq MM2, [ECX]; - movq MM1, [ESI+8]; - movq MM3, [ECX+8]; - add ESI, 16; - add ECX, 16; - psubd MM0, MM2; - psubd MM1, MM3; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - mov bptr, ECX; - } - } - } - version (D_InlineAsm_X86_64) - { - if (sse2 && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (((cast(size_t) aptr | cast(size_t) bptr) & 15) != 0) - { - asm // unaligned case - { - mov RSI, aptr; - mov RDI, n; - mov RCX, bptr; - - align 4; - startsse2u: - movdqu XMM0, [RSI]; - movdqu XMM2, [RCX]; - movdqu XMM1, [RSI+16]; - movdqu XMM3, [RCX+16]; - add RSI, 32; - add RCX, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM3; - movdqu [RSI -32], XMM0; - movdqu [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startsse2u; - - mov aptr, RSI; - mov bptr, RCX; - } - } - else - { - asm // aligned case - { - mov RSI, aptr; - mov RDI, n; - mov RCX, bptr; - - align 4; - startsse2a: - movdqa XMM0, [RSI]; - movdqa XMM2, [RCX]; - movdqa XMM1, [RSI+16]; - movdqa XMM3, [RCX+16]; - add RSI, 32; - add RCX, 32; - psubd XMM0, XMM2; - psubd XMM1, XMM3; - movdqa [RSI -32], XMM0; - movdqa [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startsse2a; - - mov aptr, RSI; - mov bptr, RCX; - } - } - } - else if (mmx && a.length >= 4) - { - auto n = aptr + (a.length & ~3); - - asm - { - mov RSI, aptr; - mov RDI, n; - mov RCX, bptr; - - align 4; - startmmx: - movq MM0, [RSI]; - movq MM2, [RCX]; - movq MM1, [RSI+8]; - movq MM3, [RCX+8]; - add RSI, 16; - add RCX, 16; - psubd MM0, MM2; - psubd MM1, MM3; - movq [RSI -16], MM0; - movq [RSI+8-16], MM1; - cmp RSI, RDI; - jb startmmx; - - emms; - mov aptr, RSI; - mov bptr, RCX; - } - } - } - - while (aptr < aend) - *aptr++ -= *bptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceMinass_i unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { - a[i] = cast(T)(i-10); - b[i] = cast(T)(i-3); - c[i] = cast(T)((i-10) * 2); - } - - b[] = c[]; - c[] -= a[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(b[i] - a[i])) - { - printf("[%d]: %d != %d - %d\n", i, c[i], b[i], a[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] * value - */ - -T[] _arraySliceExpMulSliceAssign_w(T[] a, T value, T[] b) -{ - return _arraySliceExpMulSliceAssign_i(a, value, b); -} - -T[] _arraySliceExpMulSliceAssign_k(T[] a, T value, T[] b) -{ - return _arraySliceExpMulSliceAssign_i(a, value, b); -} - -T[] _arraySliceExpMulSliceAssign_i(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceExpMulSliceAssign_i()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - if (sse41) - { - auto aligned = ((cast(size_t) aptr | cast(size_t) bptr) & 15) == 0; - - if (a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (!aligned) - { - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startsse41u: - add ESI, 32; - movdqu XMM0, [EAX]; - movdqu XMM1, [EAX+16]; - add EAX, 32; - pmulld XMM0, XMM2; - pmulld XMM1, XMM2; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse41u; - - mov aptr, ESI; - mov bptr, EAX; - } - } - else - { - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM1, value; - pshufd XMM2, XMM1, 0; - - align 4; - startsse41a: - add ESI, 32; - movdqa XMM0, [EAX]; - movdqa XMM1, [EAX+16]; - add EAX, 32; - pmulld XMM0, XMM2; - pmulld XMM1, XMM2; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse41a; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - else if (a.length >= 4) - { - if (!aligned) - { - asm - { - mov ESI, aptr; - mov EAX, bptr; - movd XMM1,value; - pshufd XMM1, XMM1, 0; - - movdqu XMM0, [EAX]; - pmulld XMM0, XMM1; - movdqu [ESI], XMM0; - - add EAX, 16; - add ESI, 16; - - mov aptr, ESI; - mov bptr, EAX; - } - } - else - { - asm - { - mov ESI, aptr; - mov EAX, bptr; - movd XMM1,value; - pshufd XMM1, XMM1, 0; - - movdqa XMM0, [EAX]; - pmulld XMM0, XMM1; - movdqa [ESI], XMM0; - - add EAX, 16; - add ESI, 16; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - } - } - version (D_InlineAsm_X86_64) - { - if (sse41) - { - auto aligned = ((cast(size_t) aptr | cast(size_t) bptr) & 15) == 0; - - if (a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (!aligned) - { - asm - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startsse41u: - add RSI, 32; - movdqu XMM0, [RAX]; - movdqu XMM1, [RAX+16]; - add RAX, 32; - pmulld XMM0, XMM2; - pmulld XMM1, XMM2; - movdqu [RSI -32], XMM0; - movdqu [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startsse41u; - - mov aptr, RSI; - mov bptr, RAX; - } - } - else - { - asm - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - movd XMM1, value; - pshufd XMM2, XMM1, 0; - - align 4; - startsse41a: - add RSI, 32; - movdqa XMM0, [RAX]; - movdqa XMM1, [RAX+16]; - add RAX, 32; - pmulld XMM0, XMM2; - pmulld XMM1, XMM2; - movdqa [RSI -32], XMM0; - movdqa [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startsse41a; - - mov aptr, RSI; - mov bptr, RAX; - } - } - } - else if (a.length >= 4) - { - if (!aligned) - {//possibly slow, needs measuring - asm - { - mov RSI, aptr; - mov RAX, bptr; - movd XMM1, value; - pshufd XMM1, XMM1, 0; - - movdqu XMM0, [RAX]; - pmulld XMM0, XMM1; - movdqu [RSI], XMM0; - - add RAX, 16; - add RSI, 16; - - mov aptr, RSI; - mov bptr, RAX; - } - } - else - { - asm - { - mov RSI, aptr; - mov RAX, bptr; - movd XMM1, value; - pshufd XMM1, XMM1, 0; - - movdqa XMM0, [RAX]; - pmulld XMM0, XMM1; - movdqa [RSI], XMM0; - - add RAX, 16; - add RSI, 16; - - mov aptr, RSI; - mov bptr, RAX; - } - } - } - } - } - - while (aptr < aend) - *aptr++ = *bptr++ * value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpMulSliceAssign_s unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - for (size_t dim = 7; dim < 68; dim += 60) - for (int j = 0; j < 2; j++) - { - T[] b = new T[dim + j]; // aligned on 16 byte boundary - b = b[j .. dim + j]; // misalign for second iteration - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { - b[i] = cast(T)(i-3); - c[i] = cast(T)((i-10) * 2); - } - - c[] = b[] * 6; - for (int i = 0; i < dim; i++) - { - //printf("[%d]: %d ?= %d * 6\n", i, c[i], b[i]); - if (c[i] != cast(T)(b[i] * 6)) - { - printf("[%d]: %d != %d * 6\n", i, c[i], b[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] * c[] - */ - -T[] _arraySliceSliceMulSliceAssign_w(T[] a, T[] c, T[] b) -{ - return _arraySliceSliceMulSliceAssign_i(a, c, b); -} - -T[] _arraySliceSliceMulSliceAssign_k(T[] a, T[] c, T[] b) -{ - return _arraySliceSliceMulSliceAssign_i(a, c, b); -} - -T[] _arraySliceSliceMulSliceAssign_i(T[] a, T[] c, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - enforceTypedArraysConformable("vector operation", a, c); - - //printf("_arraySliceSliceMulSliceAssign_i()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - auto cptr = c.ptr; - - version (D_InlineAsm_X86) - { - if (sse41) - { - auto aligned = ((cast(size_t) aptr | cast(size_t) bptr | cast(size_t) cptr) & 15) == 0; - - if (a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (!aligned) - { - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startsse41u: - add ESI, 32; - movdqu XMM0, [EAX]; - movdqu XMM2, [ECX]; - movdqu XMM1, [EAX+16]; - movdqu XMM3, [ECX+16]; - add EAX, 32; - add ECX, 32; - pmulld XMM0, XMM2; - pmulld XMM1, XMM3; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse41u; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - else - { - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startsse41a: - add ESI, 32; - movdqa XMM0, [EAX]; - movdqa XMM2, [ECX]; - movdqa XMM1, [EAX+16]; - movdqa XMM3, [ECX+16]; - add EAX, 32; - add ECX, 32; - pmulld XMM0, XMM2; - pmulld XMM1, XMM3; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse41a; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - else if (a.length >= 4) - { - if (!aligned) - {//possibly not a good idea. Performance? - asm - { - mov ESI, aptr; - mov EAX, bptr; - mov ECX, cptr; - - movdqu XMM0, [EAX]; - movdqu XMM1, [ECX]; - pmulld XMM0, XMM1; - movdqu [ESI], XMM0; - - add ESI, 16; - add EAX, 16; - add ECX, 16; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - else - { - asm - { - mov ESI, aptr; - mov EAX, bptr; - mov ECX, cptr; - - movdqa XMM0, [EAX]; - movdqa XMM1, [ECX]; - pmulld XMM0, XMM1; - movdqu [ESI], XMM0; - - add ESI, 16; - add EAX, 16; - add ECX, 16; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - } - } - version (D_InlineAsm_X86_64) - { - if (sse41) - { - auto aligned = ((cast(size_t) aptr | cast(size_t) bptr | cast(size_t) cptr) & 15) == 0; - - if (a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (!aligned) - { - asm - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - mov RCX, cptr; - - align 4; - startsse41u: - add RSI, 32; - movdqu XMM0, [RAX]; - movdqu XMM2, [RCX]; - movdqu XMM1, [RAX+16]; - movdqu XMM3, [RCX+16]; - add RAX, 32; - add RCX, 32; - pmulld XMM0, XMM2; - pmulld XMM1, XMM3; - movdqu [RSI -32], XMM0; - movdqu [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startsse41u; - - mov aptr, RSI; - mov bptr, RAX; - mov cptr, RCX; - } - } - else - { - asm - { - mov RSI, aptr; - mov RDI, n; - mov RAX, bptr; - mov RCX, cptr; - - align 4; - startsse41a: - add RSI, 32; - movdqa XMM0, [RAX]; - movdqa XMM2, [RCX]; - movdqa XMM1, [RAX+16]; - movdqa XMM3, [RCX+16]; - add RAX, 32; - add RCX, 32; - pmulld XMM0, XMM2; - pmulld XMM1, XMM3; - movdqa [RSI -32], XMM0; - movdqa [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startsse41a; - - mov aptr, RSI; - mov bptr, RAX; - mov cptr, RCX; - } - } - } - else if (a.length >= 4) - { - if (!aligned) - {//possibly not a good idea. Performance? - asm - { - mov RSI, aptr; - mov RAX, bptr; - mov RCX, cptr; - - movdqu XMM0, [RAX]; - movdqu XMM1, [RCX]; - pmulld XMM0, XMM1; - movdqu [RSI], XMM0; - - add RSI, 16; - add RAX, 16; - add RCX, 16; - - mov aptr, RSI; - mov bptr, RAX; - mov cptr, RCX; - } - } - else - { - asm - { - mov RSI, aptr; - mov RAX, bptr; - mov RCX, cptr; - - movdqa XMM0, [RAX]; - movdqa XMM1, [RCX]; - pmulld XMM0, XMM1; - movdqu [RSI], XMM0; - - add RSI, 16; - add RAX, 16; - add RCX, 16; - - mov aptr, RSI; - mov bptr, RAX; - mov cptr, RCX; - } - } - } - } - } - - - while (aptr < aend) - *aptr++ = *bptr++ * *cptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceMulSliceAssign_i unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - for (size_t dim = 7; dim < 68; dim += 60) - { - for (int j = 0; j < 2; j++) - { - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { - a[i] = cast(T)(i-10); - b[i] = cast(T)(i-3); - c[i] = cast(T)((i-10) * 2); - } - - c[] = a[] * b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] * b[i])) - { - printf("[%d]: %d != %d * %d\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] *= value - */ - -T[] _arrayExpSliceMulass_w(T[] a, T value) -{ - return _arrayExpSliceMulass_i(a, value); -} - -T[] _arrayExpSliceMulass_k(T[] a, T value) -{ - return _arrayExpSliceMulass_i(a, value); -} - -T[] _arrayExpSliceMulass_i(T[] a, T value) -{ - //printf("_arrayExpSliceMulass_i(a.length = %d, value = %Lg)\n", a.length, cast(real)value); - auto aptr = a.ptr; - auto aend = aptr + a.length; - - version (D_InlineAsm_X86) - { - if (sse41) - { - auto aligned = ((cast(size_t) aptr) & 15) == 0; - - if (a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (!aligned) - { - asm - { - mov ESI, aptr; - mov EDI, n; - movd XMM2,value; - pshufd XMM2, XMM2, 0; - - align 4; - startsse41u: - movdqu XMM0, [ESI]; - movdqu XMM1, [ESI+16]; - add ESI, 32; - pmulld XMM0, XMM2; - pmulld XMM1, XMM2; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse41u; - - mov aptr, ESI; - } - } - else - { - asm - { - mov ESI, aptr; - mov EDI, n; - movd XMM2,value; - pshufd XMM2, XMM2, 0; - - align 4; - startsse41a: - movdqa XMM0, [ESI]; - movdqa XMM1, [ESI+16]; - add ESI, 32; - pmulld XMM0, XMM2; - pmulld XMM1, XMM2; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse41a; - - mov aptr, ESI; - } - } - } - else if (a.length >= 4) - { - if (!aligned) - { - asm - { - mov ESI, aptr; - movd XMM2,value; - pshufd XMM2, XMM2, 0; - - movdqu XMM0, [ESI]; - pmulld XMM0, XMM2; - movdqu [ESI], XMM0; - - add ESI, 16; - mov aptr, ESI; - } - } - else - { - asm - { - mov ESI, aptr; - movd XMM2,value; - pshufd XMM2, XMM2, 0; - - movdqa XMM0, [ESI]; - pmulld XMM0, XMM2; - movdqa [ESI], XMM0; - - add ESI, 16; - mov aptr, ESI; - } - } - } - } - } - version (D_InlineAsm_X86_64) - { - if (sse41) - { - auto aligned = ((cast(size_t) aptr) & 15) == 0; - - if (a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (!aligned) - { - asm - { - mov RSI, aptr; - mov RDI, n; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startsse41u: - movdqu XMM0, [RSI]; - movdqu XMM1, [RSI+16]; - add RSI, 32; - pmulld XMM0, XMM2; - pmulld XMM1, XMM2; - movdqu [RSI -32], XMM0; - movdqu [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startsse41u; - - mov aptr, RSI; - } - } - else - { - asm - { - mov RSI, aptr; - mov RDI, n; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - align 4; - startsse41a: - movdqa XMM0, [RSI]; - movdqa XMM1, [RSI+16]; - add RSI, 32; - pmulld XMM0, XMM2; - pmulld XMM1, XMM2; - movdqa [RSI -32], XMM0; - movdqa [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startsse41a; - - mov aptr, RSI; - } - } - } - else if (a.length >= 4) - { - if (!aligned) - { //is the overhead worth it? - asm - { - mov RSI, aptr; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - movdqu XMM0, [RSI]; - pmulld XMM0, XMM2; - movdqu [RSI], XMM0; - - add RSI, 16; - mov aptr, RSI; - } - } - else - { - asm - { - mov RSI, aptr; - movd XMM2, value; - pshufd XMM2, XMM2, 0; - - movdqa XMM0, [RSI]; - pmulld XMM0, XMM2; - movdqa [RSI], XMM0; - - add RSI, 16; - mov aptr, RSI; - } - } - } - } - } - - while (aptr < aend) - *aptr++ *= value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMulass_i unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (size_t dim = 7; dim < 68; dim += 60) - { - for (int j = 0; j < 2; j++) - { - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { - a[i] = cast(T)(i-10); - b[i] = cast(T)(i-3); - } - - b[] = a[]; - a[] *= 6; - - for (int i = 0; i < dim; i++) - { - if (a[i] != cast(T)(b[i] * 6)) - { - printf("[%d]: %d != %d * 6\n", i, a[i], b[i]); - assert(0); - } - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] *= b[] - */ - -T[] _arraySliceSliceMulass_w(T[] a, T[] b) -{ - return _arraySliceSliceMulass_i(a, b); -} - -T[] _arraySliceSliceMulass_k(T[] a, T[] b) -{ - return _arraySliceSliceMulass_i(a, b); -} - -T[] _arraySliceSliceMulass_i(T[] a, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceSliceMulass_i()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - if (sse41) - { - auto aligned = ((cast(size_t) aptr) & 15) == 0; - - if (a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (!aligned) - { - asm - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - startsse41u: - movdqu XMM0, [ESI]; - movdqu XMM2, [ECX]; - movdqu XMM1, [ESI+16]; - movdqu XMM3, [ECX+16]; - add ESI, 32; - add ECX, 32; - pmulld XMM0, XMM2; - pmulld XMM1, XMM3; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse41u; - - mov aptr, ESI; - mov bptr, ECX; - } - } - else - { - asm - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - startsse41a: - movdqa XMM0, [ESI]; - movdqa XMM2, [ECX]; - movdqa XMM1, [ESI+16]; - movdqa XMM3, [ECX+16]; - add ESI, 32; - add ECX, 32; - pmulld XMM0, XMM2; - pmulld XMM1, XMM3; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse41a; - - mov aptr, ESI; - mov bptr, ECX; - } - } - } - else if (a.length >= 4) - { - if (!aligned) - {//is the unaligned overhead worth it - asm - { - mov ESI, aptr; - mov ECX, bptr; - - movdqu XMM0, [ESI]; - movdqu XMM2, [ECX]; - - pmulld XMM0, XMM2; - movdqu [ESI], XMM0; - - add ESI, 16; - add ECX, 16; - - mov aptr, ESI; - mov bptr, ECX; - } - } - else - { - asm - { - mov ESI, aptr; - mov ECX, bptr; - - movdqa XMM0, [ESI]; - movdqa XMM2, [ECX]; - - pmulld XMM0, XMM2; - movdqa [ESI], XMM0; - - add ESI, 16; - add ECX, 16; - - mov aptr, ESI; - mov bptr, ECX; - } - } - } - } - } - version (D_InlineAsm_X86_64) - { - if (sse41) - { - auto aligned = ((cast(size_t) aptr) & 15) == 0; - - if (a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - if (!aligned) - { - asm - { - mov RSI, aptr; - mov RDI, n; - mov RCX, bptr; - - align 4; - startsse41u: - movdqu XMM0, [RSI]; - movdqu XMM2, [RCX]; - movdqu XMM1, [RSI+16]; - movdqu XMM3, [RCX+16]; - add RSI, 32; - add RCX, 32; - pmulld XMM0, XMM2; - pmulld XMM1, XMM3; - movdqu [RSI -32], XMM0; - movdqu [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startsse41u; - - mov aptr, RSI; - mov bptr, RCX; - } - } - else - { - asm - { - mov RSI, aptr; - mov RDI, n; - mov RCX, bptr; - - align 4; - startsse41a: - movdqa XMM0, [RSI]; - movdqa XMM2, [RCX]; - movdqa XMM1, [RSI+16]; - movdqa XMM3, [RCX+16]; - add RSI, 32; - add RCX, 32; - pmulld XMM0, XMM2; - pmulld XMM1, XMM3; - movdqa [RSI -32], XMM0; - movdqa [RSI+16-32], XMM1; - cmp RSI, RDI; - jb startsse41a; - - mov aptr, RSI; - mov bptr, RCX; - } - } - } - else if (a.length >= 4) - { - if (!aligned) - {//is the unaligned overhead worth it - asm - { - mov RSI, aptr; - mov RCX, bptr; - - movdqu XMM0, [RSI]; - movdqu XMM2, [RCX]; - - pmulld XMM0, XMM2; - movdqu [RSI], XMM0; - - add RSI, 16; - add RCX, 16; - - mov aptr, RSI; - mov bptr, RCX; - } - } - else - { - asm - { - mov RSI, aptr; - mov RCX, bptr; - - movdqa XMM0, [RSI]; - movdqa XMM2, [RCX]; - - pmulld XMM0, XMM2; - movdqa [RSI], XMM0; - - add RSI, 16; - add RCX, 16; - - mov aptr, RSI; - mov bptr, RCX; - } - } - } - } - } - while (aptr < aend) - *aptr++ *= *bptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceMulass_i unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (size_t dim = 7; dim < 68; dim += 60) - { - for (int j = 0; j < 2; j++) - { - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { - a[i] = cast(T)(i-10); - b[i] = cast(T)(i-3); - c[i] = cast(T)((i-10) * 2); - } - - b[] = a[]; - a[] *= c[]; - - for (int i = 0; i < dim; i++) - { - if (a[i] != cast(T)(b[i] * c[i])) - { - printf("[%d]: %d != %d * %d\n", i, a[i], b[i], c[i]); - assert(0); - } - } - } - } - } -} diff --git a/libphobos/libdruntime/rt/arrayreal.d b/libphobos/libdruntime/rt/arrayreal.d deleted file mode 100644 index 86f274796..000000000 --- a/libphobos/libdruntime/rt/arrayreal.d +++ /dev/null @@ -1,226 +0,0 @@ -/** - * Contains SSE2 and MMX versions of certain operations for real. - * - * Copyright: Copyright Digital Mars 2008 - 2010. - * License: Boost License 1.0. - * Authors: Walter Bright, based on code originally written by Burton Radons - */ - -/* Copyright Digital Mars 2008 - 2010. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -module rt.arrayreal; - -// debug=PRINTF - -import core.cpuid; -import rt.util.array; - -version (unittest) -{ - private import core.stdc.stdio : printf; - /* This is so unit tests will test every CPU variant - */ - int cpuid; - const int CPUID_MAX = 1; - @property bool mmx() { return cpuid == 1 && core.cpuid.mmx; } - @property bool sse() { return cpuid == 2 && core.cpuid.sse; } - @property bool sse2() { return cpuid == 3 && core.cpuid.sse2; } - @property bool amd3dnow() { return cpuid == 4 && core.cpuid.amd3dnow; } -} -else -{ - alias core.cpuid.mmx mmx; - alias core.cpuid.sse sse; - alias core.cpuid.sse2 sse2; - alias core.cpuid.amd3dnow amd3dnow; -} - -//version = log; - -alias real T; - -extern (C) @trusted nothrow: - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] + c[] - */ - -T[] _arraySliceSliceAddSliceAssign_r(T[] a, T[] c, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - enforceTypedArraysConformable("vector operation", a, c); - - foreach (i; 0..a.length) - a[i] = b[i] + c[i]; - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceAddSliceAssign_r unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] + b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + b[i])) - { - printf("[%d]: %Lg != %Lg + %Lg\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] - c[] - */ - -T[] _arraySliceSliceMinSliceAssign_r(T[] a, T[] c, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - enforceTypedArraysConformable("vector operation", a, c); - - foreach (i; 0..a.length) - a[i] = b[i] - c[i]; - return a; -} - - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceMinSliceAssign_r unittest\n"); - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] - b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] - b[i])) - { - printf("[%d]: %Lg != %Lg - %Lg\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] -= b[] * value - */ - -T[] _arraySliceExpMulSliceMinass_r(T[] a, T value, T[] b) -{ - return _arraySliceExpMulSliceAddass_r(a, -value, b); -} - -/*********************** - * Computes: - * a[] += b[] * value - */ - -T[] _arraySliceExpMulSliceAddass_r(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - // Handle remainder - while (aptr < aend) - *aptr++ += *bptr++ * value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpMulSliceAddass_r unittest\n"); - - cpuid = 1; - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 1; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - b[] = c[]; - c[] += a[] * 6; - - for (int i = 0; i < dim; i++) - { - //printf("[%d]: %Lg ?= %Lg + %Lg * 6\n", i, c[i], b[i], a[i]); - if (c[i] != cast(T)(b[i] + a[i] * 6)) - { - printf("[%d]: %Lg ?= %Lg + %Lg * 6\n", i, c[i], b[i], a[i]); - assert(0); - } - } - } - } -} diff --git a/libphobos/libdruntime/rt/arrayshort.d b/libphobos/libdruntime/rt/arrayshort.d deleted file mode 100644 index ed163d6e4..000000000 --- a/libphobos/libdruntime/rt/arrayshort.d +++ /dev/null @@ -1,2259 +0,0 @@ -/** - * Contains SSE2 and MMX versions of certain operations for wchar, short, - * and ushort ('u', 's' and 't' suffixes). - * - * Copyright: Copyright Digital Mars 2008 - 2010. - * License: Boost License 1.0. - * Authors: Walter Bright, based on code originally written by Burton Radons - */ - -/* Copyright Digital Mars 2008 - 2010. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -module rt.arrayshort; - -// debug=PRINTF - -private import core.cpuid; -import rt.util.array; - -version (unittest) -{ - private import core.stdc.stdio : printf; - /* This is so unit tests will test every CPU variant - */ - int cpuid; - const int CPUID_MAX = 4; - @property bool mmx() { return cpuid == 1 && core.cpuid.mmx; } - @property bool sse() { return cpuid == 2 && core.cpuid.sse; } - @property bool sse2() { return cpuid == 3 && core.cpuid.sse2; } - @property bool amd3dnow() { return cpuid == 4 && core.cpuid.amd3dnow; } -} -else -{ - alias core.cpuid.mmx mmx; - alias core.cpuid.sse sse; - alias core.cpuid.sse2 sse2; - alias core.cpuid.sse2 sse2; -} - -//version = log; - -alias short T; - -extern (C) @trusted nothrow: - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] + value - */ - -T[] _arraySliceExpAddSliceAssign_u(T[] a, T value, T[] b) -{ - return _arraySliceExpAddSliceAssign_s(a, value, b); -} - -T[] _arraySliceExpAddSliceAssign_t(T[] a, T value, T[] b) -{ - return _arraySliceExpAddSliceAssign_s(a, value, b); -} - -T[] _arraySliceExpAddSliceAssign_s(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceExpAddSliceAssign_s()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 3343% faster - if (sse2 && a.length >= 16) - { - auto n = aptr + (a.length & ~15); - - uint l = cast(ushort) value; - l |= (l << 16); - - if (((cast(uint) aptr | cast(uint) bptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM2, l; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2u: - add ESI, 32; - movdqu XMM0, [EAX]; - movdqu XMM1, [EAX+16]; - add EAX, 32; - paddw XMM0, XMM2; - paddw XMM1, XMM2; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2u; - - mov aptr, ESI; - mov bptr, EAX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM2, l; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2a: - add ESI, 32; - movdqa XMM0, [EAX]; - movdqa XMM1, [EAX+16]; - add EAX, 32; - paddw XMM0, XMM2; - paddw XMM1, XMM2; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2a; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - else - // MMX version is 3343% faster - if (mmx && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - uint l = cast(ushort) value; - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd MM2, l; - pshufw MM2, MM2, 0; - - align 4; - startmmx: - add ESI, 16; - movq MM0, [EAX]; - movq MM1, [EAX+8]; - add EAX, 16; - paddw MM0, MM2; - paddw MM1, MM2; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - } - } - } - - while (aptr < aend) - *aptr++ = cast(T)(*bptr++ + value); - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpAddSliceAssign_s unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] + 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + 6)) - { - printf("[%d]: %d != %d + 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] + c[] - */ - -T[] _arraySliceSliceAddSliceAssign_u(T[] a, T[] c, T[] b) -{ - return _arraySliceSliceAddSliceAssign_s(a, c, b); -} - -T[] _arraySliceSliceAddSliceAssign_t(T[] a, T[] c, T[] b) -{ - return _arraySliceSliceAddSliceAssign_s(a, c, b); -} - -T[] _arraySliceSliceAddSliceAssign_s(T[] a, T[] c, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - enforceTypedArraysConformable("vector operation", a, c); - - //printf("_arraySliceSliceAddSliceAssign_s()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - auto cptr = c.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 3777% faster - if (sse2 && a.length >= 16) - { - auto n = aptr + (a.length & ~15); - - if (((cast(uint) aptr | cast(uint) bptr | cast(uint) cptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startsse2u: - add ESI, 32; - movdqu XMM0, [EAX]; - movdqu XMM1, [EAX+16]; - add EAX, 32; - movdqu XMM2, [ECX]; - movdqu XMM3, [ECX+16]; - add ECX, 32; - paddw XMM0, XMM2; - paddw XMM1, XMM3; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2u; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startsse2a: - add ESI, 32; - movdqa XMM0, [EAX]; - movdqa XMM1, [EAX+16]; - add EAX, 32; - movdqa XMM2, [ECX]; - movdqa XMM3, [ECX+16]; - add ECX, 32; - paddw XMM0, XMM2; - paddw XMM1, XMM3; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2a; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - else - // MMX version is 2068% faster - if (mmx && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startmmx: - add ESI, 16; - movq MM0, [EAX]; - movq MM1, [EAX+8]; - add EAX, 16; - movq MM2, [ECX]; - movq MM3, [ECX+8]; - add ECX, 16; - paddw MM0, MM2; - paddw MM1, MM3; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - - while (aptr < aend) - *aptr++ = cast(T)(*bptr++ + *cptr++); - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceAddSliceAssign_s unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] + b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] + b[i])) - { - printf("[%d]: %d != %d + %d\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] += value - */ - -T[] _arrayExpSliceAddass_u(T[] a, T value) -{ - return _arrayExpSliceAddass_s(a, value); -} - -T[] _arrayExpSliceAddass_t(T[] a, T value) -{ - return _arrayExpSliceAddass_s(a, value); -} - -T[] _arrayExpSliceAddass_s(T[] a, T value) -{ - //printf("_arrayExpSliceAddass_s(a.length = %d, value = %Lg)\n", a.length, cast(real)value); - auto aptr = a.ptr; - auto aend = aptr + a.length; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 832% faster - if (sse2 && a.length >= 16) - { - auto n = aptr + (a.length & ~15); - - uint l = cast(ushort) value; - l |= (l << 16); - - if (((cast(uint) aptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - movd XMM2, l; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2u: - movdqu XMM0, [ESI]; - movdqu XMM1, [ESI+16]; - add ESI, 32; - paddw XMM0, XMM2; - paddw XMM1, XMM2; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2u; - - mov aptr, ESI; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - movd XMM2, l; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2a: - movdqa XMM0, [ESI]; - movdqa XMM1, [ESI+16]; - add ESI, 32; - paddw XMM0, XMM2; - paddw XMM1, XMM2; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2a; - - mov aptr, ESI; - } - } - } - else - // MMX version is 826% faster - if (mmx && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - uint l = cast(ushort) value; - - asm - { - mov ESI, aptr; - mov EDI, n; - movd MM2, l; - pshufw MM2, MM2, 0; - - align 4; - startmmx: - movq MM0, [ESI]; - movq MM1, [ESI+8]; - add ESI, 16; - paddw MM0, MM2; - paddw MM1, MM2; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - } - } - } - - while (aptr < aend) - *aptr++ += value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceAddass_s unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - a[] += 6; - - for (int i = 0; i < dim; i++) - { - if (a[i] != cast(T)(c[i] + 6)) - { - printf("[%d]: %d != %d + 6\n", i, a[i], c[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] += b[] - */ - -T[] _arraySliceSliceAddass_u(T[] a, T[] b) -{ - return _arraySliceSliceAddass_s(a, b); -} - -T[] _arraySliceSliceAddass_t(T[] a, T[] b) -{ - return _arraySliceSliceAddass_s(a, b); -} - -T[] _arraySliceSliceAddass_s(T[] a, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceSliceAddass_s()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 2085% faster - if (sse2 && a.length >= 16) - { - auto n = aptr + (a.length & ~15); - - if (((cast(uint) aptr | cast(uint) bptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - startsse2u: - movdqu XMM0, [ESI]; - movdqu XMM1, [ESI+16]; - add ESI, 32; - movdqu XMM2, [ECX]; - movdqu XMM3, [ECX+16]; - add ECX, 32; - paddw XMM0, XMM2; - paddw XMM1, XMM3; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2u; - - mov aptr, ESI; - mov bptr, ECX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - startsse2a: - movdqa XMM0, [ESI]; - movdqa XMM1, [ESI+16]; - add ESI, 32; - movdqa XMM2, [ECX]; - movdqa XMM3, [ECX+16]; - add ECX, 32; - paddw XMM0, XMM2; - paddw XMM1, XMM3; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2a; - - mov aptr, ESI; - mov bptr, ECX; - } - } - } - else - // MMX version is 1022% faster - if (mmx && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - start: - movq MM0, [ESI]; - movq MM1, [ESI+8]; - add ESI, 16; - movq MM2, [ECX]; - movq MM3, [ECX+8]; - add ECX, 16; - paddw MM0, MM2; - paddw MM1, MM3; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb start; - - emms; - mov aptr, ESI; - mov bptr, ECX; - } - } - } - - while (aptr < aend) - *aptr++ += *bptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceAddass_s unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - b[] = c[]; - c[] += a[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(b[i] + a[i])) - { - printf("[%d]: %d != %d + %d\n", i, c[i], b[i], a[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] - value - */ - -T[] _arraySliceExpMinSliceAssign_u(T[] a, T value, T[] b) -{ - return _arraySliceExpMinSliceAssign_s(a, value, b); -} - -T[] _arraySliceExpMinSliceAssign_t(T[] a, T value, T[] b) -{ - return _arraySliceExpMinSliceAssign_s(a, value, b); -} - -T[] _arraySliceExpMinSliceAssign_s(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceExpMinSliceAssign_s()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 3695% faster - if (sse2 && a.length >= 16) - { - auto n = aptr + (a.length & ~15); - - uint l = cast(ushort) value; - l |= (l << 16); - - if (((cast(uint) aptr | cast(uint) bptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM2, l; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2u: - add ESI, 32; - movdqu XMM0, [EAX]; - movdqu XMM1, [EAX+16]; - add EAX, 32; - psubw XMM0, XMM2; - psubw XMM1, XMM2; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2u; - - mov aptr, ESI; - mov bptr, EAX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM2, l; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2a: - add ESI, 32; - movdqa XMM0, [EAX]; - movdqa XMM1, [EAX+16]; - add EAX, 32; - psubw XMM0, XMM2; - psubw XMM1, XMM2; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2a; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - else - // MMX version is 3049% faster - if (mmx && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - uint l = cast(ushort) value; - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd MM2, l; - pshufw MM2, MM2, 0; - - align 4; - startmmx: - add ESI, 16; - movq MM0, [EAX]; - movq MM1, [EAX+8]; - add EAX, 16; - psubw MM0, MM2; - psubw MM1, MM2; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - } - } - } - - while (aptr < aend) - *aptr++ = cast(T)(*bptr++ - value); - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpMinSliceAssign_s unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] - 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] - 6)) - { - printf("[%d]: %d != %d - 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = value - b[] - */ - -T[] _arrayExpSliceMinSliceAssign_u(T[] a, T[] b, T value) -{ - return _arrayExpSliceMinSliceAssign_s(a, b, value); -} - -T[] _arrayExpSliceMinSliceAssign_t(T[] a, T[] b, T value) -{ - return _arrayExpSliceMinSliceAssign_s(a, b, value); -} - -T[] _arrayExpSliceMinSliceAssign_s(T[] a, T[] b, T value) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arrayExpSliceMinSliceAssign_s()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 4995% faster - if (sse2 && a.length >= 16) - { - auto n = aptr + (a.length & ~15); - - uint l = cast(ushort) value; - l |= (l << 16); - - if (((cast(uint) aptr | cast(uint) bptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - - align 4; - startaddsse2u: - movd XMM2, l; - pshufd XMM2, XMM2, 0; - movd XMM3, l; - pshufd XMM3, XMM3, 0; - add ESI, 32; - movdqu XMM0, [EAX]; - movdqu XMM1, [EAX+16]; - add EAX, 32; - psubw XMM2, XMM0; - psubw XMM3, XMM1; - movdqu [ESI -32], XMM2; - movdqu [ESI+16-32], XMM3; - cmp ESI, EDI; - jb startaddsse2u; - - mov aptr, ESI; - mov bptr, EAX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - - align 4; - startaddsse2a: - movd XMM2, l; - pshufd XMM2, XMM2, 0; - movd XMM3, l; - pshufd XMM3, XMM3, 0; - add ESI, 32; - movdqa XMM0, [EAX]; - movdqa XMM1, [EAX+16]; - add EAX, 32; - psubw XMM2, XMM0; - psubw XMM3, XMM1; - movdqa [ESI -32], XMM2; - movdqa [ESI+16-32], XMM3; - cmp ESI, EDI; - jb startaddsse2a; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - else - // MMX version is 4562% faster - if (mmx && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - uint l = cast(ushort) value; - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd MM4, l; - pshufw MM4, MM4, 0; - - align 4; - startmmx: - add ESI, 16; - movq MM2, [EAX]; - movq MM3, [EAX+8]; - movq MM0, MM4; - movq MM1, MM4; - add EAX, 16; - psubw MM0, MM2; - psubw MM1, MM3; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - } - } - } - - while (aptr < aend) - *aptr++ = cast(T)(value - *bptr++); - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMinSliceAssign_s unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = 6 - a[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(6 - a[i])) - { - printf("[%d]: %d != 6 - %d\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] - c[] - */ - -T[] _arraySliceSliceMinSliceAssign_u(T[] a, T[] c, T[] b) -{ - return _arraySliceSliceMinSliceAssign_s(a, c, b); -} - -T[] _arraySliceSliceMinSliceAssign_t(T[] a, T[] c, T[] b) -{ - return _arraySliceSliceMinSliceAssign_s(a, c, b); -} - -T[] _arraySliceSliceMinSliceAssign_s(T[] a, T[] c, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - enforceTypedArraysConformable("vector operation", a, c); - - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - auto cptr = c.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 4129% faster - if (sse2 && a.length >= 16) - { - auto n = aptr + (a.length & ~15); - - if (((cast(uint) aptr | cast(uint) bptr | cast(uint) cptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startsse2u: - add ESI, 32; - movdqu XMM0, [EAX]; - movdqu XMM1, [EAX+16]; - add EAX, 32; - movdqu XMM2, [ECX]; - movdqu XMM3, [ECX+16]; - add ECX, 32; - psubw XMM0, XMM2; - psubw XMM1, XMM3; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2u; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startsse2a: - add ESI, 32; - movdqa XMM0, [EAX]; - movdqa XMM1, [EAX+16]; - add EAX, 32; - movdqa XMM2, [ECX]; - movdqa XMM3, [ECX+16]; - add ECX, 32; - psubw XMM0, XMM2; - psubw XMM1, XMM3; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2a; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - else - // MMX version is 2018% faster - if (mmx && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startmmx: - add ESI, 16; - movq MM0, [EAX]; - movq MM1, [EAX+8]; - add EAX, 16; - movq MM2, [ECX]; - movq MM3, [ECX+8]; - add ECX, 16; - psubw MM0, MM2; - psubw MM1, MM3; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - - while (aptr < aend) - *aptr++ = cast(T)(*bptr++ - *cptr++); - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceMinSliceAssign_s unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] - b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] - b[i])) - { - printf("[%d]: %d != %d - %d\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] -= value - */ - -T[] _arrayExpSliceMinass_u(T[] a, T value) -{ - return _arrayExpSliceMinass_s(a, value); -} - -T[] _arrayExpSliceMinass_t(T[] a, T value) -{ - return _arrayExpSliceMinass_s(a, value); -} - -T[] _arrayExpSliceMinass_s(T[] a, T value) -{ - //printf("_arrayExpSliceMinass_s(a.length = %d, value = %Lg)\n", a.length, cast(real)value); - auto aptr = a.ptr; - auto aend = aptr + a.length; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 835% faster - if (sse2 && a.length >= 16) - { - auto n = aptr + (a.length & ~15); - - uint l = cast(ushort) value; - l |= (l << 16); - - if (((cast(uint) aptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - movd XMM2, l; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2u: - movdqu XMM0, [ESI]; - movdqu XMM1, [ESI+16]; - add ESI, 32; - psubw XMM0, XMM2; - psubw XMM1, XMM2; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2u; - - mov aptr, ESI; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - movd XMM2, l; - pshufd XMM2, XMM2, 0; - - align 4; - startaddsse2a: - movdqa XMM0, [ESI]; - movdqa XMM1, [ESI+16]; - add ESI, 32; - psubw XMM0, XMM2; - psubw XMM1, XMM2; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startaddsse2a; - - mov aptr, ESI; - } - } - } - else - // MMX version is 835% faster - if (mmx && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - uint l = cast(ushort) value; - - asm - { - mov ESI, aptr; - mov EDI, n; - movd MM2, l; - pshufw MM2, MM2, 0; - - align 4; - startmmx: - movq MM0, [ESI]; - movq MM1, [ESI+8]; - add ESI, 16; - psubw MM0, MM2; - psubw MM1, MM2; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - } - } - } - - while (aptr < aend) - *aptr++ -= value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMinass_s unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - a[] = c[]; - a[] -= 6; - - for (int i = 0; i < dim; i++) - { - if (a[i] != cast(T)(c[i] - 6)) - { - printf("[%d]: %d != %d - 6\n", i, a[i], c[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] -= b[] - */ - -T[] _arraySliceSliceMinass_u(T[] a, T[] b) -{ - return _arraySliceSliceMinass_s(a, b); -} - -T[] _arraySliceSliceMinass_t(T[] a, T[] b) -{ - return _arraySliceSliceMinass_s(a, b); -} - -T[] _arraySliceSliceMinass_s(T[] a, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceSliceMinass_s()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 2121% faster - if (sse2 && a.length >= 16) - { - auto n = aptr + (a.length & ~15); - - if (((cast(uint) aptr | cast(uint) bptr) & 15) != 0) - { - asm // unaligned case - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - startsse2u: - movdqu XMM0, [ESI]; - movdqu XMM1, [ESI+16]; - add ESI, 32; - movdqu XMM2, [ECX]; - movdqu XMM3, [ECX+16]; - add ECX, 32; - psubw XMM0, XMM2; - psubw XMM1, XMM3; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2u; - - mov aptr, ESI; - mov bptr, ECX; - } - } - else - { - asm // aligned case - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - startsse2a: - movdqa XMM0, [ESI]; - movdqa XMM1, [ESI+16]; - add ESI, 32; - movdqa XMM2, [ECX]; - movdqa XMM3, [ECX+16]; - add ECX, 32; - psubw XMM0, XMM2; - psubw XMM1, XMM3; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2a; - - mov aptr, ESI; - mov bptr, ECX; - } - } - } - else - // MMX version is 1116% faster - if (mmx && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - start: - movq MM0, [ESI]; - movq MM1, [ESI+8]; - add ESI, 16; - movq MM2, [ECX]; - movq MM3, [ECX+8]; - add ECX, 16; - psubw MM0, MM2; - psubw MM1, MM3; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb start; - - emms; - mov aptr, ESI; - mov bptr, ECX; - } - } - } - - while (aptr < aend) - *aptr++ -= *bptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceMinass_s unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - b[] = c[]; - c[] -= a[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(b[i] - a[i])) - { - printf("[%d]: %d != %d - %d\n", i, c[i], b[i], a[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] * value - */ - -T[] _arraySliceExpMulSliceAssign_u(T[] a, T value, T[] b) -{ - return _arraySliceExpMulSliceAssign_s(a, value, b); -} - -T[] _arraySliceExpMulSliceAssign_t(T[] a, T value, T[] b) -{ - return _arraySliceExpMulSliceAssign_s(a, value, b); -} - -T[] _arraySliceExpMulSliceAssign_s(T[] a, T value, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceExpMulSliceAssign_s()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 3733% faster - if (sse2 && a.length >= 16) - { - auto n = aptr + (a.length & ~15); - - uint l = cast(ushort) value; - l |= l << 16; - - if (((cast(uint) aptr | cast(uint) bptr) & 15) != 0) - { - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM2, l; - pshufd XMM2, XMM2, 0; - - align 4; - startsse2u: - add ESI, 32; - movdqu XMM0, [EAX]; - movdqu XMM1, [EAX+16]; - add EAX, 32; - pmullw XMM0, XMM2; - pmullw XMM1, XMM2; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2u; - - mov aptr, ESI; - mov bptr, EAX; - } - } - else - { - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd XMM2, l; - pshufd XMM2, XMM2, 0; - - align 4; - startsse2a: - add ESI, 32; - movdqa XMM0, [EAX]; - movdqa XMM1, [EAX+16]; - add EAX, 32; - pmullw XMM0, XMM2; - pmullw XMM1, XMM2; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2a; - - mov aptr, ESI; - mov bptr, EAX; - } - } - } - else - // MMX version is 3733% faster - if (mmx && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - uint l = cast(ushort) value; - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - movd MM2, l; - pshufw MM2, MM2, 0; - - align 4; - startmmx: - add ESI, 16; - movq MM0, [EAX]; - movq MM1, [EAX+8]; - add EAX, 16; - pmullw MM0, MM2; - pmullw MM1, MM2; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - } - } - } - - while (aptr < aend) - *aptr++ = cast(T)(*bptr++ * value); - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceExpMulSliceAssign_s unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] * 6; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] * 6)) - { - printf("[%d]: %d != %d * 6\n", i, c[i], a[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] = b[] * c[] - */ - -T[] _arraySliceSliceMulSliceAssign_u(T[] a, T[] c, T[] b) -{ - return _arraySliceSliceMulSliceAssign_s(a, c, b); -} - -T[] _arraySliceSliceMulSliceAssign_t(T[] a, T[] c, T[] b) -{ - return _arraySliceSliceMulSliceAssign_s(a, c, b); -} - -T[] _arraySliceSliceMulSliceAssign_s(T[] a, T[] c, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - enforceTypedArraysConformable("vector operation", a, c); - - //printf("_arraySliceSliceMulSliceAssign_s()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - auto cptr = c.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 2515% faster - if (sse2 && a.length >= 16) - { - auto n = aptr + (a.length & ~15); - - if (((cast(uint) aptr | cast(uint) bptr | cast(uint) cptr) & 15) != 0) - { - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startsse2u: - add ESI, 32; - movdqu XMM0, [EAX]; - movdqu XMM2, [ECX]; - movdqu XMM1, [EAX+16]; - movdqu XMM3, [ECX+16]; - add EAX, 32; - add ECX, 32; - pmullw XMM0, XMM2; - pmullw XMM1, XMM3; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2u; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - else - { - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startsse2a: - add ESI, 32; - movdqa XMM0, [EAX]; - movdqa XMM2, [ECX]; - movdqa XMM1, [EAX+16]; - movdqa XMM3, [ECX+16]; - add EAX, 32; - add ECX, 32; - pmullw XMM0, XMM2; - pmullw XMM1, XMM3; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2a; - - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - else - // MMX version is 2515% faster - if (mmx && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov EAX, bptr; - mov ECX, cptr; - - align 4; - startmmx: - add ESI, 16; - movq MM0, [EAX]; - movq MM2, [ECX]; - movq MM1, [EAX+8]; - movq MM3, [ECX+8]; - add EAX, 16; - add ECX, 16; - pmullw MM0, MM2; - pmullw MM1, MM3; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - mov bptr, EAX; - mov cptr, ECX; - } - } - } - - while (aptr < aend) - *aptr++ = cast(T)(*bptr++ * *cptr++); - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceMulSliceAssign_s unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - c[] = a[] * b[]; - - for (int i = 0; i < dim; i++) - { - if (c[i] != cast(T)(a[i] * b[i])) - { - printf("[%d]: %d != %d * %d\n", i, c[i], a[i], b[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] *= value - */ - -T[] _arrayExpSliceMulass_u(T[] a, T value) -{ - return _arrayExpSliceMulass_s(a, value); -} - -T[] _arrayExpSliceMulass_t(T[] a, T value) -{ - return _arrayExpSliceMulass_s(a, value); -} - -T[] _arrayExpSliceMulass_s(T[] a, T value) -{ - //printf("_arrayExpSliceMulass_s(a.length = %d, value = %Lg)\n", a.length, cast(real)value); - auto aptr = a.ptr; - auto aend = aptr + a.length; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 2044% faster - if (sse2 && a.length >= 16) - { - auto n = aptr + (a.length & ~15); - - uint l = cast(ushort) value; - l |= l << 16; - - if (((cast(uint) aptr) & 15) != 0) - { - asm - { - mov ESI, aptr; - mov EDI, n; - movd XMM2, l; - pshufd XMM2, XMM2, 0; - - align 4; - startsse2u: - movdqu XMM0, [ESI]; - movdqu XMM1, [ESI+16]; - add ESI, 32; - pmullw XMM0, XMM2; - pmullw XMM1, XMM2; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2u; - - mov aptr, ESI; - } - } - else - { - asm - { - mov ESI, aptr; - mov EDI, n; - movd XMM2, l; - pshufd XMM2, XMM2, 0; - - align 4; - startsse2a: - movdqa XMM0, [ESI]; - movdqa XMM1, [ESI+16]; - add ESI, 32; - pmullw XMM0, XMM2; - pmullw XMM1, XMM2; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2a; - - mov aptr, ESI; - } - } - } - else - // MMX version is 2056% faster - if (mmx && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - uint l = cast(ushort) value; - - asm - { - mov ESI, aptr; - mov EDI, n; - movd MM2, l; - pshufw MM2, MM2, 0; - - align 4; - startmmx: - movq MM0, [ESI]; - movq MM1, [ESI+8]; - add ESI, 16; - pmullw MM0, MM2; - pmullw MM1, MM2; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - } - } - } - - while (aptr < aend) - *aptr++ *= value; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arrayExpSliceMulass_s unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - b[] = a[]; - a[] *= 6; - - for (int i = 0; i < dim; i++) - { - if (a[i] != cast(T)(b[i] * 6)) - { - printf("[%d]: %d != %d * 6\n", i, a[i], b[i]); - assert(0); - } - } - } - } -} - - -/* ======================================================================== */ - -/*********************** - * Computes: - * a[] *= b[] - */ - -T[] _arraySliceSliceMulass_u(T[] a, T[] b) -{ - return _arraySliceSliceMulass_s(a, b); -} - -T[] _arraySliceSliceMulass_t(T[] a, T[] b) -{ - return _arraySliceSliceMulass_s(a, b); -} - -T[] _arraySliceSliceMulass_s(T[] a, T[] b) -{ - enforceTypedArraysConformable("vector operation", a, b); - - //printf("_arraySliceSliceMulass_s()\n"); - auto aptr = a.ptr; - auto aend = aptr + a.length; - auto bptr = b.ptr; - - version (D_InlineAsm_X86) - { - // SSE2 aligned version is 2519% faster - if (sse2 && a.length >= 16) - { - auto n = aptr + (a.length & ~15); - - if (((cast(uint) aptr | cast(uint) bptr) & 15) != 0) - { - asm - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - startsse2u: - movdqu XMM0, [ESI]; - movdqu XMM2, [ECX]; - movdqu XMM1, [ESI+16]; - movdqu XMM3, [ECX+16]; - add ESI, 32; - add ECX, 32; - pmullw XMM0, XMM2; - pmullw XMM1, XMM3; - movdqu [ESI -32], XMM0; - movdqu [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2u; - - mov aptr, ESI; - mov bptr, ECX; - } - } - else - { - asm - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - startsse2a: - movdqa XMM0, [ESI]; - movdqa XMM2, [ECX]; - movdqa XMM1, [ESI+16]; - movdqa XMM3, [ECX+16]; - add ESI, 32; - add ECX, 32; - pmullw XMM0, XMM2; - pmullw XMM1, XMM3; - movdqa [ESI -32], XMM0; - movdqa [ESI+16-32], XMM1; - cmp ESI, EDI; - jb startsse2a; - - mov aptr, ESI; - mov bptr, ECX; - } - } - } - else - // MMX version is 1712% faster - if (mmx && a.length >= 8) - { - auto n = aptr + (a.length & ~7); - - asm - { - mov ESI, aptr; - mov EDI, n; - mov ECX, bptr; - - align 4; - startmmx: - movq MM0, [ESI]; - movq MM2, [ECX]; - movq MM1, [ESI+8]; - movq MM3, [ECX+8]; - add ESI, 16; - add ECX, 16; - pmullw MM0, MM2; - pmullw MM1, MM3; - movq [ESI -16], MM0; - movq [ESI+8-16], MM1; - cmp ESI, EDI; - jb startmmx; - - emms; - mov aptr, ESI; - mov bptr, ECX; - } - } - } - - while (aptr < aend) - *aptr++ *= *bptr++; - - return a; -} - -unittest -{ - debug(PRINTF) printf("_arraySliceSliceMulass_s unittest\n"); - - for (cpuid = 0; cpuid < CPUID_MAX; cpuid++) - { - version (log) printf(" cpuid %d\n", cpuid); - - for (int j = 0; j < 2; j++) - { - const int dim = 67; - T[] a = new T[dim + j]; // aligned on 16 byte boundary - a = a[j .. dim + j]; // misalign for second iteration - T[] b = new T[dim + j]; - b = b[j .. dim + j]; - T[] c = new T[dim + j]; - c = c[j .. dim + j]; - - for (int i = 0; i < dim; i++) - { a[i] = cast(T)i; - b[i] = cast(T)(i + 7); - c[i] = cast(T)(i * 2); - } - - b[] = a[]; - a[] *= c[]; - - for (int i = 0; i < dim; i++) - { - if (a[i] != cast(T)(b[i] * c[i])) - { - printf("[%d]: %d != %d * %d\n", i, a[i], b[i], c[i]); - assert(0); - } - } - } - } -} diff --git a/libphobos/libdruntime/rt/critical_.d b/libphobos/libdruntime/rt/critical_.d index 1d40198f6..46ff79153 100644 --- a/libphobos/libdruntime/rt/critical_.d +++ b/libphobos/libdruntime/rt/critical_.d @@ -13,9 +13,8 @@ */ /* NOTE: This file has been patched from the original DMD distribution to - work with the GDC compiler. - Modified by Iain Buclaw, December 2013 -*/ + * work with the GDC compiler. + */ module rt.critical_; private diff --git a/libphobos/libdruntime/rt/deh.d b/libphobos/libdruntime/rt/deh.d index 887d87759..131a364f7 100644 --- a/libphobos/libdruntime/rt/deh.d +++ b/libphobos/libdruntime/rt/deh.d @@ -9,6 +9,9 @@ * Source: $(DRUNTIMESRC src/rt/deh.d) */ +/* NOTE: This file has been patched from the original DMD distribution to + * work with the GDC compiler. + */ module rt.deh; extern (C) diff --git a/libphobos/libdruntime/rt/dmain2.d b/libphobos/libdruntime/rt/dmain2.d index 237812479..f7d5db272 100644 --- a/libphobos/libdruntime/rt/dmain2.d +++ b/libphobos/libdruntime/rt/dmain2.d @@ -10,16 +10,15 @@ */ /* NOTE: This file has been patched from the original DMD distribution to - work with the GDC compiler. - - Modified by Iain Buclaw, September 2010. -*/ + * work with the GDC compiler. + */ module rt.dmain2; private { import rt.memory; import rt.util.string; + import core.atomic; import core.stdc.stddef; import core.stdc.stdlib; import core.stdc.string; @@ -156,7 +155,7 @@ extern (C) int rt_init() initialize different D libraries without knowing about the shared druntime. Also we need to attach any thread that calls rt_init. */ - if (_initCount++) return 1; + if (atomicOp!"+="(_initCount, 1) > 1) return 1; _STI_monitor_staticctor(); _STI_critical_init(); @@ -185,7 +184,7 @@ extern (C) int rt_init() extern (C) int rt_term() { if (!_initCount) return 0; // was never initialized - if (--_initCount) return 1; + if (atomicOp!"-="(_initCount, 1)) return 1; try { @@ -291,9 +290,6 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) } } - // Allocate args[] on the stack - char[][] args = (cast(char[]*) alloca(argc * (char[]).sizeof))[0 .. argc]; - version (Windows) { /* Because we want args[] to be UTF-8, and Windows doesn't guarantee that, @@ -305,7 +301,10 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) immutable size_t wCommandLineLength = wcslen(wCommandLine); int wargc; wchar_t** wargs = CommandLineToArgvW(wCommandLine, &wargc); - assert(wargc == argc); + // assert(wargc == argc); /* argc can be broken by Unicode arguments */ + + // Allocate args[] on the stack - use wargc + char[][] args = (cast(char[]*) alloca(wargc * (char[]).sizeof))[0 .. wargc]; // This is required because WideCharToMultiByte requires int as input. assert(wCommandLineLength <= cast(size_t) int.max, "Wide char command line length must not exceed int.max"); @@ -333,6 +332,9 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) } else version (Posix) { + // Allocate args[] on the stack + char[][] args = (cast(char[]*) alloca(argc * (char[]).sizeof))[0 .. argc]; + size_t totalArgsLength = 0; foreach(i, ref arg; args) { @@ -348,10 +350,10 @@ extern (C) int _d_run_main(int argc, char **argv, MainFunc mainFunc) * This also means that when this function returns, _d_args will refer to garbage. */ { - auto buff = cast(char[]*) alloca(argc * (char[]).sizeof + totalArgsLength); + auto buff = cast(char[]*) alloca(args.length * (char[]).sizeof + totalArgsLength); - char[][] argsCopy = buff[0 .. argc]; - auto argBuff = cast(char*) (buff + argc); + char[][] argsCopy = buff[0 .. args.length]; + auto argBuff = cast(char*) (buff + args.length); foreach(i, arg; args) { argsCopy[i] = (argBuff[0 .. arg.length] = arg[]); diff --git a/libphobos/libdruntime/rt/image.d b/libphobos/libdruntime/rt/image.d deleted file mode 100644 index d4ac5bc48..000000000 --- a/libphobos/libdruntime/rt/image.d +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Copyright: Copyright Digital Mars 2010. - * License: Boost License 1.0. - * Authors: Jacob Carlborg - * Version: Initial created: Feb 23, 2010 - */ - -/* Copyright Digital Mars 2010. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -module rt.image; - -version (OSX): - -import src.core.sys.osx.mach.dyld; -import src.core.sys.osx.mach.loader; -import src.core.sys.osx.mach.getsect; - -struct Image -{ - private mach_header* header_; - private uint index_; - - this (uint index) - { - header_ = _dyld_get_image_header(index); - index_ = index; - } - - static uint numberOfImages () - { - return _dyld_image_count; - } - - static int opApply (int delegate(ref Image) dg) - { - int result; - - for (size_t i = 0; i < numberOfImages; i++) - { - result = dg(Image(i)); - - if (result) - break; - } - - return result; - } - - static int opApplyReverse (int delegate(ref Image) dg) - { - int result; - - for (int i = numberOfImages - 1; i >= 0; i--) - { - result = dg(Image(i)); - - if (result) - break; - } - - return result; - } - - mach_header* header () - { - return header_; - } - - mach_header_64* header64 () - { - return cast(mach_header_64*) header_; - } - - CPU cpu () - { - return CPU(header_); - } -} - -struct CPU -{ - private mach_header* header; - - static CPU opCall (mach_header* header) - { - CPU cpu; - cpu.header = header; - - return cpu; - } - - bool is32bit () - { - return (header.magic == MH_MAGIC); - } - - bool is64bit () - { - return (header.magic == MH_MAGIC_64); - } -} - -T[] getSectionData (T, string segmentName, string sectionName) () -{ - T[] array; - - const c_segmentName = segmentName.ptr; - const c_sectionName = sectionName.ptr; - - void* start; - void* end; - - foreach_reverse (image ; Image) - { - if (image.cpu.is32bit) - { - auto header = image.header; - section* sect = getsectbynamefromheader(header, c_segmentName, c_sectionName); - - if (sect is null || sect.size == 0) - continue; - - start = cast(void*) (cast(byte*) header + sect.offset); - end = cast(void*) (cast(byte*) start + sect.size); - } - - else - { - auto header = image.header64; - section_64* sect = getsectbynamefromheader_64(header, c_segmentName, c_sectionName); - - if (sect is null || sect.size == 0) - continue; - - start = cast(void*) (cast(byte*) header + sect.offset); - end = cast(void*) (cast(byte*) start + sect.size); - } - - size_t len = cast(T*)end - cast(T*)start; - array ~= (cast(T*)start)[0 .. len]; - } - - return array; -} - diff --git a/libphobos/libdruntime/rt/lifetime.d b/libphobos/libdruntime/rt/lifetime.d index f6ecba017..258846fa1 100644 --- a/libphobos/libdruntime/rt/lifetime.d +++ b/libphobos/libdruntime/rt/lifetime.d @@ -11,61 +11,33 @@ */ /* NOTE: This file has been patched from the original DMD distribution to - work with the GDC compiler. - Modified by Iain Buclaw, July 2010 -*/ - + * work with the GDC compiler. + */ module rt.lifetime; import core.stdc.stdlib; import core.stdc.string; import core.stdc.stdarg; import core.bitop; -static import core.memory; -private alias BlkAttr = core.memory.GC.BlkAttr; +import core.memory; debug(PRINTF) import core.stdc.stdio; static import rt.tlsgc; +alias BlkInfo = GC.BlkInfo; +alias BlkAttr = GC.BlkAttr; +import core.exception : onOutOfMemoryError, onFinalizeError; + private { - package struct BlkInfo - { - void* base; - size_t size; - uint attr; - } - - extern (C) uint gc_getAttr( in void* p ); - extern (C) uint gc_isCollecting( in void* p ); - extern (C) uint gc_setAttr( in void* p, uint a ); - extern (C) uint gc_clrAttr( in void* p, uint a ); - - extern (C) void* gc_malloc( size_t sz, uint ba = 0 ); - extern (C) BlkInfo gc_qalloc( size_t sz, uint ba = 0 ); - extern (C) void* gc_calloc( size_t sz, uint ba = 0 ); - extern (C) size_t gc_extend( void* p, size_t mx, size_t sz ); - extern (C) void gc_free( void* p ); - - extern (C) void* gc_addrOf( in void* p ); - extern (C) size_t gc_sizeOf( in void* p ); - extern (C) BlkInfo gc_query( in void* p ); - - extern (C) void onFinalizeError( ClassInfo c, Throwable e ) @safe pure nothrow; - extern (C) void onOutOfMemoryError() @trusted /* pure dmd @@@BUG11461@@@ */ nothrow; - - extern (C) void _d_monitordelete(Object h, bool det = true); - - enum - { - PAGESIZE = 4096 - } - alias bool function(Object) CollectHandler; __gshared CollectHandler collectHandler = null; - enum : size_t + extern (C) void _d_monitordelete(Object h, bool det); + + enum : size_t { - BIGLENGTHMASK = ~(cast(size_t)PAGESIZE - 1), + PAGESIZE = 4096, + BIGLENGTHMASK = ~(PAGESIZE - 1), SMALLPAD = 1, MEDPAD = ushort.sizeof, LARGEPREFIX = 16, // 16 bytes padding at the front of the array @@ -81,7 +53,7 @@ private */ extern (C) void* _d_allocmemory(size_t sz) { - return gc_malloc(sz); + return GC.malloc(sz); } @@ -112,7 +84,7 @@ extern (C) Object _d_newclass(const ClassInfo ci) attr &= ~BlkAttr.FINALIZE; if (ci.m_flags & TypeInfo_Class.ClassFlags.noPointers) attr |= BlkAttr.NO_SCAN; - p = gc_malloc(ci.init.length, attr); + p = GC.malloc(ci.init.length, attr, ci); debug(PRINTF) printf(" p = %p\n", p); } @@ -186,7 +158,7 @@ extern (C) void _d_delclass(Object* p) { rt_finalize(cast(void*) *p); } - gc_free(cast(void*) *p); + GC.free(cast(void*) *p); *p = null; } } @@ -231,7 +203,7 @@ private class ArrayAllocLengthLock where elem0 starts 16 bytes after the first byte. */ -bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, size_t oldlength = ~0) +bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, size_t oldlength = ~0) pure nothrow { if(info.size <= 256) { @@ -243,13 +215,15 @@ bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, si { if(isshared) { - synchronized(typeid(ArrayAllocLengthLock)) + try synchronized(typeid(ArrayAllocLengthLock)) { if(*length == cast(ubyte)oldlength) *length = cast(ubyte)newlength; else return false; } + catch (Throwable t) + assert(0, "Failed to synchronize."); } else { @@ -275,13 +249,15 @@ bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, si { if(isshared) { - synchronized(typeid(ArrayAllocLengthLock)) + try synchronized(typeid(ArrayAllocLengthLock)) { if(*length == oldlength) *length = cast(ushort)newlength; else return false; } + catch (Throwable t) + assert(0, "Failed to synchronize."); } else { @@ -307,13 +283,15 @@ bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, si { if(isshared) { - synchronized(typeid(ArrayAllocLengthLock)) + try synchronized(typeid(ArrayAllocLengthLock)) { if(*length == oldlength) *length = newlength; else return false; } + catch (Throwable t) + assert(0, "Failed to synchronize."); } else { @@ -402,7 +380,7 @@ static ~this() // we expect this to be called with the lock in place -void processGCMarks(BlkInfo* cache, scope rt.tlsgc.IsMarkedDg isMarked) +void processGCMarks(BlkInfo* cache, scope rt.tlsgc.IsMarkedDg isMarked) nothrow { // called after the mark routine to eliminate block cache data when it // might be ready to sweep @@ -428,7 +406,6 @@ void processGCMarks(BlkInfo* cache, scope rt.tlsgc.IsMarkedDg isMarked) unittest { - import core.memory; // Bugzilla 10701 - segfault in GC ubyte[] result; result.length = 4096; GC.free(result.ptr); @@ -553,7 +530,7 @@ void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow * It doesn't matter what the current allocated length of the array is, the * user is telling the runtime that he knows what he is doing. */ -extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) +extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) /+nothrow+/ { // note, we do not care about shared. We are setting the length no matter // what, so no lock is required. @@ -561,13 +538,16 @@ extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) auto size = ti.next.tsize; // array element size auto cursize = arr.length * size; auto bic = __getBlkInfo(arr.ptr); - auto info = bic ? *bic : gc_query(arr.ptr); + auto info = bic ? *bic : GC.query(arr.ptr); if(info.base && (info.attr & BlkAttr.APPENDABLE)) { if(info.size >= PAGESIZE) // remove prefix from the current stored size cursize -= LARGEPREFIX; debug(PRINTF) printf("setting allocated size to %d\n", (arr.ptr - info.base) + cursize); + + // Note: Since we "assume" the append is safe, it means it is not shared. + // Since it is not shared, we also know it won't throw (no lock). __setArrayAllocLength(info, (arr.ptr - info.base) + cursize, false); } } @@ -624,7 +604,7 @@ body // step 1, get the block auto isshared = typeid(ti) is typeid(TypeInfo_Shared); auto bic = !isshared ? __getBlkInfo((*p).ptr) : null; - auto info = bic ? *bic : gc_query((*p).ptr); + auto info = bic ? *bic : GC.query((*p).ptr); auto size = ti.next.tsize; version (D_InlineAsm_X86) { @@ -717,7 +697,7 @@ Lcontinue: if(info.size >= PAGESIZE && curcapacity != 0) { auto extendsize = reqsize + offset + LARGEPAD - info.size; - auto u = gc_extend((*p).ptr, extendsize, extendsize); + auto u = GC.extend((*p).ptr, extendsize, extendsize); if(u) { // extend worked, save the new current allocated size @@ -733,7 +713,7 @@ Lcontinue: reqsize += __arrayPad(reqsize); // copy attributes from original block, or from the typeinfo if the // original block doesn't exist. - info = gc_qalloc(reqsize, (info.base ? info.attr : (!(ti.next.flags & 1) ? BlkAttr.NO_SCAN : 0)) | BlkAttr.APPENDABLE); + info = GC.qalloc(reqsize, (info.base ? info.attr : (!(ti.next.flags & 1) ? BlkAttr.NO_SCAN : 0)) | BlkAttr.APPENDABLE); if(info.base is null) goto Loverflow; // copy the data over. @@ -778,146 +758,108 @@ Lcontinue: } /** - * Allocate a new array of length elements. + * Allocate a new uninitialized array of length elements. * ti is the type of the resulting array, or pointer to element. - * (For when the array is initialized to 0) */ -extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) +extern (C) void[] _d_newarrayU(const TypeInfo ti, size_t length) pure nothrow { - void[] result; - auto size = ti.next.tsize; // array element size + auto size = ti.next.tsize; - debug(PRINTF) printf("_d_newarrayT(length = x%x, size = %d)\n", length, size); + debug(PRINTF) printf("_d_newarrayU(length = x%x, size = %d)\n", length, size); if (length == 0 || size == 0) - result = null; - else + return null; + + version (D_InlineAsm_X86) { - version (D_InlineAsm_X86) + asm { - asm - { - mov EAX,size ; - mul EAX,length ; - mov size,EAX ; - jc Loverflow ; - } + mov EAX,size ; + mul EAX,length ; + mov size,EAX ; + jnc Lcontinue ; } - else version(D_InlineAsm_X86_64) + } + else version(D_InlineAsm_X86_64) + { + asm { - asm - { - mov RAX,size ; - mul RAX,length ; - mov size,RAX ; - jc Loverflow ; - } + mov RAX,size ; + mul RAX,length ; + mov size,RAX ; + jnc Lcontinue ; } - else + } + else + { + auto newsize = size * length; + if (newsize / length == size) { - auto newsize = size * length; - if (newsize / length != size) - goto Loverflow; - size = newsize; + goto Lcontinue; } - - // increase the size by the array pad. - auto pad = __arrayPad(size); - if (size + pad < size) - goto Loverflow; - - auto info = gc_qalloc(size + pad, !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); - debug(PRINTF) printf(" p = %p\n", info.base); - // update the length of the array - auto arrstart = __arrayStart(info); - memset(arrstart, 0, size); - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - __setArrayAllocLength(info, size, isshared); - result = arrstart[0..length]; } - return result; - Loverflow: onOutOfMemoryError(); assert(0); +Lcontinue: + + // increase the size by the array pad. + auto pad = __arrayPad(size); + if (size + pad < size) + goto Loverflow; + + auto info = GC.qalloc(size + pad, !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); + debug(PRINTF) printf(" p = %p\n", info.base); + // update the length of the array + auto arrstart = __arrayStart(info); + auto isshared = typeid(ti) is typeid(TypeInfo_Shared); + __setArrayAllocLength(info, size, isshared); + return arrstart[0..length]; } /** - * For when the array has a non-zero initializer. + * Allocate a new array of length elements. + * ti is the type of the resulting array, or pointer to element. + * (For when the array is initialized to 0) */ -extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) +extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow { - void[] result; - auto size = ti.next.tsize; // array element size + void[] result = _d_newarrayU(ti, length); + auto size = ti.next.tsize; - debug(PRINTF) printf("_d_newarrayiT(length = %d, size = %d)\n", length, size); + memset(result.ptr, 0, size * length); + return result; +} - if (length == 0 || size == 0) - result = null; - else - { - auto initializer = ti.next.init(); - auto isize = initializer.length; - auto q = initializer.ptr; - version (D_InlineAsm_X86) - { - asm - { - mov EAX,size ; - mul EAX,length ; - mov size,EAX ; - jc Loverflow ; - } - } - else version (D_InlineAsm_X86_64) - { - asm - { - mov RAX,size ; - mul RAX,length ; - mov size,RAX ; - jc Loverflow ; - } - } - else - { - auto newsize = size * length; - if (newsize / length != size) - goto Loverflow; +/** + * For when the array has a non-zero initializer. + */ +extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow +{ + import core.internal.traits : TypeTuple; - size = newsize; - } + void[] result = _d_newarrayU(ti, length); + auto size = ti.next.tsize; - auto info = gc_qalloc(size + __arrayPad(size), !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); - debug(PRINTF) printf(" p = %p\n", info.base); - auto arrstart = __arrayStart(info); - if (isize == 1) - memset(arrstart, *cast(ubyte*)q, size); - else if (isize == int.sizeof) - { - int init = *cast(int*)q; - auto len = size / int.sizeof; - for (size_t u = 0; u < len; u++) - { - (cast(int*)arrstart)[u] = init; - } - } - else - { - for (size_t u = 0; u < size; u += isize) - { - memcpy(arrstart + u, q, isize); - } - } - auto isshared = typeid(ti) is typeid(TypeInfo_Shared); - __setArrayAllocLength(info, size, isshared); - result = arrstart[0..length]; + auto init = ti.next.init(); + + switch (init.length) + { + foreach (T; TypeTuple!(ubyte, ushort, uint, ulong)) + { + case T.sizeof: + (cast(T*)result.ptr)[0 .. size * length / T.sizeof] = *cast(T*)init.ptr; + return result; } - return result; -Loverflow: - onOutOfMemoryError(); - assert(0); + default: + { + immutable sz = init.length; + for (size_t u = 0; u < size * length; u += sz) + memcpy(result.ptr + u, init.ptr, sz); + return result; + } + } } @@ -944,7 +886,7 @@ void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t ndims, size_t* pdim) else { auto allocsize = (void[]).sizeof * dim; - auto info = gc_qalloc(allocsize + __arrayPad(allocsize)); + auto info = GC.qalloc(allocsize + __arrayPad(allocsize)); auto isshared = typeid(ti) is typeid(TypeInfo_Shared); __setArrayAllocLength(info, allocsize, isshared); auto p = __arrayStart(info)[0 .. dim]; @@ -1021,7 +963,7 @@ extern (C) void* _d_newitemT(TypeInfo ti) else {*/ // allocate a block to hold this item - auto ptr = gc_malloc(size, !(ti.next.flags & 1) ? BlkAttr.NO_SCAN : 0); + auto ptr = GC.malloc(size, !(ti.next.flags & 1) ? BlkAttr.NO_SCAN : 0, ti); debug(PRINTF) printf(" p = %p\n", ptr); if(size == ubyte.sizeof) *cast(ubyte*)ptr = 0; @@ -1052,7 +994,7 @@ extern (C) void* _d_newitemiT(TypeInfo ti) auto isize = initializer.length; auto q = initializer.ptr; - auto ptr = gc_malloc(size, !(ti.next.flags & 1) ? BlkAttr.NO_SCAN : 0); + auto ptr = GC.malloc(size, !(ti.next.flags & 1) ? BlkAttr.NO_SCAN : 0, ti); debug(PRINTF) printf(" p = %p\n", ptr); if (isize == 1) *cast(ubyte*)ptr = *cast(ubyte*)q; @@ -1086,7 +1028,7 @@ extern (C) void _d_delarray(void[]* p) assert(!(*p).length || (*p).ptr); if ((*p).ptr) - gc_free((*p).ptr); + GC.free((*p).ptr); *p = null; } } @@ -1133,7 +1075,7 @@ extern (C) void _d_delarray_t(void[]* p, const TypeInfo ti) // clear the data from the cache, it's being deleted. bic.base = null; } - gc_free((*p).ptr); + GC.free((*p).ptr); } *p = null; } @@ -1147,7 +1089,7 @@ extern (C) void _d_delmemory(void* *p) { if (*p) { - gc_free(*p); + GC.free(*p); *p = null; } } @@ -1197,7 +1139,28 @@ extern (C) CollectHandler rt_getCollectHandler() /** * */ -extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true) +extern (C) int rt_hasFinalizerInSegment(void* p, in void[] segment) nothrow +{ + auto ppv = cast(void**) p; + if(!p || !*ppv) + return false; + + auto c = *cast(ClassInfo*)*ppv; + do + { + auto pf = c.destructor; + if (cast(size_t)(pf - segment.ptr) < segment.length) return true; + } + while ((c = c.base) !is null); + + return false; +} + + +/** + * + */ +extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true) nothrow { debug(PRINTF) printf("rt_finalize2(p = %p)\n", p); @@ -1315,7 +1278,7 @@ body { size_t size = (*p).length * sizeelem; auto bic = !isshared ? __getBlkInfo((*p).ptr) : null; - auto info = bic ? *bic : gc_query((*p).ptr); + auto info = bic ? *bic : GC.query((*p).ptr); if(info.base && (info.attr & BlkAttr.APPENDABLE)) { // calculate the extent of the array given the base. @@ -1331,7 +1294,7 @@ body { // not enough space, try extending auto extendsize = newsize + offset + LARGEPAD - info.size; - auto u = gc_extend((*p).ptr, extendsize, extendsize); + auto u = GC.extend((*p).ptr, extendsize, extendsize); if(u) { // extend worked, now try setting the length @@ -1347,7 +1310,7 @@ body } // couldn't do it, reallocate - info = gc_qalloc(newsize + LARGEPAD, info.attr); + info = GC.qalloc(newsize + LARGEPAD, info.attr); __setArrayAllocLength(info, newsize, isshared); if(!isshared) __insertBlkInfoCache(info, bic); @@ -1366,7 +1329,7 @@ body else if(!__setArrayAllocLength(info, newsize + offset, isshared, size + offset)) { // could not resize in place - info = gc_qalloc(newsize + __arrayPad(newsize), info.attr); + info = GC.qalloc(newsize + __arrayPad(newsize), info.attr); goto L2; } else if(!isshared && !bic) @@ -1377,7 +1340,7 @@ body } else { - info = gc_qalloc(newsize + __arrayPad(newsize), (info.base ? info.attr : !(ti.next.flags & 1) ? BlkAttr.NO_SCAN : 0) | BlkAttr.APPENDABLE); + info = GC.qalloc(newsize + __arrayPad(newsize), (info.base ? info.attr : !(ti.next.flags & 1) ? BlkAttr.NO_SCAN : 0) | BlkAttr.APPENDABLE); L2: __setArrayAllocLength(info, newsize, isshared); if(!isshared) @@ -1395,7 +1358,7 @@ body else { // pointer was null, need to allocate - auto info = gc_qalloc(newsize + __arrayPad(newsize), !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); + auto info = GC.qalloc(newsize + __arrayPad(newsize), !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); __setArrayAllocLength(info, newsize, isshared); if(!isshared) __insertBlkInfoCache(info, null); @@ -1493,7 +1456,7 @@ body if (newlength > (*p).length) { auto bic = !isshared ? __getBlkInfo((*p).ptr) : null; - auto info = bic ? *bic : gc_query((*p).ptr); + auto info = bic ? *bic : GC.query((*p).ptr); // calculate the extent of the array given the base. size_t offset = (*p).ptr - __arrayStart(info); @@ -1510,7 +1473,7 @@ body { // not enough space, try extending auto extendsize = newsize + offset + LARGEPAD - info.size; - auto u = gc_extend((*p).ptr, extendsize, extendsize); + auto u = GC.extend((*p).ptr, extendsize, extendsize); if(u) { // extend worked, now try setting the length @@ -1526,7 +1489,7 @@ body } // couldn't do it, reallocate - info = gc_qalloc(newsize + LARGEPAD, info.attr); + info = GC.qalloc(newsize + LARGEPAD, info.attr); __setArrayAllocLength(info, newsize, isshared); if(!isshared) __insertBlkInfoCache(info, bic); @@ -1545,7 +1508,7 @@ body else if(!__setArrayAllocLength(info, newsize + offset, isshared, size + offset)) { // could not resize in place - info = gc_qalloc(newsize + __arrayPad(newsize), info.attr); + info = GC.qalloc(newsize + __arrayPad(newsize), info.attr); // goto sucks, but this is for optimization. goto L2; } @@ -1558,7 +1521,7 @@ body else { // not appendable or not part of the heap yet. - info = gc_qalloc(newsize + __arrayPad(newsize), (info.base ? info.attr : !(ti.next.flags & 1) ? BlkAttr.NO_SCAN : 0) | BlkAttr.APPENDABLE); + info = GC.qalloc(newsize + __arrayPad(newsize), (info.base ? info.attr : !(ti.next.flags & 1) ? BlkAttr.NO_SCAN : 0) | BlkAttr.APPENDABLE); L2: __setArrayAllocLength(info, newsize, isshared); if(!isshared) @@ -1575,7 +1538,7 @@ body else { // length was zero, need to allocate - auto info = gc_qalloc(newsize + __arrayPad(newsize), !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); + auto info = GC.qalloc(newsize + __arrayPad(newsize), !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); __setArrayAllocLength(info, newsize, isshared); if(!isshared) __insertBlkInfoCache(info, null); @@ -1769,7 +1732,7 @@ byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n) auto sizeelem = ti.next.tsize; // array element size auto isshared = typeid(ti) is typeid(TypeInfo_Shared); auto bic = !isshared ? __getBlkInfo(px.ptr) : null; - auto info = bic ? *bic : gc_query(px.ptr); + auto info = bic ? *bic : GC.query(px.ptr); auto length = px.length; auto newlength = length + n; auto newsize = newlength * sizeelem; @@ -1791,7 +1754,7 @@ byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n) { // not enough space, try extending auto extendoffset = offset + LARGEPAD - info.size; - auto u = gc_extend(px.ptr, newsize + extendoffset, newcap + extendoffset); + auto u = GC.extend(px.ptr, newsize + extendoffset, newcap + extendoffset); if(u) { // extend worked, now try setting the length @@ -1807,7 +1770,7 @@ byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n) } // couldn't do it, reallocate - info = gc_qalloc(newcap + LARGEPAD, info.attr); + info = GC.qalloc(newcap + LARGEPAD, info.attr); __setArrayAllocLength(info, newsize, isshared); if(!isshared) __insertBlkInfoCache(info, bic); @@ -1826,7 +1789,7 @@ byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n) { // could not resize in place auto allocsize = newCapacity(newlength, sizeelem); - info = gc_qalloc(allocsize + __arrayPad(allocsize), info.attr); + info = GC.qalloc(allocsize + __arrayPad(allocsize), info.attr); goto L2; } else if(!isshared && !bic) @@ -1839,7 +1802,7 @@ byte[] _d_arrayappendcTX(const TypeInfo ti, ref byte[] px, size_t n) // not appendable or is null { auto allocsize = newCapacity(newlength, sizeelem); - info = gc_qalloc(allocsize + __arrayPad(allocsize), (info.base ? info.attr : !(ti.next.flags & 1) ? BlkAttr.NO_SCAN : 0) | BlkAttr.APPENDABLE); + info = GC.qalloc(allocsize + __arrayPad(allocsize), (info.base ? info.attr : !(ti.next.flags & 1) ? BlkAttr.NO_SCAN : 0) | BlkAttr.APPENDABLE); } L2: __setArrayAllocLength(info, newsize, isshared); @@ -1958,7 +1921,7 @@ out (result) assert((cast(byte*)result)[x.length * sizeelem + i] == (cast(byte*)y)[i]); } - size_t cap = gc_sizeOf(result.ptr); + size_t cap = GC.sizeOf(result.ptr); assert(!cap || cap > result.length * sizeelem); } body @@ -1987,7 +1950,7 @@ body if (!len) return null; - auto info = gc_qalloc(len + __arrayPad(len), !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); + auto info = GC.qalloc(len + __arrayPad(len), !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); byte* p = cast(byte*)__arrayStart(info); p[len] = 0; // guessing this is to optimize for null-terminated arrays? memcpy(p, x.ptr, xlen); @@ -2011,7 +1974,7 @@ extern (C) void[] _d_arraycatnT(const TypeInfo ti, uint n, ...) auto size = ti.next.tsize; // array element size Array b = void; // passed argument we are concatenating - va_start!(typeof(n))(va, n); + va_start!(uint)(va, n); for (auto i = 0; i < n; i++) { @@ -2023,12 +1986,12 @@ extern (C) void[] _d_arraycatnT(const TypeInfo ti, uint n, ...) return null; auto allocsize = length * size; - auto info = gc_qalloc(allocsize + __arrayPad(allocsize), !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); + auto info = GC.qalloc(allocsize + __arrayPad(allocsize), !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); auto isshared = typeid(ti) is typeid(TypeInfo_Shared); __setArrayAllocLength(info, allocsize, isshared); void *a = __arrayStart (info); - va_start!(typeof(n))(va, n); + va_start!(uint)(va, n); size_t j = 0; for (auto i = 0; i < n; i++) @@ -2064,7 +2027,7 @@ void* _d_arrayliteralTX(const TypeInfo ti, size_t length) else { auto allocsize = length * sizeelem; - auto info = gc_qalloc(allocsize + __arrayPad(allocsize), !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); + auto info = GC.qalloc(allocsize + __arrayPad(allocsize), !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); auto isshared = typeid(ti) is typeid(TypeInfo_Shared); __setArrayAllocLength(info, allocsize, isshared); result = __arrayStart(info); @@ -2087,7 +2050,7 @@ extern (C) void* _d_arrayliteralT(const TypeInfo ti, size_t length, ...) else { auto allocsize = length * sizeelem; - auto info = gc_qalloc(allocsize + __arrayPad(allocsize), !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); + auto info = GC.qalloc(allocsize + __arrayPad(allocsize), !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); auto isshared = typeid(ti) is typeid(TypeInfo_Shared); __setArrayAllocLength(info, allocsize, isshared); result = __arrayStart(info); @@ -2150,9 +2113,10 @@ struct Array2 /** - * + * Replaced by object.dup and object.idup. + * Remove in 2.068. */ -extern (C) void[] _adDupT(const TypeInfo ti, void[] a) +deprecated extern (C) void[] _adDupT(const TypeInfo ti, void[] a) out (result) { auto sizeelem = ti.next.tsize; // array element size @@ -2166,7 +2130,7 @@ body { auto sizeelem = ti.next.tsize; // array element size auto size = a.length * sizeelem; - auto info = gc_qalloc(size + __arrayPad(size), !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); + auto info = GC.qalloc(size + __arrayPad(size), !(ti.next.flags & 1) ? BlkAttr.NO_SCAN | BlkAttr.APPENDABLE : BlkAttr.APPENDABLE); auto isshared = typeid(ti) is typeid(TypeInfo_Shared); __setArrayAllocLength(info, size, isshared); r.ptr = __arrayStart(info); @@ -2289,25 +2253,24 @@ unittest { auto s1 = new S1; assert(s1.x == 5); - assert(gc_getAttr(s1) == BlkAttr.NO_SCAN); + assert(GC.getAttr(s1) == BlkAttr.NO_SCAN); auto s2 = new S2(3); assert(s2.x == 3); - assert(gc_getAttr(s2) == BlkAttr.NO_SCAN); + assert(GC.getAttr(s2) == BlkAttr.NO_SCAN); auto s3 = new S3(1); assert(s3.x == [1,1,1,1]); - assert(gc_getAttr(s3) == BlkAttr.NO_SCAN); - assert(gc_sizeOf(s3) == 16); + assert(GC.getAttr(s3) == BlkAttr.NO_SCAN); + assert(GC.sizeOf(s3) == 16); auto s4 = new S4; assert(s4.x == null); - assert(gc_getAttr(s4) == 0); + assert(GC.getAttr(s4) == 0); } unittest { - import core.memory; // Bugzilla 3454 - Inconsistent flag setting in GC.realloc() static void test(size_t multiplier) { diff --git a/libphobos/libdruntime/rt/memory.d b/libphobos/libdruntime/rt/memory.d index 6d3e32cda..3cc3e7c5c 100644 --- a/libphobos/libdruntime/rt/memory.d +++ b/libphobos/libdruntime/rt/memory.d @@ -10,6 +10,9 @@ * Source: $(DRUNTIMESRC src/rt/_memory.d) */ +/* NOTE: This file has been patched from the original DMD distribution to + * work with the GDC compiler. + */ module rt.memory; diff --git a/libphobos/libdruntime/rt/memory_osx.d b/libphobos/libdruntime/rt/memory_osx.d deleted file mode 100644 index bffc92f41..000000000 --- a/libphobos/libdruntime/rt/memory_osx.d +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Written in the D programming language. - * This module provides OSX-specific support routines for memory.d. - * - * Copyright: Copyright Digital Mars 2008 - 2012. - * License: Distributed under the - * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). - * (See accompanying file LICENSE) - * Authors: Walter Bright, Sean Kelly - * Source: $(DRUNTIMESRC src/rt/_memory_osx.d) - */ - -module rt.memory_osx; - -version(OSX): - -//import core.stdc.stdio; // printf -import core.sys.osx.mach.dyld; -import core.sys.osx.mach.getsect; - -extern (C) extern __gshared ModuleInfo*[] _moduleinfo_array; -extern (C) extern __gshared ubyte[] _deh_eh_array; -extern (C) extern __gshared ubyte[][2] _tls_data_array; - -extern (C) void gc_addRange( void* p, size_t sz ); -extern (C) void gc_removeRange( void* p ); - - -struct SegRef -{ - string seg; - string sect; -} - - -enum SegRef[5] dataSegs = [{SEG_DATA, SECT_DATA}, - {SEG_DATA, SECT_BSS}, - {SEG_DATA, SECT_COMMON}, - // These two must match names used by compiler machobj.c - {SEG_DATA, "__tls_data"}, - {SEG_DATA, "__tlscoal_nt"}]; - - -ubyte[] getSection(in mach_header* header, intptr_t slide, - in char* segmentName, in char* sectionName) -{ - if (header.magic == MH_MAGIC) - { - auto sect = getsectbynamefromheader(header, - segmentName, - sectionName); - - if (sect !is null && sect.size > 0) - { - auto addr = cast(size_t) sect.addr; - auto size = cast(size_t) sect.size; - return (cast(ubyte*) addr)[slide .. slide + size]; - } - return null; - - } - else if (header.magic == MH_MAGIC_64) - { - auto header64 = cast(mach_header_64*) header; - auto sect = getsectbynamefromheader_64(header64, - segmentName, - sectionName); - - if (sect !is null && sect.size > 0) - { - auto addr = cast(size_t) sect.addr; - auto size = cast(size_t) sect.size; - return (cast(ubyte*) addr)[slide .. slide + size]; - } - return null; - } - else - return null; -} - - -extern (C) void onAddImage(in mach_header* h, intptr_t slide) -{ - //printf("onAddImage()\n"); - foreach(i, e; dataSegs) - { - if (auto sect = getSection(h, slide, e.seg.ptr, e.sect.ptr)) - gc_addRange(sect.ptr, sect.length); - } - - if (auto sect = getSection(h, slide, "__DATA", "__minfodata")) - { - //printf(" minfodata\n"); - /* BUG: this will fail if there are multiple images with __minfodata - * sections. Not set up to handle that. - */ - _moduleinfo_array = (cast(ModuleInfo**)sect.ptr)[0 .. sect.length / _moduleinfo_array[0].sizeof]; - } - - if (auto sect = getSection(h, slide, "__DATA", "__deh_eh")) - { - //printf(" deh_eh\n"); - /* BUG: this will fail if there are multiple images with __deh_eh - * sections. Not set up to handle that. - */ - _deh_eh_array = sect.ptr[0 .. sect.length]; - } -} - - -extern (C) void onRemoveImage(in mach_header* h, intptr_t slide) -{ - //printf("onRemoveImage()\n"); - foreach(i, e; dataSegs) - { - if (auto sect = getSection(h, slide, e.seg.ptr, e.sect.ptr)) - gc_removeRange(sect.ptr); - } -} - - -extern (C) void _d_osx_image_init() -{ - //printf("_d_osx_image_init()\n"); - _dyld_register_func_for_add_image( &onAddImage ); - _dyld_register_func_for_remove_image( &onRemoveImage ); -} - -/********************************* - * The following is done separately because it must be done before Thread gets initialized. - */ - -extern (C) void onAddImage2(in mach_header* h, intptr_t slide) -{ - //printf("onAddImage2()\n"); - - if (auto sect = getSection(h, slide, "__DATA", "__tls_data")) - { - //printf(" tls_data %p %p\n", §[0], §[length]); - /* BUG: this will fail if there are multiple images with __tls_data - * sections. Not set up to handle that. - */ - if (!_tls_data_array[0].ptr) - _tls_data_array[0] = sect.ptr[0 .. sect.length]; - } - - if (auto sect = getSection(h, slide, "__DATA", "__tlscoal_nt")) - { - //printf(" tlscoal_nt %p %p\n", §[0], §[length]); - /* BUG: this will fail if there are multiple images with __tlscoal_nt - * sections. Not set up to handle that. - */ - if (!_tls_data_array[1].ptr) - _tls_data_array[1] = sect.ptr[0 .. sect.length]; - } -} - - -extern (C) void onRemoveImage2(in mach_header* h, intptr_t slide) -{ - //printf("onRemoveImage2()\n"); -} - - -extern (C) void _d_osx_image_init2() -{ - //printf("_d_osx_image_init2()\n"); - _dyld_register_func_for_add_image( &onAddImage2 ); - _dyld_register_func_for_remove_image( &onRemoveImage2 ); -} - diff --git a/libphobos/libdruntime/rt/minfo.d b/libphobos/libdruntime/rt/minfo.d index 739a1fe41..0b9d2ded4 100644 --- a/libphobos/libdruntime/rt/minfo.d +++ b/libphobos/libdruntime/rt/minfo.d @@ -10,6 +10,9 @@ * Source: $(DRUNTIMESRC src/rt/_minfo.d) */ +/* NOTE: This file has been patched from the original DMD distribution to + * work with the GDC compiler. + */ module rt.minfo; import core.stdc.stdlib; // alloca @@ -42,12 +45,12 @@ enum struct ModuleGroup { - this(ModuleInfo*[] modules) + this(immutable(ModuleInfo*)[] modules) { _modules = modules; } - @property inout(ModuleInfo*)[] modules() inout + @property immutable(ModuleInfo*)[] modules() const { return _modules; } @@ -67,51 +70,84 @@ struct ModuleGroup static struct StackRec { - @property ModuleInfo* mod() + @property immutable(ModuleInfo)* mod() { return _mods[_idx]; } - ModuleInfo*[] _mods; + immutable(ModuleInfo*)[] _mods; size_t _idx; } auto stack = (cast(StackRec*).calloc(len, StackRec.sizeof))[0 .. len]; - if (!stack.ptr) + // TODO: reuse GCBits by moving it to rt.util.container or core.internal + immutable nwords = (len + 8 * size_t.sizeof - 1) / (8 * size_t.sizeof); + auto ctorstart = cast(size_t*).malloc(nwords * size_t.sizeof); + auto ctordone = cast(size_t*).malloc(nwords * size_t.sizeof); + if (!stack.ptr || ctorstart is null || ctordone is null) assert(0); - scope (exit) .free(stack.ptr); + scope (exit) { .free(stack.ptr); .free(ctorstart); .free(ctordone); } + + int findModule(in ModuleInfo* mi) + { + foreach (i, m; _modules) + if (m is mi) return cast(int)i; + return -1; + } - void sort(ref ModuleInfo*[] ctors, uint mask) + void sort(ref immutable(ModuleInfo)*[] ctors, uint mask) { - ctors = (cast(ModuleInfo**).malloc(len * size_t.sizeof))[0 .. len]; + import core.bitop; + + ctors = (cast(immutable(ModuleInfo)**).malloc(len * size_t.sizeof))[0 .. len]; if (!ctors.ptr) assert(0); + // clean flags + memset(ctorstart, 0, nwords * size_t.sizeof); + memset(ctordone, 0, nwords * size_t.sizeof); size_t stackidx = 0; size_t cidx; - ModuleInfo*[] mods = _modules; + immutable(ModuleInfo*)[] mods = _modules; size_t idx; while (true) { while (idx < mods.length) { auto m = mods[idx]; - auto fl = m.flags; - if (fl & MIctorstart) + + immutable bitnum = findModule(m); + + if (bitnum < 0 || bt(ctordone, bitnum)) { - // trace back to cycle start - fl &= ~MIctorstart; + /* If the module can't be found among the ones to be + * sorted it's an imported module from another DSO. + * Those don't need to be considered during sorting as + * the OS is responsible for the DSO load order and + * module construction is done during DSO loading. + */ + ++idx; + continue; + } + else if (bt(ctorstart, bitnum)) + { + /* Trace back to the begin of the cycle. + */ + bool ctorInCycle; size_t start = stackidx; while (start--) { auto sm = stack[start].mod; if (sm == m) break; - fl |= sm.flags & MIctorstart; + immutable sbitnum = findModule(sm); + assert(sbitnum >= 0); + if (bt(ctorstart, sbitnum)) + ctorInCycle = true; } assert(stack[start].mod == m); - if (fl & MIctorstart) + if (ctorInCycle) { /* This is an illegal cycle, no partial order can be established * because the import chain have contradicting ctor/dtor @@ -136,31 +172,28 @@ struct ModuleGroup ++idx; } } - else if (fl & MIctordone) - { // already visited => skip - ++idx; - } else { - if (fl & mask) + if (m.flags & mask) { - if (fl & MIstandalone || !m.importedModules.length) + if (m.flags & MIstandalone || !m.importedModules.length) { // trivial ctor => sort in ctors[cidx++] = m; - m.flags = fl | MIctordone; + bts(ctordone, bitnum); } else { // non-trivial ctor => defer - m.flags = fl | MIctorstart; + bts(ctorstart, bitnum); } } else // no ctor => mark as visited - m.flags = fl | MIctordone; + { + bts(ctordone, bitnum); + } if (m.importedModules.length) { - /* Internal runtime error, dependency on an uninitialized - * module outside of the current module group. + /* Internal runtime error, recursion exceeds number of modules. */ (stackidx < _modules.length) || assert(0); @@ -178,20 +211,16 @@ struct ModuleGroup mods = stack[stackidx]._mods; idx = stack[stackidx]._idx; auto m = mods[idx++]; - auto fl = m.flags; - if (fl & mask && !(fl & MIctordone)) + immutable bitnum = findModule(m); + assert(bitnum >= 0); + if (m.flags & mask && !bts(ctordone, bitnum)) ctors[cidx++] = m; - m.flags = (fl & ~MIctorstart) | MIctordone; } else // done break; } - // store final number - ctors = ctors[0 .. cidx]; - - // clean flags - foreach(m; _modules) - m.flags = m.flags & ~(MIctorstart | MIctordone); + // store final number and shrink array + ctors = (cast(immutable(ModuleInfo)**).realloc(ctors.ptr, cidx * size_t.sizeof))[0 .. cidx]; } /* Do two passes: ctor/dtor, tlsctor/tlsdtor @@ -206,9 +235,6 @@ struct ModuleGroup runModuleFuncs!(m => m.ictor)(_modules); // sorted module ctors runModuleFuncs!(m => m.ctor)(_ctors); - // flag all modules as initialized - foreach (m; _modules) - m.flags = m.flags | MIctordone; } void runTlsCtors() @@ -224,9 +250,6 @@ struct ModuleGroup void runDtors() { runModuleFuncsRev!(m => m.dtor)(_ctors); - // clean all initialized flags - foreach (m; _modules) - m.flags = m.flags & ~MIctordone; } void free() @@ -241,9 +264,9 @@ struct ModuleGroup } private: - ModuleInfo*[] _modules; - ModuleInfo*[] _ctors; - ModuleInfo*[] _tlsctors; + immutable(ModuleInfo*)[] _modules; + immutable(ModuleInfo)*[] _ctors; + immutable(ModuleInfo)*[] _tlsctors; } __gshared ModuleGroup _moduleGroup; @@ -252,7 +275,7 @@ __gshared ModuleGroup _moduleGroup; * Iterate over all module infos. */ -int moduleinfos_apply(scope int delegate(ref ModuleInfo*) dg) +int moduleinfos_apply(scope int delegate(immutable(ModuleInfo*)) dg) { int ret = 0; @@ -261,12 +284,11 @@ int moduleinfos_apply(scope int delegate(ref ModuleInfo*) dg) // TODO: Should null ModuleInfo be allowed? if (m !is null) { - ret = dg(m); - if (ret) - break; + if (auto res = dg(m)) + return res; } } - return ret; + return 0; } /******************************************** @@ -295,8 +317,6 @@ void rt_moduleTlsDtor() void rt_moduleDtor() { _moduleGroup.runDtors(); - version (Win32) {} else - .free(_moduleGroup._modules.ptr); _moduleGroup.free(); } @@ -379,7 +399,7 @@ else static assert(0); } -ModuleInfo*[] getModuleInfos() +immutable(ModuleInfo*)[] getModuleInfos() out (result) { foreach(m; result) @@ -387,7 +407,7 @@ out (result) } body { - typeof(return) result = void; + ModuleInfo*[] result = void; version (GNU) { @@ -470,14 +490,14 @@ body { static assert(0); } - return result; + return cast(immutable)result; } } /******************************************** */ -void runModuleFuncs(alias getfp)(ModuleInfo*[] modules) +void runModuleFuncs(alias getfp)(const(immutable(ModuleInfo)*)[] modules) { foreach (m; modules) { @@ -486,7 +506,7 @@ void runModuleFuncs(alias getfp)(ModuleInfo*[] modules) } } -void runModuleFuncsRev(alias getfp)(ModuleInfo*[] modules) +void runModuleFuncsRev(alias getfp)(const(immutable(ModuleInfo)*)[] modules) { foreach_reverse (m; modules) { @@ -510,46 +530,52 @@ unittest { } - struct UTModuleInfo + static struct UTModuleInfo { - ModuleInfo mi; + this(uint flags) + { + mi._flags = flags; + } + + void setImports(immutable(ModuleInfo)*[] imports...) + { + import core.bitop; + assert(flags & MIimportedModules); + + immutable nfuncs = popcnt(flags & (MItlsctor|MItlsdtor|MIctor|MIdtor|MIictor)); + immutable size = nfuncs * (void function()).sizeof + + size_t.sizeof + imports.length * (ModuleInfo*).sizeof; + assert(size <= pad.sizeof); + + pad[nfuncs] = imports.length; + .memcpy(&pad[nfuncs+1], imports.ptr, imports.length * imports[0].sizeof); + } + + immutable ModuleInfo mi; size_t pad[8]; alias mi this; } - static UTModuleInfo mockMI(uint flags, ModuleInfo*[] imports...) + static UTModuleInfo mockMI(uint flags) { - import core.bitop; - size_t size = ModuleInfo.sizeof; - size += popcnt(flags & (MItlsctor|MItlsdtor|MIctor|MIdtor|MIictor)) * (void function()).sizeof; - if (imports.length) - size += size_t.sizeof + imports.length * (ModuleInfo*).sizeof; - assert(size <= UTModuleInfo.sizeof); - - UTModuleInfo mi; - mi._flags = flags; + auto mi = UTModuleInfo(flags | MIimportedModules); auto p = cast(void function()*)&mi.pad; if (flags & MItlsctor) *p++ = &stub; if (flags & MItlsdtor) *p++ = &stub; if (flags & MIctor) *p++ = &stub; if (flags & MIdtor) *p++ = &stub; if (flags & MIictor) *p++ = &stub; - if (imports.length) - { - mi._flags |= MIimportedModules; - *cast(size_t*)p++ = imports.length; - .memcpy(p, imports.ptr, imports.length * (ModuleInfo*).sizeof); - p += imports.length; - } + *cast(size_t*)p++ = 0; // number of imported modules assert(cast(void*)p <= &mi + 1); return mi; } - UTModuleInfo m0, m1, m2; - - void checkExp(ModuleInfo*[] dtors=null, ModuleInfo*[] tlsdtors=null) + static void checkExp( + immutable(ModuleInfo*)[] modules, + immutable(ModuleInfo*)[] dtors=null, + immutable(ModuleInfo*)[] tlsdtors=null) { - auto mgroup = ModuleGroup([&m0.mi, &m1.mi, &m2.mi]); + auto mgroup = ModuleGroup(modules); mgroup.sortCtors(); foreach (m; mgroup._modules) assert(!(m.flags & (MIctorstart | MIctordone))); @@ -558,86 +584,133 @@ unittest } // no ctors - m0 = mockMI(0); - m1 = mockMI(0); - m2 = mockMI(0); - checkExp(); + { + auto m0 = mockMI(0); + auto m1 = mockMI(0); + auto m2 = mockMI(0); + checkExp([&m0.mi, &m1.mi, &m2.mi]); + } // independent ctors - m0 = mockMI(MIictor); - m1 = mockMI(0); - m2 = mockMI(MIictor); - checkExp(); + { + auto m0 = mockMI(MIictor); + auto m1 = mockMI(0); + auto m2 = mockMI(MIictor); + auto mgroup = ModuleGroup([&m0.mi, &m1.mi, &m2.mi]); + checkExp([&m0.mi, &m1.mi, &m2.mi]); + } // standalone ctor - m0 = mockMI(MIstandalone | MIctor); - m1 = mockMI(0); - m2 = mockMI(0); - checkExp([&m0.mi]); + { + auto m0 = mockMI(MIstandalone | MIctor); + auto m1 = mockMI(0); + auto m2 = mockMI(0); + auto mgroup = ModuleGroup([&m0.mi, &m1.mi, &m2.mi]); + checkExp([&m0.mi, &m1.mi, &m2.mi], [&m0.mi]); + } // imported standalone => no dependency - m0 = mockMI(MIstandalone | MIctor); - m1 = mockMI(MIstandalone | MIctor, &m0.mi); - m2 = mockMI(0); - checkExp([&m0.mi, &m1.mi]); + { + auto m0 = mockMI(MIstandalone | MIctor); + auto m1 = mockMI(MIstandalone | MIctor); + auto m2 = mockMI(0); + m1.setImports(&m0.mi); + checkExp([&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]); + } - m0 = mockMI(MIstandalone | MIctor, &m1.mi); - m1 = mockMI(MIstandalone | MIctor); - m2 = mockMI(0); - checkExp([&m0.mi, &m1.mi]); + { + auto m0 = mockMI(MIstandalone | MIctor); + auto m1 = mockMI(MIstandalone | MIctor); + auto m2 = mockMI(0); + m0.setImports(&m1.mi); + checkExp([&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]); + } // standalone may have cycle - m0 = mockMI(MIstandalone | MIctor, &m1.mi); - m1 = mockMI(MIstandalone | MIctor, &m0.mi); - m2 = mockMI(0); - checkExp([&m0.mi, &m1.mi]); + { + auto m0 = mockMI(MIstandalone | MIctor); + auto m1 = mockMI(MIstandalone | MIctor); + auto m2 = mockMI(0); + m0.setImports(&m1.mi); + m1.setImports(&m0.mi); + checkExp([&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi]); + } // imported ctor => ordered ctors - m0 = mockMI(MIctor); - m1 = mockMI(MIctor, &m0.mi); - m2 = mockMI(0); - checkExp([&m0.mi, &m1.mi], []); + { + auto m0 = mockMI(MIctor); + auto m1 = mockMI(MIctor); + auto m2 = mockMI(0); + m1.setImports(&m0.mi); + checkExp([&m0.mi, &m1.mi, &m2.mi], [&m0.mi, &m1.mi], []); + } - m0 = mockMI(MIctor, &m1.mi); - m1 = mockMI(MIctor); - m2 = mockMI(0); - assert(m0.importedModules == [&m1.mi]); - checkExp([&m1.mi, &m0.mi], []); + { + auto m0 = mockMI(MIctor); + auto m1 = mockMI(MIctor); + auto m2 = mockMI(0); + m0.setImports(&m1.mi); + assert(m0.importedModules == [&m1.mi]); + checkExp([&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], []); + } // detects ctors cycles - m0 = mockMI(MIctor, &m1.mi); - m1 = mockMI(MIctor, &m0.mi); - m2 = mockMI(0); - assertThrown!Throwable(checkExp()); + { + auto m0 = mockMI(MIctor); + auto m1 = mockMI(MIctor); + auto m2 = mockMI(0); + m0.setImports(&m1.mi); + m1.setImports(&m0.mi); + assertThrown!Throwable(checkExp([&m0.mi, &m1.mi, &m2.mi])); + } // imported ctor/tlsctor => ordered ctors/tlsctors - m0 = mockMI(MIctor, &m1.mi, &m2.mi); - m1 = mockMI(MIctor); - m2 = mockMI(MItlsctor); - checkExp([&m1.mi, &m0.mi], [&m2.mi]); + { + auto m0 = mockMI(MIctor); + auto m1 = mockMI(MIctor); + auto m2 = mockMI(MItlsctor); + m0.setImports(&m1.mi, &m2.mi); + checkExp([&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi]); + } - m0 = mockMI(MIctor | MItlsctor, &m1.mi, &m2.mi); - m1 = mockMI(MIctor); - m2 = mockMI(MItlsctor); - checkExp([&m1.mi, &m0.mi], [&m2.mi, &m0.mi]); + { + auto m0 = mockMI(MIctor | MItlsctor); + auto m1 = mockMI(MIctor); + auto m2 = mockMI(MItlsctor); + m0.setImports(&m1.mi, &m2.mi); + checkExp([&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi, &m0.mi]); + } // no cycle between ctors/tlsctors - m0 = mockMI(MIctor, &m1.mi, &m2.mi); - m1 = mockMI(MIctor); - m2 = mockMI(MItlsctor, &m0.mi); - checkExp([&m1.mi, &m0.mi], [&m2.mi]); + { + auto m0 = mockMI(MIctor); + auto m1 = mockMI(MIctor); + auto m2 = mockMI(MItlsctor); + m0.setImports(&m1.mi, &m2.mi); + m2.setImports(&m0.mi); + checkExp([&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m0.mi], [&m2.mi]); + } // detects tlsctors cycle - m0 = mockMI(MItlsctor, &m2.mi); - m1 = mockMI(MIctor); - m2 = mockMI(MItlsctor, &m0.mi); - assertThrown!Throwable(checkExp()); + { + auto m0 = mockMI(MItlsctor); + auto m1 = mockMI(MIctor); + auto m2 = mockMI(MItlsctor); + m0.setImports(&m2.mi); + m2.setImports(&m0.mi); + assertThrown!Throwable(checkExp([&m0.mi, &m1.mi, &m2.mi])); + } // closed ctors cycle - m0 = mockMI(MIctor, &m1.mi); - m1 = mockMI(MIstandalone | MIctor, &m2.mi); - m2 = mockMI(MIstandalone | MIctor, &m0.mi); - checkExp([&m1.mi, &m2.mi, &m0.mi], []); + { + auto m0 = mockMI(MIctor); + auto m1 = mockMI(MIstandalone | MIctor); + auto m2 = mockMI(MIstandalone | MIctor); + m0.setImports(&m1.mi); + m1.setImports(&m2.mi); + m2.setImports(&m0.mi); + checkExp([&m0.mi, &m1.mi, &m2.mi], [&m1.mi, &m2.mi, &m0.mi]); + } } version (Win64) diff --git a/libphobos/libdruntime/rt/monitor_.d b/libphobos/libdruntime/rt/monitor_.d index 10bf15dea..46c6449b3 100644 --- a/libphobos/libdruntime/rt/monitor_.d +++ b/libphobos/libdruntime/rt/monitor_.d @@ -13,9 +13,8 @@ */ /* NOTE: This file has been patched from the original DMD distribution to - work with the GDC compiler. - Modified by Iain Buclaw, December 2013 -*/ + * work with the GDC compiler. + */ module rt.monitor_; //debug=PRINTF; diff --git a/libphobos/libdruntime/rt/tls.S b/libphobos/libdruntime/rt/tls.S deleted file mode 100644 index 558165876..000000000 --- a/libphobos/libdruntime/rt/tls.S +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Contains support code for thread-local storage. - * - * Copyright: Copyright Digital Mars 2008 - 2010. - * License: Boost License 1.0. - * Authors: Walter Bright - */ - -/* Copyright Digital Mars 2008 - 2010. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE or copy at - * http://www.boost.org/LICENSE_1_0.txt) - */ -#if linux - -/* The memory between the addresses of _tlsstart and _tlsend is the storage for - * thread-local data in D 2.0. Both of these rely on the default linker script - * of: - * .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) } - * .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) } - * to group the sections in that order. - * - * Sadly, this does not work because ld orders .tdata after .tdata.*, despite - * what the linker script says. - */ - -.file "tls.S" - -.globl _tlsstart - .section .tdata,"awT",@progbits - .align 4 - .type _tlsstart, @object - .size _tlsstart, 4 -_tlsstart: - .long 3 - -.globl _tlsend - .section .tcommon,"awT",@nobits - .align 4 - .type _tlsend, @object - .size _tlsend, 4 -_tlsend: - .zero 4 - -#endif diff --git a/libphobos/libdruntime/rt/tlsgc.d b/libphobos/libdruntime/rt/tlsgc.d index bbb7d534a..c6b249bf2 100644 --- a/libphobos/libdruntime/rt/tlsgc.d +++ b/libphobos/libdruntime/rt/tlsgc.d @@ -10,6 +10,10 @@ * (See accompanying file LICENSE or copy at * http://www.boost.org/LICENSE_1_0.txt) */ + +/* NOTE: This file has been patched from the original DMD distribution to + * work with the GDC compiler. + */ module rt.tlsgc; import core.stdc.stdlib; @@ -28,7 +32,7 @@ struct Data * Initialization hook, called FROM each thread. No assumptions about * module initialization state should be made. */ -Data* init() +void* init() { auto data = cast(Data*).malloc(Data.sizeof); *data = Data.init; @@ -43,33 +47,33 @@ Data* init() * Finalization hook, called FOR each thread. No assumptions about * module initialization state should be made. */ -void destroy(Data* data) +void destroy(void* data) { // do module specific finalization .free(data); } -alias void delegate(void* pstart, void* pend) ScanDg; +alias void delegate(void* pstart, void* pend) nothrow ScanDg; /** * GC scan hook, called FOR each thread. Can be used to scan * additional thread local memory. */ -void scan(Data* data, scope ScanDg dg) +void scan(void* data, scope ScanDg dg) nothrow { // do module specific marking } -alias int delegate(void* addr) IsMarkedDg; +alias int delegate(void* addr) nothrow IsMarkedDg; /** * GC sweep hook, called FOR each thread. Can be used to free * additional thread local memory or associated data structures. Note * that only memory allocated from the GC can have marks. */ -void processGCMarks(Data* data, scope IsMarkedDg dg) +void processGCMarks(void* data, scope IsMarkedDg dg) nothrow { // do module specific sweeping - rt.lifetime.processGCMarks(*data.blockInfoCache, dg); + rt.lifetime.processGCMarks(*(cast(Data*)data).blockInfoCache, dg); } diff --git a/libphobos/libdruntime/rt/typeinfo/ti_AC.d b/libphobos/libdruntime/rt/typeinfo/ti_AC.d index dee091414..3f3aa4b6c 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_AC.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_AC.d @@ -15,6 +15,12 @@ module rt.typeinfo.ti_AC; // Object[] +/* +NOTE: It is sometimes used for arrays of +classes or (incorrectly) interfaces. +But naked `TypeInfo_Array` is mostly used. +See @@@BUG12303@@@. +*/ class TypeInfo_AC : TypeInfo_Array { override string toString() const { return TypeInfo.toString(); } diff --git a/libphobos/libdruntime/rt/typeinfo/ti_double.d b/libphobos/libdruntime/rt/typeinfo/ti_double.d index c2cb28868..fe771c430 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_double.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_double.d @@ -87,4 +87,13 @@ class TypeInfo_d : TypeInfo { return double.alignof; } + + version (Windows) + { + } + else version (X86_64) + { + // 2 means arg to function is passed in XMM registers + override @property uint flags() nothrow pure const @safe { return 2; } + } } diff --git a/libphobos/libdruntime/rt/typeinfo/ti_float.d b/libphobos/libdruntime/rt/typeinfo/ti_float.d index f4fbfe2e4..be99b0cc3 100644 --- a/libphobos/libdruntime/rt/typeinfo/ti_float.d +++ b/libphobos/libdruntime/rt/typeinfo/ti_float.d @@ -80,4 +80,13 @@ class TypeInfo_f : TypeInfo return (cast(float *)&r)[0 .. 1]; } + + version (Windows) + { + } + else version (X86_64) + { + // 2 means arg to function is passed in XMM registers + override @property uint flags() nothrow pure const @safe { return 2; } + } } diff --git a/libphobos/libdruntime/rt/util/array.d b/libphobos/libdruntime/rt/util/array.d index fca09b7aa..25449b354 100644 --- a/libphobos/libdruntime/rt/util/array.d +++ b/libphobos/libdruntime/rt/util/array.d @@ -49,7 +49,7 @@ private void _enforceSameLength(in char[] action, private void _enforceNoOverlap(in char[] action, in void* ptr1, in void* ptr2, in size_t bytes) { - const d = ptr1 > ptr2 ? ptr1 - ptr2 : ptr2 - ptr1; + const size_t d = ptr1 > ptr2 ? ptr1 - ptr2 : ptr2 - ptr1; if(d >= bytes) return; const overlappedBytes = bytes - d; diff --git a/libphobos/libdruntime/rt/util/container/array.d b/libphobos/libdruntime/rt/util/container/array.d new file mode 100644 index 000000000..d42d537ed --- /dev/null +++ b/libphobos/libdruntime/rt/util/container/array.d @@ -0,0 +1,180 @@ +/** + * Array container for internal usage. + * + * Copyright: Copyright Martin Nowak 2013. + * License: Boost License 1.0. + * Authors: Martin Nowak + */ +module rt.util.container.array; + +static import common = rt.util.container.common; + +struct Array(T) +{ + @disable this(this); + + ~this() + { + reset(); + } + + void reset() + { + length = 0; + } + + @property size_t length() const + { + return _length; + } + + @property void length(size_t nlength) + { + if (nlength < length) + foreach (ref val; _ptr[nlength .. length]) common.destroy(val); + _ptr = cast(T*)common.xrealloc(_ptr, nlength * T.sizeof); + if (nlength > length) + foreach (ref val; _ptr[length .. nlength]) common.initialize(val); + _length = nlength; + } + + @property bool empty() const + { + return !length; + } + + @property ref inout(T) front() inout + in { assert(!empty); } + body + { + return _ptr[0]; + } + + @property ref inout(T) back() inout + in { assert(!empty); } + body + { + return _ptr[_length - 1]; + } + + ref inout(T) opIndex(size_t idx) inout + in { assert(idx < length); } + body + { + return _ptr[idx]; + } + + inout(T)[] opSlice() inout + { + return _ptr[0 .. _length]; + } + + inout(T)[] opSlice(size_t a, size_t b) inout + in { assert(a < b && b <= length); } + body + { + return _ptr[a .. b]; + } + + alias length opDollar; + + void insertBack()(auto ref T val) + { + length = length + 1; + back = val; + } + + void popBack() + { + length = length - 1; + } + + void remove(size_t idx) + in { assert(idx < length); } + body + { + foreach (i; idx .. length - 1) + _ptr[i] = _ptr[i+1]; + popBack(); + } + + void swap(ref Array other) + { + auto ptr = _ptr; + _ptr = other._ptr; + other._ptr = ptr; + immutable len = _length; + _length = other._length; + other._length = len; + } + +private: + T* _ptr; + size_t _length; +} + +unittest +{ + Array!size_t ary; + + assert(ary[] == []); + ary.insertBack(5); + assert(ary[] == [5]); + assert(ary[$-1] == 5); + ary.popBack(); + assert(ary[] == []); + ary.insertBack(0); + ary.insertBack(1); + assert(ary[] == [0, 1]); + assert(ary[0 .. 1] == [0]); + assert(ary[1 .. 2] == [1]); + assert(ary[$ - 2 .. $] == [0, 1]); + size_t idx; + foreach (val; ary) assert(idx++ == val); + foreach_reverse (val; ary) assert(--idx == val); + foreach (i, val; ary) assert(i == val); + foreach_reverse (i, val; ary) assert(i == val); + + ary.insertBack(2); + ary.remove(1); + assert(ary[] == [0, 2]); + + assert(!ary.empty); + ary.reset(); + assert(ary.empty); + ary.insertBack(0); + assert(!ary.empty); + destroy(ary); + assert(ary.empty); + + // not copyable + static assert(!__traits(compiles, { Array!size_t ary2 = ary; })); + Array!size_t ary2; + static assert(!__traits(compiles, ary = ary2)); + static void foo(Array!size_t copy) {} + static assert(!__traits(compiles, foo(ary))); + + ary2.insertBack(0); + assert(ary.empty); + assert(ary2[] == [0]); + ary.swap(ary2); + assert(ary[] == [0]); + assert(ary2.empty); +} + +unittest +{ + alias RC = common.RC; + Array!RC ary; + + size_t cnt; + assert(cnt == 0); + ary.insertBack(RC(&cnt)); + assert(cnt == 1); + ary.insertBack(ary.front); + assert(cnt == 2); + ary.popBack(); + assert(cnt == 1); + ary.popBack(); + assert(cnt == 0); +} diff --git a/libphobos/libdruntime/rt/util/container/common.d b/libphobos/libdruntime/rt/util/container/common.d new file mode 100644 index 000000000..7cc0d28a9 --- /dev/null +++ b/libphobos/libdruntime/rt/util/container/common.d @@ -0,0 +1,62 @@ +/** + * Common code for writing containers. + * + * Copyright: Copyright Martin Nowak 2013. + * License: Boost License 1.0. + * Authors: Martin Nowak + */ +module rt.util.container.common; + +import core.stdc.stdlib : malloc, realloc; +public import core.stdc.stdlib : free; + +void* xrealloc(void* ptr, size_t sz) +{ + import core.exception; + + if (!sz) { .free(ptr); return null; } + if (auto nptr = .realloc(ptr, sz)) return nptr; + .free(ptr); onOutOfMemoryError(); + assert(0); +} + +void* xmalloc(size_t sz) nothrow +{ + import core.exception; + if (auto nptr = .malloc(sz)) + return nptr; + onOutOfMemoryError(); + assert(0); +} + +void destroy(T)(ref T t) if (is(T == struct)) +{ + object.destroy(t); +} + +void destroy(T)(ref T t) if (!is(T == struct)) +{ + t = T.init; +} + +void initialize(T)(ref T t) if (is(T == struct)) +{ + import core.stdc.string; + if(auto p = typeid(T).init().ptr) + memcpy(&t, p, T.sizeof); + else + memset(&t, 0, T.sizeof); +} + +void initialize(T)(ref T t) if (!is(T == struct)) +{ + t = T.init; +} + +version (unittest) struct RC +{ + this(size_t* cnt) { ++*(_cnt = cnt); } + ~this() { if (_cnt) --*_cnt; } + this(this) { if (_cnt) ++*_cnt; } + size_t* _cnt; +} diff --git a/libphobos/libdruntime/rt/util/container.d b/libphobos/libdruntime/rt/util/container/hashtab.d similarity index 58% rename from libphobos/libdruntime/rt/util/container.d rename to libphobos/libdruntime/rt/util/container/hashtab.d index 4b788b633..ec78a693f 100644 --- a/libphobos/libdruntime/rt/util/container.d +++ b/libphobos/libdruntime/rt/util/container/hashtab.d @@ -1,226 +1,14 @@ /** - * Basic containers for internal usage. + * HashTab container for internal usage. * * Copyright: Copyright Martin Nowak 2013. * License: Boost License 1.0. * Authors: Martin Nowak - * Source: $(DRUNTIMESRC src/rt/util/_container.d) */ -module rt.util.container; +module rt.util.container.hashtab; -import core.stdc.stdlib : free, malloc, realloc; - -private void* xrealloc(void* ptr, size_t sz) -{ - import core.exception; - - if (!sz) return .free(ptr), null; - if (auto nptr = .realloc(ptr, sz)) return nptr; - .free(ptr), onOutOfMemoryError(); - assert(0); -} - -private void destroy(T)(ref T t) if (is(T == struct)) -{ - object.destroy(t); -} - -private void destroy(T)(ref T t) if (!is(T == struct)) -{ - t = T.init; -} - -private void initialize(T)(ref T t) if (is(T == struct)) -{ - import core.stdc.string; - if(auto p = typeid(T).init().ptr) - memcpy(&t, p, T.sizeof); - else - memset(&t, 0, T.sizeof); -} - -private void initialize(T)(ref T t) if (!is(T == struct)) -{ - t = T.init; -} - -struct Array(T) -{ - @disable this(this); - - ~this() - { - reset(); - } - - void reset() - { - length = 0; - } - - @property size_t length() const - { - return _length; - } - - @property void length(size_t nlength) - { - if (nlength < length) - foreach (ref val; _ptr[nlength .. length]) destroy(val); - _ptr = cast(T*)xrealloc(_ptr, nlength * T.sizeof); - if (nlength > length) - foreach (ref val; _ptr[length .. nlength]) initialize(val); - _length = nlength; - } - - @property bool empty() const - { - return !length; - } - - @property ref inout(T) front() inout - in { assert(!empty); } - body - { - return _ptr[0]; - } - - @property ref inout(T) back() inout - in { assert(!empty); } - body - { - return _ptr[_length - 1]; - } - - ref inout(T) opIndex(size_t idx) inout - in { assert(idx < length); } - body - { - return _ptr[idx]; - } - - inout(T)[] opSlice() inout - { - return _ptr[0 .. _length]; - } - - inout(T)[] opSlice(size_t a, size_t b) inout - in { assert(a < b && b <= length); } - body - { - return _ptr[a .. b]; - } - - alias length opDollar; - - void insertBack()(auto ref T val) - { - length = length + 1; - back = val; - } - - void popBack() - { - length = length - 1; - } - - void remove(size_t idx) - in { assert(idx < length); } - body - { - foreach (i; idx .. length - 1) - _ptr[i] = _ptr[i+1]; - popBack(); - } - - void swap(ref Array other) - { - auto ptr = _ptr; - _ptr = other._ptr; - other._ptr = ptr; - immutable len = _length; - _length = other._length; - other._length = len; - } - -private: - T* _ptr; - size_t _length; -} - -unittest -{ - Array!size_t ary; - - assert(ary[] == []); - ary.insertBack(5); - assert(ary[] == [5]); - assert(ary[$-1] == 5); - ary.popBack(); - assert(ary[] == []); - ary.insertBack(0); - ary.insertBack(1); - assert(ary[] == [0, 1]); - assert(ary[0 .. 1] == [0]); - assert(ary[1 .. 2] == [1]); - assert(ary[$ - 2 .. $] == [0, 1]); - size_t idx; - foreach (val; ary) assert(idx++ == val); - foreach_reverse (val; ary) assert(--idx == val); - foreach (i, val; ary) assert(i == val); - foreach_reverse (i, val; ary) assert(i == val); - - ary.insertBack(2); - ary.remove(1); - assert(ary[] == [0, 2]); - - assert(!ary.empty); - ary.reset(); - assert(ary.empty); - ary.insertBack(0); - assert(!ary.empty); - destroy(ary); - assert(ary.empty); - - // not copyable - static assert(!__traits(compiles, { Array!size_t ary2 = ary; })); - Array!size_t ary2; - static assert(!__traits(compiles, ary = ary2)); - static void foo(Array!size_t copy) {} - static assert(!__traits(compiles, foo(ary))); - - ary2.insertBack(0); - assert(ary.empty); - assert(ary2[] == [0]); - ary.swap(ary2); - assert(ary[] == [0]); - assert(ary2.empty); -} - - -version (unittest) struct RC -{ - this(size_t* cnt) { ++*(_cnt = cnt); } - ~this() { if (_cnt) --*_cnt; } - this(this) { if (_cnt) ++*_cnt; } - size_t* _cnt; -} - -unittest -{ - Array!RC ary; - - size_t cnt; - assert(cnt == 0); - ary.insertBack(RC(&cnt)); - assert(cnt == 1); - ary.insertBack(ary.front); - assert(cnt == 2); - ary.popBack(); - assert(cnt == 1); - ary.popBack(); - assert(cnt == 0); -} +import rt.util.container.array; +static import common = rt.util.container.common; struct HashTab(Key, Value) { @@ -245,8 +33,8 @@ struct HashTab(Key, Value) while (p !is null) { auto pn = p._next; - destroy(*p); - .free(p); + common.destroy(*p); + common.free(p); p = pn; } } @@ -278,8 +66,8 @@ struct HashTab(Key, Value) if (p._key == key) { *pp = p._next; - destroy(*p); - .free(p); + common.destroy(*p); + common.free(p); if (--_length < _buckets.length && _length >= 4) shrink(); return; @@ -346,8 +134,8 @@ private: _buckets.length = 4; immutable hash = hashOf(key) & mask; - auto p = cast(Node*).malloc(Node.sizeof); - initialize(*p); + auto p = cast(Node*)common.xmalloc(Node.sizeof); + common.initialize(*p); p._key = key; p._next = _buckets[hash]; _buckets[hash] = p; @@ -503,6 +291,7 @@ unittest unittest { + alias RC = common.RC; HashTab!(size_t, RC) tab; size_t cnt; diff --git a/libphobos/libdruntime/rt/util/container/treap.d b/libphobos/libdruntime/rt/util/container/treap.d new file mode 100644 index 000000000..50ec69e5a --- /dev/null +++ b/libphobos/libdruntime/rt/util/container/treap.d @@ -0,0 +1,337 @@ +/** + * Treap container for internal usage. + * + * Copyright: Copyright Digital Mars 2014 - 2014. + * License: Boost License 1.0. + */ +module rt.util.container.treap; + +static import common = rt.util.container.common; +import rt.util.random; + +struct Treap(E) +{ +nothrow: + static struct Node + { + Node* left, right; + E element; + uint priority; + } + + @disable this(this); + + ~this() + { + removeAll(); + } + + void initialize() + { + rand48.defaultSeed(); + } + + void insert(E element) + { + root = insert(root, element); + } + + void remove(E element) + { + remove(&root, element); + } + + int opApply(scope int delegate(ref E) nothrow dg) + { + return (cast(const)&this).opApply((ref const E e) => dg(*cast(E*)&e)); + } + + int opApply(scope int delegate(ref const E) nothrow dg) const + { + return opApplyHelper(root, dg); + } + + version(unittest) + bool opEquals(E[] elements) + { + size_t i; + foreach (e; this) + { + if (i >= elements.length) + return false; + if (e != elements[i++]) + return false; + } + return i == elements.length; + } + + void removeAll() + { + removeAll(root); + root = null; + } + + version(unittest) + bool valid() + { + return valid(root); + } + + + version(none) + uint height() + { + static uint height(Node* node) + { + if (!node) + return 0; + auto left = height(node.left); + auto right = height(node.right); + return 1 + (left > right ? left : right); + } + return height(root); + } + + version(none) + size_t count() + { + static size_t count(Node* node) + { + if (!node) + return 0; + return count(node.left) + count(node.right) + 1; + } + return count(root); + } + + +private: + Node* root; + Rand48 rand48; + + Node* allocNode(E element) + { + Node* node = cast(Node*)common.xmalloc(Node.sizeof); + node.left = node.right = null; + node.priority = rand48(); + node.element = element; + return node; + } + + Node* insert(Node* node, E element) + { + if (!node) + return allocNode(element); + else if (element < node.element) + { + node.left = insert(node.left, element); + if (node.left.priority < node.priority) + node = rotateR(node); + } + else if (element > node.element) + { + node.right = insert(node.right, element); + if (node.right.priority < node.priority) + node = rotateL(node); + } + else + {} // ignore duplicate + + return node; + } + +static: + + void freeNode(Node* node) + { + common.free(node); + } + + Node* rotateL(Node* root) + { + auto pivot = root.right; + root.right = pivot.left; + pivot.left = root; + return pivot; + } + + Node* rotateR(Node* root) + { + auto pivot = root.left; + root.left = pivot.right; + pivot.right = root; + return pivot; + } + + void remove(Node** ppnode, E element) + { + Node* node = *ppnode; + if (!node) + return; // element not in treap + + if (element < node.element) + { + remove(&node.left, element); + } + else if (element > node.element) + { + remove(&node.right, element); + } + else + { + while (node.left && node.right) + { + if (node.left.priority < node.right.priority) + { + *ppnode = rotateR(node); + ppnode = &(*ppnode).right; + } + else + { + *ppnode = rotateL(node); + ppnode = &(*ppnode).left; + } + } + if (!node.left) + *ppnode = node.right; + else + *ppnode = node.left; + freeNode(node); + } + } + + void removeAll(Node* node) + { + if (!node) + return; + removeAll(node.left); + removeAll(node.right); + freeNode(node); + } + + int opApplyHelper(const Node* node, scope int delegate(ref const E) nothrow dg) + { + if (!node) + return 0; + + int result = opApplyHelper(node.left, dg); + if (result) + return result; + result = dg(node.element); + if (result) + return result; + return opApplyHelper(node.right, dg); + } + + version(unittest) + bool valid(Node* node) + { + if (!node) + return true; + + if (node.left) + { + if (node.left.priority < node.priority) + return false; + if (node.left.element > node.element) + return false; + } + if (node.right) + { + if (node.right.priority < node.priority) + return false; + if (node.right.element < node.element) + return false; + } + return valid(node.left) && valid(node.right); + } +} + +unittest +{ + // randomized unittest for randomized data structure + import /*cstdlib = */core.stdc.stdlib : rand, srand; + import /*ctime = */core.stdc.time : time; + + enum OP { add, remove } + enum initialSize = 1000; + enum randOps = 1000; + + Treap!uint treap; + OP[] ops; + uint[] opdata; + + treap.initialize(); + srand(cast(uint)time(null)); + + uint[] data; +initialLoop: + foreach (i; 0 .. initialSize) + { + data ~= rand(); + treap.insert(data[$-1]); + foreach (e; data[0..$-1]) + if (e == data[$-1]) + { + data = data[0..$-1]; + continue initialLoop; + } + } + data.sort; + assert(treap == data); + assert(treap.valid()); + + for (int i = randOps; i > 0; --i) + { + ops ~= cast(OP)(rand() < uint.max / 2 ? OP.add: OP.remove); + opdata ~= rand(); + } + + foreach (op; ops) + { + if (op == OP.add) + { + treap.insert(opdata[0]); + + size_t i; + for (i = 0; i < data.length; ++i) + if (data[i] >= opdata[0]) + break; + + if (i == data.length || data[i] != opdata[0]) + { // not a duplicate + data.length++; + uint tmp = opdata[0]; + for (; i < data.length; ++i) + { + uint tmp2 = data[i]; + data[i] = tmp; + tmp = tmp2; + } + } + } + else if (!data.length) // nothing to remove + { + opdata = opdata[1..$]; + continue; + } + else + { + uint tmp = data[opdata[0]%data.length]; + treap.remove(tmp); + size_t i; + for (i = 0; data[i] < tmp; ++i) + {} + for (; i < data.length-1; ++i) + data[i] = data[i+1]; + data.length--; + } + assert(treap.valid()); + assert(treap == data); + opdata = opdata[1..$]; + } + + treap.removeAll(); + data.length = 0; + assert(treap == data); +} \ No newline at end of file diff --git a/libphobos/libdruntime/rt/util/random.d b/libphobos/libdruntime/rt/util/random.d new file mode 100644 index 000000000..1b01b5c95 --- /dev/null +++ b/libphobos/libdruntime/rt/util/random.d @@ -0,0 +1,48 @@ +/** + * Random number generators for internal usage. + * + * Copyright: Copyright Digital Mars 2014. + * License: Boost License 1.0. + */ +module rt.util.random; + +struct Rand48 +{ +nothrow: + private ulong rng_state; + + void defaultSeed() + { + import ctime = core.stdc.time : time; + seed(cast(uint)ctime.time(null)); + } + + void seed(uint seedval) + { + assert(seedval); + rng_state = cast(ulong)seedval << 16 | 0x330e; + popFront(); + } + + auto opCall() + { + auto result = front; + popFront(); + return result; + } + + @property uint front() + { + return cast(uint)(rng_state >> 16); + } + + void popFront() + { + immutable ulong a = 25214903917; + immutable ulong c = 11; + immutable ulong m_mask = (1uL << 48uL) - 1; + rng_state = (a*rng_state+c) & m_mask; + } + + enum empty = false; +} \ No newline at end of file diff --git a/libphobos/libdruntime/rt/util/utf.d b/libphobos/libdruntime/rt/util/utf.d index c787715e5..e65501a9f 100644 --- a/libphobos/libdruntime/rt/util/utf.d +++ b/libphobos/libdruntime/rt/util/utf.d @@ -540,19 +540,16 @@ code point. The code is returned in character count, not in bytes. ubyte codeLength(C)(dchar c) { - static if (C.sizeof == 1) { - return - c <= 0x7F ? 1 - : c <= 0x7FF ? 2 - : c <= 0xFFFF ? 3 - : c <= 0x10FFFF ? 4 - : (assert(false), 6); -} - + if (c <= 0x7F) return 1; + if (c <= 0x7FF) return 2; + if (c <= 0xFFFF) return 3; + if (c <= 0x10FFFF) return 4; + assert(false); + } else static if (C.sizeof == 2) -{ + { return c <= 0xFFFF ? 1 : 2; } else diff --git a/libphobos/src/Makefile.am b/libphobos/src/Makefile.am index f7ab1d38e..bfd7e2599 100644 --- a/libphobos/src/Makefile.am +++ b/libphobos/src/Makefile.am @@ -56,14 +56,12 @@ zlib/%.o: $(top_srcdir)/../zlib/%.c # GDC LibPhobos MAIN_OBJS=std/algorithm.o std/array.o std/ascii.o std/base64.o std/bigint.o \ - std/bitmanip.o std/compiler.o \ - std/container.o std/complex.o std/concurrency.o \ + std/bitmanip.o std/compiler.o std/complex.o std/concurrency.o \ std/conv.o std/cstream.o std/csv.o \ std/datetime.o std/demangle.o std/encoding.o \ std/exception.o std/format.o \ std/functional.o std/getopt.o std/json.o \ - std/math.o std/mathspecial.o \ - std/metastrings.o std/numeric.o \ + std/math.o std/mathspecial.o std/numeric.o \ std/outbuffer.o std/parallelism.o std/signals.o \ std/stdio.o std/stdiobase.o std/stdint.o std/stream.o \ std/string.o std/syserror.o std/system.o std/random.o std/range.o \ @@ -73,13 +71,16 @@ MAIN_OBJS=std/algorithm.o std/array.o std/ascii.o std/base64.o std/bigint.o \ std/c/fenv.o std/c/locale.o std/c/math.o std/c/process.o \ std/c/stdarg.o std/c/stddef.o std/c/stdio.o std/c/stdlib.o \ std/c/string.o std/c/time.o std/c/wcharh.o \ + std/container/array.o std/container/binaryheap.o \ + std/container/dlist.o std/container/package.o \ + std/container/rbtree.o std/container/slist.o std/container/util.o \ std/digest/digest.o std/digest/crc.o std/digest/md.o \ std/digest/ripemd.o std/digest/sha.o \ std/internal/digest/sha_SSSE3.o \ std/internal/math/biguintcore.o std/internal/math/biguintnoasm.o \ std/internal/math/biguintx86.o std/internal/math/gammafunction.o \ std/internal/math/errorfunction.o std/internal/processinit.o \ - std/internal/uni.o std/internal/uni_tab.o \ + std/internal/scopebuffer.o \ std/internal/unicode_comp.o std/internal/unicode_decomp.o \ std/internal/unicode_grapheme.o std/internal/unicode_norm.o \ std/internal/unicode_tables.o \ diff --git a/libphobos/src/Makefile.in b/libphobos/src/Makefile.in index 93c887b9f..747476f4e 100644 --- a/libphobos/src/Makefile.in +++ b/libphobos/src/Makefile.in @@ -228,14 +228,12 @@ SUFFIXES = .d #--------------------------------------# # GDC LibPhobos MAIN_OBJS = std/algorithm.o std/array.o std/ascii.o std/base64.o std/bigint.o \ - std/bitmanip.o std/compiler.o \ - std/container.o std/complex.o std/concurrency.o \ + std/bitmanip.o std/compiler.o std/complex.o std/concurrency.o \ std/conv.o std/cstream.o std/csv.o \ std/datetime.o std/demangle.o std/encoding.o \ std/exception.o std/format.o \ std/functional.o std/getopt.o std/json.o \ - std/math.o std/mathspecial.o \ - std/metastrings.o std/numeric.o \ + std/math.o std/mathspecial.o std/numeric.o \ std/outbuffer.o std/parallelism.o std/signals.o \ std/stdio.o std/stdiobase.o std/stdint.o std/stream.o \ std/string.o std/syserror.o std/system.o std/random.o std/range.o \ @@ -245,13 +243,16 @@ MAIN_OBJS = std/algorithm.o std/array.o std/ascii.o std/base64.o std/bigint.o \ std/c/fenv.o std/c/locale.o std/c/math.o std/c/process.o \ std/c/stdarg.o std/c/stddef.o std/c/stdio.o std/c/stdlib.o \ std/c/string.o std/c/time.o std/c/wcharh.o \ + std/container/array.o std/container/binaryheap.o \ + std/container/dlist.o std/container/package.o \ + std/container/rbtree.o std/container/slist.o std/container/util.o \ std/digest/digest.o std/digest/crc.o std/digest/md.o \ std/digest/ripemd.o std/digest/sha.o \ std/internal/digest/sha_SSSE3.o \ std/internal/math/biguintcore.o std/internal/math/biguintnoasm.o \ std/internal/math/biguintx86.o std/internal/math/gammafunction.o \ std/internal/math/errorfunction.o std/internal/processinit.o \ - std/internal/uni.o std/internal/uni_tab.o \ + std/internal/scopebuffer.o \ std/internal/unicode_comp.o std/internal/unicode_decomp.o \ std/internal/unicode_grapheme.o std/internal/unicode_norm.o \ std/internal/unicode_tables.o \ diff --git a/libphobos/src/etc/c/curl.d b/libphobos/src/etc/c/curl.d index d28c3bfbb..f4a6bb0e8 100644 --- a/libphobos/src/etc/c/curl.d +++ b/libphobos/src/etc/c/curl.d @@ -3,6 +3,10 @@ Converted to D from curl headers by $(LINK2 http://www.digitalmars.com/d/2.0/htod.html, htod) and cleaned up by Jonas Drewsen (jdrewsen) + + Windows x86 note: + A DMD compatible libcurl static library can be downloaded from the dlang.org + $(LINK2 http://dlang.org/download.html, download page). */ /* ************************************************************************** diff --git a/libphobos/src/etc/c/zlib.d b/libphobos/src/etc/c/zlib.d index 407d1a9b4..65b1d9231 100644 --- a/libphobos/src/etc/c/zlib.d +++ b/libphobos/src/etc/c/zlib.d @@ -79,15 +79,15 @@ alias void function (void* opaque, void* address) free_func; struct z_stream { - ubyte *next_in; /* next input byte */ + ubyte* next_in; /* next input byte */ uint avail_in; /* number of bytes available at next_in */ c_ulong total_in; /* total nb of input bytes read so far */ - ubyte *next_out; /* next output byte should be put there */ + ubyte* next_out; /* next output byte should be put there */ uint avail_out; /* remaining free space at next_out */ c_ulong total_out; /* total nb of bytes output so far */ - char *msg; /* last error message, NULL if no error */ + char* msg; /* last error message, NULL if no error */ void* state; /* not visible by applications */ alloc_func zalloc; /* used to allocate the internal state */ @@ -105,7 +105,8 @@ alias z_stream* z_streamp; gzip header information passed to and from zlib routines. See RFC 1952 for more details on the meanings of these fields. */ -struct gz_header { +struct gz_header +{ int text; /* true if compressed data believed to be text */ c_ulong time; /* modification time */ int xflags; /* extra flags (not used when writing a gzip file) */ @@ -113,9 +114,9 @@ struct gz_header { byte *extra; /* pointer to extra field or Z_NULL if none */ uint extra_len; /* extra field length (valid if extra != Z_NULL) */ uint extra_max; /* space at extra (only when reading header) */ - byte *name; /* pointer to zero-terminated file name or Z_NULL */ + byte* name; /* pointer to zero-terminated file name or Z_NULL */ uint name_max; /* space at name (only when reading header) */ - byte *comment; /* pointer to zero-terminated comment or Z_NULL */ + byte* comment; /* pointer to zero-terminated comment or Z_NULL */ uint comm_max; /* space at comment (only when reading header) */ int hcrc; /* true if there was or will be a header crc */ int done; /* true when done reading gzip header (not used @@ -225,7 +226,7 @@ const int Z_NULL = 0; /* for initializing zalloc, zfree, opaque */ /* basic functions */ -char* zlibVersion(); +const(char)* zlibVersion(); /* The application can compare zlibVersion and ZLIB_VERSION for consistency. If the first character differs, the library code actually used is not compatible with the zlib.h header file used by the application. @@ -558,7 +559,7 @@ int deflateInit2(z_streamp strm, not perform any compression: this will be done by deflate(). */ -int deflateSetDictionary(z_streamp strm, ubyte* dictionary, uint dictLength); +int deflateSetDictionary(z_streamp strm, const(ubyte)* dictionary, uint dictLength); /* Initializes the compression dictionary from the given byte sequence without producing any compressed output. This function must be called @@ -798,7 +799,7 @@ int inflateInit2(z_streamp strm, int windowBits) and avail_out are unchanged.) / -int inflateSetDictionary(z_streamp strm, ubyte* dictionary, uint dictLength); +int inflateSetDictionary(z_streamp strm, const(ubyte)* dictionary, uint dictLength); /* Initializes the decompression dictionary from the given uncompressed byte sequence. This function must be called immediately after a call of inflate, @@ -1017,7 +1018,7 @@ uint zlibCompileFlags(); int compress(ubyte* dest, size_t* destLen, - ubyte* source, + const(ubyte)* source, size_t sourceLen); /* Compresses the source buffer into the destination buffer. sourceLen is @@ -1034,7 +1035,7 @@ int compress(ubyte* dest, int compress2(ubyte* dest, size_t* destLen, - ubyte* source, + const(ubyte)* source, size_t sourceLen, int level); /* @@ -1059,7 +1060,7 @@ size_t compressBound(size_t sourceLen); int uncompress(ubyte* dest, size_t* destLen, - ubyte* source, + const(ubyte)* source, size_t sourceLen); /* Decompresses the source buffer into the destination buffer. sourceLen is @@ -1081,7 +1082,7 @@ int uncompress(ubyte* dest, alias void* gzFile; alias int z_off_t; // file offset -gzFile gzopen(char* path, char* mode); +gzFile gzopen(const(char)* path, const(char)* mode); /* Opens a gzip (.gz) file for reading or writing. The mode parameter is as in fopen ("rb" or "wb") but can also include a compression level @@ -1098,7 +1099,7 @@ gzFile gzopen(char* path, char* mode); can be checked to distinguish the two cases (if errno is zero, the zlib error is Z_MEM_ERROR). */ -gzFile gzdopen(int fd, char* mode); +gzFile gzdopen(int fd, const(char)* mode); /* gzdopen() associates a gzFile with the file descriptor fd. File descriptors are obtained from calls like open, dup, creat, pipe or @@ -1134,7 +1135,7 @@ int gzwrite(gzFile file, void* buf, uint len); (0 in case of error). */ -int gzprintf(gzFile file, char* format, ...); +int gzprintf(gzFile file, const(char)* format, ...); /* Converts, formats, and writes the args to the compressed file under control of the format string, as in fprintf. gzprintf returns the number of @@ -1147,14 +1148,14 @@ int gzprintf(gzFile file, char* format, ...); because the secure snprintf() or vsnprintf() functions were not available. */ -int gzputs(gzFile file, char* s); +int gzputs(gzFile file, const(char)* s); /* Writes the given null-terminated string to the compressed file, excluding the terminating null character. gzputs returns the number of characters written, or -1 in case of error. */ -char* gzgets(gzFile file, char* buf, int len); +const(char)* gzgets(gzFile file, const(char)* buf, int len); /* Reads bytes from the compressed file until len-1 characters are read, or a newline character is read and transferred to buf, or an end-of-file @@ -1247,7 +1248,7 @@ int gzclose(gzFile file); error number (see function gzerror below). */ -char* gzerror(gzFile file, int *errnum); +const(char)* gzerror(gzFile file, int* errnum); /* Returns the error message for the last error which occurred on the given compressed file. errnum is set to zlib error number. If an @@ -1271,7 +1272,7 @@ void gzclearerr (gzFile file); compression library. */ - uint adler32 (uint adler, ubyte *buf, uint len); + uint adler32 (uint adler, ubyte* buf, uint len); /* Update a running Adler-32 checksum with the bytes buf[0..len-1] and @@ -1296,7 +1297,7 @@ uint adler32_combine(uint adler1, uint adler2, z_off_t len2); seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. */ -uint crc32(uint crc, ubyte *buf, uint len); +uint crc32(uint crc, ubyte* buf, uint len); /* Update a running CRC-32 with the bytes buf[0..len-1] and return the updated CRC-32. If buf is NULL, this function returns the required initial @@ -1330,11 +1331,11 @@ uint crc32_combine (uint crc1, uint crc2, z_off_t len2); */ int deflateInit_(z_streamp strm, int level, - const char* versionx, + const(char)* versionx, int stream_size); int inflateInit_(z_streamp strm, - const char* versionx, + const(char)* versionx, int stream_size); int deflateInit2_(z_streamp strm, @@ -1343,20 +1344,20 @@ int deflateInit2_(z_streamp strm, int windowBits, int memLevel, int strategy, - const char* versionx, + const(char)* versionx, int stream_size); int inflateBackInit_(z_stream* strm, int windowBits, ubyte* window, - const char* z_version, + const(char)* z_version, int stream_size); int inflateInit2_(z_streamp strm, int windowBits, - const char* versionx, + const(char)* versionx, int stream_size); -char* zError(int err); +const(char)* zError(int err); int inflateSyncPoint(z_streamp z); -uint* get_crc_table(); +const(uint)* get_crc_table(); diff --git a/libphobos/src/index.d b/libphobos/src/index.d index a85613f8a..02c0686b5 100644 --- a/libphobos/src/index.d +++ b/libphobos/src/index.d @@ -220,7 +220,7 @@ $(V1 -
std.base64 +
std.uni
Functions that operate on Unicode characters.
std.uri diff --git a/libphobos/src/std/algorithm.d b/libphobos/src/std/algorithm.d index 5c9acfb17..236291e18 100644 --- a/libphobos/src/std/algorithm.d +++ b/libphobos/src/std/algorithm.d @@ -7,8 +7,8 @@ $(BOOKTABLE , $(TR $(TH Category) $(TH Functions) ) $(TR $(TDNW Searching) $(TD $(MYREF all) $(MYREF any) $(MYREF balancedParens) $(MYREF -boyerMooreFinder) $(MYREF canFind) $(MYREF count) $(MYREF countUntil) -$(MYREF commonPrefix) $(MYREF endsWith) $(MYREF find) $(MYREF +boyerMooreFinder) $(MYREF canFind) $(MYREF commonPrefix) $(MYREF count) +$(MYREF countUntil) $(MYREF endsWith) $(MYREF find) $(MYREF findAdjacent) $(MYREF findAmong) $(MYREF findSkip) $(MYREF findSplit) $(MYREF findSplitAfter) $(MYREF findSplitBefore) $(MYREF minCount) $(MYREF minPos) $(MYREF mismatch) $(MYREF skipOver) $(MYREF startsWith) @@ -20,10 +20,10 @@ $(MYREF min) $(MYREF mismatch) ) ) $(TR $(TDNW Iteration) $(TD $(MYREF filter) $(MYREF filterBidirectional) $(MYREF group) $(MYREF joiner) $(MYREF map) $(MYREF reduce) $(MYREF -splitter) $(MYREF uniq) ) +splitter) $(MYREF sum) $(MYREF uniq) ) ) $(TR $(TDNW Sorting) $(TD $(MYREF completeSort) $(MYREF isPartitioned) -$(MYREF isSorted) $(MYREF makeIndex) $(MYREF nextPermutation) +$(MYREF isSorted) $(MYREF makeIndex) $(MYREF multiSort) $(MYREF nextPermutation) $(MYREF nextEvenPermutation) $(MYREF partialSort) $(MYREF partition) $(MYREF partition3) $(MYREF schwartzSort) $(MYREF sort) $(MYREF topN) $(MYREF topNCopy) ) @@ -36,7 +36,9 @@ setSymmetricDifference) $(MYREF setUnion) ) $(TR $(TDNW Mutation) $(TD $(MYREF bringToFront) $(MYREF copy) $(MYREF fill) $(MYREF initializeAll) $(MYREF move) $(MYREF moveAll) $(MYREF moveSome) $(MYREF remove) $(MYREF reverse) $(MYREF strip) $(MYREF stripLeft) -$(MYREF stripRight) $(MYREF swap) $(MYREF swapRanges) $(MYREF uninitializedFill) )) +$(MYREF stripRight) $(MYREF swap) $(MYREF swapRanges) $(MYREF uninitializedFill) ) +) +$(TR $(TDNW Utility) $(TD $(MYREF forward) )) ) Implements algorithms oriented mainly towards processing of @@ -101,6 +103,9 @@ $(TR $(TDNW $(LREF countUntil)) $(TD $(D countUntil(a, b)) returns the number of steps taken in $(D a) to reach $(D b); for example, $(D countUntil("hello!", "o")) returns $(D 4).) ) +$(TR $(TDNW $(LREF commonPrefix)) $(TD $(D commonPrefix("parakeet", +"parachute")) returns $(D "para").) +) $(TR $(TDNW $(LREF endsWith)) $(TD $(D endsWith("rocks", "ks")) returns $(D true).) ) @@ -139,6 +144,9 @@ $(TR $(TDNW $(LREF minPos)) $(TD $(D minPos([2, 3, 1, 3, 4, 1])) returns the subrange $(D [1, 3, 4, 1]), i.e., positions the range at the first occurrence of its minimal element.) ) +$(TR $(TDNW $(LREF mismatch)) $(TD $(D mismatch("parakeet", "parachute")) +returns the two ranges $(D "keet") and $(D "chute").) +) $(TR $(TDNW $(LREF skipOver)) $(TD Assume $(D a = "blah"). Then $(D skipOver(a, "bi")) leaves $(D a) unchanged and returns $(D false), whereas $(D skipOver(a, "bl")) advances $(D a) to refer to $(D "ah") @@ -207,6 +215,9 @@ $(TR $(TDNW $(LREF reduce)) $(TD $(D reduce!"a + b"([1, 2, 3, $(TR $(TDNW $(LREF splitter)) $(TD Lazily splits a range by a separator.) ) +$(TR $(TDNW $(LREF sum)) $(TD Same as $(D reduce), but specialized for +accurate summation.) +) $(TR $(TDNW $(LREF uniq)) $(TD Iterates over the unique elements in a range, which is assumed sorted.) ) @@ -241,6 +252,9 @@ $(TR $(TDNW $(LREF partialSort)) $(TD If $(D a = [5, 4, 3, 2, $(TR $(TDNW $(LREF partition)) $(TD Partitions a range according to a predicate.) ) +$(TR $(TDNW $(LREF partition3)) $(TD Partitions a range +in three parts (less than, equal, greater than the given pivot).) +) $(TR $(TDNW $(LREF schwartzSort)) $(TD Sorts with the help of the $(LUCKY Schwartzian transform).) ) @@ -306,6 +320,9 @@ range to another.) $(TR $(TDNW $(LREF moveSome)) $(TD Moves as many elements as possible from one range to another.) ) +$(TR $(TDNW $(LREF remove)) $(TD Removes elements from a range +in-place, and returns the shortened range.) +) $(TR $(TDNW $(LREF reverse)) $(TD If $(D a = [1, 2, 3]), $(D reverse(a)) changes it to $(D [3, 2, 1]).) ) @@ -336,7 +353,7 @@ $(TR $(TDNW $(LREF uninitializedFill)) $(TD Fills a range Macros: WIKI = Phobos/StdAlgorithm -MYREF = $1  +MYREF = $1  Copyright: Andrei Alexandrescu 2008-. @@ -363,6 +380,15 @@ version(unittest) private T* addressOf(T)(ref T val) { return &val; } +// Same as std.string.format, but "self-importing". +// Helps reduce code and imports, particularly in static asserts. +// Also helps with missing imports errors. +private template algoFormat() +{ + import std.string : format; + alias algoFormat = std.string.format; +} + /** $(D auto map(Range)(Range r) if (isInputRange!(Unqual!Range));) @@ -376,15 +402,26 @@ template map(fun...) if (fun.length >= 1) { auto map(Range)(Range r) if (isInputRange!(Unqual!Range)) { + alias AppliedReturnType(alias f) = typeof(f(r.front)); + static if (fun.length > 1) { import std.functional : adjoin; + import std.typetuple : staticIndexOf; - alias adjoin!(staticMap!(unaryFun, fun)) _fun; + alias _funs = staticMap!(unaryFun, fun); + alias _fun = adjoin!_funs; + + alias ReturnTypes = staticMap!(AppliedReturnType, _funs); + static assert(staticIndexOf!(void, ReturnTypes) == -1, + "All mapping functions must not return void."); } else { - alias unaryFun!fun _fun; + alias _fun = unaryFun!fun; + + static assert(!is(AppliedReturnType!_fun == void), + "Mapping function must not return void."); } return MapResult!(_fun, Range)(r); @@ -427,14 +464,13 @@ unittest { import std.conv : to; - alias map!(to!string) stringize; + alias stringize = map!(to!string); assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ])); } private struct MapResult(alias fun, Range) { - alias Unqual!Range R; - //alias typeof(fun(.ElementType!R.init)) ElementType; + alias R = Unqual!Range; R _input; static if (isBidirectionalRange!R) @@ -481,9 +517,9 @@ private struct MapResult(alias fun, Range) static if (isRandomAccessRange!R) { static if (is(typeof(_input[ulong.max]))) - private alias ulong opIndex_t; + private alias opIndex_t = ulong; else - private alias uint opIndex_t; + private alias opIndex_t = uint; auto ref opIndex(opIndex_t index) { @@ -498,7 +534,7 @@ private struct MapResult(alias fun, Range) return _input.length; } - alias length opDollar; + alias opDollar = length; } static if (hasSlicing!R) @@ -535,9 +571,7 @@ private struct MapResult(alias fun, Range) { @property auto save() { - auto result = this; - result._input = result._input.save; - return result; + return typeof(this)(_input.save); } } } @@ -550,16 +584,16 @@ unittest debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); - alias map!(to!string) stringize; + alias stringize = map!(to!string); assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ])); uint counter; - alias map!((a) { return counter++; }) count; + alias count = map!((a) { return counter++; }); assert(equal(count([ 10, 2, 30, 4 ]), [ 0, 1, 2, 3 ])); counter = 0; adjoin!((a) { return counter++; }, (a) { return counter++; })(1); - alias map!((a) { return counter++; }, (a) { return counter++; }) countAndSquare; + alias countAndSquare = map!((a) { return counter++; }, (a) { return counter++; }); //assert(equal(countAndSquare([ 10, 2 ]), [ tuple(0u, 100), tuple(1u, 4) ])); } @@ -652,6 +686,14 @@ unittest static assert(!is(ms1[0..1])); //narrow strings can't be sliced assert(equal(ms2[0..2], "日本"w)); assert(equal(ms3[0..2], "HE")); + + // Issue 5753 + static void voidFun(int) {} + static int nonvoidFun(int) { return 0; } + static assert(!__traits(compiles, map!voidFun([1]))); + static assert(!__traits(compiles, map!(voidFun, voidFun)([1]))); + static assert(!__traits(compiles, map!(nonvoidFun, voidFun)([1]))); + static assert(!__traits(compiles, map!(voidFun, nonvoidFun)([1]))); } unittest { @@ -684,6 +726,12 @@ unittest assert(equal(rr[0 .. 5], [1, 4, 9, 16, 0])); } +unittest +{ + struct S {int* p;} + auto m = immutable(S).init.repeat().map!"a".save; +} + /** $(D auto reduce(Args...)(Args args) if (Args.length > 0 && Args.length <= 2 && isIterable!(Args[$ - 1]));) @@ -697,6 +745,9 @@ range), $(D result = fun(result, x)) gets evaluated. Finally, $(D result) is returned. The one-argument version $(D reduce!(fun)(range)) works similarly, but it uses the first element of the range as the seed (the range must be non-empty). + +See also: $(LREF sum) is similar to $(D reduce!((a, b) => a + b)) that offers +precise summing of floating point numbers. */ template reduce(fun...) if (fun.length >= 1) { @@ -709,8 +760,8 @@ template reduce(fun...) if (fun.length >= 1) { static if (Args.length == 2) { - alias args[0] seed; - alias args[1] r; + alias seed = args[0]; + alias r = args[1]; Unqual!(Args[0]) result = seed; for (; !r.empty; r.popFront()) { @@ -732,7 +783,7 @@ template reduce(fun...) if (fun.length >= 1) { enforce(!args[$ - 1].empty, "Cannot reduce an empty range w/o an explicit seed value."); - alias args[0] r; + alias r = args[0]; static if (fun.length == 1) { auto seed = r.front; @@ -742,7 +793,7 @@ template reduce(fun...) if (fun.length >= 1) else { import std.functional : adjoin; - import std.conv : emplace; + import std.conv : emplaceRef; static assert(fun.length > 1); Unqual!(typeof(r.front)) seed = r.front; @@ -750,7 +801,7 @@ template reduce(fun...) if (fun.length >= 1) result = void; foreach (i, T; result.Types) { - emplace(&result[i], seed); + emplaceRef!T(result[i], seed); } r.popFront(); return reduce(result, r); @@ -763,9 +814,9 @@ template reduce(fun...) if (fun.length >= 1) // copying, iterating by dchar over strings, and dealing with the // no explicit start value case would become an unreadable mess // if these were merged. - alias args[$ - 1] r; - alias Args[$ - 1] R; - alias ForeachType!R E; + alias r = args[$ - 1]; + alias R = Args[$ - 1]; + alias E = ForeachType!R; static if (args.length == 2) { @@ -807,7 +858,7 @@ template reduce(fun...) if (fun.length >= 1) } else { - import std.conv : emplace; + import std.conv : emplaceRef; static if (is(typeof(&initialized))) { @@ -816,7 +867,7 @@ template reduce(fun...) if (fun.length >= 1) foreach (i, T; result.Types) { - emplace(&result[i], elem); + emplaceRef!T(result[i], elem); } } } @@ -1000,13 +1051,198 @@ unittest assert(minmax == tuple(10, 30)); } +// sum +/** +Sums elements of $(D r), which must be a finite input range. Although +conceptually $(D sum(r)) is equivalent to $(D reduce!((a, b) => a + +b)(0, r)), $(D sum) uses specialized algorithms to maximize accuracy, +as follows. + +$(UL +$(LI If $(D ElementType!R) is a floating-point type and $(D R) is a +random-access range with length and slicing, then $(D sum) uses the +$(WEB en.wikipedia.org/wiki/Pairwise_summation, pairwise summation) +algorithm.) +$(LI If $(D ElementType!R) is a floating-point type and $(D R) is a +finite input range (but not a random-access range with slicing), then +$(D sum) uses the $(WEB en.wikipedia.org/wiki/Kahan_summation, +Kahan summation) algorithm.) +$(LI In all other cases, a simple element by element addition is done.) +) + +For floating point inputs, calculations are made in $(D real) +precision for $(D real) inputs and in $(D double) precision otherwise +(Note this is a special case that deviates from $(D reduce)'s behavior, +which would have kept $(D float) precision for a $(D float) range). +For all other types, the calculations are done in the same type obtained +from from adding two elements of the range, which may be a different +type from the elements themselves (for example, in case of integral promotion). + +A seed may be passed to $(D sum). Not only will this seed be used as an initial +value, but its type will override all the above, and determine the algorithm +and precision used for sumation. + +Note that these specialized summing algorithms execute more primitive operations +than vanilla summation. Therefore, if in certain cases maximum speed is required +at expense of precision, one can use $(D reduce!((a, b) => a + b)(0, r)), which +is not specialized for summation. + */ +auto sum(R)(R r) +if (isInputRange!R && !isInfinite!R && is(typeof(r.front + r.front))) +{ + alias E = Unqual!(ElementType!R); + static if (isFloatingPoint!E) + alias Seed = typeof(E.init + 0.0); //biggest of double/real + else + alias Seed = typeof(r.front + r.front); + return sum(r, Unqual!Seed(0)); +} +/// ditto +auto sum(R, E)(R r, E seed) +if (isInputRange!R && !isInfinite!R && is(typeof(seed = seed + r.front))) +{ + static if (isFloatingPoint!E) + { + static if (hasLength!R && hasSlicing!R) + return seed + sumPairwise!E(r); + else + return sumKahan!E(seed, r); + } + else + { + return reduce!"a + b"(seed, r); + } +} + +// Pairwise summation http://en.wikipedia.org/wiki/Pairwise_summation +private auto sumPairwise(Result, R)(R r) +{ + static assert (isFloatingPoint!Result); + switch (r.length) + { + case 0: return cast(Result) 0; + case 1: return cast(Result) r.front; + case 2: return cast(Result) r[0] + cast(Result) r[1]; + default: return sumPairwise!Result(r[0 .. $ / 2]) + sumPairwise!Result(r[$ / 2 .. $]); + } +} + +// Kahan algo http://en.wikipedia.org/wiki/Kahan_summation_algorithm +private auto sumKahan(Result, R)(Result result, R r) +{ + static assert (isFloatingPoint!Result && isMutable!Result); + Result c = 0; + for (; !r.empty; r.popFront()) + { + auto y = r.front - c; + auto t = result + y; + c = (t - result) - y; + result = t; + } + return result; +} + +/// Ditto +@safe pure nothrow unittest +{ + //simple integral sumation + assert(sum([ 1, 2, 3, 4]) == 10); + + //with integral promotion + assert(sum([false, true, true, false, true]) == 3); + assert(sum(ubyte.max.repeat(100)) == 25500); + + //The result may overflow + assert(uint.max.repeat(3).sum() == 4294967293U ); + //But a seed can be used to change the sumation primitive + assert(uint.max.repeat(3).sum(ulong.init) == 12884901885UL); + + //Floating point sumation + assert(sum([1.0, 2.0, 3.0, 4.0]) == 10); + + //Floating point operations have double precision minimum + static assert(is(typeof(sum([1F, 2F, 3F, 4F])) == double)); + assert(sum([1F, 2, 3, 4]) == 10); + + //Force pair-wise floating point sumation on large integers + import std.math : approxEqual; + assert(iota(ulong.max / 2, ulong.max / 2 + 4096).sum(0.0) + .approxEqual((ulong.max / 2) * 4096.0 + 4096^^2 / 2)); +} + +@safe pure nothrow unittest +{ + static assert(is(typeof(sum([cast( byte)1])) == int)); + static assert(is(typeof(sum([cast(ubyte)1])) == int)); + static assert(is(typeof(sum([ 1, 2, 3, 4])) == int)); + static assert(is(typeof(sum([ 1U, 2U, 3U, 4U])) == uint)); + static assert(is(typeof(sum([ 1L, 2L, 3L, 4L])) == long)); + static assert(is(typeof(sum([1UL, 2UL, 3UL, 4UL])) == ulong)); + + int[] empty; + assert(sum(empty) == 0); + assert(sum([42]) == 42); + assert(sum([42, 43]) == 42 + 43); + assert(sum([42, 43, 44]) == 42 + 43 + 44); + assert(sum([42, 43, 44, 45]) == 42 + 43 + 44 + 45); +} + +@safe pure nothrow unittest +{ + static assert(is(typeof(sum([1.0, 2.0, 3.0, 4.0])) == double)); + static assert(is(typeof(sum([ 1F, 2F, 3F, 4F])) == double)); + const(float[]) a = [1F, 2F, 3F, 4F]; + static assert(is(typeof(sum(a)) == double)); + const(float)[] b = [1F, 2F, 3F, 4F]; + static assert(is(typeof(sum(a)) == double)); + + double[] empty; + assert(sum(empty) == 0); + assert(sum([42.]) == 42); + assert(sum([42., 43.]) == 42 + 43); + assert(sum([42., 43., 44.]) == 42 + 43 + 44); + assert(sum([42., 43., 44., 45.5]) == 42 + 43 + 44 + 45.5); +} + +@safe pure nothrow unittest +{ + import std.container; + static assert(is(typeof(sum(SList!float()[])) == double)); + static assert(is(typeof(sum(SList!double()[])) == double)); + static assert(is(typeof(sum(SList!real()[])) == real)); + + assert(sum(SList!double()[]) == 0); + assert(sum(SList!double(1)[]) == 1); + assert(sum(SList!double(1, 2)[]) == 1 + 2); + assert(sum(SList!double(1, 2, 3)[]) == 1 + 2 + 3); + assert(sum(SList!double(1, 2, 3, 4)[]) == 10); +} + +@safe pure nothrow unittest // 12434 +{ + immutable a = [10, 20]; + auto s1 = sum(a); // Error + auto s2 = a.map!(x => x).sum; // Error +} + +unittest +{ + import std.bigint; + immutable BigInt[] a = BigInt("1_000_000_000_000_000_000").repeat(10).array(); + immutable ulong[] b = (ulong.max/2).repeat(10).array(); + auto sa = a.sum(); + auto sb = b.sum(BigInt(0)); //reduce ulongs into bigint + assert(sa == BigInt("10_000_000_000_000_000_000")); + assert(sb == (BigInt(ulong.max/2) * 10)); +} + /** Fills $(D range) with a $(D filler). */ void fill(Range, Value)(Range range, Value filler) if (isInputRange!Range && is(typeof(range.front = filler))) { - alias ElementType!Range T; + alias T = ElementType!Range; static if (is(typeof(range[] = filler))) { @@ -1056,7 +1292,7 @@ unittest //writeln(benchmark!(fun0, fun1, fun2)(10000)); // fill should accept InputRange - alias DummyRange!(ReturnBy.Reference, Length.No, RangeType.Input) InputRange; + alias InputRange = DummyRange!(ReturnBy.Reference, Length.No, RangeType.Input); enum filler = uint.max; InputRange range; fill(range, filler); @@ -1195,7 +1431,7 @@ unittest assert(a == [ 1, 2, 1, 2, 1 ]); // fill should accept InputRange - alias DummyRange!(ReturnBy.Reference, Length.No, RangeType.Input) InputRange; + alias InputRange = DummyRange!(ReturnBy.Reference, Length.No, RangeType.Input); InputRange range; fill(range,[1,2]); foreach (i,value;range.arr) @@ -1233,14 +1469,14 @@ assert(s == [ 42, 42, 42, 42, 42 ]); void uninitializedFill(Range, Value)(Range range, Value filler) if (isInputRange!Range && hasLvalueElements!Range && is(typeof(range.front = filler))) { - alias ElementType!Range T; + alias T = ElementType!Range; static if (hasElaborateAssign!T) { - import std.conv : emplace; + import std.conv : emplaceRef; // Must construct stuff by the book for (; !range.empty; range.popFront()) - emplace(addressOf(range.front), filler); + emplaceRef!T(range.front, filler); } else // Doesn't matter whether fill is initialized or not @@ -1268,7 +1504,7 @@ void initializeAll(Range)(Range range) { import core.stdc.string : memset, memcpy; - alias ElementType!Range T; + alias T = ElementType!Range; static if (hasElaborateAssign!T) { //Elaborate opAssign. Must go the memcpy road. @@ -1294,7 +1530,7 @@ void initializeAll(Range)(Range range) void initializeAll(Range)(Range range) if (is(Range == char[]) || is(Range == wchar[])) { - alias ElementEncodingType!Range T; + alias T = ElementEncodingType!Range; range[] = T.init; } @@ -1426,7 +1662,7 @@ unittest private struct FilterResult(alias pred, Range) { - alias Unqual!Range R; + alias R = Unqual!Range; R _input; this(R r) @@ -1596,7 +1832,7 @@ unittest private struct FilterBidiResult(alias pred, Range) { - alias Unqual!Range R; + alias R = Unqual!Range; R _input; this(R r) @@ -1643,21 +1879,18 @@ private struct FilterBidiResult(alias pred, Range) // move /** Moves $(D source) into $(D target) via a destructive -copy. Specifically: $(UL $(LI If $(D hasAliasing!T) is true (see -$(XREF traits, hasAliasing)), then the representation of $(D source) -is bitwise copied into $(D target) and then $(D source = T.init) is -evaluated.) $(LI Otherwise, $(D target = source) is evaluated.)) See -also $(XREF exception, pointsTo). - -Preconditions: -$(D &source == &target || !pointsTo(source, source)) +copy. */ void move(T)(ref T source, ref T target) { import core.stdc.string : memcpy; - import std.exception : pointsTo; - assert(!pointsTo(source, source)); + static if (hasAliasing!T) if (!__ctfe) + { + import std.exception : doesPointTo; + assert(!doesPointTo(source, source), "Cannot move object with internal pointer."); + } + static if (is(T == struct)) { if (&source == &target) return; @@ -1665,7 +1898,10 @@ void move(T)(ref T source, ref T target) // and bitblast source over it static if (hasElaborateDestructor!T) typeid(T).destroy(&target); - memcpy(&target, &source, T.sizeof); + static if (hasElaborateAssign!T || !isAssignable!T) + memcpy(&target, &source, T.sizeof); + else + target = source; // If the source defines a destructor or a postblit hook, we must obliterate the // object in order to avoid double freeing and undue aliasing @@ -1689,11 +1925,6 @@ void move(T)(ref T source, ref T target) // Primitive data (including pointers and arrays) or class - // assignment works great target = source; - // static if (is(typeof(source = null))) - // { - // // Nullify the source to help the garbage collector - // source = null; - // } } } @@ -1701,25 +1932,27 @@ unittest { debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); - Object obj1 = new Object; - Object obj2 = obj1; - Object obj3; - move(obj2, obj3); - assert(obj3 is obj1); - - static struct S1 { int a = 1, b = 2; } - S1 s11 = { 10, 11 }; - S1 s12; - move(s11, s12); - assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11); - - static struct S2 { int a = 1; int * b; } - S2 s21 = { 10, null }; - s21.b = new int; - S2 s22; - move(s21, s22); - assert(s21 == s22); - + import std.exception : assertCTFEable; + assertCTFEable!((){ + Object obj1 = new Object; + Object obj2 = obj1; + Object obj3; + move(obj2, obj3); + assert(obj3 is obj1); + + static struct S1 { int a = 1, b = 2; } + S1 s11 = { 10, 11 }; + S1 s12; + move(s11, s12); + assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11); + + static struct S2 { int a = 1; int * b; } + S2 s21 = { 10, null }; + s21.b = new int; + S2 s22; + move(s21, s22); + assert(s21 == s22); + }); // Issue 5661 test(1) static struct S3 { @@ -1752,14 +1985,20 @@ T move(T)(ref T source) { import core.stdc.string : memcpy; - // Can avoid to check aliasing. + static if (hasAliasing!T) if (!__ctfe) + { + import std.exception : doesPointTo; + assert(!doesPointTo(source, source), "Cannot move object with internal pointer."); + } T result = void; static if (is(T == struct)) { // Can avoid destructing result. - - memcpy(&result, &source, T.sizeof); + static if (hasElaborateAssign!T || !isAssignable!T) + memcpy(&result, &source, T.sizeof); + else + result = source; // If the source defines a destructor or a postblit hook, we must obliterate the // object in order to avoid double freeing and undue aliasing @@ -1791,21 +2030,24 @@ unittest { debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); - Object obj1 = new Object; - Object obj2 = obj1; - Object obj3 = move(obj2); - assert(obj3 is obj1); - - static struct S1 { int a = 1, b = 2; } - S1 s11 = { 10, 11 }; - S1 s12 = move(s11); - assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11); - - static struct S2 { int a = 1; int * b; } - S2 s21 = { 10, null }; - s21.b = new int; - S2 s22 = move(s21); - assert(s21 == s22); + import std.exception : assertCTFEable; + assertCTFEable!((){ + Object obj1 = new Object; + Object obj2 = obj1; + Object obj3 = move(obj2); + assert(obj3 is obj1); + + static struct S1 { int a = 1, b = 2; } + S1 s11 = { 10, 11 }; + S1 s12 = move(s11); + assert(s11.a == 10 && s11.b == 11 && s12.a == 10 && s12.b == 11); + + static struct S2 { int a = 1; int * b; } + S2 s21 = { 10, null }; + s21.b = new int; + S2 s22 = move(s21); + assert(s21 == s22); + }); // Issue 5661 test(1) static struct S3 @@ -1900,8 +2142,8 @@ unittest// Issue 8057 /** For each element $(D a) in $(D src) and each element $(D b) in $(D tgt) in lockstep in increasing order, calls $(D move(a, b)). Returns -the leftover portion of $(D tgt). Throws an exeption if there is not -enough room in $(D tgt) to acommodate all of $(D src). +the leftover portion of $(D tgt). Throws an exception if there is not +enough room in $(D tgt) to accommodate all of $(D src). Preconditions: $(D walkLength(src) <= walkLength(tgt)) @@ -1968,7 +2210,7 @@ unittest { debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); - int[] a = [ 1, 2, 3, 4, 5 ]; + int[] a = [ 1, 2, 3, 4, 5 ]; int[] b = new int[3]; assert(moveSome(a, b)[0] is a[3 .. $]); assert(a[0 .. 3] == b); @@ -1985,30 +2227,24 @@ If $(D lhs) and $(D rhs) reference the same instance, then nothing is done. $(D lhs) and $(D rhs) must be mutable. If $(D T) is a struct or union, then its fields must also all be (recursivelly) mutable. - -Preconditions: - -$(D !pointsTo(lhs, lhs) && !pointsTo(lhs, rhs) && !pointsTo(rhs, lhs) -&& !pointsTo(rhs, rhs)) - -See_Also: - $(XREF exception, pointsTo) - */ +*/ void swap(T)(ref T lhs, ref T rhs) @trusted pure nothrow -if (isBlitAssignable!T && !is(typeof(T.init.proxySwap(T.init)))) +if (isBlitAssignable!T && !is(typeof(lhs.proxySwap(rhs)))) { - static if (hasElaborateAssign!T || !isAssignable!T) + static if (hasAliasing!T) if (!__ctfe) { - import std.exception : pointsTo; + import std.exception : doesPointTo; + assert(!doesPointTo(lhs, lhs), "Swap: lhs internal pointer."); + assert(!doesPointTo(rhs, rhs), "Swap: rhs internal pointer."); + assert(!doesPointTo(lhs, rhs), "Swap: lhs points to rhs."); + assert(!doesPointTo(rhs, lhs), "Swap: rhs points to lhs."); + } + static if (hasElaborateAssign!T || !isAssignable!T) + { if (&lhs != &rhs) { // For structs with non-trivial assignment, move memory directly - // First check for undue aliasing - static if (hasIndirections!T) - assert(!pointsTo(lhs, rhs) && !pointsTo(rhs, lhs) - && !pointsTo(lhs, lhs) && !pointsTo(rhs, rhs)); - // Swap bits ubyte[T.sizeof] t = void; auto a = (cast(ubyte*) &lhs)[0 .. T.sizeof]; auto b = (cast(ubyte*) &rhs)[0 .. T.sizeof]; @@ -2039,7 +2275,7 @@ if (isBlitAssignable!T && !is(typeof(T.init.proxySwap(T.init)))) } // Not yet documented -void swap(T)(T lhs, T rhs) if (is(typeof(T.init.proxySwap(T.init)))) +void swap(T)(ref T lhs, ref T rhs) if (is(typeof(lhs.proxySwap(rhs)))) { lhs.proxySwap(rhs); } @@ -2150,7 +2386,32 @@ unittest // 12024 import std.datetime; SysTime a, b; +} + +unittest // 9975 +{ + import std.exception : doesPointTo, mayPointTo; + static struct S2 + { + union + { + size_t sz; + string s; + } + } + S2 a , b; + a.sz = -1; + assert(!doesPointTo(a, b)); + assert( mayPointTo(a, b)); swap(a, b); + + //Note: we can catch an error here, because there is no RAII in this test + import std.exception : assertThrown; + void* p, pp; + p = &p; + assertThrown!Error(move(p)); + assertThrown!Error(move(p, pp)); + assertThrown!Error(swap(p, pp)); } void swapFront(R1, R2)(R1 r1, R2 r2) @@ -2297,7 +2558,7 @@ if (is(typeof(ElementType!Range.init == Separator.init)) Range _input; Separator _separator; // Do we need hasLength!Range? popFront uses _input.length... - alias typeof(unsigned(_input.length)) IndexType; + alias IndexType = typeof(unsigned(_input.length)); enum IndexType _unComputed = IndexType.max - 1, _atEnd = IndexType.max; IndexType _frontLength = _unComputed; IndexType _backLength = _unComputed; @@ -2549,7 +2810,7 @@ if (is(typeof(Range.init.front == Separator.init.front) : bool) private: Range _input; Separator _separator; - alias typeof(unsigned(_input.length)) RIndexType; + alias RIndexType = typeof(unsigned(_input.length)); // _frontLength == size_t.max means empty RIndexType _frontLength = RIndexType.max; static if (isBidirectionalRange!Range) @@ -2832,7 +3093,7 @@ private struct SplitterResult(alias isTerminator, Range) static if (fullSlicing) return _input[0 .. _end]; else - return _input.save.takeExactly(_end); + return _input.takeExactly(_end); } void popFront() @@ -2895,10 +3156,8 @@ unittest writeln("unittest @", __FILE__, ":", __LINE__, " done."); void compare(string sentence, string[] witness) { - import std.string : format; - auto r = splitter!"a == ' '"(sentence); - assert(equal(r.save, witness), format("got: %(%s, %) expected: %(%s, %)", r, witness)); + assert(equal(r.save, witness), algoFormat("got: %(%s, %) expected: %(%s, %)", r, witness)); } compare(" Mary has a little lamb. ", @@ -2941,11 +3200,9 @@ unittest ]; foreach ( entry ; entries ) { - import std.string : format; - auto a = iota(entry.low, entry.high).filter!"true"(); auto b = splitter!"a%2"(a); - assert(equal!equal(b.save, entry.result), format("got: %(%s, %) expected: %(%s, %)", b, entry.result)); + assert(equal!equal(b.save, entry.result), algoFormat("got: %(%s, %) expected: %(%s, %)", b, entry.result)); } } @@ -2978,6 +3235,8 @@ if (isSomeChar!C) void getFirst() pure @safe { + import std.uni : isWhite; + auto r = find!(std.uni.isWhite)(_s); _frontLength = _s.length - r.length; } @@ -3004,12 +3263,12 @@ if (isSomeChar!C) getFirst(); } - @property bool empty() const pure nothrow @safe + @property bool empty() const @safe pure nothrow { return _s.empty; } - @property inout(Result) save() inout pure nothrow @safe + @property inout(Result) save() inout @safe pure nothrow { return this; } @@ -3068,7 +3327,7 @@ unittest unittest { import std.conv : text; - import std.string : split, format; + import std.string : split; // Check consistency: // All flavors of split should produce the same results @@ -3082,7 +3341,7 @@ unittest { auto result = split(input, s); - assert(equal(result, split(input, [s])), format(`"[%(%s,%)]"`, split(input, [s]))); + assert(equal(result, split(input, [s])), algoFormat(`"[%(%s,%)]"`, split(input, [s]))); //assert(equal(result, split(input, [s].filter!"true"()))); //Not yet implemented assert(equal(result, split!((a) => a == s)(input)), text(split!((a) => a == s)(input))); @@ -3618,7 +3877,7 @@ unittest } assert(equal(result, "abc12def34"d), - "Unexpected result: '%s'"d.format(result)); + "Unexpected result: '%s'"d.algoFormat(result)); } // Issue 8061 @@ -3750,7 +4009,7 @@ struct Group(alias pred, R) if (isInputRange!R) { private R _input; private Tuple!(ElementType!R, uint) _current; - private alias binaryFun!pred comp; + private alias comp = binaryFun!pred; this(R input) { @@ -3915,7 +4174,7 @@ haystack) are compared with $(D needle) by using predicate $(D pred). Performs $(BIGOH walkLength(haystack)) evaluations of $(D pred). -To _find the last occurence of $(D needle) in $(D haystack), call $(D +To _find the last occurrence of $(D needle) in $(D haystack), call $(D find(retro(haystack), needle)). See $(XREF range, retro). Params: @@ -4167,7 +4426,7 @@ unittest { import std.exception : assertCTFEable; - void dg() pure @safe nothrow + void dg() @safe pure nothrow { byte[] sarr = [1, 2, 3, 4]; ubyte[] uarr = [1, 2, 3, 4]; @@ -4228,9 +4487,9 @@ if (isForwardRange!R1 && isForwardRange!R2 { //return cast(R1) find(representation(haystack), representation(needle)); // Specialization for simple string search - alias Select!(haystack[0].sizeof == 1, ubyte[], - Select!(haystack[0].sizeof == 2, ushort[], uint[])) - Representation; + alias Representation = + Select!(haystack[0].sizeof == 1, ubyte[], + Select!(haystack[0].sizeof == 2, ushort[], uint[])); // Will use the array specialization return cast(R1) .find!(pred, Representation, Representation) (cast(Representation) haystack, cast(Representation) needle); @@ -4523,6 +4782,8 @@ Finds two or more $(D needles) into a $(D haystack). The predicate $(D pred) is used throughout to compare elements. By default, elements are compared for equality. +$(D BoyerMooreFinder) allocates GC memory. + Params: haystack = The target of the search. Must be an $(GLOSSARY input @@ -4668,8 +4929,8 @@ unittest struct BoyerMooreFinder(alias pred, Range) { private: - size_t[] skip; - ptrdiff_t[ElementType!(Range)] occ; + size_t[] skip; // GC allocated + ptrdiff_t[ElementType!(Range)] occ; // GC allocated Range needle; ptrdiff_t occurrence(ElementType!(Range) c) @@ -4755,7 +5016,7 @@ public: return needle.length; } - alias length opDollar; + alias opDollar = length; } /// Ditto @@ -4777,8 +5038,8 @@ unittest { debug(std_algorithm) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); - string h = "/homes/aalexand/d/dmd/bin/../lib/libphobos.a(dmain2.o)" - "(.gnu.linkonce.tmain+0x74): In function `main' undefined reference" + string h = "/homes/aalexand/d/dmd/bin/../lib/libphobos.a(dmain2.o)"~ + "(.gnu.linkonce.tmain+0x74): In function `main' undefined reference"~ " to `_Dmain':"; string[] ns = ["libphobos", "function", " undefined", "`", ":"]; foreach (n ; ns) { @@ -5317,7 +5578,7 @@ ptrdiff_t countUntil(alias pred, R)(R haystack) } else //Everything else { - alias ElementType!R T; //For narrow strings forces dchar iteration + alias T = ElementType!R; //For narrow strings forces dchar iteration foreach (T elem; haystack) { if (unaryFun!pred(elem)) return i; @@ -5529,8 +5790,8 @@ if (isInputRange!Range && Needles.length > 1 && is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[0])) : bool ) && is(typeof(.startsWith!pred(doesThisStart, withOneOfThese[1 .. $])) : uint)) { - alias doesThisStart haystack; - alias withOneOfThese needles; + alias haystack = doesThisStart; + alias needles = withOneOfThese; // Make one pass looking for empty ranges in needles foreach (i, Unused; Needles) @@ -5602,8 +5863,8 @@ if (isInputRange!R1 && isInputRange!R2 && is(typeof(binaryFun!pred(doesThisStart.front, withThis.front)) : bool)) { - alias doesThisStart haystack; - alias withThis needle; + alias haystack = doesThisStart; + alias needle = withThis; static if (is(typeof(pred) : string)) enum isDefaultPred = pred == "a == b"; @@ -5797,12 +6058,14 @@ if (is(typeof(binaryFun!pred(r1.front, r2.front)))) r.popFront(); r2.popFront(); } - return r2.empty ? (r1 = r, true) : false; + if (r2.empty) + r1 = r; + return r2.empty; } +/// unittest { - //scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " done."); auto s1 = "Hello world"; assert(!skipOver(s1, "Ha")); assert(s1 == "Hello world"); @@ -5824,11 +6087,13 @@ unchanged and return $(D false). bool skipOver(alias pred = "a == b", R, E)(ref R r, E e) if (is(typeof(binaryFun!pred(r.front, e)))) { - return binaryFun!pred(r.front, e) - ? (r.popFront(), true) - : false; + if (!binaryFun!pred(r.front, e)) + return false; + r.popFront(); + return true; } +/// unittest { auto s1 = "Hello world"; assert(!skipOver(s1, 'a')); @@ -5880,8 +6145,8 @@ if (isBidirectionalRange!Range && Needles.length > 1 && is(typeof(.endsWith!pred(doesThisEnd, withOneOfThese[0])) : bool) && is(typeof(.endsWith!pred(doesThisEnd, withOneOfThese[1 .. $])) : uint)) { - alias doesThisEnd haystack; - alias withOneOfThese needles; + alias haystack = doesThisEnd; + alias needles = withOneOfThese; // Make one pass looking for empty ranges in needles foreach (i, Unused; Needles) @@ -5947,8 +6212,8 @@ if (isBidirectionalRange!R1 && isBidirectionalRange!R2 && is(typeof(binaryFun!pred(doesThisEnd.back, withThis.back)) : bool)) { - alias doesThisEnd haystack; - alias withThis needle; + alias haystack = doesThisEnd; + alias needle = withThis; static if (is(typeof(pred) : string)) enum isDefaultPred = pred == "a == b"; @@ -6345,7 +6610,7 @@ unittest /** The first version counts the number of elements $(D x) in $(D r) for which $(D pred(x, value)) is $(D true). $(D pred) defaults to -equality. Performs $(BIGOH r.length) evaluations of $(D pred). +equality. Performs $(BIGOH haystack.length) evaluations of $(D pred). The second version returns the number of times $(D needle) occurs in $(D haystack). Throws an exception if $(D needle.empty), as the _count @@ -6354,7 +6619,7 @@ are not considered, for example $(D count("aaa", "aa")) is $(D 1), not $(D 2). The third version counts the elements for which $(D pred(x)) is $(D -true). Performs $(BIGOH r.length) evaluations of $(D pred). +true). Performs $(BIGOH haystack.length) evaluations of $(D pred). Note: Regardless of the overload, $(D count) will not accept infinite ranges for $(D haystack). @@ -6421,9 +6686,8 @@ size_t count(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) isForwardRange!R2 && is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool)) { - import std.exception : enforce; + assert(!needle.empty, "Cannot count occurrences of an empty range"); - enforce(!needle.empty, "Cannot count occurrences of an empty range"); static if (isInfinite!R2) { //Note: This is the special case of looking for an infinite inside a finite... @@ -6446,7 +6710,7 @@ size_t count(alias pred = "true", R)(R haystack) is(typeof(unaryFun!pred(haystack.front)) : bool)) { size_t result; - alias ElementType!R T; //For narrow strings forces dchar iteration + alias T = ElementType!R; //For narrow strings forces dchar iteration foreach (T elem; haystack) if (unaryFun!pred(elem)) ++result; return result; @@ -6461,6 +6725,12 @@ unittest assert(count("日本語") == 3); } +// Issue 11253 +nothrow unittest +{ + assert([1, 2, 3].count([2, 3]) == 1); +} + // balancedParens /** Checks whether $(D r) has "balanced parentheses", i.e. all instances @@ -6752,6 +7022,7 @@ int cmp(alias pred = "a < b", R1, R2)(R1 r1, R2 r2) if (isSomeString!R1 && isSom } } +/// unittest { int result; @@ -6790,59 +7061,73 @@ unittest } // MinType -template MinType(T...) +private template MinType(T...) + if (T.length >= 1) { - static assert(T.length >= 2); - static if (T.length == 2) + static if (T.length == 1) + { + alias MinType = T[0]; + } + else static if (T.length == 2) { static if (!is(typeof(T[0].min))) - alias CommonType!(T[0 .. 2]) MinType; + alias MinType = CommonType!T; else { enum hasMostNegative = is(typeof(mostNegative!(T[0]))) && is(typeof(mostNegative!(T[1]))); static if (hasMostNegative && mostNegative!(T[1]) < mostNegative!(T[0])) - alias T[1] MinType; + alias MinType = T[1]; else static if (hasMostNegative && mostNegative!(T[1]) > mostNegative!(T[0])) - alias T[0] MinType; + alias MinType = T[0]; else static if (T[1].max < T[0].max) - alias T[1] MinType; + alias MinType = T[1]; else - alias T[0] MinType; + alias MinType = T[0]; } } else { - alias MinType!(MinType!(T[0 .. 2]), T[2 .. $]) MinType; + alias MinType = MinType!(MinType!(T[0 .. ($+1)/2]), MinType!(T[($+1)/2 .. $])); } } // min /** -Returns the minimum of the passed-in values. The type of the result is -computed by using $(XREF traits, CommonType). +Returns the minimum of the passed-in values. */ -MinType!(T1, T2, T) min(T1, T2, T...)(T1 a, T2 b, T xs) - if (is(typeof(a < b))) +MinType!T min(T...)(T args) + if (T.length >= 2) { - static if (T.length == 0) + //Get "a" + static if (T.length <= 2) + alias args[0] a; + else + auto a = min(args[0 .. ($+1)/2]); + alias typeof(a) T0; + + //Get "b" + static if (T.length <= 3) + alias args[$-1] b; + else + auto b = min(args[($+1)/2 .. $]); + alias typeof(b) T1; + + static assert (is(typeof(a < b)), + algoFormat("Invalid arguments: Cannot compare types %s and %s.", T0.stringof, T1.stringof)); + + //Do the "min" proper with a and b + static if (isIntegral!T0 && isIntegral!T1 && + (mostNegative!T0 < 0) != (mostNegative!T1 < 0)) { - static if (isIntegral!T1 && isIntegral!T2 && - (mostNegative!T1 < 0) != (mostNegative!T2 < 0)) - { - static if (mostNegative!T1 < 0) - immutable chooseB = b < a && a > 0; - else - immutable chooseB = b < a || b < 0; - } + static if (mostNegative!T0 < 0) + immutable chooseB = b < a && a > 0; else - immutable chooseB = b < a; - return cast(typeof(return)) (chooseB ? b : a); + immutable chooseB = b < a || b < 0; } else - { - return min(min(a, b), xs); - } + immutable chooseB = b < a; + return cast(typeof(return)) (chooseB ? b : a); } unittest @@ -6877,50 +7162,64 @@ unittest } // MaxType -template MaxType(T...) +private template MaxType(T...) + if (T.length >= 1) { - static assert(T.length >= 2); - static if (T.length == 2) + static if (T.length == 1) + { + alias MaxType = T[0]; + } + else static if (T.length == 2) { static if (!is(typeof(T[0].min))) - alias CommonType!(T[0 .. 2]) MaxType; + alias MaxType = CommonType!T; else static if (T[1].max > T[0].max) - alias T[1] MaxType; + alias MaxType = T[1]; else - alias T[0] MaxType; + alias MaxType = T[0]; } else { - alias MaxType!(MaxType!(T[0], T[1]), T[2 .. $]) MaxType; + alias MaxType = MaxType!(MaxType!(T[0 .. ($+1)/2]), MaxType!(T[($+1)/2 .. $])); } } // max /** -Returns the maximum of the passed-in values. The type of the result is -computed by using $(XREF traits, CommonType). +Returns the maximum of the passed-in values. */ -MaxType!(T1, T2, T) max(T1, T2, T...)(T1 a, T2 b, T xs) - if (is(typeof(a < b))) +MaxType!T max(T...)(T args) + if (T.length >= 2) { - static if (T.length == 0) + //Get "a" + static if (T.length <= 2) + alias args[0] a; + else + auto a = max(args[0 .. ($+1)/2]); + alias typeof(a) T0; + + //Get "b" + static if (T.length <= 3) + alias args[$-1] b; + else + auto b = max(args[($+1)/2 .. $]); + alias typeof(b) T1; + + static assert (is(typeof(a < b)), + algoFormat("Invalid arguments: Cannot compare types %s and %s.", T0.stringof, T1.stringof)); + + //Do the "max" proper with a and b + static if (isIntegral!T0 && isIntegral!T1 && + (mostNegative!T0 < 0) != (mostNegative!T1 < 0)) { - static if (isIntegral!T1 && isIntegral!T2 && - (mostNegative!T1 < 0) != (mostNegative!T2 < 0)) - { - static if (mostNegative!T1 < 0) - immutable chooseB = b > a || a < 0; - else - immutable chooseB = b > a && b > 0; - } + static if (mostNegative!T0 < 0) + immutable chooseB = b > a || a < 0; else - immutable chooseB = b > a; - return cast(typeof(return)) (chooseB ? b : a); + immutable chooseB = b > a && b > 0; } else - { - return max(max(a, b), xs); - } + immutable chooseB = b > a; + return cast(typeof(return)) (chooseB ? b : a); } /// @@ -6986,7 +7285,7 @@ minCount(alias pred = "a < b", Range)(Range range) alias RetType = Tuple!(T, size_t); static assert (is(typeof(RetType(range.front, 1))), - format("Error: Cannot call minCount on a %s, because it is not possible " + algoFormat("Error: Cannot call minCount on a %s, because it is not possible "~ "to copy the result value (a %s) into a Tuple.", Range.stringof, T.stringof)); enforce(!range.empty, "Can't count elements from an empty range"); @@ -7009,7 +7308,7 @@ minCount(alias pred = "a < b", Range)(Range range) } return RetType(least.front, occurrences); } - else static if (isAssignable!(UT, T) || (isAssignable!UT && !hasElaborateAssign!UT)) + else static if (isAssignable!(UT, T) || (!hasElaborateAssign!UT && isAssignable!UT)) { UT v = UT.init; static if (isAssignable!(UT, T)) v = range.front; @@ -7032,14 +7331,14 @@ minCount(alias pred = "a < b", Range)(Range range) } else static if (hasLvalueElements!Range) { - T* p = &(range.front()); + T* p = addressOf(range.front); for (range.popFront(); !range.empty; range.popFront()) { if (binaryFun!pred(*p, range.front)) continue; if (binaryFun!pred(range.front, *p)) { // change the min - p = &(range.front()); + p = addressOf(range.front); occurrences = 1; } else @@ -7049,7 +7348,7 @@ minCount(alias pred = "a < b", Range)(Range range) } else static assert(false, - format("Sorry, can't find the minCount of a %s: Don't know how " + algoFormat("Sorry, can't find the minCount of a %s: Don't know how "~ "to keep track of the smallest %s element.", Range.stringof, T.stringof)); } @@ -7097,10 +7396,8 @@ unittest static struct R(T) //input range { - T[] a; - bool empty() @property{return a.empty;} - ref T front() @property{return a.front;} - void popFront() {a.popFront();} + T[] arr; + alias arr this; } immutable a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ]; @@ -7135,7 +7432,14 @@ unittest static assert(!isAssignable!(S2, IS2)); static assert(!hasElaborateAssign!S2); - foreach (Type; TypeTuple!(S1, immutable(S1), S2, immutable(S2))) + static struct S3 + { + int i; + void opAssign(ref S3 other) @disable; + } + static assert(!isAssignable!S3); + + foreach (Type; TypeTuple!(S1, IS1, S2, IS2, S3)) { static if (is(Type == immutable)) alias V = immutable int; else alias V = int; @@ -7153,7 +7457,7 @@ unittest Returns the position of the minimum element of forward range $(D range), i.e. a subrange of $(D range) starting at the position of its smallest element and with the same ending as $(D range). The function -can actually be used for counting the maximum or any other ordering +can actually be used for finding the maximum or any other ordering predicate (that's why $(D maxPos) is not provided). */ Range minPos(alias pred = "a < b", Range)(Range range) @@ -7324,25 +7628,25 @@ struct Levenshtein(Range, alias equals, CostType = size_t) auto tt = t; foreach (j; 1 .. cols) { - auto cSub = _matrix[i - 1][j - 1] + auto cSub = matrix(i - 1,j - 1) + (equals(sfront, tt.front) ? 0 : _substitutionIncrement); tt.popFront(); - auto cIns = _matrix[i][j - 1] + _insertionIncrement; - auto cDel = _matrix[i - 1][j] + _deletionIncrement; + auto cIns = matrix(i,j - 1) + _insertionIncrement; + auto cDel = matrix(i - 1,j) + _deletionIncrement; switch (min_index(cSub, cIns, cDel)) { case 0: - _matrix[i][j] = cSub; + matrix(i,j) = cSub; break; case 1: - _matrix[i][j] = cIns; + matrix(i,j) = cIns; break; default: - _matrix[i][j] = cDel; + matrix(i,j) = cDel; break; } } } - return _matrix[slen][tlen]; + return matrix(slen,tlen); } EditOp[] path(Range s, Range t) @@ -7357,14 +7661,14 @@ struct Levenshtein(Range, alias equals, CostType = size_t) size_t i = rows - 1, j = cols - 1; // restore the path while (i || j) { - auto cIns = j == 0 ? CostType.max : _matrix[i][j - 1]; - auto cDel = i == 0 ? CostType.max : _matrix[i - 1][j]; + auto cIns = j == 0 ? CostType.max : matrix(i,j - 1); + auto cDel = i == 0 ? CostType.max : matrix(i - 1,j); auto cSub = i == 0 || j == 0 ? CostType.max - : _matrix[i - 1][j - 1]; + : matrix(i - 1,j - 1); switch (min_index(cSub, cIns, cDel)) { case 0: - result ~= _matrix[i - 1][j - 1] == _matrix[i][j] + result ~= matrix(i - 1,j - 1) == matrix(i,j) ? EditOp.none : EditOp.substitute; --i; @@ -7388,27 +7692,27 @@ private: CostType _deletionIncrement = 1, _insertionIncrement = 1, _substitutionIncrement = 1; - CostType[][] _matrix; + CostType[] _matrix; size_t rows, cols; + // Treat _matrix as a rectangular array + ref CostType matrix(size_t row, size_t col) { return _matrix[row * cols + col]; } + void AllocMatrix(size_t r, size_t c) { rows = r; cols = c; - if (_matrix.length < r || _matrix[0].length < c) { + if (_matrix.length < r * c) { delete _matrix; - _matrix = new CostType[][](r, c); + _matrix = new CostType[r * c]; InitMatrix(); } } void InitMatrix() { - foreach (i, row; _matrix) { - row[0] = i * _deletionIncrement; - } - if (!_matrix.length) return; - for (auto i = 0u; i != _matrix[0].length; ++i) { - _matrix[0][i] = i * _insertionIncrement; - } + foreach (r; 0 .. rows) + matrix(r,0) = r * _deletionIncrement; + foreach (c; 0 .. cols) + matrix(0,c) = c * _insertionIncrement; } static uint min_index(CostType i0, CostType i1, CostType i2) @@ -7430,6 +7734,8 @@ distance) between $(D s) and $(D t). The Levenshtein distance computes the minimal amount of edit operations necessary to transform $(D s) into $(D t). Performs $(BIGOH s.length * t.length) evaluations of $(D equals) and occupies $(BIGOH s.length * t.length) storage. + +Allocates GC memory. */ size_t levenshteinDistance(alias equals = "a == b", Range1, Range2) (Range1 s, Range2 t) @@ -7454,6 +7760,8 @@ unittest /** Returns the Levenshtein distance and the edit path between $(D s) and $(D t). + +Allocates GC memory. */ Tuple!(size_t, EditOp[]) levenshteinDistanceAndPath(alias equals = "a == b", Range1, Range2) @@ -7491,13 +7799,15 @@ unittest Copies the content of $(D source) into $(D target) and returns the remaining (unfilled) part of $(D target). +Preconditions: $(D target) shall have enough room to accomodate +$(D source). + See_Also: $(WEB sgi.com/tech/stl/_copy.html, STL's _copy) */ Range2 copy(Range1, Range2)(Range1 source, Range2 target) if (isInputRange!Range1 && isOutputRange!(Range2, ElementType!Range1)) { - static Range2 genericImpl(Range1 source, Range2 target) { // Specialize for 2 random access ranges. @@ -7506,6 +7816,9 @@ if (isInputRange!Range1 && isOutputRange!(Range2, ElementType!Range1)) static if (isRandomAccessRange!Range1 && hasLength!Range1 && hasSlicing!Range2 && isRandomAccessRange!Range2 && hasLength!Range2) { + assert(target.length >= source.length, + "Cannot copy a source range into a smaller target range."); + auto len = source.length; foreach (idx; 0 .. len) target[idx] = source[idx]; @@ -7521,8 +7834,6 @@ if (isInputRange!Range1 && isOutputRange!(Range2, ElementType!Range1)) static if (isArray!Range1 && isArray!Range2 && is(Unqual!(typeof(source[0])) == Unqual!(typeof(target[0])))) { - import std.exception : enforce; - immutable overlaps = source.ptr < target.ptr + target.length && target.ptr < source.ptr + source.length; @@ -7535,7 +7846,7 @@ if (isInputRange!Range1 && isOutputRange!(Range2, ElementType!Range1)) // Array specialization. This uses optimized memory copying // routines under the hood and is about 10-20x faster than the // generic implementation. - enforce(target.length >= source.length, + assert(target.length >= source.length, "Cannot copy a source array into a smaller target array."); target[0..source.length] = source[]; @@ -8216,23 +8527,25 @@ movement to be done which improves the execution time of the function. The function $(D remove) works on any forward range. The moving strategy is (listed from fastest to slowest): $(UL $(LI If $(D s == -SwapStrategy.unstable && isRandomAccessRange!Range && -hasLength!Range), then elements are moved from the end of the range -into the slots to be filled. In this case, the absolute minimum of -moves is performed.) $(LI Otherwise, if $(D s == -SwapStrategy.unstable && isBidirectionalRange!Range && -hasLength!Range), then elements are still moved from the end of the -range, but time is spent on advancing between slots by repeated calls -to $(D range.popFront).) $(LI Otherwise, elements are moved incrementally -towards the front of $(D range); a given element is never moved -several times, but more elements are moved than in the previous +SwapStrategy.unstable && isRandomAccessRange!Range && hasLength!Range +&& hasLvalueElements!Range), then elements are moved from the end +of the range into the slots to be filled. In this case, the absolute +minimum of moves is performed.) $(LI Otherwise, if $(D s == +SwapStrategy.unstable && isBidirectionalRange!Range && hasLength!Range +&& hasLvalueElements!Range), then elements are still moved from the +end of the range, but time is spent on advancing between slots by repeated +calls to $(D range.popFront).) $(LI Otherwise, elements are moved +incrementally towards the front of $(D range); a given element is never +moved several times, but more elements are moved than in the previous cases.)) */ Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) if (s != SwapStrategy.stable - && isBidirectionalRange!Range && hasLength!Range + && isBidirectionalRange!Range + && hasLvalueElements!Range + && hasLength!Range && Offset.length >= 1) { Tuple!(size_t, "pos", size_t, "len")[offset.length] blackouts; @@ -8268,7 +8581,7 @@ if (s != SwapStrategy.stable // Look for a blackout on the right if (blackouts[right].pos + blackouts[right].len >= range.length) { - range.popBackN(blackouts[right].len); + range.popBackExactly(blackouts[right].len); // Since right is unsigned, we must check for this case, otherwise // we might turn it into size_t.max and the loop condition will not @@ -8283,7 +8596,7 @@ if (s != SwapStrategy.stable } // Advance to next blackout on the left assert(blackouts[left].pos >= steps); - tgt.popFrontN(blackouts[left].pos - steps); + tgt.popFrontExactly(blackouts[left].pos - steps); steps = blackouts[left].pos; auto toMove = min( blackouts[left].len, @@ -8310,7 +8623,10 @@ if (s != SwapStrategy.stable Range remove (SwapStrategy s = SwapStrategy.stable, Range, Offset...) (Range range, Offset offset) -if (s == SwapStrategy.stable && isForwardRange!Range && Offset.length >= 1) +if (s == SwapStrategy.stable + && isBidirectionalRange!Range + && hasLvalueElements!Range + && Offset.length >= 1) { import std.exception : enforce; @@ -8339,14 +8655,14 @@ if (s == SwapStrategy.stable && isForwardRange!Range && Offset.length >= 1) } else { - src.popFrontN(from); - tgt.popFrontN(from); + src.popFrontExactly(from); + tgt.popFrontExactly(from); pos = from; } // now skip source to the "to" position - src.popFrontN(delta); + src.popFrontExactly(delta); + result.popBackExactly(delta); pos += delta; - foreach (j; 0 .. delta) result.popBack(); } // leftover move moveAll(src, tgt); @@ -8415,6 +8731,19 @@ unittest auto arr = [1,2,3]; arr = arr.remove!(SwapStrategy.unstable)(2); assert(arr == [1,2]); + +} + +unittest +{ + // Bug# 12889 + int[1][] arr = [[0], [1], [2], [3], [4], [5], [6]]; + auto orig = arr.dup; + foreach (i; iota(arr.length)) + { + assert(orig == arr.remove!(SwapStrategy.unstable)(tuple(i,i))); + assert(orig == arr.remove!(SwapStrategy.stable)(tuple(i,i))); + } } /** @@ -8427,7 +8756,8 @@ order is preserved. Returns the filtered range. */ Range remove(alias pred, SwapStrategy s = SwapStrategy.stable, Range) (Range range) -if (isBidirectionalRange!Range) +if (isBidirectionalRange!Range + && hasLvalueElements!Range) { auto result = range; static if (s != SwapStrategy.stable) @@ -8466,8 +8796,20 @@ if (isBidirectionalRange!Range) /// unittest { - int[] a = [ 1, 2, 3, 2, 3, 4, 5, 2, 5, 6 ]; - assert(remove!("a == 2")(a) == [ 1, 3, 3, 4, 5, 5, 6 ]); + static immutable base = [1, 2, 3, 2, 4, 2, 5, 2]; + + int[] arr = base[].dup; + + // using a string-based predicate + assert(remove!("a == 2")(arr) == [ 1, 3, 4, 5 ]); + + // The original array contents have been modified, + // so we need to reset it to its original state. + // The length is unmodified however. + arr[] = base[]; + + // using a lambda predicate + assert(remove!(a => a == 2)(arr) == [ 1, 3, 4, 5 ]); } unittest @@ -8502,7 +8844,7 @@ assert(arr == [ 1, 3, 5, 4, 5 ]); // alias move = .move, // Range)(Range r) // { -// alias Iterator!(Range) It; +// alias It = Iterator!(Range); // static void assignIter(It a, It b) { move(*b, *a); } // return range(begin(r), partitionold!(not!(pred), ss, assignIter, Range)(r)); // } @@ -8532,7 +8874,7 @@ assert(arr == [ 1, 3, 4, 5, 4, 5, 2 ]); // SwapStrategy ss = SwapStrategy.semistable, // Range, Value)(Range r, Value v) // { -// alias Iterator!(Range) It; +// alias It = Iterator!(Range); // bool comp(typeof(*It) a) { return !binaryFun!(pred)(a, v); } // static void assignIterB(It a, It b) { *a = *b; } // return range(begin(r), @@ -8582,7 +8924,7 @@ Range partition(alias predicate, if ((ss == SwapStrategy.stable && isRandomAccessRange!(Range)) || (ss != SwapStrategy.stable && isForwardRange!(Range))) { - alias unaryFun!(predicate) pred; + alias pred = unaryFun!(predicate); if (r.empty) return r; static if (ss == SwapStrategy.stable) { @@ -8592,7 +8934,7 @@ Range partition(alias predicate, return r; } const middle = r.length / 2; - alias .partition!(pred, ss, Range) recurse; + alias recurse = .partition!(pred, ss, Range); auto lower = recurse(r[0 .. middle]); auto upper = recurse(r[middle .. $]); bringToFront(lower, r[middle .. r.length - upper.length]); @@ -8756,7 +9098,7 @@ if (ss == SwapStrategy.unstable && isRandomAccessRange!Range // The algorithm is described in "Engineering a sort function" by // Jon Bentley et al, pp 1257. - alias binaryFun!less lessFun; + alias lessFun = binaryFun!less; size_t i, j, k = r.length, l = k; bigloop: @@ -9053,32 +9395,27 @@ sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Stable sorting uses TimSort, which needs to copy elements into a buffer, requiring assignable elements. +/ { - alias binaryFun!(less) lessFun; - alias typeof(lessFun(r.front, r.front)) LessRet; // instantiate lessFun + alias lessFun = binaryFun!(less); + alias LessRet = typeof(lessFun(r.front, r.front)); // instantiate lessFun static if (is(LessRet == bool)) { - import std.conv : text; - static if (ss == SwapStrategy.unstable) - quickSortImpl!(lessFun)(r, cast(real)r.length); + quickSortImpl!(lessFun)(r, r.length); else //use Tim Sort for semistable & stable TimSortImpl!(lessFun, Range).sort(r, null); enum maxLen = 8; - assert(isSorted!lessFun(r), text("Failed to sort range of type ", - Range.stringof, ". Actual result is: ", - r[0 .. r.length > maxLen ? maxLen : r.length ], - r.length > maxLen ? "..." : "")); + assert(isSorted!lessFun(r), "Failed to sort range of type " ~ Range.stringof); } else { - static assert(false, "Invalid predicate passed to sort: "~less); + static assert(false, "Invalid predicate passed to sort: " ~ less.stringof); } return assumeSorted!less(r); } /// -unittest +@safe pure nothrow unittest { int[] array = [ 1, 2, 3, 4 ]; // sort in descending order @@ -9088,9 +9425,13 @@ unittest sort(array); assert(array == [ 1, 2, 3, 4 ]); // sort with a delegate - bool myComp(int x, int y) { return x > y; } + bool myComp(int x, int y) @safe pure nothrow { return x > y; } sort!(myComp)(array); assert(array == [ 4, 3, 2, 1 ]); +} +/// +unittest +{ // Showcase stable sorting string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ]; sort!("toUpper(a) < toUpper(b)", SwapStrategy.stable)(words); @@ -9175,6 +9516,24 @@ unittest sort!(pred, SwapStrategy.unstable)(arr); assert(comp < 25_000); } + + { + bool proxySwapCalled; + struct S + { + int i; + alias i this; + void proxySwap(ref S other) { swap(i, other.i); proxySwapCalled = true; } + @disable void opAssign(S value); + } + + alias R = S[]; + R r = [S(3), S(2), S(1)]; + static assert(hasSwappableElements!R); + static assert(!hasAssignableElements!R); + r.sort(); + assert(proxySwapCalled); + } } private template validPredicates(E, less...) { @@ -9207,14 +9566,14 @@ template multiSort(less...) //if (less.length > 1) static if (is(typeof(less[$ - 1]) == SwapStrategy)) { enum ss = less[$ - 1]; - alias less[0 .. $ - 1] funs; + alias funs = less[0 .. $ - 1]; } else { - alias SwapStrategy.unstable ss; - alias less funs; + alias ss = SwapStrategy.unstable; + alias funs = less; } - alias binaryFun!(funs[0]) lessFun; + alias lessFun = binaryFun!(funs[0]); static if (funs.length > 1) { @@ -9297,7 +9656,7 @@ private size_t getPivot(alias less, Range)(Range r) // then returns the index of the middle element. In effect, it uses the // median-of-three heuristic. - alias binaryFun!(less) pred; + alias pred = binaryFun!(less); immutable len = r.length; immutable size_t mid = len / 2; immutable uint result = ((cast(uint) (pred(r[0], r[mid]))) << 2) | @@ -9333,7 +9692,7 @@ private size_t getPivot(alias less, Range)(Range r) private void optimisticInsertionSort(alias less, Range)(Range r) { - alias binaryFun!(less) pred; + alias pred = binaryFun!(less); if (r.length < 2) { return; @@ -9399,26 +9758,26 @@ void swapAt(R)(R r, size_t i1, size_t i2) } } -private void quickSortImpl(alias less, Range)(Range r, real depth) +private void quickSortImpl(alias less, Range)(Range r, size_t depth) { - alias ElementType!(Range) Elem; + alias Elem = ElementType!(Range); enum size_t optimisticInsertionSortGetsBetter = 25; static assert(optimisticInsertionSortGetsBetter >= 1); // partition while (r.length > optimisticInsertionSortGetsBetter) { - if(depth < 1.0) + if (depth == 0) { HeapSortImpl!(less, Range).heapSort(r); return; } - depth *= (2.0/3.0); + depth = depth >= depth.max / 2 ? (depth / 3) * 2 : (depth * 2) / 3; const pivotIdx = getPivot!(less)(r); auto pivot = r[pivotIdx]; - alias binaryFun!(less) pred; + alias pred = binaryFun!(less); // partition swapAt(r, pivotIdx, r.length - 1); @@ -9459,11 +9818,12 @@ private template HeapSortImpl(alias less, Range) { static assert(isRandomAccessRange!Range); static assert(hasLength!Range); - static assert(hasAssignableElements!Range); + static assert(hasSwappableElements!Range || hasAssignableElements!Range); - alias binaryFun!less lessFun; + alias lessFun = binaryFun!less; - void heapSort(Range r) + //template because of @@@12410@@@ + void heapSort()(Range r) { // If true, there is nothing to do if(r.length < 2) return; @@ -9482,7 +9842,8 @@ private template HeapSortImpl(alias less, Range) } } - void sift(Range r, size_t parent, immutable size_t end) + //template because of @@@12410@@@ + void sift()(Range r, size_t parent, immutable size_t end) { immutable root = parent; size_t child = void; @@ -9526,9 +9887,9 @@ private template TimSortImpl(alias pred, R) static assert(hasSlicing!R); static assert(hasAssignableElements!R); - alias ElementType!R T; + alias T = ElementType!R; - alias binaryFun!pred less; + alias less = binaryFun!pred; bool greater(T a, T b){ return less(b, a); } bool greaterEqual(T a, T b){ return !less(a, b); } bool lessEqual(T a, T b){ return !less(b, a); } @@ -9951,12 +10312,12 @@ private template TimSortImpl(alias pred, R) body { size_t lower = 0, center = 1, upper = range.length; - alias center gap; + alias gap = center; static if (forwardReverse) { - static if (!lowerUpper) alias lessEqual comp; // reverse lower - static if (lowerUpper) alias less comp; // reverse upper + static if (!lowerUpper) alias comp = lessEqual; // reverse lower + static if (lowerUpper) alias comp = less; // reverse upper // Gallop Search Reverse while (gap <= upper) @@ -9983,8 +10344,8 @@ private template TimSortImpl(alias pred, R) } else { - static if (!lowerUpper) alias greater comp; // forward lower - static if (lowerUpper) alias greaterEqual comp; // forward upper + static if (!lowerUpper) alias comp = greater; // forward lower + static if (lowerUpper) alias comp = greaterEqual; // forward upper // Gallop Search Forward while (lower + gap < upper) @@ -10014,10 +10375,10 @@ private template TimSortImpl(alias pred, R) } } - alias gallopSearch!(false, false) gallopForwardLower; - alias gallopSearch!(false, true) gallopForwardUpper; - alias gallopSearch!(true, false) gallopReverseLower; - alias gallopSearch!(true, true) gallopReverseUpper; + alias gallopForwardLower = gallopSearch!(false, false); + alias gallopForwardUpper = gallopSearch!(false, true); + alias gallopReverseLower = gallopSearch!( true, false); + alias gallopReverseUpper = gallopSearch!( true, true); } unittest @@ -10337,8 +10698,6 @@ less). */ bool isSorted(alias less = "a < b", Range)(Range r) if (isForwardRange!(Range)) { - import std.conv : text; - if (r.empty) return true; static if (isRandomAccessRange!Range && hasLength!Range) @@ -10349,10 +10708,8 @@ bool isSorted(alias less = "a < b", Range)(Range r) if (isForwardRange!(Range)) if (!binaryFun!less(r[i + 1], r[i])) continue; assert( !binaryFun!less(r[i], r[i + 1]), - text("Predicate for isSorted is not antisymmetric. Both" - " pred(a, b) and pred(b, a) are true for a=", r[i], - " and b=", r[i+1], " in positions ", i, " and ", - i + 1)); + "Predicate for isSorted is not antisymmetric. Both" ~ + " pred(a, b) and pred(b, a) are true for certain values."); return false; } } @@ -10368,10 +10725,8 @@ bool isSorted(alias less = "a < b", Range)(Range r) if (isForwardRange!(Range)) // Check for antisymmetric predicate assert( !binaryFun!less(r.front, ahead.front), - text("Predicate for isSorted is not antisymmetric. Both" - " pred(a, b) and pred(b, a) are true for a=", r.front, - " and b=", ahead.front, " in positions ", i, " and ", - i + 1)); + "Predicate for isSorted is not antisymmetric. Both" ~ + " pred(a, b) and pred(b, a) are true for certain values."); return false; } } @@ -10478,7 +10833,7 @@ if (isRandomAccessRange!Range && !isInfinite!Range && import std.exception : enforce; import std.conv : to; - alias Unqual!(ElementType!RangeIndex) IndexType; + alias IndexType = Unqual!(ElementType!RangeIndex); enforce(r.length == index.length, "r and index must be same length for makeIndex."); static if (IndexType.sizeof < size_t.sizeof) @@ -10521,8 +10876,8 @@ unittest immutable(int)[] arr = [ 2, 3, 1, 5, 0 ]; // index using pointers auto index1 = new immutable(int)*[arr.length]; - alias typeof(arr) ImmRange; - alias typeof(index1) ImmIndex; + alias ImmRange = typeof(arr); + alias ImmIndex = typeof(index1); static assert(isForwardRange!(ImmRange)); static assert(isRandomAccessRange!(ImmIndex)); static assert(!isIntegral!(ElementType!(ImmIndex))); @@ -10641,11 +10996,11 @@ unittest SwapStrategy ss, SRange, TRange)(SRange source, TRange target) { - alias binaryFun!(less) lessFun; + alias lessFun = binaryFun!(less); static assert(ss == SwapStrategy.unstable, "Stable indexing not yet implemented"); - alias Iterator!(SRange) SIter; - alias std.iterator.ElementType!(TRange) TElem; + alias SIter = Iterator!(SRange); + alias TElem = std.iterator.ElementType!(TRange); enum usingInt = isIntegral!(TElem); static if (usingInt) @@ -10853,7 +11208,7 @@ unittest // SwapStrategy ss = SwapStrategy.unstable, // Range)(Range r) // { -// alias Iterator!(Range) Iter; +// alias Iter = Iterator!(Range); // auto result = new Iter[r.length]; // // assume collection already ordered // size_t i = 0; @@ -10862,7 +11217,7 @@ unittest // result[i++] = it; // } // // sort the index -// alias typeof(transform(*result[0])) Transformed; +// alias Transformed = typeof(transform(*result[0])); // static bool indirectLess(Transformed a, Transformed b) // { // return less(a, b); @@ -10906,14 +11261,14 @@ unittest // canFind /++ Convenience function. Like find, but only returns whether or not the search -was succesful. +was successful. +/ template canFind(alias pred="a == b") { /++ Returns $(D true) if and only if any value $(D v) found in the input range $(D range) satisfies the predicate $(D pred). - Performs (at most) $(BIGOH r.length) evaluations of $(D pred). + Performs (at most) $(BIGOH haystack.length) evaluations of $(D pred). +/ bool canFind(Range)(Range haystack) if (is(typeof(find!pred(haystack)))) @@ -10922,8 +11277,8 @@ template canFind(alias pred="a == b") } /++ - Returns $(D true) if and only if $(D value) can be found in $(D - range). Performs $(BIGOH needle.length) evaluations of $(D pred). + Returns $(D true) if and only if $(D needle) can be found in $(D + range). Performs $(BIGOH haystack.length) evaluations of $(D pred). +/ bool canFind(Range, Element)(Range haystack, Element needle) if (is(typeof(find!pred(haystack, needle)))) @@ -10951,17 +11306,9 @@ template canFind(alias pred="a == b") } } +/// unittest { - debug(std_algorithm) scope(success) - writeln("unittest @", __FILE__, ":", __LINE__, " done."); - auto a = rndstuff!(int)(); - if (a.length) - { - auto b = a[a.length / 2]; - assert(canFind(a, b)); - } - assert(canFind([0, 1, 2, 3], 2) == true); assert(canFind([0, 1, 2, 3], [1, 2], [2, 3])); assert(canFind([0, 1, 2, 3], [1, 2], [2, 3]) == 1); @@ -10973,6 +11320,18 @@ unittest assert(canFind([0, 1, 2, 3], [1, 3], [2, 4]) == 0); } +unittest +{ + debug(std_algorithm) scope(success) + writeln("unittest @", __FILE__, ":", __LINE__, " done."); + auto a = rndstuff!(int)(); + if (a.length) + { + auto b = a[a.length / 2]; + assert(canFind(a, b)); + } +} + unittest { assert(equal!(canFind!"a < b")([[1, 2, 3], [7, 8, 9]], [2, 8])); @@ -10980,7 +11339,7 @@ unittest /++ Checks if $(I _any) of the elements verifies $(D pred). -$(D !any) can be used to verify that $(I none) of the elemnets verify +$(D !any) can be used to verify that $(I none) of the elements verify $(D pred). +/ template any(alias pred = "a") @@ -10988,7 +11347,7 @@ template any(alias pred = "a") /++ Returns $(D true) if and only if $(I _any) value $(D v) found in the input range $(D range) satisfies the predicate $(D pred). - Performs (at most) $(BIGOH r.length) evaluations of $(D pred). + Performs (at most) $(BIGOH range.length) evaluations of $(D pred). +/ bool any(Range)(Range range) if (isInputRange!Range && is(typeof(unaryFun!pred(range.front)))) @@ -11041,7 +11400,7 @@ template all(alias pred = "a") /++ Returns $(D true) if and only if $(I _all) values $(D v) found in the input range $(D range) satisfy the predicate $(D pred). - Performs (at most) $(BIGOH r.length) evaluations of $(D pred). + Performs (at most) $(BIGOH range.length) evaluations of $(D pred). +/ bool all(Range)(Range range) if (isInputRange!Range && is(typeof(unaryFun!pred(range.front)))) @@ -11144,7 +11503,7 @@ struct SetUnion(alias less = "a < b", Rs...) if (allSatisfy!(isInputRange, Rs)) { private: Rs _r; - alias binaryFun!(less) comp; + alias comp = binaryFun!(less); uint _crt; void adjustPosition(uint candidate = 0)() @@ -11177,7 +11536,7 @@ private: } public: - alias CommonType!(staticMap!(.ElementType, Rs)) ElementType; + alias ElementType = CommonType!(staticMap!(.ElementType, Rs)); this(Rs rs) { @@ -11244,7 +11603,7 @@ public: return result; } - alias length opDollar; + alias opDollar = length; } } @@ -11419,7 +11778,7 @@ struct SetDifference(alias less = "a < b", R1, R2) private: R1 r1; R2 r2; - alias binaryFun!(less) comp; + alias comp = binaryFun!(less); void adjustPosition() { @@ -11504,7 +11863,7 @@ private: R1 r1; R2 r2; //bool usingR2; - alias binaryFun!(less) comp; + alias comp = binaryFun!(less); void adjustPosition() { @@ -11717,8 +12076,8 @@ struct NWayUnion(alias less, RangeOfRanges) { import std.container : BinaryHeap; - private alias .ElementType!(.ElementType!RangeOfRanges) ElementType; - private alias binaryFun!less comp; + private alias ElementType = .ElementType!(.ElementType!RangeOfRanges); + private alias comp = binaryFun!less; private RangeOfRanges _ror; static bool compFront(.ElementType!RangeOfRanges a, .ElementType!RangeOfRanges b) @@ -11815,7 +12174,7 @@ $(D 7.0) is the correct answer because it occurs in $(D 4) out of the $(D 5) inputs, more than any other number. The second member of the resulting tuple is indeed $(D 4) (recording the number of occurrences of $(D 7.0)). If more of the top-frequent numbers are needed, just -create a larger $(D tgt) range. In the axample above, creating $(D b) +create a larger $(D tgt) range. In the example above, creating $(D b) with length $(D 2) yields $(D tuple(1.0, 3u)) in the second position. The function $(D largestPartialIntersection) is useful for @@ -11881,7 +12240,7 @@ void largestPartialIntersectionWeighted (RangeOfRanges ror, Range tgt, WeightsAA weights, SortOutput sorted = SortOutput.no) { if (tgt.empty) return; - alias ElementType!Range InfoType; + alias InfoType = ElementType!Range; bool heapComp(InfoType a, InfoType b) { return weights[a[0]] * a[1] > weights[b[0]] * b[1]; @@ -11958,7 +12317,7 @@ unittest { import std.container : Array; - alias Tuple!(uint, uint) T; + alias T = Tuple!(uint, uint); const Array!T arrayOne = Array!T( [ T(1,2), T(3,4) ] ); const Array!T arrayTwo = Array!T([ T(1,2), T(3,4) ] ); @@ -12622,14 +12981,12 @@ unittest /// ditto auto cartesianProduct(R1, R2, RR...)(R1 range1, R2 range2, RR otherRanges) { - import std.string : format; - /* We implement the n-ary cartesian product by recursively invoking the * binary cartesian product. To make the resulting range nicer, we denest * one level of tuples so that a ternary cartesian product, for example, * returns 3-element tuples instead of nested 2-element tuples. */ - enum string denest = format("tuple(a[0], %(a[1][%d]%|,%))", + enum string denest = algoFormat("tuple(a[0], %(a[1][%d]%|,%))", iota(0, otherRanges.length+1)); return map!denest( cartesianProduct(range1, cartesianProduct(range2, otherRanges)) diff --git a/libphobos/src/std/array.d b/libphobos/src/std/array.d index 3e8a7a4f4..7d1fb871e 100644 --- a/libphobos/src/std/array.d +++ b/libphobos/src/std/array.d @@ -28,7 +28,16 @@ a special case in an overload. ForeachType!Range[] array(Range)(Range r) if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range) { - alias ForeachType!Range E; + if (__ctfe) + { + // Compile-time version to avoid memcpy calls. + typeof(return) result; + foreach (e; r) + result ~= e; + return result; + } + + alias E = ForeachType!Range; static if (hasLength!Range) { if(r.length == 0) return null; @@ -40,13 +49,9 @@ if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range) auto result = trustedAllocateArray(r.length); size_t i; - static auto trustedGetAddr(T)(ref T t) @trusted nothrow pure - { - return &t; - } foreach (e; r) { - emplace(trustedGetAddr(result[i]), e); + emplaceRef!E(result[i], e); ++i; } return cast(E[])result; @@ -97,6 +102,20 @@ if (isIterable!Range && !isNarrowString!Range && !isInfinite!Range) assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)])); } +unittest +{ + // Issue 12315 + static struct Bug12315 { immutable int i; } + enum bug12315 = [Bug12315(123456789)].array(); + static assert(bug12315[0].i == 123456789); +} + +unittest +{ + static struct S{int* p;} + auto a = array(immutable(S).init.repeat(5)); +} + /** Convert a narrow string to an array type that fully supports random access. This is handled as a special case and always returns a $(D dchar[]), @@ -243,8 +262,8 @@ auto assocArray(Range)(Range r) if (isInputRange!Range && isTuple!(ElementType!Range) && ElementType!Range.length == 2) { - alias ElementType!Range.Types[0] KeyType; - alias ElementType!Range.Types[1] ValueType; + alias KeyType = ElementType!Range.Types[0]; + alias ValueType = ElementType!Range.Types[1]; ValueType[KeyType] aa; foreach (t; r) aa[t[0]] = t[1]; @@ -306,21 +325,32 @@ version(unittest) static assert(nDimensions!(float[][]) == 2); } -/** +/++ Returns a new array of type $(D T) allocated on the garbage collected heap without initializing its elements. This can be a useful optimization if every element will be immediately initialized. $(D T) may be a multidimensional -array. In this case sizes may be specified for any number of dimensions from 1 +array. In this case sizes may be specified for any number of dimensions from 0 to the number in $(D T). -*/ -auto uninitializedArray(T, I...)(I sizes) -if(allSatisfy!(isIntegral, I)) + +uninitializedArray is nothrow and weakly pure. ++/ +auto uninitializedArray(T, I...)(I sizes) nothrow @trusted +if (isDynamicArray!T && allSatisfy!(isIntegral, I)) { - return arrayAllocImpl!(false, T, I)(sizes); -} + enum isSize_t(E) = is (E : size_t); + alias toSize_t(E) = size_t; + static assert(allSatisfy!(isSize_t, I), + format("Argument types in %s are not all convertible to size_t: %s", + I.stringof, Filter!(templateNot!(isSize_t), I).stringof)); + + //Eagerlly transform non-size_t into size_t to avoid template bloat + alias ST = staticMap!(toSize_t, I); + + return arrayAllocImpl!(false, T, ST)(sizes); +} /// -unittest +nothrow pure unittest { double[] arr = uninitializedArray!(double[])(100); assert(arr.length == 100); @@ -330,19 +360,34 @@ unittest assert(matrix[0].length == 31); } -/** +/++ Returns a new array of type $(D T) allocated on the garbage collected heap. -Initialization is guaranteed only for pointers, references and slices, -for preservation of memory safety. -*/ -auto minimallyInitializedArray(T, I...)(I sizes) @trusted -if(allSatisfy!(isIntegral, I)) + +Partial initialization is done for types with indirections, for preservation +of memory safety. Note that elements will only be initialized to 0, but not +necessarily the element type's $(D .init). + +minimallyInitializedArray is nothrow and weakly pure. ++/ +auto minimallyInitializedArray(T, I...)(I sizes) nothrow @trusted +if (isDynamicArray!T && allSatisfy!(isIntegral, I)) { - return arrayAllocImpl!(true, T, I)(sizes); + enum isSize_t(E) = is (E : size_t); + alias toSize_t(E) = size_t; + + static assert(allSatisfy!(isSize_t, I), + format("Argument types in %s are not all convertible to size_t: %s", + I.stringof, Filter!(templateNot!(isSize_t), I).stringof)); + + //Eagerlly transform non-size_t into size_t to avoid template bloat + alias ST = staticMap!(toSize_t, I); + + return arrayAllocImpl!(true, T, ST)(sizes); } -@safe unittest +@safe pure nothrow unittest { + cast(void)minimallyInitializedArray!(int[][][][][])(); double[] arr = minimallyInitializedArray!(double[])(100); assert(arr.length == 100); @@ -354,50 +399,130 @@ if(allSatisfy!(isIntegral, I)) } } -private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) -if(allSatisfy!(isIntegral, I)) +private auto arrayAllocImpl(bool minimallyInitialized, T, I...)(I sizes) nothrow { - static assert(sizes.length >= 1, - "Cannot allocate an array without the size of at least the first " ~ - " dimension."); - static assert(sizes.length <= nDimensions!T, - to!string(sizes.length) ~ " dimensions specified for a " ~ - to!string(nDimensions!T) ~ " dimensional array."); + static assert(I.length <= nDimensions!T, + format("%s dimensions specified for a %s dimensional array.", I.length, nDimensions!T)); + + alias E = ElementEncodingType!T; - alias typeof(T.init[0]) E; + E[] ret; - auto ptr = (__ctfe) ? + static if (I.length != 0) + { + static assert (is(I[0] == size_t)); + alias size = sizes[0]; + } + + static if (I.length == 1) + { + if (__ctfe) { - static if(__traits(compiles, new E[1])) + static if (__traits(compiles, new E[](size))) + ret = new E[](size); + else static if (__traits(compiles, ret ~= E.init)) { - return (new E[sizes[0]]).ptr; + try + { + //Issue: if E has an impure postblit, then all of arrayAllocImpl + //Will be impure, even during non CTFE. + foreach (i; 0 .. size) + ret ~= E.init; + } + catch (Exception e) + throw new Error(e.msg); } else - { - E[] arr; - foreach (i; 0 .. sizes[0]) - arr ~= E.init; - return arr.ptr; - } - }() : - cast(E*) GC.malloc(sizes[0] * E.sizeof, blockAttribute!(E)); - auto ret = ptr[0..sizes[0]]; - - static if(sizes.length > 1) - { - foreach(ref elem; ret) + assert(0, "No postblit nor default init on " ~ E.stringof ~ + ": At least one is required for CTFE."); + } + else { - elem = uninitializedArray!(E)(sizes[1..$]); + import core.stdc.string : memset; + auto ptr = cast(E*) GC.malloc(sizes[0] * E.sizeof, blockAttribute!E); + static if (minimallyInitialized && hasIndirections!E) + memset(ptr, 0, size * E.sizeof); + ret = ptr[0 .. size]; } } - else static if(minimallyInitialized && hasIndirections!E) + else static if (I.length > 1) { - ret[] = E.init; + ret = arrayAllocImpl!(false, E[])(size); + foreach(ref elem; ret) + elem = arrayAllocImpl!(minimallyInitialized, E)(sizes[1..$]); } return ret; } +nothrow pure unittest +{ + auto s1 = uninitializedArray!(int[])(); + auto s2 = minimallyInitializedArray!(int[])(); + assert(s1.length == 0); + assert(s2.length == 0); +} + +nothrow pure unittest //@@@9803@@@ +{ + auto a = minimallyInitializedArray!(int*[])(1); + assert(a[0] == null); + auto b = minimallyInitializedArray!(int[][])(1); + assert(b[0].empty); + auto c = minimallyInitializedArray!(int*[][])(1, 1); + assert(c[0][0] == null); +} + +unittest //@@@10637@@@ +{ + static struct S + { + static struct I{int i; alias i this;} + int* p; + this() @disable; + this(int i) + { + p = &(new I(i)).i; + } + this(this) + { + p = &(new I(*p)).i; + } + ~this() + { + assert(p != null); + } + } + auto a = minimallyInitializedArray!(S[])(1); + assert(a[0].p == null); + enum b = minimallyInitializedArray!(S[])(1); +} + +nothrow unittest +{ + static struct S1 + { + this() @disable; + this(this) @disable; + } + auto a1 = minimallyInitializedArray!(S1[][])(2, 2); + //enum b1 = minimallyInitializedArray!(S1[][])(2, 2); + static struct S2 + { + this() @disable; + //this(this) @disable; + } + auto a2 = minimallyInitializedArray!(S2[][])(2, 2); + enum b2 = minimallyInitializedArray!(S2[][])(2, 2); + static struct S3 + { + //this() @disable; + this(this) @disable; + } + auto a3 = minimallyInitializedArray!(S3[][])(2, 2); + enum b3 = minimallyInitializedArray!(S3[][])(2, 2); +} + /** Implements the range interface primitive $(D empty) for built-in arrays. Due to the fact that nonmember functions can be called with @@ -443,7 +568,7 @@ Implements the range interface primitive $(D popFront) for built-in arrays. Due to the fact that nonmember functions can be called with the first argument using the dot notation, $(D array.popFront) is equivalent to $(D popFront(array)). For $(GLOSSARY narrow strings), -$(D popFront) automaticaly advances to the next $(GLOSSARY code +$(D popFront) automatically advances to the next $(GLOSSARY code point). */ @@ -541,7 +666,7 @@ Implements the range interface primitive $(D popBack) for built-in arrays. Due to the fact that nonmember functions can be called with the first argument using the dot notation, $(D array.popBack) is equivalent to $(D popBack(array)). For $(GLOSSARY narrow strings), $(D -popFront) automaticaly eliminates the last $(GLOSSARY code point). +popFront) automatically eliminates the last $(GLOSSARY code point). */ void popBack(T)(ref T[] a) @safe pure nothrow @@ -605,7 +730,7 @@ Implements the range interface primitive $(D front) for built-in arrays. Due to the fact that nonmember functions can be called with the first argument using the dot notation, $(D array.front) is equivalent to $(D front(array)). For $(GLOSSARY narrow strings), $(D -front) automaticaly returns the first $(GLOSSARY code point) as a $(D +front) automatically returns the first $(GLOSSARY code point) as a $(D dchar). */ @property ref T front(T)(T[] a) @safe pure nothrow @@ -648,7 +773,7 @@ Implements the range interface primitive $(D back) for built-in arrays. Due to the fact that nonmember functions can be called with the first argument using the dot notation, $(D array.back) is equivalent to $(D back(array)). For $(GLOSSARY narrow strings), $(D -back) automaticaly returns the last $(GLOSSARY code point) as a $(D +back) automatically returns the last $(GLOSSARY code point) as a $(D dchar). */ @property ref T back(T)(T[] a) @safe pure nothrow if (!isNarrowString!(T[])) @@ -693,7 +818,7 @@ slice, returns that slice. Otherwise, returns the null slice. */ inout(T)[] overlap(T)(inout(T)[] r1, inout(T)[] r2) @trusted pure nothrow { - alias inout(T) U; + alias U = inout(T); static U* max(U* a, U* b) nothrow { return a > b ? a : b; } static U* min(U* a, U* b) nothrow { return a < b ? a : b; } @@ -910,19 +1035,6 @@ void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff) static if(allSatisfy!(isInputRangeWithLengthOrConvertible!T, U)) { import core.stdc.string; - void assign(E)(ref T dest, ref E src) - { - static if (is(typeof(dest.opAssign(src))) || - !is(typeof(dest = src))) - { - // this should be in-place construction - emplace(&dest, src); - } - else - { - dest = src; - } - } auto trustedAllocateArray(size_t n) @trusted nothrow { return uninitializedArray!(T[])(n); @@ -953,13 +1065,13 @@ void insertInPlace(T, U...)(ref T[] array, size_t pos, U stuff) { static if (is(E : T)) //ditto { - assign(tmp[j++], stuff[i]); + emplaceRef!T(tmp[j++], stuff[i]); } else { foreach (v; stuff[i]) { - assign(tmp[j++], v); + emplaceRef!T(tmp[j++], v); } } } @@ -1303,7 +1415,7 @@ returns a new array. For a lazy version, refer to $(XREF range, repeat). */ ElementEncodingType!S[] replicate(S)(S s, size_t n) if (isDynamicArray!S) { - alias ElementEncodingType!S[] RetType; + alias RetType = ElementEncodingType!S[]; // Optimization for return join(std.range.repeat(s, n)); if (n == 0) @@ -1525,12 +1637,12 @@ unittest +/ ElementEncodingType!(ElementType!RoR)[] join(RoR, R)(RoR ror, R sep) if(isInputRange!RoR && - isInputRange!(ElementType!RoR) && + isInputRange!(Unqual!(ElementType!RoR)) && isInputRange!R && is(Unqual!(ElementType!(ElementType!RoR)) == Unqual!(ElementType!R))) { - alias ElementType!RoR RoRElem; - alias typeof(return) RetType; + alias RoRElem = ElementType!RoR; + alias RetType = typeof(return); if (ror.empty) return RetType.init; @@ -1544,7 +1656,7 @@ ElementEncodingType!(ElementType!RoR)[] join(RoR, R)(RoR ror, R sep) else static if (!isArray!R) auto sepArr = array(sep); else - alias sep sepArr; + alias sepArr = sep; auto result = appender!RetType(); static if(isForwardRange!RoR && @@ -1572,14 +1684,14 @@ ElementEncodingType!(ElementType!RoR)[] join(RoR, R)(RoR ror, R sep) /// Ditto ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror) if(isInputRange!RoR && - isInputRange!(ElementType!RoR)) + isInputRange!(Unqual!(ElementType!RoR))) { - alias typeof(return) RetType; + alias RetType = typeof(return); if (ror.empty) return RetType.init; - alias ElementType!RoR R; + alias R = ElementType!RoR; auto result = appender!RetType(); static if(isForwardRange!RoR && (hasLength!R || isNarrowString!R)) { @@ -1601,6 +1713,10 @@ ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror) assert(join([[1, 2, 3], [4, 5]], [72, 73]) == [1, 2, 3, 72, 73, 4, 5]); assert(join([[1, 2, 3], [4, 5]]) == [1, 2, 3, 4, 5]); + + const string[] arr = ["apple", "banana"]; + assert(arr.join(",") == "apple,banana"); + assert(arr.join() == "applebanana"); } unittest @@ -1692,7 +1808,7 @@ unittest assert(join([[1, 2], [41, 42]]) == [1, 2, 41, 42]); assert(join(cast(int[][])[]).empty); - alias filter!"true" f; + alias f = filter!"true"; assert(join([[1, 2], [41, 42]], [5, 6]) == [1, 2, 5, 6, 41, 42]); assert(join(f([[1, 2], [41, 42]]), [5, 6]) == [1, 2, 5, 6, 41, 42]); assert(join([f([1, 2]), f([41, 42])], [5, 6]) == [1, 2, 5, 6, 41, 42]); @@ -1729,7 +1845,7 @@ if (isDynamicArray!(E[]) && isForwardRange!R1 && isForwardRange!R2 /++ Same as above, but outputs the result via OutputRange $(D sink). - If no match is found the original array is transfered to $(D sink) as is. + If no match is found the original array is transferred to $(D sink) as is. +/ void replaceInto(E, Sink, R1, R2)(Sink sink, E[] subject, R1 from, R2 to) if (isOutputRange!(Sink, E) && isDynamicArray!(E[]) @@ -1792,7 +1908,7 @@ unittest } foreach (S; TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[])) { - alias ElementEncodingType!S Char; + alias Char = ElementEncodingType!S; S s = to!S("yet another dummy text, yet another ..."); S from = to!S("yet another"); S into = to!S("some"); @@ -1932,7 +2048,8 @@ void replaceInPlace(T, Range)(ref T[] array, size_t from, size_t to, Range stuff // replacement reduces length immutable stuffEnd = from + stuff.length; array[from .. stuffEnd] = stuff[]; - array = remove(array, tuple(stuffEnd, to)); + if (stuffEnd < to) + array = remove(array, tuple(stuffEnd, to)); } else { @@ -1974,6 +2091,15 @@ unittest assert(a == [1, 4, 5, 5]); } +unittest +{ + // Bug# 12889 + int[1][] arr = [[0], [1], [2], [3], [4], [5], [6]]; + int[1][] stuff = [[0], [1]]; + replaceInPlace(arr, 4, 6, stuff); + assert(arr == [[0], [1], [2], [3], [0], [1], [6]]); +} + unittest { bool test(T, U, V)(T orig, size_t from, size_t to, U toReplace, V result, @@ -2068,7 +2194,7 @@ unittest foreach(S; TypeTuple!(string, wstring, dstring, char[], wchar[], dchar[], const(char[]), immutable(char[]))) { - alias Unqual!S T; + alias T = Unqual!S; auto s = to!S("This is a foo foo list"); auto from = to!T("foo"); @@ -2155,6 +2281,7 @@ struct Appender(A : T[], T) { size_t capacity; Unqual!T[] arr; + bool canExtend = false; } private Data* _data; @@ -2165,11 +2292,11 @@ struct Appender(A : T[], T) * it will be used by the appender. After initializing an appender on an array, * appending to the original array will reallocate. */ - this(Unqual!T[] arr) @safe pure nothrow + this(T[] arr) @trusted pure nothrow { // initialize to a given array. _data = new Data; - _data.arr = arr; + _data.arr = cast(Unqual!T[])arr; //trusted if (__ctfe) return; @@ -2177,14 +2304,33 @@ struct Appender(A : T[], T) // We want to use up as much of the block the array is in as possible. // if we consume all the block that we can, then array appending is // safe WRT built-in append, and we can use the entire block. - auto cap = ()@trusted{ return arr.capacity; }(); - if (cap > arr.length) - arr = ()@trusted{ return arr.ptr[0 .. cap]; }(); - // we assume no reallocation occurred - assert(arr.ptr is _data.arr.ptr); + // We only do this for mutable types that can be extended. + static if (isMutable!T && is(typeof(arr.length = size_t.max))) + { + auto cap = arr.capacity; //trusted + // Replace with "GC.setAttr( Not Appendable )" once pure (and fixed) + if (cap > arr.length) + arr.length = cap; + } _data.capacity = arr.length; } + //Broken function. To be removed. + static if (is(T == immutable)) + { + deprecated ("Using this constructor will break the type system. Please fix your code to use `Appender!(T[]).this(T[] arr)' directly.") + this(Unqual!T[] arr) pure nothrow + { + this(cast(T[]) arr); + } + + //temporary: For resolving ambiguity: + this(typeof(null)) + { + this(cast(T[]) null); + } + } + /** * Reserve at least newCapacity elements for appending. Note that more elements * may be reserved than requested. If newCapacity <= capacity, then nothing is @@ -2232,7 +2378,7 @@ struct Appender(A : T[], T) immutable len = _data.arr.length; immutable reqlen = len + nelems; - if (()@trusted{ return _data.capacity; }() >= reqlen) + if (_data.capacity >= reqlen) return; // need to increase capacity @@ -2259,26 +2405,29 @@ struct Appender(A : T[], T) // have better access to the capacity field. auto newlen = appenderNewCapacity!(T.sizeof)(_data.capacity, reqlen); // first, try extending the current block - auto u = ()@trusted{ return - GC.extend(_data.arr.ptr, nelems * T.sizeof, (newlen - len) * T.sizeof); - }(); - if (u) + if (_data.canExtend) { - // extend worked, update the capacity - _data.capacity = u / T.sizeof; - } - else - { - // didn't work, must reallocate - auto bi = ()@trusted{ return - GC.qalloc(newlen * T.sizeof, (typeid(T[]).next.flags & 1) ? 0 : GC.BlkAttr.NO_SCAN); + auto u = ()@trusted{ return + GC.extend(_data.arr.ptr, nelems * T.sizeof, (newlen - len) * T.sizeof); }(); - _data.capacity = bi.size / T.sizeof; - if (len) - ()@trusted{ memcpy(bi.base, _data.arr.ptr, len * T.sizeof); }(); - _data.arr = ()@trusted{ return (cast(Unqual!T*)bi.base)[0 .. len]; }(); - // leave the old data, for safety reasons + if (u) + { + // extend worked, update the capacity + _data.capacity = u / T.sizeof; + return; + } } + + // didn't work, must reallocate + auto bi = ()@trusted{ return + GC.qalloc(newlen * T.sizeof, blockAttribute!T); + }(); + _data.capacity = bi.size / T.sizeof; + if (len) + ()@trusted{ memcpy(bi.base, _data.arr.ptr, len * T.sizeof); }(); + _data.arr = ()@trusted{ return (cast(Unqual!T*)bi.base)[0 .. len]; }(); + _data.canExtend = true; + // leave the old data, for safety reasons } } @@ -2327,20 +2476,9 @@ struct Appender(A : T[], T) static if (is(Unqual!T == T)) alias uitem = item; else - auto ref uitem() @trusted nothrow @property { return cast(Unqual!T)item;} + auto ref uitem() @trusted nothrow @property { return cast(Unqual!T)item; } - //The idea is to only call emplace if we must. - static if ( is(typeof(bigData[0].opAssign(uitem))) || - !is(typeof(bigData[0] = uitem))) - { - //pragma(msg, T.stringof); pragma(msg, U.stringof); - emplace(&bigData[len], uitem); - } - else - { - //pragma(msg, T.stringof); pragma(msg, U.stringof); - bigData[len] = uitem; - } + emplaceRef!(Unqual!T)(bigData[len], uitem); //We do this at the end, in case of exceptions _data.arr = bigData; @@ -2350,7 +2488,7 @@ struct Appender(A : T[], T) // Const fixing hack. void put(Range)(Range items) if (canPutConstRange!Range) { - alias put!(Unqual!Range) p; + alias p = put!(Unqual!Range); p(items); } @@ -2385,34 +2523,18 @@ struct Appender(A : T[], T) auto bigDataFun() @trusted nothrow { return _data.arr.ptr[0 .. newlen];} auto bigData = bigDataFun(); - enum mustEmplace = is(typeof(bigData[0].opAssign(cast(Unqual!T)items.front))) || - !is(typeof(bigData[0] = cast(Unqual!T)items.front)); + alias UT = Unqual!T; - static if (is(typeof(_data.arr[] = items[])) && !mustEmplace) + static if (is(typeof(_data.arr[] = items[])) && + !hasElaborateAssign!(Unqual!T) && isAssignable!(UT, ElementEncodingType!Range)) { - //pragma(msg, T.stringof); pragma(msg, Range.stringof); bigData[len .. newlen] = items[]; } - else static if (is(Unqual!T == ElementType!Range)) - { - foreach (ref it ; bigData[len .. newlen]) - { - static if (mustEmplace) - emplace(&it, items.front); - else - it = items.front; - items.popFront(); - } - } else { - static auto ref getUItem(U)(U item) @trusted {return cast(Unqual!T)item;} foreach (ref it ; bigData[len .. newlen]) { - static if (mustEmplace) - emplace(&it, getUItem(items.front)); - else - it = getUItem(items.front); + emplaceRef!T(it, items.front); items.popFront(); } } @@ -2487,6 +2609,11 @@ struct Appender(A : T[], T) enforce(newlength == 0); } } + else + { + /// Clear is not available for const/immutable data. + @disable void clear(); + } } //Calculates an efficient growth scheme based on the old capacity @@ -2542,7 +2669,7 @@ struct RefAppender(A : T[], T) mixin("return impl." ~ fn ~ "(args);"); } - private alias Appender!(A, T) AppenderType; + private alias AppenderType = Appender!(A, T); /** * Appends one item to the managed array. @@ -2599,17 +2726,7 @@ Appender!(E[]) appender(A : E[], E)() /// ditto Appender!(E[]) appender(A : E[], E)(A array) { - static if (isMutable!E) - { - return Appender!(E[])(array); - } - else - { - /* @system operation: - * - casting array to Unqual!E[] (remove qualifiers) - */ - return Appender!(E[])(cast(Unqual!E[])array); - } + return Appender!(E[])(array); } @safe pure nothrow unittest @@ -2720,8 +2837,8 @@ Appender!(E[]) appender(A : E[], E)(A array) { auto w = appender!string(); w.reserve(4); - w.capacity; - w.data; + cast(void)w.capacity; + cast(void)w.data; try { wchar wc = 'a'; @@ -2734,8 +2851,8 @@ Appender!(E[]) appender(A : E[], E)(A array) { auto w = appender!(int[])(); w.reserve(4); - w.capacity; - w.data; + cast(void)w.capacity; + cast(void)w.data; w.put(10); w.put([10]); w.clear(); @@ -2827,7 +2944,8 @@ unittest } unittest -{ //9528 +{ + //9528 const(E)[] fastCopy(E)(E[] src) { auto app = appender!(const(E)[])(); foreach (i, e; src) @@ -2843,15 +2961,95 @@ unittest } unittest -{ //10753 +{ + //10753 struct Foo { immutable dchar d; } struct Bar { immutable int x; } - "12".map!Foo.array; - [1, 2].map!Bar.array; + "12".map!Foo.array; + [1, 2].map!Bar.array; +} + +unittest +{ + //New appender signature tests + alias mutARR = int[]; + alias conARR = const(int)[]; + alias immARR = immutable(int)[]; + + mutARR mut; + conARR con; + immARR imm; + + {auto app = Appender!mutARR(mut);} //Always worked. Should work. Should not create a warning. + static assert(!is(typeof(Appender!mutARR(con)))); //Never worked. Should not work. + static assert(!is(typeof(Appender!mutARR(imm)))); //Never worked. Should not work. + + {auto app = Appender!conARR(mut);} //Always worked. Should work. Should not create a warning. + {auto app = Appender!conARR(con);} //Didn't work. Now works. Should not create a warning. + {auto app = Appender!conARR(imm);} //Didn't work. Now works. Should not create a warning. + + //{auto app = Appender!immARR(mut);} //Worked. Will cease to work. Creates warning. + //static assert(!is(typeof(Appender!immARR(mut)))); //Worked. Will cease to work. Uncomment me after full deprecation. + static assert(!is(typeof(Appender!immARR(con)))); //Never worked. Should not work. + {auto app = Appender!immARR(imm);} //Didn't work. Now works. Should not create a warning. + + //Deprecated. Please uncomment and make sure this doesn't work: + //char[] cc; + //static assert(!is(typeof(Appender!string(cc)))); + + //This should always work: + {auto app = appender!string(null);} + {auto app = appender!(const(char)[])(null);} + {auto app = appender!(char[])(null);} +} + +unittest //Test large allocations (for GC.extend) +{ + Appender!(char[]) app; + app.reserve(1); //cover reserve on non-initialized + foreach(_; 0 .. 100_000) + app.put('a'); + assert(equal(app.data, 'a'.repeat(100_000))); +} + +unittest +{ + auto reference = new ubyte[](2048 + 1); //a number big enough to have a full page (EG: the GC extends) + auto arr = reference.dup; + auto app = appender(arr[0 .. 0]); + app.reserve(1); //This should not trigger a call to extend + app.put(ubyte(1)); //Don't clobber arr + assert(reference[] == arr[]); +} + +unittest // check against .clear UFCS hijacking +{ + Appender!string app; + static assert(!__traits(compiles, app.clear())); + static assert(__traits(compiles, clear(app)), + "Remove me when object.clear is removed!"); +} + +unittest +{ + // Issue 13077 + static class A {} + + // reduced case + auto w = appender!(shared(A)[])(); + w.put(new shared A()); + + // original case + import std.range; + InputRange!(shared A) foo() + { + return [new shared A].inputRangeObject; + } + auto res = foo.array; } /++ @@ -2926,6 +3124,20 @@ unittest assert(app.data == [1, 2, 3]); } +unittest +{ + string s = "hello".idup; + char[] a = "hello".dup; + auto appS = appender(s); + auto appA = appender(a); + put(appS, 'w'); + put(appA, 'w'); + s ~= 'a'; //Clobbers here? + a ~= 'a'; //Clobbers here? + assert(appS.data == "hellow"); + assert(appA.data == "hellow"); +} + /* A simple slice type only holding pointers to the beginning and the end of an array. Experimental duplication of the built-in slice - do not diff --git a/libphobos/src/std/ascii.d b/libphobos/src/std/ascii.d index fb05529a3..493fd6f6e 100644 --- a/libphobos/src/std/ascii.d +++ b/libphobos/src/std/ascii.d @@ -44,25 +44,20 @@ immutable uppercase = letters[0..26]; /// A..Z immutable lowercase = letters[26..52]; /// a..z immutable whitespace = " \t\v\r\n\f"; /// ASCII whitespace -/** -Letter case specifier. - */ +/++ + Letter case specifier. + +/ enum LetterCase : bool { upper, /// Upper case letters lower /// Lower case letters } +/// Newline sequence for this system. version(Windows) -{ - /// Newline sequence for this system. immutable newline = "\r\n"; -} else version(Posix) -{ - /// Newline sequence for this system. immutable newline = "\n"; -} else static assert(0, "Unsupported OS"); @@ -70,7 +65,7 @@ else /++ Returns whether $(D c) is a letter or a number (0..9, a..z, A..Z). +/ -bool isAlphaNum(dchar c) @safe pure nothrow +bool isAlphaNum(dchar c) @safe pure nothrow @nogc { return c <= 'z' && c >= '0' && (c <= '9' || c >= 'a' || (c >= 'A' && c <= 'Z')); } @@ -88,7 +83,7 @@ unittest /++ Returns whether $(D c) is an ASCII letter (A..Z, a..z). +/ -bool isAlpha(dchar c) @safe pure nothrow +bool isAlpha(dchar c) @safe pure nothrow @nogc { // Optimizer can turn this into a bitmask operation on 64 bit code return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); @@ -107,7 +102,7 @@ unittest /++ Returns whether $(D c) is a lowercase ASCII letter (a..z). +/ -bool isLower(dchar c) @safe pure nothrow +bool isLower(dchar c) @safe pure nothrow @nogc { return c >= 'a' && c <= 'z'; } @@ -125,7 +120,7 @@ unittest /++ Returns whether $(D c) is an uppercase ASCII letter (A..Z). +/ -bool isUpper(dchar c) @safe pure nothrow +bool isUpper(dchar c) @safe pure nothrow @nogc { return c <= 'Z' && 'A' <= c; } @@ -143,7 +138,7 @@ unittest /++ Returns whether $(D c) is a digit (0..9). +/ -bool isDigit(dchar c) @safe pure nothrow +bool isDigit(dchar c) @safe pure nothrow @nogc { return '0' <= c && c <= '9'; } @@ -161,7 +156,7 @@ unittest /++ Returns whether $(D c) is a digit in base 8 (0..7). +/ -bool isOctalDigit(dchar c) @safe pure nothrow +bool isOctalDigit(dchar c) @safe pure nothrow @nogc { return c >= '0' && c <= '7'; } @@ -179,7 +174,7 @@ unittest /++ Returns whether $(D c) is a digit in base 16 (0..9, A..F, a..f). +/ -bool isHexDigit(dchar c) @safe pure nothrow +bool isHexDigit(dchar c) @safe pure nothrow @nogc { return c <= 'f' && c >= '0' && (c <= '9' || c >= 'a' || (c >= 'A' && c <= 'F')); } @@ -198,7 +193,7 @@ unittest Whether or not $(D c) is a whitespace character. That includes the space, tab, vertical tab, form feed, carriage return, and linefeed characters. +/ -bool isWhite(dchar c) @safe pure nothrow +bool isWhite(dchar c) @safe pure nothrow @nogc { return c == ' ' || (c >= 0x09 && c <= 0x0D); } @@ -216,7 +211,7 @@ unittest /++ Returns whether $(D c) is a control character. +/ -bool isControl(dchar c) @safe pure nothrow +bool isControl(dchar c) @safe pure nothrow @nogc { return c < 0x20 || c == 0x7F; } @@ -236,7 +231,7 @@ unittest Whether or not $(D c) is a punctuation character. That includes all ASCII characters which are not control characters, letters, digits, or whitespace. +/ -bool isPunctuation(dchar c) @safe pure nothrow +bool isPunctuation(dchar c) @safe pure nothrow @nogc { return c <= '~' && c >= '!' && !isAlphaNum(c); } @@ -257,7 +252,7 @@ unittest Whether or not $(D c) is a printable character other than the space character. +/ -bool isGraphical(dchar c) @safe pure nothrow +bool isGraphical(dchar c) @safe pure nothrow @nogc { return '!' <= c && c <= '~'; } @@ -278,7 +273,7 @@ unittest Whether or not $(D c) is a printable character - including the space character. +/ -bool isPrintable(dchar c) @safe pure nothrow +bool isPrintable(dchar c) @safe pure nothrow @nogc { return c >= ' ' && c <= '~'; } @@ -299,7 +294,7 @@ unittest Whether or not $(D c) is in the ASCII character set - i.e. in the range 0..0x7F. +/ -bool isASCII(dchar c) @safe pure nothrow +bool isASCII(dchar c) @safe pure nothrow @nogc { return c <= 0x7F; } diff --git a/libphobos/src/std/base64.d b/libphobos/src/std/base64.d index 5613f9ef2..270bd2c73 100644 --- a/libphobos/src/std/base64.d +++ b/libphobos/src/std/base64.d @@ -53,13 +53,13 @@ version(unittest) import std.algorithm, std.conv, std.file, std.stdio; /** * The Base64 */ -alias Base64Impl!('+', '/') Base64; +alias Base64 = Base64Impl!('+', '/'); /** * The "URL and Filename safe" Base64 */ -alias Base64Impl!('-', '_') Base64URL; +alias Base64URL = Base64Impl!('-', '_'); /** @@ -67,8 +67,8 @@ alias Base64Impl!('-', '_') Base64URL; * * Example: * ----- - * alias Base64Impl!('+', '/') Base64; // The Base64 format(Already defined). - * alias Base64Impl!('!', '=', Base64.NoPadding) Base64Re; // non-standard Base64 format for Regular expression + * alias Base64 = Base64Impl!('+', '/'); // The Base64 format(Already defined). + * alias Base64Re = Base64Impl!('!', '=', Base64.NoPadding); // non-standard Base64 format for Regular expression * ----- * * NOTE: @@ -1422,7 +1422,7 @@ class Base64Exception : Exception unittest { - alias Base64Impl!('!', '=', Base64.NoPadding) Base64Re; + alias Base64Re = Base64Impl!('!', '=', Base64.NoPadding); // Test vectors from RFC 4648 ubyte[][string] tv = [ @@ -1646,7 +1646,7 @@ unittest } { // Encoder and Decoder for single character encoding and decoding - alias Base64Impl!('+', '/', Base64.NoPadding) Base64NoPadding; + alias Base64NoPadding = Base64Impl!('+', '/', Base64.NoPadding); auto tests = [ "" : ["", "", "", ""], diff --git a/libphobos/src/std/bigint.d b/libphobos/src/std/bigint.d index b4e96e673..ef568a219 100644 --- a/libphobos/src/std/bigint.d +++ b/libphobos/src/std/bigint.d @@ -100,26 +100,27 @@ public: ok = data.fromDecimalString(s); } assert(ok); + if (isZero()) neg = false; sign = neg; } /// - this(T)(T x) pure if (isIntegral!T) + this(T)(T x) pure nothrow if (isIntegral!T) { data = data.init; // @@@: Workaround for compiler bug opAssign(x); } /// - this(T)(T x) pure if (is(Unqual!T == BigInt)) + this(T)(T x) pure nothrow if (is(Unqual!T == BigInt)) { opAssign(x); } /// - BigInt opAssign(T)(T x) pure if (isIntegral!T) + BigInt opAssign(T)(T x) pure nothrow if (isIntegral!T) { data = cast(ulong)absUnsign(x); sign = (x < 0); @@ -135,7 +136,7 @@ public: } // BigInt op= integer - BigInt opOpAssign(string op, T)(T y) pure + BigInt opOpAssign(string op, T)(T y) pure nothrow if ((op=="+" || op=="-" || op=="*" || op=="/" || op=="%" || op==">>" || op=="<<" || op=="^^" || op=="|" || op=="&" || op=="^") && isIntegral!T) { @@ -212,8 +213,8 @@ public: } // BigInt op= BigInt - BigInt opOpAssign(string op, T)(T y) pure - if ((op=="+" || op== "-" || op=="*" || op=="/" || op=="%" || op=="|" || op=="&" || op=="^") + BigInt opOpAssign(string op, T)(T y) pure nothrow + if ((op=="+" || op== "-" || op=="*" || op=="|" || op=="&" || op=="^" || op=="/" || op=="%") && is (T: BigInt)) { static if (op == "+") @@ -253,13 +254,15 @@ public: { data = BigUint.bitwiseOp!op(data, y.data, sign, y.sign, sign); } - else static assert(0, "BigInt " ~ op[0..$-1] ~ "= " ~ T.stringof ~ " is not supported"); + else static assert(0, "BigInt " ~ op[0..$-1] ~ "= " ~ + T.stringof ~ " is not supported"); return this; } // BigInt op BigInt - BigInt opBinary(string op, T)(T y) pure const - if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="%" || op=="|" || op=="&" || op=="^") + BigInt opBinary(string op, T)(T y) pure nothrow const + if ((op=="+" || op == "*" || op=="-" || op=="|" || op=="&" || op=="^" || + op=="/" || op=="%") && is (T: BigInt)) { BigInt r = this; @@ -267,16 +270,17 @@ public: } // BigInt op integer - BigInt opBinary(string op, T)(T y) pure const - if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="|" || op=="&" || op=="^" - || op==">>" || op=="<<" || op=="^^") && isIntegral!T) + BigInt opBinary(string op, T)(T y) pure nothrow const + if ((op=="+" || op == "*" || op=="-" || op=="/" || op=="|" || op=="&" || + op=="^"|| op==">>" || op=="<<" || op=="^^") + && isIntegral!T) { BigInt r = this; return r.opOpAssign!(op)(y); } // - int opBinary(string op, T : int)(T y) pure const + int opBinary(string op, T : int)(T y) pure nothrow const if (op == "%" && isIntegral!T) { assert(y!=0); @@ -288,14 +292,14 @@ public: } // Commutative operators - BigInt opBinaryRight(string op, T)(T y) pure const + BigInt opBinaryRight(string op, T)(T y) pure nothrow const if ((op=="+" || op=="*" || op=="|" || op=="&" || op=="^") && isIntegral!T) { return opBinary!(op)(y); } // BigInt = integer op BigInt - BigInt opBinaryRight(string op, T)(T y) pure const + BigInt opBinaryRight(string op, T)(T y) pure nothrow const if (op == "-" && isIntegral!T) { ulong u = absUnsign(y); @@ -310,12 +314,13 @@ public: } // integer = integer op BigInt - T opBinaryRight(string op, T)(T x) pure const + T opBinaryRight(string op, T)(T x) pure nothrow const if ((op=="%" || op=="/") && isIntegral!T) { + checkDivByZero(); + static if (op == "%") { - checkDivByZero(); // x%y always has the same sign as x. if (data.ulongLength() > 1) return x; @@ -326,14 +331,13 @@ public: } else static if (op == "/") { - checkDivByZero(); if (data.ulongLength() > 1) return 0; return cast(T)(x / data.peekUlong(0)); } } // const unary operations - BigInt opUnary(string op)() pure const if (op=="+" || op=="-" || op=="~") + BigInt opUnary(string op)() pure nothrow const if (op=="+" || op=="-" || op=="~") { static if (op=="-") { @@ -350,7 +354,7 @@ public: } // non-const unary operations - BigInt opUnary(string op)() pure if (op=="++" || op=="--") + BigInt opUnary(string op)() pure nothrow if (op=="++" || op=="--") { static if (op=="++") { @@ -392,14 +396,14 @@ public: // Hack to make BigInt's typeinfo.compare work properly. // Note that this must appear before the other opCmp overloads, otherwise // DMD won't find it. - int opCmp(ref const BigInt y) const + int opCmp(ref const BigInt y) pure nothrow const { // Simply redirect to the "real" opCmp implementation. return this.opCmp!BigInt(y); } /// - int opCmp(T)(T y) pure const if (isIntegral!T) + int opCmp(T)(T y) pure nothrow const if (isIntegral!T) { if (sign != (y<0) ) return sign ? -1 : 1; @@ -407,7 +411,7 @@ public: return sign? -cmp: cmp; } /// - int opCmp(T:BigInt)(const T y) pure const + int opCmp(T:BigInt)(const T y) pure nothrow const { if (sign!=y.sign) return sign ? -1 : 1; @@ -416,7 +420,7 @@ public: } /// Returns the value of this BigInt as a long, /// or +- long.max if outside the representable range. - long toLong() pure const + long toLong() pure nothrow const { return (sign ? -1 : 1) * (data.ulongLength() == 1 && (data.peekUlong(0) <= sign+cast(ulong)(long.max)) // 1+long.max = |long.min| @@ -425,7 +429,7 @@ public: } /// Returns the value of this BigInt as an int, /// or +- int.max if outside the representable range. - int toInt() pure const + int toInt() pure nothrow const { return (sign ? -1 : 1) * (data.uintLength() == 1 && (data.peekUint(0) <= sign+cast(uint)(int.max)) // 1+int.max = |int.min| @@ -434,13 +438,13 @@ public: } /// Number of significant uints which are used in storing this number. /// The absolute value of this BigInt is always < 2^^(32*uintLength) - @property size_t uintLength() pure const + @property size_t uintLength() pure nothrow const { return data.uintLength(); } /// Number of significant ulongs which are used in storing this number. /// The absolute value of this BigInt is always < 2^^(64*ulongLength) - @property size_t ulongLength() pure const + @property size_t ulongLength() pure nothrow const { return data.ulongLength(); } @@ -478,9 +482,15 @@ public: if (!hex && !signChar && (f.width == 0 || minw < f.width)) { if (f.flPlus) - signChar = '+', ++minw; + { + signChar = '+'; + ++minw; + } else if (f.flSpace) - signChar = ' ', ++minw; + { + signChar = ' '; + ++minw; + } } auto maxw = minw < f.width ? f.width : minw; @@ -518,7 +528,7 @@ private: } +/ private: - void negate() pure nothrow @safe + void negate() @safe pure nothrow { if (!data.isZero()) sign = !sign; @@ -531,8 +541,9 @@ private: { return sign; } + // Generate a runtime error if division by zero occurs - void checkDivByZero() pure const @safe + void checkDivByZero() pure const nothrow @safe { if (isZero()) throw new Error("BigInt division by zero"); @@ -579,6 +590,46 @@ Unsigned!T absUnsign(T)(T x) if (isIntegral!T) } } +nothrow pure +unittest { + BigInt a, b; + a = 1; + b = 2; + auto c = a + b; +} + +nothrow pure +unittest { + long a; + BigInt b; + auto c = a + b; + auto d = b + a; +} + +nothrow pure +unittest { + BigInt x = 1, y = 2; + assert(x < y); + assert(x <= y); + assert(y >= x); + assert(y > x); + assert(x != y); + + long r1 = x.toLong; + assert(r1 == 1); + + BigInt r2 = 10 % x; + assert(r2 == 0); + + BigInt r3 = 10 / y; + assert(r3 == 5); + + BigInt[] arr = [BigInt(1)]; + auto incr = arr[0]++; + assert(arr == [BigInt(2)]); + assert(incr == BigInt(1)); +} + unittest { // Radix conversion assert( toDecimalString(BigInt("-1_234_567_890_123_456_789")) diff --git a/libphobos/src/std/bitmanip.d b/libphobos/src/std/bitmanip.d index 8144cbf0c..6d08a641b 100644 --- a/libphobos/src/std/bitmanip.d +++ b/libphobos/src/std/bitmanip.d @@ -77,7 +77,7 @@ private template createAccessors( { enum long minVal = -(1uL << (len - 1)); enum ulong maxVal = (1uL << (len - 1)) - 1; - alias Unsigned!(T) UT; + alias UT = Unsigned!(T); enum UT extendSign = cast(UT)~((~0uL) >> (64 - len)); } else @@ -103,7 +103,7 @@ private template createAccessors( { // getter enum result = "@property @safe "~T.stringof~" "~name~"() pure nothrow const { auto result = " - "("~store~" & " + ~"("~store~" & " ~ myToString(maskAllElse) ~ ") >>" ~ myToString(offset) ~ ";" ~ (T.min < 0 @@ -113,12 +113,12 @@ private template createAccessors( ~ " return cast("~T.stringof~") result;}\n" // setter ~"@property @safe void "~name~"("~T.stringof~" v) pure nothrow { " - ~"assert(v >= "~name~"_min); " - ~"assert(v <= "~name~"_max); " + ~"assert(v >= "~name~`_min, "Value is smaller than the minimum value of bitfield '`~name~`'"); ` + ~"assert(v <= "~name~`_max, "Value is greater than the maximum value of bitfield '`~name~`'"); ` ~store~" = cast(typeof("~store~"))" - " (("~store~" & ~cast(typeof("~store~"))"~myToString(maskAllElse)~")" - " | ((cast(typeof("~store~")) v << "~myToString(offset)~")" - " & "~myToString(maskAllElse)~"));}\n" + ~" (("~store~" & ~cast(typeof("~store~"))"~myToString(maskAllElse)~")" + ~" | ((cast(typeof("~store~")) v << "~myToString(offset)~")" + ~" & "~myToString(maskAllElse)~"));}\n" // constants ~"enum "~T.stringof~" "~name~"_min = cast("~T.stringof~")" ~myToString(minVal)~"; " @@ -141,17 +141,17 @@ private template createFields(string store, size_t offset, Ts...) static if (!Ts.length) { static if (offset == ubyte.sizeof * 8) - alias ubyte StoreType; + alias StoreType = ubyte; else static if (offset == ushort.sizeof * 8) - alias ushort StoreType; + alias StoreType = ushort; else static if (offset == uint.sizeof * 8) - alias uint StoreType; + alias StoreType = uint; else static if (offset == ulong.sizeof * 8) - alias ulong StoreType; + alias StoreType = ulong; else { static assert(false, "Field widths must sum to 8, 16, 32, or 64"); - alias ulong StoreType; // just to avoid another error msg + alias StoreType = ulong; // just to avoid another error msg } enum result = "private " ~ StoreType.stringof ~ " " ~ store ~ ";"; } @@ -416,6 +416,30 @@ unittest assert(f.checkExpectations(true)); } +// Issue 12477 +unittest +{ + import std.bitmanip : bitfields; + import core.exception : AssertError; + + static struct S + { + mixin(bitfields!( + uint, "a", 6, + int, "b", 2)); + } + + S s; + + try { s.a = uint.max; assert(0); } + catch (AssertError ae) + { assert(ae.msg == "Value is greater than the maximum value of bitfield 'a'", ae.msg); } + + try { s.b = int.min; assert(0); } + catch (AssertError ae) + { assert(ae.msg == "Value is smaller than the minimum value of bitfield 'b'", ae.msg); } +} + /** Allows manipulating the fraction, exponent, and sign parts of a $(D_PARAM float) separately. The definition is: @@ -860,7 +884,7 @@ struct BitArray } n = this.length & (bitsPerSizeT-1); - size_t mask = (1 << n) - 1; + size_t mask = (size_t(1) << n) - 1; //printf("i = %d, n = %d, mask = %x, %x, %x\n", i, n, mask, p1[i], p2[i]); return (mask == 0) || (p1[i] & mask) == (p2[i] & mask); } @@ -874,17 +898,22 @@ struct BitArray static bool[] bc = [1,0,1,0,1,0,1]; static bool[] bd = [1,0,1,1,1]; static bool[] be = [1,0,1,0,1]; + static bool[] bf = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; + static bool[] bg = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]; BitArray a; a.init(ba); BitArray b; b.init(bb); BitArray c; c.init(bc); BitArray d; d.init(bd); BitArray e; e.init(be); + BitArray f; f.init(bf); + BitArray g; g.init(bg); assert(a != b); assert(a != c); assert(a != d); assert(a == e); + assert(f != g); } /*************************************** @@ -900,19 +929,33 @@ struct BitArray auto p1 = this.ptr; auto p2 = a2.ptr; auto n = len / bitsPerSizeT; - for (i = 0; i < n; i++) + + for (i = 0; i < n; ++i) { if (p1[i] != p2[i]) - break; // not equal + { + return p1[i] & size_t(1) << bsf(p1[i] ^ p2[i]) ? 1 : -1; + } } - for (size_t j = 0; j < len-i * bitsPerSizeT; j++) + + immutable lenLastChunk = len % bitsPerSizeT; + if (lenLastChunk > 0) { - size_t mask = cast(size_t)(1 << j); - auto c = (cast(long)(p1[i] & mask) - cast(long)(p2[i] & mask)); - if (c) - return c > 0 ? 1 : -1; + immutable diff = p1[i] ^ p2[i]; + if (diff) + { + immutable index = bsf(diff); + if (index < lenLastChunk) + { + return p1[i] & size_t(1) << index ? 1 : -1; + } + } } - return cast(int)this.len - cast(int)a2.length; + + // Standard: + // A bool value can be implicitly converted to any integral type, + // with false becoming 0 and true becoming 1 + return (this.length > a2.length) - (this.length < a2.length); } unittest @@ -924,12 +967,16 @@ struct BitArray static bool[] bc = [1,0,1,0,1,0,1]; static bool[] bd = [1,0,1,1,1]; static bool[] be = [1,0,1,0,1]; + static bool[] bf = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1]; + static bool[] bg = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0]; BitArray a; a.init(ba); BitArray b; b.init(bb); BitArray c; c.init(bc); BitArray d; d.init(bd); BitArray e; e.init(be); + BitArray f; f.init(bf); + BitArray g; g.init(bg); assert(a > b); assert(a >= b); @@ -940,9 +987,11 @@ struct BitArray assert(a == e); assert(a <= e); assert(a >= e); + assert(f < g); + assert(g <= g); bool[] v; - for (int i = 1; i < 256; i++) + foreach (i; 1 .. 256) { v.length = i; v[] = false; @@ -952,6 +1001,23 @@ struct BitArray assert(x < y); assert(x <= y); } + + BitArray a1, a2; + + for (size_t len = 4; len <= 256; len <<= 1) + { + a1.length = a2.length = len; + a1[len-2] = a2[len-1] = true; + assert(a1 > a2); + a1[len-2] = a2[len-1] = false; + } + + foreach (j; 1 .. a1.length) + { + a1[j-1] = a2[j] = true; + assert(a1 > a2); + a1[j-1] = a2[j] = false; + } } /*************************************** @@ -989,7 +1055,9 @@ struct BitArray /*************************************** * Map the $(D BitArray) onto $(D v), with $(D numbits) being the number of bits - * in the array. Does not copy the data. + * in the array. Does not copy the data. $(D v.length) must be a multiple of + * $(D size_t.sizeof). If there are unmapped bits in the final mapped word then + * these will be set to 0. * * This is the inverse of $(D opCast). */ @@ -997,12 +1065,18 @@ struct BitArray in { assert(numbits <= v.length * 8); - assert((v.length & 3) == 0); + assert(v.length % size_t.sizeof == 0); } body { ptr = cast(size_t*)v.ptr; len = numbits; + size_t finalBits = len % bitsPerSizeT; + if (finalBits != 0) + { + // Need to mask away extraneous bits from v. + ptr[dim - 1] &= (cast(size_t)1 << finalBits) - 1; + } } unittest @@ -1555,6 +1629,7 @@ struct BitArray /// unittest { + debug(bitarray) printf("BitArray.toString unittest\n"); BitArray b; b.init([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]); @@ -1565,6 +1640,57 @@ struct BitArray assert(s2 == "00001111_00001111"); } + /*************************************** + * Return a lazy range of the indices of set bits. + */ + @property auto bitsSet() + { + import std.algorithm : filter, map, joiner; + + return iota(dim). + filter!(i => ptr[i]). + map!(i => BitsSet!size_t(ptr[i], i * bitsPerSizeT)). + joiner(); + } + + /// + unittest + { + import std.algorithm : equal; + + BitArray b1; + b1.init([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]); + assert(b1.bitsSet.equal([4, 5, 6, 7, 12, 13, 14, 15])); + + BitArray b2; + b2.length = 1000; + b2[333] = true; + b2[666] = true; + b2[999] = true; + assert(b2.bitsSet.equal([333, 666, 999])); + } + + unittest + { + import std.algorithm : equal; + + debug(bitarray) printf("BitArray.bitsSet unittest\n"); + BitArray b; + enum wordBits = size_t.sizeof * 8; + b.init([size_t.max], 0); + assert(b.bitsSet.empty); + b.init([size_t.max], 1); + assert(b.bitsSet.equal([0])); + b.init([size_t.max], wordBits); + assert(b.bitsSet.equal(iota(wordBits))); + b.init([size_t.max, size_t.max], wordBits); + assert(b.bitsSet.equal(iota(wordBits))); + b.init([size_t.max, size_t.max], wordBits + 1); + assert(b.bitsSet.equal(iota(wordBits + 1))); + b.init([size_t.max, size_t.max], wordBits * 2); + assert(b.bitsSet.equal(iota(wordBits * 2))); + } + private void formatBitString(scope void delegate(const(char)[]) sink) const { if (!length) @@ -2193,7 +2319,7 @@ unittest $(D T). The value returned is converted from the given endianness to the native endianness. The range is not consumed. - Parems: + Params: T = The integral type to convert the first $(D T.sizeof) bytes to. endianness = The endianness that the bytes are assumed to be in. range = The range to read from. @@ -2522,7 +2648,7 @@ unittest native endianness. The $(D T.sizeof) bytes which are read are consumed from the range. - Parems: + Params: T = The integral type to convert the first $(D T.sizeof) bytes to. endianness = The endianness that the bytes are assumed to be in. range = The range to read from. @@ -2779,7 +2905,7 @@ unittest to the given range of $(D ubyte)s as a sequence of $(D T.sizeof) $(D ubyte)s starting at index. $(D hasSlicing!R) must be $(D true). - Parems: + Params: T = The integral type to convert the first $(D T.sizeof) bytes to. endianness = The endianness to write the bytes in. range = The range to write to. @@ -3167,7 +3293,7 @@ unittest $(D T.sizeof) $(D ubyte)s starting at index. $(D hasSlicing!R) must be $(D true). - Parems: + Params: T = The integral type to convert the first $(D T.sizeof) bytes to. endianness = The endianness to write the bytes in. range = The range to append to. @@ -3347,7 +3473,7 @@ unittest foreach(endianness; TypeTuple!(Endian.bigEndian, Endian.littleEndian)) { auto toWrite = appender!(ubyte[])(); - alias TypeTuple!(uint, int, long, ulong, short, ubyte, ushort, byte, uint) Types; + alias Types = TypeTuple!(uint, int, long, ulong, short, ubyte, ushort, byte, uint); ulong[] values = [42, -11, long.max, 1098911981329L, 16, 255, 19012, 2, 17]; assert(Types.length == values.length); @@ -3378,3 +3504,241 @@ unittest assert(toRead.empty); } } + +/** +Counts the number of trailing zeros in the binary representation of $(D value). +For signed integers, the sign bit is included in the count. +*/ +private uint countTrailingZeros(T)(T value) + if (isIntegral!T) +{ + // bsf doesn't give the correct result for 0. + if (!value) + return 8 * T.sizeof; + + static if (T.sizeof == 8 && size_t.sizeof == 4) + { + // bsf's parameter is size_t, so it doesn't work with 64-bit integers + // on a 32-bit machine. For this case, we call bsf on each 32-bit half. + uint lower = cast(uint)value; + if (lower) + return bsf(lower); + value >>>= 32; + return 32 + bsf(cast(uint)value); + } + else + { + return bsf(value); + } +} + +/// +unittest +{ + assert(countTrailingZeros(1) == 0); + assert(countTrailingZeros(0) == 32); + assert(countTrailingZeros(int.min) == 31); + assert(countTrailingZeros(256) == 8); +} + +unittest +{ + foreach (T; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong)) + { + assert(countTrailingZeros(cast(T)0) == 8 * T.sizeof); + assert(countTrailingZeros(cast(T)1) == 0); + assert(countTrailingZeros(cast(T)2) == 1); + assert(countTrailingZeros(cast(T)3) == 0); + assert(countTrailingZeros(cast(T)4) == 2); + assert(countTrailingZeros(cast(T)5) == 0); + assert(countTrailingZeros(cast(T)64) == 6); + static if (isSigned!T) + { + assert(countTrailingZeros(cast(T)-1) == 0); + assert(countTrailingZeros(T.min) == 8 * T.sizeof - 1); + } + else + { + assert(countTrailingZeros(T.max) == 0); + } + } + assert(countTrailingZeros(1_000_000) == 6); + foreach (i; 0..63) + assert(countTrailingZeros(1UL << i) == i); +} + +/** +Counts the number of set bits in the binary representation of $(D value). +For signed integers, the sign bit is included in the count. +*/ +private uint countBitsSet(T)(T value) + if (isIntegral!T) +{ + // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + static if (T.sizeof == 8) + { + T c = value - ((value >> 1) & 0x55555555_55555555); + c = ((c >> 2) & 0x33333333_33333333) + (c & 0x33333333_33333333); + c = ((c >> 4) + c) & 0x0F0F0F0F_0F0F0F0F; + c = ((c >> 8) + c) & 0x00FF00FF_00FF00FF; + c = ((c >> 16) + c) & 0x0000FFFF_0000FFFF; + c = ((c >> 32) + c) & 0x00000000_FFFFFFFF; + } + else static if (T.sizeof == 4) + { + T c = value - ((value >> 1) & 0x55555555); + c = ((c >> 2) & 0x33333333) + (c & 0x33333333); + c = ((c >> 4) + c) & 0x0F0F0F0F; + c = ((c >> 8) + c) & 0x00FF00FF; + c = ((c >> 16) + c) & 0x0000FFFF; + } + else static if (T.sizeof == 2) + { + uint c = value - ((value >> 1) & 0x5555); + c = ((c >> 2) & 0x3333) + (c & 0X3333); + c = ((c >> 4) + c) & 0x0F0F; + c = ((c >> 8) + c) & 0x00FF; + } + else static if (T.sizeof == 1) + { + uint c = value - ((value >> 1) & 0x55); + c = ((c >> 2) & 0x33) + (c & 0X33); + c = ((c >> 4) + c) & 0x0F; + } + else + { + static assert("countBitsSet only supports 1, 2, 4, or 8 byte sized integers."); + } + return cast(uint)c; +} + +/// +unittest +{ + assert(countBitsSet(1) == 1); + assert(countBitsSet(0) == 0); + assert(countBitsSet(int.min) == 1); + assert(countBitsSet(uint.max) == 32); +} + +unittest +{ + foreach (T; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong)) + { + assert(countBitsSet(cast(T)0) == 0); + assert(countBitsSet(cast(T)1) == 1); + assert(countBitsSet(cast(T)2) == 1); + assert(countBitsSet(cast(T)3) == 2); + assert(countBitsSet(cast(T)4) == 1); + assert(countBitsSet(cast(T)5) == 2); + assert(countBitsSet(cast(T)127) == 7); + static if (isSigned!T) + { + assert(countBitsSet(cast(T)-1) == 8 * T.sizeof); + assert(countBitsSet(T.min) == 1); + } + else + { + assert(countBitsSet(T.max) == 8 * T.sizeof); + } + } + assert(countBitsSet(1_000_000) == 7); + foreach (i; 0..63) + assert(countBitsSet(1UL << i) == 1); +} + +private struct BitsSet(T) +{ + static assert(T.sizeof <= 8, "bitsSet assumes T is no more than 64-bit."); + + this(T value, size_t startIndex = 0) + { + _value = value; + uint n = countTrailingZeros(value); + _index = startIndex + n; + _value >>>= n; + } + + @property size_t front() + { + return _index; + } + + @property bool empty() const + { + return !_value; + } + + void popFront() + { + assert(_value, "Cannot call popFront on empty range."); + + _value >>>= 1; + uint n = countTrailingZeros(_value); + _value >>>= n; + _index += n + 1; + } + + @property auto save() + { + return this; + } + + @property size_t length() + { + return countBitsSet(_value); + } + + private T _value; + private size_t _index; +} + +/** +Range that iterates the indices of the set bits in $(D value). +Index 0 corresponds to the least significant bit. +For signed integers, the highest index corresponds to the sign bit. +*/ +auto bitsSet(T)(T value) + if (isIntegral!T) +{ + return BitsSet!T(value); +} + +/// +unittest +{ + import std.algorithm : equal; + + assert(bitsSet(1).equal([0])); + assert(bitsSet(5).equal([0, 2])); + assert(bitsSet(-1).equal(iota(32))); + assert(bitsSet(int.min).equal([31])); +} + +unittest +{ + import std.algorithm : equal; + + foreach (T; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong)) + { + assert(bitsSet(cast(T)0).empty); + assert(bitsSet(cast(T)1).equal([0])); + assert(bitsSet(cast(T)2).equal([1])); + assert(bitsSet(cast(T)3).equal([0, 1])); + assert(bitsSet(cast(T)4).equal([2])); + assert(bitsSet(cast(T)5).equal([0, 2])); + assert(bitsSet(cast(T)127).equal(iota(7))); + static if (isSigned!T) + { + assert(bitsSet(cast(T)-1).equal(iota(8 * T.sizeof))); + assert(bitsSet(T.min).equal([8 * T.sizeof - 1])); + } + else + { + assert(bitsSet(T.max).equal(iota(8 * T.sizeof))); + } + } + assert(bitsSet(1_000_000).equal([6, 9, 14, 16, 17, 18, 19])); + foreach (i; 0..63) + assert(bitsSet(1UL << i).equal([i])); +} diff --git a/libphobos/src/std/c/process.d b/libphobos/src/std/c/process.d index 334e2569d..650b50426 100644 --- a/libphobos/src/std/c/process.d +++ b/libphobos/src/std/c/process.d @@ -44,7 +44,7 @@ version (Windows) { uint _beginthread(void function(void *),uint,void *); - extern (Windows) alias uint function (void *) stdfp; + extern (Windows) alias stdfp = uint function (void *); uint _beginthreadex(void* security, uint stack_size, stdfp start_addr, void* arglist, uint initflag, diff --git a/libphobos/src/std/c/windows/com.d b/libphobos/src/std/c/windows/com.d index 4aa0f2fa1..f862e1025 100644 --- a/libphobos/src/std/c/windows/com.d +++ b/libphobos/src/std/c/windows/com.d @@ -8,8 +8,8 @@ import std.c.windows.windows; import std.string; alias WCHAR OLECHAR; -alias OLECHAR *LPOLESTR; -alias OLECHAR *LPCOLESTR; +alias LPOLESTR = OLECHAR*; +alias LPCOLESTR = OLECHAR*; enum { @@ -65,11 +65,11 @@ enum COINIT_DISABLE_OLE1DDE = 0x4, COINIT_SPEED_OVER_MEMORY = 0x8 } -alias DWORD COINIT; +alias COINIT = DWORD; enum RPC_E_CHANGED_MODE = 0x80010106; -alias const(GUID) IID; -alias const(GUID) CLSID; +alias IID = const(GUID); +alias CLSID = const(GUID); extern (C) { diff --git a/libphobos/src/std/c/windows/winsock.d b/libphobos/src/std/c/windows/winsock.d index 9028545f8..6f65ff096 100644 --- a/libphobos/src/std/c/windows/winsock.d +++ b/libphobos/src/std/c/windows/winsock.d @@ -7,42 +7,39 @@ module std.c.windows.winsock; version (Windows): -private import std.stdint; -private import std.c.windows.windows; - - extern(Windows): +nothrow: -alias size_t SOCKET; -alias int socklen_t; +alias SOCKET = size_t; +alias socklen_t = int; const SOCKET INVALID_SOCKET = cast(SOCKET)~0; const int SOCKET_ERROR = -1; -const int WSADESCRIPTION_LEN = 256; -const int WSASYS_STATUS_LEN = 128; +enum WSADESCRIPTION_LEN = 256; +enum WSASYS_STATUS_LEN = 128; struct WSADATA { - WORD wVersion; - WORD wHighVersion; + ushort wVersion; + ushort wHighVersion; char[WSADESCRIPTION_LEN + 1] szDescription; char[WSASYS_STATUS_LEN + 1] szSystemStatus; - USHORT iMaxSockets; - USHORT iMaxUdpDg; + ushort iMaxSockets; + ushort iMaxUdpDg; char* lpVendorInfo; } -alias WSADATA* LPWSADATA; +alias LPWSADATA = WSADATA*; const int IOCPARM_MASK = 0x7F; const int IOC_IN = cast(int)0x80000000; -const int FIONBIO = cast(int)(IOC_IN | ((UINT.sizeof & IOCPARM_MASK) << 16) | (102 << 8) | 126); +const int FIONBIO = cast(int)(IOC_IN | ((uint.sizeof & IOCPARM_MASK) << 16) | (102 << 8) | 126); enum NI_MAXHOST = 1025; enum NI_MAXSERV = 32; -int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData); +int WSAStartup(ushort wVersionRequested, LPWSADATA lpWSAData); int WSACleanup(); SOCKET socket(int af, int type, int protocol); int ioctlsocket(SOCKET s, int cmd, uint* argp); @@ -82,14 +79,133 @@ enum: int int gethostname(const char* name, int namelen); int getaddrinfo(const(char)* nodename, const(char)* servname, const(addrinfo)* hints, addrinfo** res); void freeaddrinfo(addrinfo* ai); -int getnameinfo(const(sockaddr)* sa, socklen_t salen, char* host, DWORD hostlen, char* serv, DWORD servlen, int flags); +int getnameinfo(const(sockaddr)* sa, socklen_t salen, char* host, uint hostlen, char* serv, uint servlen, int flags); + +enum WSABASEERR = 10000; + +enum: int +{ + /* + * Windows Sockets definitions of regular Microsoft C error constants + */ + WSAEINTR = (WSABASEERR+4), + WSAEBADF = (WSABASEERR+9), + WSAEACCES = (WSABASEERR+13), + WSAEFAULT = (WSABASEERR+14), + WSAEINVAL = (WSABASEERR+22), + WSAEMFILE = (WSABASEERR+24), + + /* + * Windows Sockets definitions of regular Berkeley error constants + */ + WSAEWOULDBLOCK = (WSABASEERR+35), + WSAEINPROGRESS = (WSABASEERR+36), + WSAEALREADY = (WSABASEERR+37), + WSAENOTSOCK = (WSABASEERR+38), + WSAEDESTADDRREQ = (WSABASEERR+39), + WSAEMSGSIZE = (WSABASEERR+40), + WSAEPROTOTYPE = (WSABASEERR+41), + WSAENOPROTOOPT = (WSABASEERR+42), + WSAEPROTONOSUPPORT = (WSABASEERR+43), + WSAESOCKTNOSUPPORT = (WSABASEERR+44), + WSAEOPNOTSUPP = (WSABASEERR+45), + WSAEPFNOSUPPORT = (WSABASEERR+46), + WSAEAFNOSUPPORT = (WSABASEERR+47), + WSAEADDRINUSE = (WSABASEERR+48), + WSAEADDRNOTAVAIL = (WSABASEERR+49), + WSAENETDOWN = (WSABASEERR+50), + WSAENETUNREACH = (WSABASEERR+51), + WSAENETRESET = (WSABASEERR+52), + WSAECONNABORTED = (WSABASEERR+53), + WSAECONNRESET = (WSABASEERR+54), + WSAENOBUFS = (WSABASEERR+55), + WSAEISCONN = (WSABASEERR+56), + WSAENOTCONN = (WSABASEERR+57), + WSAESHUTDOWN = (WSABASEERR+58), + WSAETOOMANYREFS = (WSABASEERR+59), + WSAETIMEDOUT = (WSABASEERR+60), + WSAECONNREFUSED = (WSABASEERR+61), + WSAELOOP = (WSABASEERR+62), + WSAENAMETOOLONG = (WSABASEERR+63), + WSAEHOSTDOWN = (WSABASEERR+64), + WSAEHOSTUNREACH = (WSABASEERR+65), + WSAENOTEMPTY = (WSABASEERR+66), + WSAEPROCLIM = (WSABASEERR+67), + WSAEUSERS = (WSABASEERR+68), + WSAEDQUOT = (WSABASEERR+69), + WSAESTALE = (WSABASEERR+70), + WSAEREMOTE = (WSABASEERR+71), + + /* + * Extended Windows Sockets error constant definitions + */ + WSASYSNOTREADY = (WSABASEERR+91), + WSAVERNOTSUPPORTED = (WSABASEERR+92), + WSANOTINITIALISED = (WSABASEERR+93), + + /* Authoritative Answer: Host not found */ + WSAHOST_NOT_FOUND = (WSABASEERR+1001), + HOST_NOT_FOUND = WSAHOST_NOT_FOUND, + + /* Non-Authoritative: Host not found, or SERVERFAIL */ + WSATRY_AGAIN = (WSABASEERR+1002), + TRY_AGAIN = WSATRY_AGAIN, + + /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */ + WSANO_RECOVERY = (WSABASEERR+1003), + NO_RECOVERY = WSANO_RECOVERY, + + /* Valid name, no data record of requested type */ + WSANO_DATA = (WSABASEERR+1004), + NO_DATA = WSANO_DATA, + + /* no address, look for MX record */ + WSANO_ADDRESS = WSANO_DATA, + NO_ADDRESS = WSANO_ADDRESS +} +/* + * Windows Sockets errors redefined as regular Berkeley error constants + */ enum: int { - WSAEWOULDBLOCK = 10035, - WSAEINTR = 10004, - WSAHOST_NOT_FOUND = 11001, - WSANO_DATA = 11004, + EWOULDBLOCK = WSAEWOULDBLOCK, + EINPROGRESS = WSAEINPROGRESS, + EALREADY = WSAEALREADY, + ENOTSOCK = WSAENOTSOCK, + EDESTADDRREQ = WSAEDESTADDRREQ, + EMSGSIZE = WSAEMSGSIZE, + EPROTOTYPE = WSAEPROTOTYPE, + ENOPROTOOPT = WSAENOPROTOOPT, + EPROTONOSUPPORT = WSAEPROTONOSUPPORT, + ESOCKTNOSUPPORT = WSAESOCKTNOSUPPORT, + EOPNOTSUPP = WSAEOPNOTSUPP, + EPFNOSUPPORT = WSAEPFNOSUPPORT, + EAFNOSUPPORT = WSAEAFNOSUPPORT, + EADDRINUSE = WSAEADDRINUSE, + EADDRNOTAVAIL = WSAEADDRNOTAVAIL, + ENETDOWN = WSAENETDOWN, + ENETUNREACH = WSAENETUNREACH, + ENETRESET = WSAENETRESET, + ECONNABORTED = WSAECONNABORTED, + ECONNRESET = WSAECONNRESET, + ENOBUFS = WSAENOBUFS, + EISCONN = WSAEISCONN, + ENOTCONN = WSAENOTCONN, + ESHUTDOWN = WSAESHUTDOWN, + ETOOMANYREFS = WSAETOOMANYREFS, + ETIMEDOUT = WSAETIMEDOUT, + ECONNREFUSED = WSAECONNREFUSED, + ELOOP = WSAELOOP, + ENAMETOOLONG = WSAENAMETOOLONG, + EHOSTDOWN = WSAEHOSTDOWN, + EHOSTUNREACH = WSAEHOSTUNREACH, + ENOTEMPTY = WSAENOTEMPTY, + EPROCLIM = WSAEPROCLIM, + EUSERS = WSAEUSERS, + EDQUOT = WSAEDQUOT, + ESTALE = WSAESTALE, + EREMOTE = WSAEREMOTE } enum: int @@ -217,16 +333,16 @@ enum: int /// In C/C++, it is redefinable by #define-ing the macro before #include-ing /// winsock.h. In D, use the $(D FD_CREATE) function to allocate a $(D fd_set) /// of an arbitrary size. -const uint FD_SETSIZE = 64; +enum int FD_SETSIZE = 64; struct fd_set_custom(uint SETSIZE) { - UINT fd_count; + uint fd_count; SOCKET[SETSIZE] fd_array; } -alias fd_set_custom!FD_SETSIZE fd_set; +alias fd_set = fd_set_custom!FD_SETSIZE; // Removes. void FD_CLR(SOCKET fd, fd_set* set) @@ -296,8 +412,8 @@ fd_set* FD_CREATE(uint capacity) struct linger { - USHORT l_onoff; - USHORT l_linger; + ushort l_onoff; + ushort l_linger; } @@ -305,7 +421,7 @@ struct protoent { char* p_name; char** p_aliases; - SHORT p_proto; + short p_proto; } @@ -317,11 +433,11 @@ struct servent version (Win64) { char* s_proto; - SHORT s_port; + short s_port; } else { - SHORT s_port; + short s_port; char* s_proto; } } @@ -332,8 +448,8 @@ union in6_addr { private union _u_t { - BYTE[16] Byte; - WORD[8] Word; + ubyte[16] Byte; + ushort[8] Word; } _u_t u; } @@ -341,20 +457,20 @@ union in6_addr struct in_addr6 { - BYTE[16] s6_addr; + ubyte[16] s6_addr; } +/ version(BigEndian) { - uint16_t htons(uint16_t x) + ushort htons(ushort x) { return x; } - uint32_t htonl(uint32_t x) + uint htonl(uint x) { return x; } @@ -364,13 +480,13 @@ else version(LittleEndian) private import core.bitop; - uint16_t htons(uint16_t x) + ushort htons(ushort x) { - return cast(uint16_t)((x >> 8) | (x << 8)); + return cast(ushort)((x >> 8) | (x << 8)); } - uint32_t htonl(uint32_t x) + uint htonl(uint x) { return bswap(x); } @@ -381,13 +497,13 @@ else } -uint16_t ntohs(uint16_t x) +ushort ntohs(ushort x) { return htons(x); } -uint32_t ntohl(uint32_t x) +uint ntohl(uint x) { return htonl(x); } @@ -461,8 +577,8 @@ enum: int struct timeval { - int32_t tv_sec; - int32_t tv_usec; + int tv_sec; + int tv_usec; } @@ -472,33 +588,33 @@ union in_addr { private struct _S_un_b_t { - uint8_t s_b1, s_b2, s_b3, s_b4; + ubyte s_b1, s_b2, s_b3, s_b4; } _S_un_b_t S_un_b; private struct _S_un_w_t { - uint16_t s_w1, s_w2; + ushort s_w1, s_w2; } _S_un_w_t S_un_w; - uint32_t S_addr; + uint S_addr; } _S_un_t S_un; - uint32_t s_addr; + uint s_addr; struct { - uint8_t s_net, s_host; + ubyte s_net, s_host; union { - uint16_t s_imp; + ushort s_imp; struct { - uint8_t s_lh, s_impno; + ubyte s_lh, s_impno; } } } @@ -509,40 +625,40 @@ union in6_addr { private union _in6_u_t { - uint8_t[16] u6_addr8; - uint16_t[8] u6_addr16; - uint32_t[4] u6_addr32; + ubyte[16] u6_addr8; + ushort[8] u6_addr16; + uint[4] u6_addr32; } _in6_u_t in6_u; - uint8_t[16] s6_addr8; - uint16_t[8] s6_addr16; - uint32_t[4] s6_addr32; + ubyte[16] s6_addr8; + ushort[8] s6_addr16; + uint[4] s6_addr32; - alias s6_addr8 s6_addr; + alias s6_addr = s6_addr8; } const in6_addr IN6ADDR_ANY = { s6_addr8: [0] }; const in6_addr IN6ADDR_LOOPBACK = { s6_addr8: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1] }; -//alias IN6ADDR_ANY IN6ADDR_ANY_INIT; -//alias IN6ADDR_LOOPBACK IN6ADDR_LOOPBACK_INIT; +//alias IN6ADDR_ANY_INIT = IN6ADDR_ANY; +//alias IN6ADDR_LOOPBACK_INIT = IN6ADDR_LOOPBACK; -const uint INET_ADDRSTRLEN = 16; -const uint INET6_ADDRSTRLEN = 46; +enum int INET_ADDRSTRLEN = 16; +enum int INET6_ADDRSTRLEN = 46; struct sockaddr { - int16_t sa_family; + short sa_family; ubyte[14] sa_data; } struct sockaddr_in { - int16_t sin_family = AF_INET; - uint16_t sin_port; + short sin_family = AF_INET; + ushort sin_port; in_addr sin_addr; ubyte[8] sin_zero; } @@ -550,20 +666,20 @@ struct sockaddr_in struct sockaddr_in6 { - int16_t sin6_family = AF_INET6; - uint16_t sin6_port; - uint32_t sin6_flowinfo; + short sin6_family = AF_INET6; + ushort sin6_port; + uint sin6_flowinfo; in6_addr sin6_addr; - uint32_t sin6_scope_id; + uint sin6_scope_id; } struct addrinfo { - int32_t ai_flags; - int32_t ai_family; - int32_t ai_socktype; - int32_t ai_protocol; + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; size_t ai_addrlen; char* ai_canonname; sockaddr* ai_addr; @@ -575,8 +691,8 @@ struct hostent { char* h_name; char** h_aliases; - int16_t h_addrtype; - int16_t h_length; + short h_addrtype; + short h_length; char** h_addr_list; @@ -586,23 +702,24 @@ struct hostent } } +// Note: These are Winsock2!! struct WSAOVERLAPPED; -alias WSAOVERLAPPED* LPWSAOVERLAPPED; -alias void function(DWORD, DWORD, LPWSAOVERLAPPED, DWORD) LPWSAOVERLAPPED_COMPLETION_ROUTINE; -int WSAIoctl(SOCKET s, DWORD dwIoControlCode, - LPVOID lpvInBuffer, DWORD cbInBuffer, - LPVOID lpvOutBuffer, DWORD cbOutBuffer, - LPDWORD lpcbBytesReturned, +alias LPWSAOVERLAPPED = WSAOVERLAPPED*; +alias LPWSAOVERLAPPED_COMPLETION_ROUTINE = void function(uint, uint, LPWSAOVERLAPPED, uint); +int WSAIoctl(SOCKET s, uint dwIoControlCode, + void* lpvInBuffer, uint cbInBuffer, + void* lpvOutBuffer, uint cbOutBuffer, + uint* lpcbBytesReturned, LPWSAOVERLAPPED lpOverlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine); -const int IOC_VENDOR = 0x18000000; -const int SIO_KEEPALIVE_VALS = IOC_IN | IOC_VENDOR | 4; +enum IOC_VENDOR = 0x18000000; +enum SIO_KEEPALIVE_VALS = IOC_IN | IOC_VENDOR | 4; /* Argument structure for SIO_KEEPALIVE_VALS */ struct tcp_keepalive { - uint32_t onoff; - uint32_t keepalivetime; - uint32_t keepaliveinterval; + uint onoff; + uint keepalivetime; + uint keepaliveinterval; } diff --git a/libphobos/src/std/complex.d b/libphobos/src/std/complex.d index b3d6d41f8..f64dde707 100644 --- a/libphobos/src/std/complex.d +++ b/libphobos/src/std/complex.d @@ -42,7 +42,7 @@ import std.format, std.math, std.numeric, std.traits; assert (z.im == 3.14L); --- */ -auto complex(T)(T re) @safe pure nothrow if (is(T : double)) +auto complex(T)(T re) @safe pure nothrow @nogc if (is(T : double)) { static if (isFloatingPoint!T) return Complex!T(re, 0); @@ -51,7 +51,7 @@ auto complex(T)(T re) @safe pure nothrow if (is(T : double)) } /// ditto -auto complex(R, I)(R re, I im) @safe pure nothrow +auto complex(R, I)(R re, I im) @safe pure nothrow @nogc if (is(R : double) && is(I : double)) { static if (isFloatingPoint!R || isFloatingPoint!I) @@ -119,7 +119,7 @@ struct Complex(T) if (isFloatingPoint!T) See the $(LINK2 std_format.html, std.format) and $(XREF string, format) documentation for more information. */ - string toString() const /* TODO: pure @safe nothrow */ + string toString() const /* TODO: @safe pure nothrow */ { import std.exception : assumeUnique; char[] buf; @@ -156,25 +156,9 @@ struct Complex(T) if (isFloatingPoint!T) sink("i"); } - /** - * $(RED Deprecated. This function will be removed in March 2014. - * Please use $(XREF string,format) instead.) - * - * Converts the complex number to a string representation. - * - * If a $(D sink) delegate is specified, the string is passed to it - * and this function returns $(D null). Otherwise, this function - * returns the string representation directly. - - * The output format is controlled via $(D formatSpec), which should consist - * of a single POSIX format specifier, including the percent (%) character. - * Note that complex numbers are floating point numbers, so the only - * valid format characters are 'e', 'f', 'g', 'a', and 's', where 's' - * gives the default behaviour. Positional parameters are not valid - * in this context. - * - * See the $(LINK2 std_format.html, std.format) and $(XREF string, format) - * documentation for more information. + /* + * Explicitly undocumented. It will be removed in October 2014. + * Please use $(XREF string,format) instead. */ deprecated("Please use std.string.format instead.") string toString(scope void delegate(const(char)[]) sink, @@ -194,7 +178,7 @@ struct Complex(T) if (isFloatingPoint!T) return null; } -@safe pure nothrow: +@safe pure nothrow @nogc: this(R : T)(Complex!R z) { @@ -267,7 +251,7 @@ struct Complex(T) if (isFloatingPoint!T) // complex op complex Complex!(CommonType!(T,R)) opBinary(string op, R)(Complex!R z) const { - alias typeof(return) C; + alias C = typeof(return); auto w = C(this.re, this.im); return w.opOpAssign!(op)(z); } @@ -276,7 +260,7 @@ struct Complex(T) if (isFloatingPoint!T) Complex!(CommonType!(T,R)) opBinary(string op, R)(R r) const if (isNumeric!R) { - alias typeof(return) C; + alias C = typeof(return); auto w = C(this.re, this.im); return w.opOpAssign!(op)(r); } @@ -300,7 +284,7 @@ struct Complex(T) if (isFloatingPoint!T) if (op == "/" && isNumeric!R) { typeof(return) w; - alias FPTemporary!(typeof(w.re)) Tmp; + alias Tmp = FPTemporary!(typeof(w.re)); if (fabs(re) < fabs(im)) { @@ -649,7 +633,7 @@ unittest assert (z.re == 2.0 && z.im == 2.0); } -unittest +deprecated unittest { // Convert to string. @@ -684,7 +668,7 @@ unittest */ template Complex(T) if (is(T R == Complex!R)) { - alias T Complex; + alias Complex = T; } unittest @@ -706,7 +690,7 @@ unittest /** Calculates the absolute value (or modulus) of a complex number. */ -T abs(T)(Complex!T z) @safe pure nothrow +T abs(T)(Complex!T z) @safe pure nothrow @nogc { return hypot(z.re, z.im); } @@ -720,7 +704,7 @@ unittest /** Calculates the argument (or phase) of a complex number. */ -T arg(T)(Complex!T z) @safe pure nothrow +T arg(T)(Complex!T z) @safe pure nothrow @nogc { return atan2(z.im, z.re); } @@ -734,7 +718,7 @@ unittest /** Returns the complex conjugate of a complex number. */ -Complex!T conj(T)(Complex!T z) @safe pure nothrow +Complex!T conj(T)(Complex!T z) @safe pure nothrow @nogc { return Complex!T(z.re, -z.im); } @@ -748,7 +732,7 @@ unittest /** Constructs a complex number given its absolute value and argument. */ Complex!(CommonType!(T, U)) fromPolar(T, U)(T modulus, U argument) - @safe pure nothrow + @safe pure nothrow @nogc { return Complex!(CommonType!(T,U)) (modulus*std.math.cos(argument), modulus*std.math.sin(argument)); @@ -763,7 +747,7 @@ unittest /** Trigonometric functions. */ -Complex!T sin(T)(Complex!T z) @safe pure nothrow +Complex!T sin(T)(Complex!T z) @safe pure nothrow @nogc { auto cs = expi(z.re); auto csh = coshisinh(z.im); @@ -778,7 +762,7 @@ unittest /// ditto -Complex!T cos(T)(Complex!T z) @safe pure nothrow +Complex!T cos(T)(Complex!T z) @safe pure nothrow @nogc { auto cs = expi(z.re); auto csh = coshisinh(z.im); @@ -801,7 +785,7 @@ unittest{ x87 $(I fsincos) instruction when possible, this function is no faster than calculating cos(y) and sin(y) separately. */ -Complex!real expi(real y) @trusted pure nothrow +Complex!real expi(real y) @trusted pure nothrow @nogc { return Complex!real(std.math.cos(y), std.math.sin(y)); } @@ -817,7 +801,7 @@ unittest /** Square root. */ -Complex!T sqrt(T)(Complex!T z) @safe pure nothrow +Complex!T sqrt(T)(Complex!T z) @safe pure nothrow @nogc { typeof(return) c; real x,y,w,r; diff --git a/libphobos/src/std/concurrency.d b/libphobos/src/std/concurrency.d index 6335e5301..abcc8c197 100644 --- a/libphobos/src/std/concurrency.d +++ b/libphobos/src/std/concurrency.d @@ -142,7 +142,7 @@ private auto map(Op)( Op op ) { - alias ParameterTypeTuple!(Op) Args; + alias Args = ParameterTypeTuple!(Op); static if( Args.length == 1 ) { @@ -163,8 +163,8 @@ private foreach( i, t1; T ) { static assert( isFunctionPointer!t1 || isDelegate!t1 ); - alias ParameterTypeTuple!(t1) a1; - alias ReturnType!(t1) r1; + alias a1 = ParameterTypeTuple!(t1); + alias r1 = ReturnType!(t1); static if( i < T.length - 1 && is( r1 == void ) ) { @@ -175,7 +175,7 @@ private foreach( t2; T[i+1 .. $] ) { static assert( isFunctionPointer!t2 || isDelegate!t2 ); - alias ParameterTypeTuple!(t2) a2; + alias a2 = ParameterTypeTuple!(t2); static assert( !is( a1 == a2 ), "function with arguments " ~ a1.stringof ~ @@ -372,8 +372,8 @@ private template isSpawnable(F, T...) { template isParamsImplicitlyConvertible(F1, F2, int i=0) { - alias ParameterTypeTuple!F1 param1; - alias ParameterTypeTuple!F2 param2; + alias param1 = ParameterTypeTuple!F1; + alias param2 = ParameterTypeTuple!F2; static if (param1.length != param2.length) enum isParamsImplicitlyConvertible = false; else static if (param1.length == i) @@ -626,7 +626,7 @@ private void _send(T...)( MsgType type, Tid tid, T vals ) void receive(T...)( T ops ) in { - assert(mbox !is null, "Cannot receive a message until a thread was spawned " + assert(mbox !is null, "Cannot receive a message until a thread was spawned "~ "or thisTid was passed to a running thread."); } body @@ -673,9 +673,9 @@ unittest private template receiveOnlyRet(T...) { static if( T.length == 1 ) - alias T[0] receiveOnlyRet; + alias receiveOnlyRet = T[0]; else - alias Tuple!(T) receiveOnlyRet; + alias receiveOnlyRet = Tuple!(T); } /** @@ -708,7 +708,7 @@ private template receiveOnlyRet(T...) receiveOnlyRet!(T) receiveOnly(T...)() in { - assert(mbox !is null, "Cannot receive a message until a thread was spawned " + assert(mbox !is null, "Cannot receive a message until a thread was spawned "~ "or thisTid was passed to a running thread."); } body @@ -775,7 +775,7 @@ unittest bool receiveTimeout(T...)( Duration duration, T ops ) in { - assert(mbox !is null, "Cannot receive a message until a thread was spawned " + assert(mbox !is null, "Cannot receive a message until a thread was spawned "~ "or thisTid was passed to a running thread."); } body @@ -926,7 +926,7 @@ static ~this() /** * Associates name with tid in a process-local map. When the thread - * represented by tid termiantes, any names associated with it will be + * represented by tid terminates, any names associated with it will be * automatically unregistered. * * Params: @@ -1127,16 +1127,16 @@ private static if( isImplicitlyConvertible!(T[0], Duration) ) { - alias TypeTuple!(T[1 .. $]) Ops; - alias vals[1 .. $] ops; + alias Ops = TypeTuple!(T[1 .. $]); + alias ops = vals[1 .. $]; assert( vals[0] >= dur!"msecs"(0) ); enum timedWait = true; Duration period = vals[0]; } else { - alias TypeTuple!(T) Ops; - alias vals[0 .. $] ops; + alias Ops = TypeTuple!(T); + alias ops = vals[0 .. $]; enum timedWait = false; } @@ -1144,7 +1144,7 @@ private { foreach( i, t; Ops ) { - alias ParameterTypeTuple!(t) Args; + alias Args = ParameterTypeTuple!(t); auto op = ops[i]; if( msg.convertsTo!(Args) ) @@ -1418,8 +1418,8 @@ private ////////////////////////////////////////////////////////////////////// - alias bool function(Tid) OnMaxFn; - alias List!(Message) ListT; + alias OnMaxFn = bool function(Tid); + alias ListT = List!(Message); private: ////////////////////////////////////////////////////////////////////// diff --git a/libphobos/src/std/container.d b/libphobos/src/std/container.d deleted file mode 100644 index 817dc07b6..000000000 --- a/libphobos/src/std/container.d +++ /dev/null @@ -1,6695 +0,0 @@ -// Written in the D programming language. - -/** -Defines generic _containers. - -Source: $(PHOBOSSRC std/_container.d) -Macros: -WIKI = Phobos/StdContainer -TEXTWITHCOMMAS = $0 - -Copyright: Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code -copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders. - -License: Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE_1_0.txt or copy at $(WEB -boost.org/LICENSE_1_0.txt)). - -Authors: Steven Schveighoffer, $(WEB erdani.com, Andrei Alexandrescu) - -$(BOOKTABLE $(TEXTWITHCOMMAS Container primitives. Below, $(D C) means -a _container type, $(D c) is a value of _container type, $(D n$(SUB -x)) represents the effective length of value $(D x), which could be a -single element (in which case $(D n$(SUB x)) is $(D 1)), a _container, -or a range.), - -$(TR $(TH Syntax) $(TH $(BIGOH ·)) $(TH Description)) - -$(TR $(TDNW $(D C(x))) $(TDNW $(D n$(SUB x))) $(TD Creates a -_container of type $(D C) from either another _container or a range.)) - -$(TR $(TDNW $(D c.dup)) $(TDNW $(D n$(SUB c))) $(TD Returns a -duplicate of the _container.)) - -$(TR $(TDNW $(D c ~ x)) $(TDNW $(D n$(SUB c) + n$(SUB x))) $(TD -Returns the concatenation of $(D c) and $(D r). $(D x) may be a single -element or an input range.)) - -$(TR $(TDNW $(D x ~ c)) $(TDNW $(D n$(SUB c) + n$(SUB x))) $(TD -Returns the concatenation of $(D x) and $(D c). $(D x) may be a -single element or an input range type.)) - -$(LEADINGROW Iteration) - -$(TR $(TD $(D c.Range)) $(TD) $(TD The primary range -type associated with the _container.)) - -$(TR $(TD $(D c[])) $(TDNW $(D log n$(SUB c))) $(TD Returns a range -iterating over the entire _container, in a _container-defined order.)) - -$(TR $(TDNW $(D c[a .. b])) $(TDNW $(D log n$(SUB c))) $(TD Fetches a -portion of the _container from key $(D a) to key $(D b).)) - -$(LEADINGROW Capacity) - -$(TR $(TD $(D c.empty)) $(TD $(D 1)) $(TD Returns $(D true) if the -_container has no elements, $(D false) otherwise.)) - -$(TR $(TD $(D c.length)) $(TDNW $(D log n$(SUB c))) $(TD Returns the -number of elements in the _container.)) - -$(TR $(TDNW $(D c.length = n)) $(TDNW $(D n$(SUB c) + n)) $(TD Forces -the number of elements in the _container to $(D n). If the _container -ends up growing, the added elements are initialized in a -_container-dependent manner (usually with $(D T.init)).)) - -$(TR $(TD $(D c.capacity)) $(TDNW $(D log n$(SUB c))) $(TD Returns the -maximum number of elements that can be stored in the _container -without triggering a reallocation.)) - -$(TR $(TD $(D c.reserve(x))) $(TD $(D n$(SUB c))) $(TD Forces $(D -capacity) to at least $(D x) without reducing it.)) - -$(LEADINGROW Access) - -$(TR $(TDNW $(D c.front)) $(TDNW $(D log n$(SUB c))) $(TD Returns the -first element of the _container, in a _container-defined order.)) - -$(TR $(TDNW $(D c.moveFront)) $(TDNW $(D log n$(SUB c))) $(TD -Destructively reads and returns the first element of the -_container. The slot is not removed from the _container; it is left -initalized with $(D T.init). This routine need not be defined if $(D -front) returns a $(D ref).)) - -$(TR $(TDNW $(D c.front = v)) $(TDNW $(D log n$(SUB c))) $(TD Assigns -$(D v) to the first element of the _container.)) - -$(TR $(TDNW $(D c.back)) $(TDNW $(D log n$(SUB c))) $(TD Returns the -last element of the _container, in a _container-defined order.)) - -$(TR $(TDNW $(D c.moveBack)) $(TDNW $(D log n$(SUB c))) $(TD -Destructively reads and returns the last element of the -container. The slot is not removed from the _container; it is left -initalized with $(D T.init). This routine need not be defined if $(D -front) returns a $(D ref).)) - -$(TR $(TDNW $(D c.back = v)) $(TDNW $(D log n$(SUB c))) $(TD Assigns -$(D v) to the last element of the _container.)) - -$(TR $(TDNW $(D c[x])) $(TDNW $(D log n$(SUB c))) $(TD Provides -indexed access into the _container. The index type is -_container-defined. A container may define several index types (and -consequently overloaded indexing).)) - -$(TR $(TDNW $(D c.moveAt(x))) $(TDNW $(D log n$(SUB c))) $(TD -Destructively reads and returns the value at position $(D x). The slot -is not removed from the _container; it is left initialized with $(D -T.init).)) - -$(TR $(TDNW $(D c[x] = v)) $(TDNW $(D log n$(SUB c))) $(TD Sets -element at specified index into the _container.)) - -$(TR $(TDNW $(D c[x] $(I op)= v)) $(TDNW $(D log n$(SUB c))) -$(TD Performs read-modify-write operation at specified index into the -_container.)) - -$(LEADINGROW Operations) - -$(TR $(TDNW $(D e in c)) $(TDNW $(D log n$(SUB c))) $(TD -Returns nonzero if e is found in $(D c).)) - -$(TR $(TDNW $(D c.lowerBound(v))) $(TDNW $(D log n$(SUB c))) $(TD -Returns a range of all elements strictly less than $(D v).)) - -$(TR $(TDNW $(D c.upperBound(v))) $(TDNW $(D log n$(SUB c))) $(TD -Returns a range of all elements strictly greater than $(D v).)) - -$(TR $(TDNW $(D c.equalRange(v))) $(TDNW $(D log n$(SUB c))) $(TD -Returns a range of all elements in $(D c) that are equal to $(D v).)) - -$(LEADINGROW Modifiers) - -$(TR $(TDNW $(D c ~= x)) $(TDNW $(D n$(SUB c) + n$(SUB x))) -$(TD Appends $(D x) to $(D c). $(D x) may be a single element or an -input range type.)) - -$(TR $(TDNW $(D c.clear())) $(TDNW $(D n$(SUB c))) $(TD Removes all -elements in $(D c).)) - -$(TR $(TDNW $(D c.insert(x))) $(TDNW $(D n$(SUB x) * log n$(SUB c))) -$(TD Inserts $(D x) in $(D c) at a position (or positions) chosen by $(D c).)) - -$(TR $(TDNW $(D c.stableInsert(x))) -$(TDNW $(D n$(SUB x) * log n$(SUB c))) $(TD Same as $(D c.insert(x)), -but is guaranteed to not invalidate any ranges.)) - -$(TR $(TDNW $(D c.linearInsert(v))) $(TDNW $(D n$(SUB c))) $(TD Same -as $(D c.insert(v)) but relaxes complexity to linear.)) - -$(TR $(TDNW $(D c.stableLinearInsert(v))) $(TDNW $(D n$(SUB c))) -$(TD Same as $(D c.stableInsert(v)) but relaxes complexity to linear.)) - -$(TR $(TDNW $(D c.removeAny())) $(TDNW $(D log n$(SUB c))) -$(TD Removes some element from $(D c) and returns it.)) - -$(TR $(TDNW $(D c.stableRemoveAny())) $(TDNW $(D log n$(SUB c))) -$(TD Same as $(D c.removeAny()), but is guaranteed to not invalidate any -iterators.)) - -$(TR $(TDNW $(D c.insertFront(v))) $(TDNW $(D log n$(SUB c))) -$(TD Inserts $(D v) at the front of $(D c).)) - -$(TR $(TDNW $(D c.stableInsertFront(v))) $(TDNW $(D log n$(SUB c))) -$(TD Same as $(D c.insertFront(v)), but guarantees no ranges will be -invalidated.)) - -$(TR $(TDNW $(D c.insertBack(v))) $(TDNW $(D log n$(SUB c))) -$(TD Inserts $(D v) at the back of $(D c).)) - -$(TR $(TDNW $(D c.stableInsertBack(v))) $(TDNW $(D log n$(SUB c))) -$(TD Same as $(D c.insertBack(v)), but guarantees no ranges will be -invalidated.)) - -$(TR $(TDNW $(D c.removeFront())) $(TDNW $(D log n$(SUB c))) -$(TD Removes the element at the front of $(D c).)) - -$(TR $(TDNW $(D c.stableRemoveFront())) $(TDNW $(D log n$(SUB c))) -$(TD Same as $(D c.removeFront()), but guarantees no ranges will be -invalidated.)) - -$(TR $(TDNW $(D c.removeBack())) $(TDNW $(D log n$(SUB c))) -$(TD Removes the value at the back of $(D c).)) - -$(TR $(TDNW $(D c.stableRemoveBack())) $(TDNW $(D log n$(SUB c))) -$(TD Same as $(D c.removeBack()), but guarantees no ranges will be -invalidated.)) - -$(TR $(TDNW $(D c.remove(r))) $(TDNW $(D n$(SUB r) * log n$(SUB c))) -$(TD Removes range $(D r) from $(D c).)) - -$(TR $(TDNW $(D c.stableRemove(r))) -$(TDNW $(D n$(SUB r) * log n$(SUB c))) -$(TD Same as $(D c.remove(r)), but guarantees iterators are not -invalidated.)) - -$(TR $(TDNW $(D c.linearRemove(r))) $(TDNW $(D n$(SUB c))) -$(TD Removes range $(D r) from $(D c).)) - -$(TR $(TDNW $(D c.stableLinearRemove(r))) $(TDNW $(D n$(SUB c))) -$(TD Same as $(D c.linearRemove(r)), but guarantees iterators are not -invalidated.)) - -$(TR $(TDNW $(D c.removeKey(k))) $(TDNW $(D log n$(SUB c))) -$(TD Removes an element from $(D c) by using its key $(D k). -The key's type is defined by the _container.)) - -$(TR $(TDNW $(D )) $(TDNW $(D )) $(TD )) - -) - */ -module std.container; - -import core.exception, core.memory, core.stdc.stdlib, core.stdc.string, - std.algorithm, std.conv, std.exception, std.functional, std.range, - std.traits, std.typecons, std.typetuple; -version(unittest) import std.stdio; - -version(unittest) version = RBDoChecks; - -//version = RBDoChecks; - -version(RBDoChecks) -{ - import std.stdio; -} - - - -/* The following documentation and type $(D TotalContainer) are -intended for developers only. - -$(D TotalContainer) is an unimplemented container that illustrates a -host of primitives that a container may define. It is to some extent -the bottom of the conceptual container hierarchy. A given container -most often will choose to only implement a subset of these primitives, -and define its own additional ones. Adhering to the standard primitive -names below allows generic code to work independently of containers. - -Things to remember: any container must be a reference type, whether -implemented as a $(D class) or $(D struct). No primitive below -requires the container to escape addresses of elements, which means -that compliant containers can be defined to use reference counting or -other deterministic memory management techniques. - -A container may choose to define additional specific operations. The -only requirement is that those operations bear different names than -the ones below, lest user code gets confused. - -Complexity of operations should be interpreted as "at least as good -as". If an operation is required to have $(BIGOH n) complexity, it -could have anything lower than that, e.g. $(BIGOH log(n)). Unless -specified otherwise, $(D n) inside a $(BIGOH) expression stands for -the number of elements in the container. - */ -struct TotalContainer(T) -{ -/** -If the container has a notion of key-value mapping, $(D KeyType) -defines the type of the key of the container. - */ - alias T KeyType; - -/** -If the container has a notion of multikey-value mapping, $(D -KeyTypes[k]), where $(D k) is a zero-based unsigned number, defines -the type of the $(D k)th key of the container. - -A container may define both $(D KeyType) and $(D KeyTypes), e.g. in -the case it has the notion of primary/preferred key. - */ - alias TypeTuple!T KeyTypes; - -/** -If the container has a notion of key-value mapping, $(D ValueType) -defines the type of the value of the container. Typically, a map-style -container mapping values of type $(D K) to values of type $(D V) -defines $(D KeyType) to be $(D K) and $(D ValueType) to be $(D V). - */ - alias T ValueType; - -/** -Defines the container's primary range, which embodies one of the -ranges defined in $(XREFMODULE range). - -Generally a container may define several types of ranges. - */ - struct Range - { - /// Range primitives. - @property bool empty() - { - assert(0); - } - /// Ditto - @property T front() - { - assert(0); - } - /// Ditto - T moveFront() - { - assert(0); - } - /// Ditto - void popFront() - { - assert(0); - } - /// Ditto - @property T back() - { - assert(0); - } - /// Ditto - T moveBack() - { - assert(0); - } - /// Ditto - void popBack() - { - assert(0); - } - /// Ditto - T opIndex(size_t i) - { - assert(0); - } - /// Ditto - void opIndexAssign(T value, size_t i) - { - assert(0); - } - /// Ditto - void opIndexOpAssign(string op)(T value, uint i) - { - assert(0); - } - /// Ditto - T moveAt(size_t i) - { - assert(0); - } - /// Ditto - @property size_t length() - { - assert(0); - } - } - -/** -Property returning $(D true) if and only if the container has no -elements. - -Complexity: $(BIGOH 1) - */ - @property bool empty() - { - assert(0); - } - -/** -Returns a duplicate of the container. The elements themselves are not -transitively duplicated. - -Complexity: $(BIGOH n). - */ - @property TotalContainer dup() - { - assert(0); - } - -/** -Returns the number of elements in the container. - -Complexity: $(BIGOH log(n)). -*/ - @property size_t length() - { - assert(0); - } - -/** -Returns the maximum number of elements the container can store without -(a) allocating memory, (b) invalidating iterators upon insertion. - -Complexity: $(BIGOH log(n)). - */ - @property size_t capacity() - { - assert(0); - } - -/** -Ensures sufficient capacity to accommodate $(D n) elements. - -Postcondition: $(D capacity >= n) - -Complexity: $(BIGOH log(e - capacity)) if $(D e > capacity), otherwise -$(BIGOH 1). - */ - void reserve(size_t e) - { - assert(0); - } - -/** -Returns a range that iterates over all elements of the container, in a -container-defined order. The container should choose the most -convenient and fast method of iteration for $(D opSlice()). - -Complexity: $(BIGOH log(n)) - */ - Range opSlice() - { - assert(0); - } - - /** - Returns a range that iterates the container between two - specified positions. - - Complexity: $(BIGOH log(n)) - */ - Range opSlice(size_t a, size_t b) - { - assert(0); - } - -/** -Forward to $(D opSlice().front) and $(D opSlice().back), respectively. - -Complexity: $(BIGOH log(n)) - */ - @property T front() - { - assert(0); - } - /// Ditto - T moveFront() - { - assert(0); - } - /// Ditto - @property T back() - { - assert(0); - } - /// Ditto - T moveBack() - { - assert(0); - } - -/** -Indexing operators yield or modify the value at a specified index. - */ - /** - Indexing operators yield or modify the value at a specified index. - */ - ValueType opIndex(KeyType) - { - assert(0); - } - /// ditto - void opIndexAssign(KeyType) - { - assert(0); - } - /// ditto - void opIndexOpAssign(string op)(KeyType) - { - assert(0); - } - T moveAt(size_t i) - { - assert(0); - } - -/** -$(D k in container) returns true if the given key is in the container. - */ - bool opBinary(string op)(KeyType k) if (op == "in") - { - assert(0); - } - -/** -Returns a range of all elements containing $(D k) (could be empty or a -singleton range). - */ - Range equalRange(KeyType k) - { - assert(0); - } - -/** -Returns a range of all elements with keys less than $(D k) (could be -empty or a singleton range). Only defined by containers that store -data sorted at all times. - */ - Range lowerBound(KeyType k) - { - assert(0); - } - -/** -Returns a range of all elements with keys larger than $(D k) (could be -empty or a singleton range). Only defined by containers that store -data sorted at all times. - */ - Range upperBound(KeyType k) - { - assert(0); - } - -/** -Returns a new container that's the concatenation of $(D this) and its -argument. $(D opBinaryRight) is only defined if $(D Stuff) does not -define $(D opBinary). - -Complexity: $(BIGOH n + m), where m is the number of elements in $(D -stuff) - */ - TotalContainer opBinary(string op)(Stuff rhs) if (op == "~") - { - assert(0); - } - - /// ditto - TotalContainer opBinaryRight(string op)(Stuff lhs) if (op == "~") - { - assert(0); - } - -/** -Forwards to $(D insertAfter(this[], stuff)). - */ - void opOpAssign(string op)(Stuff stuff) if (op == "~") - { - assert(0); - } - -/** -Removes all contents from the container. The container decides how $(D -capacity) is affected. - -Postcondition: $(D empty) - -Complexity: $(BIGOH n) - */ - void clear() - { - assert(0); - } - -/** -Sets the number of elements in the container to $(D newSize). If $(D -newSize) is greater than $(D length), the added elements are added to -unspecified positions in the container and initialized with $(D -.init). - -Complexity: $(BIGOH abs(n - newLength)) - -Postcondition: $(D _length == newLength) - */ - @property void length(size_t newLength) - { - assert(0); - } - -/** -Inserts $(D stuff) in an unspecified position in the -container. Implementations should choose whichever insertion means is -the most advantageous for the container, but document the exact -behavior. $(D stuff) can be a value convertible to the element type of -the container, or a range of values convertible to it. - -The $(D stable) version guarantees that ranges iterating over the -container are never invalidated. Client code that counts on -non-invalidating insertion should use $(D stableInsert). Such code would -not compile against containers that don't support it. - -Returns: The number of elements added. - -Complexity: $(BIGOH m * log(n)), where $(D m) is the number of -elements in $(D stuff) - */ - size_t insert(Stuff)(Stuff stuff) - { - assert(0); - } - ///ditto - size_t stableInsert(Stuff)(Stuff stuff) - { - assert(0); - } - -/** -Same as $(D insert(stuff)) and $(D stableInsert(stuff)) respectively, -but relax the complexity constraint to linear. - */ - size_t linearInsert(Stuff)(Stuff stuff) - { - assert(0); - } - ///ditto - size_t stableLinearInsert(Stuff)(Stuff stuff) - { - assert(0); - } - -/** -Picks one value in an unspecified position in the container, removes -it from the container, and returns it. Implementations should pick the -value that's the most advantageous for the container, but document the -exact behavior. The stable version behaves the same, but guarantees that -ranges iterating over the container are never invalidated. - -Precondition: $(D !empty) - -Returns: The element removed. - -Complexity: $(BIGOH log(n)). - */ - T removeAny() - { - assert(0); - } - /// ditto - T stableRemoveAny() - { - assert(0); - } - -/** -Inserts $(D value) to the front or back of the container. $(D stuff) -can be a value convertible to the container's element type or a range -of values convertible to it. The stable version behaves the same, but -guarantees that ranges iterating over the container are never -invalidated. - -Returns: The number of elements inserted - -Complexity: $(BIGOH log(n)). - */ - size_t insertFront(Stuff)(Stuff stuff) - { - assert(0); - } - /// ditto - size_t stableInsertFront(Stuff)(Stuff stuff) - { - assert(0); - } - /// ditto - size_t insertBack(Stuff)(Stuff stuff) - { - assert(0); - } - /// ditto - size_t stableInsertBack(T value) - { - assert(0); - } - -/** -Removes the value at the front or back of the container. The stable -version behaves the same, but guarantees that ranges iterating over -the container are never invalidated. The optional parameter $(D -howMany) instructs removal of that many elements. If $(D howMany > n), -all elements are removed and no exception is thrown. - -Precondition: $(D !empty) - -Complexity: $(BIGOH log(n)). - */ - void removeFront() - { - assert(0); - } - /// ditto - void stableRemoveFront() - { - assert(0); - } - /// ditto - void removeBack() - { - assert(0); - } - /// ditto - void stableRemoveBack() - { - assert(0); - } - -/** -Removes $(D howMany) values at the front or back of the -container. Unlike the unparameterized versions above, these functions -do not throw if they could not remove $(D howMany) elements. Instead, -if $(D howMany > n), all elements are removed. The returned value is -the effective number of elements removed. The stable version behaves -the same, but guarantees that ranges iterating over the container are -never invalidated. - -Returns: The number of elements removed - -Complexity: $(BIGOH howMany * log(n)). - */ - size_t removeFront(size_t howMany) - { - assert(0); - } - /// ditto - size_t stableRemoveFront(size_t howMany) - { - assert(0); - } - /// ditto - size_t removeBack(size_t howMany) - { - assert(0); - } - /// ditto - size_t stableRemoveBack(size_t howMany) - { - assert(0); - } - -/** -Removes all values corresponding to key $(D k). - -Complexity: $(BIGOH m * log(n)), where $(D m) is the number of -elements with the same key. - -Returns: The number of elements removed. - */ - size_t removeKey(KeyType k) - { - assert(0); - } - -/** -Inserts $(D stuff) before, after, or instead range $(D r), which must -be a valid range previously extracted from this container. $(D stuff) -can be a value convertible to the container's element type or a range -of objects convertible to it. The stable version behaves the same, but -guarantees that ranges iterating over the container are never -invalidated. - -Returns: The number of values inserted. - -Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff) - */ - size_t insertBefore(Stuff)(Range r, Stuff stuff) - { - assert(0); - } - /// ditto - size_t stableInsertBefore(Stuff)(Range r, Stuff stuff) - { - assert(0); - } - /// ditto - size_t insertAfter(Stuff)(Range r, Stuff stuff) - { - assert(0); - } - /// ditto - size_t stableInsertAfter(Stuff)(Range r, Stuff stuff) - { - assert(0); - } - /// ditto - size_t replace(Stuff)(Range r, Stuff stuff) - { - assert(0); - } - /// ditto - size_t stableReplace(Stuff)(Range r, Stuff stuff) - { - assert(0); - } - -/** -Removes all elements belonging to $(D r), which must be a range -obtained originally from this container. The stable version behaves the -same, but guarantees that ranges iterating over the container are -never invalidated. - -Returns: A range spanning the remaining elements in the container that -initially were right after $(D r). - -Complexity: $(BIGOH m * log(n)), where $(D m) is the number of -elements in $(D r) - */ - Range remove(Range r) - { - assert(0); - } - /// ditto - Range stableRemove(Range r) - { - assert(0); - } - -/** -Same as $(D remove) above, but has complexity relaxed to linear. - -Returns: A range spanning the remaining elements in the container that -initially were right after $(D r). - -Complexity: $(BIGOH n) - */ - Range linearRemove(Range r) - { - assert(0); - } - /// ditto - Range stableLinearRemove(Range r) - { - assert(0); - } -} - -unittest { - TotalContainer!int test; -} - -/** -Returns an initialized object. This function is mainly for eliminating -construction differences between structs and classes. It allows code to not -worry about whether the type it's constructing is a struct or a class. - -Examples: --------------------- -auto arr = make!(Array!int)([4, 2, 3, 1]); -assert(equal(arr[], [4, 2, 3, 1])); - -auto rbt = make!(RedBlackTree!(int, "a > b"))([4, 2, 3, 1]); -assert(equal(rbt[], [4, 3, 2, 1])); - -alias make!(DList!int) makeList; -auto list = makeList([1, 7, 42]); -assert(equal(list[], [1, 7, 42])); --------------------- - */ -template make(T) -if (is(T == struct) || is(T == class)) -{ - T make(Args...)(Args arguments) - if (is(T == struct) && __traits(compiles, T(arguments))) - { - return T(arguments); - } - - T make(Args...)(Args arguments) - if (is(T == class) && __traits(compiles, new T(arguments))) - { - return new T(arguments); - } -} - -//Verify Examples. -unittest -{ - auto arr = make!(Array!int)([4, 2, 3, 1]); - assert(equal(arr[], [4, 2, 3, 1])); - - auto rbt = make!(RedBlackTree!(int, "a > b"))([4, 2, 3, 1]); - assert(equal(rbt[], [4, 3, 2, 1])); - - alias make!(DList!int) makeList; - auto list = makeList([1, 7, 42]); - assert(equal(list[], [1, 7, 42])); -} - -unittest -{ - auto arr1 = make!(Array!dchar)(); - assert(arr1.empty); - auto arr2 = make!(Array!dchar)("hello"d); - assert(equal(arr2[], "hello"d)); - - auto rtb1 = make!(RedBlackTree!dchar)(); - assert(rtb1.empty); - auto rtb2 = make!(RedBlackTree!dchar)('h', 'e', 'l', 'l', 'o'); - assert(equal(rtb2[], "ehlo"d)); -} - -/** - Implements a simple and fast singly-linked list. - */ -struct SList(T) -{ - private struct Node - { - T _payload; - Node * _next; - this(T a, Node* b) { _payload = a; _next = b; } - } - private Node * _root; - - private static Node * findLastNode(Node * n) - { - assert(n); - auto ahead = n._next; - while (ahead) - { - n = ahead; - ahead = n._next; - } - return n; - } - - private static Node * findLastNode(Node * n, size_t limit) - { - assert(n && limit); - auto ahead = n._next; - while (ahead) - { - if (!--limit) break; - n = ahead; - ahead = n._next; - } - return n; - } - - private static Node * findNode(Node * n, Node * findMe) - { - assert(n); - auto ahead = n._next; - while (ahead != findMe) - { - n = ahead; - enforce(n); - ahead = n._next; - } - return n; - } - -/** -Constructor taking a number of nodes - */ - this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) - { - insertFront(values); - } - -/** -Constructor taking an input range - */ - this(Stuff)(Stuff stuff) - if (isInputRange!Stuff - && isImplicitlyConvertible!(ElementType!Stuff, T) - && !is(Stuff == T[])) - { - insertFront(stuff); - } - -/** -Comparison for equality. - -Complexity: $(BIGOH min(n, n1)) where $(D n1) is the number of -elements in $(D rhs). - */ - bool opEquals(const SList rhs) const - { - return opEquals(rhs); - } - - /// ditto - bool opEquals(ref const SList rhs) const - { - const(Node) * n1 = _root, n2 = rhs._root; - - for (;; n1 = n1._next, n2 = n2._next) - { - if (!n1) return !n2; - if (!n2 || n1._payload != n2._payload) return false; - } - } - -/** -Defines the container's primary range, which embodies a forward range. - */ - struct Range - { - private Node * _head; - private this(Node * p) { _head = p; } - - /// Input range primitives. - @property bool empty() const { return !_head; } - - /// ditto - @property T front() - { - assert(!empty, "SList.Range.front: Range is empty"); - return _head._payload; - } - - /// ditto - static if (isAssignable!(T, T)) - { - @property void front(T value) - { - assert(!empty, "SList.Range.front: Range is empty"); - move(value, _head._payload); - } - } - - /// ditto - void popFront() - { - assert(!empty, "SList.Range.popFront: Range is empty"); - _head = _head._next; - } - - /// Forward range primitive. - @property Range save() { return this; } - - T moveFront() - { - assert(!empty, "SList.Range.moveFront: Range is empty"); - return move(_head._payload); - } - - bool sameHead(Range rhs) - { - return _head && _head == rhs._head; - } - } - - unittest - { - static assert(isForwardRange!Range); - } - -/** -Property returning $(D true) if and only if the container has no -elements. - -Complexity: $(BIGOH 1) - */ - @property bool empty() const - { - return _root is null; - } - -/** -Duplicates the container. The elements themselves are not transitively -duplicated. - -Complexity: $(BIGOH n). - */ - @property SList dup() - { - return SList(this[]); - } - -/** -Returns a range that iterates over all elements of the container, in -forward order. - -Complexity: $(BIGOH 1) - */ - Range opSlice() - { - return Range(_root); - } - -/** -Forward to $(D opSlice().front). - -Complexity: $(BIGOH 1) - */ - @property T front() - { - assert(!empty, "SList.front: List is empty"); - return _root._payload; - } - -/** -Forward to $(D opSlice().front(value)). - -Complexity: $(BIGOH 1) - */ - static if (isAssignable!(T, T)) - { - @property void front(T value) - { - assert(!empty, "SList.front: List is empty"); - move(value, _root._payload); - } - } - - unittest - { - auto s = SList!int(1, 2, 3); - s.front = 42; - assert(s == SList!int(42, 2, 3)); - } - -/** -Returns a new $(D SList) that's the concatenation of $(D this) and its -argument. $(D opBinaryRight) is only defined if $(D Stuff) does not -define $(D opBinary). - */ - SList opBinary(string op, Stuff)(Stuff rhs) - if (op == "~" && is(typeof(SList(rhs)))) - { - auto toAdd = SList(rhs); - static if (is(Stuff == SList)) - { - toAdd = toAdd.dup; - } - if (empty) return toAdd; - // TODO: optimize - auto result = dup; - auto n = findLastNode(result._root); - n._next = toAdd._root; - return result; - } - -/** -Removes all contents from the $(D SList). - -Postcondition: $(D empty) - -Complexity: $(BIGOH 1) - */ - void clear() - { - _root = null; - } - -/** -Inserts $(D stuff) to the front of the container. $(D stuff) can be a -value convertible to $(D T) or a range of objects convertible to $(D -T). The stable version behaves the same, but guarantees that ranges -iterating over the container are never invalidated. - -Returns: The number of elements inserted - -Complexity: $(BIGOH m), where $(D m) is the length of $(D stuff) - */ - size_t insertFront(Stuff)(Stuff stuff) - if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - size_t result; - Node * n, newRoot; - foreach (item; stuff) - { - auto newNode = new Node(item, null); - (newRoot ? n._next : newRoot) = newNode; - n = newNode; - ++result; - } - if (!n) return 0; - // Last node points to the old root - n._next = _root; - _root = newRoot; - return result; - } - - /// ditto - size_t insertFront(Stuff)(Stuff stuff) - if (isImplicitlyConvertible!(Stuff, T)) - { - auto newRoot = new Node(stuff, _root); - _root = newRoot; - return 1; - } - -/// ditto - alias insertFront insert; - -/// ditto - alias insert stableInsert; - - /// ditto - alias insertFront stableInsertFront; - -/** -Picks one value from the front of the container, removes it from the -container, and returns it. - -Precondition: $(D !empty) - -Returns: The element removed. - -Complexity: $(BIGOH 1). - */ - T removeAny() - { - assert(!empty, "SList.removeAny: List is empty"); - auto result = move(_root._payload); - _root = _root._next; - return result; - } - /// ditto - alias removeAny stableRemoveAny; - -/** -Removes the value at the front of the container. The stable version -behaves the same, but guarantees that ranges iterating over the -container are never invalidated. - -Precondition: $(D !empty) - -Complexity: $(BIGOH 1). - */ - void removeFront() - { - assert(!empty, "SList.removeFront: List is empty"); - _root = _root._next; - } - - /// ditto - alias removeFront stableRemoveFront; - -/** -Removes $(D howMany) values at the front or back of the -container. Unlike the unparameterized versions above, these functions -do not throw if they could not remove $(D howMany) elements. Instead, -if $(D howMany > n), all elements are removed. The returned value is -the effective number of elements removed. The stable version behaves -the same, but guarantees that ranges iterating over the container are -never invalidated. - -Returns: The number of elements removed - -Complexity: $(BIGOH howMany * log(n)). - */ - size_t removeFront(size_t howMany) - { - size_t result; - while (_root && result < howMany) - { - _root = _root._next; - ++result; - } - return result; - } - - /// ditto - alias removeFront stableRemoveFront; - -/** -Inserts $(D stuff) after range $(D r), which must be a range -previously extracted from this container. Given that all ranges for a -list end at the end of the list, this function essentially appends to -the list and uses $(D r) as a potentially fast way to reach the last -node in the list. Ideally $(D r) is positioned near or at the last -element of the list. - -$(D stuff) can be a value convertible to $(D T) or a range of objects -convertible to $(D T). The stable version behaves the same, but -guarantees that ranges iterating over the container are never -invalidated. - -Returns: The number of values inserted. - -Complexity: $(BIGOH k + m), where $(D k) is the number of elements in -$(D r) and $(D m) is the length of $(D stuff). - -Examples: --------------------- -auto sl = SList!string(["a", "b", "d"]); -sl.insertAfter(sl[], "e"); // insert at the end (slowest) -assert(std.algorithm.equal(sl[], ["a", "b", "d", "e"])); -sl.insertAfter(std.range.take(sl[], 2), "c"); // insert after "b" -assert(std.algorithm.equal(sl[], ["a", "b", "c", "d", "e"])); --------------------- - */ - - size_t insertAfter(Stuff)(Range r, Stuff stuff) - { - if (!_root) - { - enforce(!r._head); - return insertFront(stuff); - } - enforce(r._head); - auto n = findLastNode(r._head); - SList tmp; - auto result = tmp.insertFront(stuff); - n._next = tmp._root; - return result; - } - -/** -Similar to $(D insertAfter) above, but accepts a range bounded in -count. This is important for ensuring fast insertions in the middle of -the list. For fast insertions after a specified position $(D r), use -$(D insertAfter(take(r, 1), stuff)). The complexity of that operation -only depends on the number of elements in $(D stuff). - -Precondition: $(D r.original.empty || r.maxLength > 0) - -Returns: The number of values inserted. - -Complexity: $(BIGOH k + m), where $(D k) is the number of elements in -$(D r) and $(D m) is the length of $(D stuff). - */ - size_t insertAfter(Stuff)(Take!Range r, Stuff stuff) - { - auto orig = r.source; - if (!orig._head) - { - // Inserting after a null range counts as insertion to the - // front - return insertFront(stuff); - } - enforce(!r.empty); - // Find the last valid element in the range - foreach (i; 1 .. r.maxLength) - { - if (!orig._head._next) break; - orig.popFront(); - } - // insert here - SList tmp; - tmp._root = orig._head._next; - auto result = tmp.insertFront(stuff); - orig._head._next = tmp._root; - return result; - } - -/// ditto - alias insertAfter stableInsertAfter; - -/** -Removes a range from the list in linear time. - -Returns: An empty range. - -Complexity: $(BIGOH n) - */ - Range linearRemove(Range r) - { - if (!_root) - { - enforce(!r._head); - return this[]; - } - auto n = findNode(_root, r._head); - n._next = null; - return Range(null); - } - -/** -Removes a $(D Take!Range) from the list in linear time. - -Returns: A range comprehending the elements after the removed range. - -Complexity: $(BIGOH n) - */ - Range linearRemove(Take!Range r) - { - auto orig = r.source; - // We have something to remove here - if (orig._head == _root) - { - // remove straight from the head of the list - for (; !r.empty; r.popFront()) - { - removeFront(); - } - return this[]; - } - if (!r.maxLength) - { - // Nothing to remove, return the range itself - return orig; - } - // Remove from somewhere in the middle of the list - enforce(_root); - auto n1 = findNode(_root, orig._head); - auto n2 = findLastNode(orig._head, r.maxLength); - n1._next = n2._next; - return Range(n1._next); - } - -/// ditto - alias linearRemove stableLinearRemove; -} - -unittest -{ - auto s = make!(SList!int)(1, 2, 3); - auto n = s.findLastNode(s._root); - assert(n && n._payload == 3); -} - -unittest -{ - auto s = SList!int(1, 2, 5, 10); - assert(walkLength(s[]) == 4); -} - -unittest -{ - auto src = take([0, 1, 2, 3], 3); - auto s = SList!int(src); - assert(s == SList!int(0, 1, 2)); -} - -unittest -{ - auto a = SList!int(1, 2, 3); - auto b = SList!int(4, 5, 6); - // @@@BUG@@@ in compiler - //auto c = a ~ b; - auto d = [ 4, 5, 6 ]; - auto e = a ~ d; - assert(e == SList!int(1, 2, 3, 4, 5, 6)); -} - -unittest -{ - auto a = SList!int(1, 2, 3); - auto c = a ~ 4; - assert(c == SList!int(1, 2, 3, 4)); -} - -unittest -{ - auto s = SList!int(1, 2, 3, 4); - s.insertFront([ 42, 43 ]); - assert(s == SList!int(42, 43, 1, 2, 3, 4)); -} - -unittest -{ - auto s = SList!int(1, 2, 3); - assert(s.removeAny() == 1); - assert(s == SList!int(2, 3)); - assert(s.stableRemoveAny() == 2); - assert(s == SList!int(3)); -} - -unittest -{ - auto s = SList!int(1, 2, 3); - s.removeFront(); - assert(equal(s[], [2, 3])); - s.stableRemoveFront(); - assert(equal(s[], [3])); -} - -unittest -{ - auto s = SList!int(1, 2, 3, 4, 5, 6, 7); - assert(s.removeFront(3) == 3); - assert(s == SList!int(4, 5, 6, 7)); -} - -unittest -{ - auto a = SList!int(1, 2, 3); - auto b = SList!int(1, 2, 3); - assert(a.insertAfter(a[], b[]) == 3); -} - -unittest -{ - auto s = SList!int(1, 2, 3, 4); - auto r = take(s[], 2); - assert(s.insertAfter(r, 5) == 1); - assert(s == SList!int(1, 2, 5, 3, 4)); -} - -unittest -{ - // insertAfter documentation example - auto sl = SList!string(["a", "b", "d"]); - sl.insertAfter(sl[], "e"); // insert at the end (slowest) - assert(std.algorithm.equal(sl[], ["a", "b", "d", "e"])); - sl.insertAfter(std.range.take(sl[], 2), "c"); // insert after "b" - assert(std.algorithm.equal(sl[], ["a", "b", "c", "d", "e"])); -} - -unittest -{ - auto s = SList!int(1, 2, 3, 4, 5); - auto r = s[]; - popFrontN(r, 3); - auto r1 = s.linearRemove(r); - assert(s == SList!int(1, 2, 3)); - assert(r1.empty); -} - -unittest -{ - auto s = SList!int(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - auto r = s[]; - popFrontN(r, 3); - auto r1 = take(r, 4); - assert(equal(r1, [4, 5, 6, 7])); - auto r2 = s.linearRemove(r1); - assert(s == SList!int(1, 2, 3, 8, 9, 10)); - assert(equal(r2, [8, 9, 10])); -} - -unittest -{ - auto lst = SList!int(1, 5, 42, 9); - assert(!lst.empty); - assert(lst.front == 1); - assert(walkLength(lst[]) == 4); - - auto lst2 = lst ~ [ 1, 2, 3 ]; - assert(walkLength(lst2[]) == 7); - - auto lst3 = lst ~ [ 7 ]; - assert(walkLength(lst3[]) == 5); -} - -unittest -{ - auto s = make!(SList!int)(1, 2, 3); -} - -unittest -{ - // 5193 - static struct Data - { - const int val; - } - SList!Data list; -} - -unittest -{ - auto s = SList!int([1, 2, 3]); - s.front = 5; //test frontAssign - assert(s.front == 5); - auto r = s[]; - r.front = 1; //test frontAssign - assert(r.front == 1); -} - -/** -Implements a doubly-linked list. - -$(D DList) uses neither reference nor value semantics. They can be seen as -several different handles into an external chain of nodes. Several different -$(D DList)s can all reference different points in a same chain. - -$(D DList.Range) is, for all intents and purposes, a DList with range -semantics. The $(D DList.Range) has a view directly into the chain itself. -It is not tied to its parent $(D DList), and may be used to operate on -other lists (that point to the same chain). - -The ONLY operation that can invalidate a $(D DList) or $(D DList.Range), but -which will invalidate BOTH, is the $(D remove) operation, if the cut Range -overlaps with the boundaries of another DList or DList.Range. - -Example: ----- -auto a = DList!int([3, 4]); //Create a new chain -auto b = a; //Point to the same chain -// (3 - 4) -assert(a[].equal([3, 4])); -assert(b[].equal([3, 4])); - -b.stableInsertFront(1); //insert before of b -b.stableInsertBack(5); //insert after of b -// (2 - (3 - 4) - 5) -assert(a[].equal([3, 4])); //a is not changed -assert(b[].equal([1, 3, 4, 5])); // but b is changed - -a.stableInsertFront(2); //insert in front of a, this will insert "inside" the chain -// (1 - (2 - 3 - 4) - 5) -assert(a[].equal([2, 3, 4])); //a is modified -assert(b[].equal([1, 2, 3, 4, 5])); //and so is b; - -a.remove(a[]); //remove all the elements of a: This will cut them from the chain; -// (1 - 5) -assert(a[].empty); //a is empty -assert(b[].equal([1, 5])); //b has lost some of its elements; - -a.insert(2); //insert in a. This will create a new chain -// (2) -// (1 - 5) -assert(a[].equal([2])); //a is a new chain -assert(b[].equal([1, 5])); //b is unchanged; ----- - */ -struct DList(T) -{ - private struct Node - { - T _payload; - Node * _prev; - Node * _next; - this(T a, Node* p, Node* n) - { - _payload = a; - _prev = p; _next = n; - if (p) p._next = &this; - if (n) n._prev = &this; - } - } - private Node * _first; - private Node * _last; - -/** -Constructor taking a number of nodes - */ - this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) - { - insertBack(values); - } - -/** -Constructor taking an input range - */ - this(Stuff)(Stuff stuff) - if (isInputRange!Stuff - && isImplicitlyConvertible!(ElementType!Stuff, T) - && !is(Stuff == T[])) - { - insertBack(stuff); - } - -/** -Comparison for equality. - -Complexity: $(BIGOH min(n, n1)) where $(D n1) is the number of -elements in $(D rhs). - */ - bool opEquals(ref const DList rhs) const - { - if(_first == rhs._first) return _last == rhs._last; - if(_last == rhs._last) return false; - - const(Node)* nthis = _first, nrhs = rhs._first; - while(true) - { - if (!nthis) return !nrhs; - if (!nrhs || nthis._payload != nrhs._payload) return false; - nthis = nthis._next; - nrhs = nrhs._next; - } - } - - /** - Defines the container's primary range, which embodies a bidirectional range. - */ - struct Range - { - private Node * _first; - private Node * _last; - private this(Node* first, Node* last) - { - assert(!!_first == !!_last, "Dlist.Rangethis: Invalid arguments"); - _first = first; _last = last; - } - private this(Node* n) { _first = _last = n; } - - /// Input range primitives. - @property const nothrow - bool empty() - { - assert(!!_first == !!_last, "DList.Range: chain is in an inconsistent state (maybe it was cut?)"); - return !_first; - } - - /// ditto - @property T front() - { - assert(!empty, "DList.Range.front: Range is empty"); - return _first._payload; - } - - /// ditto - static if(isAssignable!(T, T)) - { - @property void front(T value) - { - assert(!empty, "DList.Range.front: Range is empty"); - move(value, _first._payload); - } - } - - /// ditto - void popFront() - { - assert(!empty, "DList.Range.popFront: Range is empty"); - if (_first is _last) - { - _first = _last = null; - } - else - { - _first = _first._next; - } - } - - /// Forward range primitive. - @property Range save() { return this; } - - /// Bidirectional range primitives. - @property T back() - { - assert(!empty, "DList.Range.back: Range is empty"); - return _last._payload; - } - - /// ditto - static if(isAssignable!(T, T)) - { - @property void back(T value) - { - assert(!empty, "DList.Range.back: Range is empty"); - move(value, _last._payload); - } - } - - /// ditto - void popBack() - { - assert(!empty, "DList.Range.popBack: Range is empty"); - if (_first is _last) - { - _first = _last = null; - } - else - { - _last = _last._prev; - } - } - } - - unittest - { - static assert(isBidirectionalRange!Range); - } - -/** -Property returning $(D true) if and only if the container has no -elements. - -Complexity: $(BIGOH 1) - */ - @property const nothrow - bool empty() - { - assert(!!_first == !!_last, "DList: Internal error, inconsistant list"); - return _first is null; - } - -/** -Duplicates the container. The elements themselves are not transitively -duplicated. - -Complexity: $(BIGOH n). - */ - @property DList dup() - { - return DList(this[]); - } - -/** -Returns a range that iterates over all elements of the container, in -forward order. - -Complexity: $(BIGOH 1) - */ - Range opSlice() - { - return Range(_first, _last); - } - -/** -Forward to $(D opSlice().front). - -Complexity: $(BIGOH 1) - */ - @property T front() - { - assert(!empty, "DList.front: List is empty"); - return _first._payload; - } - -/** -Forward to $(D opSlice().front(value)). - -Complexity: $(BIGOH 1) - */ - static if(isAssignable!(T,T)) - { - @property void front(T value) - { - assert(!empty, "DList.front: List is empty"); - move(value, _first._payload); - } - } - -/** -Forward to $(D opSlice().back). - -Complexity: $(BIGOH 1) - */ - @property T back() - { - assert(!empty, "DList.back: List is empty"); - return _last._payload; - } - -/** -Forward to $(D opSlice().back(value)). - -Complexity: $(BIGOH 1) - */ - static if(isAssignable!(T,T)) - { - @property void back(T value) - { - assert(!empty, "DList.back: List is empty"); - move(value, _last._payload); - } - } - -/** -Returns a new $(D DList) that's the concatenation of $(D this) and its -argument. - */ - DList opBinary(string op, Stuff)(Stuff rhs) - if (op == "~" && isImplicitlyConvertible!(Stuff, T)) - { - auto ret = this.dup; - ret ~= rhs; - return ret; - } - /// ditto - DList opBinary(string op, Stuff)(Stuff rhs) - if (op == "~" && (is(Stuff == DList) || is(typeof(DList(rhs))))) - { - auto ret = this.dup; - ret ~= rhs; - return ret; - } - -/** -Returns a new $(D DList) that's the concatenation of the argument and $(D this) - */ - DList opBinaryRight(string op, Stuff)(Stuff rhs) - if (op == "~" && isImplicitlyConvertible!(Stuff, T)) - { - auto ret = this.dup; - ret.opOpAssignRightPrivate!"~"(rhs); - return ret; - } - -/// ditto - DList opBinaryRight(string op, Stuff)(Stuff rhs) - if (op == "~" && isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - auto ret = this.dup; - ret.opOpAssignRightPrivate!"~"(rhs); - return ret; - } - -/** -Appends the contents of stuff into this. - */ - DList opOpAssign(string op, Stuff)(Stuff rhs) - if (op == "~" && isImplicitlyConvertible!(Stuff, T)) - { - if (_last) _last._next = rhs._first; - if (rhs._first) rhs_.first._prev = _last; - } - -/// ditto - DList opOpAssign(string op, Stuff)(Stuff rhs) - if (op == "~" && isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - insertBack(rhs); - return this; - } - -// Private implementations helpers for opOpBinaryRight - DList opOpAssignRightPrivate(string op, Stuff)(Stuff lhs) - if (op == "~" && isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - this.insertFront(lhs); - return this; - } - -/** -Removes all contents from the $(D DList). - -Postcondition: $(D empty) - -Complexity: $(BIGOH 1) - */ - void clear() - { - //remove actual elements. - remove(this[]); - } - -/** -Inserts $(D stuff) to the front/back of the container. $(D stuff) can be a -value convertible to $(D T) or a range of objects convertible to $(D -T). The stable version behaves the same, but guarantees that ranges -iterating over the container are never invalidated. - -Returns: The number of elements inserted - -Complexity: $(BIGOH log(n)) - */ - size_t insertFront(Stuff)(Stuff stuff) - { - return insertBeforeNode(_first, stuff); - } - - /// ditto - size_t insertBack(Stuff)(Stuff stuff) - { - return insertBeforeNode(null, stuff); - } - - /// ditto - alias insertBack insert; - - /// ditto - alias insert stableInsert; - - /// ditto - alias insertFront stableInsertFront; - - /// ditto - alias insertBack stableInsertBack; - -/** -Picks one value from the front of the container, removes it from the -container, and returns it. - -Elements are not actually removed from the chain, but the $(D DList)'s, -first/last pointer is advanced. - -Precondition: $(D !empty) - -Returns: The element removed. - -Complexity: $(BIGOH 1). - */ - T removeAny() - { - assert(!empty, "DList.removeAny: List is empty"); - auto result = move(_last._payload); - _last = _last._prev; - if (_last is null) - { - _first = null; - } - return result; - } - /// ditto - alias removeAny stableRemoveAny; - -/** -Removes the value at the front/back of the container. The stable version -behaves the same, but guarantees that ranges iterating over the -container are never invalidated. - -Elements are not actually removed from the chain, but the $(D DList)'s, -first/last pointer is advanced. - -Precondition: $(D !empty) - -Complexity: $(BIGOH 1). - */ - void removeFront() - { - assert(!empty, "DList.removeFront: List is empty"); - _first = _first._next; - if (_first is null) - { - _last = null; - } - } - - /// ditto - alias removeFront stableRemoveFront; - - /// ditto - void removeBack() - { - assert(!empty, "DList.removeBack: List is empty"); - _last = _last._prev; - if (_last is null) - { - _first = null; - } - } - - /// ditto - alias removeBack stableRemoveBack; - -/** -Removes $(D howMany) values at the front or back of the -container. Unlike the unparameterized versions above, these functions -do not throw if they could not remove $(D howMany) elements. Instead, -if $(D howMany > n), all elements are removed. The returned value is -the effective number of elements removed. The stable version behaves -the same, but guarantees that ranges iterating over the container are -never invalidated. - -Elements are not actually removed from the chain, but the $(D DList)'s, -first/last pointer is advanced. - -Returns: The number of elements removed - -Complexity: $(BIGOH howMany * log(n)). - */ - size_t removeFront(size_t howMany) - { - size_t result; - while (_first && result < howMany) - { - _first = _first._next; - ++result; - } - if (_first is null) - { - _last = null; - } - return result; - } - - /// ditto - alias removeFront stableRemoveFront; - - /// ditto - size_t removeBack(size_t howMany) - { - size_t result; - while (_last && result < howMany) - { - _last = _last._prev; - ++result; - } - if (_last is null) - { - _first = null; - } - return result; - } - - /// ditto - alias removeBack stableRemoveBack; - -/** -Inserts $(D stuff) after range $(D r), which must be a non-empty range -previously extracted from this container. - -$(D stuff) can be a value convertible to $(D T) or a range of objects -convertible to $(D T). The stable version behaves the same, but -guarantees that ranges iterating over the container are never -invalidated. - -Elements are not actually removed from the chain, but the $(D DList)'s, -first/last pointer is advanced. - -Returns: The number of values inserted. - -Complexity: $(BIGOH k + m), where $(D k) is the number of elements in -$(D r) and $(D m) is the length of $(D stuff). - */ - size_t insertBefore(Stuff)(Range r, Stuff stuff) - { - Node* n = (r._first) ? r._first : _first; - return insertBeforeNode(n, stuff); - } - - /// ditto - alias insertBefore stableInsertBefore; - - /// ditto - size_t insertAfter(Stuff)(Range r, Stuff stuff) - { - Node* n = (r._last) ? r._last._next : null; - return insertBeforeNode(n, stuff); - } - - /// ditto - alias insertAfter stableInsertAfter; - - // Helper: insert $(D stuff) before Node $(D n). If $(D n) is $(D null) then insert at end. - private size_t insertBeforeNode(Stuff)(Node* n, Stuff stuff) - if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { size_t result; - if(stuff.empty) return result; - - Node* first; - Node* last; - //scope block - { - auto item = stuff.front; - stuff.popFront(); - last = first = new Node(item, null, null); - ++result; - } - foreach (item; stuff) - { - last = new Node(item, last, null); - ++result; - } - - //We have created a first-last chain. Now we insert it. - if(!_first) - { - _first = first; - _last = last; - } - else - { - assert(_last); - if(n) - { - if(n._prev) - { - n._prev._next = first; - first._prev = n._prev; - } - n._prev = last; - last._next = n; - if(n is _first) - _first = first; - } - else - { - if(_last._next) - { - _last._next._prev = last; - last._next = _last._next; - } - _last._next = first; - first._prev = _last; - _last = last; - } - } - assert(_first); - assert(_last); - return result; - } - - // Helper: insert $(D stuff) before Node $(D n). If $(D n) is $(D null) then insert at end. - private size_t insertBeforeNode(Stuff)(Node* n, Stuff stuff) - if (isImplicitlyConvertible!(Stuff, T)) - { - Stuff[] stuffs = (&stuff)[0 .. 1]; - return insertBeforeNode(n, stuffs); - } - -/** -Removes all elements belonging to $(D r), which must be a range -obtained originally from this container. - -This function actually removes the elements from the chain. This is the -only function that may invalidate a range, as it cuts the chain of elements: -*Ranges (and other DList) that contain $(D r) or that are inside $(D r), -as well a $(D r) itself, are never invalidated. -*Ranges (and other DList) which partially overlap with $(D r) will be cut, -and invalidated. - -Returns: A range spanning the remaining elements in the container that -initially were right after $(D r). - -Complexity: $(BIGOH 1) - */ - Range remove(Range r) - { - if (r.empty) - { - return r; - } - assert(!empty, "DList.remove: Range is empty"); - - //Note about the unusual complexity here: - //The first and last nodes are not necessarilly the actual last nodes - //of the "chain". - //If we merelly excise the range from the chain, we can run into odd behavior, - //in particlar, when the range's front and/or back coincide with the List's... - - Node* before = r._first._prev; - Node* after = r._last._next; - - Node* oldFirst = _first; - Node* oldLast = _last; - - if (before) - { - if (after) - { - before._next = after; - after._prev = before; - } - if (_first == r._first) - _first = (oldLast != r._last) ? after : null ; - } - else - { - assert(oldFirst == r._first, "Dlist.remove: Range is not part of the list"); - _first = (oldLast != r._last) ? after : null ; - } - - if (after) - { - if (before) - { - after._prev = before; - before._next = after; - } - if (_last == r._last) - _last = (oldFirst != r._first) ? before : null ; - } - else - { - assert(oldLast == r._last, "Dlist.remove: Range is not part of the list"); - _last = (oldFirst != r._first) ? before : null ; - } - - return Range(after, _last); - } - - /// ditto - template linearRemove(R) if (is(R == Range)) - { - Range linearRemove(R r) { return remove(r); } - } - - /// ditto - Range linearRemove(R)(R r) - if (is(R == Range)) - { - return remove(r); - } - -/** -$(D linearRemove) functions as $(D remove), but also accepts ranges that are -result the of a $(D take) operation. This is a convenient way to remove a -fixed amount of elements from the range. - -Complexity: $(BIGOH r.walkLength) - */ - Range linearRemove(R)(R r) - if (is(R == Take!Range)) - { - if (r.empty) - return Range(null,null); - assert(r.source._first); - - Node* first = r.source._first; - Node* last = void; - do - { - last = r.source._first; - r.popFront(); - } while ( !r.empty ); - - return remove(Range(first, last)); - } - - /** $(RED Scheduled for deprecation. These methods are not actually stable. - Use the standard $(D remove) or $(D linearRemove) instead.) - */ - alias remove stableRemove; - /// ditto - alias linearRemove stableLinearRemove; -} - -unittest -{ - auto a = DList!int([3, 4]); //Create a new chain - auto b = a; //Point to the same chain - // (3 - 4) - assert(a[].equal([3, 4])); - assert(b[].equal([3, 4])); - - b.stableInsertFront(1); //insert before of b - b.stableInsertBack(5); //insert after of b - // (2 - (3 - 4) - 5) - assert(a[].equal([3, 4])); //a is not changed - assert(b[].equal([1, 3, 4, 5])); // but b is changed - - a.stableInsertFront(2); //insert in front of a, this will insert "inside" the chain - // (1 - (2 - 3 - 4) - 5) - assert(a[].equal([2, 3, 4])); //a is modified - assert(b[].equal([1, 2, 3, 4, 5])); //and so is b; - - a.remove(a[]); //remove all the elements of a: This will cut them from the chain; - // (1 - 5) - assert(a[].empty); //a is empty - assert(b[].equal([1, 5])); //b has lost some of its elements; - - a.insert(2); //insert in a. This will create a new chain - // (2) - // (1 - 5) - assert(a[].equal([2])); //a is a new chain - assert(b[].equal([1, 5])); //b is unchanged; -} - -unittest -{ - alias DList!int IntList; - IntList list = IntList([0,1,2,3]); - assert(equal(list[],[0,1,2,3])); - list.insertBack([4,5,6,7]); - assert(equal(list[],[0,1,2,3,4,5,6,7])); - - list = IntList(); - list.insertFront([0,1,2,3]); - assert(equal(list[],[0,1,2,3])); - list.insertFront([4,5,6,7]); - assert(equal(list[],[4,5,6,7,0,1,2,3])); -} - -unittest -{ - alias DList!int IntList; - IntList list = IntList([0,1,2,3]); - auto range = list[]; - for( ; !range.empty; range.popFront()) - { - int item = range.front; - if (item == 2) - { - list.stableLinearRemove(take(range,1)); - } - } - assert(equal(list[],[0,1,3])); - - list = IntList([0,1,2,3]); - range = list[]; - for( ; !range.empty; range.popFront()) - { - int item = range.front; - if (item == 2) - { - list.stableLinearRemove(take(range,2)); - } - } - assert(equal(list[],[0,1])); - - list = IntList([0,1,2,3]); - range = list[]; - for( ; !range.empty; range.popFront()) - { - int item = range.front; - if (item == 0) - { - list.stableLinearRemove(take(range,2)); - } - } - assert(equal(list[],[2,3])); - - list = IntList([0,1,2,3]); - range = list[]; - for( ; !range.empty; range.popFront()) - { - int item = range.front; - if (item == 1) - { - list.stableLinearRemove(take(range,2)); - } - } - assert(equal(list[],[0,3])); -} - -unittest -{ - auto dl = DList!string(["a", "b", "d"]); - dl.insertAfter(dl[], "e"); // insert at the end - assert(equal(dl[], ["a", "b", "d", "e"])); - auto dlr = dl[]; - dlr.popBack(); dlr.popBack(); - dl.insertAfter(dlr, "c"); // insert after "b" - assert(equal(dl[], ["a", "b", "c", "d", "e"])); -} - -unittest -{ - auto dl = DList!string(["a", "b", "d"]); - dl.insertBefore(dl[], "e"); // insert at the front - assert(equal(dl[], ["e", "a", "b", "d"])); - auto dlr = dl[]; - dlr.popFront(); dlr.popFront(); - dl.insertBefore(dlr, "c"); // insert before "b" - assert(equal(dl[], ["e", "a", "c", "b", "d"])); -} - -unittest -{ - auto d = DList!int([1, 2, 3]); - d.front = 5; //test frontAssign - assert(d.front == 5); - auto r = d[]; - r.back = 1; - assert(r.back == 1); -} - -// Issue 8895 -unittest -{ - auto a = make!(DList!int)(1,2,3,4); - auto b = make!(DList!int)(1,2,3,4); - auto c = make!(DList!int)(1,2,3,5); - auto d = make!(DList!int)(1,2,3,4,5); - assert(a == b); // this better terminate! - assert(!(a == c)); - assert(!(a == d)); -} - -unittest -{ - auto d = DList!int([1, 2, 3]); - d.front = 5; //test frontAssign - assert(d.front == 5); - auto r = d[]; - r.back = 1; - assert(r.back == 1); -} - -unittest -{ - auto a = DList!int(); - assert(a.removeFront(10) == 0); - a.insert([1, 2, 3]); - assert(a.removeFront(10) == 3); - assert(a[].empty); -} - -unittest -{ - //Verify all flavors of ~ - auto a = DList!int(); - auto b = DList!int(); - auto c = DList!int([1, 2, 3]); - auto d = DList!int([4, 5, 6]); - - assert((a ~ b[])[].empty); - - assert((c ~ d[])[].equal([1, 2, 3, 4, 5, 6])); - assert(c[].equal([1, 2, 3])); - assert(d[].equal([4, 5, 6])); - - assert((c[] ~ d)[].equal([1, 2, 3, 4, 5, 6])); - assert(c[].equal([1, 2, 3])); - assert(d[].equal([4, 5, 6])); - - a~=c[]; - assert(a[].equal([1, 2, 3])); - assert(c[].equal([1, 2, 3])); - - a~=d[]; - assert(a[].equal([1, 2, 3, 4, 5, 6])); - assert(d[].equal([4, 5, 6])); - - a~=[7, 8, 9]; - assert(a[].equal([1, 2, 3, 4, 5, 6, 7, 8, 9])); - - //trick test: - auto r = c[]; - c.removeFront(); - c.removeBack(); - c~=d[]; - assert(c[].equal([2, 4, 5, 6])); - assert(r.equal([1, 2, 4, 5, 6, 3])); -} - -unittest -{ - //8905 - auto a = DList!int([1, 2, 3, 4]); - auto r = a[]; - a.stableRemoveBack(); - a.stableInsertBack(7); - assert(a[].equal([1, 2, 3, 7])); - assert(r.equal([1, 2, 3, 7, 4])); -} - -/** -Array type with deterministic control of memory. The memory allocated -for the array is reclaimed as soon as possible; there is no reliance -on the garbage collector. $(D Array) uses $(D malloc) and $(D free) -for managing its own memory. - */ -struct Array(T) -if (!is(Unqual!T == bool)) -{ - // This structure is not copyable. - private struct Payload - { - size_t _capacity; - T[] _payload; - - // Convenience constructor - this(T[] p) { _capacity = p.length; _payload = p; } - - // Destructor releases array memory - ~this() - { - foreach (ref e; _payload) .destroy(e); - static if (hasIndirections!T) - GC.removeRange(_payload.ptr); - free(_payload.ptr); - } - - this(this) - { - assert(0); - } - - void opAssign(Payload rhs) - { - assert(false); - } - - // Duplicate data - // @property Payload dup() - // { - // Payload result; - // result._payload = _payload.dup; - // // Conservatively assume initial capacity == length - // result._capacity = result._payload.length; - // return result; - // } - - // length - @property size_t length() const - { - return _payload.length; - } - - // length - @property void length(size_t newLength) - { - if (length >= newLength) - { - // shorten - static if (is(T == struct) && hasElaborateDestructor!T) - { - foreach (ref e; _payload.ptr[newLength .. _payload.length]) - { - .destroy(e); - } - } - _payload = _payload.ptr[0 .. newLength]; - return; - } - // enlarge - auto startEmplace = length; - _payload = (cast(T*) realloc(_payload.ptr, - T.sizeof * newLength))[0 .. newLength]; - initializeAll(_payload.ptr[startEmplace .. length]); - } - - // capacity - @property size_t capacity() const - { - return _capacity; - } - - // reserve - void reserve(size_t elements) - { - if (elements <= capacity) return; - immutable sz = elements * T.sizeof; - static if (hasIndirections!T) // should use hasPointers instead - { - /* Because of the transactional nature of this - * relative to the garbage collector, ensure no - * threading bugs by using malloc/copy/free rather - * than realloc. - */ - immutable oldLength = length; - auto newPayload = - enforce(cast(T*) malloc(sz))[0 .. oldLength]; - // copy old data over to new array - memcpy(newPayload.ptr, _payload.ptr, T.sizeof * oldLength); - // Zero out unused capacity to prevent gc from seeing - // false pointers - memset(newPayload.ptr + oldLength, - 0, - (elements - oldLength) * T.sizeof); - GC.addRange(newPayload.ptr, sz); - GC.removeRange(_payload.ptr); - free(_payload.ptr); - _payload = newPayload; - } - else - { - /* These can't have pointers, so no need to zero - * unused region - */ - auto newPayload = - enforce(cast(T*) realloc(_payload.ptr, sz))[0 .. length]; - _payload = newPayload; - } - _capacity = elements; - } - - // Insert one item - size_t insertBack(Stuff)(Stuff stuff) - if (isImplicitlyConvertible!(Stuff, T)) - { - if (_capacity == length) - { - reserve(1 + capacity * 3 / 2); - } - assert(capacity > length && _payload.ptr); - emplace(_payload.ptr + _payload.length, stuff); - _payload = _payload.ptr[0 .. _payload.length + 1]; - return 1; - } - - /// Insert a range of items - size_t insertBack(Stuff)(Stuff stuff) - if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - static if (hasLength!Stuff) - { - immutable oldLength = length; - reserve(oldLength + stuff.length); - } - size_t result; - foreach (item; stuff) - { - insertBack(item); - ++result; - } - static if (hasLength!Stuff) - { - assert(length == oldLength + stuff.length); - } - return result; - } - } - private alias RefCounted!(Payload, RefCountedAutoInitialize.no) Data; - private Data _data; - -/** -Constructor taking a number of items - */ - this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) - { - auto p = cast(T*) malloc(T.sizeof * values.length); - if (hasIndirections!T && p) - { - GC.addRange(p, T.sizeof * values.length); - } - foreach (i, e; values) - { - emplace(p + i, e); - assert(p[i] == e); - } - _data = Data(p[0 .. values.length]); - } - -/** -Constructor taking an input range - */ - this(Stuff)(Stuff stuff) - if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T) && !is(Stuff == T[])) - { - insertBack(stuff); - } - - -/** -Comparison for equality. - */ - bool opEquals(const Array rhs) const - { - return opEquals(rhs); - } - - /// ditto - bool opEquals(ref const Array rhs) const - { - if (empty) return rhs.empty; - if (rhs.empty) return false; - return _data._payload == rhs._data._payload; - } - -/** -Defines the container's primary range, which is a random-access range. - */ - static struct Range - { - private Array _outer; - private size_t _a, _b; - - private this(ref Array data, size_t a, size_t b) - { - _outer = data; - _a = a; - _b = b; - } - - @property Range save() - { - return this; - } - - @property bool empty() @safe pure nothrow const - { - return _a >= _b; - } - - @property size_t length() @safe pure nothrow const - { - return _b - _a; - } - - size_t opDollar() @safe pure nothrow const - { - return length; - } - - @property T front() - { - version (assert) if (empty) throw new RangeError(); - return _outer[_a]; - } - - @property T back() - { - version (assert) if (empty) throw new RangeError(); - return _outer[_b - 1]; - } - - @property void front(T value) - { - version (assert) if (empty) throw new RangeError(); - _outer[_a] = move(value); - } - - @property void back(T value) - { - version (assert) if (empty) throw new RangeError(); - _outer[_b - 1] = move(value); - } - - void popFront() @safe pure nothrow - { - version (assert) if (empty) throw new RangeError(); - ++_a; - } - - void popBack() @safe pure nothrow - { - version (assert) if (empty) throw new RangeError(); - --_b; - } - - T moveFront() - { - version (assert) if (empty || _a >= _outer.length) throw new RangeError(); - return move(_outer._data._payload[_a]); - } - - T moveBack() - { - version (assert) if (empty || _b > _outer.length) throw new RangeError(); - return move(_outer._data._payload[_b - 1]); - } - - T moveAt(size_t i) - { - version (assert) if (_a + i >= _b || _a + i >= _outer.length) throw new RangeError(); - return move(_outer._data._payload[_a + i]); - } - - T opIndex(size_t i) - { - version (assert) if (_a + i >= _b) throw new RangeError(); - return _outer[_a + i]; - } - - void opIndexUnary(string op)(size_t i) - if(op == "++" || op == "--") - { - version (assert) if (_a + i >= _b) throw new RangeError(); - mixin(op~"_outer[_a + i];"); - } - - T opIndexUnary(string op)(size_t i) - if(op != "++" && op != "--") - { - version (assert) if (_a + i >= _b) throw new RangeError(); - mixin("return "~op~"_outer[_a + i];"); - } - - void opIndexAssign(T value, size_t i) - { - version (assert) if (_a + i >= _b) throw new RangeError(); - _outer[_a + i] = value; - } - - void opIndexOpAssign(string op)(T value, size_t i) - { - version (assert) if (_a + i >= _b) throw new RangeError(); - mixin("_outer[i] "~op~"= value;"); - } - - typeof(this) opSlice() - { - return typeof(this)(_outer, _a, _b); - } - - typeof(this) opSlice(size_t i, size_t j) - { - version (assert) if (i > j || _a + j > _b) throw new RangeError(); - return typeof(this)(_outer, _a + i, _a + j); - } - - void opSliceAssign(T value) - { - version (assert) if (_b > _outer.length) throw new RangeError(); - _outer[_a .. _b] = value; - } - - void opSliceAssign(T value, size_t i, size_t j) - { - version (assert) if (_a + j > _b) throw new RangeError(); - _outer[_a + i .. _a + j] = value; - } - - void opSliceUnary(string op)() - if(op == "++" || op == "--") - { - version (assert) if (_b > _outer.length) throw new RangeError(); - mixin(op~"_outer[_a .. _b];"); - } - - void opSliceUnary(string op)(size_t i, size_t j) - if(op == "++" || op == "--") - { - version (assert) if (_a + j > _b) throw new RangeError(); - mixin(op~"_outer[_a + i .. _a + j];"); - } - - void opSliceOpAssign(string op)(T value) - { - version (assert) if (_b > _outer.length) throw new RangeError(); - mixin("_outer[_a .. _b] "~op~"= value;"); - } - - void opSliceOpAssign(string op)(T value, size_t i, size_t j) - { - version (assert) if (_a + j > _b) throw new RangeError(); - mixin("_outer[_a + i .. _a + j] "~op~"= value;"); - } - } - -/** -Duplicates the container. The elements themselves are not transitively -duplicated. - -Complexity: $(BIGOH n). - */ - @property Array dup() - { - if (!_data.refCountedStore.isInitialized) return this; - return Array(_data._payload); - } - -/** -Property returning $(D true) if and only if the container has no -elements. - -Complexity: $(BIGOH 1) - */ - @property bool empty() const - { - return !_data.refCountedStore.isInitialized || _data._payload.empty; - } - -/** -Returns the number of elements in the container. - -Complexity: $(BIGOH 1). - */ - @property size_t length() const - { - return _data.refCountedStore.isInitialized ? _data._payload.length : 0; - } - - /// ditto - size_t opDollar() const - { - return length; - } - -/** -Returns the maximum number of elements the container can store without - (a) allocating memory, (b) invalidating iterators upon insertion. - -Complexity: $(BIGOH 1) - */ - @property size_t capacity() - { - return _data.refCountedStore.isInitialized ? _data._capacity : 0; - } - -/** -Ensures sufficient capacity to accommodate $(D e) elements. - -Postcondition: $(D capacity >= e) - -Complexity: $(BIGOH 1) - */ - void reserve(size_t elements) - { - if (!_data.refCountedStore.isInitialized) - { - if (!elements) return; - immutable sz = elements * T.sizeof; - auto p = enforce(malloc(sz)); - static if (hasIndirections!T) - { - GC.addRange(p, sz); - } - _data = Data(cast(T[]) p[0 .. 0]); - _data._capacity = elements; - } - else - { - _data.reserve(elements); - } - } - -/** -Returns a range that iterates over elements of the container, in -forward order. - -Complexity: $(BIGOH 1) - */ - Range opSlice() - { - return Range(this, 0, length); - } - -/** -Returns a range that iterates over elements of the container from -index $(D a) up to (excluding) index $(D b). - -Precondition: $(D a <= b && b <= length) - -Complexity: $(BIGOH 1) - */ - Range opSlice(size_t i, size_t j) - { - version (assert) if (i > j || j > length) throw new RangeError(); - return Range(this, i, j); - } - -/** -Forward to $(D opSlice().front) and $(D opSlice().back), respectively. - -Precondition: $(D !empty) - -Complexity: $(BIGOH 1) - */ - @property T front() - { - version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); - return _data._payload[0]; - } - - /// ditto - @property void front(T value) - { - version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); - _data._payload[0] = value; - } - - /// ditto - @property T back() - { - version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); - return _data._payload[$ - 1]; - } - - /// ditto - @property void back(T value) - { - version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); - _data._payload[$ - 1] = value; - } - -/** -Indexing operators yield or modify the value at a specified index. - -Precondition: $(D i < length) - -Complexity: $(BIGOH 1) - */ - T opIndex(size_t i) - { - version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); - return _data._payload[i]; - } - - /// ditto - void opIndexUnary(string op)(size_t i) - if(op == "++" || op == "--") - { - version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); - mixin(op~"_data._payload[i];"); - } - - /// ditto - T opIndexUnary(string op)(size_t i) - if(op != "++" && op != "--") - { - version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); - mixin("return "~op~"_data._payload[i];"); - } - - /// ditto - void opIndexAssign(T value, size_t i) - { - version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); - _data._payload[i] = value; - } - - /// ditto - void opIndexOpAssign(string op)(T value, size_t i) - { - version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); - mixin("_data._payload[i] "~op~"= value;"); - } - -/** -Slicing operations execute an operation on an entire slice. - -Precondition: $(D i < j && j < length) - -Complexity: $(BIGOH slice.length) - */ - - void opSliceAssign(T value) - { - if (!_data.refCountedStore.isInitialized) return; - _data._payload[] = value; - } - - void opSliceAssign(T value, size_t i, size_t j) - { - auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init; - slice[i .. j] = value; - } - - void opSliceUnary(string op)() - if(op == "++" || op == "--") - { - if(!_data.refCountedStore.isInitialized) return; - mixin(op~"_data._payload[];"); - } - - /// ditto - void opSliceUnary(string op)(size_t i, size_t j) - if(op == "++" || op == "--") - { - auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init; - mixin(op~"slice[i .. j];"); - } - - /// ditto - void opSliceOpAssign(string op)(T value) - { - if(!_data.refCountedStore.isInitialized) return; - mixin("_data._payload[] "~op~"= value;"); - } - - /// ditto - void opSliceOpAssign(string op)(T value, size_t i, size_t j) - { - auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init; - mixin("slice[i .. j] "~op~"= value;"); - } - -/** -Returns a new container that's the concatenation of $(D this) and its -argument. $(D opBinaryRight) is only defined if $(D Stuff) does not -define $(D opBinary). - -Complexity: $(BIGOH n + m), where m is the number of elements in $(D -stuff) - */ - Array opBinary(string op, Stuff)(Stuff stuff) - if (op == "~") - { - // TODO: optimize - Array result; - // @@@BUG@@ result ~= this[] doesn't work - auto r = this[]; - result ~= r; - assert(result.length == length); - result ~= stuff[]; - return result; - } - -/** -Forwards to $(D insertBack(stuff)). - */ - void opOpAssign(string op, Stuff)(Stuff stuff) - if (op == "~") - { - static if (is(typeof(stuff[]))) - { - insertBack(stuff[]); - } - else - { - insertBack(stuff); - } - } - -/** -Removes all contents from the container. The container decides how $(D -capacity) is affected. - -Postcondition: $(D empty) - -Complexity: $(BIGOH n) - */ - void clear() - { - .destroy(_data); - } - -/** -Sets the number of elements in the container to $(D newSize). If $(D -newSize) is greater than $(D length), the added elements are added to -unspecified positions in the container and initialized with $(D -T.init). - -Complexity: $(BIGOH abs(n - newLength)) - -Postcondition: $(D length == newLength) - */ - @property void length(size_t newLength) - { - _data.refCountedStore.ensureInitialized(); - _data.length = newLength; - } - -/** -Picks one value in an unspecified position in the container, removes -it from the container, and returns it. Implementations should pick the -value that's the most advantageous for the container, but document the -exact behavior. The stable version behaves the same, but guarantees -that ranges iterating over the container are never invalidated. - -Precondition: $(D !empty) - -Returns: The element removed. - -Complexity: $(BIGOH log(n)). - */ - T removeAny() - { - auto result = back; - removeBack(); - return result; - } - /// ditto - alias removeAny stableRemoveAny; - -/** -Inserts $(D value) to the front or back of the container. $(D stuff) -can be a value convertible to $(D T) or a range of objects convertible -to $(D T). The stable version behaves the same, but guarantees that -ranges iterating over the container are never invalidated. - -Returns: The number of elements inserted - -Complexity: $(BIGOH m * log(n)), where $(D m) is the number of -elements in $(D stuff) - */ - size_t insertBack(Stuff)(Stuff stuff) - if (isImplicitlyConvertible!(Stuff, T) || - isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - _data.refCountedStore.ensureInitialized(); - return _data.insertBack(stuff); - } - /// ditto - alias insertBack insert; - -/** -Removes the value at the back of the container. The stable version -behaves the same, but guarantees that ranges iterating over the -container are never invalidated. - -Precondition: $(D !empty) - -Complexity: $(BIGOH log(n)). - */ - void removeBack() - { - enforce(!empty); - static if (is(T == struct)) - { - // Destroy this guy - .destroy(_data._payload[$ - 1]); - } - _data._payload = _data._payload[0 .. $ - 1]; - } - /// ditto - alias removeBack stableRemoveBack; - -/** -Removes $(D howMany) values at the front or back of the -container. Unlike the unparameterized versions above, these functions -do not throw if they could not remove $(D howMany) elements. Instead, -if $(D howMany > n), all elements are removed. The returned value is -the effective number of elements removed. The stable version behaves -the same, but guarantees that ranges iterating over the container are -never invalidated. - -Returns: The number of elements removed - -Complexity: $(BIGOH howMany). - */ - size_t removeBack(size_t howMany) - { - if (howMany > length) howMany = length; - static if (is(T == struct)) - { - // Destroy this guy - foreach (ref e; _data._payload[$ - howMany .. $]) - { - .destroy(e); - } - } - _data._payload = _data._payload[0 .. $ - howMany]; - return howMany; - } - /// ditto - alias removeBack stableRemoveBack; - -/** -Inserts $(D stuff) before, after, or instead range $(D r), which must -be a valid range previously extracted from this container. $(D stuff) -can be a value convertible to $(D T) or a range of objects convertible -to $(D T). The stable version behaves the same, but guarantees that -ranges iterating over the container are never invalidated. - -Returns: The number of values inserted. - -Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff) - */ - size_t insertBefore(Stuff)(Range r, Stuff stuff) - if (isImplicitlyConvertible!(Stuff, T)) - { - enforce(r._outer._data is _data && r._a <= length); - reserve(length + 1); - assert(_data.refCountedStore.isInitialized); - // Move elements over by one slot - memmove(_data._payload.ptr + r._a + 1, - _data._payload.ptr + r._a, - T.sizeof * (length - r._a)); - emplace(_data._payload.ptr + r._a, stuff); - _data._payload = _data._payload.ptr[0 .. _data._payload.length + 1]; - return 1; - } - - /// ditto - size_t insertBefore(Stuff)(Range r, Stuff stuff) - if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - enforce(r._outer._data is _data && r._a <= length); - static if (isForwardRange!Stuff) - { - // Can find the length in advance - auto extra = walkLength(stuff); - if (!extra) return 0; - reserve(length + extra); - assert(_data.refCountedStore.isInitialized); - // Move elements over by extra slots - memmove(_data._payload.ptr + r._a + extra, - _data._payload.ptr + r._a, - T.sizeof * (length - r._a)); - foreach (p; _data._payload.ptr + r._a .. - _data._payload.ptr + r._a + extra) - { - emplace(p, stuff.front); - stuff.popFront(); - } - _data._payload = - _data._payload.ptr[0 .. _data._payload.length + extra]; - return extra; - } - else - { - enforce(_data); - immutable offset = r._a; - enforce(offset <= length); - auto result = insertBack(stuff); - bringToFront(this[offset .. length - result], - this[length - result .. length]); - return result; - } - } - - /// ditto - size_t insertAfter(Stuff)(Range r, Stuff stuff) - { - enforce(r._outer._data is _data); - // TODO: optimize - immutable offset = r._b; - enforce(offset <= length); - auto result = insertBack(stuff); - bringToFront(this[offset .. length - result], - this[length - result .. length]); - return result; - } - - /// ditto - size_t replace(Stuff)(Range r, Stuff stuff) - if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) - { - enforce(r._outer._data is _data); - size_t result; - for (; !stuff.empty; stuff.popFront()) - { - if (r.empty) - { - // insert the rest - return result + insertBefore(r, stuff); - } - r.front = stuff.front; - r.popFront(); - ++result; - } - // Remove remaining stuff in r - linearRemove(r); - return result; - } - - /// ditto - size_t replace(Stuff)(Range r, Stuff stuff) - if (isImplicitlyConvertible!(Stuff, T)) - { - enforce(r._outer._data is _data); - if (r.empty) - { - insertBefore(r, stuff); - } - else - { - r.front = stuff; - r.popFront(); - linearRemove(r); - } - return 1; - } - -/** -Removes all elements belonging to $(D r), which must be a range -obtained originally from this container. The stable version behaves -the same, but guarantees that ranges iterating over the container are -never invalidated. - -Returns: A range spanning the remaining elements in the container that -initially were right after $(D r). - -Complexity: $(BIGOH n - m), where $(D m) is the number of elements in -$(D r) - */ - Range linearRemove(Range r) - { - enforce(r._outer._data is _data); - enforce(_data.refCountedStore.isInitialized); - enforce(r._a <= r._b && r._b <= length); - immutable offset1 = r._a; - immutable offset2 = r._b; - immutable tailLength = length - offset2; - // Use copy here, not a[] = b[] because the ranges may overlap - copy(this[offset2 .. length], this[offset1 .. offset1 + tailLength]); - length = offset1 + tailLength; - return this[length - tailLength .. length]; - } - /// ditto - alias remove stableLinearRemove; -} - -unittest -{ - Array!int a; - assert(a.empty); -} - -unittest -{ - Array!int a = Array!int(1, 2, 3); - //a._data._refCountedDebug = true; - auto b = a.dup; - assert(b == Array!int(1, 2, 3)); - b.front = 42; - assert(b == Array!int(42, 2, 3)); - assert(a == Array!int(1, 2, 3)); -} - -unittest -{ - auto a = Array!int(1, 2, 3); - assert(a.length == 3); -} - -unittest -{ - Array!int a; - a.reserve(1000); - assert(a.length == 0); - assert(a.empty); - assert(a.capacity >= 1000); - auto p = a._data._payload.ptr; - foreach (i; 0 .. 1000) - { - a.insertBack(i); - } - assert(p == a._data._payload.ptr); -} - -unittest -{ - auto a = Array!int(1, 2, 3); - a[1] *= 42; - assert(a[1] == 84); -} - -unittest -{ - auto a = Array!int(1, 2, 3); - auto b = Array!int(11, 12, 13); - auto c = a ~ b; - //foreach (e; c) writeln(e); - assert(c == Array!int(1, 2, 3, 11, 12, 13)); - //assert(a ~ b[] == Array!int(1, 2, 3, 11, 12, 13)); -} - -unittest -{ - auto a = Array!int(1, 2, 3); - auto b = Array!int(11, 12, 13); - a ~= b; - assert(a == Array!int(1, 2, 3, 11, 12, 13)); -} - -unittest -{ - auto a = Array!int(1, 2, 3, 4); - assert(a.removeAny() == 4); - assert(a == Array!int(1, 2, 3)); -} - -unittest -{ - auto a = Array!int(1, 2, 3, 4, 5); - auto r = a[2 .. a.length]; - assert(a.insertBefore(r, 42) == 1); - assert(a == Array!int(1, 2, 42, 3, 4, 5)); - r = a[2 .. 2]; - assert(a.insertBefore(r, [8, 9]) == 2); - assert(a == Array!int(1, 2, 8, 9, 42, 3, 4, 5)); -} - -unittest -{ - auto a = Array!int(0, 1, 2, 3, 4, 5, 6, 7, 8); - a.linearRemove(a[4 .. 6]); - auto b = Array!int(0, 1, 2, 3, 6, 7, 8); - //writeln(a.length); - //foreach (e; a) writeln(e); - assert(a == Array!int(0, 1, 2, 3, 6, 7, 8)); -} - -// Give the Range object some testing. -unittest -{ - auto a = Array!int(0, 1, 2, 3, 4, 5, 6)[]; - auto b = Array!int(6, 5, 4, 3, 2, 1, 0)[]; - alias typeof(a) A; - - static assert(isRandomAccessRange!A); - static assert(hasSlicing!A); - static assert(hasAssignableElements!A); - static assert(hasMobileElements!A); - - assert(equal(retro(b), a)); - assert(a.length == 7); - assert(equal(a[1..4], [1, 2, 3])); -} -// Test issue 5920 -version(unittest) -{ - //@@@BUG4274@@@: This cannot be declared as an inner struct. - private struct structBug5920 - { - int order; - uint* pDestructionMask; - ~this() - { - if (pDestructionMask) - *pDestructionMask += 1 << order; - } - } -} -unittest -{ - alias structBug5920 S; - uint dMask; - - auto arr = Array!S(cast(S[])[]); - foreach (i; 0..8) - arr.insertBack(S(i, &dMask)); - // don't check dMask now as S may be copied multiple times (it's ok?) - { - assert(arr.length == 8); - dMask = 0; - arr.length = 6; - assert(arr.length == 6); // make sure shrinking calls the d'tor - assert(dMask == 0b1100_0000); - arr.removeBack(); - assert(arr.length == 5); // make sure removeBack() calls the d'tor - assert(dMask == 0b1110_0000); - arr.removeBack(3); - assert(arr.length == 2); // ditto - assert(dMask == 0b1111_1100); - arr.clear(); - assert(arr.length == 0); // make sure clear() calls the d'tor - assert(dMask == 0b1111_1111); - } - assert(dMask == 0b1111_1111); // make sure the d'tor is called once only. -} -// Test issue 5792 (mainly just to check if this piece of code is compilable) -unittest -{ - auto a = Array!(int[])([[1,2],[3,4]]); - a.reserve(4); - assert(a.capacity >= 4); - assert(a.length == 2); - assert(a[0] == [1,2]); - assert(a[1] == [3,4]); - a.reserve(16); - assert(a.capacity >= 16); - assert(a.length == 2); - assert(a[0] == [1,2]); - assert(a[1] == [3,4]); -} - -// test replace!Stuff with range Stuff -unittest -{ - auto a = Array!int([1, 42, 5]); - a.replace(a[1 .. 2], [2, 3, 4]); - assert(equal(a[], [1, 2, 3, 4, 5])); -} - -// test insertBefore and replace with empty Arrays -unittest -{ - auto a = Array!int(); - a.insertBefore(a[], 1); - assert(equal(a[], [1])); -} -unittest -{ - auto a = Array!int(); - a.insertBefore(a[], [1, 2]); - assert(equal(a[], [1, 2])); -} -unittest -{ - auto a = Array!int(); - a.replace(a[], [1, 2]); - assert(equal(a[], [1, 2])); -} -unittest -{ - auto a = Array!int(); - a.replace(a[], 1); - assert(equal(a[], [1])); -} -// make sure that Array instances refuse ranges that don't belong to them -unittest -{ - Array!int a = [1, 2, 3]; - auto r = a.dup[]; - assertThrown(a.insertBefore(r, 42)); - assertThrown(a.insertBefore(r, [42])); - assertThrown(a.insertAfter(r, 42)); - assertThrown(a.replace(r, 42)); - assertThrown(a.replace(r, [42])); - assertThrown(a.linearRemove(r)); -} -unittest -{ - auto a = Array!int([1, 1]); - a[1] = 0; //Check Array.opIndexAssign - assert(a[1] == 0); - a[1] += 1; //Check Array.opIndexOpAssign - assert(a[1] == 1); - - //Check Array.opIndexUnary - ++a[0]; - //a[0]++ //op++ doesn't return, so this shouldn't work, even with 5044 fixed - assert(a[0] == 2); - assert(+a[0] == +2); - assert(-a[0] == -2); - assert(~a[0] == ~2); - - auto r = a[]; - r[1] = 0; //Check Array.Range.opIndexAssign - assert(r[1] == 0); - r[1] += 1; //Check Array.Range.opIndexOpAssign - assert(r[1] == 1); - - //Check Array.Range.opIndexUnary - ++r[0]; - //r[0]++ //op++ doesn't return, so this shouldn't work, even with 5044 fixed - assert(r[0] == 3); - assert(+r[0] == +3); - assert(-r[0] == -3); - assert(~r[0] == ~3); -} - -unittest -{ - //Test "array-wide" operations - auto a = Array!int([0, 1, 2]); //Array - a[] += 5; - assert(a[].equal([5, 6, 7])); - ++a[]; - assert(a[].equal([6, 7, 8])); - a[1 .. 3] *= 5; - assert(a[].equal([6, 35, 40])); - a[0 .. 2] = 0; - assert(a[].equal([0, 0, 40])); - - //Test empty array - auto a2 = Array!int.init; - ++a2[]; - ++a2[0 .. 0]; - a2[] = 0; - a2[0 .. 0] = 0; - a2[] += 0; - a2[0 .. 0] += 0; - - //Test "range-wide" operations - auto r = Array!int([0, 1, 2])[]; //Array.Range - r[] += 5; - assert(r.equal([5, 6, 7])); - ++r[]; - assert(r.equal([6, 7, 8])); - r[1 .. 3] *= 5; - assert(r.equal([6, 35, 40])); - r[0 .. 2] = 0; - assert(r.equal([0, 0, 40])); - - //Test empty Range - auto r2 = Array!int.init[]; - ++r2[]; - ++r2[0 .. 0]; - r2[] = 0; - r2[0 .. 0] = 0; - r2[] += 0; - r2[0 .. 0] += 0; -} - -// Test issue 11194 -unittest { - static struct S { - int i = 1337; - void* p; - this(this) { assert(i == 1337); } - ~this() { assert(i == 1337); } - } - Array!S arr; - S s; - arr ~= s; - arr ~= s; -} - -unittest //11459 -{ - static struct S - { - bool b; - alias b this; - } - alias A = Array!S; - alias B = Array!(shared bool); -} - -unittest //11884 -{ - auto a = Array!int([1, 2, 2].filter!"true"()); -} - -// BinaryHeap -/** -Implements a $(WEB en.wikipedia.org/wiki/Binary_heap, binary heap) -container on top of a given random-access range type (usually $(D -T[])) or a random-access container type (usually $(D Array!T)). The -documentation of $(D BinaryHeap) will refer to the underlying range or -container as the $(I store) of the heap. - -The binary heap induces structure over the underlying store such that -accessing the largest element (by using the $(D front) property) is a -$(BIGOH 1) operation and extracting it (by using the $(D -removeFront()) method) is done fast in $(BIGOH log n) time. - -If $(D less) is the less-than operator, which is the default option, -then $(D BinaryHeap) defines a so-called max-heap that optimizes -extraction of the $(I largest) elements. To define a min-heap, -instantiate BinaryHeap with $(D "a > b") as its predicate. - -Simply extracting elements from a $(D BinaryHeap) container is -tantamount to lazily fetching elements of $(D Store) in descending -order. Extracting elements from the $(D BinaryHeap) to completion -leaves the underlying store sorted in ascending order but, again, -yields elements in descending order. - -If $(D Store) is a range, the $(D BinaryHeap) cannot grow beyond the -size of that range. If $(D Store) is a container that supports $(D -insertBack), the $(D BinaryHeap) may grow by adding elements to the -container. - -Example: ----- -// Example from "Introduction to Algorithms" Cormen et al, p 146 -int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; -auto h = heapify(a); -// largest element -assert(h.front == 16); -// a has the heap property -assert(equal(a, [ 16, 14, 10, 9, 8, 7, 4, 3, 2, 1 ])); ----- - */ -struct BinaryHeap(Store, alias less = "a < b") -if (isRandomAccessRange!(Store) || isRandomAccessRange!(typeof(Store.init[]))) -{ -// Really weird @@BUG@@: if you comment out the "private:" label below, -// std.algorithm can't unittest anymore -//private: - - // The payload includes the support store and the effective length - private static struct Data - { - Store _store; - size_t _length; - } - private RefCounted!(Data, RefCountedAutoInitialize.no) _payload; - // Comparison predicate - private alias binaryFun!(less) comp; - // Convenience accessors - private @property ref Store _store() - { - assert(_payload.refCountedStore.isInitialized); - return _payload._store; - } - private @property ref size_t _length() - { - assert(_payload.refCountedStore.isInitialized); - return _payload._length; - } - - // Asserts that the heap property is respected. - private void assertValid() - { - debug - { - if (!_payload.refCountedStore.isInitialized) return; - if (_length < 2) return; - for (size_t n = _length - 1; n >= 1; --n) - { - auto parentIdx = (n - 1) / 2; - assert(!comp(_store[parentIdx], _store[n]), text(n)); - } - } - } - - // Assuming the element at index i perturbs the heap property in - // store r, percolates it down the heap such that the heap - // property is restored. - private void percolateDown(Store r, size_t i, size_t length) - { - for (;;) - { - auto left = i * 2 + 1, right = left + 1; - if (right == length) - { - if (comp(r[i], r[left])) swap(r, i, left); - return; - } - if (right > length) return; - assert(left < length && right < length); - auto largest = comp(r[i], r[left]) - ? (comp(r[left], r[right]) ? right : left) - : (comp(r[i], r[right]) ? right : i); - if (largest == i) return; - swap(r, i, largest); - i = largest; - } - } - - // @@@BUG@@@: add private here, std.algorithm doesn't unittest anymore - /*private*/ void pop(Store store) - { - assert(!store.empty); - if (store.length == 1) return; - auto t1 = moveFront(store[]); - auto t2 = moveBack(store[]); - store.front = move(t2); - store.back = move(t1); - percolateDown(store, 0, store.length - 1); - } - - /*private*/ static void swap(Store _store, size_t i, size_t j) - { - static if (is(typeof(swap(_store[i], _store[j])))) - { - swap(_store[i], _store[j]); - } - else static if (is(typeof(_store.moveAt(i)))) - { - auto t1 = _store.moveAt(i); - auto t2 = _store.moveAt(j); - _store[i] = move(t2); - _store[j] = move(t1); - } - else // assume it's a container and access its range with [] - { - auto t1 = _store[].moveAt(i); - auto t2 = _store[].moveAt(j); - _store[i] = move(t2); - _store[j] = move(t1); - } - } - -public: - - /** - Converts the store $(D s) into a heap. If $(D initialSize) is - specified, only the first $(D initialSize) elements in $(D s) - are transformed into a heap, after which the heap can grow up - to $(D r.length) (if $(D Store) is a range) or indefinitely (if - $(D Store) is a container with $(D insertBack)). Performs - $(BIGOH min(r.length, initialSize)) evaluations of $(D less). - */ - this(Store s, size_t initialSize = size_t.max) - { - acquire(s, initialSize); - } - -/** -Takes ownership of a store. After this, manipulating $(D s) may make -the heap work incorrectly. - */ - void acquire(Store s, size_t initialSize = size_t.max) - { - _payload.refCountedStore.ensureInitialized(); - _store = move(s); - _length = min(_store.length, initialSize); - if (_length < 2) return; - for (auto i = (_length - 2) / 2; ; ) - { - this.percolateDown(_store, i, _length); - if (i-- == 0) break; - } - assertValid(); - } - -/** -Takes ownership of a store assuming it already was organized as a -heap. - */ - void assume(Store s, size_t initialSize = size_t.max) - { - _payload.refCountedStore.ensureInitialized(); - _store = s; - _length = min(_store.length, initialSize); - assertValid(); - } - -/** -Clears the heap. Returns the portion of the store from $(D 0) up to -$(D length), which satisfies the $(LUCKY heap property). - */ - auto release() - { - if (!_payload.refCountedStore.isInitialized) - { - return typeof(_store[0 .. _length]).init; - } - assertValid(); - auto result = _store[0 .. _length]; - _payload = _payload.init; - return result; - } - -/** -Returns $(D true) if the heap is _empty, $(D false) otherwise. - */ - @property bool empty() - { - return !length; - } - -/** -Returns a duplicate of the heap. The underlying store must also -support a $(D dup) method. - */ - @property BinaryHeap dup() - { - BinaryHeap result; - if (!_payload.refCountedStore.isInitialized) return result; - result.assume(_store.dup, length); - return result; - } - -/** -Returns the _length of the heap. - */ - @property size_t length() - { - return _payload.refCountedStore.isInitialized ? _length : 0; - } - -/** -Returns the _capacity of the heap, which is the length of the -underlying store (if the store is a range) or the _capacity of the -underlying store (if the store is a container). - */ - @property size_t capacity() - { - if (!_payload.refCountedStore.isInitialized) return 0; - static if (is(typeof(_store.capacity) : size_t)) - { - return _store.capacity; - } - else - { - return _store.length; - } - } - -/** -Returns a copy of the _front of the heap, which is the largest element -according to $(D less). - */ - @property ElementType!Store front() - { - enforce(!empty); - return _store.front; - } - -/** -Clears the heap by detaching it from the underlying store. - */ - void clear() - { - _payload = _payload.init; - } - -/** -Inserts $(D value) into the store. If the underlying store is a range -and $(D length == capacity), throws an exception. - */ - size_t insert(ElementType!Store value) - { - static if (is(typeof(_store.insertBack(value)))) - { - _payload.refCountedStore.ensureInitialized(); - if (length == _store.length) - { - // reallocate - _store.insertBack(value); - } - else - { - // no reallocation - _store[_length] = value; - } - } - else - { - // can't grow - enforce(length < _store.length, - "Cannot grow a heap created over a range"); - _store[_length] = value; - } - - // sink down the element - for (size_t n = _length; n; ) - { - auto parentIdx = (n - 1) / 2; - if (!comp(_store[parentIdx], _store[n])) break; // done! - // must swap and continue - swap(_store, parentIdx, n); - n = parentIdx; - } - ++_length; - assertValid(); - return 1; - } - -/** -Removes the largest element from the heap. - */ - void removeFront() - { - enforce(!empty); - if (_length > 1) - { - auto t1 = moveFront(_store[]); - auto t2 = moveAt(_store[], _length - 1); - _store.front = move(t2); - _store[_length - 1] = move(t1); - } - --_length; - percolateDown(_store, 0, _length); - } - -/** -Removes the largest element from the heap and returns a copy of -it. The element still resides in the heap's store. For performance -reasons you may want to use $(D removeFront) with heaps of objects -that are expensive to copy. - */ - ElementType!Store removeAny() - { - removeFront(); - return _store[_length]; - } - -/** -Replaces the largest element in the store with $(D value). - */ - void replaceFront(ElementType!Store value) - { - // must replace the top - assert(!empty); - _store.front = value; - percolateDown(_store, 0, _length); - assertValid(); - } - -/** -If the heap has room to grow, inserts $(D value) into the store and -returns $(D true). Otherwise, if $(D less(value, front)), calls $(D -replaceFront(value)) and returns again $(D true). Otherwise, leaves -the heap unaffected and returns $(D false). This method is useful in -scenarios where the smallest $(D k) elements of a set of candidates -must be collected. - */ - bool conditionalInsert(ElementType!Store value) - { - _payload.refCountedStore.ensureInitialized(); - if (_length < _store.length) - { - insert(value); - return true; - } - // must replace the top - assert(!_store.empty); - if (!comp(value, _store.front)) return false; // value >= largest - _store.front = value; - percolateDown(_store, 0, _length); - assertValid(); - return true; - } -} - -/** -Convenience function that returns a $(D BinaryHeap!Store) object -initialized with $(D s) and $(D initialSize). - */ -BinaryHeap!(Store, less) heapify(alias less = "a < b", Store)(Store s, - size_t initialSize = size_t.max) -{ - return BinaryHeap!(Store, less)(s, initialSize); -} - -unittest -{ - { - // example from "Introduction to Algorithms" Cormen et al., p 146 - int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; - auto h = heapify(a); - h = heapify!"a < b"(a); - assert(h.front == 16); - assert(a == [ 16, 14, 10, 8, 7, 9, 3, 2, 4, 1 ]); - auto witness = [ 16, 14, 10, 9, 8, 7, 4, 3, 2, 1 ]; - for (; !h.empty; h.removeFront(), witness.popFront()) - { - assert(!witness.empty); - assert(witness.front == h.front); - } - assert(witness.empty); - } - { - int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; - int[] b = new int[a.length]; - BinaryHeap!(int[]) h = BinaryHeap!(int[])(b, 0); - foreach (e; a) - { - h.insert(e); - } - assert(b == [ 16, 14, 10, 8, 7, 3, 9, 1, 4, 2 ], text(b)); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Array!bool -//////////////////////////////////////////////////////////////////////////////// - -/** -_Array specialized for $(D bool). Packs together values efficiently by -allocating one bit per element. - */ -struct Array(T) -if (is(Unqual!T == bool)) -{ - static immutable uint bitsPerWord = size_t.sizeof * 8; - private static struct Data - { - Array!size_t.Payload _backend; - size_t _length; - } - private RefCounted!(Data, RefCountedAutoInitialize.no) _store; - - private @property ref size_t[] data() - { - assert(_store.refCountedStore.isInitialized); - return _store._backend._payload; - } - - /** - Defines the container's primary range. - */ - struct Range - { - private Array _outer; - private size_t _a, _b; - /// Range primitives - @property Range save() - { - version (bug4437) - { - return this; - } - else - { - auto copy = this; - return copy; - } - } - /// Ditto - @property bool empty() - { - return _a >= _b || _outer.length < _b; - } - /// Ditto - @property T front() - { - enforce(!empty); - return _outer[_a]; - } - /// Ditto - @property void front(bool value) - { - enforce(!empty); - _outer[_a] = value; - } - /// Ditto - T moveFront() - { - enforce(!empty); - return _outer.moveAt(_a); - } - /// Ditto - void popFront() - { - enforce(!empty); - ++_a; - } - /// Ditto - @property T back() - { - enforce(!empty); - return _outer[_b - 1]; - } - /// Ditto - @property void back(bool value) - { - enforce(!empty); - _outer[_b - 1] = value; - } - /// Ditto - T moveBack() - { - enforce(!empty); - return _outer.moveAt(_b - 1); - } - /// Ditto - void popBack() - { - enforce(!empty); - --_b; - } - /// Ditto - T opIndex(size_t i) - { - return _outer[_a + i]; - } - /// Ditto - void opIndexAssign(T value, size_t i) - { - _outer[_a + i] = value; - } - /// Ditto - T moveAt(size_t i) - { - return _outer.moveAt(_a + i); - } - /// Ditto - @property size_t length() const - { - assert(_a <= _b); - return _b - _a; - } - alias opDollar = length; - /// ditto - Range opSlice(size_t low, size_t high) - { - assert(_a <= low && low <= high && high <= _b); - return Range(_outer, _a + low, _a + high); - } - } - - /** - Property returning $(D true) if and only if the container has - no elements. - - Complexity: $(BIGOH 1) - */ - @property bool empty() - { - return !length; - } - - unittest - { - Array!bool a; - //a._store._refCountedDebug = true; - assert(a.empty); - a.insertBack(false); - assert(!a.empty); - } - - /** - Returns a duplicate of the container. The elements themselves - are not transitively duplicated. - - Complexity: $(BIGOH n). - */ - @property Array dup() - { - Array result; - result.insertBack(this[]); - return result; - } - - unittest - { - Array!bool a; - assert(a.empty); - auto b = a.dup; - assert(b.empty); - a.insertBack(true); - assert(b.empty); - } - - /** - Returns the number of elements in the container. - - Complexity: $(BIGOH log(n)). - */ - @property size_t length() const - { - return _store.refCountedStore.isInitialized ? _store._length : 0; - } - size_t opDollar() const - { - return length; - } - - unittest - { - Array!bool a; - assert(a.length == 0); - a.insert(true); - assert(a.length == 1, text(a.length)); - } - - /** - Returns the maximum number of elements the container can store - without (a) allocating memory, (b) invalidating iterators upon - insertion. - - Complexity: $(BIGOH log(n)). - */ - @property size_t capacity() - { - return _store.refCountedStore.isInitialized - ? cast(size_t) bitsPerWord * _store._backend.capacity - : 0; - } - - unittest - { - Array!bool a; - assert(a.capacity == 0); - foreach (i; 0 .. 100) - { - a.insert(true); - assert(a.capacity >= a.length, text(a.capacity)); - } - } - - /** - Ensures sufficient capacity to accommodate $(D n) elements. - - Postcondition: $(D capacity >= n) - - Complexity: $(BIGOH log(e - capacity)) if $(D e > capacity), - otherwise $(BIGOH 1). - */ - void reserve(size_t e) - { - _store.refCountedStore.ensureInitialized(); - _store._backend.reserve(to!size_t((e + bitsPerWord - 1) / bitsPerWord)); - } - - unittest - { - Array!bool a; - assert(a.capacity == 0); - a.reserve(15657); - assert(a.capacity >= 15657); - } - - /** - Returns a range that iterates over all elements of the - container, in a container-defined order. The container should - choose the most convenient and fast method of iteration for $(D - opSlice()). - - Complexity: $(BIGOH log(n)) - */ - Range opSlice() - { - return Range(this, 0, length); - } - - unittest - { - Array!bool a; - a.insertBack([true, false, true, true]); - assert(a[].length == 4); - } - - /** - Returns a range that iterates the container between two - specified positions. - - Complexity: $(BIGOH log(n)) - */ - Range opSlice(size_t a, size_t b) - { - enforce(a <= b && b <= length); - return Range(this, a, b); - } - - unittest - { - Array!bool a; - a.insertBack([true, false, true, true]); - assert(a[0 .. 2].length == 2); - } - - /** - Equivalent to $(D opSlice().front) and $(D opSlice().back), - respectively. - - Complexity: $(BIGOH log(n)) - */ - @property bool front() - { - enforce(!empty); - return data.ptr[0] & 1; - } - - /// Ditto - @property void front(bool value) - { - enforce(!empty); - if (value) data.ptr[0] |= 1; - else data.ptr[0] &= ~cast(size_t) 1; - } - - unittest - { - Array!bool a; - a.insertBack([true, false, true, true]); - assert(a.front); - a.front = false; - assert(!a.front); - } - - /// Ditto - @property bool back() - { - enforce(!empty); - return cast(bool)(data.back & (cast(size_t)1 << ((_store._length - 1) % bitsPerWord))); - } - - /// Ditto - @property void back(bool value) - { - enforce(!empty); - if (value) - { - data.back |= (cast(size_t)1 << ((_store._length - 1) % bitsPerWord)); - } - else - { - data.back &= - ~(cast(size_t)1 << ((_store._length - 1) % bitsPerWord)); - } - } - - unittest - { - Array!bool a; - a.insertBack([true, false, true, true]); - assert(a.back); - a.back = false; - assert(!a.back); - } - - /** - Indexing operators yield or modify the value at a specified index. - */ - bool opIndex(size_t i) - { - auto div = cast(size_t) (i / bitsPerWord); - auto rem = i % bitsPerWord; - enforce(div < data.length); - return cast(bool)(data.ptr[div] & (cast(size_t)1 << rem)); - } - /// ditto - void opIndexAssign(bool value, size_t i) - { - auto div = cast(size_t) (i / bitsPerWord); - auto rem = i % bitsPerWord; - enforce(div < data.length); - if (value) data.ptr[div] |= (cast(size_t)1 << rem); - else data.ptr[div] &= ~(cast(size_t)1 << rem); - } - /// ditto - void opIndexOpAssign(string op)(bool value, size_t i) - { - auto div = cast(size_t) (i / bitsPerWord); - auto rem = i % bitsPerWord; - enforce(div < data.length); - auto oldValue = cast(bool) (data.ptr[div] & (cast(size_t)1 << rem)); - // Do the deed - auto newValue = mixin("oldValue "~op~" value"); - // Write back the value - if (newValue != oldValue) - { - if (newValue) data.ptr[div] |= (cast(size_t)1 << rem); - else data.ptr[div] &= ~(cast(size_t)1 << rem); - } - } - /// Ditto - T moveAt(size_t i) - { - return this[i]; - } - - unittest - { - Array!bool a; - a.insertBack([true, false, true, true]); - assert(a[0] && !a[1]); - a[0] &= a[1]; - assert(!a[0]); - } - - /** - Returns a new container that's the concatenation of $(D this) - and its argument. - - Complexity: $(BIGOH n + m), where m is the number of elements - in $(D stuff) - */ - Array!bool opBinary(string op, Stuff)(Stuff rhs) if (op == "~") - { - auto result = this; - return result ~= rhs; - } - - unittest - { - Array!bool a; - a.insertBack([true, false, true, true]); - Array!bool b; - b.insertBack([true, true, false, true]); - assert(equal((a ~ b)[], - [true, false, true, true, true, true, false, true])); - } - - // /// ditto - // TotalContainer opBinaryRight(Stuff, string op)(Stuff lhs) if (op == "~") - // { - // assert(0); - // } - - /** - Forwards to $(D insertAfter(this[], stuff)). - */ - // @@@BUG@@@ - //ref Array!bool opOpAssign(string op, Stuff)(Stuff stuff) if (op == "~") - Array!bool opOpAssign(string op, Stuff)(Stuff stuff) if (op == "~") - { - static if (is(typeof(stuff[]))) insertBack(stuff[]); - else insertBack(stuff); - return this; - } - - unittest - { - Array!bool a; - a.insertBack([true, false, true, true]); - Array!bool b; - a.insertBack([false, true, false, true, true]); - a ~= b; - assert(equal( - a[], - [true, false, true, true, false, true, false, true, true])); - } - - /** - Removes all contents from the container. The container decides - how $(D capacity) is affected. - - Postcondition: $(D empty) - - Complexity: $(BIGOH n) - */ - void clear() - { - this = Array(); - } - - unittest - { - Array!bool a; - a.insertBack([true, false, true, true]); - a.clear(); - assert(a.capacity == 0); - } - - /** - Sets the number of elements in the container to $(D - newSize). If $(D newSize) is greater than $(D length), the - added elements are added to the container and initialized with - $(D ElementType.init). - - Complexity: $(BIGOH abs(n - newLength)) - - Postcondition: $(D _length == newLength) - */ - @property void length(size_t newLength) - { - _store.refCountedStore.ensureInitialized(); - auto newDataLength = - to!size_t((newLength + bitsPerWord - 1) / bitsPerWord); - _store._backend.length = newDataLength; - _store._length = newLength; - } - - unittest - { - Array!bool a; - a.length = 1057; - assert(a.length == 1057); - foreach (e; a) - { - assert(!e); - } - } - - /** - Inserts $(D stuff) in the container. $(D stuff) can be a value - convertible to $(D ElementType) or a range of objects - convertible to $(D ElementType). - - The $(D stable) version guarantees that ranges iterating over - the container are never invalidated. Client code that counts on - non-invalidating insertion should use $(D stableInsert). - - Returns: The number of elements added. - - Complexity: $(BIGOH m * log(n)), where $(D m) is the number of - elements in $(D stuff) - */ - alias insertBack insert; - ///ditto - alias insertBack stableInsert; - - /** - Same as $(D insert(stuff)) and $(D stableInsert(stuff)) - respectively, but relax the complexity constraint to linear. - */ - alias insertBack linearInsert; - ///ditto - alias insertBack stableLinearInsert; - - /** - Picks one value in the container, removes it from the - container, and returns it. The stable version behaves the same, - but guarantees that ranges iterating over the container are - never invalidated. - - Precondition: $(D !empty) - - Returns: The element removed. - - Complexity: $(BIGOH log(n)) - */ - T removeAny() - { - auto result = back; - removeBack(); - return result; - } - /// ditto - alias removeAny stableRemoveAny; - - unittest - { - Array!bool a; - a.length = 1057; - assert(!a.removeAny()); - assert(a.length == 1056); - foreach (e; a) - { - assert(!e); - } - } - - /** - Inserts $(D value) to the back of the container. $(D stuff) can - be a value convertible to $(D ElementType) or a range of - objects convertible to $(D ElementType). The stable version - behaves the same, but guarantees that ranges iterating over the - container are never invalidated. - - Returns: The number of elements inserted - - Complexity: $(BIGOH log(n)) - */ - size_t insertBack(Stuff)(Stuff stuff) if (is(Stuff : bool)) - { - _store.refCountedStore.ensureInitialized(); - auto rem = _store._length % bitsPerWord; - if (rem) - { - // Fits within the current array - if (stuff) - { - data[$ - 1] |= (1u << rem); - } - else - { - data[$ - 1] &= ~(1u << rem); - } - } - else - { - // Need to add more data - _store._backend.insertBack(stuff); - } - ++_store._length; - return 1; - } - /// Ditto - size_t insertBack(Stuff)(Stuff stuff) - if (isInputRange!Stuff && is(ElementType!Stuff : bool)) - { - static if (!hasLength!Stuff) size_t result; - for (; !stuff.empty; stuff.popFront()) - { - insertBack(stuff.front); - static if (!hasLength!Stuff) ++result; - } - static if (!hasLength!Stuff) return result; - else return stuff.length; - } - /// ditto - alias insertBack stableInsertBack; - - /** - Removes the value at the front or back of the container. The - stable version behaves the same, but guarantees that ranges - iterating over the container are never invalidated. The - optional parameter $(D howMany) instructs removal of that many - elements. If $(D howMany > n), all elements are removed and no - exception is thrown. - - Precondition: $(D !empty) - - Complexity: $(BIGOH log(n)). - */ - void removeBack() - { - enforce(_store._length); - if (_store._length % bitsPerWord) - { - // Cool, just decrease the length - --_store._length; - } - else - { - // Reduce the allocated space - --_store._length; - _store._backend.length = _store._backend.length - 1; - } - } - /// ditto - alias removeBack stableRemoveBack; - - /** - Removes $(D howMany) values at the front or back of the - container. Unlike the unparameterized versions above, these - functions do not throw if they could not remove $(D howMany) - elements. Instead, if $(D howMany > n), all elements are - removed. The returned value is the effective number of elements - removed. The stable version behaves the same, but guarantees - that ranges iterating over the container are never invalidated. - - Returns: The number of elements removed - - Complexity: $(BIGOH howMany * log(n)). - */ - /// ditto - size_t removeBack(size_t howMany) - { - if (howMany >= length) - { - howMany = length; - clear(); - } - else - { - length = length - howMany; - } - return howMany; - } - - unittest - { - Array!bool a; - a.length = 1057; - assert(a.removeBack(1000) == 1000); - assert(a.length == 57); - foreach (e; a) - { - assert(!e); - } - } - - /** - Inserts $(D stuff) before, after, or instead range $(D r), - which must be a valid range previously extracted from this - container. $(D stuff) can be a value convertible to $(D - ElementType) or a range of objects convertible to $(D - ElementType). The stable version behaves the same, but - guarantees that ranges iterating over the container are never - invalidated. - - Returns: The number of values inserted. - - Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff) - */ - size_t insertBefore(Stuff)(Range r, Stuff stuff) - { - // TODO: make this faster, it moves one bit at a time - immutable inserted = stableInsertBack(stuff); - immutable tailLength = length - inserted; - bringToFront( - this[r._a .. tailLength], - this[tailLength .. length]); - return inserted; - } - /// ditto - alias insertBefore stableInsertBefore; - - unittest - { - Array!bool a; - version (bugxxxx) - { - a._store.refCountedDebug = true; - } - a.insertBefore(a[], true); - assert(a.length == 1, text(a.length)); - a.insertBefore(a[], false); - assert(a.length == 2, text(a.length)); - } - - /// ditto - size_t insertAfter(Stuff)(Range r, Stuff stuff) - { - // TODO: make this faster, it moves one bit at a time - immutable inserted = stableInsertBack(stuff); - immutable tailLength = length - inserted; - bringToFront( - this[r._b .. tailLength], - this[tailLength .. length]); - return inserted; - } - /// ditto - alias insertAfter stableInsertAfter; - - unittest - { - Array!bool a; - a.length = 10; - a.insertAfter(a[0 .. 5], true); - assert(a.length == 11, text(a.length)); - assert(a[5]); - } - /// ditto - size_t replace(Stuff)(Range r, Stuff stuff) if (is(Stuff : bool)) - { - if (!r.empty) - { - // There is room - r.front = stuff; - r.popFront(); - linearRemove(r); - } - else - { - // No room, must insert - insertBefore(r, stuff); - } - return 1; - } - /// ditto - alias replace stableReplace; - - unittest - { - Array!bool a; - a.length = 10; - a.replace(a[3 .. 5], true); - assert(a.length == 9, text(a.length)); - assert(a[3]); - } - - /** - Removes all elements belonging to $(D r), which must be a range - obtained originally from this container. The stable version - behaves the same, but guarantees that ranges iterating over the - container are never invalidated. - - Returns: A range spanning the remaining elements in the container that - initially were right after $(D r). - - Complexity: $(BIGOH n) - */ - Range linearRemove(Range r) - { - copy(this[r._b .. length], this[r._a .. length]); - length = length - r.length; - return this[r._a .. length]; - } - /// ditto - alias linearRemove stableLinearRemove; -} - -unittest -{ - Array!bool a; - assert(a.empty); -} - -unittest -{ - Array!bool arr; - arr.insert([false, false, false, false]); - assert(arr.front == false); - assert(arr.back == false); - assert(arr[1] == false); - auto slice = arr[]; - slice = arr[0 .. $]; - slice = slice[1 .. $]; - slice.front = true; - slice.back = true; - slice[1] = true; - assert(slice.front == true); - assert(slice.back == true); - assert(slice[1] == true); - assert(slice.moveFront == true); - assert(slice.moveBack == true); - assert(slice.moveAt(1) == true); -} - -/* - * Implementation for a Red Black node for use in a Red Black Tree (see below) - * - * this implementation assumes we have a marker Node that is the parent of the - * root Node. This marker Node is not a valid Node, but marks the end of the - * collection. The root is the left child of the marker Node, so it is always - * last in the collection. The marker Node is passed in to the setColor - * function, and the Node which has this Node as its parent is assumed to be - * the root Node. - * - * A Red Black tree should have O(lg(n)) insertion, removal, and search time. - */ -struct RBNode(V) -{ - /* - * Convenience alias - */ - alias RBNode* Node; - - private Node _left; - private Node _right; - private Node _parent; - - /** - * The value held by this node - */ - V value; - - /** - * Enumeration determining what color the node is. Null nodes are assumed - * to be black. - */ - enum Color : byte - { - Red, - Black - } - - /** - * The color of the node. - */ - Color color; - - /** - * Get the left child - */ - @property Node left() - { - return _left; - } - - /** - * Get the right child - */ - @property Node right() - { - return _right; - } - - /** - * Get the parent - */ - @property Node parent() - { - return _parent; - } - - /** - * Set the left child. Also updates the new child's parent node. This - * does not update the previous child. - * - * Returns newNode - */ - @property Node left(Node newNode) - { - _left = newNode; - if(newNode !is null) - newNode._parent = &this; - return newNode; - } - - /** - * Set the right child. Also updates the new child's parent node. This - * does not update the previous child. - * - * Returns newNode - */ - @property Node right(Node newNode) - { - _right = newNode; - if(newNode !is null) - newNode._parent = &this; - return newNode; - } - - // assume _left is not null - // - // performs rotate-right operation, where this is T, _right is R, _left is - // L, _parent is P: - // - // P P - // | -> | - // T L - // / \ / \ - // L R a T - // / \ / \ - // a b b R - // - /** - * Rotate right. This performs the following operations: - * - The left child becomes the parent of this node. - * - This node becomes the new parent's right child. - * - The old right child of the new parent becomes the left child of this - * node. - */ - Node rotateR() - in - { - assert(_left !is null); - } - body - { - // sets _left._parent also - if(isLeftNode) - parent.left = _left; - else - parent.right = _left; - Node tmp = _left._right; - - // sets _parent also - _left.right = &this; - - // sets tmp._parent also - left = tmp; - - return &this; - } - - // assumes _right is non null - // - // performs rotate-left operation, where this is T, _right is R, _left is - // L, _parent is P: - // - // P P - // | -> | - // T R - // / \ / \ - // L R T b - // / \ / \ - // a b L a - // - /** - * Rotate left. This performs the following operations: - * - The right child becomes the parent of this node. - * - This node becomes the new parent's left child. - * - The old left child of the new parent becomes the right child of this - * node. - */ - Node rotateL() - in - { - assert(_right !is null); - } - body - { - // sets _right._parent also - if(isLeftNode) - parent.left = _right; - else - parent.right = _right; - Node tmp = _right._left; - - // sets _parent also - _right.left = &this; - - // sets tmp._parent also - right = tmp; - return &this; - } - - - /** - * Returns true if this node is a left child. - * - * Note that this should always return a value because the root has a - * parent which is the marker node. - */ - @property bool isLeftNode() const - in - { - assert(_parent !is null); - } - body - { - return _parent._left is &this; - } - - /** - * Set the color of the node after it is inserted. This performs an - * update to the whole tree, possibly rotating nodes to keep the Red-Black - * properties correct. This is an O(lg(n)) operation, where n is the - * number of nodes in the tree. - * - * end is the marker node, which is the parent of the topmost valid node. - */ - void setColor(Node end) - { - // test against the marker node - if(_parent !is end) - { - if(_parent.color == Color.Red) - { - Node cur = &this; - while(true) - { - // because root is always black, _parent._parent always exists - if(cur._parent.isLeftNode) - { - // parent is left node, y is 'uncle', could be null - Node y = cur._parent._parent._right; - if(y !is null && y.color == Color.Red) - { - cur._parent.color = Color.Black; - y.color = Color.Black; - cur = cur._parent._parent; - if(cur._parent is end) - { - // root node - cur.color = Color.Black; - break; - } - else - { - // not root node - cur.color = Color.Red; - if(cur._parent.color == Color.Black) - // satisfied, exit the loop - break; - } - } - else - { - if(!cur.isLeftNode) - cur = cur._parent.rotateL(); - cur._parent.color = Color.Black; - cur = cur._parent._parent.rotateR(); - cur.color = Color.Red; - // tree should be satisfied now - break; - } - } - else - { - // parent is right node, y is 'uncle' - Node y = cur._parent._parent._left; - if(y !is null && y.color == Color.Red) - { - cur._parent.color = Color.Black; - y.color = Color.Black; - cur = cur._parent._parent; - if(cur._parent is end) - { - // root node - cur.color = Color.Black; - break; - } - else - { - // not root node - cur.color = Color.Red; - if(cur._parent.color == Color.Black) - // satisfied, exit the loop - break; - } - } - else - { - if(cur.isLeftNode) - cur = cur._parent.rotateR(); - cur._parent.color = Color.Black; - cur = cur._parent._parent.rotateL(); - cur.color = Color.Red; - // tree should be satisfied now - break; - } - } - } - - } - } - else - { - // - // this is the root node, color it black - // - color = Color.Black; - } - } - - /** - * Remove this node from the tree. The 'end' node is used as the marker - * which is root's parent. Note that this cannot be null! - * - * Returns the next highest valued node in the tree after this one, or end - * if this was the highest-valued node. - */ - Node remove(Node end) - { - // - // remove this node from the tree, fixing the color if necessary. - // - Node x; - Node ret; - if(_left is null || _right is null) - { - ret = next; - } - else - { - // - // normally, we can just swap this node's and y's value, but - // because an iterator could be pointing to y and we don't want to - // disturb it, we swap this node and y's structure instead. This - // can also be a benefit if the value of the tree is a large - // struct, which takes a long time to copy. - // - Node yp, yl, yr; - Node y = next; - yp = y._parent; - yl = y._left; - yr = y._right; - auto yc = y.color; - auto isyleft = y.isLeftNode; - - // - // replace y's structure with structure of this node. - // - if(isLeftNode) - _parent.left = y; - else - _parent.right = y; - // - // need special case so y doesn't point back to itself - // - y.left = _left; - if(_right is y) - y.right = &this; - else - y.right = _right; - y.color = color; - - // - // replace this node's structure with structure of y. - // - left = yl; - right = yr; - if(_parent !is y) - { - if(isyleft) - yp.left = &this; - else - yp.right = &this; - } - color = yc; - - // - // set return value - // - ret = y; - } - - // if this has less than 2 children, remove it - if(_left !is null) - x = _left; - else - x = _right; - - // remove this from the tree at the end of the procedure - bool removeThis = false; - if(x is null) - { - // pretend this is a null node, remove this on finishing - x = &this; - removeThis = true; - } - else if(isLeftNode) - _parent.left = x; - else - _parent.right = x; - - // if the color of this is black, then it needs to be fixed - if(color == color.Black) - { - // need to recolor the tree. - while(x._parent !is end && x.color == Node.Color.Black) - { - if(x.isLeftNode) - { - // left node - Node w = x._parent._right; - if(w.color == Node.Color.Red) - { - w.color = Node.Color.Black; - x._parent.color = Node.Color.Red; - x._parent.rotateL(); - w = x._parent._right; - } - Node wl = w.left; - Node wr = w.right; - if((wl is null || wl.color == Node.Color.Black) && - (wr is null || wr.color == Node.Color.Black)) - { - w.color = Node.Color.Red; - x = x._parent; - } - else - { - if(wr is null || wr.color == Node.Color.Black) - { - // wl cannot be null here - wl.color = Node.Color.Black; - w.color = Node.Color.Red; - w.rotateR(); - w = x._parent._right; - } - - w.color = x._parent.color; - x._parent.color = Node.Color.Black; - w._right.color = Node.Color.Black; - x._parent.rotateL(); - x = end.left; // x = root - } - } - else - { - // right node - Node w = x._parent._left; - if(w.color == Node.Color.Red) - { - w.color = Node.Color.Black; - x._parent.color = Node.Color.Red; - x._parent.rotateR(); - w = x._parent._left; - } - Node wl = w.left; - Node wr = w.right; - if((wl is null || wl.color == Node.Color.Black) && - (wr is null || wr.color == Node.Color.Black)) - { - w.color = Node.Color.Red; - x = x._parent; - } - else - { - if(wl is null || wl.color == Node.Color.Black) - { - // wr cannot be null here - wr.color = Node.Color.Black; - w.color = Node.Color.Red; - w.rotateL(); - w = x._parent._left; - } - - w.color = x._parent.color; - x._parent.color = Node.Color.Black; - w._left.color = Node.Color.Black; - x._parent.rotateR(); - x = end.left; // x = root - } - } - } - x.color = Node.Color.Black; - } - - if(removeThis) - { - // - // clear this node out of the tree - // - if(isLeftNode) - _parent.left = null; - else - _parent.right = null; - } - - return ret; - } - - /** - * Return the leftmost descendant of this node. - */ - @property Node leftmost() - { - Node result = &this; - while(result._left !is null) - result = result._left; - return result; - } - - /** - * Return the rightmost descendant of this node - */ - @property Node rightmost() - { - Node result = &this; - while(result._right !is null) - result = result._right; - return result; - } - - /** - * Returns the next valued node in the tree. - * - * You should never call this on the marker node, as it is assumed that - * there is a valid next node. - */ - @property Node next() - { - Node n = &this; - if(n.right is null) - { - while(!n.isLeftNode) - n = n._parent; - return n._parent; - } - else - return n.right.leftmost; - } - - /** - * Returns the previous valued node in the tree. - * - * You should never call this on the leftmost node of the tree as it is - * assumed that there is a valid previous node. - */ - @property Node prev() - { - Node n = &this; - if(n.left is null) - { - while(n.isLeftNode) - n = n._parent; - return n._parent; - } - else - return n.left.rightmost; - } - - Node dup(scope Node delegate(V v) alloc) - { - // - // duplicate this and all child nodes - // - // The recursion should be lg(n), so we shouldn't have to worry about - // stack size. - // - Node copy = alloc(value); - copy.color = color; - if(_left !is null) - copy.left = _left.dup(alloc); - if(_right !is null) - copy.right = _right.dup(alloc); - return copy; - } - - Node dup() - { - Node copy = new RBNode!V; - copy.value = value; - copy.color = color; - if(_left !is null) - copy.left = _left.dup(); - if(_right !is null) - copy.right = _right.dup(); - return copy; - } -} - -/** - * Implementation of a $(LUCKY red-black tree) container. - * - * All inserts, removes, searches, and any function in general has complexity - * of $(BIGOH lg(n)). - * - * To use a different comparison than $(D "a < b"), pass a different operator string - * that can be used by $(XREF functional, binaryFun), or pass in a - * function, delegate, functor, or any type where $(D less(a, b)) results in a $(D bool) - * value. - * - * Note that less should produce a strict ordering. That is, for two unequal - * elements $(D a) and $(D b), $(D less(a, b) == !less(b, a)). $(D less(a, a)) should - * always equal $(D false). - * - * If $(D allowDuplicates) is set to $(D true), then inserting the same element more than - * once continues to add more elements. If it is $(D false), duplicate elements are - * ignored on insertion. If duplicates are allowed, then new elements are - * inserted after all existing duplicate elements. - */ -final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) - if(is(typeof(binaryFun!less(T.init, T.init)))) -{ - alias binaryFun!less _less; - - // BUG: this must come first in the struct due to issue 2810 - - // add an element to the tree, returns the node added, or the existing node - // if it has already been added and allowDuplicates is false - - private auto _add(Elem n) - { - Node result; - static if(!allowDuplicates) - bool added = true; - - if(!_end.left) - { - _end.left = _begin = result = allocate(n); - } - else - { - Node newParent = _end.left; - Node nxt = void; - while(true) - { - if(_less(n, newParent.value)) - { - nxt = newParent.left; - if(nxt is null) - { - // - // add to right of new parent - // - newParent.left = result = allocate(n); - break; - } - } - else - { - static if(!allowDuplicates) - { - if(!_less(newParent.value, n)) - { - result = newParent; - added = false; - break; - } - } - nxt = newParent.right; - if(nxt is null) - { - // - // add to right of new parent - // - newParent.right = result = allocate(n); - break; - } - } - newParent = nxt; - } - if(_begin.left) - _begin = _begin.left; - } - - static if(allowDuplicates) - { - result.setColor(_end); - version(RBDoChecks) - check(); - ++_length; - return result; - } - else - { - if(added) - { - ++_length; - result.setColor(_end); - } - version(RBDoChecks) - check(); - return Tuple!(bool, "added", Node, "n")(added, result); - } - } - - version(unittest) - { - private enum doUnittest = isIntegral!T; - - // note, this must be final so it does not affect the vtable layout - final bool arrayEqual(T[] arr) - { - if(walkLength(this[]) == arr.length) - { - foreach(v; arr) - { - if(!(v in this)) - return false; - } - return true; - } - return false; - } - } - else - { - private enum doUnittest = false; - } - - /** - * Element type for the tree - */ - alias T Elem; - - // used for convenience - private alias RBNode!Elem.Node Node; - - private Node _end; - private Node _begin; - private size_t _length; - - private void _setup() - { - assert(!_end); //Make sure that _setup isn't run more than once. - _begin = _end = allocate(); - } - - static private Node allocate() - { - return new RBNode!Elem; - } - - static private Node allocate(Elem v) - { - auto result = allocate(); - result.value = v; - return result; - } - - /** - * The range type for $(D RedBlackTree) - */ - struct Range - { - private Node _begin; - private Node _end; - - private this(Node b, Node e) - { - _begin = b; - _end = e; - } - - /** - * Returns $(D true) if the range is _empty - */ - @property bool empty() const - { - return _begin is _end; - } - - /** - * Returns the first element in the range - */ - @property Elem front() - { - return _begin.value; - } - - /** - * Returns the last element in the range - */ - @property Elem back() - { - return _end.prev.value; - } - - /** - * pop the front element from the range - * - * complexity: amortized $(BIGOH 1) - */ - void popFront() - { - _begin = _begin.next; - } - - /** - * pop the back element from the range - * - * complexity: amortized $(BIGOH 1) - */ - void popBack() - { - _end = _end.prev; - } - - /** - * Trivial _save implementation, needed for $(D isForwardRange). - */ - @property Range save() - { - return this; - } - } - - static if(doUnittest) unittest - { - auto ts = new RedBlackTree(1, 2, 3, 4, 5); - assert(ts.length == 5); - auto r = ts[]; - - static if(less == "a < b") - auto vals = [1, 2, 3, 4, 5]; - else - auto vals = [5, 4, 3, 2, 1]; - - assert(std.algorithm.equal(r, vals)); - assert(r.front == vals.front); - assert(r.back != r.front); - auto oldfront = r.front; - auto oldback = r.back; - r.popFront(); - r.popBack(); - assert(r.front != r.back); - assert(r.front != oldfront); - assert(r.back != oldback); - assert(ts.length == 5); - } - - // find a node based on an element value - private Node _find(Elem e) - { - static if(allowDuplicates) - { - Node cur = _end.left; - Node result = null; - while(cur) - { - if(_less(cur.value, e)) - cur = cur.right; - else if(_less(e, cur.value)) - cur = cur.left; - else - { - // want to find the left-most element - result = cur; - cur = cur.left; - } - } - return result; - } - else - { - Node cur = _end.left; - while(cur) - { - if(_less(cur.value, e)) - cur = cur.right; - else if(_less(e, cur.value)) - cur = cur.left; - else - return cur; - } - return null; - } - } - - /** - * Check if any elements exist in the container. Returns $(D false) if at least - * one element exists. - */ - @property bool empty() - { - return _end.left is null; - } - - /++ - Returns the number of elements in the container. - - Complexity: $(BIGOH 1). - +/ - @property size_t length() - { - return _length; - } - - /** - * Duplicate this container. The resulting container contains a shallow - * copy of the elements. - * - * Complexity: $(BIGOH n) - */ - @property RedBlackTree dup() - { - return new RedBlackTree(_end.dup(), _length); - } - - static if(doUnittest) unittest - { - auto ts = new RedBlackTree(1, 2, 3, 4, 5); - assert(ts.length == 5); - auto ts2 = ts.dup; - assert(ts2.length == 5); - assert(std.algorithm.equal(ts[], ts2[])); - ts2.insert(cast(Elem)6); - assert(!std.algorithm.equal(ts[], ts2[])); - assert(ts.length == 5 && ts2.length == 6); - } - - /** - * Fetch a range that spans all the elements in the container. - * - * Complexity: $(BIGOH log(n)) - */ - Range opSlice() - { - return Range(_begin, _end); - } - - /** - * The front element in the container - * - * Complexity: $(BIGOH log(n)) - */ - Elem front() - { - return _begin.value; - } - - /** - * The last element in the container - * - * Complexity: $(BIGOH log(n)) - */ - Elem back() - { - return _end.prev.value; - } - - /++ - $(D in) operator. Check to see if the given element exists in the - container. - - Complexity: $(BIGOH log(n)) - +/ - bool opBinaryRight(string op)(Elem e) if (op == "in") - { - return _find(e) !is null; - } - - static if(doUnittest) unittest - { - auto ts = new RedBlackTree(1, 2, 3, 4, 5); - assert(cast(Elem)3 in ts); - assert(cast(Elem)6 !in ts); - } - - /** - * Compares two trees for equality. - * - * Complexity: $(BIGOH n*log(n)) - */ - override bool opEquals(Object rhs) - { - RedBlackTree that = cast(RedBlackTree)rhs; - if (that is null) return false; - - // If there aren't the same number of nodes, we can't be equal. - if (this._length != that._length) return false; - - // FIXME: use a more efficient algo (if one exists?) - auto thisRange = this[]; - auto thatRange = that[]; - return equal!(function(Elem a, Elem b) => !_less(a,b) && !_less(b,a)) - (thisRange, thatRange); - } - - static if(doUnittest) unittest - { - auto t1 = new RedBlackTree(1,2,3,4); - auto t2 = new RedBlackTree(1,2,3,4); - auto t3 = new RedBlackTree(1,2,3,5); - auto t4 = new RedBlackTree(1,2,3,4,5); - auto o = new Object(); - - assert(t1==t2); - assert(t1!=t3); - assert(t1!=t4); - assert(t1!=o); // pathological case, must not crash - } - - /** - * Removes all elements from the container. - * - * Complexity: $(BIGOH 1) - */ - void clear() - { - _end.left = null; - _begin = _end; - _length = 0; - } - - static if(doUnittest) unittest - { - auto ts = new RedBlackTree(1,2,3,4,5); - assert(ts.length == 5); - ts.clear(); - assert(ts.empty && ts.length == 0); - } - - /** - * Insert a single element in the container. Note that this does not - * invalidate any ranges currently iterating the container. - * - * Complexity: $(BIGOH log(n)) - */ - size_t stableInsert(Stuff)(Stuff stuff) if (isImplicitlyConvertible!(Stuff, Elem)) - { - static if(allowDuplicates) - { - _add(stuff); - return 1; - } - else - { - return(_add(stuff).added ? 1 : 0); - } - } - - /** - * Insert a range of elements in the container. Note that this does not - * invalidate any ranges currently iterating the container. - * - * Complexity: $(BIGOH m * log(n)) - */ - size_t stableInsert(Stuff)(Stuff stuff) if(isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem)) - { - size_t result = 0; - static if(allowDuplicates) - { - foreach(e; stuff) - { - ++result; - _add(e); - } - } - else - { - foreach(e; stuff) - { - if(_add(e).added) - ++result; - } - } - return result; - } - - /// ditto - alias stableInsert insert; - - static if(doUnittest) unittest - { - auto ts = new RedBlackTree(2,1,3,4,5,2,5); - static if(allowDuplicates) - { - assert(ts.length == 7); - assert(ts.stableInsert(cast(Elem[])[7, 8, 6, 9, 10, 8]) == 6); - assert(ts.length == 13); - assert(ts.stableInsert(cast(Elem)11) == 1 && ts.length == 14); - assert(ts.stableInsert(cast(Elem)7) == 1 && ts.length == 15); - - static if(less == "a < b") - assert(ts.arrayEqual([1,2,2,3,4,5,5,6,7,7,8,8,9,10,11])); - else - assert(ts.arrayEqual([11,10,9,8,8,7,7,6,5,5,4,3,2,2,1])); - } - else - { - assert(ts.length == 5); - assert(ts.stableInsert(cast(Elem[])[7, 8, 6, 9, 10, 8]) == 5); - assert(ts.length == 10); - assert(ts.stableInsert(cast(Elem)11) == 1 && ts.length == 11); - assert(ts.stableInsert(cast(Elem)7) == 0 && ts.length == 11); - - static if(less == "a < b") - assert(ts.arrayEqual([1,2,3,4,5,6,7,8,9,10,11])); - else - assert(ts.arrayEqual([11,10,9,8,7,6,5,4,3,2,1])); - } - } - - /** - * Remove an element from the container and return its value. - * - * Complexity: $(BIGOH log(n)) - */ - Elem removeAny() - { - scope(success) - --_length; - auto n = _begin; - auto result = n.value; - _begin = n.remove(_end); - version(RBDoChecks) - check(); - return result; - } - - static if(doUnittest) unittest - { - auto ts = new RedBlackTree(1,2,3,4,5); - assert(ts.length == 5); - auto x = ts.removeAny(); - assert(ts.length == 4); - Elem[] arr; - foreach(Elem i; 1..6) - if(i != x) arr ~= i; - assert(ts.arrayEqual(arr)); - } - - /** - * Remove the front element from the container. - * - * Complexity: $(BIGOH log(n)) - */ - void removeFront() - { - scope(success) - --_length; - _begin = _begin.remove(_end); - version(RBDoChecks) - check(); - } - - /** - * Remove the back element from the container. - * - * Complexity: $(BIGOH log(n)) - */ - void removeBack() - { - scope(success) - --_length; - auto lastnode = _end.prev; - if(lastnode is _begin) - _begin = _begin.remove(_end); - else - lastnode.remove(_end); - version(RBDoChecks) - check(); - } - - static if(doUnittest) unittest - { - auto ts = new RedBlackTree(1,2,3,4,5); - assert(ts.length == 5); - ts.removeBack(); - assert(ts.length == 4); - - static if(less == "a < b") - assert(ts.arrayEqual([1,2,3,4])); - else - assert(ts.arrayEqual([2,3,4,5])); - - ts.removeFront(); - assert(ts.arrayEqual([2,3,4]) && ts.length == 3); - } - - /++ - Removes the given range from the container. - - Returns: A range containing all of the elements that were after the - given range. - - Complexity: $(BIGOH m * log(n)) (where m is the number of elements in - the range) - +/ - Range remove(Range r) - { - auto b = r._begin; - auto e = r._end; - if(_begin is b) - _begin = e; - while(b !is e) - { - b = b.remove(_end); - --_length; - } - version(RBDoChecks) - check(); - return Range(e, _end); - } - - static if(doUnittest) unittest - { - auto ts = new RedBlackTree(1,2,3,4,5); - assert(ts.length == 5); - auto r = ts[]; - r.popFront(); - r.popBack(); - assert(ts.length == 5); - auto r2 = ts.remove(r); - assert(ts.length == 2); - assert(ts.arrayEqual([1,5])); - - static if(less == "a < b") - assert(std.algorithm.equal(r2, [5])); - else - assert(std.algorithm.equal(r2, [1])); - } - - /++ - Removes the given $(D Take!Range) from the container - - Returns: A range containing all of the elements that were after the - given range. - - Complexity: $(BIGOH m * log(n)) (where m is the number of elements in - the range) - +/ - Range remove(Take!Range r) - { - immutable isBegin = (r.source._begin is _begin); - auto b = r.source._begin; - - while(!r.empty) - { - r.popFront(); - b = b.remove(_end); - --_length; - } - - if(isBegin) - _begin = b; - - return Range(b, _end); - } - - static if(doUnittest) unittest - { - auto ts = new RedBlackTree(1,2,3,4,5); - auto r = ts[]; - r.popFront(); - assert(ts.length == 5); - auto r2 = ts.remove(take(r, 0)); - - static if(less == "a < b") - { - assert(std.algorithm.equal(r2, [2,3,4,5])); - auto r3 = ts.remove(take(r, 2)); - assert(ts.arrayEqual([1,4,5]) && ts.length == 3); - assert(std.algorithm.equal(r3, [4,5])); - } - else - { - assert(std.algorithm.equal(r2, [4,3,2,1])); - auto r3 = ts.remove(take(r, 2)); - assert(ts.arrayEqual([5,2,1]) && ts.length == 3); - assert(std.algorithm.equal(r3, [2,1])); - } - } - - /++ - Removes elements from the container that are equal to the given values - according to the less comparator. One element is removed for each value - given which is in the container. If $(D allowDuplicates) is true, - duplicates are removed only if duplicate values are given. - - Returns: The number of elements removed. - - Complexity: $(BIGOH m log(n)) (where m is the number of elements to remove) - - Examples: --------------------- -auto rbt = redBlackTree!true(0, 1, 1, 1, 4, 5, 7); -rbt.removeKey(1, 4, 7); -assert(std.algorithm.equal(rbt[], [0, 1, 1, 5])); -rbt.removeKey(1, 1, 0); -assert(std.algorithm.equal(rbt[], [5])); --------------------- - +/ - size_t removeKey(U...)(U elems) - if(allSatisfy!(isImplicitlyConvertibleToElem, U)) - { - Elem[U.length] toRemove; - - foreach(i, e; elems) - toRemove[i] = e; - - return removeKey(toRemove[]); - } - - /++ Ditto +/ - size_t removeKey(U)(U[] elems) - if(isImplicitlyConvertible!(U, Elem)) - { - immutable lenBefore = length; - - foreach(e; elems) - { - auto beg = _firstGreaterEqual(e); - if(beg is _end || _less(e, beg.value)) - // no values are equal - continue; - immutable isBegin = (beg is _begin); - beg = beg.remove(_end); - if(isBegin) - _begin = beg; - --_length; - } - - return lenBefore - length; - } - - /++ Ditto +/ - size_t removeKey(Stuff)(Stuff stuff) - if(isInputRange!Stuff && - isImplicitlyConvertible!(ElementType!Stuff, Elem) && - !isDynamicArray!Stuff) - { - //We use array in case stuff is a Range from this RedBlackTree - either - //directly or indirectly. - return removeKey(array(stuff)); - } - - //Helper for removeKey. - private template isImplicitlyConvertibleToElem(U) - { - enum isImplicitlyConvertibleToElem = isImplicitlyConvertible!(U, Elem); - } - - static if(doUnittest) unittest - { - auto rbt = new RedBlackTree(5, 4, 3, 7, 2, 1, 7, 6, 2, 19, 45); - - //The cast(Elem) is because these tests are instantiated with a variety - //of numeric types, and the literals are all int, which is not always - //implicitly convertible to Elem (e.g. short). - static if(allowDuplicates) - { - assert(rbt.length == 11); - assert(rbt.removeKey(cast(Elem)4) == 1 && rbt.length == 10); - assert(rbt.arrayEqual([1,2,2,3,5,6,7,7,19,45]) && rbt.length == 10); - - assert(rbt.removeKey(cast(Elem)6, cast(Elem)2, cast(Elem)1) == 3); - assert(rbt.arrayEqual([2,3,5,7,7,19,45]) && rbt.length == 7); - - assert(rbt.removeKey(cast(Elem)(42)) == 0 && rbt.length == 7); - assert(rbt.removeKey(take(rbt[], 3)) == 3 && rbt.length == 4); - - static if(less == "a < b") - assert(std.algorithm.equal(rbt[], [7,7,19,45])); - else - assert(std.algorithm.equal(rbt[], [7,5,3,2])); - } - else - { - assert(rbt.length == 9); - assert(rbt.removeKey(cast(Elem)4) == 1 && rbt.length == 8); - assert(rbt.arrayEqual([1,2,3,5,6,7,19,45])); - - assert(rbt.removeKey(cast(Elem)6, cast(Elem)2, cast(Elem)1) == 3); - assert(rbt.arrayEqual([3,5,7,19,45]) && rbt.length == 5); - - assert(rbt.removeKey(cast(Elem)(42)) == 0 && rbt.length == 5); - assert(rbt.removeKey(take(rbt[], 3)) == 3 && rbt.length == 2); - - static if(less == "a < b") - assert(std.algorithm.equal(rbt[], [19,45])); - else - assert(std.algorithm.equal(rbt[], [5,3])); - } - } - - // find the first node where the value is > e - private Node _firstGreater(Elem e) - { - // can't use _find, because we cannot return null - auto cur = _end.left; - auto result = _end; - while(cur) - { - if(_less(e, cur.value)) - { - result = cur; - cur = cur.left; - } - else - cur = cur.right; - } - return result; - } - - // find the first node where the value is >= e - private Node _firstGreaterEqual(Elem e) - { - // can't use _find, because we cannot return null. - auto cur = _end.left; - auto result = _end; - while(cur) - { - if(_less(cur.value, e)) - cur = cur.right; - else - { - result = cur; - cur = cur.left; - } - - } - return result; - } - - /** - * Get a range from the container with all elements that are > e according - * to the less comparator - * - * Complexity: $(BIGOH log(n)) - */ - Range upperBound(Elem e) - { - return Range(_firstGreater(e), _end); - } - - /** - * Get a range from the container with all elements that are < e according - * to the less comparator - * - * Complexity: $(BIGOH log(n)) - */ - Range lowerBound(Elem e) - { - return Range(_begin, _firstGreaterEqual(e)); - } - - /** - * Get a range from the container with all elements that are == e according - * to the less comparator - * - * Complexity: $(BIGOH log(n)) - */ - Range equalRange(Elem e) - { - auto beg = _firstGreaterEqual(e); - if(beg is _end || _less(e, beg.value)) - // no values are equal - return Range(beg, beg); - static if(allowDuplicates) - { - return Range(beg, _firstGreater(e)); - } - else - { - // no sense in doing a full search, no duplicates are allowed, - // so we just get the next node. - return Range(beg, beg.next); - } - } - - static if(doUnittest) unittest - { - auto ts = new RedBlackTree(1, 2, 3, 4, 5); - auto rl = ts.lowerBound(3); - auto ru = ts.upperBound(3); - auto re = ts.equalRange(3); - - static if(less == "a < b") - { - assert(std.algorithm.equal(rl, [1,2])); - assert(std.algorithm.equal(ru, [4,5])); - } - else - { - assert(std.algorithm.equal(rl, [5,4])); - assert(std.algorithm.equal(ru, [2,1])); - } - - assert(std.algorithm.equal(re, [3])); - } - - version(RBDoChecks) - { - /* - * Print the tree. This prints a sideways view of the tree in ASCII form, - * with the number of indentations representing the level of the nodes. - * It does not print values, only the tree structure and color of nodes. - */ - void printTree(Node n, int indent = 0) - { - if(n !is null) - { - printTree(n.right, indent + 2); - for(int i = 0; i < indent; i++) - write("."); - writeln(n.color == n.color.Black ? "B" : "R"); - printTree(n.left, indent + 2); - } - else - { - for(int i = 0; i < indent; i++) - write("."); - writeln("N"); - } - if(indent is 0) - writeln(); - } - - /* - * Check the tree for validity. This is called after every add or remove. - * This should only be enabled to debug the implementation of the RB Tree. - */ - void check() - { - // - // check implementation of the tree - // - int recurse(Node n, string path) - { - if(n is null) - return 1; - if(n.parent.left !is n && n.parent.right !is n) - throw new Exception("Node at path " ~ path ~ " has inconsistent pointers"); - Node next = n.next; - static if(allowDuplicates) - { - if(next !is _end && _less(next.value, n.value)) - throw new Exception("ordering invalid at path " ~ path); - } - else - { - if(next !is _end && !_less(n.value, next.value)) - throw new Exception("ordering invalid at path " ~ path); - } - if(n.color == n.color.Red) - { - if((n.left !is null && n.left.color == n.color.Red) || - (n.right !is null && n.right.color == n.color.Red)) - throw new Exception("Node at path " ~ path ~ " is red with a red child"); - } - - int l = recurse(n.left, path ~ "L"); - int r = recurse(n.right, path ~ "R"); - if(l != r) - { - writeln("bad tree at:"); - printTree(n); - throw new Exception("Node at path " ~ path ~ " has different number of black nodes on left and right paths"); - } - return l + (n.color == n.color.Black ? 1 : 0); - } - - try - { - recurse(_end.left, ""); - } - catch(Exception e) - { - printTree(_end.left, 0); - throw e; - } - } - } - - /+ - For the moment, using templatized contstructors doesn't seem to work - very well (likely due to bug# 436 and/or bug# 1528). The redBlackTree - helper function seems to do the job well enough though. - - /** - * Constructor. Pass in an array of elements, or individual elements to - * initialize the tree with. - */ - this(U)(U[] elems...) if (isImplicitlyConvertible!(U, Elem)) - { - _setup(); - stableInsert(elems); - } - - /** - * Constructor. Pass in a range of elements to initialize the tree with. - */ - this(Stuff)(Stuff stuff) if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem) && !is(Stuff == Elem[])) - { - _setup(); - stableInsert(stuff); - } - +/ - - /++ +/ - this() - { - _setup(); - } - - /++ - Constructor. Pass in an array of elements, or individual elements to - initialize the tree with. - +/ - this(Elem[] elems...) - { - _setup(); - stableInsert(elems); - } - - private this(Node end, size_t length) - { - _end = end; - _begin = end.leftmost; - _length = length; - } -} - -//Verify Example for removeKey. -unittest -{ - auto rbt = redBlackTree!true(0, 1, 1, 1, 4, 5, 7); - rbt.removeKey(1, 4, 7); - assert(std.algorithm.equal(rbt[], [0, 1, 1, 5])); - rbt.removeKey(1, 1, 0); - assert(std.algorithm.equal(rbt[], [5])); -} - -//Tests for removeKey -unittest -{ - { - auto rbt = redBlackTree(["hello", "world", "foo", "bar"]); - assert(equal(rbt[], ["bar", "foo", "hello", "world"])); - assert(rbt.removeKey("hello") == 1); - assert(equal(rbt[], ["bar", "foo", "world"])); - assert(rbt.removeKey("hello") == 0); - assert(equal(rbt[], ["bar", "foo", "world"])); - assert(rbt.removeKey("hello", "foo", "bar") == 2); - assert(equal(rbt[], ["world"])); - assert(rbt.removeKey(["", "world", "hello"]) == 1); - assert(rbt.empty); - } - - { - auto rbt = redBlackTree([1, 2, 12, 27, 4, 500]); - assert(equal(rbt[], [1, 2, 4, 12, 27, 500])); - assert(rbt.removeKey(1u) == 1); - assert(equal(rbt[], [2, 4, 12, 27, 500])); - assert(rbt.removeKey(cast(byte)1) == 0); - assert(equal(rbt[], [2, 4, 12, 27, 500])); - assert(rbt.removeKey(1, 12u, cast(byte)27) == 2); - assert(equal(rbt[], [2, 4, 500])); - assert(rbt.removeKey([cast(short)0, cast(short)500, cast(short)1]) == 1); - assert(equal(rbt[], [2, 4])); - } -} - -unittest -{ - void test(T)() - { - auto rt1 = new RedBlackTree!(T, "a < b", false)(); - auto rt2 = new RedBlackTree!(T, "a < b", true)(); - auto rt3 = new RedBlackTree!(T, "a > b", false)(); - auto rt4 = new RedBlackTree!(T, "a > b", true)(); - } - - test!long(); - test!ulong(); - test!int(); - test!uint(); - test!short(); - test!ushort(); - test!byte(); - test!byte(); -} - -/++ - Convenience function for creating a $(D RedBlackTree!E) from a list of - values. - - Examples: --------------------- -auto rbt1 = redBlackTree(0, 1, 5, 7); -auto rbt2 = redBlackTree!string("hello", "world"); -auto rbt3 = redBlackTree!true(0, 1, 5, 7, 5); -auto rbt4 = redBlackTree!"a > b"(0, 1, 5, 7); -auto rbt5 = redBlackTree!("a > b", true)(0.1, 1.3, 5.9, 7.2, 5.9); --------------------- - +/ -auto redBlackTree(E)(E[] elems...) -{ - return new RedBlackTree!E(elems); -} - -/++ Ditto +/ -auto redBlackTree(bool allowDuplicates, E)(E[] elems...) -{ - return new RedBlackTree!(E, "a < b", allowDuplicates)(elems); -} - -/++ Ditto +/ -auto redBlackTree(alias less, E)(E[] elems...) -{ - return new RedBlackTree!(E, less)(elems); -} - -/++ Ditto +/ -auto redBlackTree(alias less, bool allowDuplicates, E)(E[] elems...) - if(is(typeof(binaryFun!less(E.init, E.init)))) -{ - //We shouldn't need to instantiate less here, but for some reason, - //dmd can't handle it if we don't (even though the template which - //takes less but not allowDuplicates works just fine). - return new RedBlackTree!(E, binaryFun!less, allowDuplicates)(elems); -} - -//Verify Examples. -unittest -{ - auto rbt1 = redBlackTree(0, 1, 5, 7); - auto rbt2 = redBlackTree!string("hello", "world"); - auto rbt3 = redBlackTree!true(0, 1, 5, 7, 5); - auto rbt4 = redBlackTree!"a > b"(0, 1, 5, 7); - auto rbt5 = redBlackTree!("a > b", true)(0.1, 1.3, 5.9, 7.2, 5.9); -} - -//Combinations not in examples. -unittest -{ - auto rbt1 = redBlackTree!(true, string)("hello", "hello"); - auto rbt2 = redBlackTree!((a, b){return a < b;}, double)(5.1, 2.3); - auto rbt3 = redBlackTree!("a > b", true, string)("hello", "world"); -} - -unittest -{ - auto rt1 = redBlackTree(5, 4, 3, 2, 1); - assert(rt1.length == 5); - assert(array(rt1[]) == [1, 2, 3, 4, 5]); - - auto rt2 = redBlackTree!"a > b"(1.1, 2.1); - assert(rt2.length == 2); - assert(array(rt2[]) == [2.1, 1.1]); - - auto rt3 = redBlackTree!true(5, 5, 4); - assert(rt3.length == 3); - assert(array(rt3[]) == [4, 5, 5]); - - auto rt4 = redBlackTree!string("hello", "hello"); - assert(rt4.length == 1); - assert(array(rt4[]) == ["hello"]); -} - -version(unittest) struct UnittestMe { - int a; -} - -unittest -{ - auto c = Array!UnittestMe(); -} diff --git a/libphobos/src/std/container/array.d b/libphobos/src/std/container/array.d new file mode 100644 index 000000000..1ed719de0 --- /dev/null +++ b/libphobos/src/std/container/array.d @@ -0,0 +1,1995 @@ +module std.container.array; + +import core.exception, core.memory, core.stdc.stdlib, core.stdc.string, + std.algorithm, std.conv, std.exception, std.range, + std.traits, std.typecons; +public import std.container.util; +version(unittest) import std.stdio; + +/** +Array type with deterministic control of memory. The memory allocated +for the array is reclaimed as soon as possible; there is no reliance +on the garbage collector. $(D Array) uses $(D malloc) and $(D free) +for managing its own memory. + */ +struct Array(T) +if (!is(Unqual!T == bool)) +{ + // This structure is not copyable. + private struct Payload + { + size_t _capacity; + T[] _payload; + + // Convenience constructor + this(T[] p) { _capacity = p.length; _payload = p; } + + // Destructor releases array memory + ~this() + { + //Warning: destroy will also destroy class instances. + //The hasElaborateDestructor protects us here. + static if (hasElaborateDestructor!T) + foreach (ref e; _payload) + .destroy(e); + + static if (hasIndirections!T) + GC.removeRange(_payload.ptr); + + free(_payload.ptr); + } + + this(this) + { + assert(0); + } + + void opAssign(Payload rhs) + { + assert(false); + } + + // Duplicate data + // @property Payload dup() + // { + // Payload result; + // result._payload = _payload.dup; + // // Conservatively assume initial capacity == length + // result._capacity = result._payload.length; + // return result; + // } + + // length + @property size_t length() const + { + return _payload.length; + } + + // length + @property void length(size_t newLength) + { + if (length >= newLength) + { + // shorten + static if (hasElaborateDestructor!T) + foreach (ref e; _payload.ptr[newLength .. _payload.length]) + .destroy(e); + + _payload = _payload.ptr[0 .. newLength]; + return; + } + // enlarge + auto startEmplace = length; + _payload = (cast(T*) realloc(_payload.ptr, + T.sizeof * newLength))[0 .. newLength]; + initializeAll(_payload.ptr[startEmplace .. length]); + } + + // capacity + @property size_t capacity() const + { + return _capacity; + } + + // reserve + void reserve(size_t elements) + { + if (elements <= capacity) return; + immutable sz = elements * T.sizeof; + static if (hasIndirections!T) // should use hasPointers instead + { + /* Because of the transactional nature of this + * relative to the garbage collector, ensure no + * threading bugs by using malloc/copy/free rather + * than realloc. + */ + immutable oldLength = length; + auto newPayload = + enforce(cast(T*) malloc(sz))[0 .. oldLength]; + // copy old data over to new array + memcpy(newPayload.ptr, _payload.ptr, T.sizeof * oldLength); + // Zero out unused capacity to prevent gc from seeing + // false pointers + memset(newPayload.ptr + oldLength, + 0, + (elements - oldLength) * T.sizeof); + GC.addRange(newPayload.ptr, sz); + GC.removeRange(_payload.ptr); + free(_payload.ptr); + _payload = newPayload; + } + else + { + /* These can't have pointers, so no need to zero + * unused region + */ + auto newPayload = + enforce(cast(T*) realloc(_payload.ptr, sz))[0 .. length]; + _payload = newPayload; + } + _capacity = elements; + } + + // Insert one item + size_t insertBack(Stuff)(Stuff stuff) + if (isImplicitlyConvertible!(Stuff, T)) + { + if (_capacity == length) + { + reserve(1 + capacity * 3 / 2); + } + assert(capacity > length && _payload.ptr); + emplace(_payload.ptr + _payload.length, stuff); + _payload = _payload.ptr[0 .. _payload.length + 1]; + return 1; + } + + /// Insert a range of items + size_t insertBack(Stuff)(Stuff stuff) + if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) + { + static if (hasLength!Stuff) + { + immutable oldLength = length; + reserve(oldLength + stuff.length); + } + size_t result; + foreach (item; stuff) + { + insertBack(item); + ++result; + } + static if (hasLength!Stuff) + { + assert(length == oldLength + stuff.length); + } + return result; + } + } + private alias Data = RefCounted!(Payload, RefCountedAutoInitialize.no); + private Data _data; + +/** +Constructor taking a number of items + */ + this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) + { + auto p = cast(T*) malloc(T.sizeof * values.length); + if (hasIndirections!T && p) + { + GC.addRange(p, T.sizeof * values.length); + } + foreach (i, e; values) + { + emplace(p + i, e); + assert(p[i] == e); + } + _data = Data(p[0 .. values.length]); + } + +/** +Constructor taking an input range + */ + this(Stuff)(Stuff stuff) + if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T) && !is(Stuff == T[])) + { + insertBack(stuff); + } + + +/** +Comparison for equality. + */ + bool opEquals(const Array rhs) const + { + return opEquals(rhs); + } + + /// ditto + bool opEquals(ref const Array rhs) const + { + if (empty) return rhs.empty; + if (rhs.empty) return false; + return _data._payload == rhs._data._payload; + } + +/** +Defines the container's primary range, which is a random-access range. + */ + static struct Range + { + private Array _outer; + private size_t _a, _b; + + private this(ref Array data, size_t a, size_t b) + { + _outer = data; + _a = a; + _b = b; + } + + @property Range save() + { + return this; + } + + @property bool empty() @safe pure nothrow const + { + return _a >= _b; + } + + @property size_t length() @safe pure nothrow const + { + return _b - _a; + } + alias opDollar = length; + + @property ref T front() + { + version (assert) if (empty) throw new RangeError(); + return _outer[_a]; + } + + @property ref T back() + { + version (assert) if (empty) throw new RangeError(); + return _outer[_b - 1]; + } + + void popFront() @safe pure nothrow + { + version (assert) if (empty) throw new RangeError(); + ++_a; + } + + void popBack() @safe pure nothrow + { + version (assert) if (empty) throw new RangeError(); + --_b; + } + + T moveFront() + { + version (assert) if (empty || _a >= _outer.length) throw new RangeError(); + return move(_outer._data._payload[_a]); + } + + T moveBack() + { + version (assert) if (empty || _b > _outer.length) throw new RangeError(); + return move(_outer._data._payload[_b - 1]); + } + + T moveAt(size_t i) + { + version (assert) if (_a + i >= _b || _a + i >= _outer.length) throw new RangeError(); + return move(_outer._data._payload[_a + i]); + } + + ref T opIndex(size_t i) + { + version (assert) if (_a + i >= _b) throw new RangeError(); + return _outer[_a + i]; + } + + typeof(this) opSlice() + { + return typeof(this)(_outer, _a, _b); + } + + typeof(this) opSlice(size_t i, size_t j) + { + version (assert) if (i > j || _a + j > _b) throw new RangeError(); + return typeof(this)(_outer, _a + i, _a + j); + } + + void opSliceAssign(T value) + { + version (assert) if (_b > _outer.length) throw new RangeError(); + _outer[_a .. _b] = value; + } + + void opSliceAssign(T value, size_t i, size_t j) + { + version (assert) if (_a + j > _b) throw new RangeError(); + _outer[_a + i .. _a + j] = value; + } + + void opSliceUnary(string op)() + if(op == "++" || op == "--") + { + version (assert) if (_b > _outer.length) throw new RangeError(); + mixin(op~"_outer[_a .. _b];"); + } + + void opSliceUnary(string op)(size_t i, size_t j) + if(op == "++" || op == "--") + { + version (assert) if (_a + j > _b) throw new RangeError(); + mixin(op~"_outer[_a + i .. _a + j];"); + } + + void opSliceOpAssign(string op)(T value) + { + version (assert) if (_b > _outer.length) throw new RangeError(); + mixin("_outer[_a .. _b] "~op~"= value;"); + } + + void opSliceOpAssign(string op)(T value, size_t i, size_t j) + { + version (assert) if (_a + j > _b) throw new RangeError(); + mixin("_outer[_a + i .. _a + j] "~op~"= value;"); + } + } + +/** +Duplicates the container. The elements themselves are not transitively +duplicated. + +Complexity: $(BIGOH n). + */ + @property Array dup() + { + if (!_data.refCountedStore.isInitialized) return this; + return Array(_data._payload); + } + +/** +Property returning $(D true) if and only if the container has no +elements. + +Complexity: $(BIGOH 1) + */ + @property bool empty() const + { + return !_data.refCountedStore.isInitialized || _data._payload.empty; + } + +/** +Returns the number of elements in the container. + +Complexity: $(BIGOH 1). + */ + @property size_t length() const + { + return _data.refCountedStore.isInitialized ? _data._payload.length : 0; + } + + /// ditto + size_t opDollar() const + { + return length; + } + +/** +Returns the maximum number of elements the container can store without + (a) allocating memory, (b) invalidating iterators upon insertion. + +Complexity: $(BIGOH 1) + */ + @property size_t capacity() + { + return _data.refCountedStore.isInitialized ? _data._capacity : 0; + } + +/** +Ensures sufficient capacity to accommodate $(D e) elements. + +Postcondition: $(D capacity >= e) + +Complexity: $(BIGOH 1) + */ + void reserve(size_t elements) + { + if (!_data.refCountedStore.isInitialized) + { + if (!elements) return; + immutable sz = elements * T.sizeof; + auto p = enforce(malloc(sz)); + static if (hasIndirections!T) + { + GC.addRange(p, sz); + } + _data = Data(cast(T[]) p[0 .. 0]); + _data._capacity = elements; + } + else + { + _data.reserve(elements); + } + } + +/** +Returns a range that iterates over elements of the container, in +forward order. + +Complexity: $(BIGOH 1) + */ + Range opSlice() + { + return Range(this, 0, length); + } + +/** +Returns a range that iterates over elements of the container from +index $(D a) up to (excluding) index $(D b). + +Precondition: $(D a <= b && b <= length) + +Complexity: $(BIGOH 1) + */ + Range opSlice(size_t i, size_t j) + { + version (assert) if (i > j || j > length) throw new RangeError(); + return Range(this, i, j); + } + +/** +Forward to $(D opSlice().front) and $(D opSlice().back), respectively. + +Precondition: $(D !empty) + +Complexity: $(BIGOH 1) + */ + @property ref T front() + { + version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); + return _data._payload[0]; + } + + /// ditto + @property ref T back() + { + version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); + return _data._payload[$ - 1]; + } + +/** +Indexing operators yield or modify the value at a specified index. + +Precondition: $(D i < length) + +Complexity: $(BIGOH 1) + */ + ref T opIndex(size_t i) + { + version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError(); + return _data._payload[i]; + } + +/** +Slicing operations execute an operation on an entire slice. + +Precondition: $(D i < j && j < length) + +Complexity: $(BIGOH slice.length) + */ + void opSliceAssign(T value) + { + if (!_data.refCountedStore.isInitialized) return; + _data._payload[] = value; + } + + /// ditto + void opSliceAssign(T value, size_t i, size_t j) + { + auto slice = _data.refCountedStore.isInitialized ? + _data._payload : + T[].init; + slice[i .. j] = value; + } + + /// ditto + void opSliceUnary(string op)() + if(op == "++" || op == "--") + { + if(!_data.refCountedStore.isInitialized) return; + mixin(op~"_data._payload[];"); + } + + /// ditto + void opSliceUnary(string op)(size_t i, size_t j) + if(op == "++" || op == "--") + { + auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init; + mixin(op~"slice[i .. j];"); + } + + /// ditto + void opSliceOpAssign(string op)(T value) + { + if(!_data.refCountedStore.isInitialized) return; + mixin("_data._payload[] "~op~"= value;"); + } + + /// ditto + void opSliceOpAssign(string op)(T value, size_t i, size_t j) + { + auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init; + mixin("slice[i .. j] "~op~"= value;"); + } + +/** +Returns a new container that's the concatenation of $(D this) and its +argument. $(D opBinaryRight) is only defined if $(D Stuff) does not +define $(D opBinary). + +Complexity: $(BIGOH n + m), where m is the number of elements in $(D +stuff) + */ + Array opBinary(string op, Stuff)(Stuff stuff) + if (op == "~") + { + // TODO: optimize + Array result; + // @@@BUG@@ result ~= this[] doesn't work + auto r = this[]; + result ~= r; + assert(result.length == length); + result ~= stuff[]; + return result; + } + +/** +Forwards to $(D insertBack(stuff)). + */ + void opOpAssign(string op, Stuff)(Stuff stuff) + if (op == "~") + { + static if (is(typeof(stuff[]))) + { + insertBack(stuff[]); + } + else + { + insertBack(stuff); + } + } + +/** +Removes all contents from the container. The container decides how $(D +capacity) is affected. + +Postcondition: $(D empty) + +Complexity: $(BIGOH n) + */ + void clear() + { + _data = Data.init; + } + +/** +Sets the number of elements in the container to $(D newSize). If $(D +newSize) is greater than $(D length), the added elements are added to +unspecified positions in the container and initialized with $(D +T.init). + +Complexity: $(BIGOH abs(n - newLength)) + +Postcondition: $(D length == newLength) + */ + @property void length(size_t newLength) + { + _data.refCountedStore.ensureInitialized(); + _data.length = newLength; + } + +/** +Picks one value in an unspecified position in the container, removes +it from the container, and returns it. Implementations should pick the +value that's the most advantageous for the container, but document the +exact behavior. The stable version behaves the same, but guarantees +that ranges iterating over the container are never invalidated. + +Precondition: $(D !empty) + +Returns: The element removed. + +Complexity: $(BIGOH log(n)). + */ + T removeAny() + { + auto result = back; + removeBack(); + return result; + } + /// ditto + alias stableRemoveAny = removeAny; + +/** +Inserts $(D value) to the front or back of the container. $(D stuff) +can be a value convertible to $(D T) or a range of objects convertible +to $(D T). The stable version behaves the same, but guarantees that +ranges iterating over the container are never invalidated. + +Returns: The number of elements inserted + +Complexity: $(BIGOH m * log(n)), where $(D m) is the number of +elements in $(D stuff) + */ + size_t insertBack(Stuff)(Stuff stuff) + if (isImplicitlyConvertible!(Stuff, T) || + isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) + { + _data.refCountedStore.ensureInitialized(); + return _data.insertBack(stuff); + } + /// ditto + alias insert = insertBack; + +/** +Removes the value at the back of the container. The stable version +behaves the same, but guarantees that ranges iterating over the +container are never invalidated. + +Precondition: $(D !empty) + +Complexity: $(BIGOH log(n)). + */ + void removeBack() + { + enforce(!empty); + static if (hasElaborateDestructor!T) + .destroy(_data._payload[$ - 1]); + + _data._payload = _data._payload[0 .. $ - 1]; + } + /// ditto + alias stableRemoveBack = removeBack; + +/** +Removes $(D howMany) values at the front or back of the +container. Unlike the unparameterized versions above, these functions +do not throw if they could not remove $(D howMany) elements. Instead, +if $(D howMany > n), all elements are removed. The returned value is +the effective number of elements removed. The stable version behaves +the same, but guarantees that ranges iterating over the container are +never invalidated. + +Returns: The number of elements removed + +Complexity: $(BIGOH howMany). + */ + size_t removeBack(size_t howMany) + { + if (howMany > length) howMany = length; + static if (hasElaborateDestructor!T) + foreach (ref e; _data._payload[$ - howMany .. $]) + .destroy(e); + + _data._payload = _data._payload[0 .. $ - howMany]; + return howMany; + } + /// ditto + alias stableRemoveBack = removeBack; + +/** +Inserts $(D stuff) before, after, or instead range $(D r), which must +be a valid range previously extracted from this container. $(D stuff) +can be a value convertible to $(D T) or a range of objects convertible +to $(D T). The stable version behaves the same, but guarantees that +ranges iterating over the container are never invalidated. + +Returns: The number of values inserted. + +Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff) + */ + size_t insertBefore(Stuff)(Range r, Stuff stuff) + if (isImplicitlyConvertible!(Stuff, T)) + { + enforce(r._outer._data is _data && r._a <= length); + reserve(length + 1); + assert(_data.refCountedStore.isInitialized); + // Move elements over by one slot + memmove(_data._payload.ptr + r._a + 1, + _data._payload.ptr + r._a, + T.sizeof * (length - r._a)); + emplace(_data._payload.ptr + r._a, stuff); + _data._payload = _data._payload.ptr[0 .. _data._payload.length + 1]; + return 1; + } + + /// ditto + size_t insertBefore(Stuff)(Range r, Stuff stuff) + if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) + { + enforce(r._outer._data is _data && r._a <= length); + static if (isForwardRange!Stuff) + { + // Can find the length in advance + auto extra = walkLength(stuff); + if (!extra) return 0; + reserve(length + extra); + assert(_data.refCountedStore.isInitialized); + // Move elements over by extra slots + memmove(_data._payload.ptr + r._a + extra, + _data._payload.ptr + r._a, + T.sizeof * (length - r._a)); + foreach (p; _data._payload.ptr + r._a .. + _data._payload.ptr + r._a + extra) + { + emplace(p, stuff.front); + stuff.popFront(); + } + _data._payload = + _data._payload.ptr[0 .. _data._payload.length + extra]; + return extra; + } + else + { + enforce(_data); + immutable offset = r._a; + enforce(offset <= length); + auto result = insertBack(stuff); + bringToFront(this[offset .. length - result], + this[length - result .. length]); + return result; + } + } + + /// ditto + size_t insertAfter(Stuff)(Range r, Stuff stuff) + { + enforce(r._outer._data is _data); + // TODO: optimize + immutable offset = r._b; + enforce(offset <= length); + auto result = insertBack(stuff); + bringToFront(this[offset .. length - result], + this[length - result .. length]); + return result; + } + + /// ditto + size_t replace(Stuff)(Range r, Stuff stuff) + if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) + { + enforce(r._outer._data is _data); + size_t result; + for (; !stuff.empty; stuff.popFront()) + { + if (r.empty) + { + // insert the rest + return result + insertBefore(r, stuff); + } + r.front = stuff.front; + r.popFront(); + ++result; + } + // Remove remaining stuff in r + linearRemove(r); + return result; + } + + /// ditto + size_t replace(Stuff)(Range r, Stuff stuff) + if (isImplicitlyConvertible!(Stuff, T)) + { + enforce(r._outer._data is _data); + if (r.empty) + { + insertBefore(r, stuff); + } + else + { + r.front = stuff; + r.popFront(); + linearRemove(r); + } + return 1; + } + +/** +Removes all elements belonging to $(D r), which must be a range +obtained originally from this container. The stable version behaves +the same, but guarantees that ranges iterating over the container are +never invalidated. + +Returns: A range spanning the remaining elements in the container that +initially were right after $(D r). + +Complexity: $(BIGOH n - m), where $(D m) is the number of elements in +$(D r) + */ + Range linearRemove(Range r) + { + enforce(r._outer._data is _data); + enforce(_data.refCountedStore.isInitialized); + enforce(r._a <= r._b && r._b <= length); + immutable offset1 = r._a; + immutable offset2 = r._b; + immutable tailLength = length - offset2; + // Use copy here, not a[] = b[] because the ranges may overlap + copy(this[offset2 .. length], this[offset1 .. offset1 + tailLength]); + length = offset1 + tailLength; + return this[length - tailLength .. length]; + } + /// ditto + alias stableLinearRemove = remove; +} + +unittest +{ + Array!int a; + assert(a.empty); +} + +unittest +{ + Array!int a = Array!int(1, 2, 3); + //a._data._refCountedDebug = true; + auto b = a.dup; + assert(b == Array!int(1, 2, 3)); + b.front = 42; + assert(b == Array!int(42, 2, 3)); + assert(a == Array!int(1, 2, 3)); +} + +unittest +{ + auto a = Array!int(1, 2, 3); + assert(a.length == 3); +} + +unittest +{ + Array!int a; + a.reserve(1000); + assert(a.length == 0); + assert(a.empty); + assert(a.capacity >= 1000); + auto p = a._data._payload.ptr; + foreach (i; 0 .. 1000) + { + a.insertBack(i); + } + assert(p == a._data._payload.ptr); +} + +unittest +{ + auto a = Array!int(1, 2, 3); + a[1] *= 42; + assert(a[1] == 84); +} + +unittest +{ + auto a = Array!int(1, 2, 3); + auto b = Array!int(11, 12, 13); + auto c = a ~ b; + //foreach (e; c) writeln(e); + assert(c == Array!int(1, 2, 3, 11, 12, 13)); + //assert(a ~ b[] == Array!int(1, 2, 3, 11, 12, 13)); +} + +unittest +{ + auto a = Array!int(1, 2, 3); + auto b = Array!int(11, 12, 13); + a ~= b; + assert(a == Array!int(1, 2, 3, 11, 12, 13)); +} + +unittest +{ + auto a = Array!int(1, 2, 3, 4); + assert(a.removeAny() == 4); + assert(a == Array!int(1, 2, 3)); +} + +unittest +{ + auto a = Array!int(1, 2, 3, 4, 5); + auto r = a[2 .. a.length]; + assert(a.insertBefore(r, 42) == 1); + assert(a == Array!int(1, 2, 42, 3, 4, 5)); + r = a[2 .. 2]; + assert(a.insertBefore(r, [8, 9]) == 2); + assert(a == Array!int(1, 2, 8, 9, 42, 3, 4, 5)); +} + +unittest +{ + auto a = Array!int(0, 1, 2, 3, 4, 5, 6, 7, 8); + a.linearRemove(a[4 .. 6]); + auto b = Array!int(0, 1, 2, 3, 6, 7, 8); + //writeln(a.length); + //foreach (e; a) writeln(e); + assert(a == Array!int(0, 1, 2, 3, 6, 7, 8)); +} + +// Give the Range object some testing. +unittest +{ + auto a = Array!int(0, 1, 2, 3, 4, 5, 6)[]; + auto b = Array!int(6, 5, 4, 3, 2, 1, 0)[]; + alias A = typeof(a); + + static assert(isRandomAccessRange!A); + static assert(hasSlicing!A); + static assert(hasAssignableElements!A); + static assert(hasMobileElements!A); + + assert(equal(retro(b), a)); + assert(a.length == 7); + assert(equal(a[1..4], [1, 2, 3])); +} +// Test issue 5920 +unittest +{ + struct structBug5920 + { + int order; + uint* pDestructionMask; + ~this() + { + if (pDestructionMask) + *pDestructionMask += 1 << order; + } + } + + alias S = structBug5920; + uint dMask; + + auto arr = Array!S(cast(S[])[]); + foreach (i; 0..8) + arr.insertBack(S(i, &dMask)); + // don't check dMask now as S may be copied multiple times (it's ok?) + { + assert(arr.length == 8); + dMask = 0; + arr.length = 6; + assert(arr.length == 6); // make sure shrinking calls the d'tor + assert(dMask == 0b1100_0000); + arr.removeBack(); + assert(arr.length == 5); // make sure removeBack() calls the d'tor + assert(dMask == 0b1110_0000); + arr.removeBack(3); + assert(arr.length == 2); // ditto + assert(dMask == 0b1111_1100); + arr.clear(); + assert(arr.length == 0); // make sure clear() calls the d'tor + assert(dMask == 0b1111_1111); + } + assert(dMask == 0b1111_1111); // make sure the d'tor is called once only. +} +// Test issue 5792 (mainly just to check if this piece of code is compilable) +unittest +{ + auto a = Array!(int[])([[1,2],[3,4]]); + a.reserve(4); + assert(a.capacity >= 4); + assert(a.length == 2); + assert(a[0] == [1,2]); + assert(a[1] == [3,4]); + a.reserve(16); + assert(a.capacity >= 16); + assert(a.length == 2); + assert(a[0] == [1,2]); + assert(a[1] == [3,4]); +} + +// test replace!Stuff with range Stuff +unittest +{ + auto a = Array!int([1, 42, 5]); + a.replace(a[1 .. 2], [2, 3, 4]); + assert(equal(a[], [1, 2, 3, 4, 5])); +} + +// test insertBefore and replace with empty Arrays +unittest +{ + auto a = Array!int(); + a.insertBefore(a[], 1); + assert(equal(a[], [1])); +} +unittest +{ + auto a = Array!int(); + a.insertBefore(a[], [1, 2]); + assert(equal(a[], [1, 2])); +} +unittest +{ + auto a = Array!int(); + a.replace(a[], [1, 2]); + assert(equal(a[], [1, 2])); +} +unittest +{ + auto a = Array!int(); + a.replace(a[], 1); + assert(equal(a[], [1])); +} +// make sure that Array instances refuse ranges that don't belong to them +unittest +{ + Array!int a = [1, 2, 3]; + auto r = a.dup[]; + assertThrown(a.insertBefore(r, 42)); + assertThrown(a.insertBefore(r, [42])); + assertThrown(a.insertAfter(r, 42)); + assertThrown(a.replace(r, 42)); + assertThrown(a.replace(r, [42])); + assertThrown(a.linearRemove(r)); +} +unittest +{ + auto a = Array!int([1, 1]); + a[1] = 0; //Check Array.opIndexAssign + assert(a[1] == 0); + a[1] += 1; //Check Array.opIndexOpAssign + assert(a[1] == 1); + + //Check Array.opIndexUnary + ++a[0]; + //a[0]++ //op++ doesn't return, so this shouldn't work, even with 5044 fixed + assert(a[0] == 2); + assert(+a[0] == +2); + assert(-a[0] == -2); + assert(~a[0] == ~2); + + auto r = a[]; + r[1] = 0; //Check Array.Range.opIndexAssign + assert(r[1] == 0); + r[1] += 1; //Check Array.Range.opIndexOpAssign + assert(r[1] == 1); + + //Check Array.Range.opIndexUnary + ++r[0]; + //r[0]++ //op++ doesn't return, so this shouldn't work, even with 5044 fixed + assert(r[0] == 3); + assert(+r[0] == +3); + assert(-r[0] == -3); + assert(~r[0] == ~3); +} + +unittest +{ + //Test "array-wide" operations + auto a = Array!int([0, 1, 2]); //Array + a[] += 5; + assert(a[].equal([5, 6, 7])); + ++a[]; + assert(a[].equal([6, 7, 8])); + a[1 .. 3] *= 5; + assert(a[].equal([6, 35, 40])); + a[0 .. 2] = 0; + assert(a[].equal([0, 0, 40])); + + //Test empty array + auto a2 = Array!int.init; + ++a2[]; + ++a2[0 .. 0]; + a2[] = 0; + a2[0 .. 0] = 0; + a2[] += 0; + a2[0 .. 0] += 0; + + //Test "range-wide" operations + auto r = Array!int([0, 1, 2])[]; //Array.Range + r[] += 5; + assert(r.equal([5, 6, 7])); + ++r[]; + assert(r.equal([6, 7, 8])); + r[1 .. 3] *= 5; + assert(r.equal([6, 35, 40])); + r[0 .. 2] = 0; + assert(r.equal([0, 0, 40])); + + //Test empty Range + auto r2 = Array!int.init[]; + ++r2[]; + ++r2[0 .. 0]; + r2[] = 0; + r2[0 .. 0] = 0; + r2[] += 0; + r2[0 .. 0] += 0; +} + +// Test issue 11194 +unittest { + static struct S { + int i = 1337; + void* p; + this(this) { assert(i == 1337); } + ~this() { assert(i == 1337); } + } + Array!S arr; + S s; + arr ~= s; + arr ~= s; +} + +unittest //11459 +{ + static struct S + { + bool b; + alias b this; + } + alias A = Array!S; + alias B = Array!(shared bool); +} + +unittest //11884 +{ + auto a = Array!int([1, 2, 2].filter!"true"()); +} + +unittest //8282 +{ + auto arr = new Array!int; +} + +unittest //6998 +{ + static int i = 0; + class C + { + int dummy = 1; + this(){++i;} + ~this(){--i;} + } + + assert(i == 0); + auto c = new C(); + assert(i == 1); + + //scope + { + auto arr = Array!C(c); + assert(i == 1); + } + //Array should not have destroyed the class instance + assert(i == 1); + + //Just to make sure the GC doesn't collect before the above test. + assert(c.dummy ==1); +} +unittest //6998-2 +{ + static class C {int i;} + auto c = new C; + c.i = 42; + Array!C a; + a ~= c; + a.clear; + assert(c.i == 42); //fails +} + + +//////////////////////////////////////////////////////////////////////////////// +// Array!bool +//////////////////////////////////////////////////////////////////////////////// + +/** +_Array specialized for $(D bool). Packs together values efficiently by +allocating one bit per element. + */ +struct Array(T) +if (is(Unqual!T == bool)) +{ + static immutable uint bitsPerWord = size_t.sizeof * 8; + private static struct Data + { + Array!size_t.Payload _backend; + size_t _length; + } + private RefCounted!(Data, RefCountedAutoInitialize.no) _store; + + private @property ref size_t[] data() + { + assert(_store.refCountedStore.isInitialized); + return _store._backend._payload; + } + + /** + Defines the container's primary range. + */ + struct Range + { + private Array _outer; + private size_t _a, _b; + /// Range primitives + @property Range save() + { + version (bug4437) + { + return this; + } + else + { + auto copy = this; + return copy; + } + } + /// Ditto + @property bool empty() + { + return _a >= _b || _outer.length < _b; + } + /// Ditto + @property T front() + { + enforce(!empty); + return _outer[_a]; + } + /// Ditto + @property void front(bool value) + { + enforce(!empty); + _outer[_a] = value; + } + /// Ditto + T moveFront() + { + enforce(!empty); + return _outer.moveAt(_a); + } + /// Ditto + void popFront() + { + enforce(!empty); + ++_a; + } + /// Ditto + @property T back() + { + enforce(!empty); + return _outer[_b - 1]; + } + /// Ditto + @property void back(bool value) + { + enforce(!empty); + _outer[_b - 1] = value; + } + /// Ditto + T moveBack() + { + enforce(!empty); + return _outer.moveAt(_b - 1); + } + /// Ditto + void popBack() + { + enforce(!empty); + --_b; + } + /// Ditto + T opIndex(size_t i) + { + return _outer[_a + i]; + } + /// Ditto + void opIndexAssign(T value, size_t i) + { + _outer[_a + i] = value; + } + /// Ditto + T moveAt(size_t i) + { + return _outer.moveAt(_a + i); + } + /// Ditto + @property size_t length() const + { + assert(_a <= _b); + return _b - _a; + } + alias opDollar = length; + /// ditto + Range opSlice(size_t low, size_t high) + { + assert(_a <= low && low <= high && high <= _b); + return Range(_outer, _a + low, _a + high); + } + } + + /** + Property returning $(D true) if and only if the container has + no elements. + + Complexity: $(BIGOH 1) + */ + @property bool empty() + { + return !length; + } + + unittest + { + Array!bool a; + //a._store._refCountedDebug = true; + assert(a.empty); + a.insertBack(false); + assert(!a.empty); + } + + /** + Returns a duplicate of the container. The elements themselves + are not transitively duplicated. + + Complexity: $(BIGOH n). + */ + @property Array dup() + { + Array result; + result.insertBack(this[]); + return result; + } + + unittest + { + Array!bool a; + assert(a.empty); + auto b = a.dup; + assert(b.empty); + a.insertBack(true); + assert(b.empty); + } + + /** + Returns the number of elements in the container. + + Complexity: $(BIGOH log(n)). + */ + @property size_t length() const + { + return _store.refCountedStore.isInitialized ? _store._length : 0; + } + size_t opDollar() const + { + return length; + } + + unittest + { + Array!bool a; + assert(a.length == 0); + a.insert(true); + assert(a.length == 1, text(a.length)); + } + + /** + Returns the maximum number of elements the container can store + without (a) allocating memory, (b) invalidating iterators upon + insertion. + + Complexity: $(BIGOH log(n)). + */ + @property size_t capacity() + { + return _store.refCountedStore.isInitialized + ? cast(size_t) bitsPerWord * _store._backend.capacity + : 0; + } + + unittest + { + Array!bool a; + assert(a.capacity == 0); + foreach (i; 0 .. 100) + { + a.insert(true); + assert(a.capacity >= a.length, text(a.capacity)); + } + } + + /** + Ensures sufficient capacity to accommodate $(D n) elements. + + Postcondition: $(D capacity >= n) + + Complexity: $(BIGOH log(e - capacity)) if $(D e > capacity), + otherwise $(BIGOH 1). + */ + void reserve(size_t e) + { + _store.refCountedStore.ensureInitialized(); + _store._backend.reserve(to!size_t((e + bitsPerWord - 1) / bitsPerWord)); + } + + unittest + { + Array!bool a; + assert(a.capacity == 0); + a.reserve(15657); + assert(a.capacity >= 15657); + } + + /** + Returns a range that iterates over all elements of the + container, in a container-defined order. The container should + choose the most convenient and fast method of iteration for $(D + opSlice()). + + Complexity: $(BIGOH log(n)) + */ + Range opSlice() + { + return Range(this, 0, length); + } + + unittest + { + Array!bool a; + a.insertBack([true, false, true, true]); + assert(a[].length == 4); + } + + /** + Returns a range that iterates the container between two + specified positions. + + Complexity: $(BIGOH log(n)) + */ + Range opSlice(size_t a, size_t b) + { + enforce(a <= b && b <= length); + return Range(this, a, b); + } + + unittest + { + Array!bool a; + a.insertBack([true, false, true, true]); + assert(a[0 .. 2].length == 2); + } + + /** + Equivalent to $(D opSlice().front) and $(D opSlice().back), + respectively. + + Complexity: $(BIGOH log(n)) + */ + @property bool front() + { + enforce(!empty); + return data.ptr[0] & 1; + } + + /// Ditto + @property void front(bool value) + { + enforce(!empty); + if (value) data.ptr[0] |= 1; + else data.ptr[0] &= ~cast(size_t) 1; + } + + unittest + { + Array!bool a; + a.insertBack([true, false, true, true]); + assert(a.front); + a.front = false; + assert(!a.front); + } + + /// Ditto + @property bool back() + { + enforce(!empty); + return cast(bool)(data.back & (cast(size_t)1 << ((_store._length - 1) % bitsPerWord))); + } + + /// Ditto + @property void back(bool value) + { + enforce(!empty); + if (value) + { + data.back |= (cast(size_t)1 << ((_store._length - 1) % bitsPerWord)); + } + else + { + data.back &= + ~(cast(size_t)1 << ((_store._length - 1) % bitsPerWord)); + } + } + + unittest + { + Array!bool a; + a.insertBack([true, false, true, true]); + assert(a.back); + a.back = false; + assert(!a.back); + } + + /** + Indexing operators yield or modify the value at a specified index. + */ + bool opIndex(size_t i) + { + auto div = cast(size_t) (i / bitsPerWord); + auto rem = i % bitsPerWord; + enforce(div < data.length); + return cast(bool)(data.ptr[div] & (cast(size_t)1 << rem)); + } + /// ditto + void opIndexAssign(bool value, size_t i) + { + auto div = cast(size_t) (i / bitsPerWord); + auto rem = i % bitsPerWord; + enforce(div < data.length); + if (value) data.ptr[div] |= (cast(size_t)1 << rem); + else data.ptr[div] &= ~(cast(size_t)1 << rem); + } + /// ditto + void opIndexOpAssign(string op)(bool value, size_t i) + { + auto div = cast(size_t) (i / bitsPerWord); + auto rem = i % bitsPerWord; + enforce(div < data.length); + auto oldValue = cast(bool) (data.ptr[div] & (cast(size_t)1 << rem)); + // Do the deed + auto newValue = mixin("oldValue "~op~" value"); + // Write back the value + if (newValue != oldValue) + { + if (newValue) data.ptr[div] |= (cast(size_t)1 << rem); + else data.ptr[div] &= ~(cast(size_t)1 << rem); + } + } + /// Ditto + T moveAt(size_t i) + { + return this[i]; + } + + unittest + { + Array!bool a; + a.insertBack([true, false, true, true]); + assert(a[0] && !a[1]); + a[0] &= a[1]; + assert(!a[0]); + } + + /** + Returns a new container that's the concatenation of $(D this) + and its argument. + + Complexity: $(BIGOH n + m), where m is the number of elements + in $(D stuff) + */ + Array!bool opBinary(string op, Stuff)(Stuff rhs) if (op == "~") + { + auto result = this; + return result ~= rhs; + } + + unittest + { + Array!bool a; + a.insertBack([true, false, true, true]); + Array!bool b; + b.insertBack([true, true, false, true]); + assert(equal((a ~ b)[], + [true, false, true, true, true, true, false, true])); + } + + // /// ditto + // TotalContainer opBinaryRight(Stuff, string op)(Stuff lhs) if (op == "~") + // { + // assert(0); + // } + + /** + Forwards to $(D insertAfter(this[], stuff)). + */ + // @@@BUG@@@ + //ref Array!bool opOpAssign(string op, Stuff)(Stuff stuff) if (op == "~") + Array!bool opOpAssign(string op, Stuff)(Stuff stuff) if (op == "~") + { + static if (is(typeof(stuff[]))) insertBack(stuff[]); + else insertBack(stuff); + return this; + } + + unittest + { + Array!bool a; + a.insertBack([true, false, true, true]); + Array!bool b; + a.insertBack([false, true, false, true, true]); + a ~= b; + assert(equal( + a[], + [true, false, true, true, false, true, false, true, true])); + } + + /** + Removes all contents from the container. The container decides + how $(D capacity) is affected. + + Postcondition: $(D empty) + + Complexity: $(BIGOH n) + */ + void clear() + { + this = Array(); + } + + unittest + { + Array!bool a; + a.insertBack([true, false, true, true]); + a.clear(); + assert(a.capacity == 0); + } + + /** + Sets the number of elements in the container to $(D + newSize). If $(D newSize) is greater than $(D length), the + added elements are added to the container and initialized with + $(D ElementType.init). + + Complexity: $(BIGOH abs(n - newLength)) + + Postcondition: $(D _length == newLength) + */ + @property void length(size_t newLength) + { + _store.refCountedStore.ensureInitialized(); + auto newDataLength = + to!size_t((newLength + bitsPerWord - 1) / bitsPerWord); + _store._backend.length = newDataLength; + _store._length = newLength; + } + + unittest + { + Array!bool a; + a.length = 1057; + assert(a.length == 1057); + foreach (e; a) + { + assert(!e); + } + } + + /** + Inserts $(D stuff) in the container. $(D stuff) can be a value + convertible to $(D ElementType) or a range of objects + convertible to $(D ElementType). + + The $(D stable) version guarantees that ranges iterating over + the container are never invalidated. Client code that counts on + non-invalidating insertion should use $(D stableInsert). + + Returns: The number of elements added. + + Complexity: $(BIGOH m * log(n)), where $(D m) is the number of + elements in $(D stuff) + */ + alias insert = insertBack; + ///ditto + alias stableInsert = insertBack; + + /** + Same as $(D insert(stuff)) and $(D stableInsert(stuff)) + respectively, but relax the complexity constraint to linear. + */ + alias linearInsert = insertBack; + ///ditto + alias stableLinearInsert = insertBack; + + /** + Picks one value in the container, removes it from the + container, and returns it. The stable version behaves the same, + but guarantees that ranges iterating over the container are + never invalidated. + + Precondition: $(D !empty) + + Returns: The element removed. + + Complexity: $(BIGOH log(n)) + */ + T removeAny() + { + auto result = back; + removeBack(); + return result; + } + /// ditto + alias stableRemoveAny = removeAny; + + unittest + { + Array!bool a; + a.length = 1057; + assert(!a.removeAny()); + assert(a.length == 1056); + foreach (e; a) + { + assert(!e); + } + } + + /** + Inserts $(D value) to the back of the container. $(D stuff) can + be a value convertible to $(D ElementType) or a range of + objects convertible to $(D ElementType). The stable version + behaves the same, but guarantees that ranges iterating over the + container are never invalidated. + + Returns: The number of elements inserted + + Complexity: $(BIGOH log(n)) + */ + size_t insertBack(Stuff)(Stuff stuff) if (is(Stuff : bool)) + { + _store.refCountedStore.ensureInitialized(); + auto rem = _store._length % bitsPerWord; + if (rem) + { + // Fits within the current array + if (stuff) + { + data[$ - 1] |= (1u << rem); + } + else + { + data[$ - 1] &= ~(1u << rem); + } + } + else + { + // Need to add more data + _store._backend.insertBack(stuff); + } + ++_store._length; + return 1; + } + /// Ditto + size_t insertBack(Stuff)(Stuff stuff) + if (isInputRange!Stuff && is(ElementType!Stuff : bool)) + { + static if (!hasLength!Stuff) size_t result; + for (; !stuff.empty; stuff.popFront()) + { + insertBack(stuff.front); + static if (!hasLength!Stuff) ++result; + } + static if (!hasLength!Stuff) return result; + else return stuff.length; + } + /// ditto + alias stableInsertBack = insertBack; + + /** + Removes the value at the front or back of the container. The + stable version behaves the same, but guarantees that ranges + iterating over the container are never invalidated. The + optional parameter $(D howMany) instructs removal of that many + elements. If $(D howMany > n), all elements are removed and no + exception is thrown. + + Precondition: $(D !empty) + + Complexity: $(BIGOH log(n)). + */ + void removeBack() + { + enforce(_store._length); + if (_store._length % bitsPerWord) + { + // Cool, just decrease the length + --_store._length; + } + else + { + // Reduce the allocated space + --_store._length; + _store._backend.length = _store._backend.length - 1; + } + } + /// ditto + alias stableRemoveBack = removeBack; + + /** + Removes $(D howMany) values at the front or back of the + container. Unlike the unparameterized versions above, these + functions do not throw if they could not remove $(D howMany) + elements. Instead, if $(D howMany > n), all elements are + removed. The returned value is the effective number of elements + removed. The stable version behaves the same, but guarantees + that ranges iterating over the container are never invalidated. + + Returns: The number of elements removed + + Complexity: $(BIGOH howMany * log(n)). + */ + /// ditto + size_t removeBack(size_t howMany) + { + if (howMany >= length) + { + howMany = length; + clear(); + } + else + { + length = length - howMany; + } + return howMany; + } + + unittest + { + Array!bool a; + a.length = 1057; + assert(a.removeBack(1000) == 1000); + assert(a.length == 57); + foreach (e; a) + { + assert(!e); + } + } + + /** + Inserts $(D stuff) before, after, or instead range $(D r), + which must be a valid range previously extracted from this + container. $(D stuff) can be a value convertible to $(D + ElementType) or a range of objects convertible to $(D + ElementType). The stable version behaves the same, but + guarantees that ranges iterating over the container are never + invalidated. + + Returns: The number of values inserted. + + Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff) + */ + size_t insertBefore(Stuff)(Range r, Stuff stuff) + { + // TODO: make this faster, it moves one bit at a time + immutable inserted = stableInsertBack(stuff); + immutable tailLength = length - inserted; + bringToFront( + this[r._a .. tailLength], + this[tailLength .. length]); + return inserted; + } + /// ditto + alias stableInsertBefore = insertBefore; + + unittest + { + Array!bool a; + version (bugxxxx) + { + a._store.refCountedDebug = true; + } + a.insertBefore(a[], true); + assert(a.length == 1, text(a.length)); + a.insertBefore(a[], false); + assert(a.length == 2, text(a.length)); + } + + /// ditto + size_t insertAfter(Stuff)(Range r, Stuff stuff) + { + // TODO: make this faster, it moves one bit at a time + immutable inserted = stableInsertBack(stuff); + immutable tailLength = length - inserted; + bringToFront( + this[r._b .. tailLength], + this[tailLength .. length]); + return inserted; + } + /// ditto + alias stableInsertAfter = insertAfter; + + unittest + { + Array!bool a; + a.length = 10; + a.insertAfter(a[0 .. 5], true); + assert(a.length == 11, text(a.length)); + assert(a[5]); + } + /// ditto + size_t replace(Stuff)(Range r, Stuff stuff) if (is(Stuff : bool)) + { + if (!r.empty) + { + // There is room + r.front = stuff; + r.popFront(); + linearRemove(r); + } + else + { + // No room, must insert + insertBefore(r, stuff); + } + return 1; + } + /// ditto + alias stableReplace = replace; + + unittest + { + Array!bool a; + a.length = 10; + a.replace(a[3 .. 5], true); + assert(a.length == 9, text(a.length)); + assert(a[3]); + } + + /** + Removes all elements belonging to $(D r), which must be a range + obtained originally from this container. The stable version + behaves the same, but guarantees that ranges iterating over the + container are never invalidated. + + Returns: A range spanning the remaining elements in the container that + initially were right after $(D r). + + Complexity: $(BIGOH n) + */ + Range linearRemove(Range r) + { + copy(this[r._b .. length], this[r._a .. length]); + length = length - r.length; + return this[r._a .. length]; + } + /// ditto + alias stableLinearRemove = linearRemove; +} + +unittest +{ + Array!bool a; + assert(a.empty); +} + +unittest +{ + Array!bool arr; + arr.insert([false, false, false, false]); + assert(arr.front == false); + assert(arr.back == false); + assert(arr[1] == false); + auto slice = arr[]; + slice = arr[0 .. $]; + slice = slice[1 .. $]; + slice.front = true; + slice.back = true; + slice[1] = true; + assert(slice.front == true); + assert(slice.back == true); + assert(slice[1] == true); + assert(slice.moveFront == true); + assert(slice.moveBack == true); + assert(slice.moveAt(1) == true); +} diff --git a/libphobos/src/std/container/binaryheap.d b/libphobos/src/std/container/binaryheap.d new file mode 100644 index 000000000..78db6d2b4 --- /dev/null +++ b/libphobos/src/std/container/binaryheap.d @@ -0,0 +1,442 @@ +module std.container.binaryheap; + +import std.exception, std.algorithm, std.conv, std.range, + std.traits, std.typecons; +public import std.container.util; + +// BinaryHeap +/** +Implements a $(WEB en.wikipedia.org/wiki/Binary_heap, binary heap) +container on top of a given random-access range type (usually $(D +T[])) or a random-access container type (usually $(D Array!T)). The +documentation of $(D BinaryHeap) will refer to the underlying range or +container as the $(I store) of the heap. + +The binary heap induces structure over the underlying store such that +accessing the largest element (by using the $(D front) property) is a +$(BIGOH 1) operation and extracting it (by using the $(D +removeFront()) method) is done fast in $(BIGOH log n) time. + +If $(D less) is the less-than operator, which is the default option, +then $(D BinaryHeap) defines a so-called max-heap that optimizes +extraction of the $(I largest) elements. To define a min-heap, +instantiate BinaryHeap with $(D "a > b") as its predicate. + +Simply extracting elements from a $(D BinaryHeap) container is +tantamount to lazily fetching elements of $(D Store) in descending +order. Extracting elements from the $(D BinaryHeap) to completion +leaves the underlying store sorted in ascending order but, again, +yields elements in descending order. + +If $(D Store) is a range, the $(D BinaryHeap) cannot grow beyond the +size of that range. If $(D Store) is a container that supports $(D +insertBack), the $(D BinaryHeap) may grow by adding elements to the +container. + */ +struct BinaryHeap(Store, alias less = "a < b") +if (isRandomAccessRange!(Store) || isRandomAccessRange!(typeof(Store.init[]))) +{ + import std.functional : binaryFun; + +// Really weird @@BUG@@: if you comment out the "private:" label below, +// std.algorithm can't unittest anymore +//private: + + // The payload includes the support store and the effective length + private static struct Data + { + Store _store; + size_t _length; + } + private RefCounted!(Data, RefCountedAutoInitialize.no) _payload; + // Comparison predicate + private alias comp = binaryFun!(less); + // Convenience accessors + private @property ref Store _store() + { + assert(_payload.refCountedStore.isInitialized); + return _payload._store; + } + private @property ref size_t _length() + { + assert(_payload.refCountedStore.isInitialized); + return _payload._length; + } + + // Asserts that the heap property is respected. + private void assertValid() + { + debug + { + if (!_payload.refCountedStore.isInitialized) return; + if (_length < 2) return; + for (size_t n = _length - 1; n >= 1; --n) + { + auto parentIdx = (n - 1) / 2; + assert(!comp(_store[parentIdx], _store[n]), text(n)); + } + } + } + + // Assuming the element at index i perturbs the heap property in + // store r, percolates it down the heap such that the heap + // property is restored. + private void percolateDown(Store r, size_t i, size_t length) + { + for (;;) + { + auto left = i * 2 + 1, right = left + 1; + if (right == length) + { + if (comp(r[i], r[left])) swap(r, i, left); + return; + } + if (right > length) return; + assert(left < length && right < length); + auto largest = comp(r[i], r[left]) + ? (comp(r[left], r[right]) ? right : left) + : (comp(r[i], r[right]) ? right : i); + if (largest == i) return; + swap(r, i, largest); + i = largest; + } + } + + // @@@BUG@@@: add private here, std.algorithm doesn't unittest anymore + /*private*/ void pop(Store store) + { + assert(!store.empty, "Cannot pop an empty store."); + if (store.length == 1) return; + auto t1 = moveFront(store[]); + auto t2 = moveBack(store[]); + store.front = move(t2); + store.back = move(t1); + percolateDown(store, 0, store.length - 1); + } + + /*private*/ static void swap(Store _store, size_t i, size_t j) + { + static if (is(typeof(swap(_store[i], _store[j])))) + { + swap(_store[i], _store[j]); + } + else static if (is(typeof(_store.moveAt(i)))) + { + auto t1 = _store.moveAt(i); + auto t2 = _store.moveAt(j); + _store[i] = move(t2); + _store[j] = move(t1); + } + else // assume it's a container and access its range with [] + { + auto t1 = _store[].moveAt(i); + auto t2 = _store[].moveAt(j); + _store[i] = move(t2); + _store[j] = move(t1); + } + } + +public: + + /** + Converts the store $(D s) into a heap. If $(D initialSize) is + specified, only the first $(D initialSize) elements in $(D s) + are transformed into a heap, after which the heap can grow up + to $(D r.length) (if $(D Store) is a range) or indefinitely (if + $(D Store) is a container with $(D insertBack)). Performs + $(BIGOH min(r.length, initialSize)) evaluations of $(D less). + */ + this(Store s, size_t initialSize = size_t.max) + { + acquire(s, initialSize); + } + +/** +Takes ownership of a store. After this, manipulating $(D s) may make +the heap work incorrectly. + */ + void acquire(Store s, size_t initialSize = size_t.max) + { + _payload.refCountedStore.ensureInitialized(); + _store = move(s); + _length = min(_store.length, initialSize); + if (_length < 2) return; + for (auto i = (_length - 2) / 2; ; ) + { + this.percolateDown(_store, i, _length); + if (i-- == 0) break; + } + assertValid(); + } + +/** +Takes ownership of a store assuming it already was organized as a +heap. + */ + void assume(Store s, size_t initialSize = size_t.max) + { + _payload.refCountedStore.ensureInitialized(); + _store = s; + _length = min(_store.length, initialSize); + assertValid(); + } + +/** +Clears the heap. Returns the portion of the store from $(D 0) up to +$(D length), which satisfies the $(LUCKY heap property). + */ + auto release() + { + if (!_payload.refCountedStore.isInitialized) + { + return typeof(_store[0 .. _length]).init; + } + assertValid(); + auto result = _store[0 .. _length]; + _payload = _payload.init; + return result; + } + +/** +Returns $(D true) if the heap is _empty, $(D false) otherwise. + */ + @property bool empty() + { + return !length; + } + +/** +Returns a duplicate of the heap. The underlying store must also +support a $(D dup) method. + */ + @property BinaryHeap dup() + { + BinaryHeap result; + if (!_payload.refCountedStore.isInitialized) return result; + result.assume(_store.dup, length); + return result; + } + +/** +Returns the _length of the heap. + */ + @property size_t length() + { + return _payload.refCountedStore.isInitialized ? _length : 0; + } + +/** +Returns the _capacity of the heap, which is the length of the +underlying store (if the store is a range) or the _capacity of the +underlying store (if the store is a container). + */ + @property size_t capacity() + { + if (!_payload.refCountedStore.isInitialized) return 0; + static if (is(typeof(_store.capacity) : size_t)) + { + return _store.capacity; + } + else + { + return _store.length; + } + } + +/** +Returns a copy of the _front of the heap, which is the largest element +according to $(D less). + */ + @property ElementType!Store front() + { + enforce(!empty, "Cannot call front on an empty heap."); + return _store.front; + } + +/** +Clears the heap by detaching it from the underlying store. + */ + void clear() + { + _payload = _payload.init; + } + +/** +Inserts $(D value) into the store. If the underlying store is a range +and $(D length == capacity), throws an exception. + */ + size_t insert(ElementType!Store value) + { + static if (is(typeof(_store.insertBack(value)))) + { + _payload.refCountedStore.ensureInitialized(); + if (length == _store.length) + { + // reallocate + _store.insertBack(value); + } + else + { + // no reallocation + _store[_length] = value; + } + } + else + { + // can't grow + enforce(length < _store.length, + "Cannot grow a heap created over a range"); + _store[_length] = value; + } + + // sink down the element + for (size_t n = _length; n; ) + { + auto parentIdx = (n - 1) / 2; + if (!comp(_store[parentIdx], _store[n])) break; // done! + // must swap and continue + swap(_store, parentIdx, n); + n = parentIdx; + } + ++_length; + debug(BinaryHeap) assertValid(); + return 1; + } + +/** +Removes the largest element from the heap. + */ + void removeFront() + { + enforce(!empty, "Cannot call removeFront on an empty heap."); + if (_length > 1) + { + auto t1 = moveFront(_store[]); + auto t2 = moveAt(_store[], _length - 1); + _store.front = move(t2); + _store[_length - 1] = move(t1); + } + --_length; + percolateDown(_store, 0, _length); + } + + /// ditto + alias popFront = removeFront; + +/** +Removes the largest element from the heap and returns a copy of +it. The element still resides in the heap's store. For performance +reasons you may want to use $(D removeFront) with heaps of objects +that are expensive to copy. + */ + ElementType!Store removeAny() + { + removeFront(); + return _store[_length]; + } + +/** +Replaces the largest element in the store with $(D value). + */ + void replaceFront(ElementType!Store value) + { + // must replace the top + assert(!empty, "Cannot call replaceFront on an empty heap."); + _store.front = value; + percolateDown(_store, 0, _length); + debug(BinaryHeap) assertValid(); + } + +/** +If the heap has room to grow, inserts $(D value) into the store and +returns $(D true). Otherwise, if $(D less(value, front)), calls $(D +replaceFront(value)) and returns again $(D true). Otherwise, leaves +the heap unaffected and returns $(D false). This method is useful in +scenarios where the smallest $(D k) elements of a set of candidates +must be collected. + */ + bool conditionalInsert(ElementType!Store value) + { + _payload.refCountedStore.ensureInitialized(); + if (_length < _store.length) + { + insert(value); + return true; + } + // must replace the top + assert(!_store.empty, "Cannot replace front of an empty heap."); + if (!comp(value, _store.front)) return false; // value >= largest + _store.front = value; + percolateDown(_store, 0, _length); + debug(BinaryHeap) assertValid(); + return true; + } +} + +/// Example from "Introduction to Algorithms" Cormen et al, p 146 +unittest +{ + int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; + auto h = heapify(a); + // largest element + assert(h.front == 16); + // a has the heap property + assert(equal(a, [ 16, 14, 10, 8, 7, 9, 3, 2, 4, 1 ])); +} + +/// $(D BinaryHeap) implements the standard input range interface, allowing +/// lazy iteration of the underlying range in descending order. +unittest +{ + int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]; + auto top5 = heapify(a).take(5); + assert(top5.equal([16, 14, 10, 9, 8])); +} + +/** +Convenience function that returns a $(D BinaryHeap!Store) object +initialized with $(D s) and $(D initialSize). + */ +BinaryHeap!(Store, less) heapify(alias less = "a < b", Store)(Store s, + size_t initialSize = size_t.max) +{ + return BinaryHeap!(Store, less)(s, initialSize); +} + +unittest +{ + { + // example from "Introduction to Algorithms" Cormen et al., p 146 + int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; + auto h = heapify(a); + h = heapify!"a < b"(a); + assert(h.front == 16); + assert(a == [ 16, 14, 10, 8, 7, 9, 3, 2, 4, 1 ]); + auto witness = [ 16, 14, 10, 9, 8, 7, 4, 3, 2, 1 ]; + for (; !h.empty; h.removeFront(), witness.popFront()) + { + assert(!witness.empty); + assert(witness.front == h.front); + } + assert(witness.empty); + } + { + int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ]; + int[] b = new int[a.length]; + BinaryHeap!(int[]) h = BinaryHeap!(int[])(b, 0); + foreach (e; a) + { + h.insert(e); + } + assert(b == [ 16, 14, 10, 8, 7, 3, 9, 1, 4, 2 ], text(b)); + } +} + +unittest +{ + // Test range interface. + int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]; + auto h = heapify(a); + static assert(isInputRange!(typeof(h))); + assert(h.equal([16, 14, 10, 9, 8, 7, 4, 3, 2, 1])); +} diff --git a/libphobos/src/std/container/dlist.d b/libphobos/src/std/container/dlist.d new file mode 100644 index 000000000..2cf7ba2c9 --- /dev/null +++ b/libphobos/src/std/container/dlist.d @@ -0,0 +1,813 @@ +module std.container.dlist; + +import std.exception, std.range, std.traits; +public import std.container.util; + +/** +Implements a doubly-linked list. + +$(D DList) uses reference semantics. + */ +struct DList(T) +{ + private struct Node + { + T _payload = T.init; + Node * _prev; + Node * _next; + } + private Node* _root; + private void initialize() @safe nothrow pure + { + if (_root) return; + _root = new Node(); + _root._next = _root._prev = _root; + } + private ref inout(Node*) _first() @property @safe nothrow pure inout + { + assert(_root); + return _root._next; + } + private ref inout(Node*) _last() @property @safe nothrow pure inout + { + assert(_root); + return _root._prev; + } + +/** +Constructor taking a number of nodes + */ + this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) + { + insertBack(values); + } + +/** +Constructor taking an input range + */ + this(Stuff)(Stuff stuff) + if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) + { + insertBack(stuff); + } + +/** +Comparison for equality. + +Complexity: $(BIGOH min(n, n1)) where $(D n1) is the number of +elements in $(D rhs). + */ + bool opEquals()(ref const DList rhs) const + if (is(typeof(front == front))) + { + alias lhs = this; + const lroot = lhs._root; + const rroot = rhs._root; + + if (lroot is rroot) return true; + if (lroot is null) return rroot is rroot._next; + if (rroot is null) return lroot is lroot._next; + + const(Node)* pl = lhs._first; + const(Node)* pr = rhs._first; + while (true) + { + if (pl is lroot) return pr is rroot; + if (pr is rroot) return false; + + // !== because of NaN + if (!(pl._payload == pr._payload)) return false; + + pl = pl._next; + pr = pr._next; + } + } + + /** + Defines the container's primary range, which embodies a bidirectional range. + */ + struct Range + { + private Node * _first; + private Node * _last; + private this(Node* first, Node* last) + { + assert(!!_first == !!_last, "Dlist.Range.this: Invalid arguments"); + _first = first; _last = last; + } + private this(Node* n) { _first = _last = n; } + + /// Input range primitives. + @property const nothrow + bool empty() + { + assert(!!_first == !!_last, "DList.Range: Invalidated state"); + return !_first; + } + + /// ditto + @property ref T front() + { + assert(!empty, "DList.Range.front: Range is empty"); + return _first._payload; + } + + /// ditto + void popFront() + { + assert(!empty, "DList.Range.popFront: Range is empty"); + if (_first is _last) + { + _first = _last = null; + } + else + { + assert(_first._next && _first is _first._next._prev, "DList.Range: Invalidated state"); + _first = _first._next; + } + } + + /// Forward range primitive. + @property Range save() { return this; } + + /// Bidirectional range primitives. + @property ref T back() + { + assert(!empty, "DList.Range.back: Range is empty"); + return _last._payload; + } + + /// ditto + void popBack() + { + assert(!empty, "DList.Range.popBack: Range is empty"); + if (_first is _last) + { + _first = _last = null; + } + else + { + assert(_last._prev && _last is _last._prev._next, "DList.Range: Invalidated state"); + _last = _last._prev; + } + } + } + + unittest + { + static assert(isBidirectionalRange!Range); + } + +/** +Property returning $(D true) if and only if the container has no +elements. + +Complexity: $(BIGOH 1) + */ + bool empty() @property const nothrow + { + return _root is null || _root is _first; + } + +/** +Removes all contents from the $(D DList). + +Postcondition: $(D empty) + +Complexity: $(BIGOH 1) + */ + void clear() + { + //remove actual elements. + remove(this[]); + } + +/** +Duplicates the container. The elements themselves are not transitively +duplicated. + +Complexity: $(BIGOH n). + */ + @property DList dup() + { + return DList(this[]); + } + +/** +Returns a range that iterates over all elements of the container, in +forward order. + +Complexity: $(BIGOH 1) + */ + Range opSlice() + { + if (empty) + return Range(null, null); + else + return Range(_first, _last); + } + +/** +Forward to $(D opSlice().front). + +Complexity: $(BIGOH 1) + */ + @property ref inout(T) front() inout + { + assert(!empty, "DList.front: List is empty"); + return _first._payload; + } + +/** +Forward to $(D opSlice().back). + +Complexity: $(BIGOH 1) + */ + @property ref inout(T) back() inout + { + assert(!empty, "DList.back: List is empty"); + return _last._payload; + } + +/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ +/+ BEGIN CONCAT FUNCTIONS HERE +/ +/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ + +/** +Returns a new $(D DList) that's the concatenation of $(D this) and its +argument $(D rhs). + */ + DList opBinary(string op, Stuff)(Stuff rhs) + if (op == "~" && is(typeof(insertBack(rhs)))) + { + auto ret = this.dup; + ret.insertBack(rhs); + return ret; + } + /// ditto + DList opBinary(string op)(DList rhs) + if (op == "~") + { + return ret ~ rhs[]; + } + +/** +Returns a new $(D DList) that's the concatenation of the argument $(D lhs) +and $(D this). + */ + DList opBinaryRight(string op, Stuff)(Stuff lhs) + if (op == "~" && is(typeof(insertFront(lhs)))) + { + auto ret = this.dup; + ret.insertFront(lhs); + return ret; + } + +/** +Appends the contents of the argument $(D rhs) into $(D this). + */ + DList opOpAssign(string op, Stuff)(Stuff rhs) + if (op == "~" && is(typeof(insertBack(rhs)))) + { + insertBack(rhs); + return this; + } + +/// ditto + DList opOpAssign(string op)(DList rhs) + if (op == "~") + { + return this ~= rhs[]; + } + +/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ +/+ BEGIN INSERT FUNCTIONS HERE +/ +/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ + +/** +Inserts $(D stuff) to the front/back of the container. $(D stuff) can be a +value convertible to $(D T) or a range of objects convertible to $(D +T). The stable version behaves the same, but guarantees that ranges +iterating over the container are never invalidated. + +Returns: The number of elements inserted + +Complexity: $(BIGOH log(n)) + */ + size_t insertFront(Stuff)(Stuff stuff) + { + initialize(); + return insertAfterNode(_root, stuff); + } + + /// ditto + size_t insertBack(Stuff)(Stuff stuff) + { + initialize(); + return insertBeforeNode(_root, stuff); + } + + /// ditto + alias insert = insertBack; + + /// ditto + alias stableInsert = insert; + + /// ditto + alias stableInsertFront = insertFront; + + /// ditto + alias stableInsertBack = insertBack; + +/** +Inserts $(D stuff) after range $(D r), which must be a non-empty range +previously extracted from this container. + +$(D stuff) can be a value convertible to $(D T) or a range of objects +convertible to $(D T). The stable version behaves the same, but +guarantees that ranges iterating over the container are never +invalidated. + +Returns: The number of values inserted. + +Complexity: $(BIGOH k + m), where $(D k) is the number of elements in +$(D r) and $(D m) is the length of $(D stuff). + */ + size_t insertBefore(Stuff)(Range r, Stuff stuff) + { + if (r._first) + return insertBeforeNode(r._first, stuff); + else + { + initialize(); + return insertAfterNode(_root, stuff); + } + } + + /// ditto + alias stableInsertBefore = insertBefore; + + /// ditto + size_t insertAfter(Stuff)(Range r, Stuff stuff) + { + if (r._last) + return insertAfterNode(r._last, stuff); + else + { + initialize(); + return insertBeforeNode(_root, stuff); + } + } + + /// ditto + alias stableInsertAfter = insertAfter; + +/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ +/+ BEGIN REMOVE FUNCTIONS HERE +/ +/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ + +/** +Picks one value from the front of the container, removes it from the +container, and returns it. + +Precondition: $(D !empty) + +Returns: The element removed. + +Complexity: $(BIGOH 1). + */ + T removeAny() + { + import std.algorithm : move; + + assert(!empty, "DList.removeAny: List is empty"); + auto result = move(back); + removeBack(); + return result; + } + /// ditto + alias stableRemoveAny = removeAny; + +/** +Removes the value at the front/back of the container. The stable version +behaves the same, but guarantees that ranges iterating over the +container are never invalidated. + +Precondition: $(D !empty) + +Complexity: $(BIGOH 1). + */ + void removeFront() + { + assert(!empty, "DList.removeFront: List is empty"); + assert(_root is _first._prev, "DList: Inconsistent state"); + connect(_root, _first._next); + } + + /// ditto + alias stableRemoveFront = removeFront; + + /// ditto + void removeBack() + { + assert(!empty, "DList.removeBack: List is empty"); + assert(_last._next is _root, "DList: Inconsistent state"); + connect(_last._prev, _root); + } + + /// ditto + alias stableRemoveBack = removeBack; + +/** +Removes $(D howMany) values at the front or back of the +container. Unlike the unparameterized versions above, these functions +do not throw if they could not remove $(D howMany) elements. Instead, +if $(D howMany > n), all elements are removed. The returned value is +the effective number of elements removed. The stable version behaves +the same, but guarantees that ranges iterating over the container are +never invalidated. + +Returns: The number of elements removed + +Complexity: $(BIGOH howMany). + */ + size_t removeFront(size_t howMany) + { + if (!_root) return 0; + size_t result; + auto p = _first; + while (p !is _root && result < howMany) + { + p = p._next; + ++result; + } + connect(_root, p); + return result; + } + + /// ditto + alias stableRemoveFront = removeFront; + + /// ditto + size_t removeBack(size_t howMany) + { + if (!_root) return 0; + size_t result; + auto p = _last; + while (p !is _root && result < howMany) + { + p = p._prev; + ++result; + } + connect(p, _root); + return result; + } + + /// ditto + alias stableRemoveBack = removeBack; + +/** +Removes all elements belonging to $(D r), which must be a range +obtained originally from this container. + +Returns: A range spanning the remaining elements in the container that +initially were right after $(D r). + +Complexity: $(BIGOH 1) + */ + Range remove(Range r) + { + if (r.empty) + return r; + + assert(_root !is null, "Cannot remove from an un-initialized List"); + assert(r._first, "Remove: Range is empty"); + + connect(r._first._prev, r._last._next); + return Range(r._last._next, _last); + } + + /// ditto + Range linearRemove(Range r) + { + return remove(r); + } + +/** +$(D linearRemove) functions as $(D remove), but also accepts ranges that are +result the of a $(D take) operation. This is a convenient way to remove a +fixed amount of elements from the range. + +Complexity: $(BIGOH r.walkLength) + */ + Range linearRemove(Take!Range r) + { + assert(_root !is null, "Cannot remove from an un-initialized List"); + assert(r.source._first, "Remove: Range is empty"); + + Node* first = r.source._first; + Node* last = void; + do + { + last = r.source._first; + r.popFront(); + } while ( !r.empty ); + + return remove(Range(first, last)); + } + + /// ditto + alias stableRemove = remove; + /// ditto + alias stableLinearRemove = linearRemove; + +private: + // Helper: Given nodes p and n, connects them. + void connect(Node* p, Node* n) @trusted nothrow pure + { + p._next = n; + n._prev = p; + } + + // Helper: Inserts stuff before the node n. + size_t insertBeforeNode(Stuff)(Node* n, ref Stuff stuff) + if (isImplicitlyConvertible!(Stuff, T)) + { + auto p = new Node(stuff, n._prev, n); + n._prev._next = p; + n._prev = p; + return 1; + } + // ditto + size_t insertBeforeNode(Stuff)(Node* n, ref Stuff stuff) + if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) + { + if (stuff.empty) return 0; + size_t result; + Range r = createRange(stuff, result); + connect(n._prev, r._first); + connect(r._last, n); + return result; + } + + // Helper: Inserts stuff after the node n. + size_t insertAfterNode(Stuff)(Node* n, ref Stuff stuff) + if (isImplicitlyConvertible!(Stuff, T)) + { + auto p = new Node(stuff, n, n._next); + n._next._prev = p; + n._next = p; + return 1; + } + // ditto + size_t insertAfterNode(Stuff)(Node* n, ref Stuff stuff) + if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) + { + if (stuff.empty) return 0; + size_t result; + Range r = createRange(stuff, result); + connect(r._last, n._next); + connect(n, r._first); + return result; + } + + // Helper: Creates a chain of nodes from the range stuff. + Range createRange(Stuff)(ref Stuff stuff, ref size_t result) + { + Node* first = new Node(stuff.front); + Node* last = first; + ++result; + for ( stuff.popFront() ; !stuff.empty ; stuff.popFront() ) + { + auto p = new Node(stuff.front, last); + last = last._next = p; + ++result; + } + return Range(first, last); + } +} + +unittest +{ + import std.algorithm : equal; + + //Tests construction signatures + alias IntList = DList!int; + auto a0 = IntList(); + auto a1 = IntList(0); + auto a2 = IntList(0, 1); + auto a3 = IntList([0]); + auto a4 = IntList([0, 1]); + + assert(a0[].empty); + assert(equal(a1[], [0])); + assert(equal(a2[], [0, 1])); + assert(equal(a3[], [0])); + assert(equal(a4[], [0, 1])); +} + +unittest +{ + import std.algorithm : equal; + + alias IntList = DList!int; + IntList list = IntList([0,1,2,3]); + assert(equal(list[],[0,1,2,3])); + list.insertBack([4,5,6,7]); + assert(equal(list[],[0,1,2,3,4,5,6,7])); + + list = IntList(); + list.insertFront([0,1,2,3]); + assert(equal(list[],[0,1,2,3])); + list.insertFront([4,5,6,7]); + assert(equal(list[],[4,5,6,7,0,1,2,3])); +} + +unittest +{ + import std.algorithm : equal; + + alias IntList = DList!int; + IntList list = IntList([0,1,2,3]); + auto range = list[]; + for( ; !range.empty; range.popFront()) + { + int item = range.front; + if (item == 2) + { + list.stableLinearRemove(take(range, 1)); + break; + } + } + assert(equal(list[],[0,1,3])); + + list = IntList([0,1,2,3]); + range = list[]; + for( ; !range.empty; range.popFront()) + { + int item = range.front; + if (item == 2) + { + list.stableLinearRemove(take(range,2)); + break; + } + } + assert(equal(list[],[0,1])); + + list = IntList([0,1,2,3]); + range = list[]; + for( ; !range.empty; range.popFront()) + { + int item = range.front; + if (item == 0) + { + list.stableLinearRemove(take(range,2)); + break; + } + } + assert(equal(list[],[2,3])); + + list = IntList([0,1,2,3]); + range = list[]; + for( ; !range.empty; range.popFront()) + { + int item = range.front; + if (item == 1) + { + list.stableLinearRemove(take(range,2)); + break; + } + } + assert(equal(list[],[0,3])); +} + +unittest +{ + import std.algorithm : equal; + + auto dl = DList!string(["a", "b", "d"]); + dl.insertAfter(dl[], "e"); // insert at the end + assert(equal(dl[], ["a", "b", "d", "e"])); + auto dlr = dl[]; + dlr.popBack(); dlr.popBack(); + dl.insertAfter(dlr, "c"); // insert after "b" + assert(equal(dl[], ["a", "b", "c", "d", "e"])); +} + +unittest +{ + import std.algorithm : equal; + + auto dl = DList!string(["a", "b", "d"]); + dl.insertBefore(dl[], "e"); // insert at the front + assert(equal(dl[], ["e", "a", "b", "d"])); + auto dlr = dl[]; + dlr.popFront(); dlr.popFront(); + dl.insertBefore(dlr, "c"); // insert before "b" + assert(equal(dl[], ["e", "a", "c", "b", "d"])); +} + +unittest +{ + auto d = DList!int([1, 2, 3]); + d.front = 5; //test frontAssign + assert(d.front == 5); + auto r = d[]; + r.back = 1; + assert(r.back == 1); +} + +// Issue 8895 +unittest +{ + auto a = make!(DList!int)(1,2,3,4); + auto b = make!(DList!int)(1,2,3,4); + auto c = make!(DList!int)(1,2,3,5); + auto d = make!(DList!int)(1,2,3,4,5); + assert(a == b); // this better terminate! + assert(!(a == c)); + assert(!(a == d)); +} + +unittest +{ + auto d = DList!int([1, 2, 3]); + d.front = 5; //test frontAssign + assert(d.front == 5); + auto r = d[]; + r.back = 1; + assert(r.back == 1); +} + +unittest +{ + auto a = DList!int(); + assert(a.removeFront(10) == 0); + a.insert([1, 2, 3]); + assert(a.removeFront(10) == 3); + assert(a[].empty); +} + +unittest +{ + import std.algorithm : equal; + + //Verify all flavors of ~ + auto a = DList!int(); + auto b = DList!int(); + auto c = DList!int([1, 2, 3]); + auto d = DList!int([4, 5, 6]); + + assert((a ~ b[])[].empty); + assert((c ~ d[])[].equal([1, 2, 3, 4, 5, 6])); + assert(c[].equal([1, 2, 3])); + assert(d[].equal([4, 5, 6])); + + assert((c[] ~ d)[].equal([1, 2, 3, 4, 5, 6])); + assert(c[].equal([1, 2, 3])); + assert(d[].equal([4, 5, 6])); + + a~=c[]; + assert(a[].equal([1, 2, 3])); + assert(c[].equal([1, 2, 3])); + + a~=d[]; + assert(a[].equal([1, 2, 3, 4, 5, 6])); + assert(d[].equal([4, 5, 6])); + + a~=[7, 8, 9]; + assert(a[].equal([1, 2, 3, 4, 5, 6, 7, 8, 9])); + + //trick test: + auto r = c[]; + c.removeFront(); + c.removeBack(); +} + +unittest +{ + import std.algorithm : equal; + + //8905 + auto a = DList!int([1, 2, 3, 4]); + auto r = a[]; + a.stableRemoveBack(); + a.stableInsertBack(7); + assert(a[].equal([1, 2, 3, 7])); +} + +unittest //12566 +{ + auto dl2 = DList!int([2,7]); + dl2.removeFront(); + assert(dl2[].walkLength == 1); + dl2.removeBack(); + assert(dl2.empty, "not empty?!"); +} + +unittest //13076 +{ + DList!int list; + assert(list.empty); + list.clear(); +} diff --git a/libphobos/src/std/container/package.d b/libphobos/src/std/container/package.d new file mode 100644 index 000000000..5cc47d671 --- /dev/null +++ b/libphobos/src/std/container/package.d @@ -0,0 +1,856 @@ +// Written in the D programming language. + +/** +Defines generic containers. + +Source: $(PHOBOSSRC std/container/_package.d) +Macros: +WIKI = Phobos/StdContainer +TEXTWITHCOMMAS = $0 + +Copyright: Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code +copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders. + +License: Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE_1_0.txt or copy at $(WEB +boost.org/LICENSE_1_0.txt)). + +Authors: Steven Schveighoffer, $(WEB erdani.com, Andrei Alexandrescu) + +$(BOOKTABLE $(TEXTWITHCOMMAS Container primitives. A _container need not +implement all primitives, but if a primitive is implemented, it must +support the syntax described in the $(B syntax) column with the semantics +described in the $(B description) column, and it must not have worse worst-case +complexity than denoted in big-O notation in the $(BIGOH ·) column. +Below, $(D C) means a _container type, $(D c) is a value of _container +type, $(D n$(SUBx)) represents the effective length of value $(D x), +which could be a single element (in which case $(D n$(SUB x)) is $(D 1)), +a _container, or a range.), + +$(TR $(TH Syntax) $(TH $(BIGOH ·)) $(TH Description)) + +$(TR $(TDNW $(D C(x))) $(TDNW $(D n$(SUB x))) $(TD Creates a +_container of type $(D C) from either another _container or a range.)) + +$(TR $(TDNW $(D c.dup)) $(TDNW $(D n$(SUB c))) $(TD Returns a +duplicate of the _container.)) + +$(TR $(TDNW $(D c ~ x)) $(TDNW $(D n$(SUB c) + n$(SUB x))) $(TD +Returns the concatenation of $(D c) and $(D r). $(D x) may be a single +element or an input range.)) + +$(TR $(TDNW $(D x ~ c)) $(TDNW $(D n$(SUB c) + n$(SUB x))) $(TD +Returns the concatenation of $(D x) and $(D c). $(D x) may be a +single element or an input range type.)) + +$(LEADINGROW Iteration) + +$(TR $(TD $(D c.Range)) $(TD) $(TD The primary range +type associated with the _container.)) + +$(TR $(TD $(D c[])) $(TDNW $(D log n$(SUB c))) $(TD Returns a range +iterating over the entire _container, in a _container-defined order.)) + +$(TR $(TDNW $(D c[a .. b])) $(TDNW $(D log n$(SUB c))) $(TD Fetches a +portion of the _container from key $(D a) to key $(D b).)) + +$(LEADINGROW Capacity) + +$(TR $(TD $(D c.empty)) $(TD $(D 1)) $(TD Returns $(D true) if the +_container has no elements, $(D false) otherwise.)) + +$(TR $(TD $(D c.length)) $(TDNW $(D log n$(SUB c))) $(TD Returns the +number of elements in the _container.)) + +$(TR $(TDNW $(D c.length = n)) $(TDNW $(D n$(SUB c) + n)) $(TD Forces +the number of elements in the _container to $(D n). If the _container +ends up growing, the added elements are initialized in a +_container-dependent manner (usually with $(D T.init)).)) + +$(TR $(TD $(D c.capacity)) $(TDNW $(D log n$(SUB c))) $(TD Returns the +maximum number of elements that can be stored in the _container +without triggering a reallocation.)) + +$(TR $(TD $(D c.reserve(x))) $(TD $(D n$(SUB c))) $(TD Forces $(D +capacity) to at least $(D x) without reducing it.)) + +$(LEADINGROW Access) + +$(TR $(TDNW $(D c.front)) $(TDNW $(D log n$(SUB c))) $(TD Returns the +first element of the _container, in a _container-defined order.)) + +$(TR $(TDNW $(D c.moveFront)) $(TDNW $(D log n$(SUB c))) $(TD +Destructively reads and returns the first element of the +_container. The slot is not removed from the _container; it is left +initialized with $(D T.init). This routine need not be defined if $(D +front) returns a $(D ref).)) + +$(TR $(TDNW $(D c.front = v)) $(TDNW $(D log n$(SUB c))) $(TD Assigns +$(D v) to the first element of the _container.)) + +$(TR $(TDNW $(D c.back)) $(TDNW $(D log n$(SUB c))) $(TD Returns the +last element of the _container, in a _container-defined order.)) + +$(TR $(TDNW $(D c.moveBack)) $(TDNW $(D log n$(SUB c))) $(TD +Destructively reads and returns the last element of the +container. The slot is not removed from the _container; it is left +initialized with $(D T.init). This routine need not be defined if $(D +front) returns a $(D ref).)) + +$(TR $(TDNW $(D c.back = v)) $(TDNW $(D log n$(SUB c))) $(TD Assigns +$(D v) to the last element of the _container.)) + +$(TR $(TDNW $(D c[x])) $(TDNW $(D log n$(SUB c))) $(TD Provides +indexed access into the _container. The index type is +_container-defined. A container may define several index types (and +consequently overloaded indexing).)) + +$(TR $(TDNW $(D c.moveAt(x))) $(TDNW $(D log n$(SUB c))) $(TD +Destructively reads and returns the value at position $(D x). The slot +is not removed from the _container; it is left initialized with $(D +T.init).)) + +$(TR $(TDNW $(D c[x] = v)) $(TDNW $(D log n$(SUB c))) $(TD Sets +element at specified index into the _container.)) + +$(TR $(TDNW $(D c[x] $(I op)= v)) $(TDNW $(D log n$(SUB c))) +$(TD Performs read-modify-write operation at specified index into the +_container.)) + +$(LEADINGROW Operations) + +$(TR $(TDNW $(D e in c)) $(TDNW $(D log n$(SUB c))) $(TD +Returns nonzero if e is found in $(D c).)) + +$(TR $(TDNW $(D c.lowerBound(v))) $(TDNW $(D log n$(SUB c))) $(TD +Returns a range of all elements strictly less than $(D v).)) + +$(TR $(TDNW $(D c.upperBound(v))) $(TDNW $(D log n$(SUB c))) $(TD +Returns a range of all elements strictly greater than $(D v).)) + +$(TR $(TDNW $(D c.equalRange(v))) $(TDNW $(D log n$(SUB c))) $(TD +Returns a range of all elements in $(D c) that are equal to $(D v).)) + +$(LEADINGROW Modifiers) + +$(TR $(TDNW $(D c ~= x)) $(TDNW $(D n$(SUB c) + n$(SUB x))) +$(TD Appends $(D x) to $(D c). $(D x) may be a single element or an +input range type.)) + +$(TR $(TDNW $(D c.clear())) $(TDNW $(D n$(SUB c))) $(TD Removes all +elements in $(D c).)) + +$(TR $(TDNW $(D c.insert(x))) $(TDNW $(D n$(SUB x) * log n$(SUB c))) +$(TD Inserts $(D x) in $(D c) at a position (or positions) chosen by $(D c).)) + +$(TR $(TDNW $(D c.stableInsert(x))) +$(TDNW $(D n$(SUB x) * log n$(SUB c))) $(TD Same as $(D c.insert(x)), +but is guaranteed to not invalidate any ranges.)) + +$(TR $(TDNW $(D c.linearInsert(v))) $(TDNW $(D n$(SUB c))) $(TD Same +as $(D c.insert(v)) but relaxes complexity to linear.)) + +$(TR $(TDNW $(D c.stableLinearInsert(v))) $(TDNW $(D n$(SUB c))) +$(TD Same as $(D c.stableInsert(v)) but relaxes complexity to linear.)) + +$(TR $(TDNW $(D c.removeAny())) $(TDNW $(D log n$(SUB c))) +$(TD Removes some element from $(D c) and returns it.)) + +$(TR $(TDNW $(D c.stableRemoveAny())) $(TDNW $(D log n$(SUB c))) +$(TD Same as $(D c.removeAny()), but is guaranteed to not invalidate any +iterators.)) + +$(TR $(TDNW $(D c.insertFront(v))) $(TDNW $(D log n$(SUB c))) +$(TD Inserts $(D v) at the front of $(D c).)) + +$(TR $(TDNW $(D c.stableInsertFront(v))) $(TDNW $(D log n$(SUB c))) +$(TD Same as $(D c.insertFront(v)), but guarantees no ranges will be +invalidated.)) + +$(TR $(TDNW $(D c.insertBack(v))) $(TDNW $(D log n$(SUB c))) +$(TD Inserts $(D v) at the back of $(D c).)) + +$(TR $(TDNW $(D c.stableInsertBack(v))) $(TDNW $(D log n$(SUB c))) +$(TD Same as $(D c.insertBack(v)), but guarantees no ranges will be +invalidated.)) + +$(TR $(TDNW $(D c.removeFront())) $(TDNW $(D log n$(SUB c))) +$(TD Removes the element at the front of $(D c).)) + +$(TR $(TDNW $(D c.stableRemoveFront())) $(TDNW $(D log n$(SUB c))) +$(TD Same as $(D c.removeFront()), but guarantees no ranges will be +invalidated.)) + +$(TR $(TDNW $(D c.removeBack())) $(TDNW $(D log n$(SUB c))) +$(TD Removes the value at the back of $(D c).)) + +$(TR $(TDNW $(D c.stableRemoveBack())) $(TDNW $(D log n$(SUB c))) +$(TD Same as $(D c.removeBack()), but guarantees no ranges will be +invalidated.)) + +$(TR $(TDNW $(D c.remove(r))) $(TDNW $(D n$(SUB r) * log n$(SUB c))) +$(TD Removes range $(D r) from $(D c).)) + +$(TR $(TDNW $(D c.stableRemove(r))) +$(TDNW $(D n$(SUB r) * log n$(SUB c))) +$(TD Same as $(D c.remove(r)), but guarantees iterators are not +invalidated.)) + +$(TR $(TDNW $(D c.linearRemove(r))) $(TDNW $(D n$(SUB c))) +$(TD Removes range $(D r) from $(D c).)) + +$(TR $(TDNW $(D c.stableLinearRemove(r))) $(TDNW $(D n$(SUB c))) +$(TD Same as $(D c.linearRemove(r)), but guarantees iterators are not +invalidated.)) + +$(TR $(TDNW $(D c.removeKey(k))) $(TDNW $(D log n$(SUB c))) +$(TD Removes an element from $(D c) by using its key $(D k). +The key's type is defined by the _container.)) + +$(TR $(TDNW $(D )) $(TDNW $(D )) $(TD )) + +) + */ + +module std.container; + +public import std.container.array; +public import std.container.binaryheap; +public import std.container.dlist; +public import std.container.rbtree; +public import std.container.slist; + +import std.typetuple; + + +/* The following documentation and type $(D TotalContainer) are +intended for developers only. + +$(D TotalContainer) is an unimplemented container that illustrates a +host of primitives that a container may define. It is to some extent +the bottom of the conceptual container hierarchy. A given container +most often will choose to only implement a subset of these primitives, +and define its own additional ones. Adhering to the standard primitive +names below allows generic code to work independently of containers. + +Things to remember: any container must be a reference type, whether +implemented as a $(D class) or $(D struct). No primitive below +requires the container to escape addresses of elements, which means +that compliant containers can be defined to use reference counting or +other deterministic memory management techniques. + +A container may choose to define additional specific operations. The +only requirement is that those operations bear different names than +the ones below, lest user code gets confused. + +Complexity of operations should be interpreted as "at least as good +as". If an operation is required to have $(BIGOH n) complexity, it +could have anything lower than that, e.g. $(BIGOH log(n)). Unless +specified otherwise, $(D n) inside a $(BIGOH) expression stands for +the number of elements in the container. + */ +struct TotalContainer(T) +{ +/** +If the container has a notion of key-value mapping, $(D KeyType) +defines the type of the key of the container. + */ + alias KeyType = T; + +/** +If the container has a notion of multikey-value mapping, $(D +KeyTypes[k]), where $(D k) is a zero-based unsigned number, defines +the type of the $(D k)th key of the container. + +A container may define both $(D KeyType) and $(D KeyTypes), e.g. in +the case it has the notion of primary/preferred key. + */ + alias KeyTypes = TypeTuple!T; + +/** +If the container has a notion of key-value mapping, $(D ValueType) +defines the type of the value of the container. Typically, a map-style +container mapping values of type $(D K) to values of type $(D V) +defines $(D KeyType) to be $(D K) and $(D ValueType) to be $(D V). + */ + alias ValueType = T; + +/** +Defines the container's primary range, which embodies one of the +ranges defined in $(XREFMODULE range). + +Generally a container may define several types of ranges. + */ + struct Range + { + /++ + Range primitives. + +/ + @property bool empty() + { + assert(0); + } + /// Ditto + @property ref T front() //ref return optional + { + assert(0); + } + /// Ditto + @property void front(T value) //Only when front does not return by ref + { + assert(0); + } + /// Ditto + T moveFront() + { + assert(0); + } + /// Ditto + void popFront() + { + assert(0); + } + /// Ditto + @property ref T back() //ref return optional + { + assert(0); + } + /// Ditto + @property void back(T value) //Only when front does not return by ref + { + assert(0); + } + /// Ditto + T moveBack() + { + assert(0); + } + /// Ditto + void popBack() + { + assert(0); + } + /// Ditto + T opIndex(size_t i) //ref return optional + { + assert(0); + } + /// Ditto + void opIndexAssign(size_t i, T value) //Only when front does not return by ref + { + assert(0); + } + /// Ditto + T opIndexUnary(string op)(size_t i) //Only when front does not return by ref + { + assert(0); + } + /// Ditto + void opIndexOpAssign(string op)(size_t i, T value) //Only when front does not return by ref + { + assert(0); + } + /// Ditto + T moveAt(size_t i) + { + assert(0); + } + /// Ditto + @property size_t length() + { + assert(0); + } + } + +/** +Property returning $(D true) if and only if the container has no +elements. + +Complexity: $(BIGOH 1) + */ + @property bool empty() + { + assert(0); + } + +/** +Returns a duplicate of the container. The elements themselves are not +transitively duplicated. + +Complexity: $(BIGOH n). + */ + @property TotalContainer dup() + { + assert(0); + } + +/** +Returns the number of elements in the container. + +Complexity: $(BIGOH log(n)). +*/ + @property size_t length() + { + assert(0); + } + +/** +Returns the maximum number of elements the container can store without +(a) allocating memory, (b) invalidating iterators upon insertion. + +Complexity: $(BIGOH log(n)). + */ + @property size_t capacity() + { + assert(0); + } + +/** +Ensures sufficient capacity to accommodate $(D n) elements. + +Postcondition: $(D capacity >= n) + +Complexity: $(BIGOH log(e - capacity)) if $(D e > capacity), otherwise +$(BIGOH 1). + */ + void reserve(size_t e) + { + assert(0); + } + +/** +Returns a range that iterates over all elements of the container, in a +container-defined order. The container should choose the most +convenient and fast method of iteration for $(D opSlice()). + +Complexity: $(BIGOH log(n)) + */ + Range opSlice() + { + assert(0); + } + + /** + Returns a range that iterates the container between two + specified positions. + + Complexity: $(BIGOH log(n)) + */ + Range opSlice(size_t a, size_t b) + { + assert(0); + } + +/** +Forward to $(D opSlice().front) and $(D opSlice().back), respectively. + +Complexity: $(BIGOH log(n)) + */ + @property ref T front() //ref return optional + { + assert(0); + } + /// Ditto + @property void front(T value) //Only when front does not return by ref + { + assert(0); + } + /// Ditto + T moveFront() + { + assert(0); + } + /// Ditto + @property ref T back() //ref return optional + { + assert(0); + } + /// Ditto + @property void back(T value) //Only when front does not return by ref + { + assert(0); + } + /// Ditto + T moveBack() + { + assert(0); + } + +/** +Indexing operators yield or modify the value at a specified index. + */ + ref T opIndex(KeyType) //ref return optional + { + assert(0); + } + /// ditto + void opIndexAssign(KeyType i, T value) //Only when front does not return by ref + { + assert(0); + } + /// ditto + T opIndexUnary(string op)(KeyType i) //Only when front does not return by ref + { + assert(0); + } + /// ditto + void opIndexOpAssign(string op)(KeyType i, T value) //Only when front does not return by ref + { + assert(0); + } + /// ditto + T moveAt(KeyType i) + { + assert(0); + } + +/** +$(D k in container) returns true if the given key is in the container. + */ + bool opBinaryRight(string op)(KeyType k) if (op == "in") + { + assert(0); + } + +/** +Returns a range of all elements containing $(D k) (could be empty or a +singleton range). + */ + Range equalRange(KeyType k) + { + assert(0); + } + +/** +Returns a range of all elements with keys less than $(D k) (could be +empty or a singleton range). Only defined by containers that store +data sorted at all times. + */ + Range lowerBound(KeyType k) + { + assert(0); + } + +/** +Returns a range of all elements with keys larger than $(D k) (could be +empty or a singleton range). Only defined by containers that store +data sorted at all times. + */ + Range upperBound(KeyType k) + { + assert(0); + } + +/** +Returns a new container that's the concatenation of $(D this) and its +argument. $(D opBinaryRight) is only defined if $(D Stuff) does not +define $(D opBinary). + +Complexity: $(BIGOH n + m), where m is the number of elements in $(D +stuff) + */ + TotalContainer opBinary(string op)(Stuff rhs) if (op == "~") + { + assert(0); + } + + /// ditto + TotalContainer opBinaryRight(string op)(Stuff lhs) if (op == "~") + { + assert(0); + } + +/** +Forwards to $(D insertAfter(this[], stuff)). + */ + void opOpAssign(string op)(Stuff stuff) if (op == "~") + { + assert(0); + } + +/** +Removes all contents from the container. The container decides how $(D +capacity) is affected. + +Postcondition: $(D empty) + +Complexity: $(BIGOH n) + */ + void clear() + { + assert(0); + } + +/** +Sets the number of elements in the container to $(D newSize). If $(D +newSize) is greater than $(D length), the added elements are added to +unspecified positions in the container and initialized with $(D +.init). + +Complexity: $(BIGOH abs(n - newLength)) + +Postcondition: $(D _length == newLength) + */ + @property void length(size_t newLength) + { + assert(0); + } + +/** +Inserts $(D stuff) in an unspecified position in the +container. Implementations should choose whichever insertion means is +the most advantageous for the container, but document the exact +behavior. $(D stuff) can be a value convertible to the element type of +the container, or a range of values convertible to it. + +The $(D stable) version guarantees that ranges iterating over the +container are never invalidated. Client code that counts on +non-invalidating insertion should use $(D stableInsert). Such code would +not compile against containers that don't support it. + +Returns: The number of elements added. + +Complexity: $(BIGOH m * log(n)), where $(D m) is the number of +elements in $(D stuff) + */ + size_t insert(Stuff)(Stuff stuff) + { + assert(0); + } + ///ditto + size_t stableInsert(Stuff)(Stuff stuff) + { + assert(0); + } + +/** +Same as $(D insert(stuff)) and $(D stableInsert(stuff)) respectively, +but relax the complexity constraint to linear. + */ + size_t linearInsert(Stuff)(Stuff stuff) + { + assert(0); + } + ///ditto + size_t stableLinearInsert(Stuff)(Stuff stuff) + { + assert(0); + } + +/** +Picks one value in an unspecified position in the container, removes +it from the container, and returns it. Implementations should pick the +value that's the most advantageous for the container, but document the +exact behavior. The stable version behaves the same, but guarantees that +ranges iterating over the container are never invalidated. + +Precondition: $(D !empty) + +Returns: The element removed. + +Complexity: $(BIGOH log(n)). + */ + T removeAny() + { + assert(0); + } + /// ditto + T stableRemoveAny() + { + assert(0); + } + +/** +Inserts $(D value) to the front or back of the container. $(D stuff) +can be a value convertible to the container's element type or a range +of values convertible to it. The stable version behaves the same, but +guarantees that ranges iterating over the container are never +invalidated. + +Returns: The number of elements inserted + +Complexity: $(BIGOH log(n)). + */ + size_t insertFront(Stuff)(Stuff stuff) + { + assert(0); + } + /// ditto + size_t stableInsertFront(Stuff)(Stuff stuff) + { + assert(0); + } + /// ditto + size_t insertBack(Stuff)(Stuff stuff) + { + assert(0); + } + /// ditto + size_t stableInsertBack(T value) + { + assert(0); + } + +/** +Removes the value at the front or back of the container. The stable +version behaves the same, but guarantees that ranges iterating over +the container are never invalidated. The optional parameter $(D +howMany) instructs removal of that many elements. If $(D howMany > n), +all elements are removed and no exception is thrown. + +Precondition: $(D !empty) + +Complexity: $(BIGOH log(n)). + */ + void removeFront() + { + assert(0); + } + /// ditto + void stableRemoveFront() + { + assert(0); + } + /// ditto + void removeBack() + { + assert(0); + } + /// ditto + void stableRemoveBack() + { + assert(0); + } + +/** +Removes $(D howMany) values at the front or back of the +container. Unlike the unparameterized versions above, these functions +do not throw if they could not remove $(D howMany) elements. Instead, +if $(D howMany > n), all elements are removed. The returned value is +the effective number of elements removed. The stable version behaves +the same, but guarantees that ranges iterating over the container are +never invalidated. + +Returns: The number of elements removed + +Complexity: $(BIGOH howMany * log(n)). + */ + size_t removeFront(size_t howMany) + { + assert(0); + } + /// ditto + size_t stableRemoveFront(size_t howMany) + { + assert(0); + } + /// ditto + size_t removeBack(size_t howMany) + { + assert(0); + } + /// ditto + size_t stableRemoveBack(size_t howMany) + { + assert(0); + } + +/** +Removes all values corresponding to key $(D k). + +Complexity: $(BIGOH m * log(n)), where $(D m) is the number of +elements with the same key. + +Returns: The number of elements removed. + */ + size_t removeKey(KeyType k) + { + assert(0); + } + +/** +Inserts $(D stuff) before, after, or instead range $(D r), which must +be a valid range previously extracted from this container. $(D stuff) +can be a value convertible to the container's element type or a range +of objects convertible to it. The stable version behaves the same, but +guarantees that ranges iterating over the container are never +invalidated. + +Returns: The number of values inserted. + +Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff) + */ + size_t insertBefore(Stuff)(Range r, Stuff stuff) + { + assert(0); + } + /// ditto + size_t stableInsertBefore(Stuff)(Range r, Stuff stuff) + { + assert(0); + } + /// ditto + size_t insertAfter(Stuff)(Range r, Stuff stuff) + { + assert(0); + } + /// ditto + size_t stableInsertAfter(Stuff)(Range r, Stuff stuff) + { + assert(0); + } + /// ditto + size_t replace(Stuff)(Range r, Stuff stuff) + { + assert(0); + } + /// ditto + size_t stableReplace(Stuff)(Range r, Stuff stuff) + { + assert(0); + } + +/** +Removes all elements belonging to $(D r), which must be a range +obtained originally from this container. The stable version behaves the +same, but guarantees that ranges iterating over the container are +never invalidated. + +Returns: A range spanning the remaining elements in the container that +initially were right after $(D r). + +Complexity: $(BIGOH m * log(n)), where $(D m) is the number of +elements in $(D r) + */ + Range remove(Range r) + { + assert(0); + } + /// ditto + Range stableRemove(Range r) + { + assert(0); + } + +/** +Same as $(D remove) above, but has complexity relaxed to linear. + +Returns: A range spanning the remaining elements in the container that +initially were right after $(D r). + +Complexity: $(BIGOH n) + */ + Range linearRemove(Range r) + { + assert(0); + } + /// ditto + Range stableLinearRemove(Range r) + { + assert(0); + } +} + +unittest { + TotalContainer!int test; +} diff --git a/libphobos/src/std/container/rbtree.d b/libphobos/src/std/container/rbtree.d new file mode 100644 index 000000000..540a7576b --- /dev/null +++ b/libphobos/src/std/container/rbtree.d @@ -0,0 +1,1759 @@ +module std.container.rbtree; + +import std.exception, std.algorithm, std.range, std.traits; +import std.functional : binaryFun; +import std.typetuple : allSatisfy; +public import std.container.util; + +version(unittest) version = RBDoChecks; + +//version = RBDoChecks; + +version(RBDoChecks) +{ + import std.stdio; +} + +/* + * Implementation for a Red Black node for use in a Red Black Tree (see below) + * + * this implementation assumes we have a marker Node that is the parent of the + * root Node. This marker Node is not a valid Node, but marks the end of the + * collection. The root is the left child of the marker Node, so it is always + * last in the collection. The marker Node is passed in to the setColor + * function, and the Node which has this Node as its parent is assumed to be + * the root Node. + * + * A Red Black tree should have O(lg(n)) insertion, removal, and search time. + */ +struct RBNode(V) +{ + /* + * Convenience alias + */ + alias Node = RBNode*; + + private Node _left; + private Node _right; + private Node _parent; + + /** + * The value held by this node + */ + V value; + + /** + * Enumeration determining what color the node is. Null nodes are assumed + * to be black. + */ + enum Color : byte + { + Red, + Black + } + + /** + * The color of the node. + */ + Color color; + + /** + * Get the left child + */ + @property Node left() + { + return _left; + } + + /** + * Get the right child + */ + @property Node right() + { + return _right; + } + + /** + * Get the parent + */ + @property Node parent() + { + return _parent; + } + + /** + * Set the left child. Also updates the new child's parent node. This + * does not update the previous child. + * + * Returns newNode + */ + @property Node left(Node newNode) + { + _left = newNode; + if(newNode !is null) + newNode._parent = &this; + return newNode; + } + + /** + * Set the right child. Also updates the new child's parent node. This + * does not update the previous child. + * + * Returns newNode + */ + @property Node right(Node newNode) + { + _right = newNode; + if(newNode !is null) + newNode._parent = &this; + return newNode; + } + + // assume _left is not null + // + // performs rotate-right operation, where this is T, _right is R, _left is + // L, _parent is P: + // + // P P + // | -> | + // T L + // / \ / \ + // L R a T + // / \ / \ + // a b b R + // + /** + * Rotate right. This performs the following operations: + * - The left child becomes the parent of this node. + * - This node becomes the new parent's right child. + * - The old right child of the new parent becomes the left child of this + * node. + */ + Node rotateR() + in + { + assert(_left !is null); + } + body + { + // sets _left._parent also + if(isLeftNode) + parent.left = _left; + else + parent.right = _left; + Node tmp = _left._right; + + // sets _parent also + _left.right = &this; + + // sets tmp._parent also + left = tmp; + + return &this; + } + + // assumes _right is non null + // + // performs rotate-left operation, where this is T, _right is R, _left is + // L, _parent is P: + // + // P P + // | -> | + // T R + // / \ / \ + // L R T b + // / \ / \ + // a b L a + // + /** + * Rotate left. This performs the following operations: + * - The right child becomes the parent of this node. + * - This node becomes the new parent's left child. + * - The old left child of the new parent becomes the right child of this + * node. + */ + Node rotateL() + in + { + assert(_right !is null); + } + body + { + // sets _right._parent also + if(isLeftNode) + parent.left = _right; + else + parent.right = _right; + Node tmp = _right._left; + + // sets _parent also + _right.left = &this; + + // sets tmp._parent also + right = tmp; + return &this; + } + + + /** + * Returns true if this node is a left child. + * + * Note that this should always return a value because the root has a + * parent which is the marker node. + */ + @property bool isLeftNode() const + in + { + assert(_parent !is null); + } + body + { + return _parent._left is &this; + } + + /** + * Set the color of the node after it is inserted. This performs an + * update to the whole tree, possibly rotating nodes to keep the Red-Black + * properties correct. This is an O(lg(n)) operation, where n is the + * number of nodes in the tree. + * + * end is the marker node, which is the parent of the topmost valid node. + */ + void setColor(Node end) + { + // test against the marker node + if(_parent !is end) + { + if(_parent.color == Color.Red) + { + Node cur = &this; + while(true) + { + // because root is always black, _parent._parent always exists + if(cur._parent.isLeftNode) + { + // parent is left node, y is 'uncle', could be null + Node y = cur._parent._parent._right; + if(y !is null && y.color == Color.Red) + { + cur._parent.color = Color.Black; + y.color = Color.Black; + cur = cur._parent._parent; + if(cur._parent is end) + { + // root node + cur.color = Color.Black; + break; + } + else + { + // not root node + cur.color = Color.Red; + if(cur._parent.color == Color.Black) + // satisfied, exit the loop + break; + } + } + else + { + if(!cur.isLeftNode) + cur = cur._parent.rotateL(); + cur._parent.color = Color.Black; + cur = cur._parent._parent.rotateR(); + cur.color = Color.Red; + // tree should be satisfied now + break; + } + } + else + { + // parent is right node, y is 'uncle' + Node y = cur._parent._parent._left; + if(y !is null && y.color == Color.Red) + { + cur._parent.color = Color.Black; + y.color = Color.Black; + cur = cur._parent._parent; + if(cur._parent is end) + { + // root node + cur.color = Color.Black; + break; + } + else + { + // not root node + cur.color = Color.Red; + if(cur._parent.color == Color.Black) + // satisfied, exit the loop + break; + } + } + else + { + if(cur.isLeftNode) + cur = cur._parent.rotateR(); + cur._parent.color = Color.Black; + cur = cur._parent._parent.rotateL(); + cur.color = Color.Red; + // tree should be satisfied now + break; + } + } + } + + } + } + else + { + // + // this is the root node, color it black + // + color = Color.Black; + } + } + + /** + * Remove this node from the tree. The 'end' node is used as the marker + * which is root's parent. Note that this cannot be null! + * + * Returns the next highest valued node in the tree after this one, or end + * if this was the highest-valued node. + */ + Node remove(Node end) + { + // + // remove this node from the tree, fixing the color if necessary. + // + Node x; + Node ret = next; + + // if this node has 2 children + if (_left !is null && _right !is null) + { + // + // normally, we can just swap this node's and y's value, but + // because an iterator could be pointing to y and we don't want to + // disturb it, we swap this node and y's structure instead. This + // can also be a benefit if the value of the tree is a large + // struct, which takes a long time to copy. + // + Node yp, yl, yr; + Node y = ret; // y = next + yp = y._parent; + yl = y._left; + yr = y._right; + auto yc = y.color; + auto isyleft = y.isLeftNode; + + // + // replace y's structure with structure of this node. + // + if(isLeftNode) + _parent.left = y; + else + _parent.right = y; + // + // need special case so y doesn't point back to itself + // + y.left = _left; + if(_right is y) + y.right = &this; + else + y.right = _right; + y.color = color; + + // + // replace this node's structure with structure of y. + // + left = yl; + right = yr; + if(_parent !is y) + { + if(isyleft) + yp.left = &this; + else + yp.right = &this; + } + color = yc; + } + + // if this has less than 2 children, remove it + if(_left !is null) + x = _left; + else + x = _right; + + bool deferedUnlink = false; + if(x is null) + { + // pretend this is a null node, defer unlinking the node + x = &this; + deferedUnlink = true; + } + else if(isLeftNode) + _parent.left = x; + else + _parent.right = x; + + // if the color of this is black, then it needs to be fixed + if(color == color.Black) + { + // need to recolor the tree. + while(x._parent !is end && x.color == Node.Color.Black) + { + if(x.isLeftNode) + { + // left node + Node w = x._parent._right; + if(w.color == Node.Color.Red) + { + w.color = Node.Color.Black; + x._parent.color = Node.Color.Red; + x._parent.rotateL(); + w = x._parent._right; + } + Node wl = w.left; + Node wr = w.right; + if((wl is null || wl.color == Node.Color.Black) && + (wr is null || wr.color == Node.Color.Black)) + { + w.color = Node.Color.Red; + x = x._parent; + } + else + { + if(wr is null || wr.color == Node.Color.Black) + { + // wl cannot be null here + wl.color = Node.Color.Black; + w.color = Node.Color.Red; + w.rotateR(); + w = x._parent._right; + } + + w.color = x._parent.color; + x._parent.color = Node.Color.Black; + w._right.color = Node.Color.Black; + x._parent.rotateL(); + x = end.left; // x = root + } + } + else + { + // right node + Node w = x._parent._left; + if(w.color == Node.Color.Red) + { + w.color = Node.Color.Black; + x._parent.color = Node.Color.Red; + x._parent.rotateR(); + w = x._parent._left; + } + Node wl = w.left; + Node wr = w.right; + if((wl is null || wl.color == Node.Color.Black) && + (wr is null || wr.color == Node.Color.Black)) + { + w.color = Node.Color.Red; + x = x._parent; + } + else + { + if(wl is null || wl.color == Node.Color.Black) + { + // wr cannot be null here + wr.color = Node.Color.Black; + w.color = Node.Color.Red; + w.rotateL(); + w = x._parent._left; + } + + w.color = x._parent.color; + x._parent.color = Node.Color.Black; + w._left.color = Node.Color.Black; + x._parent.rotateR(); + x = end.left; // x = root + } + } + } + x.color = Node.Color.Black; + } + + if(deferedUnlink) + { + // + // unlink this node from the tree + // + if(isLeftNode) + _parent.left = null; + else + _parent.right = null; + } + + return ret; + } + + /** + * Return the leftmost descendant of this node. + */ + @property Node leftmost() + { + Node result = &this; + while(result._left !is null) + result = result._left; + return result; + } + + /** + * Return the rightmost descendant of this node + */ + @property Node rightmost() + { + Node result = &this; + while(result._right !is null) + result = result._right; + return result; + } + + /** + * Returns the next valued node in the tree. + * + * You should never call this on the marker node, as it is assumed that + * there is a valid next node. + */ + @property Node next() + { + Node n = &this; + if(n.right is null) + { + while(!n.isLeftNode) + n = n._parent; + return n._parent; + } + else + return n.right.leftmost; + } + + /** + * Returns the previous valued node in the tree. + * + * You should never call this on the leftmost node of the tree as it is + * assumed that there is a valid previous node. + */ + @property Node prev() + { + Node n = &this; + if(n.left is null) + { + while(n.isLeftNode) + n = n._parent; + return n._parent; + } + else + return n.left.rightmost; + } + + Node dup(scope Node delegate(V v) alloc) + { + // + // duplicate this and all child nodes + // + // The recursion should be lg(n), so we shouldn't have to worry about + // stack size. + // + Node copy = alloc(value); + copy.color = color; + if(_left !is null) + copy.left = _left.dup(alloc); + if(_right !is null) + copy.right = _right.dup(alloc); + return copy; + } + + Node dup() + { + Node copy = new RBNode!V; + copy.value = value; + copy.color = color; + if(_left !is null) + copy.left = _left.dup(); + if(_right !is null) + copy.right = _right.dup(); + return copy; + } +} + +/** + * Implementation of a $(LUCKY red-black tree) container. + * + * All inserts, removes, searches, and any function in general has complexity + * of $(BIGOH lg(n)). + * + * To use a different comparison than $(D "a < b"), pass a different operator string + * that can be used by $(XREF functional, binaryFun), or pass in a + * function, delegate, functor, or any type where $(D less(a, b)) results in a $(D bool) + * value. + * + * Note that less should produce a strict ordering. That is, for two unequal + * elements $(D a) and $(D b), $(D less(a, b) == !less(b, a)). $(D less(a, a)) should + * always equal $(D false). + * + * If $(D allowDuplicates) is set to $(D true), then inserting the same element more than + * once continues to add more elements. If it is $(D false), duplicate elements are + * ignored on insertion. If duplicates are allowed, then new elements are + * inserted after all existing duplicate elements. + */ +final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false) + if(is(typeof(binaryFun!less(T.init, T.init)))) +{ + alias _less = binaryFun!less; + + // BUG: this must come first in the struct due to issue 2810 + + // add an element to the tree, returns the node added, or the existing node + // if it has already been added and allowDuplicates is false + + private auto _add(Elem n) + { + Node result; + static if(!allowDuplicates) + bool added = true; + + if(!_end.left) + { + _end.left = _begin = result = allocate(n); + } + else + { + Node newParent = _end.left; + Node nxt = void; + while(true) + { + if(_less(n, newParent.value)) + { + nxt = newParent.left; + if(nxt is null) + { + // + // add to right of new parent + // + newParent.left = result = allocate(n); + break; + } + } + else + { + static if(!allowDuplicates) + { + if(!_less(newParent.value, n)) + { + result = newParent; + added = false; + break; + } + } + nxt = newParent.right; + if(nxt is null) + { + // + // add to right of new parent + // + newParent.right = result = allocate(n); + break; + } + } + newParent = nxt; + } + if(_begin.left) + _begin = _begin.left; + } + + static if(allowDuplicates) + { + result.setColor(_end); + version(RBDoChecks) + check(); + ++_length; + return result; + } + else + { + import std.typecons : Tuple; + + if(added) + { + ++_length; + result.setColor(_end); + } + version(RBDoChecks) + check(); + return Tuple!(bool, "added", Node, "n")(added, result); + } + } + + version(unittest) + { + private enum doUnittest = isIntegral!T; + + // note, this must be final so it does not affect the vtable layout + final bool arrayEqual(T[] arr) + { + if(walkLength(this[]) == arr.length) + { + foreach(v; arr) + { + if(!(v in this)) + return false; + } + return true; + } + return false; + } + } + else + { + private enum doUnittest = false; + } + + /** + * Element type for the tree + */ + alias Elem = T; + + // used for convenience + private alias Node = RBNode!Elem.Node; + + private Node _end; + private Node _begin; + private size_t _length; + + private void _setup() + { + assert(!_end); //Make sure that _setup isn't run more than once. + _begin = _end = allocate(); + } + + static private Node allocate() + { + return new RBNode!Elem; + } + + static private Node allocate(Elem v) + { + auto result = allocate(); + result.value = v; + return result; + } + + /** + * The range type for $(D RedBlackTree) + */ + struct Range + { + private Node _begin; + private Node _end; + + private this(Node b, Node e) + { + _begin = b; + _end = e; + } + + /** + * Returns $(D true) if the range is _empty + */ + @property bool empty() const + { + return _begin is _end; + } + + /** + * Returns the first element in the range + */ + @property Elem front() + { + return _begin.value; + } + + /** + * Returns the last element in the range + */ + @property Elem back() + { + return _end.prev.value; + } + + /** + * pop the front element from the range + * + * complexity: amortized $(BIGOH 1) + */ + void popFront() + { + _begin = _begin.next; + } + + /** + * pop the back element from the range + * + * complexity: amortized $(BIGOH 1) + */ + void popBack() + { + _end = _end.prev; + } + + /** + * Trivial _save implementation, needed for $(D isForwardRange). + */ + @property Range save() + { + return this; + } + } + + static if(doUnittest) unittest + { + auto ts = new RedBlackTree(1, 2, 3, 4, 5); + assert(ts.length == 5); + auto r = ts[]; + + static if(less == "a < b") + auto vals = [1, 2, 3, 4, 5]; + else + auto vals = [5, 4, 3, 2, 1]; + + assert(std.algorithm.equal(r, vals)); + assert(r.front == vals.front); + assert(r.back != r.front); + auto oldfront = r.front; + auto oldback = r.back; + r.popFront(); + r.popBack(); + assert(r.front != r.back); + assert(r.front != oldfront); + assert(r.back != oldback); + assert(ts.length == 5); + } + + // find a node based on an element value + private Node _find(Elem e) + { + static if(allowDuplicates) + { + Node cur = _end.left; + Node result = null; + while(cur) + { + if(_less(cur.value, e)) + cur = cur.right; + else if(_less(e, cur.value)) + cur = cur.left; + else + { + // want to find the left-most element + result = cur; + cur = cur.left; + } + } + return result; + } + else + { + Node cur = _end.left; + while(cur) + { + if(_less(cur.value, e)) + cur = cur.right; + else if(_less(e, cur.value)) + cur = cur.left; + else + return cur; + } + return null; + } + } + + /** + * Check if any elements exist in the container. Returns $(D false) if at least + * one element exists. + */ + @property bool empty() + { + return _end.left is null; + } + + /++ + Returns the number of elements in the container. + + Complexity: $(BIGOH 1). + +/ + @property size_t length() + { + return _length; + } + + /** + * Duplicate this container. The resulting container contains a shallow + * copy of the elements. + * + * Complexity: $(BIGOH n) + */ + @property RedBlackTree dup() + { + return new RedBlackTree(_end.dup(), _length); + } + + static if(doUnittest) unittest + { + auto ts = new RedBlackTree(1, 2, 3, 4, 5); + assert(ts.length == 5); + auto ts2 = ts.dup; + assert(ts2.length == 5); + assert(std.algorithm.equal(ts[], ts2[])); + ts2.insert(cast(Elem)6); + assert(!std.algorithm.equal(ts[], ts2[])); + assert(ts.length == 5 && ts2.length == 6); + } + + /** + * Fetch a range that spans all the elements in the container. + * + * Complexity: $(BIGOH 1) + */ + Range opSlice() + { + return Range(_begin, _end); + } + + /** + * The front element in the container + * + * Complexity: $(BIGOH 1) + */ + Elem front() + { + return _begin.value; + } + + /** + * The last element in the container + * + * Complexity: $(BIGOH log(n)) + */ + Elem back() + { + return _end.prev.value; + } + + /++ + $(D in) operator. Check to see if the given element exists in the + container. + + Complexity: $(BIGOH log(n)) + +/ + bool opBinaryRight(string op)(Elem e) if (op == "in") + { + return _find(e) !is null; + } + + static if(doUnittest) unittest + { + auto ts = new RedBlackTree(1, 2, 3, 4, 5); + assert(cast(Elem)3 in ts); + assert(cast(Elem)6 !in ts); + } + + /** + * Compares two trees for equality. + * + * Complexity: $(BIGOH n*log(n)) + */ + override bool opEquals(Object rhs) + { + RedBlackTree that = cast(RedBlackTree)rhs; + if (that is null) return false; + + // If there aren't the same number of nodes, we can't be equal. + if (this._length != that._length) return false; + + // FIXME: use a more efficient algo (if one exists?) + auto thisRange = this[]; + auto thatRange = that[]; + return equal!(function(Elem a, Elem b) => !_less(a,b) && !_less(b,a)) + (thisRange, thatRange); + } + + static if(doUnittest) unittest + { + auto t1 = new RedBlackTree(1,2,3,4); + auto t2 = new RedBlackTree(1,2,3,4); + auto t3 = new RedBlackTree(1,2,3,5); + auto t4 = new RedBlackTree(1,2,3,4,5); + auto o = new Object(); + + assert(t1==t2); + assert(t1!=t3); + assert(t1!=t4); + assert(t1!=o); // pathological case, must not crash + } + + /** + * Removes all elements from the container. + * + * Complexity: $(BIGOH 1) + */ + void clear() + { + _end.left = null; + _begin = _end; + _length = 0; + } + + static if(doUnittest) unittest + { + auto ts = new RedBlackTree(1,2,3,4,5); + assert(ts.length == 5); + ts.clear(); + assert(ts.empty && ts.length == 0); + } + + /** + * Insert a single element in the container. Note that this does not + * invalidate any ranges currently iterating the container. + * + * Complexity: $(BIGOH log(n)) + */ + size_t stableInsert(Stuff)(Stuff stuff) if (isImplicitlyConvertible!(Stuff, Elem)) + { + static if(allowDuplicates) + { + _add(stuff); + return 1; + } + else + { + return(_add(stuff).added ? 1 : 0); + } + } + + /** + * Insert a range of elements in the container. Note that this does not + * invalidate any ranges currently iterating the container. + * + * Complexity: $(BIGOH m * log(n)) + */ + size_t stableInsert(Stuff)(Stuff stuff) if(isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem)) + { + size_t result = 0; + static if(allowDuplicates) + { + foreach(e; stuff) + { + ++result; + _add(e); + } + } + else + { + foreach(e; stuff) + { + if(_add(e).added) + ++result; + } + } + return result; + } + + /// ditto + alias insert = stableInsert; + + static if(doUnittest) unittest + { + auto ts = new RedBlackTree(2,1,3,4,5,2,5); + static if(allowDuplicates) + { + assert(ts.length == 7); + assert(ts.stableInsert(cast(Elem[])[7, 8, 6, 9, 10, 8]) == 6); + assert(ts.length == 13); + assert(ts.stableInsert(cast(Elem)11) == 1 && ts.length == 14); + assert(ts.stableInsert(cast(Elem)7) == 1 && ts.length == 15); + + static if(less == "a < b") + assert(ts.arrayEqual([1,2,2,3,4,5,5,6,7,7,8,8,9,10,11])); + else + assert(ts.arrayEqual([11,10,9,8,8,7,7,6,5,5,4,3,2,2,1])); + } + else + { + assert(ts.length == 5); + assert(ts.stableInsert(cast(Elem[])[7, 8, 6, 9, 10, 8]) == 5); + assert(ts.length == 10); + assert(ts.stableInsert(cast(Elem)11) == 1 && ts.length == 11); + assert(ts.stableInsert(cast(Elem)7) == 0 && ts.length == 11); + + static if(less == "a < b") + assert(ts.arrayEqual([1,2,3,4,5,6,7,8,9,10,11])); + else + assert(ts.arrayEqual([11,10,9,8,7,6,5,4,3,2,1])); + } + } + + /** + * Remove an element from the container and return its value. + * + * Complexity: $(BIGOH log(n)) + */ + Elem removeAny() + { + scope(success) + --_length; + auto n = _begin; + auto result = n.value; + _begin = n.remove(_end); + version(RBDoChecks) + check(); + return result; + } + + static if(doUnittest) unittest + { + auto ts = new RedBlackTree(1,2,3,4,5); + assert(ts.length == 5); + auto x = ts.removeAny(); + assert(ts.length == 4); + Elem[] arr; + foreach(Elem i; 1..6) + if(i != x) arr ~= i; + assert(ts.arrayEqual(arr)); + } + + /** + * Remove the front element from the container. + * + * Complexity: $(BIGOH log(n)) + */ + void removeFront() + { + scope(success) + --_length; + _begin = _begin.remove(_end); + version(RBDoChecks) + check(); + } + + /** + * Remove the back element from the container. + * + * Complexity: $(BIGOH log(n)) + */ + void removeBack() + { + scope(success) + --_length; + auto lastnode = _end.prev; + if(lastnode is _begin) + _begin = _begin.remove(_end); + else + lastnode.remove(_end); + version(RBDoChecks) + check(); + } + + static if(doUnittest) unittest + { + auto ts = new RedBlackTree(1,2,3,4,5); + assert(ts.length == 5); + ts.removeBack(); + assert(ts.length == 4); + + static if(less == "a < b") + assert(ts.arrayEqual([1,2,3,4])); + else + assert(ts.arrayEqual([2,3,4,5])); + + ts.removeFront(); + assert(ts.arrayEqual([2,3,4]) && ts.length == 3); + } + + /++ + Removes the given range from the container. + + Returns: A range containing all of the elements that were after the + given range. + + Complexity: $(BIGOH m * log(n)) (where m is the number of elements in + the range) + +/ + Range remove(Range r) + { + auto b = r._begin; + auto e = r._end; + if(_begin is b) + _begin = e; + while(b !is e) + { + b = b.remove(_end); + --_length; + } + version(RBDoChecks) + check(); + return Range(e, _end); + } + + static if(doUnittest) unittest + { + auto ts = new RedBlackTree(1,2,3,4,5); + assert(ts.length == 5); + auto r = ts[]; + r.popFront(); + r.popBack(); + assert(ts.length == 5); + auto r2 = ts.remove(r); + assert(ts.length == 2); + assert(ts.arrayEqual([1,5])); + + static if(less == "a < b") + assert(std.algorithm.equal(r2, [5])); + else + assert(std.algorithm.equal(r2, [1])); + } + + /++ + Removes the given $(D Take!Range) from the container + + Returns: A range containing all of the elements that were after the + given range. + + Complexity: $(BIGOH m * log(n)) (where m is the number of elements in + the range) + +/ + Range remove(Take!Range r) + { + immutable isBegin = (r.source._begin is _begin); + auto b = r.source._begin; + + while(!r.empty) + { + r.popFront(); + b = b.remove(_end); + --_length; + } + + if(isBegin) + _begin = b; + + return Range(b, _end); + } + + static if(doUnittest) unittest + { + auto ts = new RedBlackTree(1,2,3,4,5); + auto r = ts[]; + r.popFront(); + assert(ts.length == 5); + auto r2 = ts.remove(take(r, 0)); + + static if(less == "a < b") + { + assert(std.algorithm.equal(r2, [2,3,4,5])); + auto r3 = ts.remove(take(r, 2)); + assert(ts.arrayEqual([1,4,5]) && ts.length == 3); + assert(std.algorithm.equal(r3, [4,5])); + } + else + { + assert(std.algorithm.equal(r2, [4,3,2,1])); + auto r3 = ts.remove(take(r, 2)); + assert(ts.arrayEqual([5,2,1]) && ts.length == 3); + assert(std.algorithm.equal(r3, [2,1])); + } + } + + /++ + Removes elements from the container that are equal to the given values + according to the less comparator. One element is removed for each value + given which is in the container. If $(D allowDuplicates) is true, + duplicates are removed only if duplicate values are given. + + Returns: The number of elements removed. + + Complexity: $(BIGOH m log(n)) (where m is the number of elements to remove) + + Examples: +-------------------- +auto rbt = redBlackTree!true(0, 1, 1, 1, 4, 5, 7); +rbt.removeKey(1, 4, 7); +assert(std.algorithm.equal(rbt[], [0, 1, 1, 5])); +rbt.removeKey(1, 1, 0); +assert(std.algorithm.equal(rbt[], [5])); +-------------------- + +/ + size_t removeKey(U...)(U elems) + if(allSatisfy!(isImplicitlyConvertibleToElem, U)) + { + Elem[U.length] toRemove; + + foreach(i, e; elems) + toRemove[i] = e; + + return removeKey(toRemove[]); + } + + /++ Ditto +/ + size_t removeKey(U)(U[] elems) + if(isImplicitlyConvertible!(U, Elem)) + { + immutable lenBefore = length; + + foreach(e; elems) + { + auto beg = _firstGreaterEqual(e); + if(beg is _end || _less(e, beg.value)) + // no values are equal + continue; + immutable isBegin = (beg is _begin); + beg = beg.remove(_end); + if(isBegin) + _begin = beg; + --_length; + } + + return lenBefore - length; + } + + /++ Ditto +/ + size_t removeKey(Stuff)(Stuff stuff) + if(isInputRange!Stuff && + isImplicitlyConvertible!(ElementType!Stuff, Elem) && + !isDynamicArray!Stuff) + { + //We use array in case stuff is a Range from this RedBlackTree - either + //directly or indirectly. + return removeKey(array(stuff)); + } + + //Helper for removeKey. + private template isImplicitlyConvertibleToElem(U) + { + enum isImplicitlyConvertibleToElem = isImplicitlyConvertible!(U, Elem); + } + + static if(doUnittest) unittest + { + auto rbt = new RedBlackTree(5, 4, 3, 7, 2, 1, 7, 6, 2, 19, 45); + + //The cast(Elem) is because these tests are instantiated with a variety + //of numeric types, and the literals are all int, which is not always + //implicitly convertible to Elem (e.g. short). + static if(allowDuplicates) + { + assert(rbt.length == 11); + assert(rbt.removeKey(cast(Elem)4) == 1 && rbt.length == 10); + assert(rbt.arrayEqual([1,2,2,3,5,6,7,7,19,45]) && rbt.length == 10); + + assert(rbt.removeKey(cast(Elem)6, cast(Elem)2, cast(Elem)1) == 3); + assert(rbt.arrayEqual([2,3,5,7,7,19,45]) && rbt.length == 7); + + assert(rbt.removeKey(cast(Elem)(42)) == 0 && rbt.length == 7); + assert(rbt.removeKey(take(rbt[], 3)) == 3 && rbt.length == 4); + + static if(less == "a < b") + assert(std.algorithm.equal(rbt[], [7,7,19,45])); + else + assert(std.algorithm.equal(rbt[], [7,5,3,2])); + } + else + { + assert(rbt.length == 9); + assert(rbt.removeKey(cast(Elem)4) == 1 && rbt.length == 8); + assert(rbt.arrayEqual([1,2,3,5,6,7,19,45])); + + assert(rbt.removeKey(cast(Elem)6, cast(Elem)2, cast(Elem)1) == 3); + assert(rbt.arrayEqual([3,5,7,19,45]) && rbt.length == 5); + + assert(rbt.removeKey(cast(Elem)(42)) == 0 && rbt.length == 5); + assert(rbt.removeKey(take(rbt[], 3)) == 3 && rbt.length == 2); + + static if(less == "a < b") + assert(std.algorithm.equal(rbt[], [19,45])); + else + assert(std.algorithm.equal(rbt[], [5,3])); + } + } + + // find the first node where the value is > e + private Node _firstGreater(Elem e) + { + // can't use _find, because we cannot return null + auto cur = _end.left; + auto result = _end; + while(cur) + { + if(_less(e, cur.value)) + { + result = cur; + cur = cur.left; + } + else + cur = cur.right; + } + return result; + } + + // find the first node where the value is >= e + private Node _firstGreaterEqual(Elem e) + { + // can't use _find, because we cannot return null. + auto cur = _end.left; + auto result = _end; + while(cur) + { + if(_less(cur.value, e)) + cur = cur.right; + else + { + result = cur; + cur = cur.left; + } + + } + return result; + } + + /** + * Get a range from the container with all elements that are > e according + * to the less comparator + * + * Complexity: $(BIGOH log(n)) + */ + Range upperBound(Elem e) + { + return Range(_firstGreater(e), _end); + } + + /** + * Get a range from the container with all elements that are < e according + * to the less comparator + * + * Complexity: $(BIGOH log(n)) + */ + Range lowerBound(Elem e) + { + return Range(_begin, _firstGreaterEqual(e)); + } + + /** + * Get a range from the container with all elements that are == e according + * to the less comparator + * + * Complexity: $(BIGOH log(n)) + */ + Range equalRange(Elem e) + { + auto beg = _firstGreaterEqual(e); + if(beg is _end || _less(e, beg.value)) + // no values are equal + return Range(beg, beg); + static if(allowDuplicates) + { + return Range(beg, _firstGreater(e)); + } + else + { + // no sense in doing a full search, no duplicates are allowed, + // so we just get the next node. + return Range(beg, beg.next); + } + } + + static if(doUnittest) unittest + { + auto ts = new RedBlackTree(1, 2, 3, 4, 5); + auto rl = ts.lowerBound(3); + auto ru = ts.upperBound(3); + auto re = ts.equalRange(3); + + static if(less == "a < b") + { + assert(std.algorithm.equal(rl, [1,2])); + assert(std.algorithm.equal(ru, [4,5])); + } + else + { + assert(std.algorithm.equal(rl, [5,4])); + assert(std.algorithm.equal(ru, [2,1])); + } + + assert(std.algorithm.equal(re, [3])); + } + + version(RBDoChecks) + { + /* + * Print the tree. This prints a sideways view of the tree in ASCII form, + * with the number of indentations representing the level of the nodes. + * It does not print values, only the tree structure and color of nodes. + */ + void printTree(Node n, int indent = 0) + { + if(n !is null) + { + printTree(n.right, indent + 2); + for(int i = 0; i < indent; i++) + write("."); + writeln(n.color == n.color.Black ? "B" : "R"); + printTree(n.left, indent + 2); + } + else + { + for(int i = 0; i < indent; i++) + write("."); + writeln("N"); + } + if(indent is 0) + writeln(); + } + + /* + * Check the tree for validity. This is called after every add or remove. + * This should only be enabled to debug the implementation of the RB Tree. + */ + void check() + { + // + // check implementation of the tree + // + int recurse(Node n, string path) + { + if(n is null) + return 1; + if(n.parent.left !is n && n.parent.right !is n) + throw new Exception("Node at path " ~ path ~ " has inconsistent pointers"); + Node next = n.next; + static if(allowDuplicates) + { + if(next !is _end && _less(next.value, n.value)) + throw new Exception("ordering invalid at path " ~ path); + } + else + { + if(next !is _end && !_less(n.value, next.value)) + throw new Exception("ordering invalid at path " ~ path); + } + if(n.color == n.color.Red) + { + if((n.left !is null && n.left.color == n.color.Red) || + (n.right !is null && n.right.color == n.color.Red)) + throw new Exception("Node at path " ~ path ~ " is red with a red child"); + } + + int l = recurse(n.left, path ~ "L"); + int r = recurse(n.right, path ~ "R"); + if(l != r) + { + writeln("bad tree at:"); + printTree(n); + throw new Exception("Node at path " ~ path ~ " has different number of black nodes on left and right paths"); + } + return l + (n.color == n.color.Black ? 1 : 0); + } + + try + { + recurse(_end.left, ""); + } + catch(Exception e) + { + printTree(_end.left, 0); + throw e; + } + } + } + + /+ + For the moment, using templatized contstructors doesn't seem to work + very well (likely due to bug# 436 and/or bug# 1528). The redBlackTree + helper function seems to do the job well enough though. + + /** + * Constructor. Pass in an array of elements, or individual elements to + * initialize the tree with. + */ + this(U)(U[] elems...) if (isImplicitlyConvertible!(U, Elem)) + { + _setup(); + stableInsert(elems); + } + + /** + * Constructor. Pass in a range of elements to initialize the tree with. + */ + this(Stuff)(Stuff stuff) if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem) && !is(Stuff == Elem[])) + { + _setup(); + stableInsert(stuff); + } + +/ + + /++ +/ + this() + { + _setup(); + } + + /++ + Constructor. Pass in an array of elements, or individual elements to + initialize the tree with. + +/ + this(Elem[] elems...) + { + _setup(); + stableInsert(elems); + } + + private this(Node end, size_t length) + { + _end = end; + _begin = end.leftmost; + _length = length; + } +} + +//Verify Example for removeKey. +unittest +{ + auto rbt = redBlackTree!true(0, 1, 1, 1, 4, 5, 7); + rbt.removeKey(1, 4, 7); + assert(std.algorithm.equal(rbt[], [0, 1, 1, 5])); + rbt.removeKey(1, 1, 0); + assert(std.algorithm.equal(rbt[], [5])); +} + +//Tests for removeKey +unittest +{ + { + auto rbt = redBlackTree(["hello", "world", "foo", "bar"]); + assert(equal(rbt[], ["bar", "foo", "hello", "world"])); + assert(rbt.removeKey("hello") == 1); + assert(equal(rbt[], ["bar", "foo", "world"])); + assert(rbt.removeKey("hello") == 0); + assert(equal(rbt[], ["bar", "foo", "world"])); + assert(rbt.removeKey("hello", "foo", "bar") == 2); + assert(equal(rbt[], ["world"])); + assert(rbt.removeKey(["", "world", "hello"]) == 1); + assert(rbt.empty); + } + + { + auto rbt = redBlackTree([1, 2, 12, 27, 4, 500]); + assert(equal(rbt[], [1, 2, 4, 12, 27, 500])); + assert(rbt.removeKey(1u) == 1); + assert(equal(rbt[], [2, 4, 12, 27, 500])); + assert(rbt.removeKey(cast(byte)1) == 0); + assert(equal(rbt[], [2, 4, 12, 27, 500])); + assert(rbt.removeKey(1, 12u, cast(byte)27) == 2); + assert(equal(rbt[], [2, 4, 500])); + assert(rbt.removeKey([cast(short)0, cast(short)500, cast(short)1]) == 1); + assert(equal(rbt[], [2, 4])); + } +} + +unittest +{ + void test(T)() + { + auto rt1 = new RedBlackTree!(T, "a < b", false)(); + auto rt2 = new RedBlackTree!(T, "a < b", true)(); + auto rt3 = new RedBlackTree!(T, "a > b", false)(); + auto rt4 = new RedBlackTree!(T, "a > b", true)(); + } + + test!long(); + test!ulong(); + test!int(); + test!uint(); + test!short(); + test!ushort(); + test!byte(); + test!byte(); +} + +/++ + Convenience function for creating a $(D RedBlackTree!E) from a list of + values. + + Examples: +-------------------- +auto rbt1 = redBlackTree(0, 1, 5, 7); +auto rbt2 = redBlackTree!string("hello", "world"); +auto rbt3 = redBlackTree!true(0, 1, 5, 7, 5); +auto rbt4 = redBlackTree!"a > b"(0, 1, 5, 7); +auto rbt5 = redBlackTree!("a > b", true)(0.1, 1.3, 5.9, 7.2, 5.9); +-------------------- + +/ +auto redBlackTree(E)(E[] elems...) +{ + return new RedBlackTree!E(elems); +} + +/++ Ditto +/ +auto redBlackTree(bool allowDuplicates, E)(E[] elems...) +{ + return new RedBlackTree!(E, "a < b", allowDuplicates)(elems); +} + +/++ Ditto +/ +auto redBlackTree(alias less, E)(E[] elems...) +{ + return new RedBlackTree!(E, less)(elems); +} + +/++ Ditto +/ +auto redBlackTree(alias less, bool allowDuplicates, E)(E[] elems...) + if(is(typeof(binaryFun!less(E.init, E.init)))) +{ + //We shouldn't need to instantiate less here, but for some reason, + //dmd can't handle it if we don't (even though the template which + //takes less but not allowDuplicates works just fine). + return new RedBlackTree!(E, binaryFun!less, allowDuplicates)(elems); +} + +//Verify Examples. +unittest +{ + auto rbt1 = redBlackTree(0, 1, 5, 7); + auto rbt2 = redBlackTree!string("hello", "world"); + auto rbt3 = redBlackTree!true(0, 1, 5, 7, 5); + auto rbt4 = redBlackTree!"a > b"(0, 1, 5, 7); + auto rbt5 = redBlackTree!("a > b", true)(0.1, 1.3, 5.9, 7.2, 5.9); +} + +//Combinations not in examples. +unittest +{ + auto rbt1 = redBlackTree!(true, string)("hello", "hello"); + auto rbt2 = redBlackTree!((a, b){return a < b;}, double)(5.1, 2.3); + auto rbt3 = redBlackTree!("a > b", true, string)("hello", "world"); +} + +unittest +{ + auto rt1 = redBlackTree(5, 4, 3, 2, 1); + assert(rt1.length == 5); + assert(array(rt1[]) == [1, 2, 3, 4, 5]); + + auto rt2 = redBlackTree!"a > b"(1.1, 2.1); + assert(rt2.length == 2); + assert(array(rt2[]) == [2.1, 1.1]); + + auto rt3 = redBlackTree!true(5, 5, 4); + assert(rt3.length == 3); + assert(array(rt3[]) == [4, 5, 5]); + + auto rt4 = redBlackTree!string("hello", "hello"); + assert(rt4.length == 1); + assert(array(rt4[]) == ["hello"]); +} diff --git a/libphobos/src/std/container/slist.d b/libphobos/src/std/container/slist.d new file mode 100644 index 000000000..d94cb76a8 --- /dev/null +++ b/libphobos/src/std/container/slist.d @@ -0,0 +1,660 @@ +module std.container.slist; + +import std.exception, std.range, std.traits; +public import std.container.util; + +/** + Implements a simple and fast singly-linked list. + */ +struct SList(T) +{ + private struct Node + { + T _payload; + Node * _next; + this(T a, Node* b) { _payload = a; _next = b; } + } + private Node * _root; + + private static Node * findLastNode(Node * n) + { + assert(n); + auto ahead = n._next; + while (ahead) + { + n = ahead; + ahead = n._next; + } + return n; + } + + private static Node * findLastNode(Node * n, size_t limit) + { + assert(n && limit); + auto ahead = n._next; + while (ahead) + { + if (!--limit) break; + n = ahead; + ahead = n._next; + } + return n; + } + + private static Node * findNode(Node * n, Node * findMe) + { + assert(n); + auto ahead = n._next; + while (ahead != findMe) + { + n = ahead; + enforce(n); + ahead = n._next; + } + return n; + } + +/** +Constructor taking a number of nodes + */ + this(U)(U[] values...) if (isImplicitlyConvertible!(U, T)) + { + insertFront(values); + } + +/** +Constructor taking an input range + */ + this(Stuff)(Stuff stuff) + if (isInputRange!Stuff + && isImplicitlyConvertible!(ElementType!Stuff, T) + && !is(Stuff == T[])) + { + insertFront(stuff); + } + +/** +Comparison for equality. + +Complexity: $(BIGOH min(n, n1)) where $(D n1) is the number of +elements in $(D rhs). + */ + bool opEquals(const SList rhs) const + { + return opEquals(rhs); + } + + /// ditto + bool opEquals(ref const SList rhs) const + { + const(Node) * n1 = _root, n2 = rhs._root; + + for (;; n1 = n1._next, n2 = n2._next) + { + if (!n1) return !n2; + if (!n2 || n1._payload != n2._payload) return false; + } + } + +/** +Defines the container's primary range, which embodies a forward range. + */ + struct Range + { + private Node * _head; + private this(Node * p) { _head = p; } + + /// Input range primitives. + @property bool empty() const { return !_head; } + + /// ditto + @property ref T front() + { + assert(!empty, "SList.Range.front: Range is empty"); + return _head._payload; + } + + /// ditto + void popFront() + { + assert(!empty, "SList.Range.popFront: Range is empty"); + _head = _head._next; + } + + /// Forward range primitive. + @property Range save() { return this; } + + T moveFront() + { + import std.algorithm : move; + + assert(!empty, "SList.Range.moveFront: Range is empty"); + return move(_head._payload); + } + + bool sameHead(Range rhs) + { + return _head && _head == rhs._head; + } + } + + unittest + { + static assert(isForwardRange!Range); + } + +/** +Property returning $(D true) if and only if the container has no +elements. + +Complexity: $(BIGOH 1) + */ + @property bool empty() const + { + return _root is null; + } + +/** +Duplicates the container. The elements themselves are not transitively +duplicated. + +Complexity: $(BIGOH n). + */ + @property SList dup() + { + return SList(this[]); + } + +/** +Returns a range that iterates over all elements of the container, in +forward order. + +Complexity: $(BIGOH 1) + */ + Range opSlice() + { + return Range(_root); + } + +/** +Forward to $(D opSlice().front). + +Complexity: $(BIGOH 1) + */ + @property ref T front() + { + assert(!empty, "SList.front: List is empty"); + return _root._payload; + } + + unittest + { + auto s = SList!int(1, 2, 3); + s.front = 42; + assert(s == SList!int(42, 2, 3)); + } + +/** +Returns a new $(D SList) that's the concatenation of $(D this) and its +argument. $(D opBinaryRight) is only defined if $(D Stuff) does not +define $(D opBinary). + */ + SList opBinary(string op, Stuff)(Stuff rhs) + if (op == "~" && is(typeof(SList(rhs)))) + { + auto toAdd = SList(rhs); + static if (is(Stuff == SList)) + { + toAdd = toAdd.dup; + } + if (empty) return toAdd; + // TODO: optimize + auto result = dup; + auto n = findLastNode(result._root); + n._next = toAdd._root; + return result; + } + +/** +Removes all contents from the $(D SList). + +Postcondition: $(D empty) + +Complexity: $(BIGOH 1) + */ + void clear() + { + _root = null; + } + +/** +Inserts $(D stuff) to the front of the container. $(D stuff) can be a +value convertible to $(D T) or a range of objects convertible to $(D +T). The stable version behaves the same, but guarantees that ranges +iterating over the container are never invalidated. + +Returns: The number of elements inserted + +Complexity: $(BIGOH m), where $(D m) is the length of $(D stuff) + */ + size_t insertFront(Stuff)(Stuff stuff) + if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) + { + size_t result; + Node * n, newRoot; + foreach (item; stuff) + { + auto newNode = new Node(item, null); + (newRoot ? n._next : newRoot) = newNode; + n = newNode; + ++result; + } + if (!n) return 0; + // Last node points to the old root + n._next = _root; + _root = newRoot; + return result; + } + + /// ditto + size_t insertFront(Stuff)(Stuff stuff) + if (isImplicitlyConvertible!(Stuff, T)) + { + auto newRoot = new Node(stuff, _root); + _root = newRoot; + return 1; + } + +/// ditto + alias insert = insertFront; + +/// ditto + alias stableInsert = insert; + + /// ditto + alias stableInsertFront = insertFront; + +/** +Picks one value from the front of the container, removes it from the +container, and returns it. + +Precondition: $(D !empty) + +Returns: The element removed. + +Complexity: $(BIGOH 1). + */ + T removeAny() + { + import std.algorithm : move; + + assert(!empty, "SList.removeAny: List is empty"); + auto result = move(_root._payload); + _root = _root._next; + return result; + } + /// ditto + alias stableRemoveAny = removeAny; + +/** +Removes the value at the front of the container. The stable version +behaves the same, but guarantees that ranges iterating over the +container are never invalidated. + +Precondition: $(D !empty) + +Complexity: $(BIGOH 1). + */ + void removeFront() + { + assert(!empty, "SList.removeFront: List is empty"); + _root = _root._next; + } + + /// ditto + alias stableRemoveFront = removeFront; + +/** +Removes $(D howMany) values at the front or back of the +container. Unlike the unparameterized versions above, these functions +do not throw if they could not remove $(D howMany) elements. Instead, +if $(D howMany > n), all elements are removed. The returned value is +the effective number of elements removed. The stable version behaves +the same, but guarantees that ranges iterating over the container are +never invalidated. + +Returns: The number of elements removed + +Complexity: $(BIGOH howMany * log(n)). + */ + size_t removeFront(size_t howMany) + { + size_t result; + while (_root && result < howMany) + { + _root = _root._next; + ++result; + } + return result; + } + + /// ditto + alias stableRemoveFront = removeFront; + +/** +Inserts $(D stuff) after range $(D r), which must be a range +previously extracted from this container. Given that all ranges for a +list end at the end of the list, this function essentially appends to +the list and uses $(D r) as a potentially fast way to reach the last +node in the list. Ideally $(D r) is positioned near or at the last +element of the list. + +$(D stuff) can be a value convertible to $(D T) or a range of objects +convertible to $(D T). The stable version behaves the same, but +guarantees that ranges iterating over the container are never +invalidated. + +Returns: The number of values inserted. + +Complexity: $(BIGOH k + m), where $(D k) is the number of elements in +$(D r) and $(D m) is the length of $(D stuff). + +Examples: +-------------------- +auto sl = SList!string(["a", "b", "d"]); +sl.insertAfter(sl[], "e"); // insert at the end (slowest) +assert(std.algorithm.equal(sl[], ["a", "b", "d", "e"])); +sl.insertAfter(std.range.take(sl[], 2), "c"); // insert after "b" +assert(std.algorithm.equal(sl[], ["a", "b", "c", "d", "e"])); +-------------------- + */ + + size_t insertAfter(Stuff)(Range r, Stuff stuff) + { + if (!_root) + { + enforce(!r._head); + return insertFront(stuff); + } + enforce(r._head); + auto n = findLastNode(r._head); + SList tmp; + auto result = tmp.insertFront(stuff); + n._next = tmp._root; + return result; + } + +/** +Similar to $(D insertAfter) above, but accepts a range bounded in +count. This is important for ensuring fast insertions in the middle of +the list. For fast insertions after a specified position $(D r), use +$(D insertAfter(take(r, 1), stuff)). The complexity of that operation +only depends on the number of elements in $(D stuff). + +Precondition: $(D r.original.empty || r.maxLength > 0) + +Returns: The number of values inserted. + +Complexity: $(BIGOH k + m), where $(D k) is the number of elements in +$(D r) and $(D m) is the length of $(D stuff). + */ + size_t insertAfter(Stuff)(Take!Range r, Stuff stuff) + { + auto orig = r.source; + if (!orig._head) + { + // Inserting after a null range counts as insertion to the + // front + return insertFront(stuff); + } + enforce(!r.empty); + // Find the last valid element in the range + foreach (i; 1 .. r.maxLength) + { + if (!orig._head._next) break; + orig.popFront(); + } + // insert here + SList tmp; + tmp._root = orig._head._next; + auto result = tmp.insertFront(stuff); + orig._head._next = tmp._root; + return result; + } + +/// ditto + alias stableInsertAfter = insertAfter; + +/** +Removes a range from the list in linear time. + +Returns: An empty range. + +Complexity: $(BIGOH n) + */ + Range linearRemove(Range r) + { + if (!_root) + { + enforce(!r._head); + return this[]; + } + if (_root == r._head) + { + clear(); + } + else + { + auto n = findNode(_root, r._head); + n._next = null; + } + return Range(null); + } + +/** +Removes a $(D Take!Range) from the list in linear time. + +Returns: A range comprehending the elements after the removed range. + +Complexity: $(BIGOH n) + */ + Range linearRemove(Take!Range r) + { + auto orig = r.source; + // We have something to remove here + if (orig._head == _root) + { + // remove straight from the head of the list + for (; !r.empty; r.popFront()) + { + removeFront(); + } + return this[]; + } + if (!r.maxLength) + { + // Nothing to remove, return the range itself + return orig; + } + // Remove from somewhere in the middle of the list + enforce(_root); + auto n1 = findNode(_root, orig._head); + auto n2 = findLastNode(orig._head, r.maxLength); + n1._next = n2._next; + return Range(n1._next); + } + +/// ditto + alias stableLinearRemove = linearRemove; +} + +unittest +{ + auto s = SList!int(1, 2, 3); + auto n = s.findLastNode(s._root); + assert(n && n._payload == 3); +} + +unittest +{ + auto s = SList!int(1, 2, 5, 10); + assert(walkLength(s[]) == 4); +} + +unittest +{ + auto src = take([0, 1, 2, 3], 3); + auto s = SList!int(src); + assert(s == SList!int(0, 1, 2)); +} + +unittest +{ + auto a = SList!int(1, 2, 3); + auto b = SList!int(4, 5, 6); + // @@@BUG@@@ in compiler + //auto c = a ~ b; + auto d = [ 4, 5, 6 ]; + auto e = a ~ d; + assert(e == SList!int(1, 2, 3, 4, 5, 6)); +} + +unittest +{ + auto a = SList!int(1, 2, 3); + auto c = a ~ 4; + assert(c == SList!int(1, 2, 3, 4)); +} + +unittest +{ + auto s = SList!int(1, 2, 3, 4); + s.insertFront([ 42, 43 ]); + assert(s == SList!int(42, 43, 1, 2, 3, 4)); +} + +unittest +{ + auto s = SList!int(1, 2, 3); + assert(s.removeAny() == 1); + assert(s == SList!int(2, 3)); + assert(s.stableRemoveAny() == 2); + assert(s == SList!int(3)); +} + +unittest +{ + import std.algorithm : equal; + + auto s = SList!int(1, 2, 3); + s.removeFront(); + assert(equal(s[], [2, 3])); + s.stableRemoveFront(); + assert(equal(s[], [3])); +} + +unittest +{ + auto s = SList!int(1, 2, 3, 4, 5, 6, 7); + assert(s.removeFront(3) == 3); + assert(s == SList!int(4, 5, 6, 7)); +} + +unittest +{ + auto a = SList!int(1, 2, 3); + auto b = SList!int(1, 2, 3); + assert(a.insertAfter(a[], b[]) == 3); +} + +unittest +{ + auto s = SList!int(1, 2, 3, 4); + auto r = take(s[], 2); + assert(s.insertAfter(r, 5) == 1); + assert(s == SList!int(1, 2, 5, 3, 4)); +} + +unittest +{ + import std.algorithm; + + // insertAfter documentation example + auto sl = SList!string(["a", "b", "d"]); + sl.insertAfter(sl[], "e"); // insert at the end (slowest) + assert(std.algorithm.equal(sl[], ["a", "b", "d", "e"])); + sl.insertAfter(std.range.take(sl[], 2), "c"); // insert after "b" + assert(std.algorithm.equal(sl[], ["a", "b", "c", "d", "e"])); +} + +unittest +{ + auto s = SList!int(1, 2, 3, 4, 5); + auto r = s[]; + popFrontN(r, 3); + auto r1 = s.linearRemove(r); + assert(s == SList!int(1, 2, 3)); + assert(r1.empty); +} + +unittest +{ + auto s = SList!int(1, 2, 3, 4, 5); + auto r = s[]; + auto r1 = s.linearRemove(r); + assert(s == SList!int()); + assert(r1.empty); +} + +unittest +{ + import std.algorithm : equal; + + auto s = SList!int(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + auto r = s[]; + popFrontN(r, 3); + auto r1 = take(r, 4); + assert(equal(r1, [4, 5, 6, 7])); + auto r2 = s.linearRemove(r1); + assert(s == SList!int(1, 2, 3, 8, 9, 10)); + assert(equal(r2, [8, 9, 10])); +} + +unittest +{ + auto lst = SList!int(1, 5, 42, 9); + assert(!lst.empty); + assert(lst.front == 1); + assert(walkLength(lst[]) == 4); + + auto lst2 = lst ~ [ 1, 2, 3 ]; + assert(walkLength(lst2[]) == 7); + + auto lst3 = lst ~ [ 7 ]; + assert(walkLength(lst3[]) == 5); +} + +unittest +{ + auto s = make!(SList!int)(1, 2, 3); +} + +unittest +{ + // 5193 + static struct Data + { + const int val; + } + SList!Data list; +} + +unittest +{ + auto s = SList!int([1, 2, 3]); + s.front = 5; //test frontAssign + assert(s.front == 5); + auto r = s[]; + r.front = 1; //test frontAssign + assert(r.front == 1); +} diff --git a/libphobos/src/std/container/util.d b/libphobos/src/std/container/util.d new file mode 100644 index 000000000..5c9e5a83f --- /dev/null +++ b/libphobos/src/std/container/util.d @@ -0,0 +1,85 @@ +module std.container.util; + +import std.algorithm; + +/** +Returns an initialized object. This function is mainly for eliminating +construction differences between structs and classes. It allows code to not +worry about whether the type it's constructing is a struct or a class. + +Examples: +-------------------- +auto arr = make!(Array!int)([4, 2, 3, 1]); +assert(equal(arr[], [4, 2, 3, 1])); + +auto rbt = make!(RedBlackTree!(int, "a > b"))([4, 2, 3, 1]); +assert(equal(rbt[], [4, 3, 2, 1])); + +alias makeList = make!(DList!int); +auto list = makeList([1, 7, 42]); +assert(equal(list[], [1, 7, 42])); +-------------------- + */ +template make(T) +if (is(T == struct) || is(T == class)) +{ + T make(Args...)(Args arguments) + if (is(T == struct) && __traits(compiles, T(arguments))) + { + return T(arguments); + } + + T make(Args...)(Args arguments) + if (is(T == class) && __traits(compiles, new T(arguments))) + { + return new T(arguments); + } +} + +//Verify Examples. +unittest +{ + import std.container; + + auto arr = make!(Array!int)([4, 2, 3, 1]); + assert(equal(arr[], [4, 2, 3, 1])); + + auto rbt = make!(RedBlackTree!(int, "a > b"))([4, 2, 3, 1]); + assert(equal(rbt[], [4, 3, 2, 1])); + + alias makeList = make!(DList!int); + auto list = makeList([1, 7, 42]); + assert(equal(list[], [1, 7, 42])); + + auto s = make!(SList!int)(1, 2, 3); + assert(equal(s[], [1, 2, 3])); +} + +unittest +{ + import std.container; + + auto arr1 = make!(Array!dchar)(); + assert(arr1.empty); + auto arr2 = make!(Array!dchar)("hello"d); + assert(equal(arr2[], "hello"d)); + + auto rtb1 = make!(RedBlackTree!dchar)(); + assert(rtb1.empty); + auto rtb2 = make!(RedBlackTree!dchar)('h', 'e', 'l', 'l', 'o'); + assert(equal(rtb2[], "ehlo"d)); +} + +// Issue 8895 +unittest +{ + import std.container; + + auto a = make!(DList!int)(1,2,3,4); + auto b = make!(DList!int)(1,2,3,4); + auto c = make!(DList!int)(1,2,3,5); + auto d = make!(DList!int)(1,2,3,4,5); + assert(a == b); // this better terminate! + assert(!(a == c)); + assert(!(a == d)); +} \ No newline at end of file diff --git a/libphobos/src/std/conv.d b/libphobos/src/std/conv.d index 708a14adf..8ce8ad6ee 100644 --- a/libphobos/src/std/conv.d +++ b/libphobos/src/std/conv.d @@ -19,10 +19,6 @@ Macros: WIKI = Phobos/StdConv */ - -/* NOTE: This file has been patched from the original DMD distribution to - work with the GDC compiler. - */ module std.conv; import core.stdc.string; @@ -31,8 +27,6 @@ import std.algorithm, std.array, std.ascii, std.exception, std.range, std.utf; import std.format; -//debug=conv; // uncomment to turn on debugging printf's - /* ************* Exceptions *************** */ /** @@ -295,7 +289,7 @@ template to(T) } // Tests for issue 6175 -@safe pure unittest +@safe pure nothrow unittest { char[9] sarr = "blablabla"; auto darr = to!(char[])(sarr); @@ -304,14 +298,14 @@ template to(T) } // Tests for issue 7348 -@safe pure unittest +@safe pure /+nothrow+/ unittest { assert(to!string(null) == "null"); assert(text(null) == "null"); } // Tests for issue 11390 -@safe pure unittest +@safe pure /+nothrow+/ unittest { const(typeof(null)) ctn; immutable(typeof(null)) itn; @@ -373,16 +367,15 @@ T toImpl(T, S)(S value) return value; } -@safe pure unittest +@safe pure nothrow unittest { enum E { a } // Issue 9523 - Allow identity enum conversion auto e = to!E(E.a); assert(e == E.a); } -@safe pure unittest +@safe pure nothrow unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); int a = 42; auto b = to!long(a); assert(a == b); @@ -394,7 +387,7 @@ T toImpl(T, S)(S value) // Conversion between same size foreach (S; TypeTuple!(byte, short, int, long)) { - alias Unsigned!S U; + alias U = Unsigned!S; foreach (Sint; TypeTuple!(S, const S, immutable S)) foreach (Uint; TypeTuple!(U, const U, immutable U)) @@ -415,8 +408,8 @@ T toImpl(T, S)(S value) foreach (i, S1; TypeTuple!(byte, short, int, long)) foreach ( S2; TypeTuple!(byte, short, int, long)[i+1..$]) { - alias Unsigned!S1 U1; - alias Unsigned!S2 U2; + alias U1 = Unsigned!S1; + alias U2 = Unsigned!S2; static assert(U1.sizeof < S2.sizeof); @@ -466,7 +459,7 @@ T toImpl(T, S)(ref S s) return toImpl!(T, typeof(s[0])[])(s); } -@safe pure unittest +@safe pure nothrow unittest { char[4] test = ['a', 'b', 'c', 'd']; static assert(!isInputRange!(Unqual!(char[4]))); @@ -479,14 +472,34 @@ When source type supports member template function opCast, is is used. T toImpl(T, S)(S value) if (!isImplicitlyConvertible!(S, T) && is(typeof(S.init.opCast!T()) : T) && - !isExactSomeString!T) + !isExactSomeString!T && + !is(typeof(T(value)))) { return value.opCast!T(); } @safe pure unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); + static struct Test + { + struct T + { + this(S s) @safe pure { } + } + struct S + { + T opCast(U)() @safe pure { assert(false); } + } + } + to!(Test.T)(Test.S()); + + // make sure std.conv.to is doing the same thing as initialization + Test.S s; + Test.T t = s; +} + +@safe pure unittest +{ class B { T opCast(T)() { return 43; } @@ -494,7 +507,6 @@ T toImpl(T, S)(S value) auto b = new B; assert(to!int(b) == 43); - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); struct S { T opCast(T)() { return 43; } @@ -518,7 +530,6 @@ T toImpl(T, S)(S value) // Bugzilla 3961 @safe pure unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); struct Int { int x; @@ -684,7 +695,6 @@ T toImpl(T, S)(S value) @safe pure unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); // Testing object conversions class A {} class B : A {} @@ -698,18 +708,18 @@ T toImpl(T, S)(S value) // Unittest for 6288 @safe pure unittest { - template Identity(T) { alias T Identity; } - template toConst(T) { alias const(T) toConst; } - template toShared(T) { alias shared(T) toShared; } - template toSharedConst(T) { alias shared(const(T)) toSharedConst; } - template toImmutable(T) { alias immutable(T) toImmutable; } + alias Identity(T) = T; + alias toConst(T) = const T; + alias toShared(T) = shared T; + alias toSharedConst(T) = shared const T; + alias toImmutable(T) = immutable T; template AddModifier(int n) if (0 <= n && n < 5) { - static if (n == 0) alias Identity AddModifier; - else static if (n == 1) alias toConst AddModifier; - else static if (n == 2) alias toShared AddModifier; - else static if (n == 3) alias toSharedConst AddModifier; - else static if (n == 4) alias toImmutable AddModifier; + static if (n == 0) alias AddModifier = Identity; + else static if (n == 1) alias AddModifier = toConst; + else static if (n == 2) alias AddModifier = toShared; + else static if (n == 3) alias AddModifier = toSharedConst; + else static if (n == 4) alias AddModifier = toImmutable; } interface I {} @@ -723,8 +733,8 @@ T toImpl(T, S)(S value) foreach (m1; TypeTuple!(0,1,2,3,4)) // enumerate modifiers foreach (m2; TypeTuple!(0,1,2,3,4)) // ditto { - alias AddModifier!m1 srcmod; - alias AddModifier!m2 tgtmod; + alias srcmod = AddModifier!m1; + alias tgtmod = AddModifier!m2; //pragma(msg, srcmod!Object, " -> ", tgtmod!Object, ", convertible = ", // isImplicitlyConvertible!(srcmod!Object, tgtmod!Object)); @@ -827,7 +837,7 @@ T toImpl(T, S)(S value) else static if (is(S == void[]) || is(S == const(void)[]) || is(S == immutable(void)[])) { // Converting void array to string - alias Unqual!(ElementEncodingType!T) Char; + alias Char = Unqual!(ElementEncodingType!T); auto raw = cast(const(ubyte)[]) value; enforce(raw.length % Char.sizeof == 0, new ConvException("Alignment mismatch in converting a " @@ -848,20 +858,20 @@ T toImpl(T, S)(S value) { switch(value) { - foreach (I, member; NoDuplicates!(EnumMembers!S)) + foreach (member; NoDuplicates!(EnumMembers!S)) { case member: - return to!T(enumRep!(immutable(T), S, I)); + return to!T(enumRep!(immutable(T), S, member)); } default: } } else { - foreach (I, member; EnumMembers!S) + foreach (member; EnumMembers!S) { if (value == member) - return to!T(enumRep!(immutable(T), S, I)); + return to!T(enumRep!(immutable(T), S, member)); } } @@ -906,10 +916,10 @@ unittest //Static representation of the index I of the enum S, //In representation T. //T must be an immutable string (avoids un-necessary initializations). -private template enumRep(T, S, size_t I) +private template enumRep(T, S, S value) if (is (T == immutable) && isExactSomeString!T && is(S == enum)) { - static T enumRep = to!T(__traits(allMembers, S)[I]); + static T enumRep = toStr!T(value); } @safe pure unittest @@ -917,18 +927,15 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum)) void dg() { // string to string conversion - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - - alias TypeTuple!(char, wchar, dchar) Chars; + alias Chars = TypeTuple!(char, wchar, dchar); foreach (LhsC; Chars) { - alias TypeTuple!(LhsC[], const(LhsC)[], immutable(LhsC)[]) LhStrings; + alias LhStrings = TypeTuple!(LhsC[], const(LhsC)[], immutable(LhsC)[]); foreach (Lhs; LhStrings) { foreach (RhsC; Chars) { - alias TypeTuple!(RhsC[], const(RhsC)[], immutable(RhsC)[]) - RhStrings; + alias RhStrings = TypeTuple!(RhsC[], const(RhsC)[], immutable(RhsC)[]); foreach (Rhs; RhStrings) { Lhs s1 = to!Lhs("wyda"); @@ -961,8 +968,6 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum)) @safe pure unittest { // Conversion reinterpreting void array to string - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - auto a = "abcx"w; const(void)[] b = a; assert(b.length == 8); @@ -971,21 +976,16 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum)) assert(c == "abcx"); } -@system pure unittest +@system pure nothrow unittest { // char* to string conversion - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - debug(conv) printf("string.to!string(char*).unittest\n"); - assert(to!string(cast(char*) null) == ""); assert(to!string("foo\0".ptr) == "foo"); } -@safe pure unittest +@safe pure /+nothrow+/ unittest { // Conversion representing bool value with string - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - bool b; assert(to!string(b) == "false"); b = true; @@ -995,12 +995,10 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum)) @safe pure unittest { // Conversion representing character value with string - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - - alias TypeTuple!( - char, const( char), immutable( char), - wchar, const(wchar), immutable(wchar), - dchar, const(dchar), immutable(dchar)) AllChars; + alias AllChars = + TypeTuple!( char, const( char), immutable( char), + wchar, const(wchar), immutable(wchar), + dchar, const(dchar), immutable(dchar)); foreach (Char1; AllChars) { foreach (Char2; AllChars) @@ -1022,35 +1020,28 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum)) assert(s2 == "foo"); } -@safe pure unittest +@safe pure nothrow unittest { // Conversion representing integer values with string foreach (Int; TypeTuple!(ubyte, ushort, uint, ulong)) { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - debug(conv) printf("string.to!string(%.*s).unittest\n", Int.stringof.length, Int.stringof.ptr); - - assert(to!string(to!Int(0)) == "0"); - assert(to!string(to!Int(9)) == "9"); - assert(to!string(to!Int(123)) == "123"); + assert(to!string(Int(0)) == "0"); + assert(to!string(Int(9)) == "9"); + assert(to!string(Int(123)) == "123"); } foreach (Int; TypeTuple!(byte, short, int, long)) { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - debug(conv) printf("string.to!string(%.*s).unittest\n", Int.stringof.length, Int.stringof.ptr); - - assert(to!string(to!Int(0)) == "0"); - assert(to!string(to!Int(9)) == "9"); - assert(to!string(to!Int(123)) == "123"); - assert(to!string(to!Int(-0)) == "0"); - assert(to!string(to!Int(-9)) == "-9"); - assert(to!string(to!Int(-123)) == "-123"); - assert(to!string(to!(const Int)(6)) == "6"); + assert(to!string(Int(0)) == "0"); + assert(to!string(Int(9)) == "9"); + assert(to!string(Int(123)) == "123"); + assert(to!string(Int(-0)) == "0"); + assert(to!string(Int(-9)) == "-9"); + assert(to!string(Int(-123)) == "-123"); + assert(to!string(const(Int)(6)) == "6"); } - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); assert(wtext(int.max) == "2147483647"w); assert(wtext(int.min) == "-2147483648"w); assert(to!string(0L) == "0"); @@ -1063,11 +1054,9 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum)) }); } -@safe pure unittest +@safe pure /+nothrow+/ unittest { // Conversion representing dynamic/static array with string - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - long[] b = [ 1, 3, 5 ]; auto s = to!string(b); assert(to!string(b) == "[1, 3, 5]", s); @@ -1088,8 +1077,6 @@ if (is (T == immutable) && isExactSomeString!T && is(S == enum)) unittest { // Conversion representing class object with string - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - class A { override string toString() const { return "an A"; } @@ -1109,8 +1096,6 @@ unittest unittest { // Conversion representing struct object with string - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - struct S1 { string toString() { return "wyda"; } @@ -1136,11 +1121,9 @@ unittest assert(to!string(s8080) == ""); } -unittest +/+nothrow+/ unittest { // Conversion representing enum value with string - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - enum EB : bool { a = true } enum EU : uint { a = 0, b = 1, c = 2 } // base type is unsigned enum EI : int { a = -1, b = 0, c = 1 } // base type is signed (bug 7909) @@ -1167,10 +1150,15 @@ unittest enum E { foo, - bar, doo = foo, // check duplicate switch statements + bar, } + //Test regression 12494 + assert(to!string(E.foo) == "foo"); + assert(to!string(E.doo) == "foo"); + assert(to!string(E.bar) == "bar"); + foreach (S; TypeTuple!(string, wstring, dstring, const(char[]), const(wchar[]), const(dchar[]))) { auto s1 = to!S(E.foo); @@ -1244,8 +1232,6 @@ body return cast(T)buffer[index .. $].dup; } - enforce(radix >= 2 && radix <= 36, new ConvException("Radix error")); - switch(radix) { case 10: @@ -1264,30 +1250,24 @@ body } } -@safe pure unittest +@safe pure nothrow unittest { foreach (Int; TypeTuple!(uint, ulong)) { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - debug(conv) printf("string.to!string(%.*s, uint).unittest\n", Int.stringof.length, Int.stringof.ptr); - - assert(to!string(to!Int(16), 16) == "10"); - assert(to!string(to!Int(15), 2u) == "1111"); - assert(to!string(to!Int(1), 2u) == "1"); - assert(to!string(to!Int(0x1234AF), 16u) == "1234AF"); - assert(to!string(to!Int(0x1234BCD), 16u, LetterCase.upper) == "1234BCD"); - assert(to!string(to!Int(0x1234AF), 16u, LetterCase.lower) == "1234af"); + assert(to!string(Int(16), 16) == "10"); + assert(to!string(Int(15), 2u) == "1111"); + assert(to!string(Int(1), 2u) == "1"); + assert(to!string(Int(0x1234AF), 16u) == "1234AF"); + assert(to!string(Int(0x1234BCD), 16u, LetterCase.upper) == "1234BCD"); + assert(to!string(Int(0x1234AF), 16u, LetterCase.lower) == "1234af"); } foreach (Int; TypeTuple!(int, long)) { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - debug(conv) printf("string.to!string(%.*s, uint).unittest\n", Int.stringof.length, Int.stringof.ptr); - - assert(to!string(to!Int(-10), 10u) == "-10"); + assert(to!string(Int(-10), 10u) == "-10"); } - assert(to!string(cast(byte)-10, 16) == "F6"); + assert(to!string(byte(-10), 16) == "F6"); assert(to!string(long.min) == "-9223372036854775808"); assert(to!string(long.max) == "9223372036854775807"); } @@ -1393,20 +1373,28 @@ T toImpl(T, S)(S value) { alias E = typeof(T.init[0]); - auto w = appender!(E[])(); - w.reserve(value.length); - foreach (i, ref e; value) + static if (isStaticArray!T) { - w.put(to!E(e)); + auto res = to!(E[])(value); + enforceEx!ConvException(T.length == res.length, + format("Length mismatch when converting to static array: %s vs %s", T.length, res.length)); + return res[0 .. T.length]; + } + else + { + auto w = appender!(E[])(); + w.reserve(value.length); + foreach (i, ref e; value) + { + w.put(to!E(e)); + } + return w.data; } - return w.data; } @safe pure unittest { // array to array conversions - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - uint[] a = ([ 1u, 2, 3 ]).dup; auto b = to!(float[])(a); assert(b == [ 1.0f, 2, 3 ]); @@ -1429,6 +1417,22 @@ T toImpl(T, S)(S value) alias wrap this; } Wrap[] warr = to!(Wrap[])(["foo", "bar"]); // should work + + // Issue 12633 + import std.conv : to; + const s2 = ["10", "20"]; + + immutable int[2] a3 = s2.to!(int[2]); + assert(a3 == [10, 20]); + + // verify length mismatches are caught + immutable s4 = [1, 2, 3, 4]; + foreach (i; [1, 4]) + { + auto ex = collectException(s4[0 .. i].to!(int[2])); + assert(ex && ex.msg == "Length mismatch when converting to static array: 2 vs " ~ [cast(char)(i + '0')], + ex ? ex.msg : "Exception was not thrown!"); + } } /*@safe pure */unittest { @@ -1448,8 +1452,8 @@ T toImpl(T, S)(S value) { /* This code is potentially unsafe. */ - alias KeyType!T K2; - alias ValueType!T V2; + alias K2 = KeyType!T; + alias V2 = ValueType!T; // While we are "building" the AA, we need to unqualify its values, and only re-qualify at the end Unqual!V2[K2] result; @@ -1479,7 +1483,6 @@ T toImpl(T, S)(S value) a = [null:["hello":int.max]]; assertThrown!ConvOverflowException(to!(short[wstring][string[double[]]])(a)); } -version(none) // masked by unexpected linker error in posix platforms unittest // Extra cases for AA with qualifiers conversion { int[][int[]] a;// = [[], []]; @@ -1560,8 +1563,6 @@ private void testFloatingToIntegral(Floating, Integral)() @safe pure unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - alias AllInts = TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong); alias AllFloats = TypeTuple!(float, double, real); alias AllNumerics = TypeTuple!(AllInts, AllFloats); @@ -1715,7 +1716,6 @@ T toImpl(T, S)(S value, uint radix) @safe pure unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); foreach (Str; TypeTuple!(string, wstring, dstring)) { Str a = "123"; @@ -1792,7 +1792,6 @@ template roundTo(Target) unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); assert(roundTo!int(3.14) == 3); assert(roundTo!int(3.49) == 3); assert(roundTo!int(3.5) == 4); @@ -1836,6 +1835,63 @@ unittest * -------------- */ +Target parse(Target, Source)(ref Source s) + if (isInputRange!Source && + !isExactSomeString!Source && + isSomeChar!(ElementType!Source) && + is(Unqual!Target == bool)) +{ + if (!s.empty) + { + auto c1 = std.ascii.toLower(s.front); + bool result = (c1 == 't'); + if (result || c1 == 'f') + { + s.popFront(); + foreach (c; result ? "rue" : "alse") + { + if (s.empty || std.ascii.toLower(s.front) != c) + goto Lerr; + s.popFront(); + } + return result; + } + } +Lerr: + throw parseError("bool should be case-insensitive 'true' or 'false'"); +} + +unittest +{ + struct InputString + { + string _s; + @property auto front() { return _s.front; } + @property bool empty() { return _s.empty; } + void popFront() { _s.popFront(); } + } + + auto s = InputString("trueFALSETrueFalsetRUEfALSE"); + assert(parse!bool(s) == true); + assert(s.equal("FALSETrueFalsetRUEfALSE")); + assert(parse!bool(s) == false); + assert(s.equal("TrueFalsetRUEfALSE")); + assert(parse!bool(s) == true); + assert(s.equal("FalsetRUEfALSE")); + assert(parse!bool(s) == false); + assert(s.equal("tRUEfALSE")); + assert(parse!bool(s) == true); + assert(s.equal("fALSE")); + assert(parse!bool(s) == false); + assert(s.empty); + + foreach (ss; ["tfalse", "ftrue", "t", "f", "tru", "fals", ""]) + { + s = InputString(ss); + assertThrown!ConvException(parse!bool(s)); + } +} + Target parse(Target, Source)(ref Source s) if (isSomeChar!(ElementType!Source) && isIntegral!Target && !is(Target == enum)) @@ -1845,69 +1901,76 @@ Target parse(Target, Source)(ref Source s) // smaller types are handled like integers auto v = .parse!(Select!(Target.min < 0, int, uint))(s); auto result = ()@trusted{ return cast(Target) v; }(); - if (result != v) - goto Loverflow; - return result; + if (result == v) + return result; + throw new ConvOverflowException("Overflow in integral conversion"); } else { // Larger than int types static if (Target.min < 0) - int sign = 0; + bool sign = 0; else - enum int sign = 0; - Target v = 0; - bool atStart = true; - enum char maxLastDigit = Target.min < 0 ? '7' : '5'; - while (!s.empty) + enum bool sign = 0; + + enum char maxLastDigit = Target.min < 0 ? 7 : 5; + Unqual!(typeof(s.front)) c; + + if (s.empty) + goto Lerr; + + c = s.front; + s.popFront(); + static if (Target.min < 0) { - immutable c = s.front; - if (c >= '0' && c <= '9') + switch (c) { - if (v >= Target.max/10 && - (v != Target.max/10 || c + sign > maxLastDigit)) - goto Loverflow; - v = cast(Target) (v * 10 + (c - '0')); - s.popFront(); - atStart = false; + case '-': + sign = true; + goto case '+'; + case '+': + if (s.empty) + goto Lerr; + c = s.front; + s.popFront(); + break; + + default: + break; } - else static if (Target.min < 0) + } + c -= '0'; + if (c <= 9) + { + Target v = cast(Target)c; + while (!s.empty) { - if (c == '-' && atStart) + c = s.front - '0'; + if (c > 9) + break; + + if (v < Target.max/10 || + (v == Target.max/10 && c <= maxLastDigit + sign)) { + v = cast(Target) (v * 10 + c); s.popFront(); - sign = -1; } - else if (c == '+' && atStart) - s.popFront(); else - break; + throw new ConvOverflowException("Overflow in integral conversion"); } - else - break; - } - if (atStart) - goto Lerr; - static if (Target.min < 0) - { - if (sign == -1) - { + + if (sign) v = -v; - } + return v; } - return v; - } - -Loverflow: - throw new ConvOverflowException("Overflow in integral conversion"); Lerr: - throw convError!(Source, Target)(s); + throw convError!(Source, Target)(s); + } } @safe pure unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); string s = "123"; auto a = parse!int(s); } @@ -1916,11 +1979,8 @@ Lerr: { foreach (Int; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong)) { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - debug(conv) printf("conv.to!%.*s.unittest\n", Int.stringof.length, Int.stringof.ptr); - { - assert(to!Int("0") == 0); + assert(to!Int("0") == 0); static if (isSigned!Int) { @@ -2014,9 +2074,6 @@ Lerr: // parsing error check foreach (Int; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong)) { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - debug(conv) printf("conv.to!%.*s.unittest (error)\n", Int.stringof.length, Int.stringof.ptr); - { immutable string[] errors1 = [ @@ -2031,6 +2088,10 @@ Lerr: "1-", "xx", "123h", + "-+1", + "--1", + "+-1", + "++1", ]; foreach (j, s; errors1) assertThrown!ConvException(to!Int(s)); @@ -2052,9 +2113,6 @@ Lerr: // positive overflow check foreach (i, Int; TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong)) { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - debug(conv) printf("conv.to!%.*s.unittest (pos overflow)\n", Int.stringof.length, Int.stringof.ptr); - immutable string[] errors = [ "128", // > byte.max @@ -2073,9 +2131,6 @@ Lerr: // negative overflow check foreach (i, Int; TypeTuple!(byte, short, int, long)) { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - debug(conv) printf("conv.to!%.*s.unittest (neg overflow)\n", Int.stringof.length, Int.stringof.ptr); - immutable string[] errors = [ "-129", // < byte.min @@ -2151,7 +2206,6 @@ Lerr: @safe pure unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); // @@@BUG@@@ the size of China // foreach (i; 2..37) // { @@ -2222,8 +2276,6 @@ Target parse(Target, Source)(ref Source s) unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - enum EB : bool { a = true, b = false, c = a } enum EU { a, b, c } enum EI { a = -1, b = 0, c = 1 } @@ -2293,12 +2345,16 @@ Target parse(Target, Source)(ref Source p) case 'i': case 'I': p.popFront(); enforce(!p.empty, bailOut()); - if (std.ascii.toLower(p.front) == 'n' && - (p.popFront(), enforce(!p.empty, bailOut()), std.ascii.toLower(p.front) == 'f')) + if (std.ascii.toLower(p.front) == 'n') { - // 'inf' p.popFront(); - return sign ? -Target.infinity : Target.infinity; + enforce(!p.empty, bailOut()); + if (std.ascii.toLower(p.front) == 'f') + { + // 'inf' + p.popFront(); + return sign ? -Target.infinity : Target.infinity; + } } goto default; default: {} @@ -2376,7 +2432,8 @@ Target parse(Target, Source)(ref Source p) } } if (i == '.' && !dot) - { p.popFront(); + { + p.popFront(); dot = 4; } else @@ -2388,7 +2445,8 @@ Target parse(Target, Source)(ref Source p) { msdec++; if (msdec == 0) // overflow - { msdec = 0x8000000000000000L; + { + msdec = 0x8000000000000000L; exp++; } } @@ -2404,10 +2462,11 @@ Target parse(Target, Source)(ref Source p) if (!p.empty) { switch (p.front) - { case '-': sexp++; + { + case '-': sexp++; goto case; case '+': p.popFront(); enforce(!p.empty, - new ConvException("Error converting input" + new ConvException("Error converting input"~ " to floating point")); break; default: {} @@ -2425,7 +2484,7 @@ Target parse(Target, Source)(ref Source p) ndigits = 1; } exp += (sexp) ? -e : e; - enforce(ndigits, new ConvException("Error converting input" + enforce(ndigits, new ConvException("Error converting input"~ " to floating point")); static if (real.mant_dig == 64) @@ -2436,7 +2495,8 @@ Target parse(Target, Source)(ref Source p) // left justify mantissa while (msdec >= 0) - { msdec <<= 1; + { + msdec <<= 1; e2--; } @@ -2521,8 +2581,11 @@ Target parse(Target, Source)(ref Source p) if (std.ascii.toUpper(p.front) == 'N' && !startsWithZero) { // nan - enforce((p.popFront(), !p.empty && std.ascii.toUpper(p.front) == 'A') - && (p.popFront(), !p.empty && std.ascii.toUpper(p.front) == 'N'), + p.popFront(); + enforce(!p.empty && std.ascii.toUpper(p.front) == 'A', + new ConvException("error converting input to floating point")); + p.popFront(); + enforce(!p.empty && std.ascii.toUpper(p.front) == 'N', new ConvException("error converting input to floating point")); // skip past the last 'n' p.popFront(); @@ -2540,7 +2603,8 @@ Target parse(Target, Source)(ref Source p) if (msdec < (0x7FFFFFFFFFFFL-10)/10) msdec = msdec * 10 + (i - '0'); else if (msscale < (0xFFFFFFFF-10)/10) - { lsdec = lsdec * 10 + (i - '0'); + { + lsdec = lsdec * 10 + (i - '0'); msscale *= 10; } else @@ -2581,7 +2645,8 @@ Target parse(Target, Source)(ref Source p) p.popFront(); enforce(!p.empty, new ConvException("Unexpected end of input")); switch (p.front) - { case '-': sexp++; + { + case '-': sexp++; goto case; case '+': p.popFront(); break; @@ -2666,9 +2731,6 @@ unittest foreach (Float; TypeTuple!(float, double, real)) { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - debug(conv) printf("conv.to!%.*s.unittest\n", Float.stringof.length, Float.stringof.ptr); - assert(to!Float("123") == Literal!Float(123)); assert(to!Float("+123") == Literal!Float(+123)); assert(to!Float("-123") == Literal!Float(-123)); @@ -2790,7 +2852,6 @@ unittest import core.stdc.stdlib; errno = 0; // In case it was set by another unittest in a different module. - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); struct longdouble { static if(real.mant_dig == 64) @@ -2822,7 +2883,10 @@ unittest ld = parse!real(s2); assert(s2.empty); x = *cast(longdouble *)&ld; - ld1 = strtold(s.ptr, null); + version (Win64) + ld1 = 0x1.FFFFFFFFFFFFFFFEp-16382L; // strtold currently mapped to strtod + else + ld1 = strtold(s.ptr, null); x1 = *cast(longdouble *)&ld1; assert(x1 == x && ld1 == ld); @@ -2958,9 +3022,6 @@ Target parse(Target, Source)(ref Source s) */ @safe pure unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); - debug(conv) printf("conv.to!bool.unittest\n"); - assert (to!bool("TruE") == true); assert (to!bool("faLse"d) == false); assertThrown!ConvException(to!bool("maybe")); @@ -2997,7 +3058,7 @@ Target parse(Target, Source)(ref Source s) @safe pure unittest { - alias typeof(null) NullType; + alias NullType = typeof(null); auto s1 = "null"; assert(parse!NullType(s1) is null); assert(s1 == ""); @@ -3512,7 +3573,7 @@ The $(D octal) facility is intended as an experimental facility to replace _octal literals starting with $(D '0'), which many find confusing. Using $(D octal!177) or $(D octal!"177") instead of $(D 0177) as an _octal literal makes code clearer and the intent more -visible. If use of this facility becomes preponderent, a future +visible. If use of this facility becomes predominant, a future version of the language may deem old-style _octal literals deprecated. The rules for strings are the usual for literals: If it can fit in an @@ -3695,7 +3756,6 @@ template isOctalLiteral(string num) unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); // ensure that you get the right types, even with embedded underscores auto w = octal!"100_000_000_000"; static assert(!is(typeof(w) == int)); @@ -3750,6 +3810,197 @@ unittest static assert(__traits(compiles, b = octal!1L)); } +/+ +emplaceRef is a package function for phobos internal use. It works like +emplace, but takes its argument by ref (as opposed to "by pointer"). + +This makes it easier to use, easier to be safe, and faster in a non-inline +build. + +Furthermore, emplaceRef takes a type paremeter, which specifies the type we +want to build. This helps to build qualified objects on mutable buffer, +without breaking the type system with unsafe casts. ++/ +package template emplaceRef(T) +{ + alias UT = Unqual!T; + + ref UT emplaceRef()(ref UT chunk) + { + static assert (is(typeof({static T i;})), + format("Cannot emplace a %1$s because %1$s.this() is annotated with @disable.", T.stringof)); + + return emplaceInitializer(chunk); + } + + static if (!is(T == struct)) + ref UT emplaceRef(Arg)(ref UT chunk, auto ref Arg arg) + { + static assert(is(typeof({T t = arg;})), + format("%s cannot be emplaced from a %s.", T.stringof, Arg.stringof)); + + static if (isStaticArray!T) + { + alias UArg = Unqual!Arg; + alias E = ElementEncodingType!(typeof(T.init[])); + alias UE = Unqual!E; + enum N = T.length; + + static if (is(Arg : T)) + { + //Matching static array + static if (!hasElaborateAssign!UT && isAssignable!(UT, Arg)) + chunk = arg; + else static if (is(UArg == UT)) + { + memcpy(&chunk, &arg, T.sizeof); + static if (hasElaborateCopyConstructor!T) + typeid(T).postblit(cast(void*)&chunk); + } + else + .emplaceRef!T(chunk, cast(T)arg); + } + else static if (is(Arg : E[])) + { + //Matching dynamic array + static if (!hasElaborateAssign!UT && is(typeof(chunk[] = arg[]))) + chunk[] = arg[]; + else static if (is(Unqual!(ElementEncodingType!Arg) == UE)) + { + assert(N == chunk.length, "Array length missmatch in emplace"); + memcpy(cast(void*)&chunk, arg.ptr, T.sizeof); + static if (hasElaborateCopyConstructor!T) + typeid(T).postblit(cast(void*)&chunk); + } + else + .emplaceRef!T(chunk, cast(E[])arg); + } + else static if (is(Arg : E)) + { + //Case matching single element to array. + static if (!hasElaborateAssign!UT && is(typeof(chunk[] = arg))) + chunk[] = arg; + else static if (is(UArg == Unqual!E)) + { + //Note: We copy everything, and then postblit just once. + //This is as exception safe as what druntime can provide us. + foreach(i; 0 .. N) + memcpy(cast(void*)&(chunk[i]), &arg, E.sizeof); + static if (hasElaborateCopyConstructor!T) + typeid(T).postblit(cast(void*)&chunk); + } + else + //Alias this. Coerce. + .emplaceRef!T(chunk, cast(E)arg); + } + else static if (is(typeof(.emplaceRef!E(chunk[0], arg)))) + { + //Final case for everything else: + //Types that don't match (int to uint[2]) + //Recursion for multidimensions + static if (!hasElaborateAssign!UT && is(typeof(chunk[] = arg))) + chunk[] = arg; + else + foreach(i; 0 .. N) + .emplaceRef!E(chunk[i], arg); + } + else + static assert(0, format("Sorry, this implementation doesn't know how to emplace a %s with a %s", T.stringof, Arg.stringof)); + + return chunk; + } + else + { + chunk = arg; + return chunk; + } + } + // ditto + static if (is(T == struct)) + ref UT emplaceRef(Args...)(ref UT chunk, auto ref Args args) + { + static if (Args.length == 1 && is(Args[0] : T) && + is (typeof({T t = args[0];})) //Check for legal postblit + ) + { + static if (is(Unqual!T == Unqual!(Args[0]))) + { + //Types match exactly: we postblit + static if (!hasElaborateAssign!UT && isAssignable!(UT, T)) + chunk = args[0]; + else + { + memcpy(&chunk, &args[0], T.sizeof); + static if (hasElaborateCopyConstructor!T) + typeid(T).postblit(&chunk); + } + } + else + //Alias this. Coerce to type T. + .emplaceRef!T(chunk, cast(T)args[0]); + } + else static if (is(typeof(chunk.__ctor(args)))) + { + // T defines a genuine constructor accepting args + // Go the classic route: write .init first, then call ctor + emplaceInitializer(chunk); + chunk.__ctor(args); + } + else static if (is(typeof(T.opCall(args)))) + { + //Can be built calling opCall + emplaceOpCaller(chunk, args); //emplaceOpCaller is deprecated + } + else static if (is(typeof(T(args)))) + { + // Struct without constructor that has one matching field for + // each argument. Individually emplace each field + emplaceInitializer(chunk); + foreach (i, ref field; chunk.tupleof[0 .. Args.length]) + { + alias Field = typeof(field); + alias UField = Unqual!Field; + static if (is(Field == UField)) + .emplaceRef!Field(field, args[i]); + else + .emplaceRef!Field(*cast(Unqual!Field*)&field, args[i]); + } + } + else + { + //We can't emplace. Try to diagnose a disabled postblit. + static assert(!(Args.length == 1 && is(Args[0] : T)), + format("Cannot emplace a %1$s because %1$s.this(this) is annotated with @disable.", T.stringof)); + + //We can't emplace. + static assert(false, + format("%s cannot be emplaced from %s.", T.stringof, Args[].stringof)); + } + + return chunk; + } +} +//emplace helper functions +private ref T emplaceInitializer(T)(ref T chunk) @trusted pure nothrow +{ + static if (!hasElaborateAssign!T && isAssignable!T) + chunk = T.init; + else + { + static immutable T init = T.init; + memcpy(&chunk, &init, T.sizeof); + } + return chunk; +} +private deprecated("Using static opCall for emplace is deprecated. Plase use emplace(chunk, T(args)) instead.") +ref T emplaceOpCaller(T, Args...)(ref T chunk, auto ref Args args) +{ + static assert (is(typeof({T t = T.opCall(args);})), + format("%s.opCall does not return adequate data for construction.", T.stringof)); + return emplaceRef!T(chunk, chunk.opCall(args)); +} + + // emplace /** Given a pointer $(D chunk) to uninitialized memory (but already typed @@ -3759,15 +4010,35 @@ address. Returns: A pointer to the newly constructed object (which is the same as $(D chunk)). */ -T* emplace(T)(T* chunk) @safe nothrow pure +T* emplace(T)(T* chunk) @safe pure nothrow { - static assert (is(T* : void*), - format("Cannot emplace a %s because it is qualified.", T.stringof)); + emplaceRef!T(*chunk); + return chunk; +} + +/** +Given a pointer $(D chunk) to uninitialized memory (but already typed +as a non-class type $(D T)), constructs an object of type $(D T) at +that address from arguments $(D args). - static assert (is(typeof({static T i;})), - format("Cannot emplace a %1$s because %1$s.this() is annotated with @disable.", T.stringof)); +This function can be $(D @trusted) if the corresponding constructor of +$(D T) is $(D @safe). - return emplaceInitializer(chunk); +Returns: A pointer to the newly constructed object (which is the same +as $(D chunk)). + */ +T* emplace(T, Args...)(T* chunk, auto ref Args args) +if (!is(T == struct) && Args.length == 1) +{ + emplaceRef!T(*chunk, args); + return chunk; +} +/// ditto +T* emplace(T, Args...)(T* chunk, auto ref Args args) +if (is(T == struct)) +{ + emplaceRef!T(*chunk, args); + return chunk; } version(unittest) private struct __conv_EmplaceTest @@ -3891,108 +4162,10 @@ unittest emplace(&ss2, ss1); } -/** -Given a pointer $(D chunk) to uninitialized memory (but already typed -as a non-class type $(D T)), constructs an object of type $(D T) at -that address from arguments $(D args). - -This function can be $(D @trusted) if the corresponding constructor of -$(D T) is $(D @safe). - -Returns: A pointer to the newly constructed object (which is the same -as $(D chunk)). - */ -T* emplace(T, Args...)(T* chunk, auto ref Args args) - if (!is(T == struct) && Args.length == 1) -{ - alias Arg = Args[0]; - alias arg = args[0]; - - static assert (is(T* : void*), - format("Cannot emplace a %s because it is qualified.", T.stringof)); - - static assert(is(typeof({T t = args[0];})), - format("%s cannot be emplaced from a %s.", T.stringof, Arg.stringof)); - - static if (isStaticArray!T) - { - alias UArg = Unqual!Arg; - alias E = typeof(chunk.ptr[0]); - enum N = T.length; - - static if (is(Arg : T)) - { - //Matching static array - static if (isAssignable!(T, Arg) && !hasElaborateAssign!T) - *chunk = arg; - else static if (is(UArg == T)) - { - memcpy(chunk, &arg, T.sizeof); - static if (hasElaborateCopyConstructor!T) - typeid(T).postblit(cast(void*)&chunk); - } - else - emplace(chunk, cast(T)arg); - } - else static if (is(Arg : E[])) - { - //Matching dynamic array - static if (is(typeof((*chunk)[] = arg[])) && !hasElaborateAssign!T) - (*chunk)[] = arg[]; - else static if (is(UArg == E[])) - { - assert(N == chunk.length, "Array length missmatch in emplace"); - memcpy(cast(void*)chunk, arg.ptr, T.sizeof); - static if (hasElaborateCopyConstructor!T) - typeid(T).postblit(cast(void*)&chunk); - } - else - emplace(chunk, cast(E[])arg); - } - else static if (is(Arg : E)) - { - //Case matching single element to array. - static if (is(typeof((*chunk)[] = arg)) && !hasElaborateAssign!T) - (*chunk)[] = arg; - else static if (is(UArg == E)) - { - //Note: We copy everything, and then postblit just once. - //This is as exception safe as what druntime can provide us. - foreach(i; 0 .. N) - memcpy(cast(void*)(chunk.ptr + i), &arg, E.sizeof); - static if (hasElaborateCopyConstructor!T) - typeid(T).postblit(chunk); - } - else - //Alias this. Coerce. - emplace(chunk, cast(E)arg); - } - else static if (is(typeof(emplace(chunk.ptr, arg)))) - { - //Final case for everything else: - //Types that don't match (int to uint[2]) - //Recursion for multidimensions - static if (is(typeof((*chunk)[] = arg)) && !hasElaborateAssign!T) - (*chunk)[] = arg; - - foreach(i; 0 .. N) - emplace(chunk.ptr + i, arg); - } - else - static assert(0, format("Sorry, this implementation doesn't know how to emplace a %s with a %s", T.stringof, Arg.stringof)); - - return chunk; - } - else - { - *chunk = arg; - return chunk; - } -} +//Start testing emplace-args here unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); int a; int b = 42; assert(*emplace!int(&a, b) == 42); @@ -4027,105 +4200,11 @@ unittest assert(sa[0].i == 5 && sa[1].i == 5); } -/// ditto -T* emplace(T, Args...)(T* chunk, auto ref Args args) - if (is(T == struct)) -{ - static assert (is(T* : void*), - format("Cannot emplace a %s because it is qualified.", T.stringof)); - - static if (Args.length == 1 && is(Args[0] : T) && - is (typeof({T t = args[0];})) //Check for legal postblit - ) - { - static if (is(T == Unqual!(Args[0]))) - { - //Types match exactly: we postblit - static if (isAssignable!T && !hasElaborateAssign!T) - *chunk = args[0]; - else - { - memcpy(chunk, &args[0], T.sizeof); - static if (hasElaborateCopyConstructor!T) - typeid(T).postblit(chunk); - } - } - else - //Alias this. Coerce to type T. - emplace(chunk, cast(T)args[0]); - } - else static if (is(typeof(chunk.__ctor(args)))) - { - // T defines a genuine constructor accepting args - // Go the classic route: write .init first, then call ctor - emplaceInitializer(chunk); - chunk.__ctor(args); - } - else static if (is(typeof(T.opCall(args)))) - { - //Can be built calling opCall - emplaceOpCaller(chunk, args); //emplaceOpCaller is deprecated - } - else static if (is(typeof(T(args)))) - { - // Struct without constructor that has one matching field for - // each argument. Individually emplace each field - emplaceInitializer(chunk); - foreach (i, ref field; chunk.tupleof[0 .. Args.length]) - emplace(emplaceGetAddr(field), args[i]); - } - else - { - //We can't emplace. Try to diagnose a disabled postblit. - static assert(!(Args.length == 1 && is(Args[0] : T)), - format("Cannot emplace a %1$s because %1$s.this(this) is annotated with @disable.", T.stringof)); - - //We can't emplace. - static assert(false, - format("%s cannot be emplaced from %s.", T.stringof, Args[].stringof)); - } - - return chunk; -} - -//emplace helper functions -private T* emplaceInitializer(T)(T* chunk) @trusted pure nothrow -{ - static if (isAssignable!T && !hasElaborateAssign!T) - *chunk = T.init; - else - { - static immutable T init = T.init; - memcpy(chunk, &init, T.sizeof); - } - return chunk; -} -private deprecated("Using static opCall for emplace is deprecated. Plase use emplace(chunk, T(args)) instead.") -T* emplaceOpCaller(T, Args...)(T* chunk, auto ref Args args) -{ - static assert (is(typeof({T t = T.opCall(args);})), - format("%s.opCall does not return adequate data for construction.", T.stringof)); - return emplace(chunk, chunk.opCall(args)); -} -private -{ - //Helper to keep simple aggregate emplace safe. - auto emplaceGetAddr(T)(ref T t) @trusted - if (is(T == Unqual!T)) - { - return &t; - } - auto emplaceGetAddr(T)(ref T t) - if (!is(T == Unqual!T)) - { - return cast(Unqual!T*)&t; - } -} +//Start testing emplace-struct here // Test constructor branch unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); struct S { double x = 5, y = 6; @@ -4600,7 +4679,7 @@ unittest unittest //@@@9559@@@ { - alias Nullable!int I; + alias I = Nullable!int; auto ints = [0, 1, 2].map!(i => i & 1 ? I.init : I(i))(); auto asArray = std.array.array(ints); } @@ -4747,6 +4826,32 @@ unittest emplace(&sss, s); } +unittest //Constness +{ + import std.stdio; + + int a = void; + emplaceRef!(const int)(a, 5); + + immutable i = 5; + const(int)* p = void; + emplaceRef!(const int*)(p, &i); + + struct S + { + int* p; + } + alias IS = immutable(S); + S s = void; + emplaceRef!IS(s, IS()); + S[2] ss = void; + emplaceRef!(IS[2])(ss, IS()); + + IS[2] iss = IS.init; + emplaceRef!(IS[2])(ss, iss); + emplaceRef!(IS[2])(ss, iss[]); +} + private void testEmplaceChunk(void[] chunk, size_t typeSize, size_t typeAlignment, string typeName) { enforceEx!ConvException(chunk.length >= typeSize, @@ -4847,7 +4952,6 @@ unittest unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); class A { int x = 5; @@ -4876,7 +4980,6 @@ unittest unittest { - debug(conv) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); // Check fix for http://d.puremagic.com/issues/show_bug.cgi?id=2971 assert(equal(map!(to!int)(["42", "34", "345"]), [42, 34, 345])); } diff --git a/libphobos/src/std/csv.d b/libphobos/src/std/csv.d index 45744118f..da4a032a6 100644 --- a/libphobos/src/std/csv.d +++ b/libphobos/src/std/csv.d @@ -396,7 +396,7 @@ auto csvReader(Contents = string, // Test standard iteration over input. unittest { - string str = `one,"two ""quoted"""` ~ "\n\"three\nnew line\",""\nfive,six"; + string str = `one,"two ""quoted"""` ~ "\n\"three\nnew line\",\nfive,six"; auto records = csvReader(str); int count; @@ -700,7 +700,7 @@ unittest return text[0]; } } - auto ir = InputRange("Name,Occupation,Salary\r"d + auto ir = InputRange("Name,Occupation,Salary\r"d~ "Joe,Carpenter,300000\nFred,Blacksmith,400000\r\n"d); foreach(record; csvReader(ir, cast(string[])null)) @@ -1178,7 +1178,7 @@ public: if(_input.rowLength != 0) if(_input.col != _input.rowLength) throw new CSVException( - format("Row %s's length %s does not match " + format("Row %s's length %s does not match "~ "previous length of %s.", _input.row, _input.col, _input.rowLength)); return; @@ -1187,7 +1187,7 @@ public: if(_input.rowLength != 0) if(_input.col > _input.rowLength) throw new CSVException( - format("Row %s's length %s does not match " + format("Row %s's length %s does not match "~ "previous length of %s.", _input.row, _input.col, _input.rowLength)); } diff --git a/libphobos/src/std/datetime.d b/libphobos/src/std/datetime.d index bf59ef1c6..1b079a4d3 100644 --- a/libphobos/src/std/datetime.d +++ b/libphobos/src/std/datetime.d @@ -141,9 +141,6 @@ else version(Posix) import core.sys.posix.sys.time; } -//Comment this out to disable std.datetime's unit tests. -version = testStdDateTime; - version(unittest) { import std.c.string; @@ -156,13 +153,13 @@ version(unittest) //highly unlikely to conflict with anything that anyone else is doing. private alias std.string.indexOf stds_indexOf; -version(testStdDateTime) unittest +unittest { initializeTests(); } //Verify module example. -version(testStdDateTime) unittest +unittest { auto currentTime = Clock.currTime(); auto timeString = currentTime.toISOExtString(); @@ -170,7 +167,7 @@ version(testStdDateTime) unittest } //Verify Examples for core.time.Duration which couldn't be in core.time. -version(testStdDateTime) unittest +unittest { assert(std.datetime.Date(2010, 9, 7) + dur!"days"(5) == std.datetime.Date(2010, 9, 12)); @@ -179,12 +176,6 @@ version(testStdDateTime) unittest dur!"days"(-26)); } -//Note: There various functions which void as their return type and ref of the -// struct type which they're in as a commented out return type. Ideally, -// they would return the ref, but there are several dmd bugs which prevent -// that, relating to both ref and invariants. So, I've left the ref return -// types commented out with the idea that those functions can be made to -// return a ref to this once those bugs have been fixed. //============================================================================== // Section with public enums and constants. @@ -317,56 +308,6 @@ immutable string[] timeStrings = ["hnsecs", "usecs", "msecs", "seconds", "minute "hours", "days", "weeks", "months", "years"]; -//============================================================================== -// Section with private constants. -//============================================================================== - -/++ - Array of integers representing the last days of each month in a year. - +/ -private immutable int[13] lastDayNonLeap = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; - -/++ - Array of integers representing the last days of each month in a leap year. - +/ -private immutable int[13] lastDayLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]; - -/++ - Array of the long names of each month. - +/ -private immutable string[12] longMonthNames = [ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December" -]; - -/++ - Array of the short (three letter) names of each month. - +/ -private immutable string[12] shortMonthNames = [ - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec" -]; - //============================================================================== // Section with other types. //============================================================================== @@ -393,12 +334,12 @@ public: $(XREF exception, ErrnoException) (on Posix) or $(XREF exception, Exception) (on Windows) if it fails to get the time of day. +/ - static SysTime currTime(immutable TimeZone tz = LocalTime()) + static SysTime currTime(immutable TimeZone tz = LocalTime()) @safe { return SysTime(currStdTime, tz); } - version(testStdDateTime) unittest + unittest { assert(currTime(UTC()).timezone is UTC()); @@ -417,8 +358,8 @@ public: immutable unixTimeC = core.stdc.time.time(null); immutable diff = unixTimeC - unixTimeD; - _assertPred!">="(diff, -2); - _assertPred!"<="(diff, 2); + assert(diff >= -2); + assert(diff <= 2); } } @@ -429,8 +370,7 @@ public: Throws: $(LREF DateTimeException) if it fails to get the time. +/ - @trusted - static @property long currStdTime() + static @property long currStdTime() @trusted { version(Windows) { @@ -484,13 +424,12 @@ public: Throws: $(LREF DateTimeException) if it fails to get the time. +/ - @safe - static @property TickDuration currSystemTick() + static @property TickDuration currSystemTick() @safe { return TickDuration.currSystemTick; } - version(testStdDateTime) unittest + unittest { assert(Clock.currSystemTick.length > 0); } @@ -510,13 +449,12 @@ public: Throws: $(LREF DateTimeException) if it fails to get the time. +/ - @safe - static @property TickDuration currAppTick() + static @property TickDuration currAppTick() @safe { return currSystemTick - TickDuration.appOrigin; } - version(testStdDateTime) unittest + unittest { auto a = Clock.currSystemTick; auto b = Clock.currAppTick; @@ -589,7 +527,7 @@ public: $(LREF LocalTime) will be used. The given $(LREF DateTime) is assumed to be in the given time zone. +/ - this(in DateTime dateTime, immutable TimeZone tz = null) nothrow + this(in DateTime dateTime, immutable TimeZone tz = null) @safe nothrow { try this(dateTime, FracSec.from!"hnsecs"(0), tz); @@ -597,12 +535,12 @@ public: assert(0, "FracSec's constructor threw when it shouldn't have."); } - version(testStdDateTime) unittest + unittest { static void test(DateTime dt, immutable TimeZone tz, long expected) { auto sysTime = SysTime(dt, tz); - _assertPred!"=="(sysTime._stdTime, expected); + assert(sysTime._stdTime == expected); assert(sysTime._timezone is (tz is null ? LocalTime() : tz), format("Given DateTime: %s", dt)); } @@ -632,7 +570,7 @@ public: Throws: $(LREF DateTimeException) if $(D fracSec) is negative. +/ - this(in DateTime dateTime, in FracSec fracSec, immutable TimeZone tz = null) + this(in DateTime dateTime, in FracSec fracSec, immutable TimeZone tz = null) @safe { immutable fracHNSecs = fracSec.hnsecs; enforce(fracHNSecs >= 0, new DateTimeException("A SysTime cannot have negative fractional seconds.")); @@ -649,13 +587,10 @@ public: this(standardTime, _timezone); } catch(Exception e) - { - assert(0, "Date, TimeOfDay, or DateTime's constructor threw when " ~ - "it shouldn't have."); - } + assert(0, "Date, TimeOfDay, or DateTime's constructor threw when it shouldn't have."); } - version(testStdDateTime) unittest + unittest { static void test(DateTime dt, FracSec fracSec, @@ -663,7 +598,7 @@ public: long expected) { auto sysTime = SysTime(dt, fracSec, tz); - _assertPred!"=="(sysTime._stdTime, expected); + assert(sysTime._stdTime == expected); assert(sysTime._timezone is (tz is null ? LocalTime() : tz), format("Given DateTime: %s, Given FracSec: %s", dt, fracSec)); } @@ -690,7 +625,7 @@ public: $(LREF LocalTime) will be used. The given $(LREF Date) is assumed to be in the given time zone. +/ - this(in Date date, immutable TimeZone tz = null) nothrow + this(in Date date, immutable TimeZone tz = null) @safe nothrow { _timezone = tz is null ? LocalTime() : tz; @@ -705,12 +640,12 @@ public: assert(0, "Date's constructor through when it shouldn't have."); } - version(testStdDateTime) unittest + unittest { static void test(Date d, immutable TimeZone tz, long expected) { auto sysTime = SysTime(d, tz); - _assertPred!"=="(sysTime._stdTime, expected); + assert(sysTime._stdTime == expected); assert(sysTime._timezone is (tz is null ? LocalTime() : tz), format("Given Date: %s", d)); } @@ -737,18 +672,18 @@ public: tz = The $(LREF2 .TimeZone, TimeZone) to use for this $(LREF SysTime). If null, $(LREF LocalTime) will be used. +/ - this(long stdTime, immutable TimeZone tz = null) pure nothrow + this(long stdTime, immutable TimeZone tz = null) @safe pure nothrow { _stdTime = stdTime; _timezone = tz is null ? LocalTime() : tz; } - version(testStdDateTime) unittest + unittest { static void test(long stdTime, immutable TimeZone tz) { auto sysTime = SysTime(stdTime, tz); - _assertPred!"=="(sysTime._stdTime, stdTime); + assert(sysTime._stdTime == stdTime); assert(sysTime._timezone is (tz is null ? LocalTime() : tz), format("Given stdTime: %s", stdTime)); } @@ -764,7 +699,7 @@ public: Params: rhs = The $(LREF SysTime) to assign to this one. +/ - ref SysTime opAssign(const ref SysTime rhs) pure nothrow + ref SysTime opAssign(const ref SysTime rhs) @safe pure nothrow { _stdTime = rhs._stdTime; _timezone = rhs._timezone; @@ -776,7 +711,7 @@ public: Params: rhs = The $(LREF SysTime) to assign to this one. +/ - ref SysTime opAssign(SysTime rhs) pure nothrow + ref SysTime opAssign(SysTime rhs) @safe pure nothrow { _stdTime = rhs._stdTime; _timezone = rhs._timezone; @@ -791,23 +726,23 @@ public: Note that the time zone is ignored. Only the internal std times (which are in UTC) are compared. +/ - bool opEquals(const SysTime rhs) const pure nothrow + bool opEquals(const SysTime rhs) @safe const pure nothrow { return opEquals(rhs); } /// ditto - bool opEquals(const ref SysTime rhs) const pure nothrow + bool opEquals(const ref SysTime rhs) @safe const pure nothrow { return _stdTime == rhs._stdTime; } - version(testStdDateTime) unittest + unittest { - _assertPred!"=="(SysTime(DateTime.init, UTC()), SysTime(0, UTC())); - _assertPred!"=="(SysTime(DateTime.init, UTC()), SysTime(0)); - _assertPred!"=="(SysTime(Date.init, UTC()), SysTime(0)); - _assertPred!"=="(SysTime(0), SysTime(0)); + assert(SysTime(DateTime.init, UTC()) == SysTime(0, UTC())); + assert(SysTime(DateTime.init, UTC()) == SysTime(0)); + assert(SysTime(Date.init, UTC()) == SysTime(0)); + assert(SysTime(0) == SysTime(0)); static void test(DateTime dt, immutable TimeZone tz1, @@ -819,7 +754,7 @@ public: auto st2 = SysTime(dt); st2.timezone = tz2; - _assertPred!"=="(st1, st2); + assert(st1 == st2); } foreach(tz1; testTZs) @@ -857,7 +792,7 @@ public: $(TR $(TD this > rhs) $(TD > 0)) ) +/ - int opCmp(in SysTime rhs) const pure nothrow + int opCmp(in SysTime rhs) @safe const pure nothrow { if(_stdTime < rhs._stdTime) return -1; @@ -867,13 +802,12 @@ public: return 0; } - version(testStdDateTime) unittest + unittest { - _assertPred!("opCmp", "==")(SysTime(DateTime.init, UTC()), - SysTime(0, UTC())); - _assertPred!("opCmp", "==")(SysTime(DateTime.init, UTC()), SysTime(0)); - _assertPred!("opCmp", "==")(SysTime(Date.init, UTC()), SysTime(0)); - _assertPred!("opCmp", "==")(SysTime(0), SysTime(0)); + assert(SysTime(DateTime.init, UTC()).opCmp(SysTime(0, UTC())) == 0); + assert(SysTime(DateTime.init, UTC()).opCmp(SysTime(0)) == 0); + assert(SysTime(Date.init, UTC()).opCmp(SysTime(0)) == 0); + assert(SysTime(0).opCmp(SysTime(0)) == 0); static void testEqual(SysTime st, immutable TimeZone tz1, @@ -885,7 +819,7 @@ public: auto st2 = st; st2.timezone = tz2; - _assertPred!("opCmp", "==")(st1, st2); + assert(st1.opCmp(st2) == 0); } auto sts = array(map!SysTime(chain(testDateTimesBC, testDateTimesAD))); @@ -902,8 +836,8 @@ public: { st1.timezone = tz1; st2.timezone = tz2; - _assertPred!("opCmp", "<")(st1, st2); - _assertPred!("opCmp", ">")(st2, st1); + assert(st1.opCmp(st2) < 0); + assert(st2.opCmp(st1) > 0); } foreach(si, st1; sts) @@ -930,17 +864,17 @@ public: Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive are B.C. +/ - @property short year() const nothrow + @property short year() @safe const nothrow { return (cast(Date)this).year; } - version(testStdDateTime) unittest + unittest { - static void test(SysTime sysTime, long expected, size_t line = __LINE__) + static void test(SysTime sysTime, long expected) { - _assertPred!"=="(sysTime.year, expected, - format("Value given: %s", sysTime), __FILE__, line); + assert(sysTime.year == expected, + format("Value given: %s", sysTime)); } test(SysTime(0, UTC()), 1); @@ -980,15 +914,8 @@ public: Throws: $(LREF DateTimeException) if the new year is not a leap year and the resulting date would be on February 29th. - - Examples: --------------------- -assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).year == 1999); -assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).year == 2010); -assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).year == -7); --------------------- +/ - @property void year(int year) + @property void year(int year) @safe { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -1006,20 +933,20 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).year == -7); adjTime = newDaysHNSecs + hnsecs; } - //Verify Examples. - version(testStdDateTime) unittest + /// + unittest { assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).year == 1999); assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).year == 2010); assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).year == -7); } - version(testStdDateTime) unittest + unittest { - static void test(SysTime st, int year, in SysTime expected, size_t line = __LINE__) + static void test(SysTime st, int year, in SysTime expected) { st.year = year; - _assertPred!"=="(st, expected, "", __FILE__, line); + assert(st == expected); } foreach(st; chain(testSysTimesBC, testSysTimesAD)) @@ -1066,34 +993,27 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).year == -7); Throws: $(LREF DateTimeException) if $(D isAD) is true. - - Examples: --------------------- -assert(SysTime(DateTime(0, 1, 1, 12, 30, 33)).yearBC == 1); -assert(SysTime(DateTime(-1, 1, 1, 10, 7, 2)).yearBC == 2); -assert(SysTime(DateTime(-100, 1, 1, 4, 59, 0)).yearBC == 101); --------------------- +/ - @property ushort yearBC() const + @property ushort yearBC() @safe const { return (cast(Date)this).yearBC; } - //Verify Examples. - version(testStdDateTime) unittest + /// + unittest { assert(SysTime(DateTime(0, 1, 1, 12, 30, 33)).yearBC == 1); assert(SysTime(DateTime(-1, 1, 1, 10, 7, 2)).yearBC == 2); assert(SysTime(DateTime(-100, 1, 1, 4, 59, 0)).yearBC == 101); } - version(testStdDateTime) unittest + unittest { foreach(st; testSysTimesBC) { auto msg = format("SysTime: %s", st); assertNotThrown!DateTimeException(st.yearBC, msg); - _assertPred!"=="(st.yearBC, (st.year * -1) + 1, msg); + assert(st.yearBC == (st.year * -1) + 1, msg); } foreach(st; [testSysTimesAD[0], testSysTimesAD[$/2], testSysTimesAD[$-1]]) @@ -1116,18 +1036,8 @@ assert(SysTime(DateTime(-100, 1, 1, 4, 59, 0)).yearBC == 101); Throws: $(LREF DateTimeException) if a non-positive value is given. - - Examples: --------------------- -auto st = SysTime(DateTime(2010, 1, 1, 7, 30, 0)); -st.yearBC = 1; -assert(st == SysTime(DateTime(0, 1, 1, 7, 30, 0))); - -st.yearBC = 10; -assert(st == SysTime(DateTime(-9, 1, 1, 7, 30, 0))); --------------------- +/ - @property void yearBC(int year) + @property void yearBC(int year) @safe { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -1145,8 +1055,7 @@ assert(st == SysTime(DateTime(-9, 1, 1, 7, 30, 0))); adjTime = newDaysHNSecs + hnsecs; } - //Verify Examples - version(testStdDateTime) unittest + unittest { auto st = SysTime(DateTime(2010, 1, 1, 7, 30, 0)); st.yearBC = 1; @@ -1156,12 +1065,12 @@ assert(st == SysTime(DateTime(-9, 1, 1, 7, 30, 0))); assert(st == SysTime(DateTime(-9, 1, 1, 7, 30, 0))); } - version(testStdDateTime) unittest + unittest { - static void test(SysTime st, int year, in SysTime expected, size_t line = __LINE__) + static void test(SysTime st, int year, in SysTime expected) { st.yearBC = year; - _assertPred!"=="(st, expected, format("SysTime: %s", st), __FILE__, line); + assert(st == expected, format("SysTime: %s", st)); } foreach(st; chain(testSysTimesBC, testSysTimesAD)) @@ -1215,33 +1124,26 @@ assert(st == SysTime(DateTime(-9, 1, 1, 7, 30, 0))); /++ Month of a Gregorian Year. - - Examples: --------------------- -assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).month == 7); -assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).month == 10); -assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).month == 4); --------------------- +/ - @property Month month() const nothrow + @property Month month() @safe const nothrow { return (cast(Date)this).month; } - //Verify Examples. - version(testStdDateTime) unittest + /// + unittest { assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).month == 7); assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).month == 10); assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).month == 4); } - version(testStdDateTime) unittest + unittest { - static void test(SysTime sysTime, Month expected, size_t line = __LINE__) + static void test(SysTime sysTime, Month expected) { - _assertPred!"=="(sysTime.month, expected, - format("Value given: %s", sysTime), __FILE__, line); + assert(sysTime.month == expected, + format("Value given: %s", sysTime)); } test(SysTime(0, UTC()), Month.jan); @@ -1281,7 +1183,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).month == 4); Throws: $(LREF DateTimeException) if the given month is not a valid month. +/ - @property void month(Month month) + @property void month(Month month) @safe { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -1299,12 +1201,12 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).month == 4); adjTime = newDaysHNSecs + hnsecs; } - version(testStdDateTime) unittest + unittest { - static void test(SysTime st, Month month, in SysTime expected, size_t line = __LINE__) + static void test(SysTime st, Month month, in SysTime expected) { st.month = cast(Month)month; - _assertPred!"=="(st, expected, "", __FILE__, line); + assert(st == expected); } foreach(st; chain(testSysTimesBC, testSysTimesAD)) @@ -1380,33 +1282,26 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).month == 4); /++ Day of a Gregorian Month. - - Examples: --------------------- -assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).day == 6); -assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).day == 4); -assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); --------------------- +/ - @property ubyte day() const nothrow + @property ubyte day() @safe const nothrow { return (cast(Date)this).day; } - //Verify Examples. - version(testStdDateTime) unittest + /// + unittest { assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).day == 6); assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).day == 4); assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); } - version(testStdDateTime) unittest + unittest { - static void test(SysTime sysTime, int expected, size_t line = __LINE__) + static void test(SysTime sysTime, int expected) { - _assertPred!"=="(sysTime.day, expected, - format("Value given: %s", sysTime), __FILE__, line); + assert(sysTime.day == expected, + format("Value given: %s", sysTime)); } test(SysTime(0, UTC()), 1); @@ -1447,7 +1342,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); $(LREF DateTimeException) if the given day is not a valid day of the current month. +/ - @property void day(int day) + @property void day(int day) @safe { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -1465,7 +1360,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); adjTime = newDaysHNSecs + hnsecs; } - version(testStdDateTime) unittest + unittest { foreach(day; chain(testDays)) { @@ -1539,7 +1434,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); /++ Hours past midnight. +/ - @property ubyte hour() const nothrow + @property ubyte hour() @safe const nothrow { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -1553,12 +1448,12 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); return cast(ubyte)getUnitsFromHNSecs!"hours"(hnsecs); } - version(testStdDateTime) unittest + unittest { - static void test(SysTime sysTime, int expected, size_t line = __LINE__) + static void test(SysTime sysTime, int expected) { - _assertPred!"=="(sysTime.hour, expected, - format("Value given: %s", sysTime), __FILE__, line); + assert(sysTime.hour == expected, + format("Value given: %s", sysTime)); } test(SysTime(0, UTC()), 0); @@ -1606,7 +1501,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); $(LREF DateTimeException) if the given hour are not a valid hour of the day. +/ - @property void hour(int hour) + @property void hour(int hour) @safe { enforceValid!"hours"(hour); @@ -1627,7 +1522,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); adjTime = daysHNSecs + hnsecs; } - version(testStdDateTime) unittest + unittest { foreach(hour; chain(testHours)) { @@ -1657,7 +1552,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); /++ Minutes past the current hour. +/ - @property ubyte minute() const nothrow + @property ubyte minute() @safe const nothrow { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -1673,12 +1568,12 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); return cast(ubyte)getUnitsFromHNSecs!"minutes"(hnsecs); } - version(testStdDateTime) unittest + unittest { - static void test(SysTime sysTime, int expected, size_t line = __LINE__) + static void test(SysTime sysTime, int expected) { - _assertPred!"=="(sysTime.minute, expected, - format("Value given: %s", sysTime), __FILE__, line); + assert(sysTime.minute == expected, + format("Value given: %s", sysTime)); } test(SysTime(0, UTC()), 0); @@ -1726,7 +1621,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); $(LREF DateTimeException) if the given minute are not a valid minute of an hour. +/ - @property void minute(int minute) + @property void minute(int minute) @safe { enforceValid!"minutes"(minute); @@ -1750,7 +1645,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); adjTime = daysHNSecs + hnsecs; } - version(testStdDateTime) unittest + unittest { foreach(minute; testMinSecs) { @@ -1780,7 +1675,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); /++ Seconds past the current minute. +/ - @property ubyte second() const nothrow + @property ubyte second() @safe const nothrow { auto hnsecs = adjTime; auto days = splitUnitsFromHNSecs!"days"(hnsecs) + 1; @@ -1797,12 +1692,12 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); return cast(ubyte)getUnitsFromHNSecs!"seconds"(hnsecs); } - version(testStdDateTime) unittest + unittest { - static void test(SysTime sysTime, int expected, size_t line = __LINE__) + static void test(SysTime sysTime, int expected) { - _assertPred!"=="(sysTime.second, expected, - format("Value given: %s", sysTime), __FILE__, line); + assert(sysTime.second == expected, + format("Value given: %s", sysTime)); } test(SysTime(0, UTC()), 0); @@ -1850,7 +1745,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); $(LREF DateTimeException) if the given second are not a valid second of a minute. +/ - @property void second(int second) + @property void second(int second) @safe { enforceValid!"seconds"(second); @@ -1876,7 +1771,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); adjTime = daysHNSecs + hnsecs; } - version(testStdDateTime) unittest + unittest { foreach(second; testMinSecs) { @@ -1906,7 +1801,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); /++ Fractional seconds passed the second. +/ - @property FracSec fracSec() const nothrow + @property FracSec fracSec() @safe const nothrow { try { @@ -1923,12 +1818,12 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); assert(0, "FracSec.from!\"hnsecs\"() threw."); } - version(testStdDateTime) unittest + unittest { - static void test(SysTime sysTime, FracSec expected, size_t line = __LINE__) + static void test(SysTime sysTime, FracSec expected) { - _assertPred!"=="(sysTime.fracSec, expected, - format("Value given: %s", sysTime), __FILE__, line); + assert(sysTime.fracSec == expected, + format("Value given: %s", sysTime)); } test(SysTime(0, UTC()), FracSec.from!"hnsecs"(0)); @@ -1976,7 +1871,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); Throws: $(LREF DateTimeException) if $(D fracSec) is negative. +/ - @property void fracSec(FracSec fracSec) + @property void fracSec(FracSec fracSec) @safe { immutable fracHNSecs = fracSec.hnsecs; enforce(fracHNSecs >= 0, new DateTimeException("A SysTime cannot have negative fractional seconds.")); @@ -2004,7 +1899,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); adjTime = daysHNSecs + hnsecs; } - version(testStdDateTime) unittest + unittest { foreach(fracSec; testFracSecs) { @@ -2034,19 +1929,19 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); The total hnsecs from midnight, January 1st, 1 A.D. UTC. This is the internal representation of $(LREF SysTime). +/ - @property long stdTime() const pure nothrow + @property long stdTime() @safe const pure nothrow { return _stdTime; } - version(testStdDateTime) unittest + unittest { - _assertPred!"=="(SysTime(0).stdTime, 0); - _assertPred!"=="(SysTime(1).stdTime, 1); - _assertPred!"=="(SysTime(-1).stdTime, -1); - _assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 33), FracSec.from!"hnsecs"(502), UTC()).stdTime, + assert(SysTime(0).stdTime == 0); + assert(SysTime(1).stdTime == 1); + assert(SysTime(-1).stdTime == -1); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 33), FracSec.from!"hnsecs"(502), UTC()).stdTime == 330000502L); - _assertPred!"=="(SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC()).stdTime, + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), UTC()).stdTime == 621355968000000000L); const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); @@ -2063,18 +1958,18 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); Params: stdTime = The number of hnsecs since January 1st, 1 A.D. UTC. +/ - @property void stdTime(long stdTime) pure nothrow + @property void stdTime(long stdTime) @safe pure nothrow { _stdTime = stdTime; } - version(testStdDateTime) unittest + unittest { static void test(long stdTime, in SysTime expected, size_t line = __LINE__) { auto st = SysTime(0, UTC()); st.stdTime = stdTime; - _assertPred!"=="(st, expected); + assert(st == expected); } test(0, SysTime(Date(1, 1, 1), UTC())); @@ -2096,7 +1991,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); DST. Functions which return all or part of the time - such as hours - adjust the time to this $(LREF SysTime)'s time zone before returning. +/ - @property immutable(TimeZone) timezone() const pure nothrow + @property immutable(TimeZone) timezone() @safe const pure nothrow { return _timezone; } @@ -2111,7 +2006,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); Params: timezone = The $(LREF2 .TimeZone, TimeZone) to set this $(LREF SysTime)'s time zone to. +/ - @property void timezone(immutable TimeZone timezone) pure nothrow + @property void timezone(immutable TimeZone timezone) @safe pure nothrow { if(timezone is null) _timezone = LocalTime(); @@ -2123,7 +2018,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); /++ Returns whether DST is in effect for this $(LREF SysTime). +/ - @property bool dstInEffect() const nothrow + @property bool dstInEffect() @safe const nothrow { return _timezone.dstInEffect(_stdTime); //This function's unit testing is done in the time zone classes. @@ -2134,7 +2029,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); Returns what the offset from UTC is for this $(LREF SysTime). It includes the DST offset in effect at that time (if any). +/ - @property Duration utcOffset() const nothrow + @property Duration utcOffset() @safe const nothrow { return _timezone.utcOffsetAt(_stdTime); } @@ -2144,33 +2039,30 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); Returns a $(LREF SysTime) with the same std time as this one, but with $(LREF LocalTime) as its time zone. +/ - SysTime toLocalTime() const nothrow + SysTime toLocalTime() @safe const pure nothrow { return SysTime(_stdTime, LocalTime()); } unittest { - version(testStdDateTime) { - { - auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), FracSec.from!"hnsecs"(27)); - _assertPred!"=="(sysTime, sysTime.toLocalTime()); - _assertPred!"=="(sysTime._stdTime, sysTime.toLocalTime()._stdTime); - assert(sysTime.toLocalTime().timezone is LocalTime()); - assert(sysTime.toLocalTime().timezone is sysTime.timezone); - assert(sysTime.toLocalTime().timezone !is UTC()); - } + auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), FracSec.from!"hnsecs"(27)); + assert(sysTime == sysTime.toLocalTime()); + assert(sysTime._stdTime == sysTime.toLocalTime()._stdTime); + assert(sysTime.toLocalTime().timezone is LocalTime()); + assert(sysTime.toLocalTime().timezone is sysTime.timezone); + assert(sysTime.toLocalTime().timezone !is UTC()); + } - { - auto stz = new immutable SimpleTimeZone(dur!"minutes"(-3 * 60)); - auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), FracSec.from!"hnsecs"(27), stz); - _assertPred!"=="(sysTime, sysTime.toLocalTime()); - _assertPred!"=="(sysTime._stdTime, sysTime.toLocalTime()._stdTime); - assert(sysTime.toLocalTime().timezone is LocalTime()); - assert(sysTime.toLocalTime().timezone !is UTC()); - assert(sysTime.toLocalTime().timezone !is stz); - } + { + auto stz = new immutable SimpleTimeZone(dur!"minutes"(-3 * 60)); + auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), FracSec.from!"hnsecs"(27), stz); + assert(sysTime == sysTime.toLocalTime()); + assert(sysTime._stdTime == sysTime.toLocalTime()._stdTime); + assert(sysTime.toLocalTime().timezone is LocalTime()); + assert(sysTime.toLocalTime().timezone !is UTC()); + assert(sysTime.toLocalTime().timezone !is stz); } } @@ -2179,22 +2071,19 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); Returns a $(LREF SysTime) with the same std time as this one, but with $(D UTC) as its time zone. +/ - SysTime toUTC() const pure nothrow + SysTime toUTC() @safe const pure nothrow { return SysTime(_stdTime, UTC()); } unittest { - version(testStdDateTime) - { - auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), FracSec.from!"hnsecs"(27)); - _assertPred!"=="(sysTime, sysTime.toUTC()); - _assertPred!"=="(sysTime._stdTime, sysTime.toUTC()._stdTime); - assert(sysTime.toUTC().timezone is UTC()); - assert(sysTime.toUTC().timezone !is LocalTime()); - assert(sysTime.toUTC().timezone !is sysTime.timezone); - } + auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), FracSec.from!"hnsecs"(27)); + assert(sysTime == sysTime.toUTC()); + assert(sysTime._stdTime == sysTime.toUTC()._stdTime); + assert(sysTime.toUTC().timezone is UTC()); + assert(sysTime.toUTC().timezone !is LocalTime()); + assert(sysTime.toUTC().timezone !is sysTime.timezone); } @@ -2202,7 +2091,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); Returns a $(LREF SysTime) with the same std time as this one, but with given time zone as its time zone. +/ - SysTime toOtherTZ(immutable TimeZone tz) const pure nothrow + SysTime toOtherTZ(immutable TimeZone tz) @safe const pure nothrow { if(tz is null) return SysTime(_stdTime, LocalTime()); @@ -2212,16 +2101,13 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); unittest { - version(testStdDateTime) - { - auto stz = new immutable SimpleTimeZone(dur!"minutes"(11 * 60)); - auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), FracSec.from!"hnsecs"(27)); - _assertPred!"=="(sysTime, sysTime.toOtherTZ(stz)); - _assertPred!"=="(sysTime._stdTime, sysTime.toOtherTZ(stz)._stdTime); - assert(sysTime.toOtherTZ(stz).timezone is stz); - assert(sysTime.toOtherTZ(stz).timezone !is LocalTime()); - assert(sysTime.toOtherTZ(stz).timezone !is UTC()); - } + auto stz = new immutable SimpleTimeZone(dur!"minutes"(11 * 60)); + auto sysTime = SysTime(DateTime(1982, 1, 4, 8, 59, 7), FracSec.from!"hnsecs"(27)); + assert(sysTime == sysTime.toOtherTZ(stz)); + assert(sysTime._stdTime == sysTime.toOtherTZ(stz)._stdTime); + assert(sysTime.toOtherTZ(stz).timezone is stz); + assert(sysTime.toOtherTZ(stz).timezone !is LocalTime()); + assert(sysTime.toOtherTZ(stz).timezone !is UTC()); } @@ -2237,25 +2123,22 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); used (so $(D time_t.max) if it goes over and $(D time_t.min) if it goes under). +/ - time_t toUnixTime() const pure nothrow + time_t toUnixTime() @safe const pure nothrow { return stdTimeToUnixTime(_stdTime); } unittest { - version(testStdDateTime) - { - _assertPred!"=="(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime(), 0); - _assertPred!"=="(SysTime(DateTime(1970, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1), UTC()).toUnixTime(), 0); - _assertPred!"=="(SysTime(DateTime(1970, 1, 1, 0, 0, 0), FracSec.from!"usecs"(1), UTC()).toUnixTime(), 0); - _assertPred!"=="(SysTime(DateTime(1970, 1, 1, 0, 0, 0), FracSec.from!"msecs"(1), UTC()).toUnixTime(), 0); - _assertPred!"=="(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toUnixTime(), 1); - _assertPred!"=="(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC()).toUnixTime(), 0); - _assertPred!"=="(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"usecs"(999_999), UTC()).toUnixTime(), 0); - _assertPred!"=="(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"msecs"(999), UTC()).toUnixTime(), 0); - _assertPred!"=="(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toUnixTime(), -1); - } + assert(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime() == 0); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1), UTC()).toUnixTime() == 0); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), FracSec.from!"usecs"(1), UTC()).toUnixTime() == 0); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 0), FracSec.from!"msecs"(1), UTC()).toUnixTime() == 0); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toUnixTime() == 1); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC()).toUnixTime() == 0); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"usecs"(999_999), UTC()).toUnixTime() == 0); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"msecs"(999), UTC()).toUnixTime() == 0); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toUnixTime() == -1); } @@ -2270,7 +2153,7 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); used for $(D tv_sec). (so $(D time_t.max) if it goes over and $(D time_t.min) if it goes under). +/ - timeval toTimeVal() const pure nothrow + timeval toTimeVal() @safe const pure nothrow { immutable tv_sec = toUnixTime(); @@ -2282,127 +2165,114 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); unittest { - version(testStdDateTime) - { - assert(SysTime(DateTime(1970, 1, 1), UTC()).toTimeVal() == timeval(0, 0)); - assert(SysTime(DateTime(1970, 1, 1), FracSec.from!"hnsecs"(9), UTC()).toTimeVal() == timeval(0, 0)); - assert(SysTime(DateTime(1970, 1, 1), FracSec.from!"hnsecs"(10), UTC()).toTimeVal() == timeval(0, 1)); - assert(SysTime(DateTime(1970, 1, 1), FracSec.from!"usecs"(7), UTC()).toTimeVal() == timeval(0, 7)); + assert(SysTime(DateTime(1970, 1, 1), UTC()).toTimeVal() == timeval(0, 0)); + assert(SysTime(DateTime(1970, 1, 1), FracSec.from!"hnsecs"(9), UTC()).toTimeVal() == timeval(0, 0)); + assert(SysTime(DateTime(1970, 1, 1), FracSec.from!"hnsecs"(10), UTC()).toTimeVal() == timeval(0, 1)); + assert(SysTime(DateTime(1970, 1, 1), FracSec.from!"usecs"(7), UTC()).toTimeVal() == timeval(0, 7)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toTimeVal() == timeval(1, 0)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), FracSec.from!"hnsecs"(9), UTC()).toTimeVal() == timeval(1, 0)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), FracSec.from!"hnsecs"(10), UTC()).toTimeVal() == timeval(1, 1)); - assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), FracSec.from!"usecs"(7), UTC()).toTimeVal() == timeval(1, 7)); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), UTC()).toTimeVal() == timeval(1, 0)); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), FracSec.from!"hnsecs"(9), UTC()).toTimeVal() == timeval(1, 0)); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), FracSec.from!"hnsecs"(10), UTC()).toTimeVal() == timeval(1, 1)); + assert(SysTime(DateTime(1970, 1, 1, 0, 0, 1), FracSec.from!"usecs"(7), UTC()).toTimeVal() == timeval(1, 7)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC()).toTimeVal() == - timeval(0, 0)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_990), UTC()).toTimeVal() == - timeval(0, -1)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC()).toTimeVal() == + timeval(0, 0)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_990), UTC()).toTimeVal() == + timeval(0, -1)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"usecs"(999_999), UTC()).toTimeVal() == - timeval(0, -1)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"usecs"(999), UTC()).toTimeVal() == - timeval(0, -999_001)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"msecs"(999), UTC()).toTimeVal() == - timeval(0, -1000)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toTimeVal() == timeval(-1, 0)); - assert(SysTime(DateTime(1969, 12, 31, 23, 59, 58), FracSec.from!"usecs"(17), UTC()).toTimeVal() == - timeval(-1, -999_983)); - } + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"usecs"(999_999), UTC()).toTimeVal() == + timeval(0, -1)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"usecs"(999), UTC()).toTimeVal() == + timeval(0, -999_001)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), FracSec.from!"msecs"(999), UTC()).toTimeVal() == + timeval(0, -1000)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 59), UTC()).toTimeVal() == timeval(-1, 0)); + assert(SysTime(DateTime(1969, 12, 31, 23, 59, 58), FracSec.from!"usecs"(17), UTC()).toTimeVal() == + timeval(-1, -999_983)); } /++ Returns a $(D tm) which represents this $(LREF SysTime). +/ - tm toTM() const nothrow + tm toTM() @safe const nothrow { - try - { - auto dateTime = cast(DateTime)this; - tm timeInfo; + auto dateTime = cast(DateTime)this; + tm timeInfo; - timeInfo.tm_sec = dateTime.second; - timeInfo.tm_min = dateTime.minute; - timeInfo.tm_hour = dateTime.hour; - timeInfo.tm_mday = dateTime.day; - timeInfo.tm_mon = dateTime.month - 1; - timeInfo.tm_year = dateTime.year - 1900; - timeInfo.tm_wday = dateTime.dayOfWeek; - timeInfo.tm_yday = dateTime.dayOfYear - 1; - timeInfo.tm_isdst = _timezone.dstInEffect(_stdTime); + timeInfo.tm_sec = dateTime.second; + timeInfo.tm_min = dateTime.minute; + timeInfo.tm_hour = dateTime.hour; + timeInfo.tm_mday = dateTime.day; + timeInfo.tm_mon = dateTime.month - 1; + timeInfo.tm_year = dateTime.year - 1900; + timeInfo.tm_wday = dateTime.dayOfWeek; + timeInfo.tm_yday = dateTime.dayOfYear - 1; + timeInfo.tm_isdst = _timezone.dstInEffect(_stdTime); - version(Posix) - { - char[] zone = (timeInfo.tm_isdst ? _timezone.dstName : _timezone.stdName).dup; - zone ~= "\0"; - - timeInfo.tm_gmtoff = cast(int)convert!("hnsecs", "seconds")(adjTime - _stdTime); - timeInfo.tm_zone = zone.ptr; - } - - return timeInfo; + version(Posix) + { + timeInfo.tm_gmtoff = cast(int)convert!("hnsecs", "seconds")(adjTime - _stdTime); + auto zone = (timeInfo.tm_isdst ? _timezone.dstName : _timezone.stdName).dup; + timeInfo.tm_zone = zone.toUTFz!(const(char)*)(); } - catch(Exception e) - assert(0, "Either DateTime's constructor threw."); + + return timeInfo; } unittest { - version(testStdDateTime) + version(Posix) + { + scope(exit) clearTZEnvVar(); + setTZEnvVar("America/Los_Angeles"); + } + { + auto timeInfo = SysTime(DateTime(1970, 1, 1)).toTM(); + + assert(timeInfo.tm_sec == 0); + assert(timeInfo.tm_min == 0); + assert(timeInfo.tm_hour == 0); + assert(timeInfo.tm_mday == 1); + assert(timeInfo.tm_mon == 0); + assert(timeInfo.tm_year == 70); + assert(timeInfo.tm_wday == 4); + assert(timeInfo.tm_yday == 0); + version(Posix) - { - scope(exit) clearTZEnvVar(); - setTZEnvVar("America/Los_Angeles"); - } + assert(timeInfo.tm_isdst == 0); + else version(Windows) + assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1); + version(Posix) { - auto timeInfo = SysTime(DateTime(1970, 1, 1)).toTM(); - - _assertPred!"=="(timeInfo.tm_sec, 0); - _assertPred!"=="(timeInfo.tm_min, 0); - _assertPred!"=="(timeInfo.tm_hour, 0); - _assertPred!"=="(timeInfo.tm_mday, 1); - _assertPred!"=="(timeInfo.tm_mon, 0); - _assertPred!"=="(timeInfo.tm_year, 70); - _assertPred!"=="(timeInfo.tm_wday, 4); - _assertPred!"=="(timeInfo.tm_yday, 0); - - version(Posix) - _assertPred!"=="(timeInfo.tm_isdst, 0); - else version(Windows) - assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1); - - version(Posix) - { - _assertPred!"=="(timeInfo.tm_gmtoff, -8 * 60 * 60); - _assertPred!"=="(to!string(timeInfo.tm_zone), "PST"); - } + assert(timeInfo.tm_gmtoff == -8 * 60 * 60); + assert(to!string(timeInfo.tm_zone) == "PST"); } + } + + { + auto timeInfo = SysTime(DateTime(2010, 7, 4, 12, 15, 7), FracSec.from!"hnsecs"(15)).toTM(); + assert(timeInfo.tm_sec == 7); + assert(timeInfo.tm_min == 15); + assert(timeInfo.tm_hour == 12); + assert(timeInfo.tm_mday == 4); + assert(timeInfo.tm_mon == 6); + assert(timeInfo.tm_year == 110); + assert(timeInfo.tm_wday == 0); + assert(timeInfo.tm_yday == 184); + + version(Posix) + assert(timeInfo.tm_isdst == 1); + else version(Windows) + assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1); + + version(Posix) { - auto timeInfo = SysTime(DateTime(2010, 7, 4, 12, 15, 7), FracSec.from!"hnsecs"(15)).toTM(); - - _assertPred!"=="(timeInfo.tm_sec, 7); - _assertPred!"=="(timeInfo.tm_min, 15); - _assertPred!"=="(timeInfo.tm_hour, 12); - _assertPred!"=="(timeInfo.tm_mday, 4); - _assertPred!"=="(timeInfo.tm_mon, 6); - _assertPred!"=="(timeInfo.tm_year, 110); - _assertPred!"=="(timeInfo.tm_wday, 0); - _assertPred!"=="(timeInfo.tm_yday, 184); - - version(Posix) - _assertPred!"=="(timeInfo.tm_isdst, 1); - else version(Windows) - assert(timeInfo.tm_isdst == 0 || timeInfo.tm_isdst == 1); - - version(Posix) - { - _assertPred!"=="(timeInfo.tm_gmtoff, -7 * 60 * 60); - _assertPred!"=="(to!string(timeInfo.tm_zone), "PDT"); - } + assert(timeInfo.tm_gmtoff == -7 * 60 * 60); + assert(to!string(timeInfo.tm_zone) == "PDT"); } } } @@ -2426,27 +2296,8 @@ assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5); $(LREF SysTime). allowOverflow = Whether the days should be allowed to overflow, causing the month to increment. - - Examples: --------------------- -auto st1 = SysTime(DateTime(2010, 1, 1, 12, 30, 33)); -st1.add!"months"(11); -assert(st1 == SysTime(DateTime(2010, 12, 1, 12, 30, 33))); - -auto st2 = SysTime(DateTime(2010, 1, 1, 12, 30, 33)); -st2.add!"months"(-11); -assert(st2 == SysTime(DateTime(2009, 2, 1, 12, 30, 33))); - -auto st3 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); -st3.add!"years"(1); -assert(st3 == SysTime(DateTime(2001, 3, 1, 12, 30, 33))); - -auto st4 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); -st4.add!"years"(1, AllowDayOverflow.no); -assert(st4 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); --------------------- +/ - ref SysTime add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) nothrow + ref SysTime add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow if(units == "years" || units == "months") { @@ -2476,1108 +2327,1116 @@ assert(st4 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); return this; } - //Verify Examples. unittest { - version (testStdDateTime) - { - auto st1 = SysTime(DateTime(2010, 1, 1, 12, 30, 33)); - st1.add!"months"(11); - assert(st1 == SysTime(DateTime(2010, 12, 1, 12, 30, 33))); + auto st1 = SysTime(DateTime(2010, 1, 1, 12, 30, 33)); + st1.add!"months"(11); + assert(st1 == SysTime(DateTime(2010, 12, 1, 12, 30, 33))); - auto st2 = SysTime(DateTime(2010, 1, 1, 12, 30, 33)); - st2.add!"months"(-11); - assert(st2 == SysTime(DateTime(2009, 2, 1, 12, 30, 33))); + auto st2 = SysTime(DateTime(2010, 1, 1, 12, 30, 33)); + st2.add!"months"(-11); + assert(st2 == SysTime(DateTime(2009, 2, 1, 12, 30, 33))); - auto st3 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); - st3.add!"years"(1); - assert(st3 == SysTime(DateTime(2001, 3, 1, 12, 30, 33))); + auto st3 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); + st3.add!"years"(1); + assert(st3 == SysTime(DateTime(2001, 3, 1, 12, 30, 33))); - auto st4 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); - st4.add!"years"(1, AllowDayOverflow.no); - assert(st4 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); - } + auto st4 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); + st4.add!"years"(1, AllowDayOverflow.no); + assert(st4 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); } //Test add!"years"() with AllowDayOverlow.yes unittest { - version(testStdDateTime) + //Test A.D. { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"years"(7); - _assertPred!"=="(sysTime, SysTime(Date(2006, 7, 6))); - sysTime.add!"years"(-9); - _assertPred!"=="(sysTime, SysTime(Date(1997, 7, 6))); - } + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"years"(7); + assert(sysTime == SysTime(Date(2006, 7, 6))); + sysTime.add!"years"(-9); + assert(sysTime == SysTime(Date(1997, 7, 6))); + } - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.add!"years"(1); - _assertPred!"=="(sysTime, SysTime(Date(2000, 2, 28))); - } + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(Date(2000, 2, 28))); + } - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.add!"years"(-1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 3, 1))); - } + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(Date(1999, 3, 1))); + } - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234)); - sysTime.add!"years"(7); - _assertPred!"=="(sysTime, SysTime(DateTime(2006, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); - sysTime.add!"years"(-9); - _assertPred!"=="(sysTime, SysTime(DateTime(1997, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); - } + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234)); + sysTime.add!"years"(7); + assert(sysTime == SysTime(DateTime(2006, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); + sysTime.add!"years"(-9); + assert(sysTime == SysTime(DateTime(1997, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); + } - { - auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), FracSec.from!"usecs"(1207)); - sysTime.add!"years"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(2000, 2, 28, 0, 7, 2), FracSec.from!"usecs"(1207))); - } + { + auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), FracSec.from!"usecs"(1207)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(2000, 2, 28, 0, 7, 2), FracSec.from!"usecs"(1207))); + } - { - auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), FracSec.from!"usecs"(1207)); - sysTime.add!"years"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 3, 1, 0, 7, 2), FracSec.from!"usecs"(1207))); - } + { + auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), FracSec.from!"usecs"(1207)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(1999, 3, 1, 0, 7, 2), FracSec.from!"usecs"(1207))); + } - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"years"(-7); - _assertPred!"=="(sysTime, SysTime(Date(-2006, 7, 6))); - sysTime.add!"years"(9); - _assertPred!"=="(sysTime, SysTime(Date(-1997, 7, 6))); - } + //Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"years"(-7); + assert(sysTime == SysTime(Date(-2006, 7, 6))); + sysTime.add!"years"(9); + assert(sysTime == SysTime(Date(-1997, 7, 6))); + } - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.add!"years"(-1); - _assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 28))); - } + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(Date(-2000, 2, 28))); + } - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.add!"years"(1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 3, 1))); - } + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(Date(-1999, 3, 1))); + } - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234)); - sysTime.add!"years"(-7); - _assertPred!"=="(sysTime, SysTime(DateTime(-2006, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); - sysTime.add!"years"(9); - _assertPred!"=="(sysTime, SysTime(DateTime(-1997, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); - } + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234)); + sysTime.add!"years"(-7); + assert(sysTime == SysTime(DateTime(-2006, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); + sysTime.add!"years"(9); + assert(sysTime == SysTime(DateTime(-1997, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); + } - { - auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), FracSec.from!"hnsecs"(3)); - sysTime.add!"years"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(-2000, 2, 28, 3, 3, 3), FracSec.from!"hnsecs"(3))); - } + { + auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), FracSec.from!"hnsecs"(3)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(-2000, 2, 28, 3, 3, 3), FracSec.from!"hnsecs"(3))); + } - { - auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), FracSec.from!"hnsecs"(3)); - sysTime.add!"years"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 3, 1, 3, 3, 3), FracSec.from!"hnsecs"(3))); - } + { + auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), FracSec.from!"hnsecs"(3)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(-1999, 3, 1, 3, 3, 3), FracSec.from!"hnsecs"(3))); + } - //Test Both - { - auto sysTime = SysTime(Date(4, 7, 6)); - sysTime.add!"years"(-5); - _assertPred!"=="(sysTime, SysTime(Date(-1, 7, 6))); - sysTime.add!"years"(5); - _assertPred!"=="(sysTime, SysTime(Date(4, 7, 6))); - } + //Test Both + { + auto sysTime = SysTime(Date(4, 7, 6)); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(Date(-1, 7, 6))); + sysTime.add!"years"(5); + assert(sysTime == SysTime(Date(4, 7, 6))); + } - { - auto sysTime = SysTime(Date(-4, 7, 6)); - sysTime.add!"years"(5); - _assertPred!"=="(sysTime, SysTime(Date(1, 7, 6))); - sysTime.add!"years"(-5); - _assertPred!"=="(sysTime, SysTime(Date(-4, 7, 6))); - } + { + auto sysTime = SysTime(Date(-4, 7, 6)); + sysTime.add!"years"(5); + assert(sysTime == SysTime(Date(1, 7, 6))); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(Date(-4, 7, 6))); + } - { - auto sysTime = SysTime(Date(4, 7, 6)); - sysTime.add!"years"(-8); - _assertPred!"=="(sysTime, SysTime(Date(-4, 7, 6))); - sysTime.add!"years"(8); - _assertPred!"=="(sysTime, SysTime(Date(4, 7, 6))); - } + { + auto sysTime = SysTime(Date(4, 7, 6)); + sysTime.add!"years"(-8); + assert(sysTime == SysTime(Date(-4, 7, 6))); + sysTime.add!"years"(8); + assert(sysTime == SysTime(Date(4, 7, 6))); + } - { - auto sysTime = SysTime(Date(-4, 7, 6)); - sysTime.add!"years"(8); - _assertPred!"=="(sysTime, SysTime(Date(4, 7, 6))); - sysTime.add!"years"(-8); - _assertPred!"=="(sysTime, SysTime(Date(-4, 7, 6))); - } + { + auto sysTime = SysTime(Date(-4, 7, 6)); + sysTime.add!"years"(8); + assert(sysTime == SysTime(Date(4, 7, 6))); + sysTime.add!"years"(-8); + assert(sysTime == SysTime(Date(-4, 7, 6))); + } - { - auto sysTime = SysTime(Date(-4, 2, 29)); - sysTime.add!"years"(5); - _assertPred!"=="(sysTime, SysTime(Date(1, 3, 1))); - } + { + auto sysTime = SysTime(Date(-4, 2, 29)); + sysTime.add!"years"(5); + assert(sysTime == SysTime(Date(1, 3, 1))); + } - { - auto sysTime = SysTime(Date(4, 2, 29)); - sysTime.add!"years"(-5); - _assertPred!"=="(sysTime, SysTime(Date(-1, 3, 1))); - } + { + auto sysTime = SysTime(Date(4, 2, 29)); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(Date(-1, 3, 1))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.add!"years"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.add!"years"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.add!"years"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.add!"years"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } - { - auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.add!"years"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.add!"years"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } + { + auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } - { - auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.add!"years"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.add!"years"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + { + auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.add!"years"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.add!"years"(-1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } - { - auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329)); - sysTime.add!"years"(-5); - _assertPred!"=="(sysTime, SysTime(DateTime(-1, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); - sysTime.add!"years"(5); - _assertPred!"=="(sysTime, SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); - } + { + auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329)); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + sysTime.add!"years"(5); + assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + } - { - auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329)); - sysTime.add!"years"(5); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); - sysTime.add!"years"(-5); - _assertPred!"=="(sysTime, SysTime(DateTime(-4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); - } + { + auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329)); + sysTime.add!"years"(5); + assert(sysTime == SysTime(DateTime(1, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(DateTime(-4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + } - { - auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), FracSec.from!"msecs"(555)); - sysTime.add!"years"(5); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 3, 1, 5, 5, 5), FracSec.from!"msecs"(555))); - } + { + auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), FracSec.from!"msecs"(555)); + sysTime.add!"years"(5); + assert(sysTime == SysTime(DateTime(1, 3, 1, 5, 5, 5), FracSec.from!"msecs"(555))); + } - { - auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), FracSec.from!"msecs"(555)); - sysTime.add!"years"(-5); - _assertPred!"=="(sysTime, SysTime(DateTime(-1, 3, 1, 5, 5, 5), FracSec.from!"msecs"(555))); - } + { + auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), FracSec.from!"msecs"(555)); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(DateTime(-1, 3, 1, 5, 5, 5), FracSec.from!"msecs"(555))); + } - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.add!"years"(4))); - //static assert(!__traits(compiles, ist.add!"years"(4))); + { + auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), FracSec.from!"msecs"(555)); + sysTime.add!"years"(-5).add!"years"(7); + assert(sysTime == SysTime(DateTime(6, 3, 1, 5, 5, 5), FracSec.from!"msecs"(555))); } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.add!"years"(4))); + //static assert(!__traits(compiles, ist.add!"years"(4))); } //Test add!"years"() with AllowDayOverlow.no unittest { - version(testStdDateTime) + //Test A.D. { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"years"(7, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(2006, 7, 6))); - sysTime.add!"years"(-9, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1997, 7, 6))); - } + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"years"(7, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2006, 7, 6))); + sysTime.add!"years"(-9, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 7, 6))); + } - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.add!"years"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(2000, 2, 28))); - } + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2000, 2, 28))); + } - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 2, 28))); - } + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 2, 28))); + } - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234)); - sysTime.add!"years"(7, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(2006, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); - sysTime.add!"years"(-9, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1997, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); - } + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234)); + sysTime.add!"years"(7, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(2006, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); + sysTime.add!"years"(-9, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1997, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); + } - { - auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), FracSec.from!"usecs"(1207)); - sysTime.add!"years"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(2000, 2, 28, 0, 7, 2), FracSec.from!"usecs"(1207))); - } + { + auto sysTime = SysTime(DateTime(1999, 2, 28, 0, 7, 2), FracSec.from!"usecs"(1207)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(2000, 2, 28, 0, 7, 2), FracSec.from!"usecs"(1207))); + } - { - auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), FracSec.from!"usecs"(1207)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 2, 28, 0, 7, 2), FracSec.from!"usecs"(1207))); - } + { + auto sysTime = SysTime(DateTime(2000, 2, 29, 0, 7, 2), FracSec.from!"usecs"(1207)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 2, 28, 0, 7, 2), FracSec.from!"usecs"(1207))); + } - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"years"(-7, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-2006, 7, 6))); - sysTime.add!"years"(9, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1997, 7, 6))); - } + //Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"years"(-7, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2006, 7, 6))); + sysTime.add!"years"(9, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 7, 6))); + } - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 28))); - } + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2000, 2, 28))); + } - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.add!"years"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 2, 28))); - } + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 2, 28))); + } - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234)); - sysTime.add!"years"(-7, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-2006, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); - sysTime.add!"years"(9, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-1997, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); - } + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234)); + sysTime.add!"years"(-7, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2006, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); + sysTime.add!"years"(9, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1997, 7, 6, 12, 7, 3), FracSec.from!"msecs"(234))); + } - { - auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), FracSec.from!"hnsecs"(3)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-2000, 2, 28, 3, 3, 3), FracSec.from!"hnsecs"(3))); - } + { + auto sysTime = SysTime(DateTime(-1999, 2, 28, 3, 3, 3), FracSec.from!"hnsecs"(3)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2000, 2, 28, 3, 3, 3), FracSec.from!"hnsecs"(3))); + } - { - auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), FracSec.from!"hnsecs"(3)); - sysTime.add!"years"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 2, 28, 3, 3, 3), FracSec.from!"hnsecs"(3))); - } + { + auto sysTime = SysTime(DateTime(-2000, 2, 29, 3, 3, 3), FracSec.from!"hnsecs"(3)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 2, 28, 3, 3, 3), FracSec.from!"hnsecs"(3))); + } - //Test Both - { - auto sysTime = SysTime(Date(4, 7, 6)); - sysTime.add!"years"(-5, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1, 7, 6))); - sysTime.add!"years"(5, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(4, 7, 6))); - } + //Test Both + { + auto sysTime = SysTime(Date(4, 7, 6)); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1, 7, 6))); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 7, 6))); + } - { - auto sysTime = SysTime(Date(-4, 7, 6)); - sysTime.add!"years"(5, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1, 7, 6))); - sysTime.add!"years"(-5, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-4, 7, 6))); - } + { + auto sysTime = SysTime(Date(-4, 7, 6)); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1, 7, 6))); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 7, 6))); + } - { - auto sysTime = SysTime(Date(4, 7, 6)); - sysTime.add!"years"(-8, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-4, 7, 6))); - sysTime.add!"years"(8, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(4, 7, 6))); - } + { + auto sysTime = SysTime(Date(4, 7, 6)); + sysTime.add!"years"(-8, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 7, 6))); + sysTime.add!"years"(8, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 7, 6))); + } - { - auto sysTime = SysTime(Date(-4, 7, 6)); - sysTime.add!"years"(8, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(4, 7, 6))); - sysTime.add!"years"(-8, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-4, 7, 6))); - } + { + auto sysTime = SysTime(Date(-4, 7, 6)); + sysTime.add!"years"(8, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 7, 6))); + sysTime.add!"years"(-8, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 7, 6))); + } - { - auto sysTime = SysTime(Date(-4, 2, 29)); - sysTime.add!"years"(5, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1, 2, 28))); - } + { + auto sysTime = SysTime(Date(-4, 2, 29)); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1, 2, 28))); + } - { - auto sysTime = SysTime(Date(4, 2, 29)); - sysTime.add!"years"(-5, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1, 2, 28))); - } + { + auto sysTime = SysTime(Date(4, 2, 29)); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1, 2, 28))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.add!"years"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.add!"years"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.add!"years"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } - { - auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.add!"years"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.add!"years"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } + { + auto sysTime = SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } - { - auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.add!"years"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.add!"years"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + { + auto sysTime = SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.add!"years"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.add!"years"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } - { - auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329)); - sysTime.add!"years"(-5); - _assertPred!"=="(sysTime, SysTime(DateTime(-1, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); - sysTime.add!"years"(5); - _assertPred!"=="(sysTime, SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); - } + { + auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329)); + sysTime.add!"years"(-5); + assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + sysTime.add!"years"(5); + assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + } - { - auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329)); - sysTime.add!"years"(-5, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-1, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); - sysTime.add!"years"(5, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); - } + { + auto sysTime = SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329)); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + } - { - auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329)); - sysTime.add!"years"(5, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); - sysTime.add!"years"(-5, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); - } + { + auto sysTime = SysTime(DateTime(-4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329)); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-4, 7, 6, 14, 7, 1), FracSec.from!"usecs"(54329))); + } - { - auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), FracSec.from!"msecs"(555)); - sysTime.add!"years"(5, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 2, 28, 5, 5, 5), FracSec.from!"msecs"(555))); - } + { + auto sysTime = SysTime(DateTime(-4, 2, 29, 5, 5, 5), FracSec.from!"msecs"(555)); + sysTime.add!"years"(5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 2, 28, 5, 5, 5), FracSec.from!"msecs"(555))); + } - { - auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), FracSec.from!"msecs"(555)); - sysTime.add!"years"(-5, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-1, 2, 28, 5, 5, 5), FracSec.from!"msecs"(555))); - } + { + auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), FracSec.from!"msecs"(555)); + sysTime.add!"years"(-5, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1, 2, 28, 5, 5, 5), FracSec.from!"msecs"(555))); + } + + { + auto sysTime = SysTime(DateTime(4, 2, 29, 5, 5, 5), FracSec.from!"msecs"(555)); + sysTime.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(6, 2, 28, 5, 5, 5), FracSec.from!"msecs"(555))); } } //Test add!"months"() with AllowDayOverlow.yes unittest { - version(testStdDateTime) + //Test A.D. { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(3); - _assertPred!"=="(sysTime, SysTime(Date(1999, 10, 6))); - sysTime.add!"months"(-4); - _assertPred!"=="(sysTime, SysTime(Date(1999, 6, 6))); - } + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(3); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.add!"months"(-4); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(6); - _assertPred!"=="(sysTime, SysTime(Date(2000, 1, 6))); - sysTime.add!"months"(-6); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 6))); - } + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(6); + assert(sysTime == SysTime(Date(2000, 1, 6))); + sysTime.add!"months"(-6); + assert(sysTime == SysTime(Date(1999, 7, 6))); + } - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(27); - _assertPred!"=="(sysTime, SysTime(Date(2001, 10, 6))); - sysTime.add!"months"(-28); - _assertPred!"=="(sysTime, SysTime(Date(1999, 6, 6))); - } + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(27); + assert(sysTime == SysTime(Date(2001, 10, 6))); + sysTime.add!"months"(-28); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.add!"months"(1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 1))); - } + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(1999, 7, 1))); + } - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.add!"months"(-1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 5, 1))); - } + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(Date(1999, 5, 1))); + } - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.add!"months"(12); - _assertPred!"=="(sysTime, SysTime(Date(2000, 2, 28))); - } + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.add!"months"(12); + assert(sysTime == SysTime(Date(2000, 2, 28))); + } - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.add!"months"(12); - _assertPred!"=="(sysTime, SysTime(Date(2001, 3, 1))); - } + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.add!"months"(12); + assert(sysTime == SysTime(Date(2001, 3, 1))); + } - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.add!"months"(1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 8, 31))); - sysTime.add!"months"(1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 10, 1))); - } + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(1999, 8, 31))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(1999, 10, 1))); + } - { - auto sysTime = SysTime(Date(1998, 8, 31)); - sysTime.add!"months"(13); - _assertPred!"=="(sysTime, SysTime(Date(1999, 10, 1))); - sysTime.add!"months"(-13); - _assertPred!"=="(sysTime, SysTime(Date(1998, 9, 1))); - } + { + auto sysTime = SysTime(Date(1998, 8, 31)); + sysTime.add!"months"(13); + assert(sysTime == SysTime(Date(1999, 10, 1))); + sysTime.add!"months"(-13); + assert(sysTime == SysTime(Date(1998, 9, 1))); + } - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.add!"months"(13); - _assertPred!"=="(sysTime, SysTime(Date(1999, 1, 31))); - sysTime.add!"months"(-13); - _assertPred!"=="(sysTime, SysTime(Date(1997, 12, 31))); - } + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.add!"months"(13); + assert(sysTime == SysTime(Date(1999, 1, 31))); + sysTime.add!"months"(-13); + assert(sysTime == SysTime(Date(1997, 12, 31))); + } - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.add!"months"(14); - _assertPred!"=="(sysTime, SysTime(Date(1999, 3, 3))); - sysTime.add!"months"(-14); - _assertPred!"=="(sysTime, SysTime(Date(1998, 1, 3))); - } + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(1999, 3, 3))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(1998, 1, 3))); + } - { - auto sysTime = SysTime(Date(1998, 12, 31)); - sysTime.add!"months"(14); - _assertPred!"=="(sysTime, SysTime(Date(2000, 3, 2))); - sysTime.add!"months"(-14); - _assertPred!"=="(sysTime, SysTime(Date(1999, 1, 2))); - } + { + auto sysTime = SysTime(Date(1998, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(2000, 3, 2))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(1999, 1, 2))); + } - { - auto sysTime = SysTime(Date(1999, 12, 31)); - sysTime.add!"months"(14); - _assertPred!"=="(sysTime, SysTime(Date(2001, 3, 3))); - sysTime.add!"months"(-14); - _assertPred!"=="(sysTime, SysTime(Date(2000, 1, 3))); - } + { + auto sysTime = SysTime(Date(1999, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(2001, 3, 3))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(2000, 1, 3))); + } - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); - sysTime.add!"months"(3); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); - sysTime.add!"months"(-4); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); - } + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); + sysTime.add!"months"(3); + assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + sysTime.add!"months"(-4); + assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + } - { - auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); - sysTime.add!"months"(14); - _assertPred!"=="(sysTime, SysTime(DateTime(2000, 3, 2, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - sysTime.add!"months"(-14); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 1, 2, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - } + { + auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(DateTime(2000, 3, 2, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(DateTime(1999, 1, 2, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } - { - auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); - sysTime.add!"months"(14); - _assertPred!"=="(sysTime, SysTime(DateTime(2001, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - sysTime.add!"months"(-14); - _assertPred!"=="(sysTime, SysTime(DateTime(2000, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - } + { + auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(DateTime(2001, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(DateTime(2000, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(3); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 10, 6))); - sysTime.add!"months"(-4); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 6, 6))); - } + //Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(3); + assert(sysTime == SysTime(Date(-1999, 10, 6))); + sysTime.add!"months"(-4); + assert(sysTime == SysTime(Date(-1999, 6, 6))); + } - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(6); - _assertPred!"=="(sysTime, SysTime(Date(-1998, 1, 6))); - sysTime.add!"months"(-6); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 6))); - } + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(6); + assert(sysTime == SysTime(Date(-1998, 1, 6))); + sysTime.add!"months"(-6); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + } - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(-27); - _assertPred!"=="(sysTime, SysTime(Date(-2001, 4, 6))); - sysTime.add!"months"(28); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 8, 6))); - } + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(-27); + assert(sysTime == SysTime(Date(-2001, 4, 6))); + sysTime.add!"months"(28); + assert(sysTime == SysTime(Date(-1999, 8, 6))); + } - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.add!"months"(1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 1))); - } + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(-1999, 7, 1))); + } - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.add!"months"(-1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 5, 1))); - } + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(Date(-1999, 5, 1))); + } - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.add!"months"(-12); - _assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 28))); - } + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.add!"months"(-12); + assert(sysTime == SysTime(Date(-2000, 2, 28))); + } - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.add!"months"(-12); - _assertPred!"=="(sysTime, SysTime(Date(-2001, 3, 1))); - } + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.add!"months"(-12); + assert(sysTime == SysTime(Date(-2001, 3, 1))); + } - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.add!"months"(1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 8, 31))); - sysTime.add!"months"(1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 10, 1))); - } + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(-1999, 8, 31))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(-1999, 10, 1))); + } - { - auto sysTime = SysTime(Date(-1998, 8, 31)); - sysTime.add!"months"(13); - _assertPred!"=="(sysTime, SysTime(Date(-1997, 10, 1))); - sysTime.add!"months"(-13); - _assertPred!"=="(sysTime, SysTime(Date(-1998, 9, 1))); - } + { + auto sysTime = SysTime(Date(-1998, 8, 31)); + sysTime.add!"months"(13); + assert(sysTime == SysTime(Date(-1997, 10, 1))); + sysTime.add!"months"(-13); + assert(sysTime == SysTime(Date(-1998, 9, 1))); + } - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.add!"months"(13); - _assertPred!"=="(sysTime, SysTime(Date(-1995, 1, 31))); - sysTime.add!"months"(-13); - _assertPred!"=="(sysTime, SysTime(Date(-1997, 12, 31))); - } + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.add!"months"(13); + assert(sysTime == SysTime(Date(-1995, 1, 31))); + sysTime.add!"months"(-13); + assert(sysTime == SysTime(Date(-1997, 12, 31))); + } - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.add!"months"(14); - _assertPred!"=="(sysTime, SysTime(Date(-1995, 3, 3))); - sysTime.add!"months"(-14); - _assertPred!"=="(sysTime, SysTime(Date(-1996, 1, 3))); - } + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(-1995, 3, 3))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(-1996, 1, 3))); + } - { - auto sysTime = SysTime(Date(-2002, 12, 31)); - sysTime.add!"months"(14); - _assertPred!"=="(sysTime, SysTime(Date(-2000, 3, 2))); - sysTime.add!"months"(-14); - _assertPred!"=="(sysTime, SysTime(Date(-2001, 1, 2))); - } + { + auto sysTime = SysTime(Date(-2002, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(-2000, 3, 2))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(-2001, 1, 2))); + } - { - auto sysTime = SysTime(Date(-2001, 12, 31)); - sysTime.add!"months"(14); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 3, 3))); - sysTime.add!"months"(-14); - _assertPred!"=="(sysTime, SysTime(Date(-2000, 1, 3))); - } + { + auto sysTime = SysTime(Date(-2001, 12, 31)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(Date(-1999, 3, 3))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(Date(-2000, 1, 3))); + } - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); - sysTime.add!"months"(3); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); - sysTime.add!"months"(-4); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); - } + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); + sysTime.add!"months"(3); + assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + sysTime.add!"months"(-4); + assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + } - { - auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); - sysTime.add!"months"(14); - _assertPred!"=="(sysTime, SysTime(DateTime(-2000, 3, 2, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - sysTime.add!"months"(-14); - _assertPred!"=="(sysTime, SysTime(DateTime(-2001, 1, 2, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - } + { + auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(DateTime(-2000, 3, 2, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(DateTime(-2001, 1, 2, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } - { - auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); - sysTime.add!"months"(14); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - sysTime.add!"months"(-14); - _assertPred!"=="(sysTime, SysTime(DateTime(-2000, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - } + { + auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.add!"months"(14); + assert(sysTime == SysTime(DateTime(-1999, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.add!"months"(-14); + assert(sysTime == SysTime(DateTime(-2000, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } - //Test Both - { - auto sysTime = SysTime(Date(1, 1, 1)); - sysTime.add!"months"(-1); - _assertPred!"=="(sysTime, SysTime(Date(0, 12, 1))); - sysTime.add!"months"(1); - _assertPred!"=="(sysTime, SysTime(Date(1, 1, 1))); - } + //Test Both + { + auto sysTime = SysTime(Date(1, 1, 1)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(Date(0, 12, 1))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(Date(1, 1, 1))); + } - { - auto sysTime = SysTime(Date(4, 1, 1)); - sysTime.add!"months"(-48); - _assertPred!"=="(sysTime, SysTime(Date(0, 1, 1))); - sysTime.add!"months"(48); - _assertPred!"=="(sysTime, SysTime(Date(4, 1, 1))); - } + { + auto sysTime = SysTime(Date(4, 1, 1)); + sysTime.add!"months"(-48); + assert(sysTime == SysTime(Date(0, 1, 1))); + sysTime.add!"months"(48); + assert(sysTime == SysTime(Date(4, 1, 1))); + } - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.add!"months"(-49); - _assertPred!"=="(sysTime, SysTime(Date(0, 3, 2))); - sysTime.add!"months"(49); - _assertPred!"=="(sysTime, SysTime(Date(4, 4, 2))); - } + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.add!"months"(-49); + assert(sysTime == SysTime(Date(0, 3, 2))); + sysTime.add!"months"(49); + assert(sysTime == SysTime(Date(4, 4, 2))); + } - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.add!"months"(-85); - _assertPred!"=="(sysTime, SysTime(Date(-3, 3, 3))); - sysTime.add!"months"(85); - _assertPred!"=="(sysTime, SysTime(Date(4, 4, 3))); - } + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.add!"months"(-85); + assert(sysTime == SysTime(Date(-3, 3, 3))); + sysTime.add!"months"(85); + assert(sysTime == SysTime(Date(4, 4, 3))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.add!"months"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.add!"months"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.add!"months"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.add!"months"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } - { - auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.add!"months"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.add!"months"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } + { + auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } - { - auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.add!"months"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.add!"months"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + { + auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.add!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17)); - sysTime.add!"months"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); - sysTime.add!"months"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17)); + sysTime.add!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); + sysTime.add!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); + } - { - auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); - sysTime.add!"months"(-85); - _assertPred!"=="(sysTime, SysTime(DateTime(-3, 3, 3, 12, 11, 10), FracSec.from!"msecs"(9))); - sysTime.add!"months"(85); - _assertPred!"=="(sysTime, SysTime(DateTime(4, 4, 3, 12, 11, 10), FracSec.from!"msecs"(9))); - } + { + auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.add!"months"(-85); + assert(sysTime == SysTime(DateTime(-3, 3, 3, 12, 11, 10), FracSec.from!"msecs"(9))); + sysTime.add!"months"(85); + assert(sysTime == SysTime(DateTime(4, 4, 3, 12, 11, 10), FracSec.from!"msecs"(9))); + } - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); - sysTime.add!"months"(85); - _assertPred!"=="(sysTime, SysTime(DateTime(4, 5, 1, 12, 11, 10), FracSec.from!"msecs"(9))); - sysTime.add!"months"(-85); - _assertPred!"=="(sysTime, SysTime(DateTime(-3, 4, 1, 12, 11, 10), FracSec.from!"msecs"(9))); - } + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.add!"months"(85); + assert(sysTime == SysTime(DateTime(4, 5, 1, 12, 11, 10), FracSec.from!"msecs"(9))); + sysTime.add!"months"(-85); + assert(sysTime == SysTime(DateTime(-3, 4, 1, 12, 11, 10), FracSec.from!"msecs"(9))); + } - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.add!"months"(4))); - //static assert(!__traits(compiles, ist.add!"months"(4))); + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.add!"months"(85).add!"months"(-83); + assert(sysTime == SysTime(DateTime(-3, 6, 1, 12, 11, 10), FracSec.from!"msecs"(9))); } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.add!"months"(4))); + //static assert(!__traits(compiles, ist.add!"months"(4))); } //Test add!"months"() with AllowDayOverlow.no unittest { - version(testStdDateTime) + //Test A.D. { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(3, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 10, 6))); - sysTime.add!"months"(-4, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 6, 6))); - } + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.add!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(6, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(2000, 1, 6))); - sysTime.add!"months"(-6, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 6))); - } + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2000, 1, 6))); + sysTime.add!"months"(-6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 7, 6))); + } - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.add!"months"(27, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(2001, 10, 6))); - sysTime.add!"months"(-28, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 6, 6))); - } + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.add!"months"(27, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2001, 10, 6))); + sysTime.add!"months"(-28, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 6, 30))); - } + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 30))); + } - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 4, 30))); - } + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 4, 30))); + } - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.add!"months"(12, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(2000, 2, 28))); - } + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.add!"months"(12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2000, 2, 28))); + } - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.add!"months"(12, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(2001, 2, 28))); - } + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.add!"months"(12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2001, 2, 28))); + } - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 8, 31))); - sysTime.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 9, 30))); - } + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 8, 31))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 9, 30))); + } - { - auto sysTime = SysTime(Date(1998, 8, 31)); - sysTime.add!"months"(13, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 9, 30))); - sysTime.add!"months"(-13, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1998, 8, 30))); - } + { + auto sysTime = SysTime(Date(1998, 8, 31)); + sysTime.add!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 9, 30))); + sysTime.add!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 8, 30))); + } - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.add!"months"(13, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 1, 31))); - sysTime.add!"months"(-13, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1997, 12, 31))); - } + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.add!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 1, 31))); + sysTime.add!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 12, 31))); + } - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 2, 28))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1997, 12, 28))); - } + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 2, 28))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 12, 28))); + } - { - auto sysTime = SysTime(Date(1998, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(2000, 2, 29))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1998, 12, 29))); - } + { + auto sysTime = SysTime(Date(1998, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2000, 2, 29))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 12, 29))); + } - { - auto sysTime = SysTime(Date(1999, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(2001, 2, 28))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 12, 28))); - } + { + auto sysTime = SysTime(Date(1999, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2001, 2, 28))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 12, 28))); + } - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); - sysTime.add!"months"(3, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); - sysTime.add!"months"(-4, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); - } + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); + sysTime.add!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + sysTime.add!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + } - { - auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); - sysTime.add!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(2000, 2, 29, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1998, 12, 29, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - } + { + auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(2000, 2, 29, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1998, 12, 29, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } - { - auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); - sysTime.add!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(2001, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - } + { + auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(2001, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(3, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 10, 6))); - sysTime.add!"months"(-4, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 6, 6))); - } + //Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 10, 6))); + sysTime.add!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 6, 6))); + } - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(6, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1998, 1, 6))); - sysTime.add!"months"(-6, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 6))); - } + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1998, 1, 6))); + sysTime.add!"months"(-6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + } - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.add!"months"(-27, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-2001, 4, 6))); - sysTime.add!"months"(28, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 8, 6))); - } + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.add!"months"(-27, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2001, 4, 6))); + sysTime.add!"months"(28, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 8, 6))); + } - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 6, 30))); - } + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 6, 30))); + } - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 4, 30))); - } + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 4, 30))); + } - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.add!"months"(-12, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 28))); - } + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.add!"months"(-12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2000, 2, 28))); + } - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.add!"months"(-12, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-2001, 2, 28))); - } + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.add!"months"(-12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2001, 2, 28))); + } - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 8, 31))); - sysTime.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 9, 30))); - } + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 8, 31))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 9, 30))); + } - { - auto sysTime = SysTime(Date(-1998, 8, 31)); - sysTime.add!"months"(13, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1997, 9, 30))); - sysTime.add!"months"(-13, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1998, 8, 30))); - } + { + auto sysTime = SysTime(Date(-1998, 8, 31)); + sysTime.add!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 9, 30))); + sysTime.add!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1998, 8, 30))); + } - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.add!"months"(13, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1995, 1, 31))); - sysTime.add!"months"(-13, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1997, 12, 31))); - } + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.add!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1995, 1, 31))); + sysTime.add!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 12, 31))); + } - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1995, 2, 28))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1997, 12, 28))); - } + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1995, 2, 28))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 12, 28))); + } - { - auto sysTime = SysTime(Date(-2002, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 29))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-2002, 12, 29))); - } + { + auto sysTime = SysTime(Date(-2002, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2000, 2, 29))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2002, 12, 29))); + } + + { + auto sysTime = SysTime(Date(-2001, 12, 31)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 2, 28))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2001, 12, 28))); + } - { - auto sysTime = SysTime(Date(-2001, 12, 31)); - sysTime.add!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 2, 28))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-2001, 12, 28))); - } + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); + sysTime.add!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + sysTime.add!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + } - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); - sysTime.add!"months"(3, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); - sysTime.add!"months"(-4, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); - } + { + auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2000, 2, 29, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2002, 12, 29, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } - { - auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); - sysTime.add!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-2000, 2, 29, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-2002, 12, 29, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - } + { + auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.add!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.add!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2001, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } - { - auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); - sysTime.add!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - sysTime.add!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-2001, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - } + //Test Both + { + auto sysTime = SysTime(Date(1, 1, 1)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(0, 12, 1))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1, 1, 1))); + } - //Test Both - { - auto sysTime = SysTime(Date(1, 1, 1)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(0, 12, 1))); - sysTime.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1, 1, 1))); - } + { + auto sysTime = SysTime(Date(4, 1, 1)); + sysTime.add!"months"(-48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(0, 1, 1))); + sysTime.add!"months"(48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 1, 1))); + } - { - auto sysTime = SysTime(Date(4, 1, 1)); - sysTime.add!"months"(-48, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(0, 1, 1))); - sysTime.add!"months"(48, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(4, 1, 1))); - } + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.add!"months"(-49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(0, 2, 29))); + sysTime.add!"months"(49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 3, 29))); + } - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.add!"months"(-49, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(0, 2, 29))); - sysTime.add!"months"(49, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(4, 3, 29))); - } + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.add!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-3, 2, 28))); + sysTime.add!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 3, 28))); + } - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.add!"months"(-85, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-3, 2, 28))); - sysTime.add!"months"(85, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(4, 3, 28))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + { + auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } - { - auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.add!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } + { + auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } - { - auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.add!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17)); + sysTime.add!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); + sysTime.add!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17)); - sysTime.add!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); - sysTime.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); - } + { + auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.add!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 2, 28, 12, 11, 10), FracSec.from!"msecs"(9))); + sysTime.add!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(4, 3, 28, 12, 11, 10), FracSec.from!"msecs"(9))); + } - { - auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); - sysTime.add!"months"(-85, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-3, 2, 28, 12, 11, 10), FracSec.from!"msecs"(9))); - sysTime.add!"months"(85, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(4, 3, 28, 12, 11, 10), FracSec.from!"msecs"(9))); - } + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.add!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(4, 4, 30, 12, 11, 10), FracSec.from!"msecs"(9))); + sysTime.add!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 3, 30, 12, 11, 10), FracSec.from!"msecs"(9))); + } - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); - sysTime.add!"months"(85, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(4, 4, 30, 12, 11, 10), FracSec.from!"msecs"(9))); - sysTime.add!"months"(-85, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-3, 3, 30, 12, 11, 10), FracSec.from!"msecs"(9))); - } + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 5, 30, 12, 11, 10), FracSec.from!"msecs"(9))); } } @@ -3600,87 +3459,54 @@ assert(st4 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); $(LREF SysTime). allowOverflow = Whether the days should be allowed to overflow, causing the month to increment. - - Examples: --------------------- -auto st1 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); -st1.roll!"months"(1); -assert(st1 == SysTime(DateTime(2010, 2, 1, 12, 33, 33))); - -auto st2 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); -st2.roll!"months"(-1); -assert(st2 == SysTime(DateTime(2010, 12, 1, 12, 33, 33))); - -auto st3 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); -st3.roll!"months"(1); -assert(st3 == SysTime(DateTime(1999, 3, 1, 12, 33, 33))); - -auto st4 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); -st4.roll!"months"(1, AllowDayOverflow.no); -assert(st4 == SysTime(DateTime(1999, 2, 28, 12, 33, 33))); - -auto st5 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); -st5.roll!"years"(1); -assert(st5 == SysTime(DateTime(2001, 3, 1, 12, 30, 33))); - -auto st6 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); -st6.roll!"years"(1, AllowDayOverflow.no); -assert(st6 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); --------------------- +/ - /+ref SysTime+/ void roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) nothrow + ref SysTime roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow if(units == "years") { - add!"years"(value, allowOverflow); + return add!"years"(value, allowOverflow); } + /// unittest { - version(testStdDateTime) - { - //Verify Examples. - auto st1 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); - st1.roll!"months"(1); - assert(st1 == SysTime(DateTime(2010, 2, 1, 12, 33, 33))); + auto st1 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); + st1.roll!"months"(1); + assert(st1 == SysTime(DateTime(2010, 2, 1, 12, 33, 33))); - auto st2 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); - st2.roll!"months"(-1); - assert(st2 == SysTime(DateTime(2010, 12, 1, 12, 33, 33))); + auto st2 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); + st2.roll!"months"(-1); + assert(st2 == SysTime(DateTime(2010, 12, 1, 12, 33, 33))); - auto st3 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); - st3.roll!"months"(1); - assert(st3 == SysTime(DateTime(1999, 3, 1, 12, 33, 33))); + auto st3 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); + st3.roll!"months"(1); + assert(st3 == SysTime(DateTime(1999, 3, 1, 12, 33, 33))); - auto st4 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); - st4.roll!"months"(1, AllowDayOverflow.no); - assert(st4 == SysTime(DateTime(1999, 2, 28, 12, 33, 33))); + auto st4 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); + st4.roll!"months"(1, AllowDayOverflow.no); + assert(st4 == SysTime(DateTime(1999, 2, 28, 12, 33, 33))); - auto st5 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); - st5.roll!"years"(1); - assert(st5 == SysTime(DateTime(2001, 3, 1, 12, 30, 33))); + auto st5 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); + st5.roll!"years"(1); + assert(st5 == SysTime(DateTime(2001, 3, 1, 12, 30, 33))); - auto st6 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); - st6.roll!"years"(1, AllowDayOverflow.no); - assert(st6 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); - } + auto st6 = SysTime(DateTime(2000, 2, 29, 12, 30, 33)); + st6.roll!"years"(1, AllowDayOverflow.no); + assert(st6 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); } unittest { - version(testStdDateTime) - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.roll!"years"(4))); - static assert(!__traits(compiles, cst.roll!"years"(4))); - //static assert(!__traits(compiles, ist.roll!"years"(4))); - } + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, st.roll!"years"(4))); + static assert(!__traits(compiles, cst.roll!"years"(4))); + //static assert(!__traits(compiles, ist.roll!"years"(4))); } - //Shares documentation with "years" version. - /+ref SysTime+/ void roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) nothrow + //Shares documentation with "years" overload. + ref SysTime roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe nothrow if(units == "months") { auto hnsecs = adjTime; @@ -3703,769 +3529,758 @@ assert(st6 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); } immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - adjTime = newDaysHNSecs + hnsecs; + return this; } //Test roll!"months"() with AllowDayOverlow.yes unittest { - version(testStdDateTime) + //Test A.D. { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(3); - _assertPred!"=="(sysTime, SysTime(Date(1999, 10, 6))); - sysTime.roll!"months"(-4); - _assertPred!"=="(sysTime, SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(6); - _assertPred!"=="(sysTime, SysTime(Date(1999, 1, 6))); - sysTime.roll!"months"(-6); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(27); - _assertPred!"=="(sysTime, SysTime(Date(1999, 10, 6))); - sysTime.roll!"months"(-28); - _assertPred!"=="(sysTime, SysTime(Date(1999, 6, 6))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.roll!"months"(1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.roll!"months"(-1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 5, 1))); - } - - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.roll!"months"(12); - _assertPred!"=="(sysTime, SysTime(Date(1999, 2, 28))); - } + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(3); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.roll!"months"(-4); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.roll!"months"(12); - _assertPred!"=="(sysTime, SysTime(Date(2000, 2, 29))); - } + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(6); + assert(sysTime == SysTime(Date(1999, 1, 6))); + sysTime.roll!"months"(-6); + assert(sysTime == SysTime(Date(1999, 7, 6))); + } - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.roll!"months"(1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 8, 31))); - sysTime.roll!"months"(1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 10, 1))); - } + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(27); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.roll!"months"(-28); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } - { - auto sysTime = SysTime(Date(1998, 8, 31)); - sysTime.roll!"months"(13); - _assertPred!"=="(sysTime, SysTime(Date(1998, 10, 1))); - sysTime.roll!"months"(-13); - _assertPred!"=="(sysTime, SysTime(Date(1998, 9, 1))); - } + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(1999, 7, 1))); + } - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.roll!"months"(13); - _assertPred!"=="(sysTime, SysTime(Date(1997, 1, 31))); - sysTime.roll!"months"(-13); - _assertPred!"=="(sysTime, SysTime(Date(1997, 12, 31))); - } + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(Date(1999, 5, 1))); + } - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.roll!"months"(14); - _assertPred!"=="(sysTime, SysTime(Date(1997, 3, 3))); - sysTime.roll!"months"(-14); - _assertPred!"=="(sysTime, SysTime(Date(1997, 1, 3))); - } + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.roll!"months"(12); + assert(sysTime == SysTime(Date(1999, 2, 28))); + } - { - auto sysTime = SysTime(Date(1998, 12, 31)); - sysTime.roll!"months"(14); - _assertPred!"=="(sysTime, SysTime(Date(1998, 3, 3))); - sysTime.roll!"months"(-14); - _assertPred!"=="(sysTime, SysTime(Date(1998, 1, 3))); - } + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.roll!"months"(12); + assert(sysTime == SysTime(Date(2000, 2, 29))); + } - { - auto sysTime = SysTime(Date(1999, 12, 31)); - sysTime.roll!"months"(14); - _assertPred!"=="(sysTime, SysTime(Date(1999, 3, 3))); - sysTime.roll!"months"(-14); - _assertPred!"=="(sysTime, SysTime(Date(1999, 1, 3))); - } + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(1999, 8, 31))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(1999, 10, 1))); + } - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); - sysTime.roll!"months"(3); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); - sysTime.roll!"months"(-4); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); - } + { + auto sysTime = SysTime(Date(1998, 8, 31)); + sysTime.roll!"months"(13); + assert(sysTime == SysTime(Date(1998, 10, 1))); + sysTime.roll!"months"(-13); + assert(sysTime == SysTime(Date(1998, 9, 1))); + } - { - auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); - sysTime.roll!"months"(14); - _assertPred!"=="(sysTime, SysTime(DateTime(1998, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - sysTime.roll!"months"(-14); - _assertPred!"=="(sysTime, SysTime(DateTime(1998, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - } + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.roll!"months"(13); + assert(sysTime == SysTime(Date(1997, 1, 31))); + sysTime.roll!"months"(-13); + assert(sysTime == SysTime(Date(1997, 12, 31))); + } - { - auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); - sysTime.roll!"months"(14); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - sysTime.roll!"months"(-14); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - } + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(1997, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(1997, 1, 3))); + } - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(3); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 10, 6))); - sysTime.roll!"months"(-4); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 6, 6))); - } + { + auto sysTime = SysTime(Date(1998, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(1998, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(1998, 1, 3))); + } - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(6); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 1, 6))); - sysTime.roll!"months"(-6); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 6))); - } + { + auto sysTime = SysTime(Date(1999, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(1999, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(1999, 1, 3))); + } - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(-27); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 4, 6))); - sysTime.roll!"months"(28); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 8, 6))); - } + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); + sysTime.roll!"months"(3); + assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + sysTime.roll!"months"(-4); + assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + } - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.roll!"months"(1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 1))); - } + { + auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(DateTime(1998, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(DateTime(1998, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.roll!"months"(-1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 5, 1))); - } + { + auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(DateTime(1999, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(DateTime(1999, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.roll!"months"(-12); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 2, 28))); - } + //Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(3); + assert(sysTime == SysTime(Date(-1999, 10, 6))); + sysTime.roll!"months"(-4); + assert(sysTime == SysTime(Date(-1999, 6, 6))); + } - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.roll!"months"(-12); - _assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 29))); - } + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(6); + assert(sysTime == SysTime(Date(-1999, 1, 6))); + sysTime.roll!"months"(-6); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + } - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.roll!"months"(1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 8, 31))); - sysTime.roll!"months"(1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 10, 1))); - } + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(-27); + assert(sysTime == SysTime(Date(-1999, 4, 6))); + sysTime.roll!"months"(28); + assert(sysTime == SysTime(Date(-1999, 8, 6))); + } - { - auto sysTime = SysTime(Date(-1998, 8, 31)); - sysTime.roll!"months"(13); - _assertPred!"=="(sysTime, SysTime(Date(-1998, 10, 1))); - sysTime.roll!"months"(-13); - _assertPred!"=="(sysTime, SysTime(Date(-1998, 9, 1))); - } + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(-1999, 7, 1))); + } - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.roll!"months"(13); - _assertPred!"=="(sysTime, SysTime(Date(-1997, 1, 31))); - sysTime.roll!"months"(-13); - _assertPred!"=="(sysTime, SysTime(Date(-1997, 12, 31))); - } + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(Date(-1999, 5, 1))); + } - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.roll!"months"(14); - _assertPred!"=="(sysTime, SysTime(Date(-1997, 3, 3))); - sysTime.roll!"months"(-14); - _assertPred!"=="(sysTime, SysTime(Date(-1997, 1, 3))); - } + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.roll!"months"(-12); + assert(sysTime == SysTime(Date(-1999, 2, 28))); + } - { - auto sysTime = SysTime(Date(-2002, 12, 31)); - sysTime.roll!"months"(14); - _assertPred!"=="(sysTime, SysTime(Date(-2002, 3, 3))); - sysTime.roll!"months"(-14); - _assertPred!"=="(sysTime, SysTime(Date(-2002, 1, 3))); - } + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.roll!"months"(-12); + assert(sysTime == SysTime(Date(-2000, 2, 29))); + } - { - auto sysTime = SysTime(Date(-2001, 12, 31)); - sysTime.roll!"months"(14); - _assertPred!"=="(sysTime, SysTime(Date(-2001, 3, 3))); - sysTime.roll!"months"(-14); - _assertPred!"=="(sysTime, SysTime(Date(-2001, 1, 3))); - } + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(-1999, 8, 31))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(-1999, 10, 1))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.roll!"months"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.roll!"months"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } + { + auto sysTime = SysTime(Date(-1998, 8, 31)); + sysTime.roll!"months"(13); + assert(sysTime == SysTime(Date(-1998, 10, 1))); + sysTime.roll!"months"(-13); + assert(sysTime == SysTime(Date(-1998, 9, 1))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.roll!"months"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.roll!"months"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.roll!"months"(13); + assert(sysTime == SysTime(Date(-1997, 1, 31))); + sysTime.roll!"months"(-13); + assert(sysTime == SysTime(Date(-1997, 12, 31))); + } - { - auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.roll!"months"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.roll!"months"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(-1997, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(-1997, 1, 3))); + } - { - auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.roll!"months"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.roll!"months"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + { + auto sysTime = SysTime(Date(-2002, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(-2002, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(-2002, 1, 3))); + } - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), FracSec.from!"hnsecs"(5007)); - sysTime.roll!"months"(3); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 10, 6, 12, 2, 7), FracSec.from!"hnsecs"(5007))); - sysTime.roll!"months"(-4); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 6, 6, 12, 2, 7), FracSec.from!"hnsecs"(5007))); - } + { + auto sysTime = SysTime(Date(-2001, 12, 31)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(Date(-2001, 3, 3))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(Date(-2001, 1, 3))); + } - { - auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); - sysTime.roll!"months"(14); - _assertPred!"=="(sysTime, SysTime(DateTime(-2002, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - sysTime.roll!"months"(-14); - _assertPred!"=="(sysTime, SysTime(DateTime(-2002, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } - { - auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); - sysTime.roll!"months"(14); - _assertPred!"=="(sysTime, SysTime(DateTime(-2001, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - sysTime.roll!"months"(-14); - _assertPred!"=="(sysTime, SysTime(DateTime(-2001, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(DateTime(1, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } - //Test Both - { - auto sysTime = SysTime(Date(1, 1, 1)); - sysTime.roll!"months"(-1); - _assertPred!"=="(sysTime, SysTime(Date(1, 12, 1))); - sysTime.roll!"months"(1); - _assertPred!"=="(sysTime, SysTime(Date(1, 1, 1))); - } + { + auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } - { - auto sysTime = SysTime(Date(4, 1, 1)); - sysTime.roll!"months"(-48); - _assertPred!"=="(sysTime, SysTime(Date(4, 1, 1))); - sysTime.roll!"months"(48); - _assertPred!"=="(sysTime, SysTime(Date(4, 1, 1))); - } + { + auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.roll!"months"(-49); - _assertPred!"=="(sysTime, SysTime(Date(4, 3, 2))); - sysTime.roll!"months"(49); - _assertPred!"=="(sysTime, SysTime(Date(4, 4, 2))); - } + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), FracSec.from!"hnsecs"(5007)); + sysTime.roll!"months"(3); + assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), FracSec.from!"hnsecs"(5007))); + sysTime.roll!"months"(-4); + assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), FracSec.from!"hnsecs"(5007))); + } - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.roll!"months"(-85); - _assertPred!"=="(sysTime, SysTime(Date(4, 3, 2))); - sysTime.roll!"months"(85); - _assertPred!"=="(sysTime, SysTime(Date(4, 4, 2))); - } + { + auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(DateTime(-2002, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(DateTime(-2002, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } - { - auto sysTime = SysTime(Date(-1, 1, 1)); - sysTime.roll!"months"(-1); - _assertPred!"=="(sysTime, SysTime(Date(-1, 12, 1))); - sysTime.roll!"months"(1); - _assertPred!"=="(sysTime, SysTime(Date(-1, 1, 1))); - } + { + auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.roll!"months"(14); + assert(sysTime == SysTime(DateTime(-2001, 3, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.roll!"months"(-14); + assert(sysTime == SysTime(DateTime(-2001, 1, 3, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } - { - auto sysTime = SysTime(Date(-4, 1, 1)); - sysTime.roll!"months"(-48); - _assertPred!"=="(sysTime, SysTime(Date(-4, 1, 1))); - sysTime.roll!"months"(48); - _assertPred!"=="(sysTime, SysTime(Date(-4, 1, 1))); - } + //Test Both + { + auto sysTime = SysTime(Date(1, 1, 1)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(Date(1, 12, 1))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(1, 1, 1))); + } - { - auto sysTime = SysTime(Date(-4, 3, 31)); - sysTime.roll!"months"(-49); - _assertPred!"=="(sysTime, SysTime(Date(-4, 3, 2))); - sysTime.roll!"months"(49); - _assertPred!"=="(sysTime, SysTime(Date(-4, 4, 2))); - } + { + auto sysTime = SysTime(Date(4, 1, 1)); + sysTime.roll!"months"(-48); + assert(sysTime == SysTime(Date(4, 1, 1))); + sysTime.roll!"months"(48); + assert(sysTime == SysTime(Date(4, 1, 1))); + } - { - auto sysTime = SysTime(Date(-4, 3, 31)); - sysTime.roll!"months"(-85); - _assertPred!"=="(sysTime, SysTime(Date(-4, 3, 2))); - sysTime.roll!"months"(85); - _assertPred!"=="(sysTime, SysTime(Date(-4, 4, 2))); - } + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.roll!"months"(-49); + assert(sysTime == SysTime(Date(4, 3, 2))); + sysTime.roll!"months"(49); + assert(sysTime == SysTime(Date(4, 4, 2))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17)); - sysTime.roll!"months"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 12, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); - sysTime.roll!"months"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); - } + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.roll!"months"(-85); + assert(sysTime == SysTime(Date(4, 3, 2))); + sysTime.roll!"months"(85); + assert(sysTime == SysTime(Date(4, 4, 2))); + } - { - auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); - sysTime.roll!"months"(-85); - _assertPred!"=="(sysTime, SysTime(DateTime(4, 3, 2, 12, 11, 10), FracSec.from!"msecs"(9))); - sysTime.roll!"months"(85); - _assertPred!"=="(sysTime, SysTime(DateTime(4, 4, 2, 12, 11, 10), FracSec.from!"msecs"(9))); - } + { + auto sysTime = SysTime(Date(-1, 1, 1)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(Date(-1, 12, 1))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(Date(-1, 1, 1))); + } - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); - sysTime.roll!"months"(85); - _assertPred!"=="(sysTime, SysTime(DateTime(-3, 5, 1, 12, 11, 10), FracSec.from!"msecs"(9))); - sysTime.roll!"months"(-85); - _assertPred!"=="(sysTime, SysTime(DateTime(-3, 4, 1, 12, 11, 10), FracSec.from!"msecs"(9))); - } + { + auto sysTime = SysTime(Date(-4, 1, 1)); + sysTime.roll!"months"(-48); + assert(sysTime == SysTime(Date(-4, 1, 1))); + sysTime.roll!"months"(48); + assert(sysTime == SysTime(Date(-4, 1, 1))); + } - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"months"(4))); - //static assert(!__traits(compiles, ist.roll!"months"(4))); + { + auto sysTime = SysTime(Date(-4, 3, 31)); + sysTime.roll!"months"(-49); + assert(sysTime == SysTime(Date(-4, 3, 2))); + sysTime.roll!"months"(49); + assert(sysTime == SysTime(Date(-4, 4, 2))); + } + + { + auto sysTime = SysTime(Date(-4, 3, 31)); + sysTime.roll!"months"(-85); + assert(sysTime == SysTime(Date(-4, 3, 2))); + sysTime.roll!"months"(85); + assert(sysTime == SysTime(Date(-4, 4, 2))); + } - //Verify Examples. - auto st1 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); - st1.roll!"months"(1); - assert(st1 == SysTime(DateTime(2010, 2, 1, 12, 33, 33))); + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17)); + sysTime.roll!"months"(-1); + assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); + sysTime.roll!"months"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); + } - auto st2 = SysTime(DateTime(2010, 1, 1, 12, 33, 33)); - st2.roll!"months"(-1); - assert(st2 == SysTime(DateTime(2010, 12, 1, 12, 33, 33))); + { + auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.roll!"months"(-85); + assert(sysTime == SysTime(DateTime(4, 3, 2, 12, 11, 10), FracSec.from!"msecs"(9))); + sysTime.roll!"months"(85); + assert(sysTime == SysTime(DateTime(4, 4, 2, 12, 11, 10), FracSec.from!"msecs"(9))); + } - auto st3 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); - st3.roll!"months"(1); - assert(st3 == SysTime(DateTime(1999, 3, 1, 12, 33, 33))); + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.roll!"months"(85); + assert(sysTime == SysTime(DateTime(-3, 5, 1, 12, 11, 10), FracSec.from!"msecs"(9))); + sysTime.roll!"months"(-85); + assert(sysTime == SysTime(DateTime(-3, 4, 1, 12, 11, 10), FracSec.from!"msecs"(9))); + } - auto st4 = SysTime(DateTime(1999, 1, 29, 12, 33, 33)); - st4.roll!"months"(1, AllowDayOverflow.no); - assert(st4 == SysTime(DateTime(1999, 2, 28, 12, 33, 33))); + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.roll!"months"(85).roll!"months"(-83); + assert(sysTime == SysTime(DateTime(-3, 6, 1, 12, 11, 10), FracSec.from!"msecs"(9))); } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"months"(4))); + //static assert(!__traits(compiles, ist.roll!"months"(4))); } //Test roll!"months"() with AllowDayOverlow.no unittest { - version(testStdDateTime) + //Test A.D. { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(3, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 10, 6))); - sysTime.roll!"months"(-4, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 6, 6))); - } + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.roll!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(6, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 1, 6))); - sysTime.roll!"months"(-6, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 6))); - } + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 1, 6))); + sysTime.roll!"months"(-6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 7, 6))); + } - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"months"(27, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 10, 6))); - sysTime.roll!"months"(-28, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 6, 6))); - } + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"months"(27, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 10, 6))); + sysTime.roll!"months"(-28, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 6))); + } - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 6, 30))); - } + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 6, 30))); + } - { - auto sysTime = SysTime(Date(1999, 5, 31)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 4, 30))); - } + { + auto sysTime = SysTime(Date(1999, 5, 31)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 4, 30))); + } - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.roll!"months"(12, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 2, 28))); - } + { + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.roll!"months"(12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 2, 28))); + } - { - auto sysTime = SysTime(Date(2000, 2, 29)); - sysTime.roll!"months"(12, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(2000, 2, 29))); - } + { + auto sysTime = SysTime(Date(2000, 2, 29)); + sysTime.roll!"months"(12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(2000, 2, 29))); + } - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 8, 31))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 9, 30))); - } + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 8, 31))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 9, 30))); + } - { - auto sysTime = SysTime(Date(1998, 8, 31)); - sysTime.roll!"months"(13, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1998, 9, 30))); - sysTime.roll!"months"(-13, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1998, 8, 30))); - } + { + auto sysTime = SysTime(Date(1998, 8, 31)); + sysTime.roll!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 9, 30))); + sysTime.roll!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 8, 30))); + } - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.roll!"months"(13, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1997, 1, 31))); - sysTime.roll!"months"(-13, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1997, 12, 31))); - } + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.roll!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 1, 31))); + sysTime.roll!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 12, 31))); + } - { - auto sysTime = SysTime(Date(1997, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1997, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1997, 12, 28))); - } + { + auto sysTime = SysTime(Date(1997, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1997, 12, 28))); + } - { - auto sysTime = SysTime(Date(1998, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1998, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1998, 12, 28))); - } + { + auto sysTime = SysTime(Date(1998, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1998, 12, 28))); + } - { - auto sysTime = SysTime(Date(1999, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1999, 12, 28))); - } + { + auto sysTime = SysTime(Date(1999, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1999, 12, 28))); + } - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); - sysTime.roll!"months"(3, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); - sysTime.roll!"months"(-4, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); - } + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); + sysTime.roll!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + sysTime.roll!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + } - { - auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1998, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1998, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - } + { + auto sysTime = SysTime(DateTime(1998, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1998, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1998, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } - { - auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - } + { + auto sysTime = SysTime(DateTime(1999, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1999, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(3, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 10, 6))); - sysTime.roll!"months"(-4, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 6, 6))); - } + //Test B.C. + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 10, 6))); + sysTime.roll!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 6, 6))); + } - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(6, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 1, 6))); - sysTime.roll!"months"(-6, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 6))); - } + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 1, 6))); + sysTime.roll!"months"(-6, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + } - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"months"(-27, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 4, 6))); - sysTime.roll!"months"(28, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 8, 6))); - } + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"months"(-27, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 4, 6))); + sysTime.roll!"months"(28, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 8, 6))); + } - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 6, 30))); - } + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 6, 30))); + } - { - auto sysTime = SysTime(Date(-1999, 5, 31)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 4, 30))); - } + { + auto sysTime = SysTime(Date(-1999, 5, 31)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 4, 30))); + } - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.roll!"months"(-12, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 2, 28))); - } + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.roll!"months"(-12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 2, 28))); + } - { - auto sysTime = SysTime(Date(-2000, 2, 29)); - sysTime.roll!"months"(-12, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 29))); - } + { + auto sysTime = SysTime(Date(-2000, 2, 29)); + sysTime.roll!"months"(-12, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2000, 2, 29))); + } - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 8, 31))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 9, 30))); - } + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 8, 31))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1999, 9, 30))); + } - { - auto sysTime = SysTime(Date(-1998, 8, 31)); - sysTime.roll!"months"(13, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1998, 9, 30))); - sysTime.roll!"months"(-13, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1998, 8, 30))); - } + { + auto sysTime = SysTime(Date(-1998, 8, 31)); + sysTime.roll!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1998, 9, 30))); + sysTime.roll!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1998, 8, 30))); + } - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.roll!"months"(13, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1997, 1, 31))); - sysTime.roll!"months"(-13, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1997, 12, 31))); - } + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.roll!"months"(13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 1, 31))); + sysTime.roll!"months"(-13, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 12, 31))); + } - { - auto sysTime = SysTime(Date(-1997, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1997, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1997, 12, 28))); - } + { + auto sysTime = SysTime(Date(-1997, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1997, 12, 28))); + } - { - auto sysTime = SysTime(Date(-2002, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-2002, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-2002, 12, 28))); - } + { + auto sysTime = SysTime(Date(-2002, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2002, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2002, 12, 28))); + } - { - auto sysTime = SysTime(Date(-2001, 12, 31)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-2001, 2, 28))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-2001, 12, 28))); - } + { + auto sysTime = SysTime(Date(-2001, 12, 31)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2001, 2, 28))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-2001, 12, 28))); + } - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); - sysTime.roll!"months"(3, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); - sysTime.roll!"months"(-4, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); - } + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 12, 2, 7), FracSec.from!"usecs"(5007)); + sysTime.roll!"months"(3, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 10, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + sysTime.roll!"months"(-4, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-1999, 6, 6, 12, 2, 7), FracSec.from!"usecs"(5007))); + } - { - auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-2002, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-2002, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - } + { + auto sysTime = SysTime(DateTime(-2002, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2002, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2002, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } - { - auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); - sysTime.roll!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-2001, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - sysTime.roll!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-2001, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); - } + { + auto sysTime = SysTime(DateTime(-2001, 12, 31, 7, 7, 7), FracSec.from!"hnsecs"(422202)); + sysTime.roll!"months"(14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2001, 2, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + sysTime.roll!"months"(-14, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-2001, 12, 28, 7, 7, 7), FracSec.from!"hnsecs"(422202))); + } - //Test Both - { - auto sysTime = SysTime(Date(1, 1, 1)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1, 12, 1))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(1, 1, 1))); - } + //Test Both + { + auto sysTime = SysTime(Date(1, 1, 1)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1, 12, 1))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(1, 1, 1))); + } - { - auto sysTime = SysTime(Date(4, 1, 1)); - sysTime.roll!"months"(-48, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(4, 1, 1))); - sysTime.roll!"months"(48, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(4, 1, 1))); - } + { + auto sysTime = SysTime(Date(4, 1, 1)); + sysTime.roll!"months"(-48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 1, 1))); + sysTime.roll!"months"(48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 1, 1))); + } - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.roll!"months"(-49, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(4, 2, 29))); - sysTime.roll!"months"(49, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(4, 3, 29))); - } + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.roll!"months"(-49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 2, 29))); + sysTime.roll!"months"(49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 3, 29))); + } - { - auto sysTime = SysTime(Date(4, 3, 31)); - sysTime.roll!"months"(-85, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(4, 2, 29))); - sysTime.roll!"months"(85, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(4, 3, 29))); - } + { + auto sysTime = SysTime(Date(4, 3, 31)); + sysTime.roll!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 2, 29))); + sysTime.roll!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(4, 3, 29))); + } - { - auto sysTime = SysTime(Date(-1, 1, 1)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1, 12, 1))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-1, 1, 1))); - } + { + auto sysTime = SysTime(Date(-1, 1, 1)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1, 12, 1))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-1, 1, 1))); + } - { - auto sysTime = SysTime(Date(-4, 1, 1)); - sysTime.roll!"months"(-48, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-4, 1, 1))); - sysTime.roll!"months"(48, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-4, 1, 1))); - } + { + auto sysTime = SysTime(Date(-4, 1, 1)); + sysTime.roll!"months"(-48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 1, 1))); + sysTime.roll!"months"(48, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 1, 1))); + } - { - auto sysTime = SysTime(Date(-4, 3, 31)); - sysTime.roll!"months"(-49, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-4, 2, 29))); - sysTime.roll!"months"(49, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-4, 3, 29))); - } + { + auto sysTime = SysTime(Date(-4, 3, 31)); + sysTime.roll!"months"(-49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 2, 29))); + sysTime.roll!"months"(49, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 3, 29))); + } - { - auto sysTime = SysTime(Date(-4, 3, 31)); - sysTime.roll!"months"(-85, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-4, 2, 29))); - sysTime.roll!"months"(85, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(Date(-4, 3, 29))); - } + { + auto sysTime = SysTime(Date(-4, 3, 31)); + sysTime.roll!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 2, 29))); + sysTime.roll!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(Date(-4, 3, 29))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } - { - auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } + { + auto sysTime = SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } - { - auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + { + auto sysTime = SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17)); - sysTime.roll!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 12, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); - sysTime.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17)); + sysTime.roll!"months"(-1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 12, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); + sysTime.roll!"months"(1, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 7, 9), FracSec.from!"hnsecs"(17))); + } - { - auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); - sysTime.roll!"months"(-85, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(4, 2, 29, 12, 11, 10), FracSec.from!"msecs"(9))); - sysTime.roll!"months"(85, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(4, 3, 29, 12, 11, 10), FracSec.from!"msecs"(9))); - } + { + auto sysTime = SysTime(DateTime(4, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.roll!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(4, 2, 29, 12, 11, 10), FracSec.from!"msecs"(9))); + sysTime.roll!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(4, 3, 29, 12, 11, 10), FracSec.from!"msecs"(9))); + } - { - auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); - sysTime.roll!"months"(85, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-3, 4, 30, 12, 11, 10), FracSec.from!"msecs"(9))); - sysTime.roll!"months"(-85, AllowDayOverflow.no); - _assertPred!"=="(sysTime, SysTime(DateTime(-3, 3, 30, 12, 11, 10), FracSec.from!"msecs"(9))); - } + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.roll!"months"(85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 4, 30, 12, 11, 10), FracSec.from!"msecs"(9))); + sysTime.roll!"months"(-85, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 3, 30, 12, 11, 10), FracSec.from!"msecs"(9))); + } + + { + auto sysTime = SysTime(DateTime(-3, 3, 31, 12, 11, 10), FracSec.from!"msecs"(9)); + sysTime.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no); + assert(sysTime == SysTime(DateTime(-3, 5, 30, 12, 11, 10), FracSec.from!"msecs"(9))); } } @@ -4489,33 +4304,8 @@ assert(st6 == SysTime(DateTime(2001, 2, 28, 12, 30, 33))); Params: units = The units to add. value = The number of $(D_PARAM units) to add to this $(LREF SysTime). - - Examples: --------------------- -auto st1 = SysTime(DateTime(2010, 1, 1, 11, 23, 12)); -st1.roll!"days"(1); -assert(st1 == SysTime(DateTime(2010, 1, 2, 11, 23, 12))); -st1.roll!"days"(365); -assert(st1 == SysTime(DateTime(2010, 1, 26, 11, 23, 12))); -st1.roll!"days"(-32); -assert(st1 == SysTime(DateTime(2010, 1, 25, 11, 23, 12))); - -auto st2 = SysTime(DateTime(2010, 7, 4, 12, 0, 0)); -st2.roll!"hours"(1); -assert(st2 == SysTime(DateTime(2010, 7, 4, 13, 0, 0))); - -auto st3 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); -st3.roll!"seconds"(-1); -assert(st3 == SysTime(DateTime(2010, 1, 1, 0, 0, 59))); - -auto st4 = SysTime(DateTime(2010, 1, 1, 0, 0, 0), - FracSec.from!"usecs"(2_400)); -st4.roll!"usecs"(-1_200_000); -assert(st4 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), - FracSec.from!"usecs"(802_400))); --------------------- +/ - /+ref SysTime+/ void roll(string units)(long value) nothrow + ref SysTime roll(string units)(long value) @safe nothrow if(units == "days") { auto hnsecs = adjTime; @@ -4538,322 +4328,343 @@ assert(st4 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), } immutable newDaysHNSecs = convert!("days", "hnsecs")(gdays); - adjTime = newDaysHNSecs + hnsecs; + return this; } - //Verify Examples. + /// unittest { - version(testStdDateTime) - { - auto st1 = SysTime(DateTime(2010, 1, 1, 11, 23, 12)); - st1.roll!"days"(1); - assert(st1 == SysTime(DateTime(2010, 1, 2, 11, 23, 12))); - st1.roll!"days"(365); - assert(st1 == SysTime(DateTime(2010, 1, 26, 11, 23, 12))); - st1.roll!"days"(-32); - assert(st1 == SysTime(DateTime(2010, 1, 25, 11, 23, 12))); + auto st1 = SysTime(DateTime(2010, 1, 1, 11, 23, 12)); + st1.roll!"days"(1); + assert(st1 == SysTime(DateTime(2010, 1, 2, 11, 23, 12))); + st1.roll!"days"(365); + assert(st1 == SysTime(DateTime(2010, 1, 26, 11, 23, 12))); + st1.roll!"days"(-32); + assert(st1 == SysTime(DateTime(2010, 1, 25, 11, 23, 12))); - auto st2 = SysTime(DateTime(2010, 7, 4, 12, 0, 0)); - st2.roll!"hours"(1); - assert(st2 == SysTime(DateTime(2010, 7, 4, 13, 0, 0))); + auto st2 = SysTime(DateTime(2010, 7, 4, 12, 0, 0)); + st2.roll!"hours"(1); + assert(st2 == SysTime(DateTime(2010, 7, 4, 13, 0, 0))); - auto st3 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); - st3.roll!"seconds"(-1); - assert(st3 == SysTime(DateTime(2010, 1, 1, 0, 0, 59))); + auto st3 = SysTime(DateTime(2010, 2, 12, 12, 0, 0)); + st3.roll!"hours"(-1); + assert(st3 == SysTime(DateTime(2010, 2, 12, 11, 0, 0))); - auto st4 = SysTime(DateTime(2010, 1, 1, 0, 0, 0), - FracSec.from!"usecs"(2_400)); - st4.roll!"usecs"(-1_200_000); - assert(st4 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), - FracSec.from!"usecs"(802_400))); - } + auto st4 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); + st4.roll!"minutes"(1); + assert(st4 == SysTime(DateTime(2009, 12, 31, 0, 1, 0))); + + auto st5 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); + st5.roll!"minutes"(-1); + assert(st5 == SysTime(DateTime(2010, 1, 1, 0, 59, 0))); + + auto st6 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); + st6.roll!"seconds"(1); + assert(st6 == SysTime(DateTime(2009, 12, 31, 0, 0, 1))); + + auto st7 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); + st7.roll!"seconds"(-1); + assert(st7 == SysTime(DateTime(2010, 1, 1, 0, 0, 59))); + + auto st8 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); + st8.roll!"msecs"(1); + assert(st8 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), + FracSec.from!"msecs"(1))); + + auto st9 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); + st9.roll!"msecs"(-1); + assert(st9 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), + FracSec.from!"msecs"(999))); + + auto st10 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); + st10.roll!"hnsecs"(1); + assert(st10 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), + FracSec.from!"hnsecs"(1))); + + auto st11 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); + st11.roll!"hnsecs"(-1); + assert(st11 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), + FracSec.from!"hnsecs"(9_999_999))); } unittest { - version(testStdDateTime) + //Test A.D. { - //Test A.D. - { - auto sysTime = SysTime(Date(1999, 2, 28)); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 2, 1))); - sysTime.roll!"days"(-1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 2, 28))); - } - - { - auto sysTime = SysTime(Date(2000, 2, 28)); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(Date(2000, 2, 29))); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(Date(2000, 2, 1))); - sysTime.roll!"days"(-1); - _assertPred!"=="(sysTime, SysTime(Date(2000, 2, 29))); - } - - { - auto sysTime = SysTime(Date(1999, 6, 30)); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 6, 1))); - sysTime.roll!"days"(-1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 6, 30))); - } + auto sysTime = SysTime(Date(1999, 2, 28)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(1999, 2, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(1999, 2, 28))); + } - { - auto sysTime = SysTime(Date(1999, 7, 31)); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 1))); - sysTime.roll!"days"(-1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 31))); - } + { + auto sysTime = SysTime(Date(2000, 2, 28)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(2000, 2, 29))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(2000, 2, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(2000, 2, 29))); + } - { - auto sysTime = SysTime(Date(1999, 1, 1)); - sysTime.roll!"days"(-1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 1, 31))); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(Date(1999, 1, 1))); - } + { + auto sysTime = SysTime(Date(1999, 6, 30)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(1999, 6, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(1999, 6, 30))); + } - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"days"(9); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 15))); - sysTime.roll!"days"(-11); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 4))); - sysTime.roll!"days"(30); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 3))); - sysTime.roll!"days"(-3); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 31))); - } + { + auto sysTime = SysTime(Date(1999, 7, 31)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(1999, 7, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(1999, 7, 31))); + } - { - auto sysTime = SysTime(Date(1999, 7, 6)); - sysTime.roll!"days"(365); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 30))); - sysTime.roll!"days"(-365); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 6))); - sysTime.roll!"days"(366); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 31))); - sysTime.roll!"days"(730); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 17))); - sysTime.roll!"days"(-1096); - _assertPred!"=="(sysTime, SysTime(Date(1999, 7, 6))); - } + { + auto sysTime = SysTime(Date(1999, 1, 1)); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(1999, 1, 31))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(1999, 1, 1))); + } - { - auto sysTime = SysTime(Date(1999, 2, 6)); - sysTime.roll!"days"(365); - _assertPred!"=="(sysTime, SysTime(Date(1999, 2, 7))); - sysTime.roll!"days"(-365); - _assertPred!"=="(sysTime, SysTime(Date(1999, 2, 6))); - sysTime.roll!"days"(366); - _assertPred!"=="(sysTime, SysTime(Date(1999, 2, 8))); - sysTime.roll!"days"(730); - _assertPred!"=="(sysTime, SysTime(Date(1999, 2, 10))); - sysTime.roll!"days"(-1096); - _assertPred!"=="(sysTime, SysTime(Date(1999, 2, 6))); - } + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"days"(9); + assert(sysTime == SysTime(Date(1999, 7, 15))); + sysTime.roll!"days"(-11); + assert(sysTime == SysTime(Date(1999, 7, 4))); + sysTime.roll!"days"(30); + assert(sysTime == SysTime(Date(1999, 7, 3))); + sysTime.roll!"days"(-3); + assert(sysTime == SysTime(Date(1999, 7, 31))); + } - { - auto sysTime = SysTime(DateTime(1999, 2, 28, 7, 9, 2), FracSec.from!"usecs"(234578)); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 2, 1, 7, 9, 2), FracSec.from!"usecs"(234578))); - sysTime.roll!"days"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 2, 28, 7, 9, 2), FracSec.from!"usecs"(234578))); - } + { + auto sysTime = SysTime(Date(1999, 7, 6)); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(Date(1999, 7, 30))); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(Date(1999, 7, 6))); + sysTime.roll!"days"(366); + assert(sysTime == SysTime(Date(1999, 7, 31))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(Date(1999, 7, 17))); + sysTime.roll!"days"(-1096); + assert(sysTime == SysTime(Date(1999, 7, 6))); + } - { - auto sysTime = SysTime(DateTime(1999, 7, 6, 7, 9, 2), FracSec.from!"usecs"(234578)); - sysTime.roll!"days"(9); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 7, 15, 7, 9, 2), FracSec.from!"usecs"(234578))); - sysTime.roll!"days"(-11); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 7, 4, 7, 9, 2), FracSec.from!"usecs"(234578))); - sysTime.roll!"days"(30); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 7, 3, 7, 9, 2), FracSec.from!"usecs"(234578))); - sysTime.roll!"days"(-3); - _assertPred!"=="(sysTime, SysTime(DateTime(1999, 7, 31, 7, 9, 2), FracSec.from!"usecs"(234578))); - } + { + auto sysTime = SysTime(Date(1999, 2, 6)); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(Date(1999, 2, 7))); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(Date(1999, 2, 6))); + sysTime.roll!"days"(366); + assert(sysTime == SysTime(Date(1999, 2, 8))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(Date(1999, 2, 10))); + sysTime.roll!"days"(-1096); + assert(sysTime == SysTime(Date(1999, 2, 6))); + } - //Test B.C. - { - auto sysTime = SysTime(Date(-1999, 2, 28)); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 2, 1))); - sysTime.roll!"days"(-1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 2, 28))); - } + { + auto sysTime = SysTime(DateTime(1999, 2, 28, 7, 9, 2), FracSec.from!"usecs"(234578)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(1999, 2, 1, 7, 9, 2), FracSec.from!"usecs"(234578))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(1999, 2, 28, 7, 9, 2), FracSec.from!"usecs"(234578))); + } - { - auto sysTime = SysTime(Date(-2000, 2, 28)); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 29))); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 1))); - sysTime.roll!"days"(-1); - _assertPred!"=="(sysTime, SysTime(Date(-2000, 2, 29))); - } + { + auto sysTime = SysTime(DateTime(1999, 7, 6, 7, 9, 2), FracSec.from!"usecs"(234578)); + sysTime.roll!"days"(9); + assert(sysTime == SysTime(DateTime(1999, 7, 15, 7, 9, 2), FracSec.from!"usecs"(234578))); + sysTime.roll!"days"(-11); + assert(sysTime == SysTime(DateTime(1999, 7, 4, 7, 9, 2), FracSec.from!"usecs"(234578))); + sysTime.roll!"days"(30); + assert(sysTime == SysTime(DateTime(1999, 7, 3, 7, 9, 2), FracSec.from!"usecs"(234578))); + sysTime.roll!"days"(-3); + assert(sysTime == SysTime(DateTime(1999, 7, 31, 7, 9, 2), FracSec.from!"usecs"(234578))); + } - { - auto sysTime = SysTime(Date(-1999, 6, 30)); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 6, 1))); - sysTime.roll!"days"(-1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 6, 30))); - } + //Test B.C. + { + auto sysTime = SysTime(Date(-1999, 2, 28)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-1999, 2, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(-1999, 2, 28))); + } - { - auto sysTime = SysTime(Date(-1999, 7, 31)); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 1))); - sysTime.roll!"days"(-1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 31))); - } + { + auto sysTime = SysTime(Date(-2000, 2, 28)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-2000, 2, 29))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-2000, 2, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(-2000, 2, 29))); + } - { - auto sysTime = SysTime(Date(-1999, 1, 1)); - sysTime.roll!"days"(-1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 1, 31))); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 1, 1))); - } + { + auto sysTime = SysTime(Date(-1999, 6, 30)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-1999, 6, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(-1999, 6, 30))); + } - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"days"(9); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 15))); - sysTime.roll!"days"(-11); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 4))); - sysTime.roll!"days"(30); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 3))); - sysTime.roll!"days"(-3); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 31))); - } + { + auto sysTime = SysTime(Date(-1999, 7, 31)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-1999, 7, 1))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(-1999, 7, 31))); + } - { - auto sysTime = SysTime(Date(-1999, 7, 6)); - sysTime.roll!"days"(365); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 30))); - sysTime.roll!"days"(-365); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 6))); - sysTime.roll!"days"(366); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 31))); - sysTime.roll!"days"(730); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 17))); - sysTime.roll!"days"(-1096); - _assertPred!"=="(sysTime, SysTime(Date(-1999, 7, 6))); - } + { + auto sysTime = SysTime(Date(-1999, 1, 1)); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(Date(-1999, 1, 31))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(Date(-1999, 1, 1))); + } - { - auto sysTime = SysTime(DateTime(-1999, 2, 28, 7, 9, 2), FracSec.from!"usecs"(234578)); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 2, 1, 7, 9, 2), FracSec.from!"usecs"(234578))); - sysTime.roll!"days"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 2, 28, 7, 9, 2), FracSec.from!"usecs"(234578))); - } + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"days"(9); + assert(sysTime == SysTime(Date(-1999, 7, 15))); + sysTime.roll!"days"(-11); + assert(sysTime == SysTime(Date(-1999, 7, 4))); + sysTime.roll!"days"(30); + assert(sysTime == SysTime(Date(-1999, 7, 3))); + sysTime.roll!"days"(-3); + assert(sysTime == SysTime(Date(-1999, 7, 31))); + } - { - auto sysTime = SysTime(DateTime(-1999, 7, 6, 7, 9, 2), FracSec.from!"usecs"(234578)); - sysTime.roll!"days"(9); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 7, 15, 7, 9, 2), FracSec.from!"usecs"(234578))); - sysTime.roll!"days"(-11); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 7, 4, 7, 9, 2), FracSec.from!"usecs"(234578))); - sysTime.roll!"days"(30); - _assertPred!"=="(sysTime, SysTime(DateTime(-1999, 7, 3, 7, 9, 2), FracSec.from!"usecs"(234578))); - sysTime.roll!"days"(-3); - } + { + auto sysTime = SysTime(Date(-1999, 7, 6)); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(Date(-1999, 7, 30))); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + sysTime.roll!"days"(366); + assert(sysTime == SysTime(Date(-1999, 7, 31))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(Date(-1999, 7, 17))); + sysTime.roll!"days"(-1096); + assert(sysTime == SysTime(Date(-1999, 7, 6))); + } - //Test Both - { - auto sysTime = SysTime(Date(1, 7, 6)); - sysTime.roll!"days"(-365); - _assertPred!"=="(sysTime, SysTime(Date(1, 7, 13))); - sysTime.roll!"days"(365); - _assertPred!"=="(sysTime, SysTime(Date(1, 7, 6))); - sysTime.roll!"days"(-731); - _assertPred!"=="(sysTime, SysTime(Date(1, 7, 19))); - sysTime.roll!"days"(730); - _assertPred!"=="(sysTime, SysTime(Date(1, 7, 5))); - } + { + auto sysTime = SysTime(DateTime(-1999, 2, 28, 7, 9, 2), FracSec.from!"usecs"(234578)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(-1999, 2, 1, 7, 9, 2), FracSec.from!"usecs"(234578))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(-1999, 2, 28, 7, 9, 2), FracSec.from!"usecs"(234578))); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.roll!"days"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 31, 0, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } + { + auto sysTime = SysTime(DateTime(-1999, 7, 6, 7, 9, 2), FracSec.from!"usecs"(234578)); + sysTime.roll!"days"(9); + assert(sysTime == SysTime(DateTime(-1999, 7, 15, 7, 9, 2), FracSec.from!"usecs"(234578))); + sysTime.roll!"days"(-11); + assert(sysTime == SysTime(DateTime(-1999, 7, 4, 7, 9, 2), FracSec.from!"usecs"(234578))); + sysTime.roll!"days"(30); + assert(sysTime == SysTime(DateTime(-1999, 7, 3, 7, 9, 2), FracSec.from!"usecs"(234578))); + sysTime.roll!"days"(-3); + } - { - auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.roll!"days"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + //Test Both + { + auto sysTime = SysTime(Date(1, 7, 6)); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(Date(1, 7, 13))); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(Date(1, 7, 6))); + sysTime.roll!"days"(-731); + assert(sysTime == SysTime(Date(1, 7, 19))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(Date(1, 7, 5))); + } - { - auto sysTime = SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.roll!"days"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 31, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.roll!"days"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.roll!"days"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + { + auto sysTime = SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } - { - auto sysTime = SysTime(DateTime(1, 7, 6, 13, 13, 9), FracSec.from!"msecs"(22)); - sysTime.roll!"days"(-365); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 7, 13, 13, 13, 9), FracSec.from!"msecs"(22))); - sysTime.roll!"days"(365); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 7, 6, 13, 13, 9), FracSec.from!"msecs"(22))); - sysTime.roll!"days"(-731); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 7, 19, 13, 13, 9), FracSec.from!"msecs"(22))); - sysTime.roll!"days"(730); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 7, 5, 13, 13, 9), FracSec.from!"msecs"(22))); - } + { + auto sysTime = SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } - { - auto sysTime = SysTime(DateTime(0, 7, 6, 13, 13, 9), FracSec.from!"msecs"(22)); - sysTime.roll!"days"(-365); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 7, 13, 13, 13, 9), FracSec.from!"msecs"(22))); - sysTime.roll!"days"(365); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 7, 6, 13, 13, 9), FracSec.from!"msecs"(22))); - sysTime.roll!"days"(-731); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 7, 19, 13, 13, 9), FracSec.from!"msecs"(22))); - sysTime.roll!"days"(730); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 7, 5, 13, 13, 9), FracSec.from!"msecs"(22))); - } + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"days"(1); + assert(sysTime == SysTime(DateTime(0, 12, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"days"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(1, 7, 6, 13, 13, 9), FracSec.from!"msecs"(22)); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(DateTime(1, 7, 13, 13, 13, 9), FracSec.from!"msecs"(22))); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(DateTime(1, 7, 6, 13, 13, 9), FracSec.from!"msecs"(22))); + sysTime.roll!"days"(-731); + assert(sysTime == SysTime(DateTime(1, 7, 19, 13, 13, 9), FracSec.from!"msecs"(22))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(DateTime(1, 7, 5, 13, 13, 9), FracSec.from!"msecs"(22))); + } - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"days"(4))); - //static assert(!__traits(compiles, ist.roll!"days"(4))); + { + auto sysTime = SysTime(DateTime(0, 7, 6, 13, 13, 9), FracSec.from!"msecs"(22)); + sysTime.roll!"days"(-365); + assert(sysTime == SysTime(DateTime(0, 7, 13, 13, 13, 9), FracSec.from!"msecs"(22))); + sysTime.roll!"days"(365); + assert(sysTime == SysTime(DateTime(0, 7, 6, 13, 13, 9), FracSec.from!"msecs"(22))); + sysTime.roll!"days"(-731); + assert(sysTime == SysTime(DateTime(0, 7, 19, 13, 13, 9), FracSec.from!"msecs"(22))); + sysTime.roll!"days"(730); + assert(sysTime == SysTime(DateTime(0, 7, 5, 13, 13, 9), FracSec.from!"msecs"(22))); + } - //Verify Examples. - auto st = SysTime(DateTime(2010, 1, 1, 11, 23, 12)); - st.roll!"days"(1); - assert(st == SysTime(DateTime(2010, 1, 2, 11, 23, 12))); - st.roll!"days"(365); - assert(st == SysTime(DateTime(2010, 1, 26, 11, 23, 12))); - st.roll!"days"(-32); - assert(st == SysTime(DateTime(2010, 1, 25, 11, 23, 12))); + { + auto sysTime = SysTime(DateTime(0, 7, 6, 13, 13, 9), FracSec.from!"msecs"(22)); + sysTime.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730); + assert(sysTime == SysTime(DateTime(0, 7, 8, 13, 13, 9), FracSec.from!"msecs"(22))); } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"days"(4))); + //static assert(!__traits(compiles, ist.roll!"days"(4))); } //Shares documentation with "days" version. - /+ref SysTime+/ void roll(string units)(long value) nothrow + ref SysTime roll(string units)(long value) @safe nothrow if(units == "hours" || units == "minutes" || units == "seconds") @@ -4888,8 +4699,8 @@ assert(st4 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), } immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - adjTime = newDaysHNSecs + hnsecs; + return this; } catch(Exception e) assert(0, "Either DateTime's constructor or TimeOfDay's constructor threw."); @@ -4898,625 +4709,609 @@ assert(st4 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), //Test roll!"hours"(). unittest { - version(testStdDateTime) + static void TestST(SysTime orig, int hours, in SysTime expected) { - static void TestST(SysTime orig, int hours, in SysTime expected, size_t line = __LINE__) - { - orig.roll!"hours"(hours); - _assertPred!"=="(orig, expected, "", __FILE__, line); - } - - //Test A.D. - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 2, SysTime(DateTime(1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 3, SysTime(DateTime(1999, 7, 6, 15, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 4, SysTime(DateTime(1999, 7, 6, 16, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 5, SysTime(DateTime(1999, 7, 6, 17, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 6, SysTime(DateTime(1999, 7, 6, 18, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 7, SysTime(DateTime(1999, 7, 6, 19, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 8, SysTime(DateTime(1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 9, SysTime(DateTime(1999, 7, 6, 21, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 10, SysTime(DateTime(1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 11, SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 12, SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 13, SysTime(DateTime(1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 14, SysTime(DateTime(1999, 7, 6, 2, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 15, SysTime(DateTime(1999, 7, 6, 3, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 16, SysTime(DateTime(1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 17, SysTime(DateTime(1999, 7, 6, 5, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 18, SysTime(DateTime(1999, 7, 6, 6, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 19, SysTime(DateTime(1999, 7, 6, 7, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 20, SysTime(DateTime(1999, 7, 6, 8, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 21, SysTime(DateTime(1999, 7, 6, 9, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 22, SysTime(DateTime(1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 23, SysTime(DateTime(1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 24, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 50, SysTime(DateTime(1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 10_000, SysTime(DateTime(1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); - - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -2, SysTime(DateTime(1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -3, SysTime(DateTime(1999, 7, 6, 9, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -4, SysTime(DateTime(1999, 7, 6, 8, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -5, SysTime(DateTime(1999, 7, 6, 7, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -6, SysTime(DateTime(1999, 7, 6, 6, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -7, SysTime(DateTime(1999, 7, 6, 5, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -8, SysTime(DateTime(1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -9, SysTime(DateTime(1999, 7, 6, 3, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -10, SysTime(DateTime(1999, 7, 6, 2, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -11, SysTime(DateTime(1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -12, SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -13, SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -14, SysTime(DateTime(1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -15, SysTime(DateTime(1999, 7, 6, 21, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -16, SysTime(DateTime(1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -17, SysTime(DateTime(1999, 7, 6, 19, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -18, SysTime(DateTime(1999, 7, 6, 18, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -19, SysTime(DateTime(1999, 7, 6, 17, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -20, SysTime(DateTime(1999, 7, 6, 16, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -21, SysTime(DateTime(1999, 7, 6, 15, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -22, SysTime(DateTime(1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -23, SysTime(DateTime(1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -24, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -50, SysTime(DateTime(1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -10_000, SysTime(DateTime(1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); - - TestST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); - - TestST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); - - TestST(SysTime(DateTime(1999, 7, 31, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(1999, 7, 31, 0, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 8, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(1999, 8, 1, 23, 30, 33), FracSec.from!"msecs"(45))); - - TestST(SysTime(DateTime(1999, 12, 31, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(1999, 12, 31, 0, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(2000, 1, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(2000, 1, 1, 23, 30, 33), FracSec.from!"msecs"(45))); - - TestST(SysTime(DateTime(1999, 2, 28, 23, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(1999, 2, 28, 0, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1999, 3, 2, 0, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(1999, 3, 2, 23, 30, 33), FracSec.from!"msecs"(45))); - - TestST(SysTime(DateTime(2000, 2, 28, 23, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(2000, 2, 28, 0, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(2000, 3, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(2000, 3, 1, 23, 30, 33), FracSec.from!"msecs"(45))); - - //Test B.C. - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 2, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 3, SysTime(DateTime(-1999, 7, 6, 15, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 4, SysTime(DateTime(-1999, 7, 6, 16, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 5, SysTime(DateTime(-1999, 7, 6, 17, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 6, SysTime(DateTime(-1999, 7, 6, 18, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 7, SysTime(DateTime(-1999, 7, 6, 19, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 8, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 9, SysTime(DateTime(-1999, 7, 6, 21, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 10, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 11, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 12, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 13, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 14, SysTime(DateTime(-1999, 7, 6, 2, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 15, SysTime(DateTime(-1999, 7, 6, 3, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 16, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 17, SysTime(DateTime(-1999, 7, 6, 5, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 18, SysTime(DateTime(-1999, 7, 6, 6, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 19, SysTime(DateTime(-1999, 7, 6, 7, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 20, SysTime(DateTime(-1999, 7, 6, 8, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 21, SysTime(DateTime(-1999, 7, 6, 9, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 22, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 23, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 24, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 50, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 10_000, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); - - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -2, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -3, SysTime(DateTime(-1999, 7, 6, 9, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -4, SysTime(DateTime(-1999, 7, 6, 8, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -5, SysTime(DateTime(-1999, 7, 6, 7, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -6, SysTime(DateTime(-1999, 7, 6, 6, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -7, SysTime(DateTime(-1999, 7, 6, 5, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -8, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -9, SysTime(DateTime(-1999, 7, 6, 3, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -10, SysTime(DateTime(-1999, 7, 6, 2, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -11, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -12, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -13, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -14, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -15, SysTime(DateTime(-1999, 7, 6, 21, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -16, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -17, SysTime(DateTime(-1999, 7, 6, 19, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -18, SysTime(DateTime(-1999, 7, 6, 18, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -19, SysTime(DateTime(-1999, 7, 6, 17, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -20, SysTime(DateTime(-1999, 7, 6, 16, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -21, SysTime(DateTime(-1999, 7, 6, 15, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -22, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -23, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -24, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -50, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -10_000, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); - - TestST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); - - TestST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); - - TestST(SysTime(DateTime(-1999, 7, 31, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(-1999, 7, 31, 0, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-1999, 8, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(-1999, 8, 1, 23, 30, 33), FracSec.from!"msecs"(45))); - - TestST(SysTime(DateTime(-2001, 12, 31, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(-2001, 12, 31, 0, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-2000, 1, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(-2000, 1, 1, 23, 30, 33), FracSec.from!"msecs"(45))); - - TestST(SysTime(DateTime(-2001, 2, 28, 23, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(-2001, 2, 28, 0, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-2001, 3, 2, 0, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(-2001, 3, 2, 23, 30, 33), FracSec.from!"msecs"(45))); - - TestST(SysTime(DateTime(-2000, 2, 28, 23, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(-2000, 2, 28, 0, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(-2000, 3, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(-2000, 3, 1, 23, 30, 33), FracSec.from!"msecs"(45))); - - //Test Both - TestST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"msecs"(45)), 17_546, SysTime(DateTime(-1, 1, 1, 13, 30, 33), FracSec.from!"msecs"(45))); - TestST(SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"msecs"(45)), -17_546, SysTime(DateTime(1, 1, 1, 11, 30, 33), FracSec.from!"msecs"(45))); - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.roll!"hours"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.roll!"hours"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } - - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.roll!"hours"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.roll!"hours"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } - - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.roll!"hours"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.roll!"hours"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 0, 0), FracSec.from!"hnsecs"(0))); - } + orig.roll!"hours"(hours); + assert(orig == expected); + } - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.roll!"hours"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 0, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.roll!"hours"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + //Test A.D. + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 2, SysTime(DateTime(1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 3, SysTime(DateTime(1999, 7, 6, 15, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 4, SysTime(DateTime(1999, 7, 6, 16, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 5, SysTime(DateTime(1999, 7, 6, 17, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 6, SysTime(DateTime(1999, 7, 6, 18, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 7, SysTime(DateTime(1999, 7, 6, 19, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 8, SysTime(DateTime(1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 9, SysTime(DateTime(1999, 7, 6, 21, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 10, SysTime(DateTime(1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 11, SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 12, SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 13, SysTime(DateTime(1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 14, SysTime(DateTime(1999, 7, 6, 2, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 15, SysTime(DateTime(1999, 7, 6, 3, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 16, SysTime(DateTime(1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 17, SysTime(DateTime(1999, 7, 6, 5, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 18, SysTime(DateTime(1999, 7, 6, 6, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 19, SysTime(DateTime(1999, 7, 6, 7, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 20, SysTime(DateTime(1999, 7, 6, 8, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 21, SysTime(DateTime(1999, 7, 6, 9, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 22, SysTime(DateTime(1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 23, SysTime(DateTime(1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 24, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 50, SysTime(DateTime(1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 10_000, SysTime(DateTime(1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -2, SysTime(DateTime(1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -3, SysTime(DateTime(1999, 7, 6, 9, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -4, SysTime(DateTime(1999, 7, 6, 8, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -5, SysTime(DateTime(1999, 7, 6, 7, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -6, SysTime(DateTime(1999, 7, 6, 6, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -7, SysTime(DateTime(1999, 7, 6, 5, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -8, SysTime(DateTime(1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -9, SysTime(DateTime(1999, 7, 6, 3, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -10, SysTime(DateTime(1999, 7, 6, 2, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -11, SysTime(DateTime(1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -12, SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -13, SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -14, SysTime(DateTime(1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -15, SysTime(DateTime(1999, 7, 6, 21, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -16, SysTime(DateTime(1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -17, SysTime(DateTime(1999, 7, 6, 19, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -18, SysTime(DateTime(1999, 7, 6, 18, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -19, SysTime(DateTime(1999, 7, 6, 17, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -20, SysTime(DateTime(1999, 7, 6, 16, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -21, SysTime(DateTime(1999, 7, 6, 15, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -22, SysTime(DateTime(1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -23, SysTime(DateTime(1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -24, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -50, SysTime(DateTime(1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -10_000, SysTime(DateTime(1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(1999, 7, 31, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(1999, 7, 31, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 8, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(1999, 8, 1, 23, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(1999, 12, 31, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(1999, 12, 31, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(2000, 1, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(2000, 1, 1, 23, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(1999, 2, 28, 23, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(1999, 2, 28, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1999, 3, 2, 0, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(1999, 3, 2, 23, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(2000, 2, 28, 23, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(2000, 2, 28, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(2000, 3, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(2000, 3, 1, 23, 30, 33), FracSec.from!"msecs"(45))); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"hours"(4))); - //static assert(!__traits(compiles, ist.roll!"hours"(4))); + //Test B.C. + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 2, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 3, SysTime(DateTime(-1999, 7, 6, 15, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 4, SysTime(DateTime(-1999, 7, 6, 16, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 5, SysTime(DateTime(-1999, 7, 6, 17, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 6, SysTime(DateTime(-1999, 7, 6, 18, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 7, SysTime(DateTime(-1999, 7, 6, 19, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 8, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 9, SysTime(DateTime(-1999, 7, 6, 21, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 10, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 11, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 12, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 13, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 14, SysTime(DateTime(-1999, 7, 6, 2, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 15, SysTime(DateTime(-1999, 7, 6, 3, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 16, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 17, SysTime(DateTime(-1999, 7, 6, 5, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 18, SysTime(DateTime(-1999, 7, 6, 6, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 19, SysTime(DateTime(-1999, 7, 6, 7, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 20, SysTime(DateTime(-1999, 7, 6, 8, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 21, SysTime(DateTime(-1999, 7, 6, 9, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 22, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 23, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 24, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 50, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), 10_000, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -2, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -3, SysTime(DateTime(-1999, 7, 6, 9, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -4, SysTime(DateTime(-1999, 7, 6, 8, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -5, SysTime(DateTime(-1999, 7, 6, 7, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -6, SysTime(DateTime(-1999, 7, 6, 6, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -7, SysTime(DateTime(-1999, 7, 6, 5, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -8, SysTime(DateTime(-1999, 7, 6, 4, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -9, SysTime(DateTime(-1999, 7, 6, 3, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -10, SysTime(DateTime(-1999, 7, 6, 2, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -11, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -12, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -13, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -14, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -15, SysTime(DateTime(-1999, 7, 6, 21, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -16, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -17, SysTime(DateTime(-1999, 7, 6, 19, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -18, SysTime(DateTime(-1999, 7, 6, 18, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -19, SysTime(DateTime(-1999, 7, 6, 17, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -20, SysTime(DateTime(-1999, 7, 6, 16, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -21, SysTime(DateTime(-1999, 7, 6, 15, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -22, SysTime(DateTime(-1999, 7, 6, 14, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -23, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -24, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -50, SysTime(DateTime(-1999, 7, 6, 10, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(45)), -10_000, SysTime(DateTime(-1999, 7, 6, 20, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(-1999, 7, 6, 1, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(-1999, 7, 6, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), 0, SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 7, 6, 23, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(-1999, 7, 6, 22, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(-1999, 7, 31, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(-1999, 7, 31, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-1999, 8, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(-1999, 8, 1, 23, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(-2001, 12, 31, 23, 30, 33), FracSec.from!"msecs"(45)), 1, SysTime(DateTime(-2001, 12, 31, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-2000, 1, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -1, SysTime(DateTime(-2000, 1, 1, 23, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(-2001, 2, 28, 23, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(-2001, 2, 28, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-2001, 3, 2, 0, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(-2001, 3, 2, 23, 30, 33), FracSec.from!"msecs"(45))); + + TestST(SysTime(DateTime(-2000, 2, 28, 23, 30, 33), FracSec.from!"msecs"(45)), 25, SysTime(DateTime(-2000, 2, 28, 0, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(-2000, 3, 1, 0, 30, 33), FracSec.from!"msecs"(45)), -25, SysTime(DateTime(-2000, 3, 1, 23, 30, 33), FracSec.from!"msecs"(45))); + + //Test Both + TestST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"msecs"(45)), 17_546, SysTime(DateTime(-1, 1, 1, 13, 30, 33), FracSec.from!"msecs"(45))); + TestST(SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"msecs"(45)), -17_546, SysTime(DateTime(1, 1, 1, 11, 30, 33), FracSec.from!"msecs"(45))); + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"hours"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"hours"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"hours"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"hours"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"hours"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"hours"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"hours"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 0, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"hours"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"hours"(1).roll!"hours"(-67); + assert(sysTime == SysTime(DateTime(0, 12, 31, 5, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } - //Verify Examples. - auto st1 = SysTime(DateTime(2010, 7, 4, 12, 0, 0)); - st1.roll!"hours"(1); - assert(st1 == SysTime(DateTime(2010, 7, 4, 13, 0, 0))); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"hours"(4))); + //static assert(!__traits(compiles, ist.roll!"hours"(4))); + } - auto st2 = SysTime(DateTime(2010, 2, 12, 12, 0, 0)); - st2.roll!"hours"(-1); - assert(st2 == SysTime(DateTime(2010, 2, 12, 11, 0, 0))); + //Test roll!"minutes"(). + unittest + { + static void TestST(SysTime orig, int minutes, in SysTime expected) + { + orig.roll!"minutes"(minutes); + assert(orig == expected); + } - auto st3 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); - st3.roll!"minutes"(1); - assert(st3 == SysTime(DateTime(2009, 12, 31, 0, 1, 0))); + //Test A.D. + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 2, SysTime(DateTime(1999, 7, 6, 12, 32, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 3, SysTime(DateTime(1999, 7, 6, 12, 33, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 4, SysTime(DateTime(1999, 7, 6, 12, 34, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 5, SysTime(DateTime(1999, 7, 6, 12, 35, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 10, SysTime(DateTime(1999, 7, 6, 12, 40, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 15, SysTime(DateTime(1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 29, SysTime(DateTime(1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 30, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 45, SysTime(DateTime(1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 75, SysTime(DateTime(1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 90, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 100, SysTime(DateTime(1999, 7, 6, 12, 10, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 689, SysTime(DateTime(1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 690, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 691, SysTime(DateTime(1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 960, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1439, SysTime(DateTime(1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1440, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1441, SysTime(DateTime(1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 2880, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -2, SysTime(DateTime(1999, 7, 6, 12, 28, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -3, SysTime(DateTime(1999, 7, 6, 12, 27, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -4, SysTime(DateTime(1999, 7, 6, 12, 26, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -5, SysTime(DateTime(1999, 7, 6, 12, 25, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -10, SysTime(DateTime(1999, 7, 6, 12, 20, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -15, SysTime(DateTime(1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -29, SysTime(DateTime(1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -30, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -45, SysTime(DateTime(1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -75, SysTime(DateTime(1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -90, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -100, SysTime(DateTime(1999, 7, 6, 12, 50, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -749, SysTime(DateTime(1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -750, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -751, SysTime(DateTime(1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -960, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1439, SysTime(DateTime(1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1440, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1441, SysTime(DateTime(1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -2880, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1999, 7, 6, 11, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1999, 7, 6, 11, 58, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1999, 7, 6, 0, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1999, 7, 6, 0, 59, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1999, 7, 5, 23, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1999, 7, 5, 23, 58, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1998, 12, 31, 23, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1998, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1998, 12, 31, 23, 58, 33), FracSec.from!"usecs"(7203))); - auto st4 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); - st4.roll!"minutes"(-1); - assert(st4 == SysTime(DateTime(2010, 1, 1, 0, 59, 0))); + //Test B.C. + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 2, SysTime(DateTime(-1999, 7, 6, 12, 32, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 3, SysTime(DateTime(-1999, 7, 6, 12, 33, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 4, SysTime(DateTime(-1999, 7, 6, 12, 34, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 5, SysTime(DateTime(-1999, 7, 6, 12, 35, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 10, SysTime(DateTime(-1999, 7, 6, 12, 40, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 15, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 29, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 30, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 45, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 75, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 90, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 100, SysTime(DateTime(-1999, 7, 6, 12, 10, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 689, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 690, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 691, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 960, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1439, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1440, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1441, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 2880, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -2, SysTime(DateTime(-1999, 7, 6, 12, 28, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -3, SysTime(DateTime(-1999, 7, 6, 12, 27, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -4, SysTime(DateTime(-1999, 7, 6, 12, 26, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -5, SysTime(DateTime(-1999, 7, 6, 12, 25, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -10, SysTime(DateTime(-1999, 7, 6, 12, 20, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -15, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -29, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -30, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -45, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -75, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -90, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -100, SysTime(DateTime(-1999, 7, 6, 12, 50, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -749, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -750, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -751, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -960, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1439, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1440, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1441, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -2880, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-1999, 7, 6, 11, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-1999, 7, 6, 11, 58, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-1999, 7, 6, 0, 1, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-1999, 7, 6, 0, 59, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-1999, 7, 5, 23, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-1999, 7, 5, 23, 58, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-2000, 12, 31, 23, 0, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-2000, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-2000, 12, 31, 23, 58, 33), FracSec.from!"usecs"(7203))); + + //Test Both + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), -1, SysTime(DateTime(1, 1, 1, 0, 59, 0))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 0)), 1, SysTime(DateTime(0, 12, 31, 23, 0, 0))); + + TestST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), -1, SysTime(DateTime(0, 1, 1, 0, 59, 0))); + TestST(SysTime(DateTime(-1, 12, 31, 23, 59, 0)), 1, SysTime(DateTime(-1, 12, 31, 23, 0, 0))); + + TestST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"usecs"(7203)), 1_052_760, SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"usecs"(7203)), -1_052_760, SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"usecs"(7203))); + + TestST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"usecs"(7203)), 1_052_782, SysTime(DateTime(-1, 1, 1, 11, 52, 33), FracSec.from!"usecs"(7203))); + TestST(SysTime(DateTime(1, 1, 1, 13, 52, 33), FracSec.from!"usecs"(7203)), -1_052_782, SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"usecs"(7203))); + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"minutes"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"minutes"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"minutes"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"minutes"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"minutes"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"minutes"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0), FracSec.from!"hnsecs"(0))); + } - auto st5 = SysTime(DateTime(2009, 12, 31, 0, 0, 0)); - st5.roll!"seconds"(1); - assert(st5 == SysTime(DateTime(2009, 12, 31, 0, 0, 1))); + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"minutes"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 0, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"minutes"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } - auto st6 = SysTime(DateTime(2010, 1, 1, 0, 0, 0)); - st6.roll!"seconds"(-1); - assert(st6 == SysTime(DateTime(2010, 1, 1, 0, 0, 59))); + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"minutes"(1).roll!"minutes"(-79); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 41, 59), FracSec.from!"hnsecs"(9_999_999))); } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"minutes"(4))); + //static assert(!__traits(compiles, ist.roll!"minutes"(4))); } - //Test roll!"minutes"(). + //Test roll!"seconds"(). unittest { - version(testStdDateTime) + static void TestST(SysTime orig, int seconds, in SysTime expected) { - static void TestST(SysTime orig, int minutes, in SysTime expected, size_t line = __LINE__) - { - orig.roll!"minutes"(minutes); - _assertPred!"=="(orig, expected, "", __FILE__, line); - } + orig.roll!"seconds"(seconds); + assert(orig == expected); + } - //Test A.D. - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 2, SysTime(DateTime(1999, 7, 6, 12, 32, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 3, SysTime(DateTime(1999, 7, 6, 12, 33, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 4, SysTime(DateTime(1999, 7, 6, 12, 34, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 5, SysTime(DateTime(1999, 7, 6, 12, 35, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 10, SysTime(DateTime(1999, 7, 6, 12, 40, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 15, SysTime(DateTime(1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 29, SysTime(DateTime(1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 30, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 45, SysTime(DateTime(1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 75, SysTime(DateTime(1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 90, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 100, SysTime(DateTime(1999, 7, 6, 12, 10, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 689, SysTime(DateTime(1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 690, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 691, SysTime(DateTime(1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 960, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1439, SysTime(DateTime(1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1440, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1441, SysTime(DateTime(1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 2880, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -2, SysTime(DateTime(1999, 7, 6, 12, 28, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -3, SysTime(DateTime(1999, 7, 6, 12, 27, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -4, SysTime(DateTime(1999, 7, 6, 12, 26, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -5, SysTime(DateTime(1999, 7, 6, 12, 25, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -10, SysTime(DateTime(1999, 7, 6, 12, 20, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -15, SysTime(DateTime(1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -29, SysTime(DateTime(1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -30, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -45, SysTime(DateTime(1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -75, SysTime(DateTime(1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -90, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -100, SysTime(DateTime(1999, 7, 6, 12, 50, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -749, SysTime(DateTime(1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -750, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -751, SysTime(DateTime(1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -960, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1439, SysTime(DateTime(1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1440, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1441, SysTime(DateTime(1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -2880, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1999, 7, 6, 11, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1999, 7, 6, 11, 58, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1999, 7, 6, 0, 1, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1999, 7, 6, 0, 59, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1999, 7, 5, 23, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1999, 7, 5, 23, 58, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(1998, 12, 31, 23, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(1998, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(1998, 12, 31, 23, 58, 33), FracSec.from!"usecs"(7203))); - - //Test B.C. - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 2, SysTime(DateTime(-1999, 7, 6, 12, 32, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 3, SysTime(DateTime(-1999, 7, 6, 12, 33, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 4, SysTime(DateTime(-1999, 7, 6, 12, 34, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 5, SysTime(DateTime(-1999, 7, 6, 12, 35, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 10, SysTime(DateTime(-1999, 7, 6, 12, 40, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 15, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 29, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 30, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 45, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 75, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 90, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 100, SysTime(DateTime(-1999, 7, 6, 12, 10, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 689, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 690, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 691, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 960, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1439, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1440, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 1441, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), 2880, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -2, SysTime(DateTime(-1999, 7, 6, 12, 28, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -3, SysTime(DateTime(-1999, 7, 6, 12, 27, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -4, SysTime(DateTime(-1999, 7, 6, 12, 26, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -5, SysTime(DateTime(-1999, 7, 6, 12, 25, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -10, SysTime(DateTime(-1999, 7, 6, 12, 20, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -15, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -29, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -30, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -45, SysTime(DateTime(-1999, 7, 6, 12, 45, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -75, SysTime(DateTime(-1999, 7, 6, 12, 15, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -90, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -100, SysTime(DateTime(-1999, 7, 6, 12, 50, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -749, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -750, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -751, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -960, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1439, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1440, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -1441, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203)), -2880, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-1999, 7, 6, 12, 1, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-1999, 7, 6, 12, 59, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-1999, 7, 6, 11, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 11, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-1999, 7, 6, 11, 58, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-1999, 7, 6, 0, 1, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-1999, 7, 6, 0, 59, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-1999, 7, 5, 23, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-1999, 7, 5, 23, 58, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), 1, SysTime(DateTime(-2000, 12, 31, 23, 0, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), 0, SysTime(DateTime(-2000, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 33), FracSec.from!"usecs"(7203)), -1, SysTime(DateTime(-2000, 12, 31, 23, 58, 33), FracSec.from!"usecs"(7203))); - - //Test Both - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0)), -1, SysTime(DateTime(1, 1, 1, 0, 59, 0))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 0)), 1, SysTime(DateTime(0, 12, 31, 23, 0, 0))); - - TestST(SysTime(DateTime(0, 1, 1, 0, 0, 0)), -1, SysTime(DateTime(0, 1, 1, 0, 59, 0))); - TestST(SysTime(DateTime(-1, 12, 31, 23, 59, 0)), 1, SysTime(DateTime(-1, 12, 31, 23, 0, 0))); - - TestST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"usecs"(7203)), 1_052_760, SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"usecs"(7203)), -1_052_760, SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"usecs"(7203))); - - TestST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"usecs"(7203)), 1_052_782, SysTime(DateTime(-1, 1, 1, 11, 52, 33), FracSec.from!"usecs"(7203))); - TestST(SysTime(DateTime(1, 1, 1, 13, 52, 33), FracSec.from!"usecs"(7203)), -1_052_782, SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"usecs"(7203))); + //Test A.D. + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 35), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3, SysTime(DateTime(1999, 7, 6, 12, 30, 36), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 4, SysTime(DateTime(1999, 7, 6, 12, 30, 37), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 5, SysTime(DateTime(1999, 7, 6, 12, 30, 38), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 43), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 15, SysTime(DateTime(1999, 7, 6, 12, 30, 48), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26, SysTime(DateTime(1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 27, SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 30, SysTime(DateTime(1999, 7, 6, 12, 30, 3), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 59, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 61, SysTime(DateTime(1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1766, SysTime(DateTime(1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1767, SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1768, SysTime(DateTime(1999, 7, 6, 12, 30, 1), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2007, SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3599, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3600, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3601, SysTime(DateTime(1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 7200, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 31), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -3, SysTime(DateTime(1999, 7, 6, 12, 30, 30), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -4, SysTime(DateTime(1999, 7, 6, 12, 30, 29), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -5, SysTime(DateTime(1999, 7, 6, 12, 30, 28), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 23), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -15, SysTime(DateTime(1999, 7, 6, 12, 30, 18), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33, SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -34, SysTime(DateTime(1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -35, SysTime(DateTime(1999, 7, 6, 12, 30, 58), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -59, SysTime(DateTime(1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -61, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 1), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 0, 1), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 0, 59), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 0, 0, 1), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 0, 0, 59), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 5, 23, 59, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 5, 23, 59, 58), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1998, 12, 31, 23, 59, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1998, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1998, 12, 31, 23, 59, 58), FracSec.from!"msecs"(274))); - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.roll!"minutes"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 59, 0), FracSec.from!"hnsecs"(0))); - sysTime.roll!"minutes"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } + //Test B.C. + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 35), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3, SysTime(DateTime(-1999, 7, 6, 12, 30, 36), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 4, SysTime(DateTime(-1999, 7, 6, 12, 30, 37), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 5, SysTime(DateTime(-1999, 7, 6, 12, 30, 38), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 43), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 15, SysTime(DateTime(-1999, 7, 6, 12, 30, 48), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 27, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 30, SysTime(DateTime(-1999, 7, 6, 12, 30, 3), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 59, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 61, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.roll!"minutes"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.roll!"minutes"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 59), FracSec.from!"hnsecs"(9_999_999))); - } + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1766, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1767, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1768, SysTime(DateTime(-1999, 7, 6, 12, 30, 1), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2007, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3599, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3600, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3601, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 7200, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 0), FracSec.from!"hnsecs"(0)); - sysTime.roll!"minutes"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 0, 0), FracSec.from!"hnsecs"(0))); - sysTime.roll!"minutes"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 59, 0), FracSec.from!"hnsecs"(0))); - } + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 31), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -3, SysTime(DateTime(-1999, 7, 6, 12, 30, 30), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -4, SysTime(DateTime(-1999, 7, 6, 12, 30, 29), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -5, SysTime(DateTime(-1999, 7, 6, 12, 30, 28), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 23), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -15, SysTime(DateTime(-1999, 7, 6, 12, 30, 18), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -34, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -35, SysTime(DateTime(-1999, 7, 6, 12, 30, 58), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -59, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -61, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.roll!"minutes"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 0, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.roll!"minutes"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 1), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"minutes"(4))); - //static assert(!__traits(compiles, ist.roll!"minutes"(4))); - } - } + TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 0, 1), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 0, 59), FracSec.from!"msecs"(274))); - //Test roll!"seconds"(). - unittest - { - version(testStdDateTime) - { - static void TestST(SysTime orig, int seconds, in SysTime expected, size_t line = __LINE__) - { - orig.roll!"seconds"(seconds); - _assertPred!"=="(orig, expected, "", __FILE__, line); - } + TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 0, 0, 1), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 0, 0, 59), FracSec.from!"msecs"(274))); - //Test A.D. - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 35), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3, SysTime(DateTime(1999, 7, 6, 12, 30, 36), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 4, SysTime(DateTime(1999, 7, 6, 12, 30, 37), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 5, SysTime(DateTime(1999, 7, 6, 12, 30, 38), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 43), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 15, SysTime(DateTime(1999, 7, 6, 12, 30, 48), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26, SysTime(DateTime(1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 27, SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 30, SysTime(DateTime(1999, 7, 6, 12, 30, 3), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 59, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 61, SysTime(DateTime(1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1766, SysTime(DateTime(1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1767, SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1768, SysTime(DateTime(1999, 7, 6, 12, 30, 1), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2007, SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3599, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3600, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3601, SysTime(DateTime(1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 7200, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 31), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -3, SysTime(DateTime(1999, 7, 6, 12, 30, 30), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -4, SysTime(DateTime(1999, 7, 6, 12, 30, 29), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -5, SysTime(DateTime(1999, 7, 6, 12, 30, 28), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 23), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -15, SysTime(DateTime(1999, 7, 6, 12, 30, 18), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33, SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -34, SysTime(DateTime(1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -35, SysTime(DateTime(1999, 7, 6, 12, 30, 58), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -59, SysTime(DateTime(1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -60, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -61, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 1), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 0, 1), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 0, 59), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 0, 0, 1), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 0, 0, 59), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 5, 23, 59, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 5, 23, 59, 58), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1998, 12, 31, 23, 59, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1998, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1998, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1998, 12, 31, 23, 59, 58), FracSec.from!"msecs"(274))); - - //Test B.C. - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 35), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3, SysTime(DateTime(-1999, 7, 6, 12, 30, 36), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 4, SysTime(DateTime(-1999, 7, 6, 12, 30, 37), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 5, SysTime(DateTime(-1999, 7, 6, 12, 30, 38), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 43), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 15, SysTime(DateTime(-1999, 7, 6, 12, 30, 48), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 27, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 30, SysTime(DateTime(-1999, 7, 6, 12, 30, 3), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 59, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 61, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1766, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1767, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1768, SysTime(DateTime(-1999, 7, 6, 12, 30, 1), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2007, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3599, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3600, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 3601, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 7200, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 31), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -3, SysTime(DateTime(-1999, 7, 6, 12, 30, 30), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -4, SysTime(DateTime(-1999, 7, 6, 12, 30, 29), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -5, SysTime(DateTime(-1999, 7, 6, 12, 30, 28), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 23), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -15, SysTime(DateTime(-1999, 7, 6, 12, 30, 18), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -34, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -35, SysTime(DateTime(-1999, 7, 6, 12, 30, 58), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -59, SysTime(DateTime(-1999, 7, 6, 12, 30, 34), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -60, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -61, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 1), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 59), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 0, 1), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 0, 59), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 0, 0, 1), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 0, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 0, 0, 59), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 5, 23, 59, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 5, 23, 59, 58), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-2000, 12, 31, 23, 59, 0), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-2000, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-2000, 12, 31, 23, 59, 58), FracSec.from!"msecs"(274))); - - //Test Both - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1, 1, 1, 0, 0, 59), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(0, 12, 31, 23, 59, 0), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(0, 1, 1, 0, 0, 59), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1, 12, 31, 23, 59, 0), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"msecs"(274)), 63_165_600L, SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"msecs"(274)), -63_165_600L, SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"msecs"(274))); - - TestST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"msecs"(274)), 63_165_617L, SysTime(DateTime(-1, 1, 1, 11, 30, 50), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1, 1, 1, 13, 30, 50), FracSec.from!"msecs"(274)), -63_165_617L, SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 5, 23, 59, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 5, 23, 59, 59), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 5, 23, 59, 58), FracSec.from!"msecs"(274))); - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); - sysTime.roll!"seconds"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 59), FracSec.from!"hnsecs"(0))); - sysTime.roll!"seconds"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - } + TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-2000, 12, 31, 23, 59, 0), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-2000, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-2000, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-2000, 12, 31, 23, 59, 58), FracSec.from!"msecs"(274))); - { - auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)); - sysTime.roll!"seconds"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 59), FracSec.from!"hnsecs"(9_999_999))); - sysTime.roll!"seconds"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999))); - } + //Test Both + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1, 1, 1, 0, 0, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(0, 12, 31, 23, 59, 0), FracSec.from!"msecs"(274))); - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)); - sysTime.roll!"seconds"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 59, 0), FracSec.from!"hnsecs"(0))); - sysTime.roll!"seconds"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); - } + TestST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(0, 1, 1, 0, 0, 59), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1, 12, 31, 23, 59, 59), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1, 12, 31, 23, 59, 0), FracSec.from!"msecs"(274))); - { - auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); - sysTime.roll!"seconds"(1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 59, 0), FracSec.from!"hnsecs"(9_999_999))); - sysTime.roll!"seconds"(-1); - _assertPred!"=="(sysTime, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + TestST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"msecs"(274)), 63_165_600L, SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"msecs"(274)), -63_165_600L, SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"msecs"(274))); + + TestST(SysTime(DateTime(-1, 1, 1, 11, 30, 33), FracSec.from!"msecs"(274)), 63_165_617L, SysTime(DateTime(-1, 1, 1, 11, 30, 50), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1, 1, 1, 13, 30, 50), FracSec.from!"msecs"(274)), -63_165_617L, SysTime(DateTime(1, 1, 1, 13, 30, 33), FracSec.from!"msecs"(274))); + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)); + sysTime.roll!"seconds"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59), FracSec.from!"hnsecs"(0))); + sysTime.roll!"seconds"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"seconds"(-1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 59), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"seconds"(1); + assert(sysTime == SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)); + sysTime.roll!"seconds"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0), FracSec.from!"hnsecs"(0))); + sysTime.roll!"seconds"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + } + + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"seconds"(1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 0), FracSec.from!"hnsecs"(9_999_999))); + sysTime.roll!"seconds"(-1); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + } - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"seconds"(4))); - //static assert(!__traits(compiles, ist.roll!"seconds"(4))); + { + auto sysTime = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + sysTime.roll!"seconds"(1).roll!"seconds"(-102); + assert(sysTime == SysTime(DateTime(0, 12, 31, 23, 59, 18), FracSec.from!"hnsecs"(9_999_999))); } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"seconds"(4))); + //static assert(!__traits(compiles, ist.roll!"seconds"(4))); } //Shares documentation with "days" version. - /+ref SysTime+/ void roll(string units)(long value) nothrow + ref SysTime roll(string units)(long value) @safe nothrow if(units == "msecs" || units == "usecs" || units == "hnsecs") @@ -5529,363 +5324,376 @@ assert(st4 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), hnsecs += convert!("hours", "hnsecs")(24); immutable seconds = splitUnitsFromHNSecs!"seconds"(hnsecs); - hnsecs += convert!(units, "hnsecs")(value); hnsecs %= convert!("seconds", "hnsecs")(1); if(hnsecs < 0) hnsecs += convert!("seconds", "hnsecs")(1); - hnsecs += convert!("seconds", "hnsecs")(seconds); if(negative) hnsecs -= convert!("hours", "hnsecs")(24); immutable newDaysHNSecs = convert!("days", "hnsecs")(days); - adjTime = newDaysHNSecs + hnsecs; + return this; } //Test roll!"msecs"(). unittest { - version(testStdDateTime) + static void TestST(SysTime orig, int milliseconds, in SysTime expected) { - static void TestST(SysTime orig, int milliseconds, in SysTime expected, size_t line = __LINE__) - { - orig.roll!"msecs"(milliseconds); - _assertPred!"=="(orig, expected, "", __FILE__, line); - } + orig.roll!"msecs"(milliseconds); + assert(orig == expected); + } - //Test A.D. - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(275))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(276))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(284))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(374))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(275))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(1))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); - - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(273))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(272))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(264))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(174))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(273))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); - - //Test B.C. - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(275))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(276))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(284))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(374))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(275))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(1))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); - - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(273))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(272))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(264))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(174))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(273))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); - - //Test Both - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(1))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), 0, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(999))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(998))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(445))); - - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_989_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(19_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(5_549_999))); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.addMSecs(4))); - //static assert(!__traits(compiles, ist.addMSecs(4))); + //Test A.D. + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(276))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(284))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(374))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(1))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(272))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(264))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(174))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + + //Test B.C. + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(276))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(284))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(374))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(1))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(272))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(264))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(174))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(274)), -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(999))); + + //Test Both + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(1))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), 0, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(999))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(998))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(0)), -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"msecs"(445))); + + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_989_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(19_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(5_549_999))); + + { + auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + st.roll!"msecs"(1202).roll!"msecs"(-703); + assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(4_989_999))); } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.addMSecs(4))); + //static assert(!__traits(compiles, ist.addMSecs(4))); } //Test roll!"usecs"(). unittest { - version(testStdDateTime) + static void TestST(SysTime orig, long microseconds, in SysTime expected) { - static void TestST(SysTime orig, long microseconds, in SysTime expected, size_t line = __LINE__) - { - orig.roll!"usecs"(microseconds); - _assertPred!"=="(orig, expected, "", __FILE__, line); - } + orig.roll!"usecs"(microseconds); + assert(orig == expected); + } + + //Test A.D. + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(276))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(284))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(374))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(2274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(26_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(27_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(27_001))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(766_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(767_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(272))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(264))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(174))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(998_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(967_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(966_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(167_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(166_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); - //Test A.D. - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(275))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(276))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(284))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(374))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1275))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(2274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(26_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(27_000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(27_001))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(766_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(767_000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); - - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(273))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(272))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(264))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(174))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(0))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_273))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(998_274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(967_000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(966_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(167_000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(166_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); - - //Test B.C. - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(275))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(276))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(284))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(374))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1275))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(2274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(26_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(27_000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(27_001))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(766_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(767_000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); - - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(273))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(272))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(264))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(174))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(0))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_273))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(998_274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(967_000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(966_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(167_000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(166_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); - - //Test Both - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(1))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), 0, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(999_999))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(999_998))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(999_000))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(998_000))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(997_445))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(666_667))); - - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_989))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(19))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(19_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(25_549))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(3_333_329))); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"usecs"(4))); - //static assert(!__traits(compiles, ist.roll!"usecs"(4))); + //Test B.C. + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(276))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(284))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(374))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(1275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(2274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(26_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(27_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(27_001))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(766_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(767_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(272))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(264))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(174))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(999_273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(998_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(967_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(966_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(167_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(166_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274)), -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(274))); + + //Test Both + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(1))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), 0, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(999_999))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(999_998))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(999_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(998_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(997_445))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(0)), -2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"usecs"(666_667))); + + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_989))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(19))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(19_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(25_549))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(3_333_329))); + + { + auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + st.roll!"usecs"(9_020_027); + assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(200_269))); + } + + { + auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + st.roll!"usecs"(9_020_027).roll!"usecs"(-70_034); + assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_499_929))); } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"usecs"(4))); + //static assert(!__traits(compiles, ist.roll!"usecs"(4))); } //Test roll!"hnsecs"(). unittest { - version(testStdDateTime) + static void TestST(SysTime orig, long hnsecs, in SysTime expected) { - static void TestST(SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) - { - orig.roll!"hnsecs"(hnsecs); - _assertPred!"=="(orig, expected, "", __FILE__, line); - } + orig.roll!"hnsecs"(hnsecs); + assert(orig == expected); + } + + //Test A.D. + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_998_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_967_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_966_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(8_167_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(8_166_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_000_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - //Test A.D. - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_273))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_998_274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_967_000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_966_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(8_167_000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(8_166_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_000_274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - - //Test B.C. - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_273))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_998_274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_967_000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_966_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(8_167_000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(8_166_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_000_274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - - //Test Both - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_998))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_000))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_998_000))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_997_445))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_000_000))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(8_000_000))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(7_666_667))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_111_112))); - - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(2554))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(2_333_332))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(888_887))); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.roll!"hnsecs"(4))); - //static assert(!__traits(compiles, ist.roll!"hnsecs"(4))); + //Test B.C. + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_999_273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_998_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_967_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_966_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(8_167_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(8_166_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(9_000_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + + //Test Both + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_998))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_998_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_997_445))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_000_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(8_000_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(7_666_667))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_111_112))); + + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(2554))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(2_333_332))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(888_887))); + + { + auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + st.roll!"hnsecs"(70_777_222).roll!"hnsecs"(-222_555_292); + assert(st == SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(8_221_929))); } + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.roll!"hnsecs"(4))); + //static assert(!__traits(compiles, ist.roll!"hnsecs"(4))); } @@ -5904,7 +5712,7 @@ assert(st4 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), duration = The duration to add to or subtract from this $(LREF SysTime). +/ - SysTime opBinary(string op, D)(in D duration) const pure nothrow + SysTime opBinary(string op, D)(in D duration) @safe const pure nothrow if((op == "+" || op == "-") && (is(Unqual!D == Duration) || is(Unqual!D == TickDuration))) @@ -5916,202 +5724,187 @@ assert(st4 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), else static if(is(Unqual!D == TickDuration)) immutable hnsecs = duration.hnsecs; - //Ideally, this would just be - //retval._stdTime += unaryFun!(op ~ "a")(hnsecs); - //But there isn't currently a pure version of unaryFun!(). - - static if(op == "+") - immutable signedHNSecs = hnsecs; - else static if(op == "-") - immutable signedHNSecs = -hnsecs; - else - static assert(0); - - retval._stdTime += signedHNSecs; - + mixin(format("retval._stdTime %s= hnsecs;", op)); return retval; } unittest { - version(testStdDateTime) - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678)); - - _assertPred!"=="(st + dur!"weeks"(7), SysTime(DateTime(1999, 8, 24, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st + dur!"weeks"(-7), SysTime(DateTime(1999, 5, 18, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st + dur!"days"(7), SysTime(DateTime(1999, 7, 13, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st + dur!"days"(-7), SysTime(DateTime(1999, 6, 29, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st + dur!"hours"(7), SysTime(DateTime(1999, 7, 6, 19, 30, 33), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st + dur!"hours"(-7), SysTime(DateTime(1999, 7, 6, 5, 30, 33), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st + dur!"minutes"(7), SysTime(DateTime(1999, 7, 6, 12, 37, 33), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st + dur!"minutes"(-7), SysTime(DateTime(1999, 7, 6, 12, 23, 33), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st + dur!"seconds"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 40), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st + dur!"seconds"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 26), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st + dur!"msecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_415_678))); - _assertPred!"=="(st + dur!"msecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_275_678))); - _assertPred!"=="(st + dur!"usecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_748))); - _assertPred!"=="(st + dur!"usecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_608))); - _assertPred!"=="(st + dur!"hnsecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_685))); - _assertPred!"=="(st + dur!"hnsecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_671))); - - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if(TickDuration.ticksPerSec == 1_000_000) - { - _assertPred!"=="(st + TickDuration.from!"usecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_748))); - _assertPred!"=="(st + TickDuration.from!"usecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_608))); - } - - _assertPred!"=="(st - dur!"weeks"(-7), SysTime(DateTime(1999, 8, 24, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st - dur!"weeks"(7), SysTime(DateTime(1999, 5, 18, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st - dur!"days"(-7), SysTime(DateTime(1999, 7, 13, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st - dur!"days"(7), SysTime(DateTime(1999, 6, 29, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st - dur!"hours"(-7), SysTime(DateTime(1999, 7, 6, 19, 30, 33), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st - dur!"hours"(7), SysTime(DateTime(1999, 7, 6, 5, 30, 33), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st - dur!"minutes"(-7), SysTime(DateTime(1999, 7, 6, 12, 37, 33), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st - dur!"minutes"(7), SysTime(DateTime(1999, 7, 6, 12, 23, 33), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st - dur!"seconds"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 40), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st - dur!"seconds"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 26), FracSec.from!"hnsecs"(2_345_678))); - _assertPred!"=="(st - dur!"msecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_415_678))); - _assertPred!"=="(st - dur!"msecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_275_678))); - _assertPred!"=="(st - dur!"usecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_748))); - _assertPred!"=="(st - dur!"usecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_608))); - _assertPred!"=="(st - dur!"hnsecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_685))); - _assertPred!"=="(st - dur!"hnsecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_671))); - - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if(TickDuration.ticksPerSec == 1_000_000) - { - _assertPred!"=="(st - TickDuration.from!"usecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_748))); - _assertPred!"=="(st - TickDuration.from!"usecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_608))); - } + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678)); + + assert(st + dur!"weeks"(7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assert(st + dur!"weeks"(-7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assert(st + dur!"days"(7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assert(st + dur!"days"(-7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assert(st + dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assert(st + dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assert(st + dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33), FracSec.from!"hnsecs"(2_345_678))); + assert(st + dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33), FracSec.from!"hnsecs"(2_345_678))); + assert(st + dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40), FracSec.from!"hnsecs"(2_345_678))); + assert(st + dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26), FracSec.from!"hnsecs"(2_345_678))); + assert(st + dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_415_678))); + assert(st + dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_275_678))); + assert(st + dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_748))); + assert(st + dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_608))); + assert(st + dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_685))); + assert(st + dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_671))); + + //This probably only runs in cases where gettimeofday() is used, but it's + //hard to do this test correctly with variable ticksPerSec. + if(TickDuration.ticksPerSec == 1_000_000) + { + assert(st + TickDuration.from!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_748))); + assert(st + TickDuration.from!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_608))); + } + + assert(st - dur!"weeks"(-7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assert(st - dur!"weeks"(7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assert(st - dur!"days"(-7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assert(st - dur!"days"(7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assert(st - dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assert(st - dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33), FracSec.from!"hnsecs"(2_345_678))); + assert(st - dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33), FracSec.from!"hnsecs"(2_345_678))); + assert(st - dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33), FracSec.from!"hnsecs"(2_345_678))); + assert(st - dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40), FracSec.from!"hnsecs"(2_345_678))); + assert(st - dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26), FracSec.from!"hnsecs"(2_345_678))); + assert(st - dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_415_678))); + assert(st - dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_275_678))); + assert(st - dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_748))); + assert(st - dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_608))); + assert(st - dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_685))); + assert(st - dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_671))); + + //This probably only runs in cases where gettimeofday() is used, but it's + //hard to do this test correctly with variable ticksPerSec. + if(TickDuration.ticksPerSec == 1_000_000) + { + assert(st - TickDuration.from!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_748))); + assert(st - TickDuration.from!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2_345_608))); + } + + static void TestST(in SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) + { + assert(orig + dur!"hnsecs"(hnsecs) == expected); + } - static void TestST(in SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) - { - _assertPred!"=="(orig + dur!"hnsecs"(hnsecs), expected, "", __FILE__, line); - } + //Test A.D. + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 39), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 36, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 31, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 13, 30, 33), FracSec.from!"hnsecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_998_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_967_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_966_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_167_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_166_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_000_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 27), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 24, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 29, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 11, 30, 33), FracSec.from!"hnsecs"(274))); - //Test A.D. - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 39), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 36, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 31, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 13, 30, 33), FracSec.from!"hnsecs"(274))); - - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_273))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_998_274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_967_000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_966_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_167_000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_166_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_000_274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 27), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 24, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 29, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 11, 30, 33), FracSec.from!"hnsecs"(274))); - - //Test B.C. - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 39), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 36, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), FracSec.from!"hnsecs"(274))); - - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_273))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_998_274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_967_000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_966_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_167_000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_166_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_000_274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 27), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 24, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), FracSec.from!"hnsecs"(274))); - - //Test Both - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_000))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_998_000))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_997_445))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_000_000))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(8_000_000))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(7_666_667))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 58), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 57), FracSec.from!"hnsecs"(9_111_112))); - - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(2554))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(2_333_332))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 1), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 2), FracSec.from!"hnsecs"(888_887))); - - auto duration = dur!"seconds"(12); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst + duration)); - //static assert(__traits(compiles, ist + duration)); - static assert(__traits(compiles, cst - duration)); - //static assert(__traits(compiles, ist - duration)); - } + //Test B.C. + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 39), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 36, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), FracSec.from!"hnsecs"(274))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_998_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_967_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_966_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_167_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_166_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_000_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 27), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 24, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), FracSec.from!"hnsecs"(274))); + + //Test Both + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_998_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_997_445))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_000_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(8_000_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(7_666_667))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 58), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 57), FracSec.from!"hnsecs"(9_111_112))); + + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(2554))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(2_333_332))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 1), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 2), FracSec.from!"hnsecs"(888_887))); + + auto duration = dur!"seconds"(12); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cst + duration)); + //static assert(__traits(compiles, ist + duration)); + static assert(__traits(compiles, cst - duration)); + //static assert(__traits(compiles, ist - duration)); } @@ -6130,7 +5923,7 @@ assert(st4 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), duration = The duration to add to or subtract from this $(LREF SysTime). +/ - /+ref+/ SysTime opOpAssign(string op, D)(in D duration) pure nothrow + ref SysTime opOpAssign(string op, D)(in D duration) @safe pure nothrow if((op == "+" || op == "-") && (is(Unqual!D == Duration) || is(Unqual!D == TickDuration))) @@ -6140,187 +5933,178 @@ assert(st4 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), else static if(is(Unqual!D == TickDuration)) auto hnsecs = duration.hnsecs; - //Ideally, this would just be - //_stdTime += unaryFun!(op ~ "a")(hnsecs); - //But there isn't currently a pure version of unaryFun!(). - - static if(op == "+") - immutable signedHNSecs = hnsecs; - else static if(op == "-") - immutable signedHNSecs = -hnsecs; - else - static assert(0); - - _stdTime += signedHNSecs; - + mixin(format("_stdTime %s= hnsecs;", op)); return this; } unittest { - version(testStdDateTime) - { - _assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"weeks"(7), SysTime(DateTime(1999, 8, 24, 12, 30, 33))); - _assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"weeks"(-7), SysTime(DateTime(1999, 5, 18, 12, 30, 33))); - _assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"days"(7), SysTime(DateTime(1999, 7, 13, 12, 30, 33))); - _assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"days"(-7), SysTime(DateTime(1999, 6, 29, 12, 30, 33))); - - _assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"hours"(7), SysTime(DateTime(1999, 7, 6, 19, 30, 33))); - _assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"hours"(-7), SysTime(DateTime(1999, 7, 6, 5, 30, 33))); - _assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"minutes"(7), SysTime(DateTime(1999, 7, 6, 12, 37, 33))); - _assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"minutes"(-7), SysTime(DateTime(1999, 7, 6, 12, 23, 33))); - _assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"seconds"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 40))); - _assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"seconds"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 26))); - _assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"msecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(7))); - _assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"msecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(993))); - _assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"usecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7))); - _assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"usecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"usecs"(999_993))); - _assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"hnsecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(7))); - _assertPred!"+="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"hnsecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_993))); - - _assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"weeks"(-7), SysTime(DateTime(1999, 8, 24, 12, 30, 33))); - _assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"weeks"(7), SysTime(DateTime(1999, 5, 18, 12, 30, 33))); - _assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"days"(-7), SysTime(DateTime(1999, 7, 13, 12, 30, 33))); - _assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"days"(7), SysTime(DateTime(1999, 6, 29, 12, 30, 33))); - - _assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"hours"(-7), SysTime(DateTime(1999, 7, 6, 19, 30, 33))); - _assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"hours"(7), SysTime(DateTime(1999, 7, 6, 5, 30, 33))); - _assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"minutes"(-7), SysTime(DateTime(1999, 7, 6, 12, 37, 33))); - _assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"minutes"(7), SysTime(DateTime(1999, 7, 6, 12, 23, 33))); - _assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"seconds"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 40))); - _assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"seconds"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 26))); - _assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"msecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(7))); - _assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"msecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(993))); - _assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"usecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7))); - _assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"usecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"usecs"(999_993))); - _assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"hnsecs"(-7), SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(7))); - _assertPred!"-="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)), dur!"hnsecs"(7), SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_993))); - - static void TestST(SysTime orig, long hnsecs, in SysTime expected, size_t line = __LINE__) - { - orig += dur!"hnsecs"(hnsecs); - _assertPred!"=="(orig, expected, "", __FILE__, line); - } - - //Test A.D. - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 39), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 36, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 31, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 13, 30, 33), FracSec.from!"hnsecs"(274))); - - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_273))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_998_274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_967_000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_966_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_167_000))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_166_999))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_000_274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 27), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 24, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 29, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 11, 30, 33), FracSec.from!"hnsecs"(274))); - - //Test B.C. - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 39), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 36, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), FracSec.from!"hnsecs"(274))); - - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_273))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_998_274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_967_000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_966_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_167_000))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_166_999))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_000_274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 27), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 24, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), FracSec.from!"hnsecs"(274))); - TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), FracSec.from!"hnsecs"(274))); - - //Test Both - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_000))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_998_000))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_997_445))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_000_000))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(8_000_000))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(7_666_667))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 58), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 57), FracSec.from!"hnsecs"(9_111_112))); - - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(2554))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(2_333_332))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 1), FracSec.from!"hnsecs"(9_999_999))); - TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 2), FracSec.from!"hnsecs"(888_887))); - - auto duration = dur!"seconds"(12); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst += duration)); - //static assert(!__traits(compiles, ist += duration)); - static assert(!__traits(compiles, cst -= duration)); - //static assert(!__traits(compiles, ist -= duration)); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) + dur!"weeks"(7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) + dur!"weeks"(-7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) + dur!"days"(7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) + dur!"days"(-7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33))); + + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) + dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) + dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) + dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) + dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) + dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) + dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) + dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(7))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) + dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(993))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) + dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) + dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"usecs"(999_993))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) + dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(7))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) + dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_993))); + + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - dur!"weeks"(-7) == SysTime(DateTime(1999, 8, 24, 12, 30, 33))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - dur!"weeks"(7) == SysTime(DateTime(1999, 5, 18, 12, 30, 33))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - dur!"days"(-7) == SysTime(DateTime(1999, 7, 13, 12, 30, 33))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - dur!"days"(7) == SysTime(DateTime(1999, 6, 29, 12, 30, 33))); + + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - dur!"hours"(-7) == SysTime(DateTime(1999, 7, 6, 19, 30, 33))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - dur!"hours"(7) == SysTime(DateTime(1999, 7, 6, 5, 30, 33))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - dur!"minutes"(-7) == SysTime(DateTime(1999, 7, 6, 12, 37, 33))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - dur!"minutes"(7) == SysTime(DateTime(1999, 7, 6, 12, 23, 33))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - dur!"seconds"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 40))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - dur!"seconds"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 26))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - dur!"msecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(7))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - dur!"msecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"msecs"(993))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - dur!"usecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(7))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - dur!"usecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"usecs"(999_993))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - dur!"hnsecs"(-7) == SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(7))); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - dur!"hnsecs"(7) == SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_993))); + + static void TestST(SysTime orig, long hnsecs, in SysTime expected) + { + orig += dur!"hnsecs"(hnsecs); + assert(orig == expected); } + + //Test A.D. + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 39), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 36, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 31, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(1999, 7, 6, 13, 30, 33), FracSec.from!"hnsecs"(274))); + + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_273))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_998_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_967_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_966_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_167_000))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_166_999))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_000_274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(1999, 7, 6, 12, 30, 27), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 24, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(1999, 7, 6, 12, 29, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(1999, 7, 6, 11, 30, 33), FracSec.from!"hnsecs"(274))); + + //Test B.C. + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 0, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(276))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(284))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(374))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1275))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(2274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(26_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 26_727, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(27_001))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_725, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_766_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_766_726, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_767_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_000_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 39), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 36, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 31, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), 36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 13, 30, 33), FracSec.from!"hnsecs"(274))); + + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(272))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -10, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(264))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -100, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(174))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -274, SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1001, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_999_273))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -2000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_998_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_967_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -33_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_966_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_274, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_167_000))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_833_275, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(8_166_999))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -1_000_000, SysTime(DateTime(-1999, 7, 6, 12, 30, 32), FracSec.from!"hnsecs"(9_000_274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -60_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 30, 27), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -3_600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 24, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -600_000_000L, SysTime(DateTime(-1999, 7, 6, 12, 29, 33), FracSec.from!"hnsecs"(274))); + TestST(SysTime(DateTime(-1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(274)), -36_000_000_000L, SysTime(DateTime(-1999, 7, 6, 11, 30, 33), FracSec.from!"hnsecs"(274))); + + //Test Both + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_998_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2555, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_997_445))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -1_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_000_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(8_000_000))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -2_333_333, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(7_666_667))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -10_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_000_000, SysTime(DateTime(0, 12, 31, 23, 59, 58), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), -20_888_888, SysTime(DateTime(0, 12, 31, 23, 59, 57), FracSec.from!"hnsecs"(9_111_112))); + + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), -1, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2555, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(2554))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 2_333_333, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(2_333_332))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 10_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_000_000, SysTime(DateTime(1, 1, 1, 0, 0, 1), FracSec.from!"hnsecs"(9_999_999))); + TestST(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 20_888_888, SysTime(DateTime(1, 1, 1, 0, 0, 2), FracSec.from!"hnsecs"(888_887))); + + { + auto st = SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)); + (st += dur!"hnsecs"(52)) += dur!"seconds"(-907); + assert(st == SysTime(DateTime(0, 12, 31, 23, 44, 53), FracSec.from!"hnsecs"(51))); + } + + auto duration = dur!"seconds"(12); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst += duration)); + //static assert(!__traits(compiles, ist += duration)); + static assert(!__traits(compiles, cst -= duration)); + //static assert(!__traits(compiles, ist -= duration)); } @@ -6331,102 +6115,99 @@ assert(st4 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), $(BOOKTABLE, $(TR $(TD SysTime) $(TD -) $(TD SysTime) $(TD -->) $(TD duration)) - ) - +/ - Duration opBinary(string op)(in SysTime rhs) const pure nothrow - if(op == "-") - { - return dur!"hnsecs"(_stdTime - rhs._stdTime); - } - - unittest - { - version(testStdDateTime) - { - _assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1998, 7, 6, 12, 30, 33)), - dur!"seconds"(31_536_000)); - _assertPred!"=="(SysTime(DateTime(1998, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), - dur!"seconds"(-31_536_000)); - - _assertPred!"=="(SysTime(DateTime(1999, 8, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), - dur!"seconds"(26_78_400)); - _assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 8, 6, 12, 30, 33)), - dur!"seconds"(-26_78_400)); - - _assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 5, 12, 30, 33)), - dur!"seconds"(86_400)); - _assertPred!"=="(SysTime(DateTime(1999, 7, 5, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), - dur!"seconds"(-86_400)); - - _assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 11, 30, 33)), - dur!"seconds"(3600)); - _assertPred!"=="(SysTime(DateTime(1999, 7, 6, 11, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), - dur!"seconds"(-3600)); - - _assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 31, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), - dur!"seconds"(60)); - _assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 31, 33)), - dur!"seconds"(-60)); - - _assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 34)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), - dur!"seconds"(1)); - _assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 34)), - dur!"seconds"(-1)); - - _assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(532)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), - dur!"msecs"(532)); - _assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(532)), - dur!"msecs"(-532)); - - _assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(333_347)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), - dur!"usecs"(333_347)); - _assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(333_347)), - dur!"usecs"(-333_347)); - - _assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_234_567)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)), - dur!"hnsecs"(1_234_567)); - _assertPred!"=="(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_234_567)), - dur!"hnsecs"(-1_234_567)); - - _assertPred!"=="(SysTime(DateTime(1, 1, 1, 12, 30, 33)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)), dur!"seconds"(45033)); - _assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(1, 1, 1, 12, 30, 33)), dur!"seconds"(-45033)); - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 12, 30, 33)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)), dur!"seconds"(-41367)); - _assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(0, 12, 31, 12, 30, 33)), dur!"seconds"(41367)); - - _assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), - dur!"hnsecs"(1)); - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)), - dur!"hnsecs"(-1)); - - auto tz = TimeZone.getTimeZone("America/Los_Angeles"); - - _assertPred!"=="(SysTime(DateTime(2011, 1, 13, 8, 17, 2), FracSec.from!"msecs"(296), tz) - - SysTime(DateTime(2011, 1, 13, 8, 17, 2), FracSec.from!"msecs"(296), tz), - dur!"hnsecs"(0)); - - _assertPred!"=="(SysTime(DateTime(2011, 1, 13, 8, 17, 2), FracSec.from!"msecs"(296), tz) - - SysTime(DateTime(2011, 1, 13, 8, 17, 2), FracSec.from!"msecs"(296), UTC()), - dur!"hours"(8)); + ) + +/ + Duration opBinary(string op)(in SysTime rhs) @safe const pure nothrow + if(op == "-") + { + return dur!"hnsecs"(_stdTime - rhs._stdTime); + } - _assertPred!"=="(SysTime(DateTime(2011, 1, 13, 8, 17, 2), FracSec.from!"msecs"(296), UTC()) - - SysTime(DateTime(2011, 1, 13, 8, 17, 2), FracSec.from!"msecs"(296), tz), - dur!"hours"(-8)); + unittest + { + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1998, 7, 6, 12, 30, 33)) == + dur!"seconds"(31_536_000)); + assert(SysTime(DateTime(1998, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(-31_536_000)); + + assert(SysTime(DateTime(1999, 8, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(26_78_400)); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 8, 6, 12, 30, 33)) == + dur!"seconds"(-26_78_400)); + + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 5, 12, 30, 33)) == + dur!"seconds"(86_400)); + assert(SysTime(DateTime(1999, 7, 5, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(-86_400)); + + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 11, 30, 33)) == + dur!"seconds"(3600)); + assert(SysTime(DateTime(1999, 7, 6, 11, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(-3600)); + + assert(SysTime(DateTime(1999, 7, 6, 12, 31, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(60)); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 31, 33)) == + dur!"seconds"(-60)); + + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 34)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"seconds"(1)); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 34)) == + dur!"seconds"(-1)); + + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(532)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"msecs"(532)); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"msecs"(532)) == + dur!"msecs"(-532)); + + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(333_347)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"usecs"(333_347)); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"usecs"(333_347)) == + dur!"usecs"(-333_347)); + + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_234_567)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33)) == + dur!"hnsecs"(1_234_567)); + assert(SysTime(DateTime(1999, 7, 6, 12, 30, 33)) - SysTime(DateTime(1999, 7, 6, 12, 30, 33), FracSec.from!"hnsecs"(1_234_567)) == + dur!"hnsecs"(-1_234_567)); + + assert(SysTime(DateTime(1, 1, 1, 12, 30, 33)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == dur!"seconds"(45033)); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(1, 1, 1, 12, 30, 33)) == dur!"seconds"(-45033)); + assert(SysTime(DateTime(0, 12, 31, 12, 30, 33)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == dur!"seconds"(-41367)); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(0, 12, 31, 12, 30, 33)) == dur!"seconds"(41367)); + + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)) - SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)) == + dur!"hnsecs"(1)); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)) - SysTime(DateTime(1, 1, 1, 0, 0, 0)) == + dur!"hnsecs"(-1)); + + auto tz = TimeZone.getTimeZone("America/Los_Angeles"); + + assert(SysTime(DateTime(2011, 1, 13, 8, 17, 2), FracSec.from!"msecs"(296), tz) - + SysTime(DateTime(2011, 1, 13, 8, 17, 2), FracSec.from!"msecs"(296), tz) == + dur!"hnsecs"(0)); + + assert(SysTime(DateTime(2011, 1, 13, 8, 17, 2), FracSec.from!"msecs"(296), tz) - + SysTime(DateTime(2011, 1, 13, 8, 17, 2), FracSec.from!"msecs"(296), UTC()) == + dur!"hours"(8)); + + assert(SysTime(DateTime(2011, 1, 13, 8, 17, 2), FracSec.from!"msecs"(296), UTC()) - + SysTime(DateTime(2011, 1, 13, 8, 17, 2), FracSec.from!"msecs"(296), tz) == + dur!"hours"(-8)); - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st - st)); - static assert(__traits(compiles, cst - st)); - //static assert(__traits(compiles, ist - st)); + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, st - st)); + static assert(__traits(compiles, cst - st)); + //static assert(__traits(compiles, ist - st)); - static assert(__traits(compiles, st - cst)); - static assert(__traits(compiles, cst - cst)); - //static assert(__traits(compiles, ist - cst)); + static assert(__traits(compiles, st - cst)); + static assert(__traits(compiles, cst - cst)); + //static assert(__traits(compiles, ist - cst)); - //static assert(__traits(compiles, st - ist)); - //static assert(__traits(compiles, cst - ist)); - //static assert(__traits(compiles, ist - ist)); - } + //static assert(__traits(compiles, st - ist)); + //static assert(__traits(compiles, cst - ist)); + //static assert(__traits(compiles, ist - ist)); } @@ -6450,123 +6231,109 @@ assert(st4 == SysTime(DateTime(2010, 1, 1, 0, 0, 0), Params: rhs = The $(LREF SysTime) to subtract from this one. - - Examples: --------------------- -assert(SysTime(Date(1999, 2, 1)).diffMonths(SysTime(Date(1999, 1, 31))) == 1); -assert(SysTime(Date(1999, 1, 31)).diffMonths(SysTime(Date(1999, 2, 1))) == -1); -assert(SysTime(Date(1999, 3, 1)).diffMonths(SysTime(Date(1999, 1, 1))) == 2); -assert(SysTime(Date(1999, 1, 1)).diffMonths(SysTime(Date(1999, 3, 31))) == -2); --------------------- +/ - int diffMonths(in SysTime rhs) const nothrow + int diffMonths(in SysTime rhs) @safe const nothrow { return (cast(Date)this).diffMonths(cast(Date)rhs); } + /// unittest { - version(testStdDateTime) - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.diffMonths(st))); - static assert(__traits(compiles, cst.diffMonths(st))); - //static assert(__traits(compiles, ist.diffMonths(st))); + assert(SysTime(Date(1999, 2, 1)).diffMonths( + SysTime(Date(1999, 1, 31))) == 1); - static assert(__traits(compiles, st.diffMonths(cst))); - static assert(__traits(compiles, cst.diffMonths(cst))); - //static assert(__traits(compiles, ist.diffMonths(cst))); + assert(SysTime(Date(1999, 1, 31)).diffMonths( + SysTime(Date(1999, 2, 1))) == -1); - //static assert(__traits(compiles, st.diffMonths(ist))); - //static assert(__traits(compiles, cst.diffMonths(ist))); - //static assert(__traits(compiles, ist.diffMonths(ist))); + assert(SysTime(Date(1999, 3, 1)).diffMonths( + SysTime(Date(1999, 1, 1))) == 2); - //Verify Examples. - assert(SysTime(Date(1999, 2, 1)).diffMonths(SysTime(Date(1999, 1, 31))) == 1); - assert(SysTime(Date(1999, 1, 31)).diffMonths(SysTime(Date(1999, 2, 1))) == -1); - assert(SysTime(Date(1999, 3, 1)).diffMonths(SysTime(Date(1999, 1, 1))) == 2); - assert(SysTime(Date(1999, 1, 1)).diffMonths(SysTime(Date(1999, 3, 31))) == -2); - } + assert(SysTime(Date(1999, 1, 1)).diffMonths( + SysTime(Date(1999, 3, 31))) == -2); + } + + unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, st.diffMonths(st))); + static assert(__traits(compiles, cst.diffMonths(st))); + //static assert(__traits(compiles, ist.diffMonths(st))); + + static assert(__traits(compiles, st.diffMonths(cst))); + static assert(__traits(compiles, cst.diffMonths(cst))); + //static assert(__traits(compiles, ist.diffMonths(cst))); + + //static assert(__traits(compiles, st.diffMonths(ist))); + //static assert(__traits(compiles, cst.diffMonths(ist))); + //static assert(__traits(compiles, ist.diffMonths(ist))); } /++ Whether this $(LREF SysTime) is in a leap year. +/ - @property bool isLeapYear() const nothrow + @property bool isLeapYear() @safe const nothrow { return (cast(Date)this).isLeapYear; } unittest { - version(testStdDateTime) - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.isLeapYear)); - static assert(__traits(compiles, cst.isLeapYear)); - //static assert(__traits(compiles, ist.isLeapYear)); - } + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, st.isLeapYear)); + static assert(__traits(compiles, cst.isLeapYear)); + //static assert(__traits(compiles, ist.isLeapYear)); } /++ Day of the week this $(LREF SysTime) is on. +/ - @property DayOfWeek dayOfWeek() const nothrow + @property DayOfWeek dayOfWeek() @safe const nothrow { return getDayOfWeek(dayOfGregorianCal); } unittest { - version(testStdDateTime) - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.dayOfWeek)); - static assert(__traits(compiles, cst.dayOfWeek)); - //static assert(__traits(compiles, ist.dayOfWeek)); - } + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, st.dayOfWeek)); + static assert(__traits(compiles, cst.dayOfWeek)); + //static assert(__traits(compiles, ist.dayOfWeek)); } /++ Day of the year this $(LREF SysTime) is on. - - Examples: --------------------- -assert(SysTime(DateTime(1999, 1, 1, 12, 22, 7)).dayOfYear == 1); -assert(SysTime(DateTime(1999, 12, 31, 7, 2, 59)).dayOfYear == 365); -assert(SysTime(DateTime(2000, 12, 31, 21, 20, 0)).dayOfYear == 366); --------------------- +/ - @property ushort dayOfYear() const nothrow + @property ushort dayOfYear() @safe const nothrow { return (cast(Date)this).dayOfYear; } + /// unittest { - version(testStdDateTime) - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.dayOfYear)); - static assert(__traits(compiles, cst.dayOfYear)); - //static assert(__traits(compiles, ist.dayOfYear)); + assert(SysTime(DateTime(1999, 1, 1, 12, 22, 7)).dayOfYear == 1); + assert(SysTime(DateTime(1999, 12, 31, 7, 2, 59)).dayOfYear == 365); + assert(SysTime(DateTime(2000, 12, 31, 21, 20, 0)).dayOfYear == 366); + } - //Verify Examples. - assert(SysTime(DateTime(1999, 1, 1, 12, 22, 7)).dayOfYear == 1); - assert(SysTime(DateTime(1999, 12, 31, 7, 2, 59)).dayOfYear == 365); - assert(SysTime(DateTime(2000, 12, 31, 21, 20, 0)).dayOfYear == 366); - } + unittest + { + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, st.dayOfYear)); + static assert(__traits(compiles, cst.dayOfYear)); + //static assert(__traits(compiles, ist.dayOfYear)); } @@ -6577,7 +6344,7 @@ assert(SysTime(DateTime(2000, 12, 31, 21, 20, 0)).dayOfYear == 366); day = The day of the year to set which day of the year this $(LREF SysTime) is on. +/ - @property void dayOfYear(int day) + @property void dayOfYear(int day) @safe { immutable hnsecs = adjTime; immutable days = convert!("hnsecs", "days")(hnsecs); @@ -6593,36 +6360,19 @@ assert(SysTime(DateTime(2000, 12, 31, 21, 20, 0)).dayOfYear == 366); unittest { - version(testStdDateTime) - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.dayOfYear = 12)); - static assert(!__traits(compiles, cst.dayOfYear = 12)); - //static assert(!__traits(compiles, ist.dayOfYear = 12)); - } + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, st.dayOfYear = 12)); + static assert(!__traits(compiles, cst.dayOfYear = 12)); + //static assert(!__traits(compiles, ist.dayOfYear = 12)); } /++ The Xth day of the Gregorian Calendar that this $(LREF SysTime) is on. - - Examples: --------------------- -assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); -assert(SysTime(DateTime(1, 12, 31, 23, 59, 59)).dayOfGregorianCal == 365); -assert(SysTime(DateTime(2, 1, 1, 2, 2, 2)).dayOfGregorianCal == 366); - -assert(SysTime(DateTime(0, 12, 31, 7, 7, 7)).dayOfGregorianCal == 0); -assert(SysTime(DateTime(0, 1, 1, 19, 30, 0)).dayOfGregorianCal == -365); -assert(SysTime(DateTime(-1, 12, 31, 4, 7, 0)).dayOfGregorianCal == -366); - -assert(SysTime(DateTime(2000, 1, 1, 9, 30, 20)).dayOfGregorianCal == 730_120); -assert(SysTime(DateTime(2010, 12, 31, 15, 45, 50)).dayOfGregorianCal == 734_137); --------------------- +/ - @property int dayOfGregorianCal() const nothrow + @property int dayOfGregorianCal() @safe const nothrow { immutable adjustedTime = adjTime; @@ -6638,186 +6388,186 @@ assert(SysTime(DateTime(2010, 12, 31, 15, 45, 50)).dayOfGregorianCal == 734_137) return hnsecs == 0 ? days + 1 : days; } + /// unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal, 1); - _assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)).dayOfGregorianCal, 1); - _assertPred!"=="(SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal, 1); - - _assertPred!"=="(SysTime(DateTime(1, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 1); - _assertPred!"=="(SysTime(DateTime(1, 1, 2, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 2); - _assertPred!"=="(SysTime(DateTime(1, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 32); - _assertPred!"=="(SysTime(DateTime(2, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 366); - _assertPred!"=="(SysTime(DateTime(3, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 731); - _assertPred!"=="(SysTime(DateTime(4, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 1096); - _assertPred!"=="(SysTime(DateTime(5, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 1462); - _assertPred!"=="(SysTime(DateTime(50, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 17_898); - _assertPred!"=="(SysTime(DateTime(97, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 35_065); - _assertPred!"=="(SysTime(DateTime(100, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 36_160); - _assertPred!"=="(SysTime(DateTime(101, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 36_525); - _assertPred!"=="(SysTime(DateTime(105, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 37_986); - _assertPred!"=="(SysTime(DateTime(200, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 72_684); - _assertPred!"=="(SysTime(DateTime(201, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 73_049); - _assertPred!"=="(SysTime(DateTime(300, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 109_208); - _assertPred!"=="(SysTime(DateTime(301, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 109_573); - _assertPred!"=="(SysTime(DateTime(400, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 145_732); - _assertPred!"=="(SysTime(DateTime(401, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 146_098); - _assertPred!"=="(SysTime(DateTime(500, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 182_257); - _assertPred!"=="(SysTime(DateTime(501, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 182_622); - _assertPred!"=="(SysTime(DateTime(1000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 364_878); - _assertPred!"=="(SysTime(DateTime(1001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 365_243); - _assertPred!"=="(SysTime(DateTime(1600, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 584_023); - _assertPred!"=="(SysTime(DateTime(1601, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 584_389); - _assertPred!"=="(SysTime(DateTime(1900, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 693_596); - _assertPred!"=="(SysTime(DateTime(1901, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 693_961); - _assertPred!"=="(SysTime(DateTime(1945, 11, 12, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 710_347); - _assertPred!"=="(SysTime(DateTime(1999, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 729_755); - _assertPred!"=="(SysTime(DateTime(2000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 730_120); - _assertPred!"=="(SysTime(DateTime(2001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 730_486); - - _assertPred!"=="(SysTime(DateTime(2010, 1, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_773); - _assertPred!"=="(SysTime(DateTime(2010, 1, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_803); - _assertPred!"=="(SysTime(DateTime(2010, 2, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_804); - _assertPred!"=="(SysTime(DateTime(2010, 2, 28, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_831); - _assertPred!"=="(SysTime(DateTime(2010, 3, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_832); - _assertPred!"=="(SysTime(DateTime(2010, 3, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_862); - _assertPred!"=="(SysTime(DateTime(2010, 4, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_863); - _assertPred!"=="(SysTime(DateTime(2010, 4, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_892); - _assertPred!"=="(SysTime(DateTime(2010, 5, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_893); - _assertPred!"=="(SysTime(DateTime(2010, 5, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_923); - _assertPred!"=="(SysTime(DateTime(2010, 6, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_924); - _assertPred!"=="(SysTime(DateTime(2010, 6, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_953); - _assertPred!"=="(SysTime(DateTime(2010, 7, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_954); - _assertPred!"=="(SysTime(DateTime(2010, 7, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_984); - _assertPred!"=="(SysTime(DateTime(2010, 8, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 733_985); - _assertPred!"=="(SysTime(DateTime(2010, 8, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_015); - _assertPred!"=="(SysTime(DateTime(2010, 9, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_016); - _assertPred!"=="(SysTime(DateTime(2010, 9, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_045); - _assertPred!"=="(SysTime(DateTime(2010, 10, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_046); - _assertPred!"=="(SysTime(DateTime(2010, 10, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_076); - _assertPred!"=="(SysTime(DateTime(2010, 11, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_077); - _assertPred!"=="(SysTime(DateTime(2010, 11, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_106); - _assertPred!"=="(SysTime(DateTime(2010, 12, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_107); - _assertPred!"=="(SysTime(DateTime(2010, 12, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, 734_137); - - _assertPred!"=="(SysTime(DateTime(2012, 2, 1, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, 734_534); - _assertPred!"=="(SysTime(DateTime(2012, 2, 28, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, 734_561); - _assertPred!"=="(SysTime(DateTime(2012, 2, 29, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, 734_562); - _assertPred!"=="(SysTime(DateTime(2012, 3, 1, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, 734_563); - - //Test B.C. - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal, 0); - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998)).dayOfGregorianCal, 0); - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal, 0); - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(1)).dayOfGregorianCal, 0); - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal, 0); - - _assertPred!"=="(SysTime(DateTime(-1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal, -366); - _assertPred!"=="(SysTime(DateTime(-1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998)).dayOfGregorianCal, -366); - _assertPred!"=="(SysTime(DateTime(-1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal, -366); - _assertPred!"=="(SysTime(DateTime(-1, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal, -366); - - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, 0); - _assertPred!"=="(SysTime(DateTime(0, 12, 30, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -1); - _assertPred!"=="(SysTime(DateTime(0, 12, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -30); - _assertPred!"=="(SysTime(DateTime(0, 11, 30, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -31); - - _assertPred!"=="(SysTime(DateTime(-1, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -366); - _assertPred!"=="(SysTime(DateTime(-1, 12, 30, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -367); - _assertPred!"=="(SysTime(DateTime(-1, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -730); - _assertPred!"=="(SysTime(DateTime(-2, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -731); - _assertPred!"=="(SysTime(DateTime(-2, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -1095); - _assertPred!"=="(SysTime(DateTime(-3, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -1096); - _assertPred!"=="(SysTime(DateTime(-3, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -1460); - _assertPred!"=="(SysTime(DateTime(-4, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -1461); - _assertPred!"=="(SysTime(DateTime(-4, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -1826); - _assertPred!"=="(SysTime(DateTime(-5, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -1827); - _assertPred!"=="(SysTime(DateTime(-5, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -2191); - _assertPred!"=="(SysTime(DateTime(-9, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -3652); - - _assertPred!"=="(SysTime(DateTime(-49, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -18_262); - _assertPred!"=="(SysTime(DateTime(-50, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -18_627); - _assertPred!"=="(SysTime(DateTime(-97, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -35_794); - _assertPred!"=="(SysTime(DateTime(-99, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -36_160); - _assertPred!"=="(SysTime(DateTime(-99, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -36_524); - _assertPred!"=="(SysTime(DateTime(-100, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -36_889); - _assertPred!"=="(SysTime(DateTime(-101, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -37_254); - _assertPred!"=="(SysTime(DateTime(-105, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -38_715); - _assertPred!"=="(SysTime(DateTime(-200, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -73_413); - _assertPred!"=="(SysTime(DateTime(-201, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -73_778); - _assertPred!"=="(SysTime(DateTime(-300, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -109_937); - _assertPred!"=="(SysTime(DateTime(-301, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -110_302); - _assertPred!"=="(SysTime(DateTime(-400, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -146_097); - _assertPred!"=="(SysTime(DateTime(-400, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -146_462); - _assertPred!"=="(SysTime(DateTime(-401, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -146_827); - _assertPred!"=="(SysTime(DateTime(-499, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -182_621); - _assertPred!"=="(SysTime(DateTime(-500, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -182_986); - _assertPred!"=="(SysTime(DateTime(-501, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -183_351); - _assertPred!"=="(SysTime(DateTime(-1000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -365_607); - _assertPred!"=="(SysTime(DateTime(-1001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -365_972); - _assertPred!"=="(SysTime(DateTime(-1599, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -584_387); - _assertPred!"=="(SysTime(DateTime(-1600, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -584_388); - _assertPred!"=="(SysTime(DateTime(-1600, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -584_753); - _assertPred!"=="(SysTime(DateTime(-1601, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -585_118); - _assertPred!"=="(SysTime(DateTime(-1900, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -694_325); - _assertPred!"=="(SysTime(DateTime(-1901, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -694_690); - _assertPred!"=="(SysTime(DateTime(-1999, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -730_484); - _assertPred!"=="(SysTime(DateTime(-2000, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -730_485); - _assertPred!"=="(SysTime(DateTime(-2000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -730_850); - _assertPred!"=="(SysTime(DateTime(-2001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal, -731_215); - - _assertPred!"=="(SysTime(DateTime(-2010, 1, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_502); - _assertPred!"=="(SysTime(DateTime(-2010, 1, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_472); - _assertPred!"=="(SysTime(DateTime(-2010, 2, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_471); - _assertPred!"=="(SysTime(DateTime(-2010, 2, 28, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_444); - _assertPred!"=="(SysTime(DateTime(-2010, 3, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_443); - _assertPred!"=="(SysTime(DateTime(-2010, 3, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_413); - _assertPred!"=="(SysTime(DateTime(-2010, 4, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_412); - _assertPred!"=="(SysTime(DateTime(-2010, 4, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_383); - _assertPred!"=="(SysTime(DateTime(-2010, 5, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_382); - _assertPred!"=="(SysTime(DateTime(-2010, 5, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_352); - _assertPred!"=="(SysTime(DateTime(-2010, 6, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_351); - _assertPred!"=="(SysTime(DateTime(-2010, 6, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_322); - _assertPred!"=="(SysTime(DateTime(-2010, 7, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_321); - _assertPred!"=="(SysTime(DateTime(-2010, 7, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_291); - _assertPred!"=="(SysTime(DateTime(-2010, 8, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_290); - _assertPred!"=="(SysTime(DateTime(-2010, 8, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_260); - _assertPred!"=="(SysTime(DateTime(-2010, 9, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_259); - _assertPred!"=="(SysTime(DateTime(-2010, 9, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_230); - _assertPred!"=="(SysTime(DateTime(-2010, 10, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_229); - _assertPred!"=="(SysTime(DateTime(-2010, 10, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_199); - _assertPred!"=="(SysTime(DateTime(-2010, 11, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_198); - _assertPred!"=="(SysTime(DateTime(-2010, 11, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_169); - _assertPred!"=="(SysTime(DateTime(-2010, 12, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_168); - _assertPred!"=="(SysTime(DateTime(-2010, 12, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal, -734_138); - - _assertPred!"=="(SysTime(DateTime(-2012, 2, 1, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, -735_202); - _assertPred!"=="(SysTime(DateTime(-2012, 2, 28, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, -735_175); - _assertPred!"=="(SysTime(DateTime(-2012, 2, 29, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, -735_174); - _assertPred!"=="(SysTime(DateTime(-2012, 3, 1, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, -735_173); - - _assertPred!"=="(SysTime(DateTime(-3760, 9, 7, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal, -1_373_427); //Start of Hebrew Calendar - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.dayOfGregorianCal)); - //static assert(__traits(compiles, ist.dayOfGregorianCal)); - - //Verify Examples. - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); - assert(SysTime(DateTime(1, 12, 31, 23, 59, 59)).dayOfGregorianCal == 365); - assert(SysTime(DateTime(2, 1, 1, 2, 2, 2)).dayOfGregorianCal == 366); - - assert(SysTime(DateTime(0, 12, 31, 7, 7, 7)).dayOfGregorianCal == 0); - assert(SysTime(DateTime(0, 1, 1, 19, 30, 0)).dayOfGregorianCal == -365); - assert(SysTime(DateTime(-1, 12, 31, 4, 7, 0)).dayOfGregorianCal == -366); - - assert(SysTime(DateTime(2000, 1, 1, 9, 30, 20)).dayOfGregorianCal == 730_120); - assert(SysTime(DateTime(2010, 12, 31, 15, 45, 50)).dayOfGregorianCal == 734_137); - } + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1); + assert(SysTime(DateTime(1, 12, 31, 23, 59, 59)).dayOfGregorianCal == 365); + assert(SysTime(DateTime(2, 1, 1, 2, 2, 2)).dayOfGregorianCal == 366); + + assert(SysTime(DateTime(0, 12, 31, 7, 7, 7)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 1, 1, 19, 30, 0)).dayOfGregorianCal == -365); + assert(SysTime(DateTime(-1, 12, 31, 4, 7, 0)).dayOfGregorianCal == -366); + + assert(SysTime(DateTime(2000, 1, 1, 9, 30, 20)).dayOfGregorianCal == 730_120); + assert(SysTime(DateTime(2010, 12, 31, 15, 45, 50)).dayOfGregorianCal == 734_137); + } + + unittest + { + //Test A.D. + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal == 1); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)).dayOfGregorianCal == 1); + assert(SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal == 1); + + assert(SysTime(DateTime(1, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 1); + assert(SysTime(DateTime(1, 1, 2, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 2); + assert(SysTime(DateTime(1, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 32); + assert(SysTime(DateTime(2, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 366); + assert(SysTime(DateTime(3, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 731); + assert(SysTime(DateTime(4, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 1096); + assert(SysTime(DateTime(5, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 1462); + assert(SysTime(DateTime(50, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 17_898); + assert(SysTime(DateTime(97, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 35_065); + assert(SysTime(DateTime(100, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 36_160); + assert(SysTime(DateTime(101, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 36_525); + assert(SysTime(DateTime(105, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 37_986); + assert(SysTime(DateTime(200, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 72_684); + assert(SysTime(DateTime(201, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 73_049); + assert(SysTime(DateTime(300, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 109_208); + assert(SysTime(DateTime(301, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 109_573); + assert(SysTime(DateTime(400, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 145_732); + assert(SysTime(DateTime(401, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 146_098); + assert(SysTime(DateTime(500, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 182_257); + assert(SysTime(DateTime(501, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 182_622); + assert(SysTime(DateTime(1000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 364_878); + assert(SysTime(DateTime(1001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 365_243); + assert(SysTime(DateTime(1600, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 584_023); + assert(SysTime(DateTime(1601, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 584_389); + assert(SysTime(DateTime(1900, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 693_596); + assert(SysTime(DateTime(1901, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 693_961); + assert(SysTime(DateTime(1945, 11, 12, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 710_347); + assert(SysTime(DateTime(1999, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 729_755); + assert(SysTime(DateTime(2000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 730_120); + assert(SysTime(DateTime(2001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 730_486); + + assert(SysTime(DateTime(2010, 1, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 733_773); + assert(SysTime(DateTime(2010, 1, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 733_803); + assert(SysTime(DateTime(2010, 2, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 733_804); + assert(SysTime(DateTime(2010, 2, 28, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 733_831); + assert(SysTime(DateTime(2010, 3, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 733_832); + assert(SysTime(DateTime(2010, 3, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 733_862); + assert(SysTime(DateTime(2010, 4, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 733_863); + assert(SysTime(DateTime(2010, 4, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 733_892); + assert(SysTime(DateTime(2010, 5, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 733_893); + assert(SysTime(DateTime(2010, 5, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 733_923); + assert(SysTime(DateTime(2010, 6, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 733_924); + assert(SysTime(DateTime(2010, 6, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 733_953); + assert(SysTime(DateTime(2010, 7, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 733_954); + assert(SysTime(DateTime(2010, 7, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 733_984); + assert(SysTime(DateTime(2010, 8, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 733_985); + assert(SysTime(DateTime(2010, 8, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 734_015); + assert(SysTime(DateTime(2010, 9, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 734_016); + assert(SysTime(DateTime(2010, 9, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 734_045); + assert(SysTime(DateTime(2010, 10, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 734_046); + assert(SysTime(DateTime(2010, 10, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 734_076); + assert(SysTime(DateTime(2010, 11, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 734_077); + assert(SysTime(DateTime(2010, 11, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 734_106); + assert(SysTime(DateTime(2010, 12, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 734_107); + assert(SysTime(DateTime(2010, 12, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == 734_137); + + assert(SysTime(DateTime(2012, 2, 1, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal == 734_534); + assert(SysTime(DateTime(2012, 2, 28, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal == 734_561); + assert(SysTime(DateTime(2012, 2, 29, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal == 734_562); + assert(SysTime(DateTime(2012, 3, 1, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal == 734_563); + + //Test B.C. + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(1)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal == 0); + + assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal == -366); + assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_998)).dayOfGregorianCal == -366); + assert(SysTime(DateTime(-1, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal == -366); + assert(SysTime(DateTime(-1, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal == -366); + + assert(SysTime(DateTime(0, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == 0); + assert(SysTime(DateTime(0, 12, 30, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -1); + assert(SysTime(DateTime(0, 12, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -30); + assert(SysTime(DateTime(0, 11, 30, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -31); + + assert(SysTime(DateTime(-1, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -366); + assert(SysTime(DateTime(-1, 12, 30, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -367); + assert(SysTime(DateTime(-1, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -730); + assert(SysTime(DateTime(-2, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -731); + assert(SysTime(DateTime(-2, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -1095); + assert(SysTime(DateTime(-3, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -1096); + assert(SysTime(DateTime(-3, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -1460); + assert(SysTime(DateTime(-4, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -1461); + assert(SysTime(DateTime(-4, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -1826); + assert(SysTime(DateTime(-5, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -1827); + assert(SysTime(DateTime(-5, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -2191); + assert(SysTime(DateTime(-9, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -3652); + + assert(SysTime(DateTime(-49, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -18_262); + assert(SysTime(DateTime(-50, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -18_627); + assert(SysTime(DateTime(-97, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -35_794); + assert(SysTime(DateTime(-99, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -36_160); + assert(SysTime(DateTime(-99, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -36_524); + assert(SysTime(DateTime(-100, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -36_889); + assert(SysTime(DateTime(-101, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -37_254); + assert(SysTime(DateTime(-105, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -38_715); + assert(SysTime(DateTime(-200, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -73_413); + assert(SysTime(DateTime(-201, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -73_778); + assert(SysTime(DateTime(-300, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -109_937); + assert(SysTime(DateTime(-301, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -110_302); + assert(SysTime(DateTime(-400, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -146_097); + assert(SysTime(DateTime(-400, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -146_462); + assert(SysTime(DateTime(-401, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -146_827); + assert(SysTime(DateTime(-499, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -182_621); + assert(SysTime(DateTime(-500, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -182_986); + assert(SysTime(DateTime(-501, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -183_351); + assert(SysTime(DateTime(-1000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -365_607); + assert(SysTime(DateTime(-1001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -365_972); + assert(SysTime(DateTime(-1599, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -584_387); + assert(SysTime(DateTime(-1600, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -584_388); + assert(SysTime(DateTime(-1600, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -584_753); + assert(SysTime(DateTime(-1601, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -585_118); + assert(SysTime(DateTime(-1900, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -694_325); + assert(SysTime(DateTime(-1901, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -694_690); + assert(SysTime(DateTime(-1999, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -730_484); + assert(SysTime(DateTime(-2000, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -730_485); + assert(SysTime(DateTime(-2000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -730_850); + assert(SysTime(DateTime(-2001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)).dayOfGregorianCal == -731_215); + + assert(SysTime(DateTime(-2010, 1, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_502); + assert(SysTime(DateTime(-2010, 1, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_472); + assert(SysTime(DateTime(-2010, 2, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_471); + assert(SysTime(DateTime(-2010, 2, 28, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_444); + assert(SysTime(DateTime(-2010, 3, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_443); + assert(SysTime(DateTime(-2010, 3, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_413); + assert(SysTime(DateTime(-2010, 4, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_412); + assert(SysTime(DateTime(-2010, 4, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_383); + assert(SysTime(DateTime(-2010, 5, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_382); + assert(SysTime(DateTime(-2010, 5, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_352); + assert(SysTime(DateTime(-2010, 6, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_351); + assert(SysTime(DateTime(-2010, 6, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_322); + assert(SysTime(DateTime(-2010, 7, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_321); + assert(SysTime(DateTime(-2010, 7, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_291); + assert(SysTime(DateTime(-2010, 8, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_290); + assert(SysTime(DateTime(-2010, 8, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_260); + assert(SysTime(DateTime(-2010, 9, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_259); + assert(SysTime(DateTime(-2010, 9, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_230); + assert(SysTime(DateTime(-2010, 10, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_229); + assert(SysTime(DateTime(-2010, 10, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_199); + assert(SysTime(DateTime(-2010, 11, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_198); + assert(SysTime(DateTime(-2010, 11, 30, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_169); + assert(SysTime(DateTime(-2010, 12, 1, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_168); + assert(SysTime(DateTime(-2010, 12, 31, 23, 59, 59), FracSec.from!"msecs"(999)).dayOfGregorianCal == -734_138); + + assert(SysTime(DateTime(-2012, 2, 1, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal == -735_202); + assert(SysTime(DateTime(-2012, 2, 28, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal == -735_175); + assert(SysTime(DateTime(-2012, 2, 29, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal == -735_174); + assert(SysTime(DateTime(-2012, 3, 1, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal == -735_173); + + assert(SysTime(DateTime(-3760, 9, 7, 0, 0, 0), FracSec.from!"msecs"(0)).dayOfGregorianCal == -1_373_427); //Start of Hebrew Calendar + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cst.dayOfGregorianCal)); + //static assert(__traits(compiles, ist.dayOfGregorianCal)); } @@ -6825,154 +6575,151 @@ assert(SysTime(DateTime(2010, 12, 31, 15, 45, 50)).dayOfGregorianCal == 734_137) //between Date and SysTime. unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(Date(1, 1, 1).dayOfGregorianCal, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(1, 1, 2).dayOfGregorianCal, SysTime(DateTime(1, 1, 2, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(1, 2, 1).dayOfGregorianCal, SysTime(DateTime(1, 2, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(2, 1, 1).dayOfGregorianCal, SysTime(DateTime(2, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(3, 1, 1).dayOfGregorianCal, SysTime(DateTime(3, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(4, 1, 1).dayOfGregorianCal, SysTime(DateTime(4, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(5, 1, 1).dayOfGregorianCal, SysTime(DateTime(5, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(50, 1, 1).dayOfGregorianCal, SysTime(DateTime(50, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(97, 1, 1).dayOfGregorianCal, SysTime(DateTime(97, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(100, 1, 1).dayOfGregorianCal, SysTime(DateTime(100, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(101, 1, 1).dayOfGregorianCal, SysTime(DateTime(101, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(105, 1, 1).dayOfGregorianCal, SysTime(DateTime(105, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(200, 1, 1).dayOfGregorianCal, SysTime(DateTime(200, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(201, 1, 1).dayOfGregorianCal, SysTime(DateTime(201, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(300, 1, 1).dayOfGregorianCal, SysTime(DateTime(300, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(301, 1, 1).dayOfGregorianCal, SysTime(DateTime(301, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(400, 1, 1).dayOfGregorianCal, SysTime(DateTime(400, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(401, 1, 1).dayOfGregorianCal, SysTime(DateTime(401, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(500, 1, 1).dayOfGregorianCal, SysTime(DateTime(500, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(501, 1, 1).dayOfGregorianCal, SysTime(DateTime(501, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(1000, 1, 1).dayOfGregorianCal, SysTime(DateTime(1000, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(1001, 1, 1).dayOfGregorianCal, SysTime(DateTime(1001, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(1600, 1, 1).dayOfGregorianCal, SysTime(DateTime(1600, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(1601, 1, 1).dayOfGregorianCal, SysTime(DateTime(1601, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(1900, 1, 1).dayOfGregorianCal, SysTime(DateTime(1900, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(1901, 1, 1).dayOfGregorianCal, SysTime(DateTime(1901, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(1945, 11, 12).dayOfGregorianCal, SysTime(DateTime(1945, 11, 12, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(1999, 1, 1).dayOfGregorianCal, SysTime(DateTime(1999, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(1999, 7, 6).dayOfGregorianCal, SysTime(DateTime(1999, 7, 6, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(2000, 1, 1).dayOfGregorianCal, SysTime(DateTime(2000, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(2001, 1, 1).dayOfGregorianCal, SysTime(DateTime(2001, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - - _assertPred!"=="(Date(2010, 1, 1).dayOfGregorianCal, SysTime(DateTime(2010, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 1, 31).dayOfGregorianCal, SysTime(DateTime(2010, 1, 31, 23, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 2, 1).dayOfGregorianCal, SysTime(DateTime(2010, 2, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 2, 28).dayOfGregorianCal, SysTime(DateTime(2010, 2, 28, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 3, 1).dayOfGregorianCal, SysTime(DateTime(2010, 3, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 3, 31).dayOfGregorianCal, SysTime(DateTime(2010, 3, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 4, 1).dayOfGregorianCal, SysTime(DateTime(2010, 4, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 4, 30).dayOfGregorianCal, SysTime(DateTime(2010, 4, 30, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 5, 1).dayOfGregorianCal, SysTime(DateTime(2010, 5, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 5, 31).dayOfGregorianCal, SysTime(DateTime(2010, 5, 31, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 6, 1).dayOfGregorianCal, SysTime(DateTime(2010, 6, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 6, 30).dayOfGregorianCal, SysTime(DateTime(2010, 6, 30, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 7, 1).dayOfGregorianCal, SysTime(DateTime(2010, 7, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 7, 31).dayOfGregorianCal, SysTime(DateTime(2010, 7, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 8, 1).dayOfGregorianCal, SysTime(DateTime(2010, 8, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 8, 31).dayOfGregorianCal, SysTime(DateTime(2010, 8, 31, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 9, 1).dayOfGregorianCal, SysTime(DateTime(2010, 9, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 9, 30).dayOfGregorianCal, SysTime(DateTime(2010, 9, 30, 12, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 10, 1).dayOfGregorianCal, SysTime(DateTime(2010, 10, 1, 0, 12, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 10, 31).dayOfGregorianCal, SysTime(DateTime(2010, 10, 31, 0, 0, 12), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 11, 1).dayOfGregorianCal, SysTime(DateTime(2010, 11, 1, 23, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 11, 30).dayOfGregorianCal, SysTime(DateTime(2010, 11, 30, 0, 59, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 12, 1).dayOfGregorianCal, SysTime(DateTime(2010, 12, 1, 0, 0, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(2010, 12, 31).dayOfGregorianCal, SysTime(DateTime(2010, 12, 31, 0, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - - _assertPred!"=="(Date(2012, 2, 1).dayOfGregorianCal, SysTime(DateTime(2012, 2, 1, 23, 0, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(2012, 2, 28).dayOfGregorianCal, SysTime(DateTime(2012, 2, 28, 23, 59, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(2012, 2, 29).dayOfGregorianCal, SysTime(DateTime(2012, 2, 29, 7, 7, 7), FracSec.from!"hnsecs"(7)).dayOfGregorianCal); - _assertPred!"=="(Date(2012, 3, 1).dayOfGregorianCal, SysTime(DateTime(2012, 3, 1, 7, 7, 7), FracSec.from!"hnsecs"(7)).dayOfGregorianCal); - - //Test B.C. - _assertPred!"=="(Date(0, 12, 31).dayOfGregorianCal, SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(0, 12, 30).dayOfGregorianCal, SysTime(DateTime(0, 12, 30, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(0, 12, 1).dayOfGregorianCal, SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(0, 11, 30).dayOfGregorianCal, SysTime(DateTime(0, 11, 30, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - - _assertPred!"=="(Date(-1, 12, 31).dayOfGregorianCal, SysTime(DateTime(-1, 12, 31, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-1, 12, 30).dayOfGregorianCal, SysTime(DateTime(-1, 12, 30, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-1, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(-2, 12, 31).dayOfGregorianCal, SysTime(DateTime(-2, 12, 31, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(-2, 1, 1).dayOfGregorianCal, SysTime(DateTime(-2, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-3, 12, 31).dayOfGregorianCal, SysTime(DateTime(-3, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-3, 1, 1).dayOfGregorianCal, SysTime(DateTime(-3, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(-4, 12, 31).dayOfGregorianCal, SysTime(DateTime(-4, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(-4, 1, 1).dayOfGregorianCal, SysTime(DateTime(-4, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-5, 12, 31).dayOfGregorianCal, SysTime(DateTime(-5, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-5, 1, 1).dayOfGregorianCal, SysTime(DateTime(-5, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(-9, 1, 1).dayOfGregorianCal, SysTime(DateTime(-9, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - - _assertPred!"=="(Date(-49, 1, 1).dayOfGregorianCal, SysTime(DateTime(-49, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-50, 1, 1).dayOfGregorianCal, SysTime(DateTime(-50, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-97, 1, 1).dayOfGregorianCal, SysTime(DateTime(-97, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(-99, 12, 31).dayOfGregorianCal, SysTime(DateTime(-99, 12, 31, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(-99, 1, 1).dayOfGregorianCal, SysTime(DateTime(-99, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-100, 1, 1).dayOfGregorianCal, SysTime(DateTime(-100, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-101, 1, 1).dayOfGregorianCal, SysTime(DateTime(-101, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(-105, 1, 1).dayOfGregorianCal, SysTime(DateTime(-105, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(-200, 1, 1).dayOfGregorianCal, SysTime(DateTime(-200, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-201, 1, 1).dayOfGregorianCal, SysTime(DateTime(-201, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-300, 1, 1).dayOfGregorianCal, SysTime(DateTime(-300, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(-301, 1, 1).dayOfGregorianCal, SysTime(DateTime(-301, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(-400, 12, 31).dayOfGregorianCal, SysTime(DateTime(-400, 12, 31, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-400, 1, 1).dayOfGregorianCal, SysTime(DateTime(-400, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-401, 1, 1).dayOfGregorianCal, SysTime(DateTime(-401, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(-499, 1, 1).dayOfGregorianCal, SysTime(DateTime(-499, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(-500, 1, 1).dayOfGregorianCal, SysTime(DateTime(-500, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-501, 1, 1).dayOfGregorianCal, SysTime(DateTime(-501, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-1000, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1000, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(-1001, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1001, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(-1599, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1599, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-1600, 12, 31).dayOfGregorianCal, SysTime(DateTime(-1600, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-1600, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1600, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(-1601, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1601, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(-1900, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1900, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-1901, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1901, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-1999, 1, 1).dayOfGregorianCal, SysTime(DateTime(-1999, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(-1999, 7, 6).dayOfGregorianCal, SysTime(DateTime(-1999, 7, 6, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(-2000, 12, 31).dayOfGregorianCal, SysTime(DateTime(-2000, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-2000, 1, 1).dayOfGregorianCal, SysTime(DateTime(-2000, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-2001, 1, 1).dayOfGregorianCal, SysTime(DateTime(-2001, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - - _assertPred!"=="(Date(-2010, 1, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 1, 31).dayOfGregorianCal, SysTime(DateTime(-2010, 1, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 2, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 2, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 2, 28).dayOfGregorianCal, SysTime(DateTime(-2010, 2, 28, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 3, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 3, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 3, 31).dayOfGregorianCal, SysTime(DateTime(-2010, 3, 31, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 4, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 4, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 4, 30).dayOfGregorianCal, SysTime(DateTime(-2010, 4, 30, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 5, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 5, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 5, 31).dayOfGregorianCal, SysTime(DateTime(-2010, 5, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 6, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 6, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 6, 30).dayOfGregorianCal, SysTime(DateTime(-2010, 6, 30, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 7, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 7, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 7, 31).dayOfGregorianCal, SysTime(DateTime(-2010, 7, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 8, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 8, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 8, 31).dayOfGregorianCal, SysTime(DateTime(-2010, 8, 31, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 9, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 9, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 9, 30).dayOfGregorianCal, SysTime(DateTime(-2010, 9, 30, 12, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 10, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 10, 1, 0, 12, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 10, 31).dayOfGregorianCal, SysTime(DateTime(-2010, 10, 31, 0, 0, 12), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 11, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 11, 1, 23, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 11, 30).dayOfGregorianCal, SysTime(DateTime(-2010, 11, 30, 0, 59, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 12, 1).dayOfGregorianCal, SysTime(DateTime(-2010, 12, 1, 0, 0, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); - _assertPred!"=="(Date(-2010, 12, 31).dayOfGregorianCal, SysTime(DateTime(-2010, 12, 31, 0, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); - - _assertPred!"=="(Date(-2012, 2, 1).dayOfGregorianCal, SysTime(DateTime(-2012, 2, 1, 23, 0, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); - _assertPred!"=="(Date(-2012, 2, 28).dayOfGregorianCal, SysTime(DateTime(-2012, 2, 28, 23, 59, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - _assertPred!"=="(Date(-2012, 2, 29).dayOfGregorianCal, SysTime(DateTime(-2012, 2, 29, 7, 7, 7), FracSec.from!"hnsecs"(7)).dayOfGregorianCal); - _assertPred!"=="(Date(-2012, 3, 1).dayOfGregorianCal, SysTime(DateTime(-2012, 3, 1, 7, 7, 7), FracSec.from!"hnsecs"(7)).dayOfGregorianCal); - - _assertPred!"=="(Date(-3760, 9, 7).dayOfGregorianCal, SysTime(DateTime(-3760, 9, 7, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); - } + //Test A.D. + assert(Date(1, 1, 1).dayOfGregorianCal == SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(1, 1, 2).dayOfGregorianCal == SysTime(DateTime(1, 1, 2, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(1, 2, 1).dayOfGregorianCal == SysTime(DateTime(1, 2, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(2, 1, 1).dayOfGregorianCal == SysTime(DateTime(2, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(3, 1, 1).dayOfGregorianCal == SysTime(DateTime(3, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(4, 1, 1).dayOfGregorianCal == SysTime(DateTime(4, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(5, 1, 1).dayOfGregorianCal == SysTime(DateTime(5, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(50, 1, 1).dayOfGregorianCal == SysTime(DateTime(50, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(97, 1, 1).dayOfGregorianCal == SysTime(DateTime(97, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(100, 1, 1).dayOfGregorianCal == SysTime(DateTime(100, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(101, 1, 1).dayOfGregorianCal == SysTime(DateTime(101, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(105, 1, 1).dayOfGregorianCal == SysTime(DateTime(105, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(200, 1, 1).dayOfGregorianCal == SysTime(DateTime(200, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(201, 1, 1).dayOfGregorianCal == SysTime(DateTime(201, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(300, 1, 1).dayOfGregorianCal == SysTime(DateTime(300, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(301, 1, 1).dayOfGregorianCal == SysTime(DateTime(301, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(400, 1, 1).dayOfGregorianCal == SysTime(DateTime(400, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(401, 1, 1).dayOfGregorianCal == SysTime(DateTime(401, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(500, 1, 1).dayOfGregorianCal == SysTime(DateTime(500, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(501, 1, 1).dayOfGregorianCal == SysTime(DateTime(501, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(1000, 1, 1).dayOfGregorianCal == SysTime(DateTime(1000, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(1001, 1, 1).dayOfGregorianCal == SysTime(DateTime(1001, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(1600, 1, 1).dayOfGregorianCal == SysTime(DateTime(1600, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(1601, 1, 1).dayOfGregorianCal == SysTime(DateTime(1601, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(1900, 1, 1).dayOfGregorianCal == SysTime(DateTime(1900, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(1901, 1, 1).dayOfGregorianCal == SysTime(DateTime(1901, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(1945, 11, 12).dayOfGregorianCal == SysTime(DateTime(1945, 11, 12, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(1999, 1, 1).dayOfGregorianCal == SysTime(DateTime(1999, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(1999, 7, 6).dayOfGregorianCal == SysTime(DateTime(1999, 7, 6, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(2000, 1, 1).dayOfGregorianCal == SysTime(DateTime(2000, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(2001, 1, 1).dayOfGregorianCal == SysTime(DateTime(2001, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + + assert(Date(2010, 1, 1).dayOfGregorianCal == SysTime(DateTime(2010, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(2010, 1, 31).dayOfGregorianCal == SysTime(DateTime(2010, 1, 31, 23, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(2010, 2, 1).dayOfGregorianCal == SysTime(DateTime(2010, 2, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(2010, 2, 28).dayOfGregorianCal == SysTime(DateTime(2010, 2, 28, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(2010, 3, 1).dayOfGregorianCal == SysTime(DateTime(2010, 3, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(2010, 3, 31).dayOfGregorianCal == SysTime(DateTime(2010, 3, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(2010, 4, 1).dayOfGregorianCal == SysTime(DateTime(2010, 4, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(2010, 4, 30).dayOfGregorianCal == SysTime(DateTime(2010, 4, 30, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(2010, 5, 1).dayOfGregorianCal == SysTime(DateTime(2010, 5, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(2010, 5, 31).dayOfGregorianCal == SysTime(DateTime(2010, 5, 31, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(2010, 6, 1).dayOfGregorianCal == SysTime(DateTime(2010, 6, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(2010, 6, 30).dayOfGregorianCal == SysTime(DateTime(2010, 6, 30, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(2010, 7, 1).dayOfGregorianCal == SysTime(DateTime(2010, 7, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(2010, 7, 31).dayOfGregorianCal == SysTime(DateTime(2010, 7, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(2010, 8, 1).dayOfGregorianCal == SysTime(DateTime(2010, 8, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(2010, 8, 31).dayOfGregorianCal == SysTime(DateTime(2010, 8, 31, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(2010, 9, 1).dayOfGregorianCal == SysTime(DateTime(2010, 9, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(2010, 9, 30).dayOfGregorianCal == SysTime(DateTime(2010, 9, 30, 12, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(2010, 10, 1).dayOfGregorianCal == SysTime(DateTime(2010, 10, 1, 0, 12, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(2010, 10, 31).dayOfGregorianCal == SysTime(DateTime(2010, 10, 31, 0, 0, 12), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(2010, 11, 1).dayOfGregorianCal == SysTime(DateTime(2010, 11, 1, 23, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(2010, 11, 30).dayOfGregorianCal == SysTime(DateTime(2010, 11, 30, 0, 59, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(2010, 12, 1).dayOfGregorianCal == SysTime(DateTime(2010, 12, 1, 0, 0, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(2010, 12, 31).dayOfGregorianCal == SysTime(DateTime(2010, 12, 31, 0, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + + assert(Date(2012, 2, 1).dayOfGregorianCal == SysTime(DateTime(2012, 2, 1, 23, 0, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(2012, 2, 28).dayOfGregorianCal == SysTime(DateTime(2012, 2, 28, 23, 59, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(2012, 2, 29).dayOfGregorianCal == SysTime(DateTime(2012, 2, 29, 7, 7, 7), FracSec.from!"hnsecs"(7)).dayOfGregorianCal); + assert(Date(2012, 3, 1).dayOfGregorianCal == SysTime(DateTime(2012, 3, 1, 7, 7, 7), FracSec.from!"hnsecs"(7)).dayOfGregorianCal); + + //Test B.C. + assert(Date(0, 12, 31).dayOfGregorianCal == SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(0, 12, 30).dayOfGregorianCal == SysTime(DateTime(0, 12, 30, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(0, 12, 1).dayOfGregorianCal == SysTime(DateTime(0, 12, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(0, 11, 30).dayOfGregorianCal == SysTime(DateTime(0, 11, 30, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + + assert(Date(-1, 12, 31).dayOfGregorianCal == SysTime(DateTime(-1, 12, 31, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-1, 12, 30).dayOfGregorianCal == SysTime(DateTime(-1, 12, 30, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-1, 1, 1).dayOfGregorianCal == SysTime(DateTime(-1, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(-2, 12, 31).dayOfGregorianCal == SysTime(DateTime(-2, 12, 31, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(-2, 1, 1).dayOfGregorianCal == SysTime(DateTime(-2, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-3, 12, 31).dayOfGregorianCal == SysTime(DateTime(-3, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-3, 1, 1).dayOfGregorianCal == SysTime(DateTime(-3, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(-4, 12, 31).dayOfGregorianCal == SysTime(DateTime(-4, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(-4, 1, 1).dayOfGregorianCal == SysTime(DateTime(-4, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-5, 12, 31).dayOfGregorianCal == SysTime(DateTime(-5, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-5, 1, 1).dayOfGregorianCal == SysTime(DateTime(-5, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(-9, 1, 1).dayOfGregorianCal == SysTime(DateTime(-9, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + + assert(Date(-49, 1, 1).dayOfGregorianCal == SysTime(DateTime(-49, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-50, 1, 1).dayOfGregorianCal == SysTime(DateTime(-50, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-97, 1, 1).dayOfGregorianCal == SysTime(DateTime(-97, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(-99, 12, 31).dayOfGregorianCal == SysTime(DateTime(-99, 12, 31, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(-99, 1, 1).dayOfGregorianCal == SysTime(DateTime(-99, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-100, 1, 1).dayOfGregorianCal == SysTime(DateTime(-100, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-101, 1, 1).dayOfGregorianCal == SysTime(DateTime(-101, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(-105, 1, 1).dayOfGregorianCal == SysTime(DateTime(-105, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(-200, 1, 1).dayOfGregorianCal == SysTime(DateTime(-200, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-201, 1, 1).dayOfGregorianCal == SysTime(DateTime(-201, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-300, 1, 1).dayOfGregorianCal == SysTime(DateTime(-300, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(-301, 1, 1).dayOfGregorianCal == SysTime(DateTime(-301, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(-400, 12, 31).dayOfGregorianCal == SysTime(DateTime(-400, 12, 31, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-400, 1, 1).dayOfGregorianCal == SysTime(DateTime(-400, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-401, 1, 1).dayOfGregorianCal == SysTime(DateTime(-401, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(-499, 1, 1).dayOfGregorianCal == SysTime(DateTime(-499, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(-500, 1, 1).dayOfGregorianCal == SysTime(DateTime(-500, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-501, 1, 1).dayOfGregorianCal == SysTime(DateTime(-501, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-1000, 1, 1).dayOfGregorianCal == SysTime(DateTime(-1000, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(-1001, 1, 1).dayOfGregorianCal == SysTime(DateTime(-1001, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(-1599, 1, 1).dayOfGregorianCal == SysTime(DateTime(-1599, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-1600, 12, 31).dayOfGregorianCal == SysTime(DateTime(-1600, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-1600, 1, 1).dayOfGregorianCal == SysTime(DateTime(-1600, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(-1601, 1, 1).dayOfGregorianCal == SysTime(DateTime(-1601, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(-1900, 1, 1).dayOfGregorianCal == SysTime(DateTime(-1900, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-1901, 1, 1).dayOfGregorianCal == SysTime(DateTime(-1901, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-1999, 1, 1).dayOfGregorianCal == SysTime(DateTime(-1999, 1, 1, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(-1999, 7, 6).dayOfGregorianCal == SysTime(DateTime(-1999, 7, 6, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(-2000, 12, 31).dayOfGregorianCal == SysTime(DateTime(-2000, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-2000, 1, 1).dayOfGregorianCal == SysTime(DateTime(-2000, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-2001, 1, 1).dayOfGregorianCal == SysTime(DateTime(-2001, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + + assert(Date(-2010, 1, 1).dayOfGregorianCal == SysTime(DateTime(-2010, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(-2010, 1, 31).dayOfGregorianCal == SysTime(DateTime(-2010, 1, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-2010, 2, 1).dayOfGregorianCal == SysTime(DateTime(-2010, 2, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-2010, 2, 28).dayOfGregorianCal == SysTime(DateTime(-2010, 2, 28, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(-2010, 3, 1).dayOfGregorianCal == SysTime(DateTime(-2010, 3, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(-2010, 3, 31).dayOfGregorianCal == SysTime(DateTime(-2010, 3, 31, 12, 13, 14), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-2010, 4, 1).dayOfGregorianCal == SysTime(DateTime(-2010, 4, 1, 12, 13, 14), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-2010, 4, 30).dayOfGregorianCal == SysTime(DateTime(-2010, 4, 30, 12, 13, 14), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(-2010, 5, 1).dayOfGregorianCal == SysTime(DateTime(-2010, 5, 1, 12, 13, 14), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(-2010, 5, 31).dayOfGregorianCal == SysTime(DateTime(-2010, 5, 31, 23, 59, 59), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-2010, 6, 1).dayOfGregorianCal == SysTime(DateTime(-2010, 6, 1, 23, 59, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-2010, 6, 30).dayOfGregorianCal == SysTime(DateTime(-2010, 6, 30, 23, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(-2010, 7, 1).dayOfGregorianCal == SysTime(DateTime(-2010, 7, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(-2010, 7, 31).dayOfGregorianCal == SysTime(DateTime(-2010, 7, 31, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-2010, 8, 1).dayOfGregorianCal == SysTime(DateTime(-2010, 8, 1, 0, 0, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-2010, 8, 31).dayOfGregorianCal == SysTime(DateTime(-2010, 8, 31, 0, 0, 0), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(-2010, 9, 1).dayOfGregorianCal == SysTime(DateTime(-2010, 9, 1, 0, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(-2010, 9, 30).dayOfGregorianCal == SysTime(DateTime(-2010, 9, 30, 12, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-2010, 10, 1).dayOfGregorianCal == SysTime(DateTime(-2010, 10, 1, 0, 12, 0), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-2010, 10, 31).dayOfGregorianCal == SysTime(DateTime(-2010, 10, 31, 0, 0, 12), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + assert(Date(-2010, 11, 1).dayOfGregorianCal == SysTime(DateTime(-2010, 11, 1, 23, 0, 0), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(-2010, 11, 30).dayOfGregorianCal == SysTime(DateTime(-2010, 11, 30, 0, 59, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-2010, 12, 1).dayOfGregorianCal == SysTime(DateTime(-2010, 12, 1, 0, 0, 59), FracSec.from!"hnsecs"(500)).dayOfGregorianCal); + assert(Date(-2010, 12, 31).dayOfGregorianCal == SysTime(DateTime(-2010, 12, 31, 0, 59, 59), FracSec.from!"hnsecs"(50_000)).dayOfGregorianCal); + + assert(Date(-2012, 2, 1).dayOfGregorianCal == SysTime(DateTime(-2012, 2, 1, 23, 0, 59), FracSec.from!"hnsecs"(9_999_999)).dayOfGregorianCal); + assert(Date(-2012, 2, 28).dayOfGregorianCal == SysTime(DateTime(-2012, 2, 28, 23, 59, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); + assert(Date(-2012, 2, 29).dayOfGregorianCal == SysTime(DateTime(-2012, 2, 29, 7, 7, 7), FracSec.from!"hnsecs"(7)).dayOfGregorianCal); + assert(Date(-2012, 3, 1).dayOfGregorianCal == SysTime(DateTime(-2012, 3, 1, 7, 7, 7), FracSec.from!"hnsecs"(7)).dayOfGregorianCal); + + assert(Date(-3760, 9, 7).dayOfGregorianCal == SysTime(DateTime(-3760, 9, 7, 0, 0, 0), FracSec.from!"hnsecs"(0)).dayOfGregorianCal); } @@ -6983,36 +6730,8 @@ assert(SysTime(DateTime(2010, 12, 31, 15, 45, 50)).dayOfGregorianCal == 734_137) Params: days = The day of the Gregorian Calendar to set this $(LREF SysTime) to. - - Examples: --------------------- -auto st = SysTime(DateTime(0, 0, 0, 12, 0, 0)); -st.dayOfGregorianCal = 1; -assert(st == SysTime(DateTime(1, 1, 1, 12, 0, 0))); - -st.dayOfGregorianCal = 365; -assert(st == SysTime(DateTime(1, 12, 31, 12, 0, 0))); - -st.dayOfGregorianCal = 366; -assert(st == SysTime(DateTime(2, 1, 1, 12, 0, 0))); - -st.dayOfGregorianCal = 0; -assert(st == SysTime(DateTime(0, 12, 31, 12, 0, 0))); - -st.dayOfGregorianCal = -365; -assert(st == SysTime(DateTime(-0, 1, 1, 12, 0, 0))); - -st.dayOfGregorianCal = -366; -assert(st == SysTime(DateTime(-1, 12, 31, 12, 0, 0))); - -st.dayOfGregorianCal = 730_120; -assert(st == SysTime(DateTime(2000, 1, 1, 12, 0, 0))); - -st.dayOfGregorianCal = 734_137; -assert(st == SysTime(DateTime(2010, 12, 31, 12, 0, 0))); --------------------- +/ - @property void dayOfGregorianCal(int days) nothrow + @property void dayOfGregorianCal(int days) @safe nothrow { auto hnsecs = adjTime; hnsecs = removeUnitsFromHNSecs!"days"(hnsecs); @@ -7031,224 +6750,224 @@ assert(st == SysTime(DateTime(2010, 12, 31, 12, 0, 0))); adjTime = newDaysHNSecs + hnsecs; } + /// unittest { - version(testStdDateTime) - { - void testST(SysTime orig, int day, in SysTime expected, size_t line = __LINE__) - { - orig.dayOfGregorianCal = day; - _assertPred!"=="(orig, expected, "", __FILE__, line); - } + auto st = SysTime(DateTime(0, 1, 1, 12, 0, 0)); + st.dayOfGregorianCal = 1; + assert(st == SysTime(DateTime(1, 1, 1, 12, 0, 0))); - //Test A.D. - testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); - testST(SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + st.dayOfGregorianCal = 365; + assert(st == SysTime(DateTime(1, 12, 31, 12, 0, 0))); - //Test B.C. - testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0))); - testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(1)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1))); - testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + st.dayOfGregorianCal = 366; + assert(st == SysTime(DateTime(2, 1, 1, 12, 0, 0))); - //Test Both. - testST(SysTime(DateTime(-512, 7, 20, 0, 0, 0), FracSec.from!"hnsecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); - testST(SysTime(DateTime(-513, 6, 6, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); - testST(SysTime(DateTime(-511, 5, 7, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + st.dayOfGregorianCal = 0; + assert(st == SysTime(DateTime(0, 12, 31, 12, 0, 0))); - testST(SysTime(DateTime(1607, 4, 8, 0, 0, 0), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0))); - testST(SysTime(DateTime(1500, 3, 9, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - testST(SysTime(DateTime(999, 2, 10, 23, 59, 59), FracSec.from!"hnsecs"(1)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1))); - testST(SysTime(DateTime(2007, 12, 11, 23, 59, 59), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + st.dayOfGregorianCal = -365; + assert(st == SysTime(DateTime(-0, 1, 1, 12, 0, 0))); + st.dayOfGregorianCal = -366; + assert(st == SysTime(DateTime(-1, 12, 31, 12, 0, 0))); - auto sysTime = SysTime(DateTime(1, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)); + st.dayOfGregorianCal = 730_120; + assert(st == SysTime(DateTime(2000, 1, 1, 12, 0, 0))); - void testST2(int day, in SysTime expected, size_t line = __LINE__) - { - sysTime.dayOfGregorianCal = day; - _assertPred!"=="(sysTime, expected, "", __FILE__, line); - } + st.dayOfGregorianCal = 734_137; + assert(st == SysTime(DateTime(2010, 12, 31, 12, 0, 0))); + } + + unittest + { + void testST(SysTime orig, int day, in SysTime expected) + { + orig.dayOfGregorianCal = day; + assert(orig == expected); + } + + //Test A.D. + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + + //Test B.C. + testST(SysTime(DateTime(0, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(1)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(0, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + + //Test Both. + testST(SysTime(DateTime(-512, 7, 20, 0, 0, 0), FracSec.from!"hnsecs"(0)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(-513, 6, 6, 0, 0, 0), FracSec.from!"hnsecs"(1)), 1, SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(-511, 5, 7, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 1, SysTime(DateTime(1, 1, 1, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - //Test A.D. - testST2(1, SysTime(DateTime(1, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(2, SysTime(DateTime(1, 1, 2, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(32, SysTime(DateTime(1, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(366, SysTime(DateTime(2, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(731, SysTime(DateTime(3, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(1096, SysTime(DateTime(4, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(1462, SysTime(DateTime(5, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(17_898, SysTime(DateTime(50, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(35_065, SysTime(DateTime(97, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(36_160, SysTime(DateTime(100, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(36_525, SysTime(DateTime(101, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(37_986, SysTime(DateTime(105, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(72_684, SysTime(DateTime(200, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(73_049, SysTime(DateTime(201, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(109_208, SysTime(DateTime(300, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(109_573, SysTime(DateTime(301, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(145_732, SysTime(DateTime(400, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(146_098, SysTime(DateTime(401, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(182_257, SysTime(DateTime(500, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(182_622, SysTime(DateTime(501, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(364_878, SysTime(DateTime(1000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(365_243, SysTime(DateTime(1001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(584_023, SysTime(DateTime(1600, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(584_389, SysTime(DateTime(1601, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(693_596, SysTime(DateTime(1900, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(693_961, SysTime(DateTime(1901, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(729_755, SysTime(DateTime(1999, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(730_120, SysTime(DateTime(2000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(730_486, SysTime(DateTime(2001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - - testST2(733_773, SysTime(DateTime(2010, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(733_803, SysTime(DateTime(2010, 1, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(733_804, SysTime(DateTime(2010, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(733_831, SysTime(DateTime(2010, 2, 28, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(733_832, SysTime(DateTime(2010, 3, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(733_862, SysTime(DateTime(2010, 3, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(733_863, SysTime(DateTime(2010, 4, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(733_892, SysTime(DateTime(2010, 4, 30, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(733_893, SysTime(DateTime(2010, 5, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(733_923, SysTime(DateTime(2010, 5, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(733_924, SysTime(DateTime(2010, 6, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(733_953, SysTime(DateTime(2010, 6, 30, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(733_954, SysTime(DateTime(2010, 7, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(733_984, SysTime(DateTime(2010, 7, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(733_985, SysTime(DateTime(2010, 8, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(734_015, SysTime(DateTime(2010, 8, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(734_016, SysTime(DateTime(2010, 9, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(734_045, SysTime(DateTime(2010, 9, 30, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(734_046, SysTime(DateTime(2010, 10, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(734_076, SysTime(DateTime(2010, 10, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(734_077, SysTime(DateTime(2010, 11, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(734_106, SysTime(DateTime(2010, 11, 30, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(734_107, SysTime(DateTime(2010, 12, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(734_137, SysTime(DateTime(2010, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - - testST2(734_534, SysTime(DateTime(2012, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(734_561, SysTime(DateTime(2012, 2, 28, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(734_562, SysTime(DateTime(2012, 2, 29, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(734_563, SysTime(DateTime(2012, 3, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - - testST2(734_534, SysTime(DateTime(2012, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - - testST2(734_561, SysTime(DateTime(2012, 2, 28, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(734_562, SysTime(DateTime(2012, 2, 29, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(734_563, SysTime(DateTime(2012, 3, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - - //Test B.C. - testST2(0, SysTime(DateTime(0, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-1, SysTime(DateTime(0, 12, 30, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-30, SysTime(DateTime(0, 12, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-31, SysTime(DateTime(0, 11, 30, 12, 2, 9), FracSec.from!"msecs"(212))); - - testST2(-366, SysTime(DateTime(-1, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-367, SysTime(DateTime(-1, 12, 30, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-730, SysTime(DateTime(-1, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-731, SysTime(DateTime(-2, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-1095, SysTime(DateTime(-2, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-1096, SysTime(DateTime(-3, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-1460, SysTime(DateTime(-3, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-1461, SysTime(DateTime(-4, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-1826, SysTime(DateTime(-4, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-1827, SysTime(DateTime(-5, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-2191, SysTime(DateTime(-5, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-3652, SysTime(DateTime(-9, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - - testST2(-18_262, SysTime(DateTime(-49, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-18_627, SysTime(DateTime(-50, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-35_794, SysTime(DateTime(-97, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-36_160, SysTime(DateTime(-99, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-36_524, SysTime(DateTime(-99, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-36_889, SysTime(DateTime(-100, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-37_254, SysTime(DateTime(-101, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-38_715, SysTime(DateTime(-105, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-73_413, SysTime(DateTime(-200, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-73_778, SysTime(DateTime(-201, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-109_937, SysTime(DateTime(-300, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-110_302, SysTime(DateTime(-301, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-146_097, SysTime(DateTime(-400, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-146_462, SysTime(DateTime(-400, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-146_827, SysTime(DateTime(-401, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-182_621, SysTime(DateTime(-499, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-182_986, SysTime(DateTime(-500, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-183_351, SysTime(DateTime(-501, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-365_607, SysTime(DateTime(-1000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-365_972, SysTime(DateTime(-1001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-584_387, SysTime(DateTime(-1599, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-584_388, SysTime(DateTime(-1600, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-584_753, SysTime(DateTime(-1600, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-585_118, SysTime(DateTime(-1601, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-694_325, SysTime(DateTime(-1900, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-694_690, SysTime(DateTime(-1901, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-730_484, SysTime(DateTime(-1999, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-730_485, SysTime(DateTime(-2000, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-730_850, SysTime(DateTime(-2000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-731_215, SysTime(DateTime(-2001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - - testST2(-734_502, SysTime(DateTime(-2010, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_472, SysTime(DateTime(-2010, 1, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_471, SysTime(DateTime(-2010, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_444, SysTime(DateTime(-2010, 2, 28, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_443, SysTime(DateTime(-2010, 3, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_413, SysTime(DateTime(-2010, 3, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_412, SysTime(DateTime(-2010, 4, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_383, SysTime(DateTime(-2010, 4, 30, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_382, SysTime(DateTime(-2010, 5, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_352, SysTime(DateTime(-2010, 5, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_351, SysTime(DateTime(-2010, 6, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_322, SysTime(DateTime(-2010, 6, 30, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_321, SysTime(DateTime(-2010, 7, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_291, SysTime(DateTime(-2010, 7, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_290, SysTime(DateTime(-2010, 8, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_260, SysTime(DateTime(-2010, 8, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_259, SysTime(DateTime(-2010, 9, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_230, SysTime(DateTime(-2010, 9, 30, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_229, SysTime(DateTime(-2010, 10, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_199, SysTime(DateTime(-2010, 10, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_198, SysTime(DateTime(-2010, 11, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_169, SysTime(DateTime(-2010, 11, 30, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_168, SysTime(DateTime(-2010, 12, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-734_138, SysTime(DateTime(-2010, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); - - testST2(-735_202, SysTime(DateTime(-2012, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-735_175, SysTime(DateTime(-2012, 2, 28, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-735_174, SysTime(DateTime(-2012, 2, 29, 12, 2, 9), FracSec.from!"msecs"(212))); - testST2(-735_173, SysTime(DateTime(-2012, 3, 1, 12, 2, 9), FracSec.from!"msecs"(212))); - - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(!__traits(compiles, cst.dayOfGregorianCal = 7)); - //static assert(!__traits(compiles, ist.dayOfGregorianCal = 7)); - - //Verify Examples. - auto st = SysTime(DateTime(0, 1, 1, 12, 0, 0)); - st.dayOfGregorianCal = 1; - assert(st == SysTime(DateTime(1, 1, 1, 12, 0, 0))); - - st.dayOfGregorianCal = 365; - assert(st == SysTime(DateTime(1, 12, 31, 12, 0, 0))); - - st.dayOfGregorianCal = 366; - assert(st == SysTime(DateTime(2, 1, 1, 12, 0, 0))); - - st.dayOfGregorianCal = 0; - assert(st == SysTime(DateTime(0, 12, 31, 12, 0, 0))); - - st.dayOfGregorianCal = -365; - assert(st == SysTime(DateTime(-0, 1, 1, 12, 0, 0))); - - st.dayOfGregorianCal = -366; - assert(st == SysTime(DateTime(-1, 12, 31, 12, 0, 0))); - - st.dayOfGregorianCal = 730_120; - assert(st == SysTime(DateTime(2000, 1, 1, 12, 0, 0))); - - st.dayOfGregorianCal = 734_137; - assert(st == SysTime(DateTime(2010, 12, 31, 12, 0, 0))); + testST(SysTime(DateTime(1607, 4, 8, 0, 0, 0), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(0, 12, 31, 0, 0, 0), FracSec.from!"hnsecs"(0))); + testST(SysTime(DateTime(1500, 3, 9, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + testST(SysTime(DateTime(999, 2, 10, 23, 59, 59), FracSec.from!"hnsecs"(1)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1))); + testST(SysTime(DateTime(2007, 12, 11, 23, 59, 59), FracSec.from!"hnsecs"(0)), 0, SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(0))); + + + auto sysTime = SysTime(DateTime(1, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212)); + + void testST2(int day, in SysTime expected) + { + sysTime.dayOfGregorianCal = day; + assert(sysTime == expected); } + + //Test A.D. + testST2(1, SysTime(DateTime(1, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(2, SysTime(DateTime(1, 1, 2, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(32, SysTime(DateTime(1, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(366, SysTime(DateTime(2, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(731, SysTime(DateTime(3, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(1096, SysTime(DateTime(4, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(1462, SysTime(DateTime(5, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(17_898, SysTime(DateTime(50, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(35_065, SysTime(DateTime(97, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(36_160, SysTime(DateTime(100, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(36_525, SysTime(DateTime(101, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(37_986, SysTime(DateTime(105, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(72_684, SysTime(DateTime(200, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(73_049, SysTime(DateTime(201, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(109_208, SysTime(DateTime(300, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(109_573, SysTime(DateTime(301, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(145_732, SysTime(DateTime(400, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(146_098, SysTime(DateTime(401, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(182_257, SysTime(DateTime(500, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(182_622, SysTime(DateTime(501, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(364_878, SysTime(DateTime(1000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(365_243, SysTime(DateTime(1001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(584_023, SysTime(DateTime(1600, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(584_389, SysTime(DateTime(1601, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(693_596, SysTime(DateTime(1900, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(693_961, SysTime(DateTime(1901, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(729_755, SysTime(DateTime(1999, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(730_120, SysTime(DateTime(2000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(730_486, SysTime(DateTime(2001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + + testST2(733_773, SysTime(DateTime(2010, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_803, SysTime(DateTime(2010, 1, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_804, SysTime(DateTime(2010, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_831, SysTime(DateTime(2010, 2, 28, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_832, SysTime(DateTime(2010, 3, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_862, SysTime(DateTime(2010, 3, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_863, SysTime(DateTime(2010, 4, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_892, SysTime(DateTime(2010, 4, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_893, SysTime(DateTime(2010, 5, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_923, SysTime(DateTime(2010, 5, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_924, SysTime(DateTime(2010, 6, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_953, SysTime(DateTime(2010, 6, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_954, SysTime(DateTime(2010, 7, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_984, SysTime(DateTime(2010, 7, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(733_985, SysTime(DateTime(2010, 8, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_015, SysTime(DateTime(2010, 8, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_016, SysTime(DateTime(2010, 9, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_045, SysTime(DateTime(2010, 9, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_046, SysTime(DateTime(2010, 10, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_076, SysTime(DateTime(2010, 10, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_077, SysTime(DateTime(2010, 11, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_106, SysTime(DateTime(2010, 11, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_107, SysTime(DateTime(2010, 12, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_137, SysTime(DateTime(2010, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + + testST2(734_534, SysTime(DateTime(2012, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_561, SysTime(DateTime(2012, 2, 28, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_562, SysTime(DateTime(2012, 2, 29, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_563, SysTime(DateTime(2012, 3, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + + testST2(734_534, SysTime(DateTime(2012, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + + testST2(734_561, SysTime(DateTime(2012, 2, 28, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_562, SysTime(DateTime(2012, 2, 29, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(734_563, SysTime(DateTime(2012, 3, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + + //Test B.C. + testST2(0, SysTime(DateTime(0, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-1, SysTime(DateTime(0, 12, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-30, SysTime(DateTime(0, 12, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-31, SysTime(DateTime(0, 11, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + + testST2(-366, SysTime(DateTime(-1, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-367, SysTime(DateTime(-1, 12, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-730, SysTime(DateTime(-1, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-731, SysTime(DateTime(-2, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-1095, SysTime(DateTime(-2, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-1096, SysTime(DateTime(-3, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-1460, SysTime(DateTime(-3, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-1461, SysTime(DateTime(-4, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-1826, SysTime(DateTime(-4, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-1827, SysTime(DateTime(-5, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-2191, SysTime(DateTime(-5, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-3652, SysTime(DateTime(-9, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + + testST2(-18_262, SysTime(DateTime(-49, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-18_627, SysTime(DateTime(-50, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-35_794, SysTime(DateTime(-97, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-36_160, SysTime(DateTime(-99, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-36_524, SysTime(DateTime(-99, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-36_889, SysTime(DateTime(-100, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-37_254, SysTime(DateTime(-101, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-38_715, SysTime(DateTime(-105, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-73_413, SysTime(DateTime(-200, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-73_778, SysTime(DateTime(-201, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-109_937, SysTime(DateTime(-300, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-110_302, SysTime(DateTime(-301, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-146_097, SysTime(DateTime(-400, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-146_462, SysTime(DateTime(-400, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-146_827, SysTime(DateTime(-401, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-182_621, SysTime(DateTime(-499, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-182_986, SysTime(DateTime(-500, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-183_351, SysTime(DateTime(-501, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-365_607, SysTime(DateTime(-1000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-365_972, SysTime(DateTime(-1001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-584_387, SysTime(DateTime(-1599, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-584_388, SysTime(DateTime(-1600, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-584_753, SysTime(DateTime(-1600, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-585_118, SysTime(DateTime(-1601, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-694_325, SysTime(DateTime(-1900, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-694_690, SysTime(DateTime(-1901, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-730_484, SysTime(DateTime(-1999, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-730_485, SysTime(DateTime(-2000, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-730_850, SysTime(DateTime(-2000, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-731_215, SysTime(DateTime(-2001, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + + testST2(-734_502, SysTime(DateTime(-2010, 1, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_472, SysTime(DateTime(-2010, 1, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_471, SysTime(DateTime(-2010, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_444, SysTime(DateTime(-2010, 2, 28, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_443, SysTime(DateTime(-2010, 3, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_413, SysTime(DateTime(-2010, 3, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_412, SysTime(DateTime(-2010, 4, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_383, SysTime(DateTime(-2010, 4, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_382, SysTime(DateTime(-2010, 5, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_352, SysTime(DateTime(-2010, 5, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_351, SysTime(DateTime(-2010, 6, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_322, SysTime(DateTime(-2010, 6, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_321, SysTime(DateTime(-2010, 7, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_291, SysTime(DateTime(-2010, 7, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_290, SysTime(DateTime(-2010, 8, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_260, SysTime(DateTime(-2010, 8, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_259, SysTime(DateTime(-2010, 9, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_230, SysTime(DateTime(-2010, 9, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_229, SysTime(DateTime(-2010, 10, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_199, SysTime(DateTime(-2010, 10, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_198, SysTime(DateTime(-2010, 11, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_169, SysTime(DateTime(-2010, 11, 30, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_168, SysTime(DateTime(-2010, 12, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-734_138, SysTime(DateTime(-2010, 12, 31, 12, 2, 9), FracSec.from!"msecs"(212))); + + testST2(-735_202, SysTime(DateTime(-2012, 2, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-735_175, SysTime(DateTime(-2012, 2, 28, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-735_174, SysTime(DateTime(-2012, 2, 29, 12, 2, 9), FracSec.from!"msecs"(212))); + testST2(-735_173, SysTime(DateTime(-2012, 3, 1, 12, 2, 9), FracSec.from!"msecs"(212))); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(!__traits(compiles, cst.dayOfGregorianCal = 7)); + //static assert(!__traits(compiles, ist.dayOfGregorianCal = 7)); } @@ -7258,52 +6977,27 @@ assert(st == SysTime(DateTime(2010, 12, 31, 12, 0, 0))); See_Also: $(WEB en.wikipedia.org/wiki/ISO_week_date, ISO Week Date). +/ - @property ubyte isoWeek() const nothrow + @property ubyte isoWeek() @safe const nothrow { return (cast(Date)this).isoWeek; } unittest { - version(testStdDateTime) - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.isoWeek)); - static assert(__traits(compiles, cst.isoWeek)); - //static assert(__traits(compiles, ist.isoWeek)); - } + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, st.isoWeek)); + static assert(__traits(compiles, cst.isoWeek)); + //static assert(__traits(compiles, ist.isoWeek)); } /++ $(LREF SysTime) for the last day in the month that this Date is in. The time portion of endOfMonth is always 23:59:59.9999999. - - Examples: --------------------- -assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonth == - SysTime(DateTime(1999, 1, 31, 23, 59, 59), - FracSec.from!"hnsecs"(9_999_999))); - -assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0), - FracSec.from!"msecs"(24)).endOfMonth == - SysTime(DateTime(1999, 2, 28, 23, 59, 59), - FracSec.from!"hnsecs"(9_999_999))); - -assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27), - FracSec.from!"usecs"(5203)).endOfMonth == - SysTime(DateTime(2000, 2, 29, 23, 59, 59), - FracSec.from!"hnsecs"(9_999_999))); - -assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9), - FracSec.from!"hnsecs"(12345)).endOfMonth == - SysTime(DateTime(2000, 6, 30, 23, 59, 59), - FracSec.from!"hnsecs"(9_999_999))); --------------------- +/ - @property SysTime endOfMonth() const nothrow + @property SysTime endOfMonth() @safe const nothrow { immutable hnsecs = adjTime; immutable days = getUnitsFromHNSecs!"days"(hnsecs); @@ -7328,156 +7022,154 @@ assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9), return retval; } + /// unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(SysTime(Date(1999, 1, 1)).endOfMonth, SysTime(DateTime(1999, 1, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(1999, 2, 1)).endOfMonth, SysTime(DateTime(1999, 2, 28, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(2000, 2, 1)).endOfMonth, SysTime(DateTime(2000, 2, 29, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(1999, 3, 1)).endOfMonth, SysTime(DateTime(1999, 3, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(1999, 4, 1)).endOfMonth, SysTime(DateTime(1999, 4, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(1999, 5, 1)).endOfMonth, SysTime(DateTime(1999, 5, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(1999, 6, 1)).endOfMonth, SysTime(DateTime(1999, 6, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(1999, 7, 1)).endOfMonth, SysTime(DateTime(1999, 7, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(1999, 8, 1)).endOfMonth, SysTime(DateTime(1999, 8, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(1999, 9, 1)).endOfMonth, SysTime(DateTime(1999, 9, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(1999, 10, 1)).endOfMonth, SysTime(DateTime(1999, 10, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(1999, 11, 1)).endOfMonth, SysTime(DateTime(1999, 11, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(1999, 12, 1)).endOfMonth, SysTime(DateTime(1999, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonth == + SysTime(DateTime(1999, 1, 31, 23, 59, 59), + FracSec.from!"hnsecs"(9_999_999))); - //Test B.C. - _assertPred!"=="(SysTime(Date(-1999, 1, 1)).endOfMonth, SysTime(DateTime(-1999, 1, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(-1999, 2, 1)).endOfMonth, SysTime(DateTime(-1999, 2, 28, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(-2000, 2, 1)).endOfMonth, SysTime(DateTime(-2000, 2, 29, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(-1999, 3, 1)).endOfMonth, SysTime(DateTime(-1999, 3, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(-1999, 4, 1)).endOfMonth, SysTime(DateTime(-1999, 4, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(-1999, 5, 1)).endOfMonth, SysTime(DateTime(-1999, 5, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(-1999, 6, 1)).endOfMonth, SysTime(DateTime(-1999, 6, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(-1999, 7, 1)).endOfMonth, SysTime(DateTime(-1999, 7, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(-1999, 8, 1)).endOfMonth, SysTime(DateTime(-1999, 8, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(-1999, 9, 1)).endOfMonth, SysTime(DateTime(-1999, 9, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(-1999, 10, 1)).endOfMonth, SysTime(DateTime(-1999, 10, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(-1999, 11, 1)).endOfMonth, SysTime(DateTime(-1999, 11, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - _assertPred!"=="(SysTime(Date(-1999, 12, 1)).endOfMonth, SysTime(DateTime(-1999, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0), + FracSec.from!"msecs"(24)).endOfMonth == + SysTime(DateTime(1999, 2, 28, 23, 59, 59), + FracSec.from!"hnsecs"(9_999_999))); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.endOfMonth)); - //static assert(__traits(compiles, ist.endOfMonth)); + assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27), + FracSec.from!"usecs"(5203)).endOfMonth == + SysTime(DateTime(2000, 2, 29, 23, 59, 59), + FracSec.from!"hnsecs"(9_999_999))); - //Verify Examples. - assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonth == SysTime(DateTime(1999, 1, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0), FracSec.from!"msecs"(24)).endOfMonth == SysTime(DateTime(1999, 2, 28, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27), FracSec.from!"usecs"(5203)).endOfMonth == SysTime(DateTime(2000, 2, 29, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9), FracSec.from!"hnsecs"(12345)).endOfMonth == SysTime(DateTime(2000, 6, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); - } + assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9), + FracSec.from!"hnsecs"(12345)).endOfMonth == + SysTime(DateTime(2000, 6, 30, 23, 59, 59), + FracSec.from!"hnsecs"(9_999_999))); + } + + unittest + { + //Test A.D. + assert(SysTime(Date(1999, 1, 1)).endOfMonth == SysTime(DateTime(1999, 1, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(1999, 2, 1)).endOfMonth == SysTime(DateTime(1999, 2, 28, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(2000, 2, 1)).endOfMonth == SysTime(DateTime(2000, 2, 29, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(1999, 3, 1)).endOfMonth == SysTime(DateTime(1999, 3, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(1999, 4, 1)).endOfMonth == SysTime(DateTime(1999, 4, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(1999, 5, 1)).endOfMonth == SysTime(DateTime(1999, 5, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(1999, 6, 1)).endOfMonth == SysTime(DateTime(1999, 6, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(1999, 7, 1)).endOfMonth == SysTime(DateTime(1999, 7, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(1999, 8, 1)).endOfMonth == SysTime(DateTime(1999, 8, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(1999, 9, 1)).endOfMonth == SysTime(DateTime(1999, 9, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(1999, 10, 1)).endOfMonth == SysTime(DateTime(1999, 10, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(1999, 11, 1)).endOfMonth == SysTime(DateTime(1999, 11, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(1999, 12, 1)).endOfMonth == SysTime(DateTime(1999, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + + //Test B.C. + assert(SysTime(Date(-1999, 1, 1)).endOfMonth == SysTime(DateTime(-1999, 1, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(-1999, 2, 1)).endOfMonth == SysTime(DateTime(-1999, 2, 28, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(-2000, 2, 1)).endOfMonth == SysTime(DateTime(-2000, 2, 29, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(-1999, 3, 1)).endOfMonth == SysTime(DateTime(-1999, 3, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(-1999, 4, 1)).endOfMonth == SysTime(DateTime(-1999, 4, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(-1999, 5, 1)).endOfMonth == SysTime(DateTime(-1999, 5, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(-1999, 6, 1)).endOfMonth == SysTime(DateTime(-1999, 6, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(-1999, 7, 1)).endOfMonth == SysTime(DateTime(-1999, 7, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(-1999, 8, 1)).endOfMonth == SysTime(DateTime(-1999, 8, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(-1999, 9, 1)).endOfMonth == SysTime(DateTime(-1999, 9, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(-1999, 10, 1)).endOfMonth == SysTime(DateTime(-1999, 10, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(-1999, 11, 1)).endOfMonth == SysTime(DateTime(-1999, 11, 30, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + assert(SysTime(Date(-1999, 12, 1)).endOfMonth == SysTime(DateTime(-1999, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999))); + + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cst.endOfMonth)); + //static assert(__traits(compiles, ist.endOfMonth)); } /++ The last day in the month that this $(LREF SysTime) is in. - - Examples: --------------------- -assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).daysInMonth == 31); -assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0)).daysInMonth == 28); -assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27)).daysInMonth == 29); -assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9)).daysInMonth == 30); --------------------- +/ - @property ubyte daysInMonth() const nothrow + @property ubyte daysInMonth() @safe const nothrow { return Date(dayOfGregorianCal).daysInMonth; } + /// unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(SysTime(DateTime(1999, 1, 1, 12, 1, 13)).daysInMonth, 31); - _assertPred!"=="(SysTime(DateTime(1999, 2, 1, 17, 13, 12)).daysInMonth, 28); - _assertPred!"=="(SysTime(DateTime(2000, 2, 1, 13, 2, 12)).daysInMonth, 29); - _assertPred!"=="(SysTime(DateTime(1999, 3, 1, 12, 13, 12)).daysInMonth, 31); - _assertPred!"=="(SysTime(DateTime(1999, 4, 1, 12, 6, 13)).daysInMonth, 30); - _assertPred!"=="(SysTime(DateTime(1999, 5, 1, 15, 13, 12)).daysInMonth, 31); - _assertPred!"=="(SysTime(DateTime(1999, 6, 1, 13, 7, 12)).daysInMonth, 30); - _assertPred!"=="(SysTime(DateTime(1999, 7, 1, 12, 13, 17)).daysInMonth, 31); - _assertPred!"=="(SysTime(DateTime(1999, 8, 1, 12, 3, 13)).daysInMonth, 31); - _assertPred!"=="(SysTime(DateTime(1999, 9, 1, 12, 13, 12)).daysInMonth, 30); - _assertPred!"=="(SysTime(DateTime(1999, 10, 1, 13, 19, 12)).daysInMonth, 31); - _assertPred!"=="(SysTime(DateTime(1999, 11, 1, 12, 13, 17)).daysInMonth, 30); - _assertPred!"=="(SysTime(DateTime(1999, 12, 1, 12, 52, 13)).daysInMonth, 31); + assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0)).daysInMonth == 28); + assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27)).daysInMonth == 29); + assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9)).daysInMonth == 30); + } - //Test B.C. - _assertPred!"=="(SysTime(DateTime(-1999, 1, 1, 12, 1, 13)).daysInMonth, 31); - _assertPred!"=="(SysTime(DateTime(-1999, 2, 1, 7, 13, 12)).daysInMonth, 28); - _assertPred!"=="(SysTime(DateTime(-2000, 2, 1, 13, 2, 12)).daysInMonth, 29); - _assertPred!"=="(SysTime(DateTime(-1999, 3, 1, 12, 13, 12)).daysInMonth, 31); - _assertPred!"=="(SysTime(DateTime(-1999, 4, 1, 12, 6, 13)).daysInMonth, 30); - _assertPred!"=="(SysTime(DateTime(-1999, 5, 1, 5, 13, 12)).daysInMonth, 31); - _assertPred!"=="(SysTime(DateTime(-1999, 6, 1, 13, 7, 12)).daysInMonth, 30); - _assertPred!"=="(SysTime(DateTime(-1999, 7, 1, 12, 13, 17)).daysInMonth, 31); - _assertPred!"=="(SysTime(DateTime(-1999, 8, 1, 12, 3, 13)).daysInMonth, 31); - _assertPred!"=="(SysTime(DateTime(-1999, 9, 1, 12, 13, 12)).daysInMonth, 30); - _assertPred!"=="(SysTime(DateTime(-1999, 10, 1, 13, 19, 12)).daysInMonth, 31); - _assertPred!"=="(SysTime(DateTime(-1999, 11, 1, 12, 13, 17)).daysInMonth, 30); - _assertPred!"=="(SysTime(DateTime(-1999, 12, 1, 12, 52, 13)).daysInMonth, 31); + unittest + { + //Test A.D. + assert(SysTime(DateTime(1999, 1, 1, 12, 1, 13)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 2, 1, 17, 13, 12)).daysInMonth == 28); + assert(SysTime(DateTime(2000, 2, 1, 13, 2, 12)).daysInMonth == 29); + assert(SysTime(DateTime(1999, 3, 1, 12, 13, 12)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 4, 1, 12, 6, 13)).daysInMonth == 30); + assert(SysTime(DateTime(1999, 5, 1, 15, 13, 12)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 6, 1, 13, 7, 12)).daysInMonth == 30); + assert(SysTime(DateTime(1999, 7, 1, 12, 13, 17)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 8, 1, 12, 3, 13)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 9, 1, 12, 13, 12)).daysInMonth == 30); + assert(SysTime(DateTime(1999, 10, 1, 13, 19, 12)).daysInMonth == 31); + assert(SysTime(DateTime(1999, 11, 1, 12, 13, 17)).daysInMonth == 30); + assert(SysTime(DateTime(1999, 12, 1, 12, 52, 13)).daysInMonth == 31); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.daysInMonth)); - //static assert(__traits(compiles, ist.daysInMonth)); + //Test B.C. + assert(SysTime(DateTime(-1999, 1, 1, 12, 1, 13)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 2, 1, 7, 13, 12)).daysInMonth == 28); + assert(SysTime(DateTime(-2000, 2, 1, 13, 2, 12)).daysInMonth == 29); + assert(SysTime(DateTime(-1999, 3, 1, 12, 13, 12)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 4, 1, 12, 6, 13)).daysInMonth == 30); + assert(SysTime(DateTime(-1999, 5, 1, 5, 13, 12)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 6, 1, 13, 7, 12)).daysInMonth == 30); + assert(SysTime(DateTime(-1999, 7, 1, 12, 13, 17)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 8, 1, 12, 3, 13)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 9, 1, 12, 13, 12)).daysInMonth == 30); + assert(SysTime(DateTime(-1999, 10, 1, 13, 19, 12)).daysInMonth == 31); + assert(SysTime(DateTime(-1999, 11, 1, 12, 13, 17)).daysInMonth == 30); + assert(SysTime(DateTime(-1999, 12, 1, 12, 52, 13)).daysInMonth == 31); - //Verify Examples. - assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).daysInMonth == 31); - assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0)).daysInMonth == 28); - assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27)).daysInMonth == 29); - assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9)).daysInMonth == 30); - } + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cst.daysInMonth)); + //static assert(__traits(compiles, ist.daysInMonth)); } /++ Whether the current year is a date in A.D. - - Examples: --------------------- -assert(SysTime(DateTime(1, 1, 1, 12, 7, 0)).isAD); -assert(SysTime(DateTime(2010, 12, 31, 0, 0, 0)).isAD); -assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); -assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); --------------------- +/ - @property bool isAD() const nothrow + @property bool isAD() @safe const nothrow { return adjTime >= 0; } + /// unittest { - version(testStdDateTime) - { - assert(SysTime(DateTime(2010, 7, 4, 12, 0, 9)).isAD); - assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).isAD); - assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); - assert(!SysTime(DateTime(0, 1, 1, 23, 59, 59)).isAD); - assert(!SysTime(DateTime(-1, 1, 1, 23 ,59 ,59)).isAD); - assert(!SysTime(DateTime(-2010, 7, 4, 12, 2, 2)).isAD); + assert(SysTime(DateTime(1, 1, 1, 12, 7, 0)).isAD); + assert(SysTime(DateTime(2010, 12, 31, 0, 0, 0)).isAD); + assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); + assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); + } - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.isAD)); - //static assert(__traits(compiles, ist.isAD)); + unittest + { + assert(SysTime(DateTime(2010, 7, 4, 12, 0, 9)).isAD); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).isAD); + assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); + assert(!SysTime(DateTime(0, 1, 1, 23, 59, 59)).isAD); + assert(!SysTime(DateTime(-1, 1, 1, 23 ,59 ,59)).isAD); + assert(!SysTime(DateTime(-2010, 7, 4, 12, 2, 2)).isAD); - //Verify Examples. - assert(SysTime(DateTime(1, 1, 1, 12, 7, 0)).isAD); - assert(SysTime(DateTime(2010, 12, 31, 0, 0, 0)).isAD); - assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD); - assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); - } + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cst.isAD)); + //static assert(__traits(compiles, ist.isAD)); } @@ -7488,7 +7180,7 @@ assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); this function returns 2_450_173, while from noon onward, the Julian day number would be 2_450_174, so this function returns 2_450_174. +/ - @property long julianDay() const nothrow + @property long julianDay() @safe const nothrow { immutable jd = dayOfGregorianCal + 1_721_425; @@ -7497,37 +7189,34 @@ assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); unittest { - version(testStdDateTime) - { - _assertPred!"=="(SysTime(DateTime(-4713, 11, 24, 0, 0, 0)).julianDay, -1); - _assertPred!"=="(SysTime(DateTime(-4713, 11, 24, 12, 0, 0)).julianDay, 0); + assert(SysTime(DateTime(-4713, 11, 24, 0, 0, 0)).julianDay == -1); + assert(SysTime(DateTime(-4713, 11, 24, 12, 0, 0)).julianDay == 0); - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 0, 0, 0)).julianDay, 1_721_424); - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 12, 0, 0)).julianDay, 1_721_425); + assert(SysTime(DateTime(0, 12, 31, 0, 0, 0)).julianDay == 1_721_424); + assert(SysTime(DateTime(0, 12, 31, 12, 0, 0)).julianDay == 1_721_425); - _assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0)).julianDay, 1_721_425); - _assertPred!"=="(SysTime(DateTime(1, 1, 1, 12, 0, 0)).julianDay, 1_721_426); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).julianDay == 1_721_425); + assert(SysTime(DateTime(1, 1, 1, 12, 0, 0)).julianDay == 1_721_426); - _assertPred!"=="(SysTime(DateTime(1582, 10, 15, 0, 0, 0)).julianDay, 2_299_160); - _assertPred!"=="(SysTime(DateTime(1582, 10, 15, 12, 0, 0)).julianDay, 2_299_161); + assert(SysTime(DateTime(1582, 10, 15, 0, 0, 0)).julianDay == 2_299_160); + assert(SysTime(DateTime(1582, 10, 15, 12, 0, 0)).julianDay == 2_299_161); - _assertPred!"=="(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).julianDay, 2_400_000); - _assertPred!"=="(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).julianDay, 2_400_001); + assert(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).julianDay == 2_400_000); + assert(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).julianDay == 2_400_001); - _assertPred!"=="(SysTime(DateTime(1982, 1, 4, 0, 0, 0)).julianDay, 2_444_973); - _assertPred!"=="(SysTime(DateTime(1982, 1, 4, 12, 0, 0)).julianDay, 2_444_974); + assert(SysTime(DateTime(1982, 1, 4, 0, 0, 0)).julianDay == 2_444_973); + assert(SysTime(DateTime(1982, 1, 4, 12, 0, 0)).julianDay == 2_444_974); - _assertPred!"=="(SysTime(DateTime(1996, 3, 31, 0, 0, 0)).julianDay, 2_450_173); - _assertPred!"=="(SysTime(DateTime(1996, 3, 31, 12, 0, 0)).julianDay, 2_450_174); + assert(SysTime(DateTime(1996, 3, 31, 0, 0, 0)).julianDay == 2_450_173); + assert(SysTime(DateTime(1996, 3, 31, 12, 0, 0)).julianDay == 2_450_174); - _assertPred!"=="(SysTime(DateTime(2010, 8, 24, 0, 0, 0)).julianDay, 2_455_432); - _assertPred!"=="(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).julianDay, 2_455_433); + assert(SysTime(DateTime(2010, 8, 24, 0, 0, 0)).julianDay == 2_455_432); + assert(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).julianDay == 2_455_433); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.julianDay)); - //static assert(__traits(compiles, ist.julianDay)); - } + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cst.julianDay)); + //static assert(__traits(compiles, ist.julianDay)); } @@ -7535,33 +7224,30 @@ assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); The modified $(WEB en.wikipedia.org/wiki/Julian_day, Julian day) for any time on this date (since, the modified Julian day changes at midnight). +/ - @property long modJulianDay() const nothrow + @property long modJulianDay() @safe const nothrow { return (dayOfGregorianCal + 1_721_425) - 2_400_001; } unittest { - version(testStdDateTime) - { - _assertPred!"=="(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).modJulianDay, 0); - _assertPred!"=="(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).modJulianDay, 0); + assert(SysTime(DateTime(1858, 11, 17, 0, 0, 0)).modJulianDay == 0); + assert(SysTime(DateTime(1858, 11, 17, 12, 0, 0)).modJulianDay == 0); - _assertPred!"=="(SysTime(DateTime(2010, 8, 24, 0, 0, 0)).modJulianDay, 55_432); - _assertPred!"=="(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).modJulianDay, 55_432); + assert(SysTime(DateTime(2010, 8, 24, 0, 0, 0)).modJulianDay == 55_432); + assert(SysTime(DateTime(2010, 8, 24, 12, 0, 0)).modJulianDay == 55_432); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cst.modJulianDay)); - //static assert(__traits(compiles, ist.modJulianDay)); - } + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cst.modJulianDay)); + //static assert(__traits(compiles, ist.modJulianDay)); } /++ Returns a $(LREF Date) equivalent to this $(LREF SysTime). +/ - Date opCast(T)() const nothrow + Date opCast(T)() @safe const nothrow if(is(Unqual!T == Date)) { return Date(dayOfGregorianCal); @@ -7569,36 +7255,33 @@ assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); unittest { - version(testStdDateTime) - { - _assertPred!"=="(cast(Date)SysTime(Date(1999, 7, 6)), Date(1999, 7, 6)); - _assertPred!"=="(cast(Date)SysTime(Date(2000, 12, 31)), Date(2000, 12, 31)); - _assertPred!"=="(cast(Date)SysTime(Date(2001, 1, 1)), Date(2001, 1, 1)); + assert(cast(Date)SysTime(Date(1999, 7, 6)) == Date(1999, 7, 6)); + assert(cast(Date)SysTime(Date(2000, 12, 31)) == Date(2000, 12, 31)); + assert(cast(Date)SysTime(Date(2001, 1, 1)) == Date(2001, 1, 1)); - _assertPred!"=="(cast(Date)SysTime(DateTime(1999, 7, 6, 12, 10, 9)), Date(1999, 7, 6)); - _assertPred!"=="(cast(Date)SysTime(DateTime(2000, 12, 31, 13, 11, 10)), Date(2000, 12, 31)); - _assertPred!"=="(cast(Date)SysTime(DateTime(2001, 1, 1, 14, 12, 11)), Date(2001, 1, 1)); + assert(cast(Date)SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == Date(1999, 7, 6)); + assert(cast(Date)SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == Date(2000, 12, 31)); + assert(cast(Date)SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == Date(2001, 1, 1)); - _assertPred!"=="(cast(Date)SysTime(Date(-1999, 7, 6)), Date(-1999, 7, 6)); - _assertPred!"=="(cast(Date)SysTime(Date(-2000, 12, 31)), Date(-2000, 12, 31)); - _assertPred!"=="(cast(Date)SysTime(Date(-2001, 1, 1)), Date(-2001, 1, 1)); + assert(cast(Date)SysTime(Date(-1999, 7, 6)) == Date(-1999, 7, 6)); + assert(cast(Date)SysTime(Date(-2000, 12, 31)) == Date(-2000, 12, 31)); + assert(cast(Date)SysTime(Date(-2001, 1, 1)) == Date(-2001, 1, 1)); - _assertPred!"=="(cast(Date)SysTime(DateTime(-1999, 7, 6, 12, 10, 9)), Date(-1999, 7, 6)); - _assertPred!"=="(cast(Date)SysTime(DateTime(-2000, 12, 31, 13, 11, 10)), Date(-2000, 12, 31)); - _assertPred!"=="(cast(Date)SysTime(DateTime(-2001, 1, 1, 14, 12, 11)), Date(-2001, 1, 1)); + assert(cast(Date)SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == Date(-1999, 7, 6)); + assert(cast(Date)SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == Date(-2000, 12, 31)); + assert(cast(Date)SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == Date(-2001, 1, 1)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cast(Date)cst)); - //static assert(__traits(compiles, cast(Date)ist)); - } + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cast(Date)cst)); + //static assert(__traits(compiles, cast(Date)ist)); } /++ Returns a $(LREF DateTime) equivalent to this $(LREF SysTime). +/ - DateTime opCast(T)() const nothrow + DateTime opCast(T)() @safe const nothrow if(is(Unqual!T == DateTime)) { try @@ -7624,43 +7307,40 @@ assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); unittest { - version(testStdDateTime) - { - _assertPred!"=="(cast(DateTime)SysTime(DateTime(1, 1, 6, 7, 12, 22)), DateTime(1, 1, 6, 7, 12, 22)); - _assertPred!"=="(cast(DateTime)SysTime(DateTime(1, 1, 6, 7, 12, 22), FracSec.from!"msecs"(22)), DateTime(1, 1, 6, 7, 12, 22)); - _assertPred!"=="(cast(DateTime)SysTime(Date(1999, 7, 6)), DateTime(1999, 7, 6, 0, 0, 0)); - _assertPred!"=="(cast(DateTime)SysTime(Date(2000, 12, 31)), DateTime(2000, 12, 31, 0, 0, 0)); - _assertPred!"=="(cast(DateTime)SysTime(Date(2001, 1, 1)), DateTime(2001, 1, 1, 0, 0, 0)); + assert(cast(DateTime)SysTime(DateTime(1, 1, 6, 7, 12, 22)) == DateTime(1, 1, 6, 7, 12, 22)); + assert(cast(DateTime)SysTime(DateTime(1, 1, 6, 7, 12, 22), FracSec.from!"msecs"(22)) == DateTime(1, 1, 6, 7, 12, 22)); + assert(cast(DateTime)SysTime(Date(1999, 7, 6)) == DateTime(1999, 7, 6, 0, 0, 0)); + assert(cast(DateTime)SysTime(Date(2000, 12, 31)) == DateTime(2000, 12, 31, 0, 0, 0)); + assert(cast(DateTime)SysTime(Date(2001, 1, 1)) == DateTime(2001, 1, 1, 0, 0, 0)); - _assertPred!"=="(cast(DateTime)SysTime(DateTime(1999, 7, 6, 12, 10, 9)), DateTime(1999, 7, 6, 12, 10, 9)); - _assertPred!"=="(cast(DateTime)SysTime(DateTime(2000, 12, 31, 13, 11, 10)), DateTime(2000, 12, 31, 13, 11, 10)); - _assertPred!"=="(cast(DateTime)SysTime(DateTime(2001, 1, 1, 14, 12, 11)), DateTime(2001, 1, 1, 14, 12, 11)); + assert(cast(DateTime)SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == DateTime(1999, 7, 6, 12, 10, 9)); + assert(cast(DateTime)SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == DateTime(2000, 12, 31, 13, 11, 10)); + assert(cast(DateTime)SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == DateTime(2001, 1, 1, 14, 12, 11)); - _assertPred!"=="(cast(DateTime)SysTime(DateTime(-1, 1, 6, 7, 12, 22)), DateTime(-1, 1, 6, 7, 12, 22)); - _assertPred!"=="(cast(DateTime)SysTime(DateTime(-1, 1, 6, 7, 12, 22), FracSec.from!"msecs"(22)), DateTime(-1, 1, 6, 7, 12, 22)); - _assertPred!"=="(cast(DateTime)SysTime(Date(-1999, 7, 6)), DateTime(-1999, 7, 6, 0, 0, 0)); - _assertPred!"=="(cast(DateTime)SysTime(Date(-2000, 12, 31)), DateTime(-2000, 12, 31, 0, 0, 0)); - _assertPred!"=="(cast(DateTime)SysTime(Date(-2001, 1, 1)), DateTime(-2001, 1, 1, 0, 0, 0)); + assert(cast(DateTime)SysTime(DateTime(-1, 1, 6, 7, 12, 22)) == DateTime(-1, 1, 6, 7, 12, 22)); + assert(cast(DateTime)SysTime(DateTime(-1, 1, 6, 7, 12, 22), FracSec.from!"msecs"(22)) == DateTime(-1, 1, 6, 7, 12, 22)); + assert(cast(DateTime)SysTime(Date(-1999, 7, 6)) == DateTime(-1999, 7, 6, 0, 0, 0)); + assert(cast(DateTime)SysTime(Date(-2000, 12, 31)) == DateTime(-2000, 12, 31, 0, 0, 0)); + assert(cast(DateTime)SysTime(Date(-2001, 1, 1)) == DateTime(-2001, 1, 1, 0, 0, 0)); - _assertPred!"=="(cast(DateTime)SysTime(DateTime(-1999, 7, 6, 12, 10, 9)), DateTime(-1999, 7, 6, 12, 10, 9)); - _assertPred!"=="(cast(DateTime)SysTime(DateTime(-2000, 12, 31, 13, 11, 10)), DateTime(-2000, 12, 31, 13, 11, 10)); - _assertPred!"=="(cast(DateTime)SysTime(DateTime(-2001, 1, 1, 14, 12, 11)), DateTime(-2001, 1, 1, 14, 12, 11)); + assert(cast(DateTime)SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == DateTime(-1999, 7, 6, 12, 10, 9)); + assert(cast(DateTime)SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == DateTime(-2000, 12, 31, 13, 11, 10)); + assert(cast(DateTime)SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == DateTime(-2001, 1, 1, 14, 12, 11)); - _assertPred!"=="(cast(DateTime)SysTime(DateTime(2011, 1, 13, 8, 17, 2), FracSec.from!"msecs"(296), LocalTime()), - DateTime(2011, 1, 13, 8, 17, 2)); + assert(cast(DateTime)SysTime(DateTime(2011, 1, 13, 8, 17, 2), FracSec.from!"msecs"(296), LocalTime()) == + DateTime(2011, 1, 13, 8, 17, 2)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cast(DateTime)cst)); - //static assert(__traits(compiles, cast(DateTime)ist)); - } + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cast(DateTime)cst)); + //static assert(__traits(compiles, cast(DateTime)ist)); } /++ Returns a $(LREF TimeOfDay) equivalent to this $(LREF SysTime). +/ - TimeOfDay opCast(T)() const nothrow + TimeOfDay opCast(T)() @safe const nothrow if(is(Unqual!T == TimeOfDay)) { try @@ -7683,29 +7363,26 @@ assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); unittest { - version(testStdDateTime) - { - _assertPred!"=="(cast(TimeOfDay)SysTime(Date(1999, 7, 6)), TimeOfDay(0, 0, 0)); - _assertPred!"=="(cast(TimeOfDay)SysTime(Date(2000, 12, 31)), TimeOfDay(0, 0, 0)); - _assertPred!"=="(cast(TimeOfDay)SysTime(Date(2001, 1, 1)), TimeOfDay(0, 0, 0)); + assert(cast(TimeOfDay)SysTime(Date(1999, 7, 6)) == TimeOfDay(0, 0, 0)); + assert(cast(TimeOfDay)SysTime(Date(2000, 12, 31)) == TimeOfDay(0, 0, 0)); + assert(cast(TimeOfDay)SysTime(Date(2001, 1, 1)) == TimeOfDay(0, 0, 0)); - _assertPred!"=="(cast(TimeOfDay)SysTime(DateTime(1999, 7, 6, 12, 10, 9)), TimeOfDay(12, 10, 9)); - _assertPred!"=="(cast(TimeOfDay)SysTime(DateTime(2000, 12, 31, 13, 11, 10)), TimeOfDay(13, 11, 10)); - _assertPred!"=="(cast(TimeOfDay)SysTime(DateTime(2001, 1, 1, 14, 12, 11)), TimeOfDay(14, 12, 11)); + assert(cast(TimeOfDay)SysTime(DateTime(1999, 7, 6, 12, 10, 9)) == TimeOfDay(12, 10, 9)); + assert(cast(TimeOfDay)SysTime(DateTime(2000, 12, 31, 13, 11, 10)) == TimeOfDay(13, 11, 10)); + assert(cast(TimeOfDay)SysTime(DateTime(2001, 1, 1, 14, 12, 11)) == TimeOfDay(14, 12, 11)); - _assertPred!"=="(cast(TimeOfDay)SysTime(Date(-1999, 7, 6)), TimeOfDay(0, 0, 0)); - _assertPred!"=="(cast(TimeOfDay)SysTime(Date(-2000, 12, 31)), TimeOfDay(0, 0, 0)); - _assertPred!"=="(cast(TimeOfDay)SysTime(Date(-2001, 1, 1)), TimeOfDay(0, 0, 0)); + assert(cast(TimeOfDay)SysTime(Date(-1999, 7, 6)) == TimeOfDay(0, 0, 0)); + assert(cast(TimeOfDay)SysTime(Date(-2000, 12, 31)) == TimeOfDay(0, 0, 0)); + assert(cast(TimeOfDay)SysTime(Date(-2001, 1, 1)) == TimeOfDay(0, 0, 0)); - _assertPred!"=="(cast(TimeOfDay)SysTime(DateTime(-1999, 7, 6, 12, 10, 9)), TimeOfDay(12, 10, 9)); - _assertPred!"=="(cast(TimeOfDay)SysTime(DateTime(-2000, 12, 31, 13, 11, 10)), TimeOfDay(13, 11, 10)); - _assertPred!"=="(cast(TimeOfDay)SysTime(DateTime(-2001, 1, 1, 14, 12, 11)), TimeOfDay(14, 12, 11)); + assert(cast(TimeOfDay)SysTime(DateTime(-1999, 7, 6, 12, 10, 9)) == TimeOfDay(12, 10, 9)); + assert(cast(TimeOfDay)SysTime(DateTime(-2000, 12, 31, 13, 11, 10)) == TimeOfDay(13, 11, 10)); + assert(cast(TimeOfDay)SysTime(DateTime(-2001, 1, 1, 14, 12, 11)) == TimeOfDay(14, 12, 11)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cast(TimeOfDay)cst)); - //static assert(__traits(compiles, cast(TimeOfDay)ist)); - } + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cast(TimeOfDay)cst)); + //static assert(__traits(compiles, cast(TimeOfDay)ist)); } @@ -7714,7 +7391,7 @@ assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); //It may be a good idea to keep it though, since casting from a type to itself //should be allowed, and it doesn't work without this opCast() since opCast() //has already been defined for other types. - SysTime opCast(T)() const pure nothrow + SysTime opCast(T)() @safe const pure nothrow if(is(Unqual!T == SysTime)) { return SysTime(_stdTime, _timezone); @@ -7738,25 +7415,8 @@ assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD); is $(I not) enough to uniquely identify the time zone. Time zone offsets will be in the form +HH:MM or -HH:MM. - - Examples: --------------------- -assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOString() == - "20100704T070612"); - -assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), - FracSec.from!"msecs"(24)).toISOString() == - "19981225T021500.024"); - -assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOString() == - "00000105T230959"); - -assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), - FracSec.from!"hnsecs"(520_920)).toISOString() == - "-00040105T000002.052092"); --------------------- +/ - string toISOString() const nothrow + string toISOString() @safe const nothrow { try { @@ -7795,73 +7455,73 @@ assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), assert(0, "format() threw."); } + /// unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(SysTime(DateTime.init, UTC()).toISOString(), "00010101T000000Z"); - _assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1), UTC()).toISOString(), "00010101T000000.0000001Z"); + assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOString() == + "20100704T070612"); - _assertPred!"=="(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toISOString(), "00091204T000000"); - _assertPred!"=="(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toISOString(), "00991204T050612"); - _assertPred!"=="(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toISOString(), "09991204T134459"); - _assertPred!"=="(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toISOString(), "99990704T235959"); - _assertPred!"=="(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toISOString(), "+100001020T010101"); + assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), + FracSec.from!"msecs"(24)).toISOString() == + "19981225T021500.024"); - _assertPred!"=="(SysTime(DateTime(9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toISOString(), "00091204T000000.042"); - _assertPred!"=="(SysTime(DateTime(99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toISOString(), "00991204T050612.1"); - _assertPred!"=="(SysTime(DateTime(999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toISOString(), "09991204T134459.04502"); - _assertPred!"=="(SysTime(DateTime(9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toISOString(), "99990704T235959.0000012"); - _assertPred!"=="(SysTime(DateTime(10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toISOString(), "+100001020T010101.050789"); + assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOString() == + "00000105T230959"); - _assertPred!"=="(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(-360))).toISOString(), - "20121221T121212-06:00"); - - _assertPred!"=="(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(420))).toISOString(), - "20121221T121212+07:00"); - - //Test B.C. - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC()).toISOString(), "00001231T235959.9999999Z"); - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1), UTC()).toISOString(), "00001231T235959.0000001Z"); - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toISOString(), "00001231T235959Z"); + assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), + FracSec.from!"hnsecs"(520_920)).toISOString() == + "-00040105T000002.052092"); + } - _assertPred!"=="(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toISOString(), "00001204T001204"); - _assertPred!"=="(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toISOString(), "-00091204T000000"); - _assertPred!"=="(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toISOString(), "-00991204T050612"); - _assertPred!"=="(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toISOString(), "-09991204T134459"); - _assertPred!"=="(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toISOString(), "-99990704T235959"); - _assertPred!"=="(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toISOString(), "-100001020T010101"); + unittest + { + //Test A.D. + assert(SysTime(DateTime.init, UTC()).toISOString() == "00010101T000000Z"); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1), UTC()).toISOString() == "00010101T000000.0000001Z"); - _assertPred!"=="(SysTime(DateTime(0, 12, 4, 0, 0, 0), FracSec.from!"msecs"(7)).toISOString(), "00001204T000000.007"); - _assertPred!"=="(SysTime(DateTime(-9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toISOString(), "-00091204T000000.042"); - _assertPred!"=="(SysTime(DateTime(-99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toISOString(), "-00991204T050612.1"); - _assertPred!"=="(SysTime(DateTime(-999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toISOString(), "-09991204T134459.04502"); - _assertPred!"=="(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toISOString(), "-99990704T235959.0000012"); - _assertPred!"=="(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toISOString(), "-100001020T010101.050789"); + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toISOString() == "00091204T000000"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toISOString() == "00991204T050612"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toISOString() == "09991204T134459"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toISOString() == "99990704T235959"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toISOString() == "+100001020T010101"); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cast(TimeOfDay)cst)); - //static assert(__traits(compiles, cast(TimeOfDay)ist)); + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toISOString() == "00091204T000000.042"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toISOString() == "00991204T050612.1"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toISOString() == "09991204T134459.04502"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toISOString() == "99990704T235959.0000012"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toISOString() == "+100001020T010101.050789"); - //Verify Examples. - assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOString() == - "20100704T070612"); + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(-360))).toISOString() == + "20121221T121212-06:00"); - assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), - FracSec.from!"msecs"(24)).toISOString() == - "19981225T021500.024"); + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(420))).toISOString() == + "20121221T121212+07:00"); - assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOString() == - "00000105T230959"); + //Test B.C. + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC()).toISOString() == "00001231T235959.9999999Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1), UTC()).toISOString() == "00001231T235959.0000001Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toISOString() == "00001231T235959Z"); + + assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toISOString() == "00001204T001204"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toISOString() == "-00091204T000000"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toISOString() == "-00991204T050612"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toISOString() == "-09991204T134459"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toISOString() == "-99990704T235959"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toISOString() == "-100001020T010101"); + + assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), FracSec.from!"msecs"(7)).toISOString() == "00001204T000000.007"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toISOString() == "-00091204T000000.042"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toISOString() == "-00991204T050612.1"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toISOString() == "-09991204T134459.04502"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toISOString() == "-99990704T235959.0000012"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toISOString() == "-100001020T010101.050789"); - assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), - FracSec.from!"hnsecs"(520_920)).toISOString() == - "-00040105T000002.052092"); - } + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cast(TimeOfDay)cst)); + //static assert(__traits(compiles, cast(TimeOfDay)ist)); } @@ -7883,25 +7543,8 @@ assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), $(I not) enough to uniquely identify the time zone. Time zone offsets will be in the form +HH:MM or -HH:MM. - - Examples: --------------------- -assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOExtString() == - "2010-07-04T07:06:12"); - -assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), - FracSec.from!"msecs"(24)).toISOExtString() == - "1998-12-25T02:15:00.024"); - -assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOExtString() == - "0000-01-05T23:09:59"); - -assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), - FracSec.from!"hnsecs"(520_920)).toISOExtString() == - "-0004-01-05T00:00:02.052092"); --------------------- +/ - string toISOExtString() const nothrow + string toISOExtString() @safe const nothrow { try { @@ -7940,73 +7583,73 @@ assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), assert(0, "format() threw."); } + /// unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(SysTime(DateTime.init, UTC()).toISOExtString(), "0001-01-01T00:00:00Z"); - _assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1), UTC()).toISOExtString(), "0001-01-01T00:00:00.0000001Z"); - - _assertPred!"=="(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toISOExtString(), "0009-12-04T00:00:00"); - _assertPred!"=="(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toISOExtString(), "0099-12-04T05:06:12"); - _assertPred!"=="(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toISOExtString(), "0999-12-04T13:44:59"); - _assertPred!"=="(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toISOExtString(), "9999-07-04T23:59:59"); - _assertPred!"=="(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toISOExtString(), "+10000-10-20T01:01:01"); + assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOExtString() == + "2010-07-04T07:06:12"); - _assertPred!"=="(SysTime(DateTime(9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toISOExtString(), "0009-12-04T00:00:00.042"); - _assertPred!"=="(SysTime(DateTime(99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toISOExtString(), "0099-12-04T05:06:12.1"); - _assertPred!"=="(SysTime(DateTime(999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toISOExtString(), "0999-12-04T13:44:59.04502"); - _assertPred!"=="(SysTime(DateTime(9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toISOExtString(), "9999-07-04T23:59:59.0000012"); - _assertPred!"=="(SysTime(DateTime(10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toISOExtString(), "+10000-10-20T01:01:01.050789"); + assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), + FracSec.from!"msecs"(24)).toISOExtString() == + "1998-12-25T02:15:00.024"); - _assertPred!"=="(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(-360))).toISOExtString(), - "2012-12-21T12:12:12-06:00"); + assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOExtString() == + "0000-01-05T23:09:59"); - _assertPred!"=="(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(420))).toISOExtString(), - "2012-12-21T12:12:12+07:00"); - - //Test B.C. - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC()).toISOExtString(), "0000-12-31T23:59:59.9999999Z"); - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1), UTC()).toISOExtString(), "0000-12-31T23:59:59.0000001Z"); - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toISOExtString(), "0000-12-31T23:59:59Z"); + assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), + FracSec.from!"hnsecs"(520_920)).toISOExtString() == + "-0004-01-05T00:00:02.052092"); + } - _assertPred!"=="(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toISOExtString(), "0000-12-04T00:12:04"); - _assertPred!"=="(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toISOExtString(), "-0009-12-04T00:00:00"); - _assertPred!"=="(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toISOExtString(), "-0099-12-04T05:06:12"); - _assertPred!"=="(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toISOExtString(), "-0999-12-04T13:44:59"); - _assertPred!"=="(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toISOExtString(), "-9999-07-04T23:59:59"); - _assertPred!"=="(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toISOExtString(), "-10000-10-20T01:01:01"); + unittest + { + //Test A.D. + assert(SysTime(DateTime.init, UTC()).toISOExtString() == "0001-01-01T00:00:00Z"); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1), UTC()).toISOExtString() == "0001-01-01T00:00:00.0000001Z"); - _assertPred!"=="(SysTime(DateTime(0, 12, 4, 0, 0, 0), FracSec.from!"msecs"(7)).toISOExtString(), "0000-12-04T00:00:00.007"); - _assertPred!"=="(SysTime(DateTime(-9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toISOExtString(), "-0009-12-04T00:00:00.042"); - _assertPred!"=="(SysTime(DateTime(-99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toISOExtString(), "-0099-12-04T05:06:12.1"); - _assertPred!"=="(SysTime(DateTime(-999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toISOExtString(), "-0999-12-04T13:44:59.04502"); - _assertPred!"=="(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toISOExtString(), "-9999-07-04T23:59:59.0000012"); - _assertPred!"=="(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toISOExtString(), "-10000-10-20T01:01:01.050789"); + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01"); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cast(TimeOfDay)cst)); - //static assert(__traits(compiles, cast(TimeOfDay)ist)); + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toISOExtString() == "0009-12-04T00:00:00.042"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toISOExtString() == "0099-12-04T05:06:12.1"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toISOExtString() == "0999-12-04T13:44:59.04502"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toISOExtString() == "9999-07-04T23:59:59.0000012"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toISOExtString() == "+10000-10-20T01:01:01.050789"); - //Verify Examples. - assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOExtString() == - "2010-07-04T07:06:12"); + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(-360))).toISOExtString() == + "2012-12-21T12:12:12-06:00"); - assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), - FracSec.from!"msecs"(24)).toISOExtString() == - "1998-12-25T02:15:00.024"); + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(420))).toISOExtString() == + "2012-12-21T12:12:12+07:00"); - assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOExtString() == - "0000-01-05T23:09:59"); + //Test B.C. + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC()).toISOExtString() == "0000-12-31T23:59:59.9999999Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1), UTC()).toISOExtString() == "0000-12-31T23:59:59.0000001Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toISOExtString() == "0000-12-31T23:59:59Z"); + + assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01"); + + assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), FracSec.from!"msecs"(7)).toISOExtString() == "0000-12-04T00:00:00.007"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toISOExtString() == "-0009-12-04T00:00:00.042"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toISOExtString() == "-0099-12-04T05:06:12.1"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toISOExtString() == "-0999-12-04T13:44:59.04502"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toISOExtString() == "-9999-07-04T23:59:59.0000012"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toISOExtString() == "-10000-10-20T01:01:01.050789"); - assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), - FracSec.from!"hnsecs"(520_920)).toISOExtString() == - "-0004-01-05T00:00:02.052092"); - } + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cast(TimeOfDay)cst)); + //static assert(__traits(compiles, cast(TimeOfDay)ist)); } /++ @@ -8026,25 +7669,8 @@ assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), $(I not) enough to uniquely identify the time zone. Time zone offsets will be in the form +HH:MM or -HH:MM. - - Examples: --------------------- -assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toSimpleString() == - "2010-Jul-04 07:06:12"); - -assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), - FracSec.from!"msecs"(24)).toSimpleString() == - "1998-Dec-25 02:15:00.024"); - -assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toSimpleString() == - "0000-Jan-05 23:09:59"); - -assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), - FracSec.from!"hnsecs"(520_920)).toSimpleString() == - "-0004-Jan-05 00:00:02.052092"); --------------------- +/ - string toSimpleString() const nothrow + string toSimpleString() @safe const nothrow { try { @@ -8083,109 +7709,92 @@ assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), assert(0, "format() threw."); } + /// unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(SysTime(DateTime.init, UTC()).toString(), "0001-Jan-01 00:00:00Z"); - _assertPred!"=="(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1), UTC()).toString(), "0001-Jan-01 00:00:00.0000001Z"); - - _assertPred!"=="(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toSimpleString(), "0009-Dec-04 00:00:00"); - _assertPred!"=="(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toSimpleString(), "0099-Dec-04 05:06:12"); - _assertPred!"=="(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toSimpleString(), "0999-Dec-04 13:44:59"); - _assertPred!"=="(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toSimpleString(), "9999-Jul-04 23:59:59"); - _assertPred!"=="(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toSimpleString(), "+10000-Oct-20 01:01:01"); - - _assertPred!"=="(SysTime(DateTime(9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toSimpleString(), "0009-Dec-04 00:00:00.042"); - _assertPred!"=="(SysTime(DateTime(99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toSimpleString(), "0099-Dec-04 05:06:12.1"); - _assertPred!"=="(SysTime(DateTime(999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toSimpleString(), "0999-Dec-04 13:44:59.04502"); - _assertPred!"=="(SysTime(DateTime(9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toSimpleString(), "9999-Jul-04 23:59:59.0000012"); - _assertPred!"=="(SysTime(DateTime(10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toSimpleString(), "+10000-Oct-20 01:01:01.050789"); + assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toSimpleString() == + "2010-Jul-04 07:06:12"); - _assertPred!"=="(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(-360))).toSimpleString(), - "2012-Dec-21 12:12:12-06:00"); + assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), + FracSec.from!"msecs"(24)).toSimpleString() == + "1998-Dec-25 02:15:00.024"); - _assertPred!"=="(SysTime(DateTime(2012, 12, 21, 12, 12, 12), - new immutable SimpleTimeZone(dur!"minutes"(420))).toSimpleString(), - "2012-Dec-21 12:12:12+07:00"); + assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toSimpleString() == + "0000-Jan-05 23:09:59"); - //Test B.C. - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC()).toSimpleString(), "0000-Dec-31 23:59:59.9999999Z"); - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1), UTC()).toSimpleString(), "0000-Dec-31 23:59:59.0000001Z"); - _assertPred!"=="(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toSimpleString(), "0000-Dec-31 23:59:59Z"); - - _assertPred!"=="(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toSimpleString(), "0000-Dec-04 00:12:04"); - _assertPred!"=="(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toSimpleString(), "-0009-Dec-04 00:00:00"); - _assertPred!"=="(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toSimpleString(), "-0099-Dec-04 05:06:12"); - _assertPred!"=="(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toSimpleString(), "-0999-Dec-04 13:44:59"); - _assertPred!"=="(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toSimpleString(), "-9999-Jul-04 23:59:59"); - _assertPred!"=="(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toSimpleString(), "-10000-Oct-20 01:01:01"); - - _assertPred!"=="(SysTime(DateTime(0, 12, 4, 0, 0, 0), FracSec.from!"msecs"(7)).toSimpleString(), "0000-Dec-04 00:00:00.007"); - _assertPred!"=="(SysTime(DateTime(-9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toSimpleString(), "-0009-Dec-04 00:00:00.042"); - _assertPred!"=="(SysTime(DateTime(-99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toSimpleString(), "-0099-Dec-04 05:06:12.1"); - _assertPred!"=="(SysTime(DateTime(-999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toSimpleString(), "-0999-Dec-04 13:44:59.04502"); - _assertPred!"=="(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toSimpleString(), "-9999-Jul-04 23:59:59.0000012"); - _assertPred!"=="(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toSimpleString(), "-10000-Oct-20 01:01:01.050789"); + assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), + FracSec.from!"hnsecs"(520_920)).toSimpleString() == + "-0004-Jan-05 00:00:02.052092"); + } - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, cast(TimeOfDay)cst)); - //static assert(__traits(compiles, cast(TimeOfDay)ist)); + unittest + { + //Test A.D. + assert(SysTime(DateTime.init, UTC()).toString() == "0001-Jan-01 00:00:00Z"); + assert(SysTime(DateTime(1, 1, 1, 0, 0, 0), FracSec.from!"hnsecs"(1), UTC()).toString() == "0001-Jan-01 00:00:00.0000001Z"); - //Verify Examples. - assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toSimpleString() == - "2010-Jul-04 07:06:12"); + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01"); - assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), - FracSec.from!"msecs"(24)).toSimpleString() == - "1998-Dec-25 02:15:00.024"); + assert(SysTime(DateTime(9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toSimpleString() == "0009-Dec-04 00:00:00.042"); + assert(SysTime(DateTime(99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toSimpleString() == "0099-Dec-04 05:06:12.1"); + assert(SysTime(DateTime(999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toSimpleString() == "0999-Dec-04 13:44:59.04502"); + assert(SysTime(DateTime(9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toSimpleString() == "9999-Jul-04 23:59:59.0000012"); + assert(SysTime(DateTime(10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toSimpleString() == "+10000-Oct-20 01:01:01.050789"); - assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toSimpleString() == - "0000-Jan-05 23:09:59"); + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(-360))).toSimpleString() == + "2012-Dec-21 12:12:12-06:00"); - assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), - FracSec.from!"hnsecs"(520_920)).toSimpleString() == - "-0004-Jan-05 00:00:02.052092"); - } - } + assert(SysTime(DateTime(2012, 12, 21, 12, 12, 12), + new immutable SimpleTimeZone(dur!"minutes"(420))).toSimpleString() == + "2012-Dec-21 12:12:12+07:00"); + //Test B.C. + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(9_999_999), UTC()).toSimpleString() == "0000-Dec-31 23:59:59.9999999Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), FracSec.from!"hnsecs"(1), UTC()).toSimpleString() == "0000-Dec-31 23:59:59.0000001Z"); + assert(SysTime(DateTime(0, 12, 31, 23, 59, 59), UTC()).toSimpleString() == "0000-Dec-31 23:59:59Z"); + + assert(SysTime(DateTime(0, 12, 4, 0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01"); + + assert(SysTime(DateTime(0, 12, 4, 0, 0, 0), FracSec.from!"msecs"(7)).toSimpleString() == "0000-Dec-04 00:00:00.007"); + assert(SysTime(DateTime(-9, 12, 4, 0, 0, 0), FracSec.from!"msecs"(42)).toSimpleString() == "-0009-Dec-04 00:00:00.042"); + assert(SysTime(DateTime(-99, 12, 4, 5, 6, 12), FracSec.from!"msecs"(100)).toSimpleString() == "-0099-Dec-04 05:06:12.1"); + assert(SysTime(DateTime(-999, 12, 4, 13, 44, 59), FracSec.from!"usecs"(45020)).toSimpleString() == "-0999-Dec-04 13:44:59.04502"); + assert(SysTime(DateTime(-9999, 7, 4, 23, 59, 59), FracSec.from!"hnsecs"(12)).toSimpleString() == "-9999-Jul-04 23:59:59.0000012"); + assert(SysTime(DateTime(-10000, 10, 20, 1, 1, 1), FracSec.from!"hnsecs"(507890)).toSimpleString() == "-10000-Oct-20 01:01:01.050789"); - /+ - Converts this $(LREF SysTime) to a string. - +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() - { - return toSimpleString(); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, cast(TimeOfDay)cst)); + //static assert(__traits(compiles, cast(TimeOfDay)ist)); } + /++ Converts this $(LREF SysTime) to a string. +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() const nothrow + string toString() @safe const nothrow { return toSimpleString(); } unittest { - version(testStdDateTime) - { - auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); - static assert(__traits(compiles, st.toString())); - static assert(__traits(compiles, cst.toString())); - //static assert(__traits(compiles, ist.toString())); - } + auto st = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + const cst = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + //immutable ist = SysTime(DateTime(1999, 7, 6, 12, 30, 33)); + static assert(__traits(compiles, st.toString())); + static assert(__traits(compiles, cst.toString())); + //static assert(__traits(compiles, ist.toString())); } @@ -8218,31 +7827,8 @@ assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), Throws: $(LREF DateTimeException) if the given string is not in the ISO format or if the resulting $(LREF SysTime) would not be valid. - - Examples: --------------------- -assert(SysTime.fromISOString("20100704T070612") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); -assert(SysTime.fromISOString("19981225T021500.007") == - SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(7))); -assert(SysTime.fromISOString("00000105T230959.00002") == - SysTime(DateTime(0, 1, 5, 23, 9, 59), FracSec.from!"usecs"(20))); -assert(SysTime.fromISOString("-00040105T000002") == - SysTime(DateTime(-4, 1, 5, 0, 0, 2))); -assert(SysTime.fromISOString(" 20100704T070612 ") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - -assert(SysTime.fromISOString("20100704T070612Z") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); -assert(SysTime.fromISOString("20100704T070612-8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new SimpleTimeZone(dur!"hours"(-8)))); -assert(SysTime.fromISOString("20100704T070612+8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new SimpleTimeZone(dur!"hours"(8)))); --------------------- +/ - static SysTime fromISOString(S)(in S isoString, immutable TimeZone tz = null) + static SysTime fromISOString(S)(in S isoString, immutable TimeZone tz = null) @safe if(isSomeString!S) { auto dstr = to!dstring(strip(isoString)); @@ -8296,122 +7882,130 @@ assert(SysTime.fromISOString("20100704T070612+8:00") == throw new DateTimeException(format("Invalid ISO String: %s", isoString)); } + /// unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException(SysTime.fromISOString("")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704000000")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704 000000")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704t000000")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.A")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.Z")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.00000000")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.00000000")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000-")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000:")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000-:")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+:")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000-1:")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+1:")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+1:0")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000-24.00")); - assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+24.00")); - - assertThrown!DateTimeException(SysTime.fromISOString("2010-07-0400:00:00")); - assertThrown!DateTimeException(SysTime.fromISOString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOString("2010-07-04T00:00:00.")); - - assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-04 00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-04T00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-04 00:00:00.")); - - assertThrown!DateTimeException(SysTime.fromISOString("2010-12-22T172201")); - assertThrown!DateTimeException(SysTime.fromISOString("2010-Dec-22 17:22:01")); - - _assertPred!"=="(SysTime.fromISOString("20101222T172201"), SysTime(DateTime(2010, 12, 22, 17, 22, 01))); - _assertPred!"=="(SysTime.fromISOString("19990706T123033"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - _assertPred!"=="(SysTime.fromISOString("-19990706T123033"), SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - _assertPred!"=="(SysTime.fromISOString("+019990706T123033"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - _assertPred!"=="(SysTime.fromISOString("19990706T123033 "), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - _assertPred!"=="(SysTime.fromISOString(" 19990706T123033"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - _assertPred!"=="(SysTime.fromISOString(" 19990706T123033 "), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - - _assertPred!"=="(SysTime.fromISOString("19070707T121212.0"), SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - _assertPred!"=="(SysTime.fromISOString("19070707T121212.0000000"), SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - _assertPred!"=="(SysTime.fromISOString("19070707T121212.0000001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"hnsecs"(1))); - _assertPred!"=="(SysTime.fromISOString("19070707T121212.000001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); - _assertPred!"=="(SysTime.fromISOString("19070707T121212.0000010"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); - _assertPred!"=="(SysTime.fromISOString("19070707T121212.001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); - _assertPred!"=="(SysTime.fromISOString("19070707T121212.0010000"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); - - _assertPred!"=="(SysTime.fromISOString("20101222T172201Z"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); - _assertPred!"=="(SysTime.fromISOString("20101222T172201-1:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); - _assertPred!"=="(SysTime.fromISOString("20101222T172201-1"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); - _assertPred!"=="(SysTime.fromISOString("20101222T172201-1:30"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-90)))); - _assertPred!"=="(SysTime.fromISOString("20101222T172201-8:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-480)))); - _assertPred!"=="(SysTime.fromISOString("20101222T172201+1:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); - _assertPred!"=="(SysTime.fromISOString("20101222T172201+1"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); - _assertPred!"=="(SysTime.fromISOString("20101222T172201+1:30"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(90)))); - _assertPred!"=="(SysTime.fromISOString("20101222T172201+8:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(480)))); - - _assertPred!"=="(SysTime.fromISOString("20101103T065106.57159Z"), - SysTime(DateTime(2010, 11, 3, 6, 51, 6), FracSec.from!"hnsecs"(5715900), UTC())); - - _assertPred!"=="(SysTime.fromISOString("20101222T172201.23412Z"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_341_200), UTC())); - _assertPred!"=="(SysTime.fromISOString("20101222T172201.23112-1:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_311_200), - new immutable SimpleTimeZone(dur!"minutes"(-60)))); - _assertPred!"=="(SysTime.fromISOString("20101222T172201.45-1"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), - new immutable SimpleTimeZone(dur!"minutes"(-60)))); - _assertPred!"=="(SysTime.fromISOString("20101222T172201.1-1:30"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_000_000), - new immutable SimpleTimeZone(dur!"minutes"(-90)))); - _assertPred!"=="(SysTime.fromISOString("20101222T172201.55-8:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(5_500_000), - new immutable SimpleTimeZone(dur!"minutes"(-480)))); - _assertPred!"=="(SysTime.fromISOString("20101222T172201.1234567+1:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_234_567), - new immutable SimpleTimeZone(dur!"minutes"(60)))); - _assertPred!"=="(SysTime.fromISOString("20101222T172201.0+1"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), - new immutable SimpleTimeZone(dur!"minutes"(60)))); - _assertPred!"=="(SysTime.fromISOString("20101222T172201.0000000+1:30"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), - new immutable SimpleTimeZone(dur!"minutes"(90)))); - _assertPred!"=="(SysTime.fromISOString("20101222T172201.45+8:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), - new immutable SimpleTimeZone(dur!"minutes"(480)))); - - //Verify Examples. - assert(SysTime.fromISOString("20100704T070612") == SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - assert(SysTime.fromISOString("19981225T021500.007") == SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(7))); - assert(SysTime.fromISOString("00000105T230959.00002") == SysTime(DateTime(0, 1, 5, 23, 9, 59), FracSec.from!"usecs"(20))); - assert(SysTime.fromISOString("-00040105T000002") == SysTime(DateTime(-4, 1, 5, 0, 0, 2))); - assert(SysTime.fromISOString(" 20100704T070612 ") == SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromISOString("20100704T070612Z") == SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); - assert(SysTime.fromISOString("20100704T070612-8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), new immutable SimpleTimeZone(dur!"hours"(-8)))); - assert(SysTime.fromISOString("20100704T070612+8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), new immutable SimpleTimeZone(dur!"hours"(8)))); - } + assert(SysTime.fromISOString("20100704T070612") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + assert(SysTime.fromISOString("19981225T021500.007") == + SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(7))); + assert(SysTime.fromISOString("00000105T230959.00002") == + SysTime(DateTime(0, 1, 5, 23, 9, 59), FracSec.from!"usecs"(20))); + assert(SysTime.fromISOString("-00040105T000002") == + SysTime(DateTime(-4, 1, 5, 0, 0, 2))); + assert(SysTime.fromISOString(" 20100704T070612 ") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromISOString("20100704T070612Z") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); + assert(SysTime.fromISOString("20100704T070612-8:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(dur!"hours"(-8)))); + assert(SysTime.fromISOString("20100704T070612+8:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(dur!"hours"(8)))); + } + + unittest + { + assertThrown!DateTimeException(SysTime.fromISOString("")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704000000")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704 000000")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704t000000")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.A")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.Z")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.00000000")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000.00000000")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000-")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000:")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000-:")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+:")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000-1:")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+1:")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+1:0")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000-24.00")); + assertThrown!DateTimeException(SysTime.fromISOString("20100704T000000+24.00")); + + assertThrown!DateTimeException(SysTime.fromISOString("2010-07-0400:00:00")); + assertThrown!DateTimeException(SysTime.fromISOString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOString("2010-07-04T00:00:00.")); + + assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-04 00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-04T00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOString("2010-Jul-04 00:00:00.")); + + assertThrown!DateTimeException(SysTime.fromISOString("2010-12-22T172201")); + assertThrown!DateTimeException(SysTime.fromISOString("2010-Dec-22 17:22:01")); + + assert(SysTime.fromISOString("20101222T172201") == SysTime(DateTime(2010, 12, 22, 17, 22, 01))); + assert(SysTime.fromISOString("19990706T123033") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assert(SysTime.fromISOString("-19990706T123033") == SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + assert(SysTime.fromISOString("+019990706T123033") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assert(SysTime.fromISOString("19990706T123033 ") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assert(SysTime.fromISOString(" 19990706T123033") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assert(SysTime.fromISOString(" 19990706T123033 ") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + + assert(SysTime.fromISOString("19070707T121212.0") == SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + assert(SysTime.fromISOString("19070707T121212.0000000") == SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + assert(SysTime.fromISOString("19070707T121212.0000001") == SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"hnsecs"(1))); + assert(SysTime.fromISOString("19070707T121212.000001") == SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); + assert(SysTime.fromISOString("19070707T121212.0000010") == SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); + assert(SysTime.fromISOString("19070707T121212.001") == SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); + assert(SysTime.fromISOString("19070707T121212.0010000") == SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); + + assert(SysTime.fromISOString("20101222T172201Z") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); + assert(SysTime.fromISOString("20101222T172201-1:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); + assert(SysTime.fromISOString("20101222T172201-1") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); + assert(SysTime.fromISOString("20101222T172201-1:30") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-90)))); + assert(SysTime.fromISOString("20101222T172201-8:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-480)))); + assert(SysTime.fromISOString("20101222T172201+1:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); + assert(SysTime.fromISOString("20101222T172201+1") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); + assert(SysTime.fromISOString("20101222T172201+1:30") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(90)))); + assert(SysTime.fromISOString("20101222T172201+8:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(480)))); + + assert(SysTime.fromISOString("20101103T065106.57159Z") == + SysTime(DateTime(2010, 11, 3, 6, 51, 6), FracSec.from!"hnsecs"(5715900), UTC())); + + assert(SysTime.fromISOString("20101222T172201.23412Z") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_341_200), UTC())); + assert(SysTime.fromISOString("20101222T172201.23112-1:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_311_200), + new immutable SimpleTimeZone(dur!"minutes"(-60)))); + assert(SysTime.fromISOString("20101222T172201.45-1") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), + new immutable SimpleTimeZone(dur!"minutes"(-60)))); + assert(SysTime.fromISOString("20101222T172201.1-1:30") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_000_000), + new immutable SimpleTimeZone(dur!"minutes"(-90)))); + assert(SysTime.fromISOString("20101222T172201.55-8:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(5_500_000), + new immutable SimpleTimeZone(dur!"minutes"(-480)))); + assert(SysTime.fromISOString("20101222T172201.1234567+1:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_234_567), + new immutable SimpleTimeZone(dur!"minutes"(60)))); + assert(SysTime.fromISOString("20101222T172201.0+1") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), + new immutable SimpleTimeZone(dur!"minutes"(60)))); + assert(SysTime.fromISOString("20101222T172201.0000000+1:30") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), + new immutable SimpleTimeZone(dur!"minutes"(90)))); + assert(SysTime.fromISOString("20101222T172201.45+8:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), + new immutable SimpleTimeZone(dur!"minutes"(480)))); } @@ -8445,31 +8039,8 @@ assert(SysTime.fromISOString("20100704T070612+8:00") == Throws: $(LREF DateTimeException) if the given string is not in the ISO format or if the resulting $(LREF SysTime) would not be valid. - - Examples: --------------------- -assert(SysTime.fromISOExtString("2010-07-04T07:06:12") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); -assert(SysTime.fromISOExtString("1998-12-25T02:15:00.007") == - SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(7))); -assert(SysTime.fromISOExtString("0000-01-05T23:09:59.00002") == - SysTime(DateTime(0, 1, 5, 23, 9, 59), FracSec.from!"usecs"(20))); -assert(SysTime.fromISOExtString("-0004-01-05T00:00:02") == - SysTime(DateTime(-4, 1, 5, 0, 0, 2))); -assert(SysTime.fromISOExtString(" 2010-07-04T07:06:12 ") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - -assert(SysTime.fromISOExtString("2010-07-04T07:06:12Z") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); -assert(SysTime.fromISOExtString("2010-07-04T07:06:12-8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new SimpleTimeZone(dur!"hours"(-8)))); -assert(SysTime.fromISOExtString("2010-07-04T07:06:12+8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new SimpleTimeZone(dur!"hours"(8)))); --------------------- +/ - static SysTime fromISOExtString(S)(in S isoExtString, immutable TimeZone tz = null) + static SysTime fromISOExtString(S)(in S isoExtString, immutable TimeZone tz = null) @safe if(isSomeString!(S)) { auto dstr = to!dstring(strip(isoExtString)); @@ -8525,125 +8096,131 @@ assert(SysTime.fromISOExtString("2010-07-04T07:06:12+8:00") == throw new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString)); } + /// unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException(SysTime.fromISOExtString("")); - assertThrown!DateTimeException(SysTime.fromISOExtString("20100704000000")); - assertThrown!DateTimeException(SysTime.fromISOExtString("20100704 000000")); - assertThrown!DateTimeException(SysTime.fromISOExtString("20100704t000000")); - assertThrown!DateTimeException(SysTime.fromISOExtString("20100704T000000.")); - assertThrown!DateTimeException(SysTime.fromISOExtString("20100704T000000.0")); - - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07:0400:00:00")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00.A")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00.Z")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00.00000000")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00.00000000")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00+")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00-")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00:")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00-:")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00+:")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00-1:")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00+1:")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00+1:0")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00-24.00")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00+24.00")); - - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-Jul-04 00:00:00.0")); - - assertThrown!DateTimeException(SysTime.fromISOExtString("20101222T172201")); - assertThrown!DateTimeException(SysTime.fromISOExtString("2010-Dec-22 17:22:01")); - - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01"), SysTime(DateTime(2010, 12, 22, 17, 22, 01))); - _assertPred!"=="(SysTime.fromISOExtString("1999-07-06T12:30:33"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - _assertPred!"=="(SysTime.fromISOExtString("-1999-07-06T12:30:33"), SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - _assertPred!"=="(SysTime.fromISOExtString("+01999-07-06T12:30:33"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - _assertPred!"=="(SysTime.fromISOExtString("1999-07-06T12:30:33 "), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - _assertPred!"=="(SysTime.fromISOExtString(" 1999-07-06T12:30:33"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - _assertPred!"=="(SysTime.fromISOExtString(" 1999-07-06T12:30:33 "), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - - _assertPred!"=="(SysTime.fromISOExtString("1907-07-07T12:12:12.0"), SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - _assertPred!"=="(SysTime.fromISOExtString("1907-07-07T12:12:12.0000000"), SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - _assertPred!"=="(SysTime.fromISOExtString("1907-07-07T12:12:12.0000001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"hnsecs"(1))); - _assertPred!"=="(SysTime.fromISOExtString("1907-07-07T12:12:12.000001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); - _assertPred!"=="(SysTime.fromISOExtString("1907-07-07T12:12:12.0000010"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); - _assertPred!"=="(SysTime.fromISOExtString("1907-07-07T12:12:12.001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); - _assertPred!"=="(SysTime.fromISOExtString("1907-07-07T12:12:12.0010000"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); - - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01Z"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01-1:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01-1"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01-1:30"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-90)))); - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01-8:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-480)))); - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01+1:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01+1"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01+1:30"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(90)))); - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01+8:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(480)))); - - _assertPred!"=="(SysTime.fromISOExtString("2010-11-03T06:51:06.57159Z"), - SysTime(DateTime(2010, 11, 3, 6, 51, 6), FracSec.from!"hnsecs"(5715900), UTC())); - - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01.23412Z"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_341_200), UTC())); - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01.23112-1:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_311_200), - new immutable SimpleTimeZone(dur!"minutes"(-60)))); - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01.45-1"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), - new immutable SimpleTimeZone(dur!"minutes"(-60)))); - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01.1-1:30"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_000_000), - new immutable SimpleTimeZone(dur!"minutes"(-90)))); - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01.55-8:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(5_500_000), - new immutable SimpleTimeZone(dur!"minutes"(-480)))); - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01.1234567+1:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_234_567), - new immutable SimpleTimeZone(dur!"minutes"(60)))); - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01.0+1"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), - new immutable SimpleTimeZone(dur!"minutes"(60)))); - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01.0000000+1:30"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), - new immutable SimpleTimeZone(dur!"minutes"(90)))); - _assertPred!"=="(SysTime.fromISOExtString("2010-12-22T17:22:01.45+8:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), - new immutable SimpleTimeZone(dur!"minutes"(480)))); - - //Verify Examples. - assert(SysTime.fromISOExtString("2010-07-04T07:06:12") == SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - assert(SysTime.fromISOExtString("1998-12-25T02:15:00.007") == - SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(7))); - assert(SysTime.fromISOExtString("0000-01-05T23:09:59.00002") == - SysTime(DateTime(0, 1, 5, 23, 9, 59), FracSec.from!"usecs"(20))); - assert(SysTime.fromISOExtString("-0004-01-05T00:00:02") == SysTime(DateTime(-4, 1, 5, 0, 0, 2))); - assert(SysTime.fromISOExtString(" 2010-07-04T07:06:12 ") == SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromISOExtString("2010-07-04T07:06:12Z") == SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); - assert(SysTime.fromISOExtString("2010-07-04T07:06:12-8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), new immutable SimpleTimeZone(dur!"hours"(-8)))); - assert(SysTime.fromISOExtString("2010-07-04T07:06:12+8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), new immutable SimpleTimeZone(dur!"hours"(8)))); - } + assert(SysTime.fromISOExtString("2010-07-04T07:06:12") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + assert(SysTime.fromISOExtString("1998-12-25T02:15:00.007") == + SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(7))); + assert(SysTime.fromISOExtString("0000-01-05T23:09:59.00002") == + SysTime(DateTime(0, 1, 5, 23, 9, 59), FracSec.from!"usecs"(20))); + assert(SysTime.fromISOExtString("-0004-01-05T00:00:02") == + SysTime(DateTime(-4, 1, 5, 0, 0, 2))); + assert(SysTime.fromISOExtString(" 2010-07-04T07:06:12 ") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromISOExtString("2010-07-04T07:06:12Z") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); + assert(SysTime.fromISOExtString("2010-07-04T07:06:12-8:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(dur!"hours"(-8)))); + assert(SysTime.fromISOExtString("2010-07-04T07:06:12+8:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(dur!"hours"(8)))); + } + + unittest + { + assertThrown!DateTimeException(SysTime.fromISOExtString("")); + assertThrown!DateTimeException(SysTime.fromISOExtString("20100704000000")); + assertThrown!DateTimeException(SysTime.fromISOExtString("20100704 000000")); + assertThrown!DateTimeException(SysTime.fromISOExtString("20100704t000000")); + assertThrown!DateTimeException(SysTime.fromISOExtString("20100704T000000.")); + assertThrown!DateTimeException(SysTime.fromISOExtString("20100704T000000.0")); + + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07:0400:00:00")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00.A")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00.Z")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00.00000000")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00.00000000")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00+")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00-")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00:")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00-:")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00+:")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00-1:")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00+1:")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00+1:0")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00-24.00")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-07-04T00:00:00+24.00")); + + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-Jul-04 00:00:00.0")); + + assertThrown!DateTimeException(SysTime.fromISOExtString("20101222T172201")); + assertThrown!DateTimeException(SysTime.fromISOExtString("2010-Dec-22 17:22:01")); + + assert(SysTime.fromISOExtString("2010-12-22T17:22:01") == SysTime(DateTime(2010, 12, 22, 17, 22, 01))); + assert(SysTime.fromISOExtString("1999-07-06T12:30:33") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assert(SysTime.fromISOExtString("-1999-07-06T12:30:33") == SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + assert(SysTime.fromISOExtString("+01999-07-06T12:30:33") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assert(SysTime.fromISOExtString("1999-07-06T12:30:33 ") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assert(SysTime.fromISOExtString(" 1999-07-06T12:30:33") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assert(SysTime.fromISOExtString(" 1999-07-06T12:30:33 ") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + + assert(SysTime.fromISOExtString("1907-07-07T12:12:12.0") == SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + assert(SysTime.fromISOExtString("1907-07-07T12:12:12.0000000") == SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + assert(SysTime.fromISOExtString("1907-07-07T12:12:12.0000001") == SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"hnsecs"(1))); + assert(SysTime.fromISOExtString("1907-07-07T12:12:12.000001") == SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); + assert(SysTime.fromISOExtString("1907-07-07T12:12:12.0000010") == SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); + assert(SysTime.fromISOExtString("1907-07-07T12:12:12.001") == SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); + assert(SysTime.fromISOExtString("1907-07-07T12:12:12.0010000") == SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); + + assert(SysTime.fromISOExtString("2010-12-22T17:22:01Z") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); + assert(SysTime.fromISOExtString("2010-12-22T17:22:01-1:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); + assert(SysTime.fromISOExtString("2010-12-22T17:22:01-1") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); + assert(SysTime.fromISOExtString("2010-12-22T17:22:01-1:30") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-90)))); + assert(SysTime.fromISOExtString("2010-12-22T17:22:01-8:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-480)))); + assert(SysTime.fromISOExtString("2010-12-22T17:22:01+1:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); + assert(SysTime.fromISOExtString("2010-12-22T17:22:01+1") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); + assert(SysTime.fromISOExtString("2010-12-22T17:22:01+1:30") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(90)))); + assert(SysTime.fromISOExtString("2010-12-22T17:22:01+8:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(480)))); + + assert(SysTime.fromISOExtString("2010-11-03T06:51:06.57159Z") == + SysTime(DateTime(2010, 11, 3, 6, 51, 6), FracSec.from!"hnsecs"(5715900), UTC())); + + assert(SysTime.fromISOExtString("2010-12-22T17:22:01.23412Z") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_341_200), UTC())); + assert(SysTime.fromISOExtString("2010-12-22T17:22:01.23112-1:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_311_200), + new immutable SimpleTimeZone(dur!"minutes"(-60)))); + assert(SysTime.fromISOExtString("2010-12-22T17:22:01.45-1") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), + new immutable SimpleTimeZone(dur!"minutes"(-60)))); + assert(SysTime.fromISOExtString("2010-12-22T17:22:01.1-1:30") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_000_000), + new immutable SimpleTimeZone(dur!"minutes"(-90)))); + assert(SysTime.fromISOExtString("2010-12-22T17:22:01.55-8:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(5_500_000), + new immutable SimpleTimeZone(dur!"minutes"(-480)))); + assert(SysTime.fromISOExtString("2010-12-22T17:22:01.1234567+1:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_234_567), + new immutable SimpleTimeZone(dur!"minutes"(60)))); + assert(SysTime.fromISOExtString("2010-12-22T17:22:01.0+1") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), + new immutable SimpleTimeZone(dur!"minutes"(60)))); + assert(SysTime.fromISOExtString("2010-12-22T17:22:01.0000000+1:30") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), + new immutable SimpleTimeZone(dur!"minutes"(90)))); + assert(SysTime.fromISOExtString("2010-12-22T17:22:01.45+8:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), + new immutable SimpleTimeZone(dur!"minutes"(480)))); } @@ -8678,31 +8255,8 @@ assert(SysTime.fromISOExtString("2010-07-04T07:06:12+8:00") == Throws: $(LREF DateTimeException) if the given string is not in the ISO format or if the resulting $(LREF SysTime) would not be valid. - - Examples: --------------------- -assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); -assert(SysTime.fromSimpleString("1998-Dec-25 02:15:00.007") == - SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(7))); -assert(SysTime.fromSimpleString("0000-Jan-05 23:09:59.00002") == - SysTime(DateTime(0, 1, 5, 23, 9, 59), FracSec.from!"usecs"(20))); -assert(SysTime.fromSimpleString("-0004-Jan-05 00:00:02") == - SysTime(DateTime(-4, 1, 5, 0, 0, 2))); -assert(SysTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - -assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12Z") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); -assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12-8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new SimpleTimeZone(dur!"hours"(-8)))); -assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12+8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), - new SimpleTimeZone(dur!"hours"(8)))); --------------------- +/ - static SysTime fromSimpleString(S)(in S simpleString, immutable TimeZone tz = null) + static SysTime fromSimpleString(S)(in S simpleString, immutable TimeZone tz = null) @safe if(isSomeString!(S)) { auto dstr = to!dstring(strip(simpleString)); @@ -8758,132 +8312,132 @@ assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12+8:00") == throw new DateTimeException(format("Invalid Simple String: %s", simpleString)); } + /// + unittest + { + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + assert(SysTime.fromSimpleString("1998-Dec-25 02:15:00.007") == + SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(7))); + assert(SysTime.fromSimpleString("0000-Jan-05 23:09:59.00002") == + SysTime(DateTime(0, 1, 5, 23, 9, 59), FracSec.from!"usecs"(20))); + assert(SysTime.fromSimpleString("-0004-Jan-05 00:00:02") == + SysTime(DateTime(-4, 1, 5, 0, 0, 2))); + assert(SysTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12))); + + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12Z") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12-8:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(dur!"hours"(-8)))); + assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12+8:00") == + SysTime(DateTime(2010, 7, 4, 7, 6, 12), + new immutable SimpleTimeZone(dur!"hours"(8)))); + } + unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException(SysTime.fromSimpleString("")); - assertThrown!DateTimeException(SysTime.fromSimpleString("20100704000000")); - assertThrown!DateTimeException(SysTime.fromSimpleString("20100704 000000")); - assertThrown!DateTimeException(SysTime.fromSimpleString("20100704t000000")); - assertThrown!DateTimeException(SysTime.fromSimpleString("20100704T000000.")); - assertThrown!DateTimeException(SysTime.fromSimpleString("20100704T000000.0")); - - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-0400:00:00")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-04T00:00:00.0")); - - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04T00:00:00")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.A")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.Z")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.00000000")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.00000000")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00-")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00:")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00-:")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+:")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00-1:")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+1:")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+1:0")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00-24.00")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+24.00")); - - assertThrown!DateTimeException(SysTime.fromSimpleString("20101222T172201")); - assertThrown!DateTimeException(SysTime.fromSimpleString("2010-12-22T172201")); - - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01"), SysTime(DateTime(2010, 12, 22, 17, 22, 01))); - _assertPred!"=="(SysTime.fromSimpleString("1999-Jul-06 12:30:33"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - _assertPred!"=="(SysTime.fromSimpleString("-1999-Jul-06 12:30:33"), SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); - _assertPred!"=="(SysTime.fromSimpleString("+01999-Jul-06 12:30:33"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - _assertPred!"=="(SysTime.fromSimpleString("1999-Jul-06 12:30:33 "), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - _assertPred!"=="(SysTime.fromSimpleString(" 1999-Jul-06 12:30:33"), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - _assertPred!"=="(SysTime.fromSimpleString(" 1999-Jul-06 12:30:33 "), SysTime(DateTime(1999, 7, 6, 12, 30, 33))); - - _assertPred!"=="(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0"), SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - _assertPred!"=="(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0000000"), SysTime(DateTime(1907, 07, 07, 12, 12, 12))); - _assertPred!"=="(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0000001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"hnsecs"(1))); - _assertPred!"=="(SysTime.fromSimpleString("1907-Jul-07 12:12:12.000001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); - _assertPred!"=="(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0000010"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); - _assertPred!"=="(SysTime.fromSimpleString("1907-Jul-07 12:12:12.001"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); - _assertPred!"=="(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0010000"), SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); - - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01Z"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01-1:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01-1"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01-1:30"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-90)))); - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01-8:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-480)))); - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01+1:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01+1"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01+1:30"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(90)))); - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01+8:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(480)))); - - _assertPred!"=="(SysTime.fromSimpleString("2010-Nov-03 06:51:06.57159Z"), - SysTime(DateTime(2010, 11, 3, 6, 51, 6), FracSec.from!"hnsecs"(5715900), UTC())); - - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.23412Z"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_341_200), UTC())); - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.23112-1:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_311_200), - new immutable SimpleTimeZone(dur!"minutes"(-60)))); - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.45-1"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), - new immutable SimpleTimeZone(dur!"minutes"(-60)))); - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.1-1:30"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_000_000), - new immutable SimpleTimeZone(dur!"minutes"(-90)))); - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.55-8:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(5_500_000), - new immutable SimpleTimeZone(dur!"minutes"(-480)))); - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.1234567+1:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_234_567), - new immutable SimpleTimeZone(dur!"minutes"(60)))); - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.0+1"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), - new immutable SimpleTimeZone(dur!"minutes"(60)))); - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.0000000+1:30"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), - new immutable SimpleTimeZone(dur!"minutes"(90)))); - _assertPred!"=="(SysTime.fromSimpleString("2010-Dec-22 17:22:01.45+8:00"), - SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), - new immutable SimpleTimeZone(dur!"minutes"(480)))); - - //Verify Examples. - assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12") == SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - assert(SysTime.fromSimpleString("1998-Dec-25 02:15:00.007") == SysTime(DateTime(1998, 12, 25, 2, 15, 0), FracSec.from!"msecs"(7))); - assert(SysTime.fromSimpleString("0000-Jan-05 23:09:59.00002") == SysTime(DateTime(0, 1, 5, 23, 9, 59), FracSec.from!"usecs"(20))); - assert(SysTime.fromSimpleString("-0004-Jan-05 00:00:02") == SysTime(DateTime(-4, 1, 5, 0, 0, 2))); - assert(SysTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == SysTime(DateTime(2010, 7, 4, 7, 6, 12))); - - assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12Z") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC())); - assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12-8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), new immutable SimpleTimeZone(dur!"hours"(-8)))); - assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12+8:00") == - SysTime(DateTime(2010, 7, 4, 7, 6, 12), new immutable SimpleTimeZone(dur!"hours"(8)))); - } - } - - - //TODO Add function which takes a user-specified time format and produces a SysTime - - //TODO Add function which takes pretty much any time-string and produces a SysTime. - // Obviously, it will be less efficient, and it probably won't manage _every_ - // possible date format, but a smart conversion function would be nice. + assertThrown!DateTimeException(SysTime.fromSimpleString("")); + assertThrown!DateTimeException(SysTime.fromSimpleString("20100704000000")); + assertThrown!DateTimeException(SysTime.fromSimpleString("20100704 000000")); + assertThrown!DateTimeException(SysTime.fromSimpleString("20100704t000000")); + assertThrown!DateTimeException(SysTime.fromSimpleString("20100704T000000.")); + assertThrown!DateTimeException(SysTime.fromSimpleString("20100704T000000.0")); + + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-0400:00:00")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-07-04T00:00:00.0")); + + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04T00:00:00")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.A")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.Z")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.00000000")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00.00000000")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00-")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00:")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00-:")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+:")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00-1:")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+1:")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+1:0")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00-24.00")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-Jul-04 00:00:00+24.00")); + + assertThrown!DateTimeException(SysTime.fromSimpleString("20101222T172201")); + assertThrown!DateTimeException(SysTime.fromSimpleString("2010-12-22T172201")); + + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01") == SysTime(DateTime(2010, 12, 22, 17, 22, 01))); + assert(SysTime.fromSimpleString("1999-Jul-06 12:30:33") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assert(SysTime.fromSimpleString("-1999-Jul-06 12:30:33") == SysTime(DateTime(-1999, 7, 6, 12, 30, 33))); + assert(SysTime.fromSimpleString("+01999-Jul-06 12:30:33") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assert(SysTime.fromSimpleString("1999-Jul-06 12:30:33 ") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assert(SysTime.fromSimpleString(" 1999-Jul-06 12:30:33") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + assert(SysTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") == SysTime(DateTime(1999, 7, 6, 12, 30, 33))); + + assert(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0") == SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + assert(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0000000") == SysTime(DateTime(1907, 07, 07, 12, 12, 12))); + assert(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0000001") == SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"hnsecs"(1))); + assert(SysTime.fromSimpleString("1907-Jul-07 12:12:12.000001") == SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); + assert(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0000010") == SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"usecs"(1))); + assert(SysTime.fromSimpleString("1907-Jul-07 12:12:12.001") == SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); + assert(SysTime.fromSimpleString("1907-Jul-07 12:12:12.0010000") == SysTime(DateTime(1907, 07, 07, 12, 12, 12), FracSec.from!"msecs"(1))); + + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01Z") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), UTC())); + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01-1:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01-1") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-60)))); + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01-1:30") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-90)))); + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01-8:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(-480)))); + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01+1:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01+1") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(60)))); + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01+1:30") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(90)))); + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01+8:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), new immutable SimpleTimeZone(dur!"minutes"(480)))); + + assert(SysTime.fromSimpleString("2010-Nov-03 06:51:06.57159Z") == + SysTime(DateTime(2010, 11, 3, 6, 51, 6), FracSec.from!"hnsecs"(5715900), UTC())); + + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.23412Z") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_341_200), UTC())); + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.23112-1:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(2_311_200), + new immutable SimpleTimeZone(dur!"minutes"(-60)))); + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.45-1") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), + new immutable SimpleTimeZone(dur!"minutes"(-60)))); + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.1-1:30") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_000_000), + new immutable SimpleTimeZone(dur!"minutes"(-90)))); + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.55-8:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(5_500_000), + new immutable SimpleTimeZone(dur!"minutes"(-480)))); + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.1234567+1:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(1_234_567), + new immutable SimpleTimeZone(dur!"minutes"(60)))); + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.0+1") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), + new immutable SimpleTimeZone(dur!"minutes"(60)))); + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.0000000+1:30") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(0), + new immutable SimpleTimeZone(dur!"minutes"(90)))); + assert(SysTime.fromSimpleString("2010-Dec-22 17:22:01.45+8:00") == + SysTime(DateTime(2010, 12, 22, 17, 22, 01), FracSec.from!"hnsecs"(4_500_000), + new immutable SimpleTimeZone(dur!"minutes"(480)))); + } /++ @@ -8892,18 +8446,15 @@ assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12+8:00") == The $(LREF SysTime) which is returned is in UTC. +/ - @property static SysTime min() pure nothrow + @property static SysTime min() @safe pure nothrow { return SysTime(long.min, UTC()); } unittest { - version(testStdDateTime) - { - assert(SysTime.min.year < 0); - assert(SysTime.min < SysTime.max); - } + assert(SysTime.min.year < 0); + assert(SysTime.min < SysTime.max); } @@ -8913,18 +8464,15 @@ assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12+8:00") == The $(LREF SysTime) which is returned is in UTC. +/ - @property static SysTime max() pure nothrow + @property static SysTime max() @safe pure nothrow { return SysTime(long.max, UTC()); } unittest { - version(testStdDateTime) - { - assert(SysTime.max.year > 0); - assert(SysTime.max > SysTime.min); - } + assert(SysTime.max.year > 0); + assert(SysTime.max > SysTime.min); } @@ -8933,7 +8481,7 @@ private: /+ Returns $(D stdTime) converted to $(LREF SysTime)'s time zone. +/ - @property long adjTime() const nothrow + @property long adjTime() @safe const nothrow { return _timezone.utcToTZ(_stdTime); } @@ -8942,7 +8490,7 @@ private: /+ Converts the given hnsecs from $(LREF SysTime)'s time zone to std time. +/ - @property void adjTime(long adjTime) nothrow + @property void adjTime(long adjTime) @safe nothrow { _stdTime = _timezone.tzToUTC(adjTime); } @@ -8995,7 +8543,7 @@ public: month = Month of the year. day = Day of the month. +/ - this(int year, int month, int day) pure + this(int year, int month, int day) @safe pure { enforceValid!"months"(cast(Month)month); enforceValid!"days"(year, cast(Month)month, day); @@ -9007,64 +8555,61 @@ public: unittest { - version(testStdDateTime) + assert(Date(1, 1, 1) == Date.init); + + static void testDate(in Date date, int year, int month, int day) { - _assertPred!"=="(Date(1, 1, 1), Date.init); + assert(date._year == year); + assert(date._month == month); + assert(date._day == day); + } - static void testDate(in Date date, int year, int month, int day, size_t line = __LINE__) - { - _assertPred!"=="(date._year, year, "", __FILE__, line); - _assertPred!"=="(date._month, month, "", __FILE__, line); - _assertPred!"=="(date._day, day, "", __FILE__, line); - } + testDate(Date(1999, 1 , 1), 1999, Month.jan, 1); + testDate(Date(1999, 7 , 1), 1999, Month.jul, 1); + testDate(Date(1999, 7 , 6), 1999, Month.jul, 6); - testDate(Date(1999, 1 , 1), 1999, Month.jan, 1); - testDate(Date(1999, 7 , 1), 1999, Month.jul, 1); - testDate(Date(1999, 7 , 6), 1999, Month.jul, 6); - - //Test A.D. - assertThrown!DateTimeException(Date(1, 0, 1)); - assertThrown!DateTimeException(Date(1, 1, 0)); - assertThrown!DateTimeException(Date(1999, 13, 1)); - assertThrown!DateTimeException(Date(1999, 1, 32)); - assertThrown!DateTimeException(Date(1999, 2, 29)); - assertThrown!DateTimeException(Date(2000, 2, 30)); - assertThrown!DateTimeException(Date(1999, 3, 32)); - assertThrown!DateTimeException(Date(1999, 4, 31)); - assertThrown!DateTimeException(Date(1999, 5, 32)); - assertThrown!DateTimeException(Date(1999, 6, 31)); - assertThrown!DateTimeException(Date(1999, 7, 32)); - assertThrown!DateTimeException(Date(1999, 8, 32)); - assertThrown!DateTimeException(Date(1999, 9, 31)); - assertThrown!DateTimeException(Date(1999, 10, 32)); - assertThrown!DateTimeException(Date(1999, 11, 31)); - assertThrown!DateTimeException(Date(1999, 12, 32)); - - assertNotThrown!DateTimeException(Date(1999, 1, 31)); - assertNotThrown!DateTimeException(Date(1999, 2, 28)); - assertNotThrown!DateTimeException(Date(2000, 2, 29)); - assertNotThrown!DateTimeException(Date(1999, 3, 31)); - assertNotThrown!DateTimeException(Date(1999, 4, 30)); - assertNotThrown!DateTimeException(Date(1999, 5, 31)); - assertNotThrown!DateTimeException(Date(1999, 6, 30)); - assertNotThrown!DateTimeException(Date(1999, 7, 31)); - assertNotThrown!DateTimeException(Date(1999, 8, 31)); - assertNotThrown!DateTimeException(Date(1999, 9, 30)); - assertNotThrown!DateTimeException(Date(1999, 10, 31)); - assertNotThrown!DateTimeException(Date(1999, 11, 30)); - assertNotThrown!DateTimeException(Date(1999, 12, 31)); - - //Test B.C. - assertNotThrown!DateTimeException(Date(0, 1, 1)); - assertNotThrown!DateTimeException(Date(-1, 1, 1)); - assertNotThrown!DateTimeException(Date(-1, 12, 31)); - assertNotThrown!DateTimeException(Date(-1, 2, 28)); - assertNotThrown!DateTimeException(Date(-4, 2, 29)); - - assertThrown!DateTimeException(Date(-1, 2, 29)); - assertThrown!DateTimeException(Date(-2, 2, 29)); - assertThrown!DateTimeException(Date(-3, 2, 29)); - } + //Test A.D. + assertThrown!DateTimeException(Date(1, 0, 1)); + assertThrown!DateTimeException(Date(1, 1, 0)); + assertThrown!DateTimeException(Date(1999, 13, 1)); + assertThrown!DateTimeException(Date(1999, 1, 32)); + assertThrown!DateTimeException(Date(1999, 2, 29)); + assertThrown!DateTimeException(Date(2000, 2, 30)); + assertThrown!DateTimeException(Date(1999, 3, 32)); + assertThrown!DateTimeException(Date(1999, 4, 31)); + assertThrown!DateTimeException(Date(1999, 5, 32)); + assertThrown!DateTimeException(Date(1999, 6, 31)); + assertThrown!DateTimeException(Date(1999, 7, 32)); + assertThrown!DateTimeException(Date(1999, 8, 32)); + assertThrown!DateTimeException(Date(1999, 9, 31)); + assertThrown!DateTimeException(Date(1999, 10, 32)); + assertThrown!DateTimeException(Date(1999, 11, 31)); + assertThrown!DateTimeException(Date(1999, 12, 32)); + + assertNotThrown!DateTimeException(Date(1999, 1, 31)); + assertNotThrown!DateTimeException(Date(1999, 2, 28)); + assertNotThrown!DateTimeException(Date(2000, 2, 29)); + assertNotThrown!DateTimeException(Date(1999, 3, 31)); + assertNotThrown!DateTimeException(Date(1999, 4, 30)); + assertNotThrown!DateTimeException(Date(1999, 5, 31)); + assertNotThrown!DateTimeException(Date(1999, 6, 30)); + assertNotThrown!DateTimeException(Date(1999, 7, 31)); + assertNotThrown!DateTimeException(Date(1999, 8, 31)); + assertNotThrown!DateTimeException(Date(1999, 9, 30)); + assertNotThrown!DateTimeException(Date(1999, 10, 31)); + assertNotThrown!DateTimeException(Date(1999, 11, 30)); + assertNotThrown!DateTimeException(Date(1999, 12, 31)); + + //Test B.C. + assertNotThrown!DateTimeException(Date(0, 1, 1)); + assertNotThrown!DateTimeException(Date(-1, 1, 1)); + assertNotThrown!DateTimeException(Date(-1, 12, 31)); + assertNotThrown!DateTimeException(Date(-1, 2, 28)); + assertNotThrown!DateTimeException(Date(-4, 2, 29)); + + assertThrown!DateTimeException(Date(-1, 2, 29)); + assertThrown!DateTimeException(Date(-2, 2, 29)); + assertThrown!DateTimeException(Date(-3, 2, 29)); } @@ -9073,7 +8618,7 @@ public: day = The Xth day of the Gregorian Calendar that the constructed $(LREF Date) will be for. +/ - this(int day) pure nothrow + this(int day) @safe pure nothrow { if(day > 0) { @@ -9196,11 +8741,11 @@ public: } } - version(testStdDateTime) unittest + unittest { //Test A.D. foreach(gd; chain(testGregDaysBC, testGregDaysAD)) - _assertPred!"=="(Date(gd.day), gd.date); + assert(Date(gd.day) == gd.date); } @@ -9214,7 +8759,7 @@ public: $(TR $(TD this > rhs) $(TD > 0)) ) +/ - int opCmp(in Date rhs) const pure nothrow + int opCmp(in Date rhs) @safe const pure nothrow { if(_year < rhs._year) return -1; @@ -9236,125 +8781,115 @@ public: unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!("opCmp", "==")(Date(1, 1, 1), Date.init); + //Test A.D. + assert(Date(1, 1, 1).opCmp(Date.init) == 0); - _assertPred!("opCmp", "==")(Date(1999, 1, 1), Date(1999, 1, 1)); - _assertPred!("opCmp", "==")(Date(1, 7, 1), Date(1, 7, 1)); - _assertPred!("opCmp", "==")(Date(1, 1, 6), Date(1, 1, 6)); + assert(Date(1999, 1, 1).opCmp(Date(1999, 1, 1)) == 0); + assert(Date(1, 7, 1).opCmp(Date(1, 7, 1)) == 0); + assert(Date(1, 1, 6).opCmp(Date(1, 1, 6)) == 0); - _assertPred!("opCmp", "==")(Date(1999, 7, 1), Date(1999, 7, 1)); - _assertPred!("opCmp", "==")(Date(1999, 7, 6), Date(1999, 7, 6)); + assert(Date(1999, 7, 1).opCmp(Date(1999, 7, 1)) == 0); + assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 6)) == 0); - _assertPred!("opCmp", "==")(Date(1, 7, 6), Date(1, 7, 6)); + assert(Date(1, 7, 6).opCmp(Date(1, 7, 6)) == 0); - _assertPred!("opCmp", "<")(Date(1999, 7, 6), Date(2000, 7, 6)); - _assertPred!("opCmp", ">")(Date(2000, 7, 6), Date(1999, 7, 6)); - _assertPred!("opCmp", "<")(Date(1999, 7, 6), Date(1999, 8, 6)); - _assertPred!("opCmp", ">")(Date(1999, 8, 6), Date(1999, 7, 6)); - _assertPred!("opCmp", "<")(Date(1999, 7, 6), Date(1999, 7, 7)); - _assertPred!("opCmp", ">")(Date(1999, 7, 7), Date(1999, 7, 6)); + assert(Date(1999, 7, 6).opCmp(Date(2000, 7, 6)) < 0); + assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 6)) > 0); + assert(Date(1999, 7, 6).opCmp(Date(1999, 8, 6)) < 0); + assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 6)) > 0); + assert(Date(1999, 7, 6).opCmp(Date(1999, 7, 7)) < 0); + assert(Date(1999, 7, 7).opCmp(Date(1999, 7, 6)) > 0); - _assertPred!("opCmp", "<")(Date(1999, 8, 7), Date(2000, 7, 6)); - _assertPred!("opCmp", ">")(Date(2000, 8, 6), Date(1999, 7, 7)); - _assertPred!("opCmp", "<")(Date(1999, 7, 7), Date(2000, 7, 6)); - _assertPred!("opCmp", ">")(Date(2000, 7, 6), Date(1999, 7, 7)); - _assertPred!("opCmp", "<")(Date(1999, 7, 7), Date(1999, 8, 6)); - _assertPred!("opCmp", ">")(Date(1999, 8, 6), Date(1999, 7, 7)); + assert(Date(1999, 8, 7).opCmp(Date(2000, 7, 6)) < 0); + assert(Date(2000, 8, 6).opCmp(Date(1999, 7, 7)) > 0); + assert(Date(1999, 7, 7).opCmp(Date(2000, 7, 6)) < 0); + assert(Date(2000, 7, 6).opCmp(Date(1999, 7, 7)) > 0); + assert(Date(1999, 7, 7).opCmp(Date(1999, 8, 6)) < 0); + assert(Date(1999, 8, 6).opCmp(Date(1999, 7, 7)) > 0); - //Test B.C. - _assertPred!("opCmp", "==")(Date(0, 1, 1), Date(0, 1, 1)); - _assertPred!("opCmp", "==")(Date(-1, 1, 1), Date(-1, 1, 1)); - _assertPred!("opCmp", "==")(Date(-1, 7, 1), Date(-1, 7, 1)); - _assertPred!("opCmp", "==")(Date(-1, 1, 6), Date(-1, 1, 6)); + //Test B.C. + assert(Date(0, 1, 1).opCmp(Date(0, 1, 1)) == 0); + assert(Date(-1, 1, 1).opCmp(Date(-1, 1, 1)) == 0); + assert(Date(-1, 7, 1).opCmp(Date(-1, 7, 1)) == 0); + assert(Date(-1, 1, 6).opCmp(Date(-1, 1, 6)) == 0); - _assertPred!("opCmp", "==")(Date(-1999, 7, 1), Date(-1999, 7, 1)); - _assertPred!("opCmp", "==")(Date(-1999, 7, 6), Date(-1999, 7, 6)); + assert(Date(-1999, 7, 1).opCmp(Date(-1999, 7, 1)) == 0); + assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 6)) == 0); - _assertPred!("opCmp", "==")(Date(-1, 7, 6), Date(-1, 7, 6)); + assert(Date(-1, 7, 6).opCmp(Date(-1, 7, 6)) == 0); - _assertPred!("opCmp", "<")(Date(-2000, 7, 6), Date(-1999, 7, 6)); - _assertPred!("opCmp", ">")(Date(-1999, 7, 6), Date(-2000, 7, 6)); - _assertPred!("opCmp", "<")(Date(-1999, 7, 6), Date(-1999, 8, 6)); - _assertPred!("opCmp", ">")(Date(-1999, 8, 6), Date(-1999, 7, 6)); - _assertPred!("opCmp", "<")(Date(-1999, 7, 6), Date(-1999, 7, 7)); - _assertPred!("opCmp", ">")(Date(-1999, 7, 7), Date(-1999, 7, 6)); + assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 6)) < 0); + assert(Date(-1999, 7, 6).opCmp(Date(-2000, 7, 6)) > 0); + assert(Date(-1999, 7, 6).opCmp(Date(-1999, 8, 6)) < 0); + assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 6)) > 0); + assert(Date(-1999, 7, 6).opCmp(Date(-1999, 7, 7)) < 0); + assert(Date(-1999, 7, 7).opCmp(Date(-1999, 7, 6)) > 0); - _assertPred!("opCmp", "<")(Date(-2000, 8, 6), Date(-1999, 7, 7)); - _assertPred!("opCmp", ">")(Date(-1999, 8, 7), Date(-2000, 7, 6)); - _assertPred!("opCmp", "<")(Date(-2000, 7, 6), Date(-1999, 7, 7)); - _assertPred!("opCmp", ">")(Date(-1999, 7, 7), Date(-2000, 7, 6)); - _assertPred!("opCmp", "<")(Date(-1999, 7, 7), Date(-1999, 8, 6)); - _assertPred!("opCmp", ">")(Date(-1999, 8, 6), Date(-1999, 7, 7)); + assert(Date(-2000, 8, 6).opCmp(Date(-1999, 7, 7)) < 0); + assert(Date(-1999, 8, 7).opCmp(Date(-2000, 7, 6)) > 0); + assert(Date(-2000, 7, 6).opCmp(Date(-1999, 7, 7)) < 0); + assert(Date(-1999, 7, 7).opCmp(Date(-2000, 7, 6)) > 0); + assert(Date(-1999, 7, 7).opCmp(Date(-1999, 8, 6)) < 0); + assert(Date(-1999, 8, 6).opCmp(Date(-1999, 7, 7)) > 0); - //Test Both - _assertPred!("opCmp", "<")(Date(-1999, 7, 6), Date(1999, 7, 6)); - _assertPred!("opCmp", ">")(Date(1999, 7, 6), Date(-1999, 7, 6)); + //Test Both + assert(Date(-1999, 7, 6).opCmp(Date(1999, 7, 6)) < 0); + assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 6)) > 0); - _assertPred!("opCmp", "<")(Date(-1999, 8, 6), Date(1999, 7, 6)); - _assertPred!("opCmp", ">")(Date(1999, 7, 6), Date(-1999, 8, 6)); + assert(Date(-1999, 8, 6).opCmp(Date(1999, 7, 6)) < 0); + assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 6)) > 0); - _assertPred!("opCmp", "<")(Date(-1999, 7, 7), Date(1999, 7, 6)); - _assertPred!("opCmp", ">")(Date(1999, 7, 6), Date(-1999, 7, 7)); + assert(Date(-1999, 7, 7).opCmp(Date(1999, 7, 6)) < 0); + assert(Date(1999, 7, 6).opCmp(Date(-1999, 7, 7)) > 0); - _assertPred!("opCmp", "<")(Date(-1999, 8, 7), Date(1999, 7, 6)); - _assertPred!("opCmp", ">")(Date(1999, 7, 6), Date(-1999, 8, 7)); + assert(Date(-1999, 8, 7).opCmp(Date(1999, 7, 6)) < 0); + assert(Date(1999, 7, 6).opCmp(Date(-1999, 8, 7)) > 0); - _assertPred!("opCmp", "<")(Date(-1999, 8, 6), Date(1999, 6, 6)); - _assertPred!("opCmp", ">")(Date(1999, 6, 8), Date(-1999, 7, 6)); + assert(Date(-1999, 8, 6).opCmp(Date(1999, 6, 6)) < 0); + assert(Date(1999, 6, 8).opCmp(Date(-1999, 7, 6)) > 0); - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, date.opCmp(date))); - static assert(__traits(compiles, date.opCmp(cdate))); - static assert(__traits(compiles, date.opCmp(idate))); - static assert(__traits(compiles, cdate.opCmp(date))); - static assert(__traits(compiles, cdate.opCmp(cdate))); - static assert(__traits(compiles, cdate.opCmp(idate))); - static assert(__traits(compiles, idate.opCmp(date))); - static assert(__traits(compiles, idate.opCmp(cdate))); - static assert(__traits(compiles, idate.opCmp(idate))); - } + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, date.opCmp(date))); + static assert(__traits(compiles, date.opCmp(cdate))); + static assert(__traits(compiles, date.opCmp(idate))); + static assert(__traits(compiles, cdate.opCmp(date))); + static assert(__traits(compiles, cdate.opCmp(cdate))); + static assert(__traits(compiles, cdate.opCmp(idate))); + static assert(__traits(compiles, idate.opCmp(date))); + static assert(__traits(compiles, idate.opCmp(cdate))); + static assert(__traits(compiles, idate.opCmp(idate))); } /++ Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive are B.C. - - Examples: --------------------- -assert(Date(1999, 7, 6).year == 1999); -assert(Date(2010, 10, 4).year == 2010); -assert(Date(-7, 4, 5).year == -7); --------------------- +/ - @property short year() const pure nothrow + @property short year() @safe const pure nothrow { return _year; } + /// unittest { - version(testStdDateTime) - { - _assertPred!"=="(Date.init.year, 1); - _assertPred!"=="(Date(1999, 7, 6).year, 1999); - _assertPred!"=="(Date(-1999, 7, 6).year, -1999); + assert(Date(1999, 7, 6).year == 1999); + assert(Date(2010, 10, 4).year == 2010); + assert(Date(-7, 4, 5).year == -7); + } - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.year == 1999)); - static assert(__traits(compiles, idate.year == 1999)); + unittest + { + assert(Date.init.year == 1); + assert(Date(1999, 7, 6).year == 1999); + assert(Date(-1999, 7, 6).year == -1999); - //Verify Examples. - assert(Date(1999, 7, 6).year == 1999); - assert(Date(2010, 10, 4).year == 2010); - assert(Date(-7, 4, 5).year == -7); - } + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.year == 1999)); + static assert(__traits(compiles, idate.year == 1999)); } /++ @@ -9368,43 +8903,43 @@ assert(Date(-7, 4, 5).year == -7); $(LREF DateTimeException) if the new year is not a leap year and the resulting date would be on February 29th. +/ - @property void year(int year) pure + @property void year(int year) @safe pure { enforceValid!"days"(year, _month, _day); _year = cast(short)year; } + /// unittest { - version(testStdDateTime) - { - static void testDateInvalid(Date date, int year) - { - date.year = year; - } + assert(Date(1999, 7, 6).year == 1999); + assert(Date(2010, 10, 4).year == 2010); + assert(Date(-7, 4, 5).year == -7); + } - static void testDate(Date date, int year, in Date expected, size_t line = __LINE__) - { - date.year = year; - _assertPred!"=="(date, expected, "", __FILE__, line); - } + unittest + { + static void testDateInvalid(Date date, int year) + { + date.year = year; + } - assertThrown!DateTimeException(testDateInvalid(Date(4, 2, 29), 1)); + static void testDate(Date date, int year, in Date expected) + { + date.year = year; + assert(date == expected); + } - testDate(Date(1, 1, 1), 1999, Date(1999, 1, 1)); - testDate(Date(1, 1, 1), 0, Date(0, 1, 1)); - testDate(Date(1, 1, 1), -1999, Date(-1999, 1, 1)); + assertThrown!DateTimeException(testDateInvalid(Date(4, 2, 29), 1)); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.year = 1999)); - static assert(!__traits(compiles, idate.year = 1999)); + testDate(Date(1, 1, 1), 1999, Date(1999, 1, 1)); + testDate(Date(1, 1, 1), 0, Date(0, 1, 1)); + testDate(Date(1, 1, 1), -1999, Date(-1999, 1, 1)); - //Verify Examples. - assert(Date(1999, 7, 6).year == 1999); - assert(Date(2010, 10, 4).year == 2010); - assert(Date(-7, 4, 5).year == -7); - } + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.year = 1999)); + static assert(!__traits(compiles, idate.year = 1999)); } @@ -9413,42 +8948,32 @@ assert(Date(-7, 4, 5).year == -7); Throws: $(LREF DateTimeException) if $(D isAD) is true. - - Examples: --------------------- -assert(Date(0, 1, 1).yearBC == 1); -assert(Date(-1, 1, 1).yearBC == 2); -assert(Date(-100, 1, 1).yearBC == 101); --------------------- +/ - @property ushort yearBC() const pure + @property ushort yearBC() @safe const pure { if(isAD) - throw new DateTimeException("Year " ~ numToString(_year) ~ " is A.D."); - //Once format is pure, this would be a better error message. - //throw new DateTimeException(format("%s is A.D.", this)); - + throw new DateTimeException(format("Year %s is A.D.", _year)); return cast(ushort)((_year * -1) + 1); } + /// unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException((in Date date){date.yearBC;}(Date(1, 1, 1))); + assert(Date(0, 1, 1).yearBC == 1); + assert(Date(-1, 1, 1).yearBC == 2); + assert(Date(-100, 1, 1).yearBC == 101); + } - auto date = Date(0, 7, 6); - const cdate = Date(0, 7, 6); - immutable idate = Date(0, 7, 6); - static assert(__traits(compiles, date.yearBC)); - static assert(__traits(compiles, cdate.yearBC)); - static assert(__traits(compiles, idate.yearBC)); + unittest + { + assertThrown!DateTimeException((in Date date){date.yearBC;}(Date(1, 1, 1))); - //Verify Examples. - assert(Date(0, 1, 1).yearBC == 1); - assert(Date(-1, 1, 1).yearBC == 2); - assert(Date(-100, 1, 1).yearBC == 101); - } + auto date = Date(0, 7, 6); + const cdate = Date(0, 7, 6); + immutable idate = Date(0, 7, 6); + static assert(__traits(compiles, date.yearBC)); + static assert(__traits(compiles, cdate.yearBC)); + static assert(__traits(compiles, idate.yearBC)); } @@ -9460,18 +8985,8 @@ assert(Date(-100, 1, 1).yearBC == 101); Throws: $(LREF DateTimeException) if a non-positive value is given. - - Examples: --------------------- -auto date = Date(2010, 1, 1); -date.yearBC = 1; -assert(date == Date(0, 1, 1)); - -date.yearBC = 10; -assert(date == Date(-9, 1, 1)); --------------------- +/ - @property void yearBC(int year) pure + @property void yearBC(int year) @safe pure { if(year <= 0) throw new DateTimeException("The given year is not a year B.C."); @@ -9479,67 +8994,56 @@ assert(date == Date(-9, 1, 1)); _year = cast(short)((year - 1) * -1); } + /// unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException((Date date){date.yearBC = -1;}(Date(1, 1, 1))); + auto date = Date(2010, 1, 1); + date.yearBC = 1; + assert(date == Date(0, 1, 1)); - { - auto date = Date(0, 7, 6); - const cdate = Date(0, 7, 6); - immutable idate = Date(0, 7, 6); - static assert(__traits(compiles, date.yearBC = 7)); - static assert(!__traits(compiles, cdate.yearBC = 7)); - static assert(!__traits(compiles, idate.yearBC = 7)); - } + date.yearBC = 10; + assert(date == Date(-9, 1, 1)); + } - //Verify Examples. - { - auto date = Date(2010, 1, 1); - date.yearBC = 1; - assert(date == Date(0, 1, 1)); + unittest + { + assertThrown!DateTimeException((Date date){date.yearBC = -1;}(Date(1, 1, 1))); - date.yearBC = 10; - assert(date == Date(-9, 1, 1)); - } - } + auto date = Date(0, 7, 6); + const cdate = Date(0, 7, 6); + immutable idate = Date(0, 7, 6); + static assert(__traits(compiles, date.yearBC = 7)); + static assert(!__traits(compiles, cdate.yearBC = 7)); + static assert(!__traits(compiles, idate.yearBC = 7)); } /++ Month of a Gregorian Year. - - Examples: --------------------- -assert(Date(1999, 7, 6).month == 7); -assert(Date(2010, 10, 4).month == 10); -assert(Date(-7, 4, 5).month == 4); --------------------- +/ - @property Month month() const pure nothrow + @property Month month() @safe const pure nothrow { return _month; } + /// unittest { - version(testStdDateTime) - { - _assertPred!"=="(Date.init.month, 1); - _assertPred!"=="(Date(1999, 7, 6).month, 7); - _assertPred!"=="(Date(-1999, 7, 6).month, 7); + assert(Date(1999, 7, 6).month == 7); + assert(Date(2010, 10, 4).month == 10); + assert(Date(-7, 4, 5).month == 4); + } - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.month == 7)); - static assert(__traits(compiles, idate.month == 7)); + unittest + { + assert(Date.init.month == 1); + assert(Date(1999, 7, 6).month == 7); + assert(Date(-1999, 7, 6).month == 7); - //Verify Examples. - assert(Date(1999, 7, 6).month == 7); - assert(Date(2010, 10, 4).month == 10); - assert(Date(-7, 4, 5).month == 4); - } + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.month == 7)); + static assert(__traits(compiles, idate.month == 7)); } /++ @@ -9552,7 +9056,7 @@ assert(Date(-7, 4, 5).month == 4); $(LREF DateTimeException) if the given month is not a valid month or if the current day would not be valid in the given month. +/ - @property void month(Month month) pure + @property void month(Month month) @safe pure { enforceValid!"months"(month); enforceValid!"days"(_year, month, _day); @@ -9561,60 +9065,50 @@ assert(Date(-7, 4, 5).month == 4); unittest { - version(testStdDateTime) + static void testDate(Date date, Month month, in Date expected = Date.init) { - static void testDate(Date date, Month month, in Date expected = Date.init, size_t line = __LINE__) - { - date.month = month; - assert(expected != Date.init); - _assertPred!"=="(date, expected, "", __FILE__, line); - } + date.month = month; + assert(expected != Date.init); + assert(date == expected); + } - assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month)0)); - assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month)13)); - assertThrown!DateTimeException(testDate(Date(1, 1, 29), cast(Month)2)); - assertThrown!DateTimeException(testDate(Date(0, 1, 30), cast(Month)2)); + assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month)0)); + assertThrown!DateTimeException(testDate(Date(1, 1, 1), cast(Month)13)); + assertThrown!DateTimeException(testDate(Date(1, 1, 29), cast(Month)2)); + assertThrown!DateTimeException(testDate(Date(0, 1, 30), cast(Month)2)); - testDate(Date(1, 1, 1), cast(Month)7, Date(1, 7, 1)); - testDate(Date(-1, 1, 1), cast(Month)7, Date(-1, 7, 1)); + testDate(Date(1, 1, 1), cast(Month)7, Date(1, 7, 1)); + testDate(Date(-1, 1, 1), cast(Month)7, Date(-1, 7, 1)); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.month = 7)); - static assert(!__traits(compiles, idate.month = 7)); - } + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.month = 7)); + static assert(!__traits(compiles, idate.month = 7)); } /++ Day of a Gregorian Month. - - Examples: --------------------- -assert(Date(1999, 7, 6).day == 6); -assert(Date(2010, 10, 4).day == 4); -assert(Date(-7, 4, 5).day == 5); --------------------- +/ - @property ubyte day() const pure nothrow + @property ubyte day() @safe const pure nothrow { return _day; } - //Verify Examples. - version(testStdDateTime) unittest + /// + unittest { assert(Date(1999, 7, 6).day == 6); assert(Date(2010, 10, 4).day == 4); assert(Date(-7, 4, 5).day == 5); } - version(testStdDateTime) unittest + unittest { - static void test(Date date, int expected, size_t line = __LINE__) + static void test(Date date, int expected) { - _assertPred!"=="(date.day, expected, - format("Value given: %s", date), __FILE__, line); + assert(date.day == expected, + format("Value given: %s", date)); } foreach(year; chain(testYearsBC, testYearsAD)) @@ -9639,7 +9133,7 @@ assert(Date(-7, 4, 5).day == 5); $(LREF DateTimeException) if the given day is not a valid day of the current month. +/ - @property void day(int day) pure + @property void day(int day) @safe pure { enforceValid!"days"(_year, _month, day); _day = cast(ubyte)day; @@ -9647,90 +9141,87 @@ assert(Date(-7, 4, 5).day == 5); unittest { - version(testStdDateTime) + static void testDate(Date date, int day) { - static void testDate(Date date, int day) - { - date.day = day; - } - - //Test A.D. - assertThrown!DateTimeException(testDate(Date(1, 1, 1), 0)); - assertThrown!DateTimeException(testDate(Date(1, 1, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 2, 1), 29)); - assertThrown!DateTimeException(testDate(Date(4, 2, 1), 30)); - assertThrown!DateTimeException(testDate(Date(1, 3, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 4, 1), 31)); - assertThrown!DateTimeException(testDate(Date(1, 5, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 6, 1), 31)); - assertThrown!DateTimeException(testDate(Date(1, 7, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 8, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 9, 1), 31)); - assertThrown!DateTimeException(testDate(Date(1, 10, 1), 32)); - assertThrown!DateTimeException(testDate(Date(1, 11, 1), 31)); - assertThrown!DateTimeException(testDate(Date(1, 12, 1), 32)); - - assertNotThrown!DateTimeException(testDate(Date(1, 1, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 2, 1), 28)); - assertNotThrown!DateTimeException(testDate(Date(4, 2, 1), 29)); - assertNotThrown!DateTimeException(testDate(Date(1, 3, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 4, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(1, 5, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 6, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(1, 7, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 8, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 9, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(1, 10, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(1, 11, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(1, 12, 1), 31)); - - { - auto date = Date(1, 1, 1); - date.day = 6; - _assertPred!"=="(date, Date(1, 1, 6)); - } - - //Test B.C. - assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 0)); - assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 2, 1), 29)); - assertThrown!DateTimeException(testDate(Date(0, 2, 1), 30)); - assertThrown!DateTimeException(testDate(Date(-1, 3, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 4, 1), 31)); - assertThrown!DateTimeException(testDate(Date(-1, 5, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 6, 1), 31)); - assertThrown!DateTimeException(testDate(Date(-1, 7, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 8, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 9, 1), 31)); - assertThrown!DateTimeException(testDate(Date(-1, 10, 1), 32)); - assertThrown!DateTimeException(testDate(Date(-1, 11, 1), 31)); - assertThrown!DateTimeException(testDate(Date(-1, 12, 1), 32)); - - assertNotThrown!DateTimeException(testDate(Date(-1, 1, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 2, 1), 28)); - assertNotThrown!DateTimeException(testDate(Date(0, 2, 1), 29)); - assertNotThrown!DateTimeException(testDate(Date(-1, 3, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 4, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(-1, 5, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 6, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(-1, 7, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 8, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 9, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(-1, 10, 1), 31)); - assertNotThrown!DateTimeException(testDate(Date(-1, 11, 1), 30)); - assertNotThrown!DateTimeException(testDate(Date(-1, 12, 1), 31)); + date.day = day; + } - { - auto date = Date(-1, 1, 1); - date.day = 6; - _assertPred!"=="(date, Date(-1, 1, 6)); - } + //Test A.D. + assertThrown!DateTimeException(testDate(Date(1, 1, 1), 0)); + assertThrown!DateTimeException(testDate(Date(1, 1, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 2, 1), 29)); + assertThrown!DateTimeException(testDate(Date(4, 2, 1), 30)); + assertThrown!DateTimeException(testDate(Date(1, 3, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 4, 1), 31)); + assertThrown!DateTimeException(testDate(Date(1, 5, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 6, 1), 31)); + assertThrown!DateTimeException(testDate(Date(1, 7, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 8, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 9, 1), 31)); + assertThrown!DateTimeException(testDate(Date(1, 10, 1), 32)); + assertThrown!DateTimeException(testDate(Date(1, 11, 1), 31)); + assertThrown!DateTimeException(testDate(Date(1, 12, 1), 32)); + + assertNotThrown!DateTimeException(testDate(Date(1, 1, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 2, 1), 28)); + assertNotThrown!DateTimeException(testDate(Date(4, 2, 1), 29)); + assertNotThrown!DateTimeException(testDate(Date(1, 3, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 4, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(1, 5, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 6, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(1, 7, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 8, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 9, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(1, 10, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(1, 11, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(1, 12, 1), 31)); + + { + auto date = Date(1, 1, 1); + date.day = 6; + assert(date == Date(1, 1, 6)); + } - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.day = 6)); - static assert(!__traits(compiles, idate.day = 6)); + //Test B.C. + assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 0)); + assertThrown!DateTimeException(testDate(Date(-1, 1, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 2, 1), 29)); + assertThrown!DateTimeException(testDate(Date(0, 2, 1), 30)); + assertThrown!DateTimeException(testDate(Date(-1, 3, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 4, 1), 31)); + assertThrown!DateTimeException(testDate(Date(-1, 5, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 6, 1), 31)); + assertThrown!DateTimeException(testDate(Date(-1, 7, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 8, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 9, 1), 31)); + assertThrown!DateTimeException(testDate(Date(-1, 10, 1), 32)); + assertThrown!DateTimeException(testDate(Date(-1, 11, 1), 31)); + assertThrown!DateTimeException(testDate(Date(-1, 12, 1), 32)); + + assertNotThrown!DateTimeException(testDate(Date(-1, 1, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 2, 1), 28)); + assertNotThrown!DateTimeException(testDate(Date(0, 2, 1), 29)); + assertNotThrown!DateTimeException(testDate(Date(-1, 3, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 4, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(-1, 5, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 6, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(-1, 7, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 8, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 9, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(-1, 10, 1), 31)); + assertNotThrown!DateTimeException(testDate(Date(-1, 11, 1), 30)); + assertNotThrown!DateTimeException(testDate(Date(-1, 12, 1), 31)); + + { + auto date = Date(-1, 1, 1); + date.day = 6; + assert(date == Date(-1, 1, 6)); } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.day = 6)); + static assert(!__traits(compiles, idate.day = 6)); } @@ -9752,27 +9243,8 @@ assert(Date(-7, 4, 5).day == 5); $(LREF Date). allowOverflow = Whether the day should be allowed to overflow, causing the month to increment. - - Examples: --------------------- -auto d1 = Date(2010, 1, 1); -d1.add!"months"(11); -assert(d1 == Date(2010, 12, 1)); - -auto d2 = Date(2010, 1, 1); -d2.add!"months"(-11); -assert(d2 == Date(2009, 2, 1)); - -auto d3 = Date(2000, 2, 29); -d3.add!"years"(1); -assert(d3 == Date(2001, 3, 1)); - -auto d4 = Date(2000, 2, 29); -d4.add!"years"(1, AllowDayOverflow.no); -assert(d4 == Date(2001, 2, 28)); --------------------- +/ - /+ref Date+/ void add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) pure nothrow + ref Date add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow if(units == "years") { immutable newYear = _year + value; @@ -9789,227 +9261,232 @@ assert(d4 == Date(2001, 2, 28)); else _day = 28; } + + return this; } - //Verify Examples. + /// unittest { - version(stdStdDateTime) - { - auto d1 = Date(2010, 1, 1); - d1.add!"months"(11); - assert(d1 == Date(2010, 12, 1)); + auto d1 = Date(2010, 1, 1); + d1.add!"months"(11); + assert(d1 == Date(2010, 12, 1)); - auto d2 = Date(2010, 1, 1); - d2.add!"months"(-11); - assert(d2 == Date(2009, 2, 1)); + auto d2 = Date(2010, 1, 1); + d2.add!"months"(-11); + assert(d2 == Date(2009, 2, 1)); - auto d3 = Date(2000, 2, 29); - d3.add!"years"(1); - assert(d3 == Date(2001, 3, 1)); + auto d3 = Date(2000, 2, 29); + d3.add!"years"(1); + assert(d3 == Date(2001, 3, 1)); - auto d4 = Date(2000, 2, 29); - d4.add!"years"(1, AllowDayOverflow.no); - assert(d4 == Date(2001, 2, 28)); - } + auto d4 = Date(2000, 2, 29); + d4.add!"years"(1, AllowDayOverflow.no); + assert(d4 == Date(2001, 2, 28)); } //Test add!"years"() with AllowDayOverlow.yes unittest { - version(testStdDateTime) + //Test A.D. { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.add!"years"(7); - _assertPred!"=="(date, Date(2006, 7, 6)); - date.add!"years"(-9); - _assertPred!"=="(date, Date(1997, 7, 6)); - } + auto date = Date(1999, 7, 6); + date.add!"years"(7); + assert(date == Date(2006, 7, 6)); + date.add!"years"(-9); + assert(date == Date(1997, 7, 6)); + } - { - auto date = Date(1999, 2, 28); - date.add!"years"(1); - _assertPred!"=="(date, Date(2000, 2, 28)); - } + { + auto date = Date(1999, 2, 28); + date.add!"years"(1); + assert(date == Date(2000, 2, 28)); + } - { - auto date = Date(2000, 2, 29); - date.add!"years"(-1); - _assertPred!"=="(date, Date(1999, 3, 1)); - } + { + auto date = Date(2000, 2, 29); + date.add!"years"(-1); + assert(date == Date(1999, 3, 1)); + } - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.add!"years"(-7); - _assertPred!"=="(date, Date(-2006, 7, 6)); - date.add!"years"(9); - _assertPred!"=="(date, Date(-1997, 7, 6)); - } + //Test B.C. + { + auto date = Date(-1999, 7, 6); + date.add!"years"(-7); + assert(date == Date(-2006, 7, 6)); + date.add!"years"(9); + assert(date == Date(-1997, 7, 6)); + } - { - auto date = Date(-1999, 2, 28); - date.add!"years"(-1); - _assertPred!"=="(date, Date(-2000, 2, 28)); - } + { + auto date = Date(-1999, 2, 28); + date.add!"years"(-1); + assert(date == Date(-2000, 2, 28)); + } - { - auto date = Date(-2000, 2, 29); - date.add!"years"(1); - _assertPred!"=="(date, Date(-1999, 3, 1)); - } + { + auto date = Date(-2000, 2, 29); + date.add!"years"(1); + assert(date == Date(-1999, 3, 1)); + } - //Test Both - { - auto date = Date(4, 7, 6); - date.add!"years"(-5); - _assertPred!"=="(date, Date(-1, 7, 6)); - date.add!"years"(5); - _assertPred!"=="(date, Date(4, 7, 6)); - } + //Test Both + { + auto date = Date(4, 7, 6); + date.add!"years"(-5); + assert(date == Date(-1, 7, 6)); + date.add!"years"(5); + assert(date == Date(4, 7, 6)); + } - { - auto date = Date(-4, 7, 6); - date.add!"years"(5); - _assertPred!"=="(date, Date(1, 7, 6)); - date.add!"years"(-5); - _assertPred!"=="(date, Date(-4, 7, 6)); - } + { + auto date = Date(-4, 7, 6); + date.add!"years"(5); + assert(date == Date(1, 7, 6)); + date.add!"years"(-5); + assert(date == Date(-4, 7, 6)); + } - { - auto date = Date(4, 7, 6); - date.add!"years"(-8); - _assertPred!"=="(date, Date(-4, 7, 6)); - date.add!"years"(8); - _assertPred!"=="(date, Date(4, 7, 6)); - } + { + auto date = Date(4, 7, 6); + date.add!"years"(-8); + assert(date == Date(-4, 7, 6)); + date.add!"years"(8); + assert(date == Date(4, 7, 6)); + } - { - auto date = Date(-4, 7, 6); - date.add!"years"(8); - _assertPred!"=="(date, Date(4, 7, 6)); - date.add!"years"(-8); - _assertPred!"=="(date, Date(-4, 7, 6)); - } + { + auto date = Date(-4, 7, 6); + date.add!"years"(8); + assert(date == Date(4, 7, 6)); + date.add!"years"(-8); + assert(date == Date(-4, 7, 6)); + } - { - auto date = Date(-4, 2, 29); - date.add!"years"(5); - _assertPred!"=="(date, Date(1, 3, 1)); - } + { + auto date = Date(-4, 2, 29); + date.add!"years"(5); + assert(date == Date(1, 3, 1)); + } - { - auto date = Date(4, 2, 29); - date.add!"years"(-5); - _assertPred!"=="(date, Date(-1, 3, 1)); - } + { + auto date = Date(4, 2, 29); + date.add!"years"(-5); + assert(date == Date(-1, 3, 1)); + } - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.add!"years"(7))); - static assert(!__traits(compiles, idate.add!"years"(7))); + { + auto date = Date(4, 2, 29); + date.add!"years"(-5).add!"years"(7); + assert(date == Date(6, 3, 1)); } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.add!"years"(7))); + static assert(!__traits(compiles, idate.add!"years"(7))); } //Test add!"years"() with AllowDayOverlow.no unittest { - version(testStdDateTime) + //Test A.D. { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.add!"years"(7, AllowDayOverflow.no); - _assertPred!"=="(date, Date(2006, 7, 6)); - date.add!"years"(-9, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1997, 7, 6)); - } + auto date = Date(1999, 7, 6); + date.add!"years"(7, AllowDayOverflow.no); + assert(date == Date(2006, 7, 6)); + date.add!"years"(-9, AllowDayOverflow.no); + assert(date == Date(1997, 7, 6)); + } - { - auto date = Date(1999, 2, 28); - date.add!"years"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(2000, 2, 28)); - } + { + auto date = Date(1999, 2, 28); + date.add!"years"(1, AllowDayOverflow.no); + assert(date == Date(2000, 2, 28)); + } - { - auto date = Date(2000, 2, 29); - date.add!"years"(-1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 2, 28)); - } + { + auto date = Date(2000, 2, 29); + date.add!"years"(-1, AllowDayOverflow.no); + assert(date == Date(1999, 2, 28)); + } - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.add!"years"(-7, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-2006, 7, 6)); - date.add!"years"(9, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1997, 7, 6)); - } + //Test B.C. + { + auto date = Date(-1999, 7, 6); + date.add!"years"(-7, AllowDayOverflow.no); + assert(date == Date(-2006, 7, 6)); + date.add!"years"(9, AllowDayOverflow.no); + assert(date == Date(-1997, 7, 6)); + } - { - auto date = Date(-1999, 2, 28); - date.add!"years"(-1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-2000, 2, 28)); - } + { + auto date = Date(-1999, 2, 28); + date.add!"years"(-1, AllowDayOverflow.no); + assert(date == Date(-2000, 2, 28)); + } - { - auto date = Date(-2000, 2, 29); - date.add!"years"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 2, 28)); - } + { + auto date = Date(-2000, 2, 29); + date.add!"years"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 2, 28)); + } - //Test Both - { - auto date = Date(4, 7, 6); - date.add!"years"(-5, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1, 7, 6)); - date.add!"years"(5, AllowDayOverflow.no); - _assertPred!"=="(date, Date(4, 7, 6)); - } + //Test Both + { + auto date = Date(4, 7, 6); + date.add!"years"(-5, AllowDayOverflow.no); + assert(date == Date(-1, 7, 6)); + date.add!"years"(5, AllowDayOverflow.no); + assert(date == Date(4, 7, 6)); + } - { - auto date = Date(-4, 7, 6); - date.add!"years"(5, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1, 7, 6)); - date.add!"years"(-5, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-4, 7, 6)); - } + { + auto date = Date(-4, 7, 6); + date.add!"years"(5, AllowDayOverflow.no); + assert(date == Date(1, 7, 6)); + date.add!"years"(-5, AllowDayOverflow.no); + assert(date == Date(-4, 7, 6)); + } - { - auto date = Date(4, 7, 6); - date.add!"years"(-8, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-4, 7, 6)); - date.add!"years"(8, AllowDayOverflow.no); - _assertPred!"=="(date, Date(4, 7, 6)); - } + { + auto date = Date(4, 7, 6); + date.add!"years"(-8, AllowDayOverflow.no); + assert(date == Date(-4, 7, 6)); + date.add!"years"(8, AllowDayOverflow.no); + assert(date == Date(4, 7, 6)); + } - { - auto date = Date(-4, 7, 6); - date.add!"years"(8, AllowDayOverflow.no); - _assertPred!"=="(date, Date(4, 7, 6)); - date.add!"years"(-8, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-4, 7, 6)); - } + { + auto date = Date(-4, 7, 6); + date.add!"years"(8, AllowDayOverflow.no); + assert(date == Date(4, 7, 6)); + date.add!"years"(-8, AllowDayOverflow.no); + assert(date == Date(-4, 7, 6)); + } - { - auto date = Date(-4, 2, 29); - date.add!"years"(5, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1, 2, 28)); - } + { + auto date = Date(-4, 2, 29); + date.add!"years"(5, AllowDayOverflow.no); + assert(date == Date(1, 2, 28)); + } - { - auto date = Date(4, 2, 29); - date.add!"years"(-5, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1, 2, 28)); - } + { + auto date = Date(4, 2, 29); + date.add!"years"(-5, AllowDayOverflow.no); + assert(date == Date(-1, 2, 28)); + } + + { + auto date = Date(4, 2, 29); + date.add!"years"(-5, AllowDayOverflow.no).add!"years"(7, AllowDayOverflow.no); + assert(date == Date(6, 2, 28)); } } //Shares documentation with "years" version. - /+ref Date+/ void add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) pure nothrow + ref Date add(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow if(units == "months") { auto years = months / 12; @@ -10046,478 +9523,486 @@ assert(d4 == Date(2001, 2, 28)); else _day = cast(ubyte)currMaxDay; } + + return this; } //Test add!"months"() with AllowDayOverlow.yes unittest { - version(testStdDateTime) + //Test A.D. { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.add!"months"(3); - _assertPred!"=="(date, Date(1999, 10, 6)); - date.add!"months"(-4); - _assertPred!"=="(date, Date(1999, 6, 6)); - } + auto date = Date(1999, 7, 6); + date.add!"months"(3); + assert(date == Date(1999, 10, 6)); + date.add!"months"(-4); + assert(date == Date(1999, 6, 6)); + } - { - auto date = Date(1999, 7, 6); - date.add!"months"(6); - _assertPred!"=="(date, Date(2000, 1, 6)); - date.add!"months"(-6); - _assertPred!"=="(date, Date(1999, 7, 6)); - } + { + auto date = Date(1999, 7, 6); + date.add!"months"(6); + assert(date == Date(2000, 1, 6)); + date.add!"months"(-6); + assert(date == Date(1999, 7, 6)); + } - { - auto date = Date(1999, 7, 6); - date.add!"months"(27); - _assertPred!"=="(date, Date(2001, 10, 6)); - date.add!"months"(-28); - _assertPred!"=="(date, Date(1999, 6, 6)); - } + { + auto date = Date(1999, 7, 6); + date.add!"months"(27); + assert(date == Date(2001, 10, 6)); + date.add!"months"(-28); + assert(date == Date(1999, 6, 6)); + } - { - auto date = Date(1999, 5, 31); - date.add!"months"(1); - _assertPred!"=="(date, Date(1999, 7, 1)); - } + { + auto date = Date(1999, 5, 31); + date.add!"months"(1); + assert(date == Date(1999, 7, 1)); + } - { - auto date = Date(1999, 5, 31); - date.add!"months"(-1); - _assertPred!"=="(date, Date(1999, 5, 1)); - } + { + auto date = Date(1999, 5, 31); + date.add!"months"(-1); + assert(date == Date(1999, 5, 1)); + } - { - auto date = Date(1999, 2, 28); - date.add!"months"(12); - _assertPred!"=="(date, Date(2000, 2, 28)); - } + { + auto date = Date(1999, 2, 28); + date.add!"months"(12); + assert(date == Date(2000, 2, 28)); + } - { - auto date = Date(2000, 2, 29); - date.add!"months"(12); - _assertPred!"=="(date, Date(2001, 3, 1)); - } + { + auto date = Date(2000, 2, 29); + date.add!"months"(12); + assert(date == Date(2001, 3, 1)); + } - { - auto date = Date(1999, 7, 31); - date.add!"months"(1); - _assertPred!"=="(date, Date(1999, 8, 31)); - date.add!"months"(1); - _assertPred!"=="(date, Date(1999, 10, 1)); - } + { + auto date = Date(1999, 7, 31); + date.add!"months"(1); + assert(date == Date(1999, 8, 31)); + date.add!"months"(1); + assert(date == Date(1999, 10, 1)); + } - { - auto date = Date(1998, 8, 31); - date.add!"months"(13); - _assertPred!"=="(date, Date(1999, 10, 1)); - date.add!"months"(-13); - _assertPred!"=="(date, Date(1998, 9, 1)); - } + { + auto date = Date(1998, 8, 31); + date.add!"months"(13); + assert(date == Date(1999, 10, 1)); + date.add!"months"(-13); + assert(date == Date(1998, 9, 1)); + } - { - auto date = Date(1997, 12, 31); - date.add!"months"(13); - _assertPred!"=="(date, Date(1999, 1, 31)); - date.add!"months"(-13); - _assertPred!"=="(date, Date(1997, 12, 31)); - } + { + auto date = Date(1997, 12, 31); + date.add!"months"(13); + assert(date == Date(1999, 1, 31)); + date.add!"months"(-13); + assert(date == Date(1997, 12, 31)); + } - { - auto date = Date(1997, 12, 31); - date.add!"months"(14); - _assertPred!"=="(date, Date(1999, 3, 3)); - date.add!"months"(-14); - _assertPred!"=="(date, Date(1998, 1, 3)); - } + { + auto date = Date(1997, 12, 31); + date.add!"months"(14); + assert(date == Date(1999, 3, 3)); + date.add!"months"(-14); + assert(date == Date(1998, 1, 3)); + } - { - auto date = Date(1998, 12, 31); - date.add!"months"(14); - _assertPred!"=="(date, Date(2000, 3, 2)); - date.add!"months"(-14); - _assertPred!"=="(date, Date(1999, 1, 2)); - } + { + auto date = Date(1998, 12, 31); + date.add!"months"(14); + assert(date == Date(2000, 3, 2)); + date.add!"months"(-14); + assert(date == Date(1999, 1, 2)); + } - { - auto date = Date(1999, 12, 31); - date.add!"months"(14); - _assertPred!"=="(date, Date(2001, 3, 3)); - date.add!"months"(-14); - _assertPred!"=="(date, Date(2000, 1, 3)); - } + { + auto date = Date(1999, 12, 31); + date.add!"months"(14); + assert(date == Date(2001, 3, 3)); + date.add!"months"(-14); + assert(date == Date(2000, 1, 3)); + } - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.add!"months"(3); - _assertPred!"=="(date, Date(-1999, 10, 6)); - date.add!"months"(-4); - _assertPred!"=="(date, Date(-1999, 6, 6)); - } + //Test B.C. + { + auto date = Date(-1999, 7, 6); + date.add!"months"(3); + assert(date == Date(-1999, 10, 6)); + date.add!"months"(-4); + assert(date == Date(-1999, 6, 6)); + } - { - auto date = Date(-1999, 7, 6); - date.add!"months"(6); - _assertPred!"=="(date, Date(-1998, 1, 6)); - date.add!"months"(-6); - _assertPred!"=="(date, Date(-1999, 7, 6)); - } + { + auto date = Date(-1999, 7, 6); + date.add!"months"(6); + assert(date == Date(-1998, 1, 6)); + date.add!"months"(-6); + assert(date == Date(-1999, 7, 6)); + } - { - auto date = Date(-1999, 7, 6); - date.add!"months"(-27); - _assertPred!"=="(date, Date(-2001, 4, 6)); - date.add!"months"(28); - _assertPred!"=="(date, Date(-1999, 8, 6)); - } + { + auto date = Date(-1999, 7, 6); + date.add!"months"(-27); + assert(date == Date(-2001, 4, 6)); + date.add!"months"(28); + assert(date == Date(-1999, 8, 6)); + } - { - auto date = Date(-1999, 5, 31); - date.add!"months"(1); - _assertPred!"=="(date, Date(-1999, 7, 1)); - } + { + auto date = Date(-1999, 5, 31); + date.add!"months"(1); + assert(date == Date(-1999, 7, 1)); + } - { - auto date = Date(-1999, 5, 31); - date.add!"months"(-1); - _assertPred!"=="(date, Date(-1999, 5, 1)); - } + { + auto date = Date(-1999, 5, 31); + date.add!"months"(-1); + assert(date == Date(-1999, 5, 1)); + } - { - auto date = Date(-1999, 2, 28); - date.add!"months"(-12); - _assertPred!"=="(date, Date(-2000, 2, 28)); - } + { + auto date = Date(-1999, 2, 28); + date.add!"months"(-12); + assert(date == Date(-2000, 2, 28)); + } - { - auto date = Date(-2000, 2, 29); - date.add!"months"(-12); - _assertPred!"=="(date, Date(-2001, 3, 1)); - } + { + auto date = Date(-2000, 2, 29); + date.add!"months"(-12); + assert(date == Date(-2001, 3, 1)); + } - { - auto date = Date(-1999, 7, 31); - date.add!"months"(1); - _assertPred!"=="(date, Date(-1999, 8, 31)); - date.add!"months"(1); - _assertPred!"=="(date, Date(-1999, 10, 1)); - } + { + auto date = Date(-1999, 7, 31); + date.add!"months"(1); + assert(date == Date(-1999, 8, 31)); + date.add!"months"(1); + assert(date == Date(-1999, 10, 1)); + } - { - auto date = Date(-1998, 8, 31); - date.add!"months"(13); - _assertPred!"=="(date, Date(-1997, 10, 1)); - date.add!"months"(-13); - _assertPred!"=="(date, Date(-1998, 9, 1)); - } + { + auto date = Date(-1998, 8, 31); + date.add!"months"(13); + assert(date == Date(-1997, 10, 1)); + date.add!"months"(-13); + assert(date == Date(-1998, 9, 1)); + } - { - auto date = Date(-1997, 12, 31); - date.add!"months"(13); - _assertPred!"=="(date, Date(-1995, 1, 31)); - date.add!"months"(-13); - _assertPred!"=="(date, Date(-1997, 12, 31)); - } + { + auto date = Date(-1997, 12, 31); + date.add!"months"(13); + assert(date == Date(-1995, 1, 31)); + date.add!"months"(-13); + assert(date == Date(-1997, 12, 31)); + } - { - auto date = Date(-1997, 12, 31); - date.add!"months"(14); - _assertPred!"=="(date, Date(-1995, 3, 3)); - date.add!"months"(-14); - _assertPred!"=="(date, Date(-1996, 1, 3)); - } + { + auto date = Date(-1997, 12, 31); + date.add!"months"(14); + assert(date == Date(-1995, 3, 3)); + date.add!"months"(-14); + assert(date == Date(-1996, 1, 3)); + } - { - auto date = Date(-2002, 12, 31); - date.add!"months"(14); - _assertPred!"=="(date, Date(-2000, 3, 2)); - date.add!"months"(-14); - _assertPred!"=="(date, Date(-2001, 1, 2)); - } + { + auto date = Date(-2002, 12, 31); + date.add!"months"(14); + assert(date == Date(-2000, 3, 2)); + date.add!"months"(-14); + assert(date == Date(-2001, 1, 2)); + } - { - auto date = Date(-2001, 12, 31); - date.add!"months"(14); - _assertPred!"=="(date, Date(-1999, 3, 3)); - date.add!"months"(-14); - _assertPred!"=="(date, Date(-2000, 1, 3)); - } + { + auto date = Date(-2001, 12, 31); + date.add!"months"(14); + assert(date == Date(-1999, 3, 3)); + date.add!"months"(-14); + assert(date == Date(-2000, 1, 3)); + } - //Test Both - { - auto date = Date(1, 1, 1); - date.add!"months"(-1); - _assertPred!"=="(date, Date(0, 12, 1)); - date.add!"months"(1); - _assertPred!"=="(date, Date(1, 1, 1)); - } + //Test Both + { + auto date = Date(1, 1, 1); + date.add!"months"(-1); + assert(date == Date(0, 12, 1)); + date.add!"months"(1); + assert(date == Date(1, 1, 1)); + } - { - auto date = Date(4, 1, 1); - date.add!"months"(-48); - _assertPred!"=="(date, Date(0, 1, 1)); - date.add!"months"(48); - _assertPred!"=="(date, Date(4, 1, 1)); - } + { + auto date = Date(4, 1, 1); + date.add!"months"(-48); + assert(date == Date(0, 1, 1)); + date.add!"months"(48); + assert(date == Date(4, 1, 1)); + } - { - auto date = Date(4, 3, 31); - date.add!"months"(-49); - _assertPred!"=="(date, Date(0, 3, 2)); - date.add!"months"(49); - _assertPred!"=="(date, Date(4, 4, 2)); - } + { + auto date = Date(4, 3, 31); + date.add!"months"(-49); + assert(date == Date(0, 3, 2)); + date.add!"months"(49); + assert(date == Date(4, 4, 2)); + } - { - auto date = Date(4, 3, 31); - date.add!"months"(-85); - _assertPred!"=="(date, Date(-3, 3, 3)); - date.add!"months"(85); - _assertPred!"=="(date, Date(4, 4, 3)); - } + { + auto date = Date(4, 3, 31); + date.add!"months"(-85); + assert(date == Date(-3, 3, 3)); + date.add!"months"(85); + assert(date == Date(4, 4, 3)); + } - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.add!"months"(3))); - static assert(!__traits(compiles, idate.add!"months"(3))); + { + auto date = Date(-3, 3, 31); + date.add!"months"(85).add!"months"(-83); + assert(date == Date(-3, 6, 1)); } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.add!"months"(3))); + static assert(!__traits(compiles, idate.add!"months"(3))); } //Test add!"months"() with AllowDayOverlow.no unittest { - version(testStdDateTime) + //Test A.D. { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.add!"months"(3, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 10, 6)); - date.add!"months"(-4, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 6, 6)); - } + auto date = Date(1999, 7, 6); + date.add!"months"(3, AllowDayOverflow.no); + assert(date == Date(1999, 10, 6)); + date.add!"months"(-4, AllowDayOverflow.no); + assert(date == Date(1999, 6, 6)); + } - { - auto date = Date(1999, 7, 6); - date.add!"months"(6, AllowDayOverflow.no); - _assertPred!"=="(date, Date(2000, 1, 6)); - date.add!"months"(-6, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 7, 6)); - } + { + auto date = Date(1999, 7, 6); + date.add!"months"(6, AllowDayOverflow.no); + assert(date == Date(2000, 1, 6)); + date.add!"months"(-6, AllowDayOverflow.no); + assert(date == Date(1999, 7, 6)); + } - { - auto date = Date(1999, 7, 6); - date.add!"months"(27, AllowDayOverflow.no); - _assertPred!"=="(date, Date(2001, 10, 6)); - date.add!"months"(-28, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 6, 6)); - } + { + auto date = Date(1999, 7, 6); + date.add!"months"(27, AllowDayOverflow.no); + assert(date == Date(2001, 10, 6)); + date.add!"months"(-28, AllowDayOverflow.no); + assert(date == Date(1999, 6, 6)); + } - { - auto date = Date(1999, 5, 31); - date.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 6, 30)); - } + { + auto date = Date(1999, 5, 31); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 6, 30)); + } - { - auto date = Date(1999, 5, 31); - date.add!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 4, 30)); - } + { + auto date = Date(1999, 5, 31); + date.add!"months"(-1, AllowDayOverflow.no); + assert(date == Date(1999, 4, 30)); + } - { - auto date = Date(1999, 2, 28); - date.add!"months"(12, AllowDayOverflow.no); - _assertPred!"=="(date, Date(2000, 2, 28)); - } + { + auto date = Date(1999, 2, 28); + date.add!"months"(12, AllowDayOverflow.no); + assert(date == Date(2000, 2, 28)); + } - { - auto date = Date(2000, 2, 29); - date.add!"months"(12, AllowDayOverflow.no); - _assertPred!"=="(date, Date(2001, 2, 28)); - } + { + auto date = Date(2000, 2, 29); + date.add!"months"(12, AllowDayOverflow.no); + assert(date == Date(2001, 2, 28)); + } - { - auto date = Date(1999, 7, 31); - date.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 8, 31)); - date.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 9, 30)); - } + { + auto date = Date(1999, 7, 31); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 8, 31)); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 9, 30)); + } - { - auto date = Date(1998, 8, 31); - date.add!"months"(13, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 9, 30)); - date.add!"months"(-13, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1998, 8, 30)); - } + { + auto date = Date(1998, 8, 31); + date.add!"months"(13, AllowDayOverflow.no); + assert(date == Date(1999, 9, 30)); + date.add!"months"(-13, AllowDayOverflow.no); + assert(date == Date(1998, 8, 30)); + } - { - auto date = Date(1997, 12, 31); - date.add!"months"(13, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 1, 31)); - date.add!"months"(-13, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1997, 12, 31)); - } + { + auto date = Date(1997, 12, 31); + date.add!"months"(13, AllowDayOverflow.no); + assert(date == Date(1999, 1, 31)); + date.add!"months"(-13, AllowDayOverflow.no); + assert(date == Date(1997, 12, 31)); + } - { - auto date = Date(1997, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 2, 28)); - date.add!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1997, 12, 28)); - } + { + auto date = Date(1997, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(1999, 2, 28)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1997, 12, 28)); + } - { - auto date = Date(1998, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(2000, 2, 29)); - date.add!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1998, 12, 29)); - } + { + auto date = Date(1998, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(2000, 2, 29)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1998, 12, 29)); + } - { - auto date = Date(1999, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(2001, 2, 28)); - date.add!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 12, 28)); - } + { + auto date = Date(1999, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(2001, 2, 28)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1999, 12, 28)); + } - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.add!"months"(3, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 10, 6)); - date.add!"months"(-4, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 6, 6)); - } + //Test B.C. + { + auto date = Date(-1999, 7, 6); + date.add!"months"(3, AllowDayOverflow.no); + assert(date == Date(-1999, 10, 6)); + date.add!"months"(-4, AllowDayOverflow.no); + assert(date == Date(-1999, 6, 6)); + } - { - auto date = Date(-1999, 7, 6); - date.add!"months"(6, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1998, 1, 6)); - date.add!"months"(-6, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 7, 6)); - } + { + auto date = Date(-1999, 7, 6); + date.add!"months"(6, AllowDayOverflow.no); + assert(date == Date(-1998, 1, 6)); + date.add!"months"(-6, AllowDayOverflow.no); + assert(date == Date(-1999, 7, 6)); + } - { - auto date = Date(-1999, 7, 6); - date.add!"months"(-27, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-2001, 4, 6)); - date.add!"months"(28, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 8, 6)); - } + { + auto date = Date(-1999, 7, 6); + date.add!"months"(-27, AllowDayOverflow.no); + assert(date == Date(-2001, 4, 6)); + date.add!"months"(28, AllowDayOverflow.no); + assert(date == Date(-1999, 8, 6)); + } - { - auto date = Date(-1999, 5, 31); - date.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 6, 30)); - } + { + auto date = Date(-1999, 5, 31); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 6, 30)); + } - { - auto date = Date(-1999, 5, 31); - date.add!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 4, 30)); - } + { + auto date = Date(-1999, 5, 31); + date.add!"months"(-1, AllowDayOverflow.no); + assert(date == Date(-1999, 4, 30)); + } - { - auto date = Date(-1999, 2, 28); - date.add!"months"(-12, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-2000, 2, 28)); - } + { + auto date = Date(-1999, 2, 28); + date.add!"months"(-12, AllowDayOverflow.no); + assert(date == Date(-2000, 2, 28)); + } - { - auto date = Date(-2000, 2, 29); - date.add!"months"(-12, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-2001, 2, 28)); - } + { + auto date = Date(-2000, 2, 29); + date.add!"months"(-12, AllowDayOverflow.no); + assert(date == Date(-2001, 2, 28)); + } - { - auto date = Date(-1999, 7, 31); - date.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 8, 31)); - date.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 9, 30)); - } + { + auto date = Date(-1999, 7, 31); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 8, 31)); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 9, 30)); + } - { - auto date = Date(-1998, 8, 31); - date.add!"months"(13, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1997, 9, 30)); - date.add!"months"(-13, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1998, 8, 30)); - } + { + auto date = Date(-1998, 8, 31); + date.add!"months"(13, AllowDayOverflow.no); + assert(date == Date(-1997, 9, 30)); + date.add!"months"(-13, AllowDayOverflow.no); + assert(date == Date(-1998, 8, 30)); + } - { - auto date = Date(-1997, 12, 31); - date.add!"months"(13, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1995, 1, 31)); - date.add!"months"(-13, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1997, 12, 31)); - } + { + auto date = Date(-1997, 12, 31); + date.add!"months"(13, AllowDayOverflow.no); + assert(date == Date(-1995, 1, 31)); + date.add!"months"(-13, AllowDayOverflow.no); + assert(date == Date(-1997, 12, 31)); + } - { - auto date = Date(-1997, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1995, 2, 28)); - date.add!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1997, 12, 28)); - } + { + auto date = Date(-1997, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(-1995, 2, 28)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-1997, 12, 28)); + } - { - auto date = Date(-2002, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-2000, 2, 29)); - date.add!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-2002, 12, 29)); - } + { + auto date = Date(-2002, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(-2000, 2, 29)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-2002, 12, 29)); + } - { - auto date = Date(-2001, 12, 31); - date.add!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 2, 28)); - date.add!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-2001, 12, 28)); - } + { + auto date = Date(-2001, 12, 31); + date.add!"months"(14, AllowDayOverflow.no); + assert(date == Date(-1999, 2, 28)); + date.add!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-2001, 12, 28)); + } - //Test Both - { - auto date = Date(1, 1, 1); - date.add!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(0, 12, 1)); - date.add!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1, 1, 1)); - } + //Test Both + { + auto date = Date(1, 1, 1); + date.add!"months"(-1, AllowDayOverflow.no); + assert(date == Date(0, 12, 1)); + date.add!"months"(1, AllowDayOverflow.no); + assert(date == Date(1, 1, 1)); + } - { - auto date = Date(4, 1, 1); - date.add!"months"(-48, AllowDayOverflow.no); - _assertPred!"=="(date, Date(0, 1, 1)); - date.add!"months"(48, AllowDayOverflow.no); - _assertPred!"=="(date, Date(4, 1, 1)); - } + { + auto date = Date(4, 1, 1); + date.add!"months"(-48, AllowDayOverflow.no); + assert(date == Date(0, 1, 1)); + date.add!"months"(48, AllowDayOverflow.no); + assert(date == Date(4, 1, 1)); + } - { - auto date = Date(4, 3, 31); - date.add!"months"(-49, AllowDayOverflow.no); - _assertPred!"=="(date, Date(0, 2, 29)); - date.add!"months"(49, AllowDayOverflow.no); - _assertPred!"=="(date, Date(4, 3, 29)); - } + { + auto date = Date(4, 3, 31); + date.add!"months"(-49, AllowDayOverflow.no); + assert(date == Date(0, 2, 29)); + date.add!"months"(49, AllowDayOverflow.no); + assert(date == Date(4, 3, 29)); + } - { - auto date = Date(4, 3, 31); - date.add!"months"(-85, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-3, 2, 28)); - date.add!"months"(85, AllowDayOverflow.no); - _assertPred!"=="(date, Date(4, 3, 28)); - } + { + auto date = Date(4, 3, 31); + date.add!"months"(-85, AllowDayOverflow.no); + assert(date == Date(-3, 2, 28)); + date.add!"months"(85, AllowDayOverflow.no); + assert(date == Date(4, 3, 28)); + } + + { + auto date = Date(-3, 3, 31); + date.add!"months"(85, AllowDayOverflow.no).add!"months"(-83, AllowDayOverflow.no); + assert(date == Date(-3, 5, 30)); } } @@ -10540,85 +10025,52 @@ assert(d4 == Date(2001, 2, 28)); $(LREF Date). allowOverflow = Whether the day should be allowed to overflow, causing the month to increment. - - Examples: --------------------- -auto d1 = Date(2010, 1, 1); -d1.roll!"months"(1); -assert(d1 == Date(2010, 2, 1)); - -auto d2 = Date(2010, 1, 1); -d2.roll!"months"(-1); -assert(d2 == Date(2010, 12, 1)); - -auto d3 = Date(1999, 1, 29); -d3.roll!"months"(1); -assert(d3 == Date(1999, 3, 1)); - -auto d4 = Date(1999, 1, 29); -d4.roll!"months"(1, AllowDayOverflow.no); -assert(d4 == Date(1999, 2, 28)); - -auto d5 = Date(2000, 2, 29); -d5.roll!"years"(1); -assert(d5 == Date(2001, 3, 1)); - -auto d6 = Date(2000, 2, 29); -d6.roll!"years"(1, AllowDayOverflow.no); -assert(d6 == Date(2001, 2, 28)); --------------------- +/ - /+ref Date+/ void roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) pure nothrow + ref Date roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow if(units == "years") { - add!"years"(value, allowOverflow); + return add!"years"(value, allowOverflow); } - //Verify Examples. + /// unittest { - version(testStdDateTime) - { - auto d1 = Date(2010, 1, 1); - d1.roll!"months"(1); - assert(d1 == Date(2010, 2, 1)); + auto d1 = Date(2010, 1, 1); + d1.roll!"months"(1); + assert(d1 == Date(2010, 2, 1)); - auto d2 = Date(2010, 1, 1); - d2.roll!"months"(-1); - assert(d2 == Date(2010, 12, 1)); + auto d2 = Date(2010, 1, 1); + d2.roll!"months"(-1); + assert(d2 == Date(2010, 12, 1)); - auto d3 = Date(1999, 1, 29); - d3.roll!"months"(1); - assert(d3 == Date(1999, 3, 1)); + auto d3 = Date(1999, 1, 29); + d3.roll!"months"(1); + assert(d3 == Date(1999, 3, 1)); - auto d4 = Date(1999, 1, 29); - d4.roll!"months"(1, AllowDayOverflow.no); - assert(d4 == Date(1999, 2, 28)); + auto d4 = Date(1999, 1, 29); + d4.roll!"months"(1, AllowDayOverflow.no); + assert(d4 == Date(1999, 2, 28)); - auto d5 = Date(2000, 2, 29); - d5.roll!"years"(1); - assert(d5 == Date(2001, 3, 1)); + auto d5 = Date(2000, 2, 29); + d5.roll!"years"(1); + assert(d5 == Date(2001, 3, 1)); - auto d6 = Date(2000, 2, 29); - d6.roll!"years"(1, AllowDayOverflow.no); - assert(d6 == Date(2001, 2, 28)); - } + auto d6 = Date(2000, 2, 29); + d6.roll!"years"(1, AllowDayOverflow.no); + assert(d6 == Date(2001, 2, 28)); } unittest { - version(testStdDateTime) - { - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.roll!"years"(3))); - static assert(!__traits(compiles, idate.rolYears(3))); - } + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.roll!"years"(3))); + static assert(!__traits(compiles, idate.rolYears(3))); } //Shares documentation with "years" version. - /+ref Date+/ void roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) pure nothrow + ref Date roll(string units)(long months, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow if(units == "months") { months %= 12; @@ -10650,559 +10102,550 @@ assert(d6 == Date(2001, 2, 28)); else _day = cast(ubyte)currMaxDay; } + + return this; } //Test roll!"months"() with AllowDayOverlow.yes unittest { - version(testStdDateTime) + //Test A.D. { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.roll!"months"(3); - _assertPred!"=="(date, Date(1999, 10, 6)); - date.roll!"months"(-4); - _assertPred!"=="(date, Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"months"(6); - _assertPred!"=="(date, Date(1999, 1, 6)); - date.roll!"months"(-6); - _assertPred!"=="(date, Date(1999, 7, 6)); - } - - { - auto date = Date(1999, 7, 6); - date.roll!"months"(27); - _assertPred!"=="(date, Date(1999, 10, 6)); - date.roll!"months"(-28); - _assertPred!"=="(date, Date(1999, 6, 6)); - } - - { - auto date = Date(1999, 5, 31); - date.roll!"months"(1); - _assertPred!"=="(date, Date(1999, 7, 1)); - } - - { - auto date = Date(1999, 5, 31); - date.roll!"months"(-1); - _assertPred!"=="(date, Date(1999, 5, 1)); - } + auto date = Date(1999, 7, 6); + date.roll!"months"(3); + assert(date == Date(1999, 10, 6)); + date.roll!"months"(-4); + assert(date == Date(1999, 6, 6)); + } - { - auto date = Date(1999, 2, 28); - date.roll!"months"(12); - _assertPred!"=="(date, Date(1999, 2, 28)); - } + { + auto date = Date(1999, 7, 6); + date.roll!"months"(6); + assert(date == Date(1999, 1, 6)); + date.roll!"months"(-6); + assert(date == Date(1999, 7, 6)); + } - { - auto date = Date(2000, 2, 29); - date.roll!"months"(12); - _assertPred!"=="(date, Date(2000, 2, 29)); - } + { + auto date = Date(1999, 7, 6); + date.roll!"months"(27); + assert(date == Date(1999, 10, 6)); + date.roll!"months"(-28); + assert(date == Date(1999, 6, 6)); + } - { - auto date = Date(1999, 7, 31); - date.roll!"months"(1); - _assertPred!"=="(date, Date(1999, 8, 31)); - date.roll!"months"(1); - _assertPred!"=="(date, Date(1999, 10, 1)); - } + { + auto date = Date(1999, 5, 31); + date.roll!"months"(1); + assert(date == Date(1999, 7, 1)); + } - { - auto date = Date(1998, 8, 31); - date.roll!"months"(13); - _assertPred!"=="(date, Date(1998, 10, 1)); - date.roll!"months"(-13); - _assertPred!"=="(date, Date(1998, 9, 1)); - } + { + auto date = Date(1999, 5, 31); + date.roll!"months"(-1); + assert(date == Date(1999, 5, 1)); + } - { - auto date = Date(1997, 12, 31); - date.roll!"months"(13); - _assertPred!"=="(date, Date(1997, 1, 31)); - date.roll!"months"(-13); - _assertPred!"=="(date, Date(1997, 12, 31)); - } + { + auto date = Date(1999, 2, 28); + date.roll!"months"(12); + assert(date == Date(1999, 2, 28)); + } - { - auto date = Date(1997, 12, 31); - date.roll!"months"(14); - _assertPred!"=="(date, Date(1997, 3, 3)); - date.roll!"months"(-14); - _assertPred!"=="(date, Date(1997, 1, 3)); - } + { + auto date = Date(2000, 2, 29); + date.roll!"months"(12); + assert(date == Date(2000, 2, 29)); + } - { - auto date = Date(1998, 12, 31); - date.roll!"months"(14); - _assertPred!"=="(date, Date(1998, 3, 3)); - date.roll!"months"(-14); - _assertPred!"=="(date, Date(1998, 1, 3)); - } + { + auto date = Date(1999, 7, 31); + date.roll!"months"(1); + assert(date == Date(1999, 8, 31)); + date.roll!"months"(1); + assert(date == Date(1999, 10, 1)); + } - { - auto date = Date(1999, 12, 31); - date.roll!"months"(14); - _assertPred!"=="(date, Date(1999, 3, 3)); - date.roll!"months"(-14); - _assertPred!"=="(date, Date(1999, 1, 3)); - } + { + auto date = Date(1998, 8, 31); + date.roll!"months"(13); + assert(date == Date(1998, 10, 1)); + date.roll!"months"(-13); + assert(date == Date(1998, 9, 1)); + } - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(3); - _assertPred!"=="(date, Date(-1999, 10, 6)); - date.roll!"months"(-4); - _assertPred!"=="(date, Date(-1999, 6, 6)); - } + { + auto date = Date(1997, 12, 31); + date.roll!"months"(13); + assert(date == Date(1997, 1, 31)); + date.roll!"months"(-13); + assert(date == Date(1997, 12, 31)); + } - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(6); - _assertPred!"=="(date, Date(-1999, 1, 6)); - date.roll!"months"(-6); - _assertPred!"=="(date, Date(-1999, 7, 6)); - } + { + auto date = Date(1997, 12, 31); + date.roll!"months"(14); + assert(date == Date(1997, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(1997, 1, 3)); + } - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(-27); - _assertPred!"=="(date, Date(-1999, 4, 6)); - date.roll!"months"(28); - _assertPred!"=="(date, Date(-1999, 8, 6)); - } + { + auto date = Date(1998, 12, 31); + date.roll!"months"(14); + assert(date == Date(1998, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(1998, 1, 3)); + } - { - auto date = Date(-1999, 5, 31); - date.roll!"months"(1); - _assertPred!"=="(date, Date(-1999, 7, 1)); - } + { + auto date = Date(1999, 12, 31); + date.roll!"months"(14); + assert(date == Date(1999, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(1999, 1, 3)); + } - { - auto date = Date(-1999, 5, 31); - date.roll!"months"(-1); - _assertPred!"=="(date, Date(-1999, 5, 1)); - } + //Test B.C. + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(3); + assert(date == Date(-1999, 10, 6)); + date.roll!"months"(-4); + assert(date == Date(-1999, 6, 6)); + } - { - auto date = Date(-1999, 2, 28); - date.roll!"months"(-12); - _assertPred!"=="(date, Date(-1999, 2, 28)); - } + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(6); + assert(date == Date(-1999, 1, 6)); + date.roll!"months"(-6); + assert(date == Date(-1999, 7, 6)); + } - { - auto date = Date(-2000, 2, 29); - date.roll!"months"(-12); - _assertPred!"=="(date, Date(-2000, 2, 29)); - } + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(-27); + assert(date == Date(-1999, 4, 6)); + date.roll!"months"(28); + assert(date == Date(-1999, 8, 6)); + } - { - auto date = Date(-1999, 7, 31); - date.roll!"months"(1); - _assertPred!"=="(date, Date(-1999, 8, 31)); - date.roll!"months"(1); - _assertPred!"=="(date, Date(-1999, 10, 1)); - } + { + auto date = Date(-1999, 5, 31); + date.roll!"months"(1); + assert(date == Date(-1999, 7, 1)); + } - { - auto date = Date(-1998, 8, 31); - date.roll!"months"(13); - _assertPred!"=="(date, Date(-1998, 10, 1)); - date.roll!"months"(-13); - _assertPred!"=="(date, Date(-1998, 9, 1)); - } + { + auto date = Date(-1999, 5, 31); + date.roll!"months"(-1); + assert(date == Date(-1999, 5, 1)); + } - { - auto date = Date(-1997, 12, 31); - date.roll!"months"(13); - _assertPred!"=="(date, Date(-1997, 1, 31)); - date.roll!"months"(-13); - _assertPred!"=="(date, Date(-1997, 12, 31)); - } + { + auto date = Date(-1999, 2, 28); + date.roll!"months"(-12); + assert(date == Date(-1999, 2, 28)); + } - { - auto date = Date(-1997, 12, 31); - date.roll!"months"(14); - _assertPred!"=="(date, Date(-1997, 3, 3)); - date.roll!"months"(-14); - _assertPred!"=="(date, Date(-1997, 1, 3)); - } + { + auto date = Date(-2000, 2, 29); + date.roll!"months"(-12); + assert(date == Date(-2000, 2, 29)); + } - { - auto date = Date(-2002, 12, 31); - date.roll!"months"(14); - _assertPred!"=="(date, Date(-2002, 3, 3)); - date.roll!"months"(-14); - _assertPred!"=="(date, Date(-2002, 1, 3)); - } + { + auto date = Date(-1999, 7, 31); + date.roll!"months"(1); + assert(date == Date(-1999, 8, 31)); + date.roll!"months"(1); + assert(date == Date(-1999, 10, 1)); + } - { - auto date = Date(-2001, 12, 31); - date.roll!"months"(14); - _assertPred!"=="(date, Date(-2001, 3, 3)); - date.roll!"months"(-14); - _assertPred!"=="(date, Date(-2001, 1, 3)); - } + { + auto date = Date(-1998, 8, 31); + date.roll!"months"(13); + assert(date == Date(-1998, 10, 1)); + date.roll!"months"(-13); + assert(date == Date(-1998, 9, 1)); + } - //Test Both - { - auto date = Date(1, 1, 1); - date.roll!"months"(-1); - _assertPred!"=="(date, Date(1, 12, 1)); - date.roll!"months"(1); - _assertPred!"=="(date, Date(1, 1, 1)); - } + { + auto date = Date(-1997, 12, 31); + date.roll!"months"(13); + assert(date == Date(-1997, 1, 31)); + date.roll!"months"(-13); + assert(date == Date(-1997, 12, 31)); + } - { - auto date = Date(4, 1, 1); - date.roll!"months"(-48); - _assertPred!"=="(date, Date(4, 1, 1)); - date.roll!"months"(48); - _assertPred!"=="(date, Date(4, 1, 1)); - } + { + auto date = Date(-1997, 12, 31); + date.roll!"months"(14); + assert(date == Date(-1997, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(-1997, 1, 3)); + } - { - auto date = Date(4, 3, 31); - date.roll!"months"(-49); - _assertPred!"=="(date, Date(4, 3, 2)); - date.roll!"months"(49); - _assertPred!"=="(date, Date(4, 4, 2)); - } + { + auto date = Date(-2002, 12, 31); + date.roll!"months"(14); + assert(date == Date(-2002, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(-2002, 1, 3)); + } - { - auto date = Date(4, 3, 31); - date.roll!"months"(-85); - _assertPred!"=="(date, Date(4, 3, 2)); - date.roll!"months"(85); - _assertPred!"=="(date, Date(4, 4, 2)); - } + { + auto date = Date(-2001, 12, 31); + date.roll!"months"(14); + assert(date == Date(-2001, 3, 3)); + date.roll!"months"(-14); + assert(date == Date(-2001, 1, 3)); + } - { - auto date = Date(-1, 1, 1); - date.roll!"months"(-1); - _assertPred!"=="(date, Date(-1, 12, 1)); - date.roll!"months"(1); - _assertPred!"=="(date, Date(-1, 1, 1)); - } + //Test Both + { + auto date = Date(1, 1, 1); + date.roll!"months"(-1); + assert(date == Date(1, 12, 1)); + date.roll!"months"(1); + assert(date == Date(1, 1, 1)); + } - { - auto date = Date(-4, 1, 1); - date.roll!"months"(-48); - _assertPred!"=="(date, Date(-4, 1, 1)); - date.roll!"months"(48); - _assertPred!"=="(date, Date(-4, 1, 1)); - } + { + auto date = Date(4, 1, 1); + date.roll!"months"(-48); + assert(date == Date(4, 1, 1)); + date.roll!"months"(48); + assert(date == Date(4, 1, 1)); + } - { - auto date = Date(-4, 3, 31); - date.roll!"months"(-49); - _assertPred!"=="(date, Date(-4, 3, 2)); - date.roll!"months"(49); - _assertPred!"=="(date, Date(-4, 4, 2)); - } + { + auto date = Date(4, 3, 31); + date.roll!"months"(-49); + assert(date == Date(4, 3, 2)); + date.roll!"months"(49); + assert(date == Date(4, 4, 2)); + } - { - auto date = Date(-4, 3, 31); - date.roll!"months"(-85); - _assertPred!"=="(date, Date(-4, 3, 2)); - date.roll!"months"(85); - _assertPred!"=="(date, Date(-4, 4, 2)); - } + { + auto date = Date(4, 3, 31); + date.roll!"months"(-85); + assert(date == Date(4, 3, 2)); + date.roll!"months"(85); + assert(date == Date(4, 4, 2)); + } - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.roll!"months"(3))); - static assert(!__traits(compiles, idate.roll!"months"(3))); + { + auto date = Date(-1, 1, 1); + date.roll!"months"(-1); + assert(date == Date(-1, 12, 1)); + date.roll!"months"(1); + assert(date == Date(-1, 1, 1)); + } - //Verify Examples. - auto date1 = Date(2010, 1, 1); - date1.roll!"months"(1); - assert(date1 == Date(2010, 2, 1)); + { + auto date = Date(-4, 1, 1); + date.roll!"months"(-48); + assert(date == Date(-4, 1, 1)); + date.roll!"months"(48); + assert(date == Date(-4, 1, 1)); + } - auto date2 = Date(2010, 1, 1); - date2.roll!"months"(-1); - assert(date2 == Date(2010, 12, 1)); + { + auto date = Date(-4, 3, 31); + date.roll!"months"(-49); + assert(date == Date(-4, 3, 2)); + date.roll!"months"(49); + assert(date == Date(-4, 4, 2)); + } - auto date3 = Date(1999, 1, 29); - date3.roll!"months"(1); - assert(date3 == Date(1999, 3, 1)); + { + auto date = Date(-4, 3, 31); + date.roll!"months"(-85); + assert(date == Date(-4, 3, 2)); + date.roll!"months"(85); + assert(date == Date(-4, 4, 2)); + } - auto date4 = Date(1999, 1, 29); - date4.roll!"months"(1, AllowDayOverflow.no); - assert(date4 == Date(1999, 2, 28)); + { + auto date = Date(-3, 3, 31); + date.roll!"months"(85).roll!"months"(-83); + assert(date == Date(-3, 6, 1)); } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.roll!"months"(3))); + static assert(!__traits(compiles, idate.roll!"months"(3))); } //Test roll!"months"() with AllowDayOverlow.no unittest { - version(testStdDateTime) + //Test A.D. { - //Test A.D. - { - auto date = Date(1999, 7, 6); - date.roll!"months"(3, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 10, 6)); - date.roll!"months"(-4, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 6, 6)); - } + auto date = Date(1999, 7, 6); + date.roll!"months"(3, AllowDayOverflow.no); + assert(date == Date(1999, 10, 6)); + date.roll!"months"(-4, AllowDayOverflow.no); + assert(date == Date(1999, 6, 6)); + } - { - auto date = Date(1999, 7, 6); - date.roll!"months"(6, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 1, 6)); - date.roll!"months"(-6, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 7, 6)); - } + { + auto date = Date(1999, 7, 6); + date.roll!"months"(6, AllowDayOverflow.no); + assert(date == Date(1999, 1, 6)); + date.roll!"months"(-6, AllowDayOverflow.no); + assert(date == Date(1999, 7, 6)); + } - { - auto date = Date(1999, 7, 6); - date.roll!"months"(27, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 10, 6)); - date.roll!"months"(-28, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 6, 6)); - } + { + auto date = Date(1999, 7, 6); + date.roll!"months"(27, AllowDayOverflow.no); + assert(date == Date(1999, 10, 6)); + date.roll!"months"(-28, AllowDayOverflow.no); + assert(date == Date(1999, 6, 6)); + } - { - auto date = Date(1999, 5, 31); - date.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 6, 30)); - } + { + auto date = Date(1999, 5, 31); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 6, 30)); + } - { - auto date = Date(1999, 5, 31); - date.roll!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 4, 30)); - } + { + auto date = Date(1999, 5, 31); + date.roll!"months"(-1, AllowDayOverflow.no); + assert(date == Date(1999, 4, 30)); + } - { - auto date = Date(1999, 2, 28); - date.roll!"months"(12, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 2, 28)); - } + { + auto date = Date(1999, 2, 28); + date.roll!"months"(12, AllowDayOverflow.no); + assert(date == Date(1999, 2, 28)); + } - { - auto date = Date(2000, 2, 29); - date.roll!"months"(12, AllowDayOverflow.no); - _assertPred!"=="(date, Date(2000, 2, 29)); - } + { + auto date = Date(2000, 2, 29); + date.roll!"months"(12, AllowDayOverflow.no); + assert(date == Date(2000, 2, 29)); + } - { - auto date = Date(1999, 7, 31); - date.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 8, 31)); - date.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 9, 30)); - } + { + auto date = Date(1999, 7, 31); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 8, 31)); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(1999, 9, 30)); + } - { - auto date = Date(1998, 8, 31); - date.roll!"months"(13, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1998, 9, 30)); - date.roll!"months"(-13, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1998, 8, 30)); - } + { + auto date = Date(1998, 8, 31); + date.roll!"months"(13, AllowDayOverflow.no); + assert(date == Date(1998, 9, 30)); + date.roll!"months"(-13, AllowDayOverflow.no); + assert(date == Date(1998, 8, 30)); + } - { - auto date = Date(1997, 12, 31); - date.roll!"months"(13, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1997, 1, 31)); - date.roll!"months"(-13, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1997, 12, 31)); - } + { + auto date = Date(1997, 12, 31); + date.roll!"months"(13, AllowDayOverflow.no); + assert(date == Date(1997, 1, 31)); + date.roll!"months"(-13, AllowDayOverflow.no); + assert(date == Date(1997, 12, 31)); + } - { - auto date = Date(1997, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1997, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1997, 12, 28)); - } + { + auto date = Date(1997, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(1997, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1997, 12, 28)); + } - { - auto date = Date(1998, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1998, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1998, 12, 28)); - } + { + auto date = Date(1998, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(1998, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1998, 12, 28)); + } - { - auto date = Date(1999, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1999, 12, 28)); - } + { + auto date = Date(1999, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(1999, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(1999, 12, 28)); + } - //Test B.C. - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(3, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 10, 6)); - date.roll!"months"(-4, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 6, 6)); - } + //Test B.C. + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(3, AllowDayOverflow.no); + assert(date == Date(-1999, 10, 6)); + date.roll!"months"(-4, AllowDayOverflow.no); + assert(date == Date(-1999, 6, 6)); + } - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(6, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 1, 6)); - date.roll!"months"(-6, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 7, 6)); - } + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(6, AllowDayOverflow.no); + assert(date == Date(-1999, 1, 6)); + date.roll!"months"(-6, AllowDayOverflow.no); + assert(date == Date(-1999, 7, 6)); + } + + { + auto date = Date(-1999, 7, 6); + date.roll!"months"(-27, AllowDayOverflow.no); + assert(date == Date(-1999, 4, 6)); + date.roll!"months"(28, AllowDayOverflow.no); + assert(date == Date(-1999, 8, 6)); + } - { - auto date = Date(-1999, 7, 6); - date.roll!"months"(-27, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 4, 6)); - date.roll!"months"(28, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 8, 6)); - } + { + auto date = Date(-1999, 5, 31); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 6, 30)); + } - { - auto date = Date(-1999, 5, 31); - date.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 6, 30)); - } + { + auto date = Date(-1999, 5, 31); + date.roll!"months"(-1, AllowDayOverflow.no); + assert(date == Date(-1999, 4, 30)); + } - { - auto date = Date(-1999, 5, 31); - date.roll!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 4, 30)); - } + { + auto date = Date(-1999, 2, 28); + date.roll!"months"(-12, AllowDayOverflow.no); + assert(date == Date(-1999, 2, 28)); + } - { - auto date = Date(-1999, 2, 28); - date.roll!"months"(-12, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 2, 28)); - } + { + auto date = Date(-2000, 2, 29); + date.roll!"months"(-12, AllowDayOverflow.no); + assert(date == Date(-2000, 2, 29)); + } - { - auto date = Date(-2000, 2, 29); - date.roll!"months"(-12, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-2000, 2, 29)); - } + { + auto date = Date(-1999, 7, 31); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 8, 31)); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1999, 9, 30)); + } - { - auto date = Date(-1999, 7, 31); - date.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 8, 31)); - date.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1999, 9, 30)); - } + { + auto date = Date(-1998, 8, 31); + date.roll!"months"(13, AllowDayOverflow.no); + assert(date == Date(-1998, 9, 30)); + date.roll!"months"(-13, AllowDayOverflow.no); + assert(date == Date(-1998, 8, 30)); + } - { - auto date = Date(-1998, 8, 31); - date.roll!"months"(13, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1998, 9, 30)); - date.roll!"months"(-13, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1998, 8, 30)); - } + { + auto date = Date(-1997, 12, 31); + date.roll!"months"(13, AllowDayOverflow.no); + assert(date == Date(-1997, 1, 31)); + date.roll!"months"(-13, AllowDayOverflow.no); + assert(date == Date(-1997, 12, 31)); + } - { - auto date = Date(-1997, 12, 31); - date.roll!"months"(13, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1997, 1, 31)); - date.roll!"months"(-13, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1997, 12, 31)); - } + { + auto date = Date(-1997, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(-1997, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-1997, 12, 28)); + } - { - auto date = Date(-1997, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1997, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1997, 12, 28)); - } + { + auto date = Date(-2002, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(-2002, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-2002, 12, 28)); + } - { - auto date = Date(-2002, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-2002, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-2002, 12, 28)); - } + { + auto date = Date(-2001, 12, 31); + date.roll!"months"(14, AllowDayOverflow.no); + assert(date == Date(-2001, 2, 28)); + date.roll!"months"(-14, AllowDayOverflow.no); + assert(date == Date(-2001, 12, 28)); + } - { - auto date = Date(-2001, 12, 31); - date.roll!"months"(14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-2001, 2, 28)); - date.roll!"months"(-14, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-2001, 12, 28)); - } + //Test Both + { + auto date = Date(1, 1, 1); + date.roll!"months"(-1, AllowDayOverflow.no); + assert(date == Date(1, 12, 1)); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(1, 1, 1)); + } - //Test Both - { - auto date = Date(1, 1, 1); - date.roll!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1, 12, 1)); - date.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(1, 1, 1)); - } + { + auto date = Date(4, 1, 1); + date.roll!"months"(-48, AllowDayOverflow.no); + assert(date == Date(4, 1, 1)); + date.roll!"months"(48, AllowDayOverflow.no); + assert(date == Date(4, 1, 1)); + } - { - auto date = Date(4, 1, 1); - date.roll!"months"(-48, AllowDayOverflow.no); - _assertPred!"=="(date, Date(4, 1, 1)); - date.roll!"months"(48, AllowDayOverflow.no); - _assertPred!"=="(date, Date(4, 1, 1)); - } + { + auto date = Date(4, 3, 31); + date.roll!"months"(-49, AllowDayOverflow.no); + assert(date == Date(4, 2, 29)); + date.roll!"months"(49, AllowDayOverflow.no); + assert(date == Date(4, 3, 29)); + } - { - auto date = Date(4, 3, 31); - date.roll!"months"(-49, AllowDayOverflow.no); - _assertPred!"=="(date, Date(4, 2, 29)); - date.roll!"months"(49, AllowDayOverflow.no); - _assertPred!"=="(date, Date(4, 3, 29)); - } + { + auto date = Date(4, 3, 31); + date.roll!"months"(-85, AllowDayOverflow.no); + assert(date == Date(4, 2, 29)); + date.roll!"months"(85, AllowDayOverflow.no); + assert(date == Date(4, 3, 29)); + } - { - auto date = Date(4, 3, 31); - date.roll!"months"(-85, AllowDayOverflow.no); - _assertPred!"=="(date, Date(4, 2, 29)); - date.roll!"months"(85, AllowDayOverflow.no); - _assertPred!"=="(date, Date(4, 3, 29)); - } + { + auto date = Date(-1, 1, 1); + date.roll!"months"(-1, AllowDayOverflow.no); + assert(date == Date(-1, 12, 1)); + date.roll!"months"(1, AllowDayOverflow.no); + assert(date == Date(-1, 1, 1)); + } - { - auto date = Date(-1, 1, 1); - date.roll!"months"(-1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1, 12, 1)); - date.roll!"months"(1, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-1, 1, 1)); - } + { + auto date = Date(-4, 1, 1); + date.roll!"months"(-48, AllowDayOverflow.no); + assert(date == Date(-4, 1, 1)); + date.roll!"months"(48, AllowDayOverflow.no); + assert(date == Date(-4, 1, 1)); + } - { - auto date = Date(-4, 1, 1); - date.roll!"months"(-48, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-4, 1, 1)); - date.roll!"months"(48, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-4, 1, 1)); - } + { + auto date = Date(-4, 3, 31); + date.roll!"months"(-49, AllowDayOverflow.no); + assert(date == Date(-4, 2, 29)); + date.roll!"months"(49, AllowDayOverflow.no); + assert(date == Date(-4, 3, 29)); + } - { - auto date = Date(-4, 3, 31); - date.roll!"months"(-49, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-4, 2, 29)); - date.roll!"months"(49, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-4, 3, 29)); - } + { + auto date = Date(-4, 3, 31); + date.roll!"months"(-85, AllowDayOverflow.no); + assert(date == Date(-4, 2, 29)); + date.roll!"months"(85, AllowDayOverflow.no); + assert(date == Date(-4, 3, 29)); + } - { - auto date = Date(-4, 3, 31); - date.roll!"months"(-85, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-4, 2, 29)); - date.roll!"months"(85, AllowDayOverflow.no); - _assertPred!"=="(date, Date(-4, 3, 29)); - } + { + auto date = Date(-3, 3, 31); + date.roll!"months"(85, AllowDayOverflow.no).roll!"months"(-83, AllowDayOverflow.no); + assert(date == Date(-3, 5, 30)); } } @@ -11220,19 +10663,8 @@ assert(d6 == Date(2001, 2, 28)); Params: units = The units to add. Must be $(D "days"). days = The number of days to add to this $(LREF Date). - - Examples: --------------------- -auto d = Date(2010, 1, 1); -d.roll!"days"(1); -assert(d == Date(2010, 1, 2)); -d.roll!"days"(365); -assert(d == Date(2010, 1, 26)); -d.roll!"days"(-32); -assert(d == Date(2010, 1, 25)); --------------------- +/ - /+ref Date+/ void roll(string units)(long days) pure nothrow + ref Date roll(string units)(long days) @safe pure nothrow if(units == "days") { immutable limit = maxDay(_year, _month); @@ -11248,218 +10680,210 @@ assert(d == Date(2010, 1, 25)); newDay -= limit; _day = cast(ubyte)newDay; + return this; } - //Verify Examples. + /// unittest { - version(testStdDateTime) - { - auto d = Date(2010, 1, 1); - d.roll!"days"(1); - assert(d == Date(2010, 1, 2)); - d.roll!"days"(365); - assert(d == Date(2010, 1, 26)); - d.roll!"days"(-32); - assert(d == Date(2010, 1, 25)); - } + auto d = Date(2010, 1, 1); + d.roll!"days"(1); + assert(d == Date(2010, 1, 2)); + d.roll!"days"(365); + assert(d == Date(2010, 1, 26)); + d.roll!"days"(-32); + assert(d == Date(2010, 1, 25)); } unittest { - version(testStdDateTime) + //Test A.D. { - //Test A.D. - { - auto date = Date(1999, 2, 28); - date.roll!"days"(1); - _assertPred!"=="(date, Date(1999, 2, 1)); - date.roll!"days"(-1); - _assertPred!"=="(date, Date(1999, 2, 28)); - } - - { - auto date = Date(2000, 2, 28); - date.roll!"days"(1); - _assertPred!"=="(date, Date(2000, 2, 29)); - date.roll!"days"(1); - _assertPred!"=="(date, Date(2000, 2, 1)); - date.roll!"days"(-1); - _assertPred!"=="(date, Date(2000, 2, 29)); - } - - { - auto date = Date(1999, 6, 30); - date.roll!"days"(1); - _assertPred!"=="(date, Date(1999, 6, 1)); - date.roll!"days"(-1); - _assertPred!"=="(date, Date(1999, 6, 30)); - } - - { - auto date = Date(1999, 7, 31); - date.roll!"days"(1); - _assertPred!"=="(date, Date(1999, 7, 1)); - date.roll!"days"(-1); - _assertPred!"=="(date, Date(1999, 7, 31)); - } + auto date = Date(1999, 2, 28); + date.roll!"days"(1); + assert(date == Date(1999, 2, 1)); + date.roll!"days"(-1); + assert(date == Date(1999, 2, 28)); + } - { - auto date = Date(1999, 1, 1); - date.roll!"days"(-1); - _assertPred!"=="(date, Date(1999, 1, 31)); - date.roll!"days"(1); - _assertPred!"=="(date, Date(1999, 1, 1)); - } + { + auto date = Date(2000, 2, 28); + date.roll!"days"(1); + assert(date == Date(2000, 2, 29)); + date.roll!"days"(1); + assert(date == Date(2000, 2, 1)); + date.roll!"days"(-1); + assert(date == Date(2000, 2, 29)); + } - { - auto date = Date(1999, 7, 6); - date.roll!"days"(9); - _assertPred!"=="(date, Date(1999, 7, 15)); - date.roll!"days"(-11); - _assertPred!"=="(date, Date(1999, 7, 4)); - date.roll!"days"(30); - _assertPred!"=="(date, Date(1999, 7, 3)); - date.roll!"days"(-3); - _assertPred!"=="(date, Date(1999, 7, 31)); - } + { + auto date = Date(1999, 6, 30); + date.roll!"days"(1); + assert(date == Date(1999, 6, 1)); + date.roll!"days"(-1); + assert(date == Date(1999, 6, 30)); + } - { - auto date = Date(1999, 7, 6); - date.roll!"days"(365); - _assertPred!"=="(date, Date(1999, 7, 30)); - date.roll!"days"(-365); - _assertPred!"=="(date, Date(1999, 7, 6)); - date.roll!"days"(366); - _assertPred!"=="(date, Date(1999, 7, 31)); - date.roll!"days"(730); - _assertPred!"=="(date, Date(1999, 7, 17)); - date.roll!"days"(-1096); - _assertPred!"=="(date, Date(1999, 7, 6)); - } + { + auto date = Date(1999, 7, 31); + date.roll!"days"(1); + assert(date == Date(1999, 7, 1)); + date.roll!"days"(-1); + assert(date == Date(1999, 7, 31)); + } - { - auto date = Date(1999, 2, 6); - date.roll!"days"(365); - _assertPred!"=="(date, Date(1999, 2, 7)); - date.roll!"days"(-365); - _assertPred!"=="(date, Date(1999, 2, 6)); - date.roll!"days"(366); - _assertPred!"=="(date, Date(1999, 2, 8)); - date.roll!"days"(730); - _assertPred!"=="(date, Date(1999, 2, 10)); - date.roll!"days"(-1096); - _assertPred!"=="(date, Date(1999, 2, 6)); - } + { + auto date = Date(1999, 1, 1); + date.roll!"days"(-1); + assert(date == Date(1999, 1, 31)); + date.roll!"days"(1); + assert(date == Date(1999, 1, 1)); + } - //Test B.C. - { - auto date = Date(-1999, 2, 28); - date.roll!"days"(1); - _assertPred!"=="(date, Date(-1999, 2, 1)); - date.roll!"days"(-1); - _assertPred!"=="(date, Date(-1999, 2, 28)); - } + { + auto date = Date(1999, 7, 6); + date.roll!"days"(9); + assert(date == Date(1999, 7, 15)); + date.roll!"days"(-11); + assert(date == Date(1999, 7, 4)); + date.roll!"days"(30); + assert(date == Date(1999, 7, 3)); + date.roll!"days"(-3); + assert(date == Date(1999, 7, 31)); + } - { - auto date = Date(-2000, 2, 28); - date.roll!"days"(1); - _assertPred!"=="(date, Date(-2000, 2, 29)); - date.roll!"days"(1); - _assertPred!"=="(date, Date(-2000, 2, 1)); - date.roll!"days"(-1); - _assertPred!"=="(date, Date(-2000, 2, 29)); - } + { + auto date = Date(1999, 7, 6); + date.roll!"days"(365); + assert(date == Date(1999, 7, 30)); + date.roll!"days"(-365); + assert(date == Date(1999, 7, 6)); + date.roll!"days"(366); + assert(date == Date(1999, 7, 31)); + date.roll!"days"(730); + assert(date == Date(1999, 7, 17)); + date.roll!"days"(-1096); + assert(date == Date(1999, 7, 6)); + } - { - auto date = Date(-1999, 6, 30); - date.roll!"days"(1); - _assertPred!"=="(date, Date(-1999, 6, 1)); - date.roll!"days"(-1); - _assertPred!"=="(date, Date(-1999, 6, 30)); - } + { + auto date = Date(1999, 2, 6); + date.roll!"days"(365); + assert(date == Date(1999, 2, 7)); + date.roll!"days"(-365); + assert(date == Date(1999, 2, 6)); + date.roll!"days"(366); + assert(date == Date(1999, 2, 8)); + date.roll!"days"(730); + assert(date == Date(1999, 2, 10)); + date.roll!"days"(-1096); + assert(date == Date(1999, 2, 6)); + } - { - auto date = Date(-1999, 7, 31); - date.roll!"days"(1); - _assertPred!"=="(date, Date(-1999, 7, 1)); - date.roll!"days"(-1); - _assertPred!"=="(date, Date(-1999, 7, 31)); - } + //Test B.C. + { + auto date = Date(-1999, 2, 28); + date.roll!"days"(1); + assert(date == Date(-1999, 2, 1)); + date.roll!"days"(-1); + assert(date == Date(-1999, 2, 28)); + } - { - auto date = Date(-1999, 1, 1); - date.roll!"days"(-1); - _assertPred!"=="(date, Date(-1999, 1, 31)); - date.roll!"days"(1); - _assertPred!"=="(date, Date(-1999, 1, 1)); - } + { + auto date = Date(-2000, 2, 28); + date.roll!"days"(1); + assert(date == Date(-2000, 2, 29)); + date.roll!"days"(1); + assert(date == Date(-2000, 2, 1)); + date.roll!"days"(-1); + assert(date == Date(-2000, 2, 29)); + } - { - auto date = Date(-1999, 7, 6); - date.roll!"days"(9); - _assertPred!"=="(date, Date(-1999, 7, 15)); - date.roll!"days"(-11); - _assertPred!"=="(date, Date(-1999, 7, 4)); - date.roll!"days"(30); - _assertPred!"=="(date, Date(-1999, 7, 3)); - date.roll!"days"(-3); - _assertPred!"=="(date, Date(-1999, 7, 31)); - } + { + auto date = Date(-1999, 6, 30); + date.roll!"days"(1); + assert(date == Date(-1999, 6, 1)); + date.roll!"days"(-1); + assert(date == Date(-1999, 6, 30)); + } - { - auto date = Date(-1999, 7, 6); - date.roll!"days"(365); - _assertPred!"=="(date, Date(-1999, 7, 30)); - date.roll!"days"(-365); - _assertPred!"=="(date, Date(-1999, 7, 6)); - date.roll!"days"(366); - _assertPred!"=="(date, Date(-1999, 7, 31)); - date.roll!"days"(730); - _assertPred!"=="(date, Date(-1999, 7, 17)); - date.roll!"days"(-1096); - _assertPred!"=="(date, Date(-1999, 7, 6)); - } + { + auto date = Date(-1999, 7, 31); + date.roll!"days"(1); + assert(date == Date(-1999, 7, 1)); + date.roll!"days"(-1); + assert(date == Date(-1999, 7, 31)); + } - //Test Both - { - auto date = Date(1, 7, 6); - date.roll!"days"(-365); - _assertPred!"=="(date, Date(1, 7, 13)); - date.roll!"days"(365); - _assertPred!"=="(date, Date(1, 7, 6)); - date.roll!"days"(-731); - _assertPred!"=="(date, Date(1, 7, 19)); - date.roll!"days"(730); - _assertPred!"=="(date, Date(1, 7, 5)); - } + { + auto date = Date(-1999, 1, 1); + date.roll!"days"(-1); + assert(date == Date(-1999, 1, 31)); + date.roll!"days"(1); + assert(date == Date(-1999, 1, 1)); + } - { - auto date = Date(0, 7, 6); - date.roll!"days"(-365); - _assertPred!"=="(date, Date(0, 7, 13)); - date.roll!"days"(365); - _assertPred!"=="(date, Date(0, 7, 6)); - date.roll!"days"(-731); - _assertPred!"=="(date, Date(0, 7, 19)); - date.roll!"days"(730); - _assertPred!"=="(date, Date(0, 7, 5)); - } + { + auto date = Date(-1999, 7, 6); + date.roll!"days"(9); + assert(date == Date(-1999, 7, 15)); + date.roll!"days"(-11); + assert(date == Date(-1999, 7, 4)); + date.roll!"days"(30); + assert(date == Date(-1999, 7, 3)); + date.roll!"days"(-3); + assert(date == Date(-1999, 7, 31)); + } - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.roll!"days"(12))); - static assert(!__traits(compiles, idate.roll!"days"(12))); + { + auto date = Date(-1999, 7, 6); + date.roll!"days"(365); + assert(date == Date(-1999, 7, 30)); + date.roll!"days"(-365); + assert(date == Date(-1999, 7, 6)); + date.roll!"days"(366); + assert(date == Date(-1999, 7, 31)); + date.roll!"days"(730); + assert(date == Date(-1999, 7, 17)); + date.roll!"days"(-1096); + assert(date == Date(-1999, 7, 6)); + } + + //Test Both + { + auto date = Date(1, 7, 6); + date.roll!"days"(-365); + assert(date == Date(1, 7, 13)); + date.roll!"days"(365); + assert(date == Date(1, 7, 6)); + date.roll!"days"(-731); + assert(date == Date(1, 7, 19)); + date.roll!"days"(730); + assert(date == Date(1, 7, 5)); + } - //Verify Examples. - auto date = Date(2010, 1, 1); - date.roll!"days"(1); - assert(date == Date(2010, 1, 2)); + { + auto date = Date(0, 7, 6); + date.roll!"days"(-365); + assert(date == Date(0, 7, 13)); date.roll!"days"(365); - assert(date == Date(2010, 1, 26)); - date.roll!"days"(-32); - assert(date == Date(2010, 1, 25)); + assert(date == Date(0, 7, 6)); + date.roll!"days"(-731); + assert(date == Date(0, 7, 19)); + date.roll!"days"(730); + assert(date == Date(0, 7, 5)); + } + + { + auto date = Date(0, 7, 6); + date.roll!"days"(-365).roll!"days"(362).roll!"days"(-12).roll!"days"(730); + assert(date == Date(0, 7, 8)); } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.roll!"days"(12))); + static assert(!__traits(compiles, idate.roll!"days"(12))); } @@ -11477,7 +10901,7 @@ assert(d == Date(2010, 1, 25)); Params: duration = The duration to add to or subtract from this $(LREF Date). +/ - Date opBinary(string op, D)(in D duration) const pure nothrow + Date opBinary(string op, D)(in D duration) @safe const pure nothrow if((op == "+" || op == "-") && (is(Unqual!D == Duration) || is(Unqual!D == TickDuration))) @@ -11489,89 +10913,75 @@ assert(d == Date(2010, 1, 25)); else static if(is(Unqual!D == TickDuration)) immutable days = convert!("hnsecs", "days")(duration.hnsecs); - //Ideally, this would just be - //return retval.addDays(unaryFun!(op ~ "a")(days)); - //But there isn't currently a pure version of unaryFun!(). - - static if(op == "+") - immutable signedDays = days; - else static if(op == "-") - immutable signedDays = -days; - else - static assert(0); - - return retval.addDays(signedDays); + mixin(format("return retval._addDays(%sdays);", op)); } unittest { - version(testStdDateTime) - { - auto date = Date(1999, 7, 6); - - _assertPred!"=="(date + dur!"weeks"(7), Date(1999, 8, 24)); - _assertPred!"=="(date + dur!"weeks"(-7), Date(1999, 5, 18)); - _assertPred!"=="(date + dur!"days"(7), Date(1999, 7, 13)); - _assertPred!"=="(date + dur!"days"(-7), Date(1999, 6, 29)); - - _assertPred!"=="(date + dur!"hours"(24), Date(1999, 7, 7)); - _assertPred!"=="(date + dur!"hours"(-24), Date(1999, 7, 5)); - _assertPred!"=="(date + dur!"minutes"(1440), Date(1999, 7, 7)); - _assertPred!"=="(date + dur!"minutes"(-1440), Date(1999, 7, 5)); - _assertPred!"=="(date + dur!"seconds"(86_400), Date(1999, 7, 7)); - _assertPred!"=="(date + dur!"seconds"(-86_400), Date(1999, 7, 5)); - _assertPred!"=="(date + dur!"msecs"(86_400_000), Date(1999, 7, 7)); - _assertPred!"=="(date + dur!"msecs"(-86_400_000), Date(1999, 7, 5)); - _assertPred!"=="(date + dur!"usecs"(86_400_000_000), Date(1999, 7, 7)); - _assertPred!"=="(date + dur!"usecs"(-86_400_000_000), Date(1999, 7, 5)); - _assertPred!"=="(date + dur!"hnsecs"(864_000_000_000), Date(1999, 7, 7)); - _assertPred!"=="(date + dur!"hnsecs"(-864_000_000_000), Date(1999, 7, 5)); - - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if(TickDuration.ticksPerSec == 1_000_000) - { - _assertPred!"=="(date + TickDuration.from!"usecs"(86_400_000_000), Date(1999, 7, 7)); - _assertPred!"=="(date + TickDuration.from!"usecs"(-86_400_000_000), Date(1999, 7, 5)); - } - - _assertPred!"=="(date - dur!"weeks"(-7), Date(1999, 8, 24)); - _assertPred!"=="(date - dur!"weeks"(7), Date(1999, 5, 18)); - _assertPred!"=="(date - dur!"days"(-7), Date(1999, 7, 13)); - _assertPred!"=="(date - dur!"days"(7), Date(1999, 6, 29)); - - _assertPred!"=="(date - dur!"hours"(-24), Date(1999, 7, 7)); - _assertPred!"=="(date - dur!"hours"(24), Date(1999, 7, 5)); - _assertPred!"=="(date - dur!"minutes"(-1440), Date(1999, 7, 7)); - _assertPred!"=="(date - dur!"minutes"(1440), Date(1999, 7, 5)); - _assertPred!"=="(date - dur!"seconds"(-86_400), Date(1999, 7, 7)); - _assertPred!"=="(date - dur!"seconds"(86_400), Date(1999, 7, 5)); - _assertPred!"=="(date - dur!"msecs"(-86_400_000), Date(1999, 7, 7)); - _assertPred!"=="(date - dur!"msecs"(86_400_000), Date(1999, 7, 5)); - _assertPred!"=="(date - dur!"usecs"(-86_400_000_000), Date(1999, 7, 7)); - _assertPred!"=="(date - dur!"usecs"(86_400_000_000), Date(1999, 7, 5)); - _assertPred!"=="(date - dur!"hnsecs"(-864_000_000_000), Date(1999, 7, 7)); - _assertPred!"=="(date - dur!"hnsecs"(864_000_000_000), Date(1999, 7, 5)); - - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if(TickDuration.ticksPerSec == 1_000_000) - { - _assertPred!"=="(date - TickDuration.from!"usecs"(-86_400_000_000), Date(1999, 7, 7)); - _assertPred!"=="(date - TickDuration.from!"usecs"(86_400_000_000), Date(1999, 7, 5)); - } + auto date = Date(1999, 7, 6); - auto duration = dur!"days"(12); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, date + duration)); - static assert(__traits(compiles, cdate + duration)); - static assert(__traits(compiles, idate + duration)); + assert(date + dur!"weeks"(7) == Date(1999, 8, 24)); + assert(date + dur!"weeks"(-7) == Date(1999, 5, 18)); + assert(date + dur!"days"(7) == Date(1999, 7, 13)); + assert(date + dur!"days"(-7) == Date(1999, 6, 29)); + + assert(date + dur!"hours"(24) == Date(1999, 7, 7)); + assert(date + dur!"hours"(-24) == Date(1999, 7, 5)); + assert(date + dur!"minutes"(1440) == Date(1999, 7, 7)); + assert(date + dur!"minutes"(-1440) == Date(1999, 7, 5)); + assert(date + dur!"seconds"(86_400) == Date(1999, 7, 7)); + assert(date + dur!"seconds"(-86_400) == Date(1999, 7, 5)); + assert(date + dur!"msecs"(86_400_000) == Date(1999, 7, 7)); + assert(date + dur!"msecs"(-86_400_000) == Date(1999, 7, 5)); + assert(date + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7)); + assert(date + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); + assert(date + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7)); + assert(date + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5)); + + //This probably only runs in cases where gettimeofday() is used, but it's + //hard to do this test correctly with variable ticksPerSec. + if(TickDuration.ticksPerSec == 1_000_000) + { + assert(date + TickDuration.from!"usecs"(86_400_000_000) == Date(1999, 7, 7)); + assert(date + TickDuration.from!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); + } + + assert(date - dur!"weeks"(-7) == Date(1999, 8, 24)); + assert(date - dur!"weeks"(7) == Date(1999, 5, 18)); + assert(date - dur!"days"(-7) == Date(1999, 7, 13)); + assert(date - dur!"days"(7) == Date(1999, 6, 29)); + + assert(date - dur!"hours"(-24) == Date(1999, 7, 7)); + assert(date - dur!"hours"(24) == Date(1999, 7, 5)); + assert(date - dur!"minutes"(-1440) == Date(1999, 7, 7)); + assert(date - dur!"minutes"(1440) == Date(1999, 7, 5)); + assert(date - dur!"seconds"(-86_400) == Date(1999, 7, 7)); + assert(date - dur!"seconds"(86_400) == Date(1999, 7, 5)); + assert(date - dur!"msecs"(-86_400_000) == Date(1999, 7, 7)); + assert(date - dur!"msecs"(86_400_000) == Date(1999, 7, 5)); + assert(date - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); + assert(date - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5)); + assert(date - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7)); + assert(date - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5)); + + //This probably only runs in cases where gettimeofday() is used, but it's + //hard to do this test correctly with variable ticksPerSec. + if(TickDuration.ticksPerSec == 1_000_000) + { + assert(date - TickDuration.from!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); + assert(date - TickDuration.from!"usecs"(86_400_000_000) == Date(1999, 7, 5)); + } + + auto duration = dur!"days"(12); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, date + duration)); + static assert(__traits(compiles, cdate + duration)); + static assert(__traits(compiles, idate + duration)); - static assert(__traits(compiles, date - duration)); - static assert(__traits(compiles, cdate - duration)); - static assert(__traits(compiles, idate - duration)); - } + static assert(__traits(compiles, date - duration)); + static assert(__traits(compiles, cdate - duration)); + static assert(__traits(compiles, idate - duration)); } @@ -11589,7 +10999,7 @@ assert(d == Date(2010, 1, 25)); Params: duration = The duration to add to or subtract from this $(LREF Date). +/ - /+ref+/ Date opOpAssign(string op, D)(in D duration) pure nothrow + ref Date opOpAssign(string op, D)(in D duration) @safe pure nothrow if((op == "+" || op == "-") && (is(Unqual!D == Duration) || is(Unqual!D == TickDuration))) @@ -11599,72 +11009,64 @@ assert(d == Date(2010, 1, 25)); else static if(is(Unqual!D == TickDuration)) immutable days = convert!("hnsecs", "days")(duration.hnsecs); - //Ideally, this would just be - //return addDays(unaryFun!(op ~ "a")(days)); - //But there isn't currently a pure version of unaryFun!(). + mixin(format("return _addDays(%sdays);", op)); + } - static if(op == "+") - immutable signedDays = days; - else static if(op == "-") - immutable signedDays = -days; - else - static assert(0); - - return addDays(signedDays); - } - - unittest - { - version(testStdDateTime) - { - _assertPred!"+="(Date(1999, 7, 6), dur!"weeks"(7), Date(1999, 8, 24)); - _assertPred!"+="(Date(1999, 7, 6), dur!"weeks"(-7), Date(1999, 5, 18)); - _assertPred!"+="(Date(1999, 7, 6), dur!"days"(7), Date(1999, 7, 13)); - _assertPred!"+="(Date(1999, 7, 6), dur!"days"(-7), Date(1999, 6, 29)); - - _assertPred!"+="(Date(1999, 7, 6), dur!"hours"(24), Date(1999, 7, 7)); - _assertPred!"+="(Date(1999, 7, 6), dur!"hours"(-24), Date(1999, 7, 5)); - _assertPred!"+="(Date(1999, 7, 6), dur!"minutes"(1440), Date(1999, 7, 7)); - _assertPred!"+="(Date(1999, 7, 6), dur!"minutes"(-1440), Date(1999, 7, 5)); - _assertPred!"+="(Date(1999, 7, 6), dur!"seconds"(86_400), Date(1999, 7, 7)); - _assertPred!"+="(Date(1999, 7, 6), dur!"seconds"(-86_400), Date(1999, 7, 5)); - _assertPred!"+="(Date(1999, 7, 6), dur!"msecs"(86_400_000), Date(1999, 7, 7)); - _assertPred!"+="(Date(1999, 7, 6), dur!"msecs"(-86_400_000), Date(1999, 7, 5)); - _assertPred!"+="(Date(1999, 7, 6), dur!"usecs"(86_400_000_000), Date(1999, 7, 7)); - _assertPred!"+="(Date(1999, 7, 6), dur!"usecs"(-86_400_000_000), Date(1999, 7, 5)); - _assertPred!"+="(Date(1999, 7, 6), dur!"hnsecs"(864_000_000_000), Date(1999, 7, 7)); - _assertPred!"+="(Date(1999, 7, 6), dur!"hnsecs"(-864_000_000_000), Date(1999, 7, 5)); - - _assertPred!"-="(Date(1999, 7, 6), dur!"weeks"(-7), Date(1999, 8, 24)); - _assertPred!"-="(Date(1999, 7, 6), dur!"weeks"(7), Date(1999, 5, 18)); - _assertPred!"-="(Date(1999, 7, 6), dur!"days"(-7), Date(1999, 7, 13)); - _assertPred!"-="(Date(1999, 7, 6), dur!"days"(7), Date(1999, 6, 29)); - - _assertPred!"-="(Date(1999, 7, 6), dur!"hours"(-24), Date(1999, 7, 7)); - _assertPred!"-="(Date(1999, 7, 6), dur!"hours"(24), Date(1999, 7, 5)); - _assertPred!"-="(Date(1999, 7, 6), dur!"minutes"(-1440), Date(1999, 7, 7)); - _assertPred!"-="(Date(1999, 7, 6), dur!"minutes"(1440), Date(1999, 7, 5)); - _assertPred!"-="(Date(1999, 7, 6), dur!"seconds"(-86_400), Date(1999, 7, 7)); - _assertPred!"-="(Date(1999, 7, 6), dur!"seconds"(86_400), Date(1999, 7, 5)); - _assertPred!"-="(Date(1999, 7, 6), dur!"msecs"(-86_400_000), Date(1999, 7, 7)); - _assertPred!"-="(Date(1999, 7, 6), dur!"msecs"(86_400_000), Date(1999, 7, 5)); - _assertPred!"-="(Date(1999, 7, 6), dur!"usecs"(-86_400_000_000), Date(1999, 7, 7)); - _assertPred!"-="(Date(1999, 7, 6), dur!"usecs"(86_400_000_000), Date(1999, 7, 5)); - _assertPred!"-="(Date(1999, 7, 6), dur!"hnsecs"(-864_000_000_000), Date(1999, 7, 7)); - _assertPred!"-="(Date(1999, 7, 6), dur!"hnsecs"(864_000_000_000), Date(1999, 7, 5)); - - auto duration = dur!"days"(12); - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, date += duration)); - static assert(!__traits(compiles, cdate += duration)); - static assert(!__traits(compiles, idate += duration)); + unittest + { + assert(Date(1999, 7, 6) + dur!"weeks"(7) == Date(1999, 8, 24)); + assert(Date(1999, 7, 6) + dur!"weeks"(-7) == Date(1999, 5, 18)); + assert(Date(1999, 7, 6) + dur!"days"(7) == Date(1999, 7, 13)); + assert(Date(1999, 7, 6) + dur!"days"(-7) == Date(1999, 6, 29)); + + assert(Date(1999, 7, 6) + dur!"hours"(24) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"hours"(-24) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) + dur!"minutes"(1440) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"minutes"(-1440) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) + dur!"seconds"(86_400) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"seconds"(-86_400) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) + dur!"msecs"(86_400_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"msecs"(-86_400_000) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) + dur!"usecs"(86_400_000_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"usecs"(-86_400_000_000) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) + dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) + dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 5)); + + assert(Date(1999, 7, 6) - dur!"weeks"(-7) == Date(1999, 8, 24)); + assert(Date(1999, 7, 6) - dur!"weeks"(7) == Date(1999, 5, 18)); + assert(Date(1999, 7, 6) - dur!"days"(-7) == Date(1999, 7, 13)); + assert(Date(1999, 7, 6) - dur!"days"(7) == Date(1999, 6, 29)); + + assert(Date(1999, 7, 6) - dur!"hours"(-24) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"hours"(24) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) - dur!"minutes"(-1440) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"minutes"(1440) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) - dur!"seconds"(-86_400) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"seconds"(86_400) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) - dur!"msecs"(-86_400_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"msecs"(86_400_000) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) - dur!"usecs"(-86_400_000_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"usecs"(86_400_000_000) == Date(1999, 7, 5)); + assert(Date(1999, 7, 6) - dur!"hnsecs"(-864_000_000_000) == Date(1999, 7, 7)); + assert(Date(1999, 7, 6) - dur!"hnsecs"(864_000_000_000) == Date(1999, 7, 5)); + + { + auto date = Date(0, 1, 31); + (date += dur!"days"(507)) += dur!"days"(-2); + assert(date == Date(1, 6, 19)); + } + + auto duration = dur!"days"(12); + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, date += duration)); + static assert(!__traits(compiles, cdate += duration)); + static assert(!__traits(compiles, idate += duration)); - static assert(__traits(compiles, date -= duration)); - static assert(!__traits(compiles, cdate -= duration)); - static assert(!__traits(compiles, idate -= duration)); - } + static assert(__traits(compiles, date -= duration)); + static assert(!__traits(compiles, cdate -= duration)); + static assert(!__traits(compiles, idate -= duration)); } @@ -11677,7 +11079,7 @@ assert(d == Date(2010, 1, 25)); $(TR $(TD Date) $(TD -) $(TD Date) $(TD -->) $(TD duration)) ) +/ - Duration opBinary(string op)(in Date rhs) const pure nothrow + Duration opBinary(string op)(in Date rhs) @safe const pure nothrow if(op == "-") { return dur!"days"(this.dayOfGregorianCal - rhs.dayOfGregorianCal); @@ -11685,31 +11087,28 @@ assert(d == Date(2010, 1, 25)); unittest { - version(testStdDateTime) - { - auto date = Date(1999, 7, 6); + auto date = Date(1999, 7, 6); - _assertPred!"=="(Date(1999, 7, 6) - Date(1998, 7, 6), dur!"days"(365)); - _assertPred!"=="(Date(1998, 7, 6) - Date(1999, 7, 6), dur!"days"(-365)); - _assertPred!"=="(Date(1999, 6, 6) - Date(1999, 5, 6), dur!"days"(31)); - _assertPred!"=="(Date(1999, 5, 6) - Date(1999, 6, 6), dur!"days"(-31)); - _assertPred!"=="(Date(1999, 1, 1) - Date(1998, 12, 31), dur!"days"(1)); - _assertPred!"=="(Date(1998, 12, 31) - Date(1999, 1, 1), dur!"days"(-1)); + assert(Date(1999, 7, 6) - Date(1998, 7, 6) == dur!"days"(365)); + assert(Date(1998, 7, 6) - Date(1999, 7, 6) == dur!"days"(-365)); + assert(Date(1999, 6, 6) - Date(1999, 5, 6) == dur!"days"(31)); + assert(Date(1999, 5, 6) - Date(1999, 6, 6) == dur!"days"(-31)); + assert(Date(1999, 1, 1) - Date(1998, 12, 31) == dur!"days"(1)); + assert(Date(1998, 12, 31) - Date(1999, 1, 1) == dur!"days"(-1)); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, date - date)); - static assert(__traits(compiles, cdate - date)); - static assert(__traits(compiles, idate - date)); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, date - date)); + static assert(__traits(compiles, cdate - date)); + static assert(__traits(compiles, idate - date)); - static assert(__traits(compiles, date - cdate)); - static assert(__traits(compiles, cdate - cdate)); - static assert(__traits(compiles, idate - cdate)); + static assert(__traits(compiles, date - cdate)); + static assert(__traits(compiles, cdate - cdate)); + static assert(__traits(compiles, idate - cdate)); - static assert(__traits(compiles, date - idate)); - static assert(__traits(compiles, cdate - idate)); - static assert(__traits(compiles, idate - idate)); - } + static assert(__traits(compiles, date - idate)); + static assert(__traits(compiles, cdate - idate)); + static assert(__traits(compiles, idate - idate)); } @@ -11733,16 +11132,8 @@ assert(d == Date(2010, 1, 25)); Params: rhs = The $(LREF Date) to subtract from this one. - - Examples: --------------------- -assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1); -assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1); -assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2); -assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2); --------------------- +/ - int diffMonths(in Date rhs) const pure nothrow + int diffMonths(in Date rhs) @safe const pure nothrow { immutable yearDiff = _year - rhs._year; immutable monthDiff = _month - rhs._month; @@ -11750,288 +11141,275 @@ assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2); return yearDiff * 12 + monthDiff; } + /// unittest { - version(testStdDateTime) - { - auto date = Date(1999, 7, 6); + assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1); + assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1); + assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2); + assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2); + } - //Test A.D. - _assertPred!"=="(date.diffMonths(Date(1998, 6, 5)), 13); - _assertPred!"=="(date.diffMonths(Date(1998, 7, 5)), 12); - _assertPred!"=="(date.diffMonths(Date(1998, 8, 5)), 11); - _assertPred!"=="(date.diffMonths(Date(1998, 9, 5)), 10); - _assertPred!"=="(date.diffMonths(Date(1998, 10, 5)), 9); - _assertPred!"=="(date.diffMonths(Date(1998, 11, 5)), 8); - _assertPred!"=="(date.diffMonths(Date(1998, 12, 5)), 7); - _assertPred!"=="(date.diffMonths(Date(1999, 1, 5)), 6); - _assertPred!"=="(date.diffMonths(Date(1999, 2, 6)), 5); - _assertPred!"=="(date.diffMonths(Date(1999, 3, 6)), 4); - _assertPred!"=="(date.diffMonths(Date(1999, 4, 6)), 3); - _assertPred!"=="(date.diffMonths(Date(1999, 5, 6)), 2); - _assertPred!"=="(date.diffMonths(Date(1999, 6, 6)), 1); - _assertPred!"=="(date.diffMonths(date), 0); - _assertPred!"=="(date.diffMonths(Date(1999, 8, 6)), -1); - _assertPred!"=="(date.diffMonths(Date(1999, 9, 6)), -2); - _assertPred!"=="(date.diffMonths(Date(1999, 10, 6)), -3); - _assertPred!"=="(date.diffMonths(Date(1999, 11, 6)), -4); - _assertPred!"=="(date.diffMonths(Date(1999, 12, 6)), -5); - _assertPred!"=="(date.diffMonths(Date(2000, 1, 6)), -6); - _assertPred!"=="(date.diffMonths(Date(2000, 2, 6)), -7); - _assertPred!"=="(date.diffMonths(Date(2000, 3, 6)), -8); - _assertPred!"=="(date.diffMonths(Date(2000, 4, 6)), -9); - _assertPred!"=="(date.diffMonths(Date(2000, 5, 6)), -10); - _assertPred!"=="(date.diffMonths(Date(2000, 6, 6)), -11); - _assertPred!"=="(date.diffMonths(Date(2000, 7, 6)), -12); - _assertPred!"=="(date.diffMonths(Date(2000, 8, 6)), -13); - - _assertPred!"=="(Date(1998, 6, 5).diffMonths(date), -13); - _assertPred!"=="(Date(1998, 7, 5).diffMonths(date), -12); - _assertPred!"=="(Date(1998, 8, 5).diffMonths(date), -11); - _assertPred!"=="(Date(1998, 9, 5).diffMonths(date), -10); - _assertPred!"=="(Date(1998, 10, 5).diffMonths(date), -9); - _assertPred!"=="(Date(1998, 11, 5).diffMonths(date), -8); - _assertPred!"=="(Date(1998, 12, 5).diffMonths(date), -7); - _assertPred!"=="(Date(1999, 1, 5).diffMonths(date), -6); - _assertPred!"=="(Date(1999, 2, 6).diffMonths(date), -5); - _assertPred!"=="(Date(1999, 3, 6).diffMonths(date), -4); - _assertPred!"=="(Date(1999, 4, 6).diffMonths(date), -3); - _assertPred!"=="(Date(1999, 5, 6).diffMonths(date), -2); - _assertPred!"=="(Date(1999, 6, 6).diffMonths(date), -1); - _assertPred!"=="(Date(1999, 8, 6).diffMonths(date), 1); - _assertPred!"=="(Date(1999, 9, 6).diffMonths(date), 2); - _assertPred!"=="(Date(1999, 10, 6).diffMonths(date), 3); - _assertPred!"=="(Date(1999, 11, 6).diffMonths(date), 4); - _assertPred!"=="(Date(1999, 12, 6).diffMonths(date), 5); - _assertPred!"=="(Date(2000, 1, 6).diffMonths(date), 6); - _assertPred!"=="(Date(2000, 2, 6).diffMonths(date), 7); - _assertPred!"=="(Date(2000, 3, 6).diffMonths(date), 8); - _assertPred!"=="(Date(2000, 4, 6).diffMonths(date), 9); - _assertPred!"=="(Date(2000, 5, 6).diffMonths(date), 10); - _assertPred!"=="(Date(2000, 6, 6).diffMonths(date), 11); - _assertPred!"=="(Date(2000, 7, 6).diffMonths(date), 12); - _assertPred!"=="(Date(2000, 8, 6).diffMonths(date), 13); - - _assertPred!"=="(date.diffMonths(Date(1999, 6, 30)), 1); - _assertPred!"=="(date.diffMonths(Date(1999, 7, 1)), 0); - _assertPred!"=="(date.diffMonths(Date(1999, 7, 6)), 0); - _assertPred!"=="(date.diffMonths(Date(1999, 7, 11)), 0); - _assertPred!"=="(date.diffMonths(Date(1999, 7, 16)), 0); - _assertPred!"=="(date.diffMonths(Date(1999, 7, 21)), 0); - _assertPred!"=="(date.diffMonths(Date(1999, 7, 26)), 0); - _assertPred!"=="(date.diffMonths(Date(1999, 7, 31)), 0); - _assertPred!"=="(date.diffMonths(Date(1999, 8, 1)), -1); - - _assertPred!"=="(date.diffMonths(Date(1990, 6, 30)), 109); - _assertPred!"=="(date.diffMonths(Date(1990, 7, 1)), 108); - _assertPred!"=="(date.diffMonths(Date(1990, 7, 6)), 108); - _assertPred!"=="(date.diffMonths(Date(1990, 7, 11)), 108); - _assertPred!"=="(date.diffMonths(Date(1990, 7, 16)), 108); - _assertPred!"=="(date.diffMonths(Date(1990, 7, 21)), 108); - _assertPred!"=="(date.diffMonths(Date(1990, 7, 26)), 108); - _assertPred!"=="(date.diffMonths(Date(1990, 7, 31)), 108); - _assertPred!"=="(date.diffMonths(Date(1990, 8, 1)), 107); - - _assertPred!"=="(Date(1999, 6, 30).diffMonths(date), -1); - _assertPred!"=="(Date(1999, 7, 1).diffMonths(date), 0); - _assertPred!"=="(Date(1999, 7, 6).diffMonths(date), 0); - _assertPred!"=="(Date(1999, 7, 11).diffMonths(date), 0); - _assertPred!"=="(Date(1999, 7, 16).diffMonths(date), 0); - _assertPred!"=="(Date(1999, 7, 21).diffMonths(date), 0); - _assertPred!"=="(Date(1999, 7, 26).diffMonths(date), 0); - _assertPred!"=="(Date(1999, 7, 31).diffMonths(date), 0); - _assertPred!"=="(Date(1999, 8, 1).diffMonths(date), 1); - - _assertPred!"=="(Date(1990, 6, 30).diffMonths(date), -109); - _assertPred!"=="(Date(1990, 7, 1).diffMonths(date), -108); - _assertPred!"=="(Date(1990, 7, 6).diffMonths(date), -108); - _assertPred!"=="(Date(1990, 7, 11).diffMonths(date), -108); - _assertPred!"=="(Date(1990, 7, 16).diffMonths(date), -108); - _assertPred!"=="(Date(1990, 7, 21).diffMonths(date), -108); - _assertPred!"=="(Date(1990, 7, 26).diffMonths(date), -108); - _assertPred!"=="(Date(1990, 7, 31).diffMonths(date), -108); - _assertPred!"=="(Date(1990, 8, 1).diffMonths(date), -107); - - //Test B.C. - auto dateBC = Date(-1999, 7, 6); - - _assertPred!"=="(dateBC.diffMonths(Date(-2000, 6, 5)), 13); - _assertPred!"=="(dateBC.diffMonths(Date(-2000, 7, 5)), 12); - _assertPred!"=="(dateBC.diffMonths(Date(-2000, 8, 5)), 11); - _assertPred!"=="(dateBC.diffMonths(Date(-2000, 9, 5)), 10); - _assertPred!"=="(dateBC.diffMonths(Date(-2000, 10, 5)), 9); - _assertPred!"=="(dateBC.diffMonths(Date(-2000, 11, 5)), 8); - _assertPred!"=="(dateBC.diffMonths(Date(-2000, 12, 5)), 7); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 1, 5)), 6); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 2, 6)), 5); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 3, 6)), 4); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 4, 6)), 3); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 5, 6)), 2); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 6, 6)), 1); - _assertPred!"=="(dateBC.diffMonths(dateBC), 0); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 8, 6)), -1); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 9, 6)), -2); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 10, 6)), -3); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 11, 6)), -4); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 12, 6)), -5); - _assertPred!"=="(dateBC.diffMonths(Date(-1998, 1, 6)), -6); - _assertPred!"=="(dateBC.diffMonths(Date(-1998, 2, 6)), -7); - _assertPred!"=="(dateBC.diffMonths(Date(-1998, 3, 6)), -8); - _assertPred!"=="(dateBC.diffMonths(Date(-1998, 4, 6)), -9); - _assertPred!"=="(dateBC.diffMonths(Date(-1998, 5, 6)), -10); - _assertPred!"=="(dateBC.diffMonths(Date(-1998, 6, 6)), -11); - _assertPred!"=="(dateBC.diffMonths(Date(-1998, 7, 6)), -12); - _assertPred!"=="(dateBC.diffMonths(Date(-1998, 8, 6)), -13); - - _assertPred!"=="(Date(-2000, 6, 5).diffMonths(dateBC), -13); - _assertPred!"=="(Date(-2000, 7, 5).diffMonths(dateBC), -12); - _assertPred!"=="(Date(-2000, 8, 5).diffMonths(dateBC), -11); - _assertPred!"=="(Date(-2000, 9, 5).diffMonths(dateBC), -10); - _assertPred!"=="(Date(-2000, 10, 5).diffMonths(dateBC), -9); - _assertPred!"=="(Date(-2000, 11, 5).diffMonths(dateBC), -8); - _assertPred!"=="(Date(-2000, 12, 5).diffMonths(dateBC), -7); - _assertPred!"=="(Date(-1999, 1, 5).diffMonths(dateBC), -6); - _assertPred!"=="(Date(-1999, 2, 6).diffMonths(dateBC), -5); - _assertPred!"=="(Date(-1999, 3, 6).diffMonths(dateBC), -4); - _assertPred!"=="(Date(-1999, 4, 6).diffMonths(dateBC), -3); - _assertPred!"=="(Date(-1999, 5, 6).diffMonths(dateBC), -2); - _assertPred!"=="(Date(-1999, 6, 6).diffMonths(dateBC), -1); - _assertPred!"=="(Date(-1999, 8, 6).diffMonths(dateBC), 1); - _assertPred!"=="(Date(-1999, 9, 6).diffMonths(dateBC), 2); - _assertPred!"=="(Date(-1999, 10, 6).diffMonths(dateBC), 3); - _assertPred!"=="(Date(-1999, 11, 6).diffMonths(dateBC), 4); - _assertPred!"=="(Date(-1999, 12, 6).diffMonths(dateBC), 5); - _assertPred!"=="(Date(-1998, 1, 6).diffMonths(dateBC), 6); - _assertPred!"=="(Date(-1998, 2, 6).diffMonths(dateBC), 7); - _assertPred!"=="(Date(-1998, 3, 6).diffMonths(dateBC), 8); - _assertPred!"=="(Date(-1998, 4, 6).diffMonths(dateBC), 9); - _assertPred!"=="(Date(-1998, 5, 6).diffMonths(dateBC), 10); - _assertPred!"=="(Date(-1998, 6, 6).diffMonths(dateBC), 11); - _assertPred!"=="(Date(-1998, 7, 6).diffMonths(dateBC), 12); - _assertPred!"=="(Date(-1998, 8, 6).diffMonths(dateBC), 13); - - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 6, 30)), 1); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 7, 1)), 0); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 7, 6)), 0); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 7, 11)), 0); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 7, 16)), 0); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 7, 21)), 0); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 7, 26)), 0); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 7, 31)), 0); - _assertPred!"=="(dateBC.diffMonths(Date(-1999, 8, 1)), -1); - - _assertPred!"=="(dateBC.diffMonths(Date(-2008, 6, 30)), 109); - _assertPred!"=="(dateBC.diffMonths(Date(-2008, 7, 1)), 108); - _assertPred!"=="(dateBC.diffMonths(Date(-2008, 7, 6)), 108); - _assertPred!"=="(dateBC.diffMonths(Date(-2008, 7, 11)), 108); - _assertPred!"=="(dateBC.diffMonths(Date(-2008, 7, 16)), 108); - _assertPred!"=="(dateBC.diffMonths(Date(-2008, 7, 21)), 108); - _assertPred!"=="(dateBC.diffMonths(Date(-2008, 7, 26)), 108); - _assertPred!"=="(dateBC.diffMonths(Date(-2008, 7, 31)), 108); - _assertPred!"=="(dateBC.diffMonths(Date(-2008, 8, 1)), 107); - - _assertPred!"=="(Date(-1999, 6, 30).diffMonths(dateBC), -1); - _assertPred!"=="(Date(-1999, 7, 1).diffMonths(dateBC), 0); - _assertPred!"=="(Date(-1999, 7, 6).diffMonths(dateBC), 0); - _assertPred!"=="(Date(-1999, 7, 11).diffMonths(dateBC), 0); - _assertPred!"=="(Date(-1999, 7, 16).diffMonths(dateBC), 0); - _assertPred!"=="(Date(-1999, 7, 21).diffMonths(dateBC), 0); - _assertPred!"=="(Date(-1999, 7, 26).diffMonths(dateBC), 0); - _assertPred!"=="(Date(-1999, 7, 31).diffMonths(dateBC), 0); - _assertPred!"=="(Date(-1999, 8, 1).diffMonths(dateBC), 1); - - _assertPred!"=="(Date(-2008, 6, 30).diffMonths(dateBC), -109); - _assertPred!"=="(Date(-2008, 7, 1).diffMonths(dateBC), -108); - _assertPred!"=="(Date(-2008, 7, 6).diffMonths(dateBC), -108); - _assertPred!"=="(Date(-2008, 7, 11).diffMonths(dateBC), -108); - _assertPred!"=="(Date(-2008, 7, 16).diffMonths(dateBC), -108); - _assertPred!"=="(Date(-2008, 7, 21).diffMonths(dateBC), -108); - _assertPred!"=="(Date(-2008, 7, 26).diffMonths(dateBC), -108); - _assertPred!"=="(Date(-2008, 7, 31).diffMonths(dateBC), -108); - _assertPred!"=="(Date(-2008, 8, 1).diffMonths(dateBC), -107); - - //Test Both - _assertPred!"=="(Date(3, 3, 3).diffMonths(Date(-5, 5, 5)), 94); - _assertPred!"=="(Date(-5, 5, 5).diffMonths(Date(3, 3, 3)), -94); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, date.diffMonths(date))); - static assert(__traits(compiles, cdate.diffMonths(date))); - static assert(__traits(compiles, idate.diffMonths(date))); - - static assert(__traits(compiles, date.diffMonths(cdate))); - static assert(__traits(compiles, cdate.diffMonths(cdate))); - static assert(__traits(compiles, idate.diffMonths(cdate))); - - static assert(__traits(compiles, date.diffMonths(idate))); - static assert(__traits(compiles, cdate.diffMonths(idate))); - static assert(__traits(compiles, idate.diffMonths(idate))); - - //Verify Examples. - assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1); - assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1); - assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2); - assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2); - } + unittest + { + auto date = Date(1999, 7, 6); + + //Test A.D. + assert(date.diffMonths(Date(1998, 6, 5)) == 13); + assert(date.diffMonths(Date(1998, 7, 5)) == 12); + assert(date.diffMonths(Date(1998, 8, 5)) == 11); + assert(date.diffMonths(Date(1998, 9, 5)) == 10); + assert(date.diffMonths(Date(1998, 10, 5)) == 9); + assert(date.diffMonths(Date(1998, 11, 5)) == 8); + assert(date.diffMonths(Date(1998, 12, 5)) == 7); + assert(date.diffMonths(Date(1999, 1, 5)) == 6); + assert(date.diffMonths(Date(1999, 2, 6)) == 5); + assert(date.diffMonths(Date(1999, 3, 6)) == 4); + assert(date.diffMonths(Date(1999, 4, 6)) == 3); + assert(date.diffMonths(Date(1999, 5, 6)) == 2); + assert(date.diffMonths(Date(1999, 6, 6)) == 1); + assert(date.diffMonths(date) == 0); + assert(date.diffMonths(Date(1999, 8, 6)) == -1); + assert(date.diffMonths(Date(1999, 9, 6)) == -2); + assert(date.diffMonths(Date(1999, 10, 6)) == -3); + assert(date.diffMonths(Date(1999, 11, 6)) == -4); + assert(date.diffMonths(Date(1999, 12, 6)) == -5); + assert(date.diffMonths(Date(2000, 1, 6)) == -6); + assert(date.diffMonths(Date(2000, 2, 6)) == -7); + assert(date.diffMonths(Date(2000, 3, 6)) == -8); + assert(date.diffMonths(Date(2000, 4, 6)) == -9); + assert(date.diffMonths(Date(2000, 5, 6)) == -10); + assert(date.diffMonths(Date(2000, 6, 6)) == -11); + assert(date.diffMonths(Date(2000, 7, 6)) == -12); + assert(date.diffMonths(Date(2000, 8, 6)) == -13); + + assert(Date(1998, 6, 5).diffMonths(date) == -13); + assert(Date(1998, 7, 5).diffMonths(date) == -12); + assert(Date(1998, 8, 5).diffMonths(date) == -11); + assert(Date(1998, 9, 5).diffMonths(date) == -10); + assert(Date(1998, 10, 5).diffMonths(date) == -9); + assert(Date(1998, 11, 5).diffMonths(date) == -8); + assert(Date(1998, 12, 5).diffMonths(date) == -7); + assert(Date(1999, 1, 5).diffMonths(date) == -6); + assert(Date(1999, 2, 6).diffMonths(date) == -5); + assert(Date(1999, 3, 6).diffMonths(date) == -4); + assert(Date(1999, 4, 6).diffMonths(date) == -3); + assert(Date(1999, 5, 6).diffMonths(date) == -2); + assert(Date(1999, 6, 6).diffMonths(date) == -1); + assert(Date(1999, 8, 6).diffMonths(date) == 1); + assert(Date(1999, 9, 6).diffMonths(date) == 2); + assert(Date(1999, 10, 6).diffMonths(date) == 3); + assert(Date(1999, 11, 6).diffMonths(date) == 4); + assert(Date(1999, 12, 6).diffMonths(date) == 5); + assert(Date(2000, 1, 6).diffMonths(date) == 6); + assert(Date(2000, 2, 6).diffMonths(date) == 7); + assert(Date(2000, 3, 6).diffMonths(date) == 8); + assert(Date(2000, 4, 6).diffMonths(date) == 9); + assert(Date(2000, 5, 6).diffMonths(date) == 10); + assert(Date(2000, 6, 6).diffMonths(date) == 11); + assert(Date(2000, 7, 6).diffMonths(date) == 12); + assert(Date(2000, 8, 6).diffMonths(date) == 13); + + assert(date.diffMonths(Date(1999, 6, 30)) == 1); + assert(date.diffMonths(Date(1999, 7, 1)) == 0); + assert(date.diffMonths(Date(1999, 7, 6)) == 0); + assert(date.diffMonths(Date(1999, 7, 11)) == 0); + assert(date.diffMonths(Date(1999, 7, 16)) == 0); + assert(date.diffMonths(Date(1999, 7, 21)) == 0); + assert(date.diffMonths(Date(1999, 7, 26)) == 0); + assert(date.diffMonths(Date(1999, 7, 31)) == 0); + assert(date.diffMonths(Date(1999, 8, 1)) == -1); + + assert(date.diffMonths(Date(1990, 6, 30)) == 109); + assert(date.diffMonths(Date(1990, 7, 1)) == 108); + assert(date.diffMonths(Date(1990, 7, 6)) == 108); + assert(date.diffMonths(Date(1990, 7, 11)) == 108); + assert(date.diffMonths(Date(1990, 7, 16)) == 108); + assert(date.diffMonths(Date(1990, 7, 21)) == 108); + assert(date.diffMonths(Date(1990, 7, 26)) == 108); + assert(date.diffMonths(Date(1990, 7, 31)) == 108); + assert(date.diffMonths(Date(1990, 8, 1)) == 107); + + assert(Date(1999, 6, 30).diffMonths(date) == -1); + assert(Date(1999, 7, 1).diffMonths(date) == 0); + assert(Date(1999, 7, 6).diffMonths(date) == 0); + assert(Date(1999, 7, 11).diffMonths(date) == 0); + assert(Date(1999, 7, 16).diffMonths(date) == 0); + assert(Date(1999, 7, 21).diffMonths(date) == 0); + assert(Date(1999, 7, 26).diffMonths(date) == 0); + assert(Date(1999, 7, 31).diffMonths(date) == 0); + assert(Date(1999, 8, 1).diffMonths(date) == 1); + + assert(Date(1990, 6, 30).diffMonths(date) == -109); + assert(Date(1990, 7, 1).diffMonths(date) == -108); + assert(Date(1990, 7, 6).diffMonths(date) == -108); + assert(Date(1990, 7, 11).diffMonths(date) == -108); + assert(Date(1990, 7, 16).diffMonths(date) == -108); + assert(Date(1990, 7, 21).diffMonths(date) == -108); + assert(Date(1990, 7, 26).diffMonths(date) == -108); + assert(Date(1990, 7, 31).diffMonths(date) == -108); + assert(Date(1990, 8, 1).diffMonths(date) == -107); + + //Test B.C. + auto dateBC = Date(-1999, 7, 6); + + assert(dateBC.diffMonths(Date(-2000, 6, 5)) == 13); + assert(dateBC.diffMonths(Date(-2000, 7, 5)) == 12); + assert(dateBC.diffMonths(Date(-2000, 8, 5)) == 11); + assert(dateBC.diffMonths(Date(-2000, 9, 5)) == 10); + assert(dateBC.diffMonths(Date(-2000, 10, 5)) == 9); + assert(dateBC.diffMonths(Date(-2000, 11, 5)) == 8); + assert(dateBC.diffMonths(Date(-2000, 12, 5)) == 7); + assert(dateBC.diffMonths(Date(-1999, 1, 5)) == 6); + assert(dateBC.diffMonths(Date(-1999, 2, 6)) == 5); + assert(dateBC.diffMonths(Date(-1999, 3, 6)) == 4); + assert(dateBC.diffMonths(Date(-1999, 4, 6)) == 3); + assert(dateBC.diffMonths(Date(-1999, 5, 6)) == 2); + assert(dateBC.diffMonths(Date(-1999, 6, 6)) == 1); + assert(dateBC.diffMonths(dateBC) == 0); + assert(dateBC.diffMonths(Date(-1999, 8, 6)) == -1); + assert(dateBC.diffMonths(Date(-1999, 9, 6)) == -2); + assert(dateBC.diffMonths(Date(-1999, 10, 6)) == -3); + assert(dateBC.diffMonths(Date(-1999, 11, 6)) == -4); + assert(dateBC.diffMonths(Date(-1999, 12, 6)) == -5); + assert(dateBC.diffMonths(Date(-1998, 1, 6)) == -6); + assert(dateBC.diffMonths(Date(-1998, 2, 6)) == -7); + assert(dateBC.diffMonths(Date(-1998, 3, 6)) == -8); + assert(dateBC.diffMonths(Date(-1998, 4, 6)) == -9); + assert(dateBC.diffMonths(Date(-1998, 5, 6)) == -10); + assert(dateBC.diffMonths(Date(-1998, 6, 6)) == -11); + assert(dateBC.diffMonths(Date(-1998, 7, 6)) == -12); + assert(dateBC.diffMonths(Date(-1998, 8, 6)) == -13); + + assert(Date(-2000, 6, 5).diffMonths(dateBC) == -13); + assert(Date(-2000, 7, 5).diffMonths(dateBC) == -12); + assert(Date(-2000, 8, 5).diffMonths(dateBC) == -11); + assert(Date(-2000, 9, 5).diffMonths(dateBC) == -10); + assert(Date(-2000, 10, 5).diffMonths(dateBC) == -9); + assert(Date(-2000, 11, 5).diffMonths(dateBC) == -8); + assert(Date(-2000, 12, 5).diffMonths(dateBC) == -7); + assert(Date(-1999, 1, 5).diffMonths(dateBC) == -6); + assert(Date(-1999, 2, 6).diffMonths(dateBC) == -5); + assert(Date(-1999, 3, 6).diffMonths(dateBC) == -4); + assert(Date(-1999, 4, 6).diffMonths(dateBC) == -3); + assert(Date(-1999, 5, 6).diffMonths(dateBC) == -2); + assert(Date(-1999, 6, 6).diffMonths(dateBC) == -1); + assert(Date(-1999, 8, 6).diffMonths(dateBC) == 1); + assert(Date(-1999, 9, 6).diffMonths(dateBC) == 2); + assert(Date(-1999, 10, 6).diffMonths(dateBC) == 3); + assert(Date(-1999, 11, 6).diffMonths(dateBC) == 4); + assert(Date(-1999, 12, 6).diffMonths(dateBC) == 5); + assert(Date(-1998, 1, 6).diffMonths(dateBC) == 6); + assert(Date(-1998, 2, 6).diffMonths(dateBC) == 7); + assert(Date(-1998, 3, 6).diffMonths(dateBC) == 8); + assert(Date(-1998, 4, 6).diffMonths(dateBC) == 9); + assert(Date(-1998, 5, 6).diffMonths(dateBC) == 10); + assert(Date(-1998, 6, 6).diffMonths(dateBC) == 11); + assert(Date(-1998, 7, 6).diffMonths(dateBC) == 12); + assert(Date(-1998, 8, 6).diffMonths(dateBC) == 13); + + assert(dateBC.diffMonths(Date(-1999, 6, 30)) == 1); + assert(dateBC.diffMonths(Date(-1999, 7, 1)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 6)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 11)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 16)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 21)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 26)) == 0); + assert(dateBC.diffMonths(Date(-1999, 7, 31)) == 0); + assert(dateBC.diffMonths(Date(-1999, 8, 1)) == -1); + + assert(dateBC.diffMonths(Date(-2008, 6, 30)) == 109); + assert(dateBC.diffMonths(Date(-2008, 7, 1)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 6)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 11)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 16)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 21)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 26)) == 108); + assert(dateBC.diffMonths(Date(-2008, 7, 31)) == 108); + assert(dateBC.diffMonths(Date(-2008, 8, 1)) == 107); + + assert(Date(-1999, 6, 30).diffMonths(dateBC) == -1); + assert(Date(-1999, 7, 1).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 6).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 11).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 16).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 21).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 26).diffMonths(dateBC) == 0); + assert(Date(-1999, 7, 31).diffMonths(dateBC) == 0); + assert(Date(-1999, 8, 1).diffMonths(dateBC) == 1); + + assert(Date(-2008, 6, 30).diffMonths(dateBC) == -109); + assert(Date(-2008, 7, 1).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 6).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 11).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 16).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 21).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 26).diffMonths(dateBC) == -108); + assert(Date(-2008, 7, 31).diffMonths(dateBC) == -108); + assert(Date(-2008, 8, 1).diffMonths(dateBC) == -107); + + //Test Both + assert(Date(3, 3, 3).diffMonths(Date(-5, 5, 5)) == 94); + assert(Date(-5, 5, 5).diffMonths(Date(3, 3, 3)) == -94); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, date.diffMonths(date))); + static assert(__traits(compiles, cdate.diffMonths(date))); + static assert(__traits(compiles, idate.diffMonths(date))); + + static assert(__traits(compiles, date.diffMonths(cdate))); + static assert(__traits(compiles, cdate.diffMonths(cdate))); + static assert(__traits(compiles, idate.diffMonths(cdate))); + + static assert(__traits(compiles, date.diffMonths(idate))); + static assert(__traits(compiles, cdate.diffMonths(idate))); + static assert(__traits(compiles, idate.diffMonths(idate))); } /++ Whether this $(LREF Date) is in a leap year. +/ - @property bool isLeapYear() const pure nothrow + @property bool isLeapYear() @safe const pure nothrow { return yearIsLeapYear(_year); } unittest { - version(testStdDateTime) - { - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, date.isLeapYear = true)); - static assert(!__traits(compiles, cdate.isLeapYear = true)); - static assert(!__traits(compiles, idate.isLeapYear = true)); - } + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, date.isLeapYear = true)); + static assert(!__traits(compiles, cdate.isLeapYear = true)); + static assert(!__traits(compiles, idate.isLeapYear = true)); } /++ Day of the week this $(LREF Date) is on. +/ - @property DayOfWeek dayOfWeek() const pure nothrow + @property DayOfWeek dayOfWeek() @safe const pure nothrow { return getDayOfWeek(dayOfGregorianCal); } unittest { - version(testStdDateTime) - { - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.dayOfWeek == DayOfWeek.sun)); - static assert(!__traits(compiles, cdate.dayOfWeek = DayOfWeek.sun)); - static assert(__traits(compiles, idate.dayOfWeek == DayOfWeek.sun)); - static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun)); - } + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.dayOfWeek == DayOfWeek.sun)); + static assert(!__traits(compiles, cdate.dayOfWeek = DayOfWeek.sun)); + static assert(__traits(compiles, idate.dayOfWeek == DayOfWeek.sun)); + static assert(!__traits(compiles, idate.dayOfWeek = DayOfWeek.sun)); } /++ Day of the year this $(LREF Date) is on. - - Examples: --------------------- -assert(Date(1999, 1, 1).dayOfYear == 1); -assert(Date(1999, 12, 31).dayOfYear == 365); -assert(Date(2000, 12, 31).dayOfYear == 366); --------------------- +/ - @property ushort dayOfYear() const pure nothrow + @property ushort dayOfYear() @safe const pure nothrow { if (_month >= Month.jan && _month <= Month.dec) { @@ -12043,22 +11421,22 @@ assert(Date(2000, 12, 31).dayOfYear == 366); assert(0, "Invalid month."); } - //Verify Examples. - version(testStdDateTime) unittest + /// + unittest { assert(Date(1999, 1, 1).dayOfYear == 1); assert(Date(1999, 12, 31).dayOfYear == 365); assert(Date(2000, 12, 31).dayOfYear == 366); } - version(testStdDateTime) unittest + unittest { foreach(year; filter!((a){return !yearIsLeapYear(a);}) (chain(testYearsBC, testYearsAD))) { foreach(doy; testDaysOfYear) { - _assertPred!"=="(Date(year, doy.md.month, doy.md.day).dayOfYear, + assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day); } } @@ -12068,7 +11446,7 @@ assert(Date(2000, 12, 31).dayOfYear == 366); { foreach(doy; testDaysOfLeapYear) { - _assertPred!"=="(Date(year, doy.md.month, doy.md.day).dayOfYear, + assert(Date(year, doy.md.month, doy.md.day).dayOfYear == doy.day); } } @@ -12090,7 +11468,7 @@ assert(Date(2000, 12, 31).dayOfYear == 366); $(LREF DateTimeException) if the given day is an invalid day of the year. +/ - @property void dayOfYear(int day) pure + @property void dayOfYear(int day) @safe pure { immutable int[] lastDay = isLeapYear ? lastDayLeap : lastDayNonLeap; @@ -12109,13 +11487,13 @@ assert(Date(2000, 12, 31).dayOfYear == 366); assert(0, "Invalid day of the year."); } - version(testStdDateTime) unittest + unittest { static void test(Date date, int day, MonthDay expected, size_t line = __LINE__) { date.dayOfYear = day; - _assertPred!"=="(date.month, expected.month, "", __FILE__, line); - _assertPred!"=="(date.day, expected.day, "", __FILE__, line); + assert(date.month == expected.month); + assert(date.day == expected.day); } foreach(doy; testDaysOfYear) @@ -12139,22 +11517,8 @@ assert(Date(2000, 12, 31).dayOfYear == 366); /++ The Xth day of the Gregorian Calendar that this $(LREF Date) is on. - - Examples: --------------------- -assert(Date(1, 1, 1).dayOfGregorianCal == 1); -assert(Date(1, 12, 31).dayOfGregorianCal == 365); -assert(Date(2, 1, 1).dayOfGregorianCal == 366); - -assert(Date(0, 12, 31).dayOfGregorianCal == 0); -assert(Date(0, 1, 1).dayOfGregorianCal == -365); -assert(Date(-1, 12, 31).dayOfGregorianCal == -366); - -assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120); -assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137); --------------------- +/ - @property int dayOfGregorianCal() const pure nothrow + @property int dayOfGregorianCal() @safe const pure nothrow { if(isAD) { @@ -12207,8 +11571,8 @@ assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137); } } - //Verify Examples. - version(testStdDateTime) unittest + /// + unittest { assert(Date(1, 1, 1).dayOfGregorianCal == 1); assert(Date(1, 12, 31).dayOfGregorianCal == 365); @@ -12222,10 +11586,10 @@ assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137); assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137); } - version(testStdDateTime) unittest + unittest { foreach(gd; chain(testGregDaysBC, testGregDaysAD)) - _assertPred!"=="(gd.date.dayOfGregorianCal, gd.day); + assert(gd.date.dayOfGregorianCal == gd.day); auto date = Date(1999, 7, 6); const cdate = Date(1999, 7, 6); @@ -12240,81 +11604,49 @@ assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137); Params: day = The day of the Gregorian Calendar to set this $(LREF Date) to. - - Examples: --------------------- -auto date = Date.init; -date.dayOfGregorianCal = 1; -assert(date == Date(1, 1, 1)); - -date.dayOfGregorianCal = 365; -assert(date == Date(1, 12, 31)); - -date.dayOfGregorianCal = 366; -assert(date == Date(2, 1, 1)); - -date.dayOfGregorianCal = 0; -assert(date == Date(0, 12, 31)); - -date.dayOfGregorianCal = -365; -assert(date == Date(-0, 1, 1)); - -date.dayOfGregorianCal = -366; -assert(date == Date(-1, 12, 31)); - -date.dayOfGregorianCal = 730_120; -assert(date == Date(2000, 1, 1)); - -date.dayOfGregorianCal = 734_137; -assert(date == Date(2010, 12, 31)); --------------------- +/ - @property void dayOfGregorianCal(int day) pure nothrow + @property void dayOfGregorianCal(int day) @safe pure nothrow { this = Date(day); } + /// unittest { - version(testStdDateTime) - { - { - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, date.dayOfGregorianCal = 187)); - static assert(!__traits(compiles, cdate.dayOfGregorianCal = 187)); - static assert(!__traits(compiles, idate.dayOfGregorianCal = 187)); - } + auto date = Date.init; + date.dayOfGregorianCal = 1; + assert(date == Date(1, 1, 1)); - //Verify Examples. - { - auto date = Date.init; - date.dayOfGregorianCal = 1; - assert(date == Date(1, 1, 1)); + date.dayOfGregorianCal = 365; + assert(date == Date(1, 12, 31)); - date.dayOfGregorianCal = 365; - assert(date == Date(1, 12, 31)); + date.dayOfGregorianCal = 366; + assert(date == Date(2, 1, 1)); - date.dayOfGregorianCal = 366; - assert(date == Date(2, 1, 1)); + date.dayOfGregorianCal = 0; + assert(date == Date(0, 12, 31)); - date.dayOfGregorianCal = 0; - assert(date == Date(0, 12, 31)); + date.dayOfGregorianCal = -365; + assert(date == Date(-0, 1, 1)); - date.dayOfGregorianCal = -365; - assert(date == Date(-0, 1, 1)); + date.dayOfGregorianCal = -366; + assert(date == Date(-1, 12, 31)); - date.dayOfGregorianCal = -366; - assert(date == Date(-1, 12, 31)); + date.dayOfGregorianCal = 730_120; + assert(date == Date(2000, 1, 1)); - date.dayOfGregorianCal = 730_120; - assert(date == Date(2000, 1, 1)); + date.dayOfGregorianCal = 734_137; + assert(date == Date(2010, 12, 31)); + } - date.dayOfGregorianCal = 734_137; - assert(date == Date(2010, 12, 31)); - } - } + unittest + { + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, date.dayOfGregorianCal = 187)); + static assert(!__traits(compiles, cdate.dayOfGregorianCal = 187)); + static assert(!__traits(compiles, idate.dayOfGregorianCal = 187)); } @@ -12324,7 +11656,7 @@ assert(date == Date(2010, 12, 31)); See_Also: $(WEB en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) +/ - @property ubyte isoWeek() const pure nothrow + @property ubyte isoWeek() @safe const pure nothrow { immutable weekday = dayOfWeek; immutable adjustedWeekday = weekday == DayOfWeek.sun ? 7 : weekday; @@ -12360,81 +11692,70 @@ assert(date == Date(2010, 12, 31)); unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(Date(2009, 12, 28).isoWeek, 53); - _assertPred!"=="(Date(2009, 12, 29).isoWeek, 53); - _assertPred!"=="(Date(2009, 12, 30).isoWeek, 53); - _assertPred!"=="(Date(2009, 12, 31).isoWeek, 53); - _assertPred!"=="(Date(2010, 1, 1).isoWeek, 53); - _assertPred!"=="(Date(2010, 1, 2).isoWeek, 53); - _assertPred!"=="(Date(2010, 1, 3).isoWeek, 53); - _assertPred!"=="(Date(2010, 1, 4).isoWeek, 1); - _assertPred!"=="(Date(2010, 1, 5).isoWeek, 1); - _assertPred!"=="(Date(2010, 1, 6).isoWeek, 1); - _assertPred!"=="(Date(2010, 1, 7).isoWeek, 1); - _assertPred!"=="(Date(2010, 1, 8).isoWeek, 1); - _assertPred!"=="(Date(2010, 1, 9).isoWeek, 1); - _assertPred!"=="(Date(2010, 1, 10).isoWeek, 1); - _assertPred!"=="(Date(2010, 1, 11).isoWeek, 2); - _assertPred!"=="(Date(2010, 12, 31).isoWeek, 52); - - _assertPred!"=="(Date(2004, 12, 26).isoWeek, 52); - _assertPred!"=="(Date(2004, 12, 27).isoWeek, 53); - _assertPred!"=="(Date(2004, 12, 28).isoWeek, 53); - _assertPred!"=="(Date(2004, 12, 29).isoWeek, 53); - _assertPred!"=="(Date(2004, 12, 30).isoWeek, 53); - _assertPred!"=="(Date(2004, 12, 31).isoWeek, 53); - _assertPred!"=="(Date(2005, 1, 1).isoWeek, 53); - _assertPred!"=="(Date(2005, 1, 2).isoWeek, 53); - - _assertPred!"=="(Date(2005, 12, 31).isoWeek, 52); - _assertPred!"=="(Date(2007, 1, 1).isoWeek, 1); - - _assertPred!"=="(Date(2007, 12, 30).isoWeek, 52); - _assertPred!"=="(Date(2007, 12, 31).isoWeek, 1); - _assertPred!"=="(Date(2008, 1, 1).isoWeek, 1); - - _assertPred!"=="(Date(2008, 12, 28).isoWeek, 52); - _assertPred!"=="(Date(2008, 12, 29).isoWeek, 1); - _assertPred!"=="(Date(2008, 12, 30).isoWeek, 1); - _assertPred!"=="(Date(2008, 12, 31).isoWeek, 1); - _assertPred!"=="(Date(2009, 1, 1).isoWeek, 1); - _assertPred!"=="(Date(2009, 1, 2).isoWeek, 1); - _assertPred!"=="(Date(2009, 1, 3).isoWeek, 1); - _assertPred!"=="(Date(2009, 1, 4).isoWeek, 1); - - //Test B.C. - //The algorithm should work identically for both A.D. and B.C. since - //it doesn't really take the year into account, so B.C. testing - //probably isn't really needed. - _assertPred!"=="(Date(0, 12, 31).isoWeek, 52); - _assertPred!"=="(Date(0, 1, 4).isoWeek, 1); - _assertPred!"=="(Date(0, 1, 1).isoWeek, 52); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.isoWeek == 3)); - static assert(!__traits(compiles, cdate.isoWeek = 3)); - static assert(__traits(compiles, idate.isoWeek == 3)); - static assert(!__traits(compiles, idate.isoWeek = 3)); - } + //Test A.D. + assert(Date(2009, 12, 28).isoWeek == 53); + assert(Date(2009, 12, 29).isoWeek == 53); + assert(Date(2009, 12, 30).isoWeek == 53); + assert(Date(2009, 12, 31).isoWeek == 53); + assert(Date(2010, 1, 1).isoWeek == 53); + assert(Date(2010, 1, 2).isoWeek == 53); + assert(Date(2010, 1, 3).isoWeek == 53); + assert(Date(2010, 1, 4).isoWeek == 1); + assert(Date(2010, 1, 5).isoWeek == 1); + assert(Date(2010, 1, 6).isoWeek == 1); + assert(Date(2010, 1, 7).isoWeek == 1); + assert(Date(2010, 1, 8).isoWeek == 1); + assert(Date(2010, 1, 9).isoWeek == 1); + assert(Date(2010, 1, 10).isoWeek == 1); + assert(Date(2010, 1, 11).isoWeek == 2); + assert(Date(2010, 12, 31).isoWeek == 52); + + assert(Date(2004, 12, 26).isoWeek == 52); + assert(Date(2004, 12, 27).isoWeek == 53); + assert(Date(2004, 12, 28).isoWeek == 53); + assert(Date(2004, 12, 29).isoWeek == 53); + assert(Date(2004, 12, 30).isoWeek == 53); + assert(Date(2004, 12, 31).isoWeek == 53); + assert(Date(2005, 1, 1).isoWeek == 53); + assert(Date(2005, 1, 2).isoWeek == 53); + + assert(Date(2005, 12, 31).isoWeek == 52); + assert(Date(2007, 1, 1).isoWeek == 1); + + assert(Date(2007, 12, 30).isoWeek == 52); + assert(Date(2007, 12, 31).isoWeek == 1); + assert(Date(2008, 1, 1).isoWeek == 1); + + assert(Date(2008, 12, 28).isoWeek == 52); + assert(Date(2008, 12, 29).isoWeek == 1); + assert(Date(2008, 12, 30).isoWeek == 1); + assert(Date(2008, 12, 31).isoWeek == 1); + assert(Date(2009, 1, 1).isoWeek == 1); + assert(Date(2009, 1, 2).isoWeek == 1); + assert(Date(2009, 1, 3).isoWeek == 1); + assert(Date(2009, 1, 4).isoWeek == 1); + + //Test B.C. + //The algorithm should work identically for both A.D. and B.C. since + //it doesn't really take the year into account, so B.C. testing + //probably isn't really needed. + assert(Date(0, 12, 31).isoWeek == 52); + assert(Date(0, 1, 4).isoWeek == 1); + assert(Date(0, 1, 1).isoWeek == 52); + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.isoWeek == 3)); + static assert(!__traits(compiles, cdate.isoWeek = 3)); + static assert(__traits(compiles, idate.isoWeek == 3)); + static assert(!__traits(compiles, idate.isoWeek = 3)); } /++ $(LREF Date) for the last day in the month that this $(LREF Date) is in. - - Examples: --------------------- -assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31)); -assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28)); -assert(Date(2000, 2, 7).endOfMonth == Date(1999, 2, 29)); -assert(Date(2000, 6, 4).endOfMonth == Date(1999, 6, 30)); --------------------- +/ - @property Date endOfMonth() const pure nothrow + @property Date endOfMonth() @safe const pure nothrow { try return Date(_year, _month, maxDay(_year, _month)); @@ -12442,155 +11763,139 @@ assert(Date(2000, 6, 4).endOfMonth == Date(1999, 6, 30)); assert(0, "Date's constructor threw."); } + /// unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(Date(1999, 1, 1).endOfMonth, Date(1999, 1, 31)); - _assertPred!"=="(Date(1999, 2, 1).endOfMonth, Date(1999, 2, 28)); - _assertPred!"=="(Date(2000, 2, 1).endOfMonth, Date(2000, 2, 29)); - _assertPred!"=="(Date(1999, 3, 1).endOfMonth, Date(1999, 3, 31)); - _assertPred!"=="(Date(1999, 4, 1).endOfMonth, Date(1999, 4, 30)); - _assertPred!"=="(Date(1999, 5, 1).endOfMonth, Date(1999, 5, 31)); - _assertPred!"=="(Date(1999, 6, 1).endOfMonth, Date(1999, 6, 30)); - _assertPred!"=="(Date(1999, 7, 1).endOfMonth, Date(1999, 7, 31)); - _assertPred!"=="(Date(1999, 8, 1).endOfMonth, Date(1999, 8, 31)); - _assertPred!"=="(Date(1999, 9, 1).endOfMonth, Date(1999, 9, 30)); - _assertPred!"=="(Date(1999, 10, 1).endOfMonth, Date(1999, 10, 31)); - _assertPred!"=="(Date(1999, 11, 1).endOfMonth, Date(1999, 11, 30)); - _assertPred!"=="(Date(1999, 12, 1).endOfMonth, Date(1999, 12, 31)); + assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31)); + assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28)); + assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29)); + assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30)); + } - //Test B.C. - _assertPred!"=="(Date(-1999, 1, 1).endOfMonth, Date(-1999, 1, 31)); - _assertPred!"=="(Date(-1999, 2, 1).endOfMonth, Date(-1999, 2, 28)); - _assertPred!"=="(Date(-2000, 2, 1).endOfMonth, Date(-2000, 2, 29)); - _assertPred!"=="(Date(-1999, 3, 1).endOfMonth, Date(-1999, 3, 31)); - _assertPred!"=="(Date(-1999, 4, 1).endOfMonth, Date(-1999, 4, 30)); - _assertPred!"=="(Date(-1999, 5, 1).endOfMonth, Date(-1999, 5, 31)); - _assertPred!"=="(Date(-1999, 6, 1).endOfMonth, Date(-1999, 6, 30)); - _assertPred!"=="(Date(-1999, 7, 1).endOfMonth, Date(-1999, 7, 31)); - _assertPred!"=="(Date(-1999, 8, 1).endOfMonth, Date(-1999, 8, 31)); - _assertPred!"=="(Date(-1999, 9, 1).endOfMonth, Date(-1999, 9, 30)); - _assertPred!"=="(Date(-1999, 10, 1).endOfMonth, Date(-1999, 10, 31)); - _assertPred!"=="(Date(-1999, 11, 1).endOfMonth, Date(-1999, 11, 30)); - _assertPred!"=="(Date(-1999, 12, 1).endOfMonth, Date(-1999, 12, 31)); + unittest + { + //Test A.D. + assert(Date(1999, 1, 1).endOfMonth == Date(1999, 1, 31)); + assert(Date(1999, 2, 1).endOfMonth == Date(1999, 2, 28)); + assert(Date(2000, 2, 1).endOfMonth == Date(2000, 2, 29)); + assert(Date(1999, 3, 1).endOfMonth == Date(1999, 3, 31)); + assert(Date(1999, 4, 1).endOfMonth == Date(1999, 4, 30)); + assert(Date(1999, 5, 1).endOfMonth == Date(1999, 5, 31)); + assert(Date(1999, 6, 1).endOfMonth == Date(1999, 6, 30)); + assert(Date(1999, 7, 1).endOfMonth == Date(1999, 7, 31)); + assert(Date(1999, 8, 1).endOfMonth == Date(1999, 8, 31)); + assert(Date(1999, 9, 1).endOfMonth == Date(1999, 9, 30)); + assert(Date(1999, 10, 1).endOfMonth == Date(1999, 10, 31)); + assert(Date(1999, 11, 1).endOfMonth == Date(1999, 11, 30)); + assert(Date(1999, 12, 1).endOfMonth == Date(1999, 12, 31)); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.endOfMonth = Date(1999, 7, 30))); - static assert(!__traits(compiles, idate.endOfMonth = Date(1999, 7, 30))); + //Test B.C. + assert(Date(-1999, 1, 1).endOfMonth == Date(-1999, 1, 31)); + assert(Date(-1999, 2, 1).endOfMonth == Date(-1999, 2, 28)); + assert(Date(-2000, 2, 1).endOfMonth == Date(-2000, 2, 29)); + assert(Date(-1999, 3, 1).endOfMonth == Date(-1999, 3, 31)); + assert(Date(-1999, 4, 1).endOfMonth == Date(-1999, 4, 30)); + assert(Date(-1999, 5, 1).endOfMonth == Date(-1999, 5, 31)); + assert(Date(-1999, 6, 1).endOfMonth == Date(-1999, 6, 30)); + assert(Date(-1999, 7, 1).endOfMonth == Date(-1999, 7, 31)); + assert(Date(-1999, 8, 1).endOfMonth == Date(-1999, 8, 31)); + assert(Date(-1999, 9, 1).endOfMonth == Date(-1999, 9, 30)); + assert(Date(-1999, 10, 1).endOfMonth == Date(-1999, 10, 31)); + assert(Date(-1999, 11, 1).endOfMonth == Date(-1999, 11, 30)); + assert(Date(-1999, 12, 1).endOfMonth == Date(-1999, 12, 31)); - //Verify Examples. - assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31)); - assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28)); - assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29)); - assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30)); - } + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.endOfMonth = Date(1999, 7, 30))); + static assert(!__traits(compiles, idate.endOfMonth = Date(1999, 7, 30))); } /++ The last day in the month that this $(LREF Date) is in. - - Examples: --------------------- -assert(Date(1999, 1, 6).daysInMonth == 31); -assert(Date(1999, 2, 7).daysInMonth == 28); -assert(Date(2000, 2, 7).daysInMonth == 29); -assert(Date(2000, 6, 4).daysInMonth == 30); --------------------- +/ - @property ubyte daysInMonth() const pure nothrow + @property ubyte daysInMonth() @safe const pure nothrow { return maxDay(_year, _month); } + /// unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(Date(1999, 1, 1).daysInMonth, 31); - _assertPred!"=="(Date(1999, 2, 1).daysInMonth, 28); - _assertPred!"=="(Date(2000, 2, 1).daysInMonth, 29); - _assertPred!"=="(Date(1999, 3, 1).daysInMonth, 31); - _assertPred!"=="(Date(1999, 4, 1).daysInMonth, 30); - _assertPred!"=="(Date(1999, 5, 1).daysInMonth, 31); - _assertPred!"=="(Date(1999, 6, 1).daysInMonth, 30); - _assertPred!"=="(Date(1999, 7, 1).daysInMonth, 31); - _assertPred!"=="(Date(1999, 8, 1).daysInMonth, 31); - _assertPred!"=="(Date(1999, 9, 1).daysInMonth, 30); - _assertPred!"=="(Date(1999, 10, 1).daysInMonth, 31); - _assertPred!"=="(Date(1999, 11, 1).daysInMonth, 30); - _assertPred!"=="(Date(1999, 12, 1).daysInMonth, 31); + assert(Date(1999, 1, 6).daysInMonth == 31); + assert(Date(1999, 2, 7).daysInMonth == 28); + assert(Date(2000, 2, 7).daysInMonth == 29); + assert(Date(2000, 6, 4).daysInMonth == 30); + } - //Test B.C. - _assertPred!"=="(Date(-1999, 1, 1).daysInMonth, 31); - _assertPred!"=="(Date(-1999, 2, 1).daysInMonth, 28); - _assertPred!"=="(Date(-2000, 2, 1).daysInMonth, 29); - _assertPred!"=="(Date(-1999, 3, 1).daysInMonth, 31); - _assertPred!"=="(Date(-1999, 4, 1).daysInMonth, 30); - _assertPred!"=="(Date(-1999, 5, 1).daysInMonth, 31); - _assertPred!"=="(Date(-1999, 6, 1).daysInMonth, 30); - _assertPred!"=="(Date(-1999, 7, 1).daysInMonth, 31); - _assertPred!"=="(Date(-1999, 8, 1).daysInMonth, 31); - _assertPred!"=="(Date(-1999, 9, 1).daysInMonth, 30); - _assertPred!"=="(Date(-1999, 10, 1).daysInMonth, 31); - _assertPred!"=="(Date(-1999, 11, 1).daysInMonth, 30); - _assertPred!"=="(Date(-1999, 12, 1).daysInMonth, 31); + unittest + { + //Test A.D. + assert(Date(1999, 1, 1).daysInMonth == 31); + assert(Date(1999, 2, 1).daysInMonth == 28); + assert(Date(2000, 2, 1).daysInMonth == 29); + assert(Date(1999, 3, 1).daysInMonth == 31); + assert(Date(1999, 4, 1).daysInMonth == 30); + assert(Date(1999, 5, 1).daysInMonth == 31); + assert(Date(1999, 6, 1).daysInMonth == 30); + assert(Date(1999, 7, 1).daysInMonth == 31); + assert(Date(1999, 8, 1).daysInMonth == 31); + assert(Date(1999, 9, 1).daysInMonth == 30); + assert(Date(1999, 10, 1).daysInMonth == 31); + assert(Date(1999, 11, 1).daysInMonth == 30); + assert(Date(1999, 12, 1).daysInMonth == 31); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.daysInMonth = 30)); - static assert(!__traits(compiles, idate.daysInMonth = 30)); + //Test B.C. + assert(Date(-1999, 1, 1).daysInMonth == 31); + assert(Date(-1999, 2, 1).daysInMonth == 28); + assert(Date(-2000, 2, 1).daysInMonth == 29); + assert(Date(-1999, 3, 1).daysInMonth == 31); + assert(Date(-1999, 4, 1).daysInMonth == 30); + assert(Date(-1999, 5, 1).daysInMonth == 31); + assert(Date(-1999, 6, 1).daysInMonth == 30); + assert(Date(-1999, 7, 1).daysInMonth == 31); + assert(Date(-1999, 8, 1).daysInMonth == 31); + assert(Date(-1999, 9, 1).daysInMonth == 30); + assert(Date(-1999, 10, 1).daysInMonth == 31); + assert(Date(-1999, 11, 1).daysInMonth == 30); + assert(Date(-1999, 12, 1).daysInMonth == 31); - //Verify Examples. - assert(Date(1999, 1, 6).daysInMonth == 31); - assert(Date(1999, 2, 7).daysInMonth == 28); - assert(Date(2000, 2, 7).daysInMonth == 29); - assert(Date(2000, 6, 4).daysInMonth == 30); - } + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate.daysInMonth = 30)); + static assert(!__traits(compiles, idate.daysInMonth = 30)); } /++ Whether the current year is a date in A.D. - - Examples: --------------------- -assert(Date(1, 1, 1).isAD); -assert(Date(2010, 12, 31).isAD); -assert(!Date(0, 12, 31).isAD); -assert(!Date(-2010, 1, 1).isAD); --------------------- +/ - @property bool isAD() const pure nothrow + @property bool isAD() @safe const pure nothrow { return _year > 0; } + /// unittest { - version(testStdDateTime) - { - assert(Date(2010, 7, 4).isAD); - assert(Date(1, 1, 1).isAD); - assert(!Date(0, 1, 1).isAD); - assert(!Date(-1, 1, 1).isAD); - assert(!Date(-2010, 7, 4).isAD); + assert(Date(1, 1, 1).isAD); + assert(Date(2010, 12, 31).isAD); + assert(!Date(0, 12, 31).isAD); + assert(!Date(-2010, 1, 1).isAD); + } - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.isAD)); - static assert(__traits(compiles, idate.isAD)); + unittest + { + assert(Date(2010, 7, 4).isAD); + assert(Date(1, 1, 1).isAD); + assert(!Date(0, 1, 1).isAD); + assert(!Date(-1, 1, 1).isAD); + assert(!Date(-2010, 7, 4).isAD); - //Verify Examples. - assert(Date(1, 1, 1).isAD); - assert(Date(2010, 12, 31).isAD); - assert(!Date(0, 12, 31).isAD); - assert(!Date(-2010, 1, 1).isAD); - } + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.isAD)); + static assert(__traits(compiles, idate.isAD)); } @@ -12598,29 +11903,26 @@ assert(!Date(-2010, 1, 1).isAD); The $(WEB en.wikipedia.org/wiki/Julian_day, Julian day) for this $(LREF Date) at noon (since the Julian day changes at noon). +/ - @property long julianDay() const pure nothrow + @property long julianDay() @safe const pure nothrow { return dayOfGregorianCal + 1_721_425; } unittest { - version(testStdDateTime) - { - _assertPred!"=="(Date(-4713, 11, 24).julianDay, 0); - _assertPred!"=="(Date(0, 12, 31).julianDay, 1_721_425); - _assertPred!"=="(Date(1, 1, 1).julianDay, 1_721_426); - _assertPred!"=="(Date(1582, 10, 15).julianDay, 2_299_161); - _assertPred!"=="(Date(1858, 11, 17).julianDay, 2_400_001); - _assertPred!"=="(Date(1982, 1, 4).julianDay, 2_444_974); - _assertPred!"=="(Date(1996, 3, 31).julianDay, 2_450_174); - _assertPred!"=="(Date(2010, 8, 24).julianDay, 2_455_433); + assert(Date(-4713, 11, 24).julianDay == 0); + assert(Date(0, 12, 31).julianDay == 1_721_425); + assert(Date(1, 1, 1).julianDay == 1_721_426); + assert(Date(1582, 10, 15).julianDay == 2_299_161); + assert(Date(1858, 11, 17).julianDay == 2_400_001); + assert(Date(1982, 1, 4).julianDay == 2_444_974); + assert(Date(1996, 3, 31).julianDay == 2_450_174); + assert(Date(2010, 8, 24).julianDay == 2_455_433); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.julianDay)); - static assert(__traits(compiles, idate.julianDay)); - } + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.julianDay)); + static assert(__traits(compiles, idate.julianDay)); } @@ -12628,38 +11930,27 @@ assert(!Date(-2010, 1, 1).isAD); The modified $(WEB en.wikipedia.org/wiki/Julian_day, Julian day) for any time on this date (since, the modified Julian day changes at midnight). +/ - @property long modJulianDay() const pure nothrow + @property long modJulianDay() @safe const pure nothrow { return julianDay - 2_400_001; } unittest { - version(testStdDateTime) - { - _assertPred!"=="(Date(1858, 11, 17).modJulianDay, 0); - _assertPred!"=="(Date(2010, 8, 24).modJulianDay, 55_432); + assert(Date(1858, 11, 17).modJulianDay == 0); + assert(Date(2010, 8, 24).modJulianDay == 55_432); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.modJulianDay)); - static assert(__traits(compiles, idate.modJulianDay)); - } + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.modJulianDay)); + static assert(__traits(compiles, idate.modJulianDay)); } /++ Converts this $(LREF Date) to a string with the format YYYYMMDD. - - Examples: --------------------- -assert(Date(2010, 7, 4).toISOString() == "20100704"); -assert(Date(1998, 12, 25).toISOString() == "19981225"); -assert(Date(0, 1, 5).toISOString() == "00000105"); -assert(Date(-4, 1, 5).toISOString() == "-00040105"); --------------------- +/ - string toISOString() const nothrow + string toISOString() @safe const pure nothrow { try { @@ -12679,50 +11970,42 @@ assert(Date(-4, 1, 5).toISOString() == "-00040105"); assert(0, "format() threw."); } + /// unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(Date(9, 12, 4).toISOString(), "00091204"); - _assertPred!"=="(Date(99, 12, 4).toISOString(), "00991204"); - _assertPred!"=="(Date(999, 12, 4).toISOString(), "09991204"); - _assertPred!"=="(Date(9999, 7, 4).toISOString(), "99990704"); - _assertPred!"=="(Date(10000, 10, 20).toISOString(), "+100001020"); + assert(Date(2010, 7, 4).toISOString() == "20100704"); + assert(Date(1998, 12, 25).toISOString() == "19981225"); + assert(Date(0, 1, 5).toISOString() == "00000105"); + assert(Date(-4, 1, 5).toISOString() == "-00040105"); + } - //Test B.C. - _assertPred!"=="(Date(0, 12, 4).toISOString(), "00001204"); - _assertPred!"=="(Date(-9, 12, 4).toISOString(), "-00091204"); - _assertPred!"=="(Date(-99, 12, 4).toISOString(), "-00991204"); - _assertPred!"=="(Date(-999, 12, 4).toISOString(), "-09991204"); - _assertPred!"=="(Date(-9999, 7, 4).toISOString(), "-99990704"); - _assertPred!"=="(Date(-10000, 10, 20).toISOString(), "-100001020"); + unittest + { + //Test A.D. + assert(Date(9, 12, 4).toISOString() == "00091204"); + assert(Date(99, 12, 4).toISOString() == "00991204"); + assert(Date(999, 12, 4).toISOString() == "09991204"); + assert(Date(9999, 7, 4).toISOString() == "99990704"); + assert(Date(10000, 10, 20).toISOString() == "+100001020"); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.toISOString())); - static assert(__traits(compiles, idate.toISOString())); + //Test B.C. + assert(Date(0, 12, 4).toISOString() == "00001204"); + assert(Date(-9, 12, 4).toISOString() == "-00091204"); + assert(Date(-99, 12, 4).toISOString() == "-00991204"); + assert(Date(-999, 12, 4).toISOString() == "-09991204"); + assert(Date(-9999, 7, 4).toISOString() == "-99990704"); + assert(Date(-10000, 10, 20).toISOString() == "-100001020"); - //Verify Examples. - assert(Date(2010, 7, 4).toISOString() == "20100704"); - assert(Date(1998, 12, 25).toISOString() == "19981225"); - assert(Date(0, 1, 5).toISOString() == "00000105"); - assert(Date(-4, 1, 5).toISOString() == "-00040105"); - } + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.toISOString())); + static assert(__traits(compiles, idate.toISOString())); } /++ Converts this $(LREF Date) to a string with the format YYYY-MM-DD. - - Examples: --------------------- -assert(Date(2010, 7, 4).toISOExtString() == "2010-07-04"); -assert(Date(1998, 12, 25).toISOExtString() == "1998-12-25"); -assert(Date(0, 1, 5).toISOExtString() == "0000-01-05"); -assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05"); --------------------- +/ - string toISOExtString() const nothrow + string toISOExtString() @safe const pure nothrow { try { @@ -12742,137 +12025,110 @@ assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05"); assert(0, "format() threw."); } + /// unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(Date(9, 12, 4).toISOExtString(), "0009-12-04"); - _assertPred!"=="(Date(99, 12, 4).toISOExtString(), "0099-12-04"); - _assertPred!"=="(Date(999, 12, 4).toISOExtString(), "0999-12-04"); - _assertPred!"=="(Date(9999, 7, 4).toISOExtString(), "9999-07-04"); - _assertPred!"=="(Date(10000, 10, 20).toISOExtString(), "+10000-10-20"); + assert(Date(2010, 7, 4).toISOExtString() == "2010-07-04"); + assert(Date(1998, 12, 25).toISOExtString() == "1998-12-25"); + assert(Date(0, 1, 5).toISOExtString() == "0000-01-05"); + assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05"); + } - //Test B.C. - _assertPred!"=="(Date(0, 12, 4).toISOExtString(), "0000-12-04"); - _assertPred!"=="(Date(-9, 12, 4).toISOExtString(), "-0009-12-04"); - _assertPred!"=="(Date(-99, 12, 4).toISOExtString(), "-0099-12-04"); - _assertPred!"=="(Date(-999, 12, 4).toISOExtString(), "-0999-12-04"); - _assertPred!"=="(Date(-9999, 7, 4).toISOExtString(), "-9999-07-04"); - _assertPred!"=="(Date(-10000, 10, 20).toISOExtString(), "-10000-10-20"); + unittest + { + //Test A.D. + assert(Date(9, 12, 4).toISOExtString() == "0009-12-04"); + assert(Date(99, 12, 4).toISOExtString() == "0099-12-04"); + assert(Date(999, 12, 4).toISOExtString() == "0999-12-04"); + assert(Date(9999, 7, 4).toISOExtString() == "9999-07-04"); + assert(Date(10000, 10, 20).toISOExtString() == "+10000-10-20"); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.toISOExtString())); - static assert(__traits(compiles, idate.toISOExtString())); + //Test B.C. + assert(Date(0, 12, 4).toISOExtString() == "0000-12-04"); + assert(Date(-9, 12, 4).toISOExtString() == "-0009-12-04"); + assert(Date(-99, 12, 4).toISOExtString() == "-0099-12-04"); + assert(Date(-999, 12, 4).toISOExtString() == "-0999-12-04"); + assert(Date(-9999, 7, 4).toISOExtString() == "-9999-07-04"); + assert(Date(-10000, 10, 20).toISOExtString() == "-10000-10-20"); - //Verify Examples. - assert(Date(2010, 7, 4).toISOExtString() == "2010-07-04"); - assert(Date(1998, 12, 25).toISOExtString() == "1998-12-25"); - assert(Date(0, 1, 5).toISOExtString() == "0000-01-05"); - assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05"); - } + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.toISOExtString())); + static assert(__traits(compiles, idate.toISOExtString())); } /++ Converts this $(LREF Date) to a string with the format YYYY-Mon-DD. - - Examples: --------------------- -assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04"); -assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25"); -assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05"); -assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05"); --------------------- +/ - string toSimpleString() const nothrow + string toSimpleString() @safe const pure nothrow { try { if(_year >= 0) { if(_year < 10_000) - return format("%04d-%s-%02d", _year, monthToString(_month, false), _day); + return format("%04d-%s-%02d", _year, monthToString(_month), _day); else - return format("+%05d-%s-%02d", _year, monthToString(_month, false), _day); + return format("+%05d-%s-%02d", _year, monthToString(_month), _day); } else if(_year > -10_000) - return format("%05d-%s-%02d", _year, monthToString(_month, false), _day); + return format("%05d-%s-%02d", _year, monthToString(_month), _day); else - return format("%06d-%s-%02d", _year, monthToString(_month, false), _day); + return format("%06d-%s-%02d", _year, monthToString(_month), _day); } catch(Exception e) assert(0, "format() threw."); } + /// unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(Date(9, 12, 4).toSimpleString(), "0009-Dec-04"); - _assertPred!"=="(Date(99, 12, 4).toSimpleString(), "0099-Dec-04"); - _assertPred!"=="(Date(999, 12, 4).toSimpleString(), "0999-Dec-04"); - _assertPred!"=="(Date(9999, 7, 4).toSimpleString(), "9999-Jul-04"); - _assertPred!"=="(Date(10000, 10, 20).toSimpleString(), "+10000-Oct-20"); - - //Test B.C. - _assertPred!"=="(Date(0, 12, 4).toSimpleString(), "0000-Dec-04"); - _assertPred!"=="(Date(-9, 12, 4).toSimpleString(), "-0009-Dec-04"); - _assertPred!"=="(Date(-99, 12, 4).toSimpleString(), "-0099-Dec-04"); - _assertPred!"=="(Date(-999, 12, 4).toSimpleString(), "-0999-Dec-04"); - _assertPred!"=="(Date(-9999, 7, 4).toSimpleString(), "-9999-Jul-04"); - _assertPred!"=="(Date(-10000, 10, 20).toSimpleString(), "-10000-Oct-20"); - - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, cdate.toSimpleString())); - static assert(__traits(compiles, idate.toSimpleString())); - - //Verify Examples. - assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04"); - assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25"); - assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05"); - assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05"); - } + assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04"); + assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25"); + assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05"); + assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05"); } - //TODO Add a function which returns a string in a user-specified format. + unittest + { + //Test A.D. + assert(Date(9, 12, 4).toSimpleString() == "0009-Dec-04"); + assert(Date(99, 12, 4).toSimpleString() == "0099-Dec-04"); + assert(Date(999, 12, 4).toSimpleString() == "0999-Dec-04"); + assert(Date(9999, 7, 4).toSimpleString() == "9999-Jul-04"); + assert(Date(10000, 10, 20).toSimpleString() == "+10000-Oct-20"); + //Test B.C. + assert(Date(0, 12, 4).toSimpleString() == "0000-Dec-04"); + assert(Date(-9, 12, 4).toSimpleString() == "-0009-Dec-04"); + assert(Date(-99, 12, 4).toSimpleString() == "-0099-Dec-04"); + assert(Date(-999, 12, 4).toSimpleString() == "-0999-Dec-04"); + assert(Date(-9999, 7, 4).toSimpleString() == "-9999-Jul-04"); + assert(Date(-10000, 10, 20).toSimpleString() == "-10000-Oct-20"); - /+ - Converts this $(LREF Date) to a string. - +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() - { - return toSimpleString(); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, cdate.toSimpleString())); + static assert(__traits(compiles, idate.toSimpleString())); } + /++ Converts this $(LREF Date) to a string. +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() const nothrow + string toString() @safe const pure nothrow { return toSimpleString(); } unittest { - version(testStdDateTime) - { - auto date = Date(1999, 7, 6); - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(__traits(compiles, date.toString())); - static assert(__traits(compiles, cdate.toString())); - static assert(__traits(compiles, idate.toString())); - } + auto date = Date(1999, 7, 6); + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(__traits(compiles, date.toString())); + static assert(__traits(compiles, cdate.toString())); + static assert(__traits(compiles, idate.toString())); } @@ -12886,17 +12142,8 @@ assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05"); Throws: $(LREF DateTimeException) if the given string is not in the ISO format or if the resulting $(LREF Date) would not be valid. - - Examples: --------------------- -assert(Date.fromISOString("20100704") == Date(2010, 7, 4)); -assert(Date.fromISOString("19981225") == Date(1998, 12, 25)); -assert(Date.fromISOString("00000105") == Date(0, 1, 5)); -assert(Date.fromISOString("-00040105") == Date(-4, 1, 5)); -assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4)); --------------------- +/ - static Date fromISOString(S)(in S isoString) + static Date fromISOString(S)(in S isoString) @safe pure if(isSomeString!S) { auto dstr = to!dstring(strip(isoString)); @@ -12907,96 +12154,96 @@ assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4)); auto month = dstr[$-4 .. $-2]; auto year = dstr[0 .. $-4]; - enforce(!canFind!(not!isDigit)(day), new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(!canFind!(not!isDigit)(month), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(all!isDigit(day), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(all!isDigit(month), new DateTimeException(format("Invalid ISO String: %s", isoString))); if(year.length > 4) { enforce(year.startsWith("-") || year.startsWith("+"), new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(!canFind!(not!isDigit)(year[1..$]), + enforce(all!isDigit(year[1..$]), new DateTimeException(format("Invalid ISO String: %s", isoString))); } else - enforce(!canFind!(not!isDigit)(year), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(all!isDigit(year), new DateTimeException(format("Invalid ISO String: %s", isoString))); return Date(to!short(year), to!ubyte(month), to!ubyte(day)); } + /// unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException(Date.fromISOString("")); - assertThrown!DateTimeException(Date.fromISOString("990704")); - assertThrown!DateTimeException(Date.fromISOString("0100704")); - assertThrown!DateTimeException(Date.fromISOString("2010070")); - assertThrown!DateTimeException(Date.fromISOString("2010070 ")); - assertThrown!DateTimeException(Date.fromISOString("120100704")); - assertThrown!DateTimeException(Date.fromISOString("-0100704")); - assertThrown!DateTimeException(Date.fromISOString("+0100704")); - assertThrown!DateTimeException(Date.fromISOString("2010070a")); - assertThrown!DateTimeException(Date.fromISOString("20100a04")); - assertThrown!DateTimeException(Date.fromISOString("2010a704")); - - assertThrown!DateTimeException(Date.fromISOString("99-07-04")); - assertThrown!DateTimeException(Date.fromISOString("010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-07-0")); - assertThrown!DateTimeException(Date.fromISOString("2010-07-0 ")); - assertThrown!DateTimeException(Date.fromISOString("12010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("-010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("+010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-07-0a")); - assertThrown!DateTimeException(Date.fromISOString("2010-0a-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-a7-04")); - assertThrown!DateTimeException(Date.fromISOString("2010/07/04")); - assertThrown!DateTimeException(Date.fromISOString("2010/7/04")); - assertThrown!DateTimeException(Date.fromISOString("2010/7/4")); - assertThrown!DateTimeException(Date.fromISOString("2010/07/4")); - assertThrown!DateTimeException(Date.fromISOString("2010-7-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-7-4")); - assertThrown!DateTimeException(Date.fromISOString("2010-07-4")); - - assertThrown!DateTimeException(Date.fromISOString("99Jul04")); - assertThrown!DateTimeException(Date.fromISOString("010Jul04")); - assertThrown!DateTimeException(Date.fromISOString("2010Jul0")); - assertThrown!DateTimeException(Date.fromISOString("2010Jul0 ")); - assertThrown!DateTimeException(Date.fromISOString("12010Jul04")); - assertThrown!DateTimeException(Date.fromISOString("-010Jul04")); - assertThrown!DateTimeException(Date.fromISOString("+010Jul04")); - assertThrown!DateTimeException(Date.fromISOString("2010Jul0a")); - assertThrown!DateTimeException(Date.fromISOString("2010Jua04")); - assertThrown!DateTimeException(Date.fromISOString("2010aul04")); - - assertThrown!DateTimeException(Date.fromISOString("99-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0 ")); - assertThrown!DateTimeException(Date.fromISOString("12010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("-010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("+010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0a")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jua-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jal-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-aul-04")); - - assertThrown!DateTimeException(Date.fromISOString("2010-07-04")); - assertThrown!DateTimeException(Date.fromISOString("2010-Jul-04")); - - _assertPred!"=="(Date.fromISOString("19990706"), Date(1999, 7, 6)); - _assertPred!"=="(Date.fromISOString("-19990706"), Date(-1999, 7, 6)); - _assertPred!"=="(Date.fromISOString("+019990706"), Date(1999, 7, 6)); - _assertPred!"=="(Date.fromISOString("19990706 "), Date(1999, 7, 6)); - _assertPred!"=="(Date.fromISOString(" 19990706"), Date(1999, 7, 6)); - _assertPred!"=="(Date.fromISOString(" 19990706 "), Date(1999, 7, 6)); - - //Verify Examples. - assert(Date.fromISOString("20100704") == Date(2010, 7, 4)); - assert(Date.fromISOString("19981225") == Date(1998, 12, 25)); - assert(Date.fromISOString("00000105") == Date(0, 1, 5)); - assert(Date.fromISOString("-00040105") == Date(-4, 1, 5)); - assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4)); - } + assert(Date.fromISOString("20100704") == Date(2010, 7, 4)); + assert(Date.fromISOString("19981225") == Date(1998, 12, 25)); + assert(Date.fromISOString("00000105") == Date(0, 1, 5)); + assert(Date.fromISOString("-00040105") == Date(-4, 1, 5)); + assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4)); + } + + unittest + { + assertThrown!DateTimeException(Date.fromISOString("")); + assertThrown!DateTimeException(Date.fromISOString("990704")); + assertThrown!DateTimeException(Date.fromISOString("0100704")); + assertThrown!DateTimeException(Date.fromISOString("2010070")); + assertThrown!DateTimeException(Date.fromISOString("2010070 ")); + assertThrown!DateTimeException(Date.fromISOString("120100704")); + assertThrown!DateTimeException(Date.fromISOString("-0100704")); + assertThrown!DateTimeException(Date.fromISOString("+0100704")); + assertThrown!DateTimeException(Date.fromISOString("2010070a")); + assertThrown!DateTimeException(Date.fromISOString("20100a04")); + assertThrown!DateTimeException(Date.fromISOString("2010a704")); + + assertThrown!DateTimeException(Date.fromISOString("99-07-04")); + assertThrown!DateTimeException(Date.fromISOString("010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-07-0")); + assertThrown!DateTimeException(Date.fromISOString("2010-07-0 ")); + assertThrown!DateTimeException(Date.fromISOString("12010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("-010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("+010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-07-0a")); + assertThrown!DateTimeException(Date.fromISOString("2010-0a-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-a7-04")); + assertThrown!DateTimeException(Date.fromISOString("2010/07/04")); + assertThrown!DateTimeException(Date.fromISOString("2010/7/04")); + assertThrown!DateTimeException(Date.fromISOString("2010/7/4")); + assertThrown!DateTimeException(Date.fromISOString("2010/07/4")); + assertThrown!DateTimeException(Date.fromISOString("2010-7-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-7-4")); + assertThrown!DateTimeException(Date.fromISOString("2010-07-4")); + + assertThrown!DateTimeException(Date.fromISOString("99Jul04")); + assertThrown!DateTimeException(Date.fromISOString("010Jul04")); + assertThrown!DateTimeException(Date.fromISOString("2010Jul0")); + assertThrown!DateTimeException(Date.fromISOString("2010Jul0 ")); + assertThrown!DateTimeException(Date.fromISOString("12010Jul04")); + assertThrown!DateTimeException(Date.fromISOString("-010Jul04")); + assertThrown!DateTimeException(Date.fromISOString("+010Jul04")); + assertThrown!DateTimeException(Date.fromISOString("2010Jul0a")); + assertThrown!DateTimeException(Date.fromISOString("2010Jua04")); + assertThrown!DateTimeException(Date.fromISOString("2010aul04")); + + assertThrown!DateTimeException(Date.fromISOString("99-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0 ")); + assertThrown!DateTimeException(Date.fromISOString("12010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("-010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("+010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jul-0a")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jua-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jal-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-aul-04")); + + assertThrown!DateTimeException(Date.fromISOString("2010-07-04")); + assertThrown!DateTimeException(Date.fromISOString("2010-Jul-04")); + + assert(Date.fromISOString("19990706") == Date(1999, 7, 6)); + assert(Date.fromISOString("-19990706") == Date(-1999, 7, 6)); + assert(Date.fromISOString("+019990706") == Date(1999, 7, 6)); + assert(Date.fromISOString("19990706 ") == Date(1999, 7, 6)); + assert(Date.fromISOString(" 19990706") == Date(1999, 7, 6)); + assert(Date.fromISOString(" 19990706 ") == Date(1999, 7, 6)); } @@ -13011,17 +12258,8 @@ assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4)); Throws: $(LREF DateTimeException) if the given string is not in the ISO Extended format or if the resulting $(LREF Date) would not be valid. - - Examples: --------------------- -assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4)); -assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25)); -assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5)); -assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5)); -assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4)); --------------------- +/ - static Date fromISOExtString(S)(in S isoExtString) + static Date fromISOExtString(S)(in S isoExtString) @safe pure if(isSomeString!(S)) { auto dstr = to!dstring(strip(isoExtString)); @@ -13034,99 +12272,99 @@ assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4)); enforce(dstr[$-3] == '-', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); enforce(dstr[$-6] == '-', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(!canFind!(not!isDigit)(day), + enforce(all!isDigit(day), new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(!canFind!(not!isDigit)(month), + enforce(all!isDigit(month), new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); if(year.length > 4) { enforce(year.startsWith("-") || year.startsWith("+"), new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(!canFind!(not!isDigit)(year[1..$]), + enforce(all!isDigit(year[1..$]), new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); } else - enforce(!canFind!(not!isDigit)(year), + enforce(all!isDigit(year), new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); return Date(to!short(year), to!ubyte(month), to!ubyte(day)); } + /// unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException(Date.fromISOExtString("")); - assertThrown!DateTimeException(Date.fromISOExtString("990704")); - assertThrown!DateTimeException(Date.fromISOExtString("0100704")); - assertThrown!DateTimeException(Date.fromISOExtString("2010070")); - assertThrown!DateTimeException(Date.fromISOExtString("2010070 ")); - assertThrown!DateTimeException(Date.fromISOExtString("120100704")); - assertThrown!DateTimeException(Date.fromISOExtString("-0100704")); - assertThrown!DateTimeException(Date.fromISOExtString("+0100704")); - assertThrown!DateTimeException(Date.fromISOExtString("2010070a")); - assertThrown!DateTimeException(Date.fromISOExtString("20100a04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010a704")); - - assertThrown!DateTimeException(Date.fromISOExtString("99-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("010-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0 ")); - assertThrown!DateTimeException(Date.fromISOExtString("12010-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("-010-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("+010-07-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0a")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-0a-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-a7-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010/07/04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010/7/04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010/7/4")); - assertThrown!DateTimeException(Date.fromISOExtString("2010/07/4")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-7-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-7-4")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-07-4")); - - assertThrown!DateTimeException(Date.fromISOExtString("99Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("010Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0 ")); - assertThrown!DateTimeException(Date.fromISOExtString("12010Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("-010Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("+010Jul04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0a")); - assertThrown!DateTimeException(Date.fromISOExtString("2010Jua04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010aul04")); - - assertThrown!DateTimeException(Date.fromISOExtString("99-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0")); - assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0 ")); - assertThrown!DateTimeException(Date.fromISOExtString("12010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("-010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("+010-Jul-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0a")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jua-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jal-04")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-aul-04")); - - assertThrown!DateTimeException(Date.fromISOExtString("20100704")); - assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-04")); - - _assertPred!"=="(Date.fromISOExtString("1999-07-06"), Date(1999, 7, 6)); - _assertPred!"=="(Date.fromISOExtString("-1999-07-06"), Date(-1999, 7, 6)); - _assertPred!"=="(Date.fromISOExtString("+01999-07-06"), Date(1999, 7, 6)); - _assertPred!"=="(Date.fromISOExtString("1999-07-06 "), Date(1999, 7, 6)); - _assertPred!"=="(Date.fromISOExtString(" 1999-07-06"), Date(1999, 7, 6)); - _assertPred!"=="(Date.fromISOExtString(" 1999-07-06 "), Date(1999, 7, 6)); - - //Verify Examples. - assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4)); - assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25)); - assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5)); - assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5)); - assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4)); - } + assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4)); + assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25)); + assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5)); + assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5)); + assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4)); + } + + unittest + { + assertThrown!DateTimeException(Date.fromISOExtString("")); + assertThrown!DateTimeException(Date.fromISOExtString("990704")); + assertThrown!DateTimeException(Date.fromISOExtString("0100704")); + assertThrown!DateTimeException(Date.fromISOExtString("2010070")); + assertThrown!DateTimeException(Date.fromISOExtString("2010070 ")); + assertThrown!DateTimeException(Date.fromISOExtString("120100704")); + assertThrown!DateTimeException(Date.fromISOExtString("-0100704")); + assertThrown!DateTimeException(Date.fromISOExtString("+0100704")); + assertThrown!DateTimeException(Date.fromISOExtString("2010070a")); + assertThrown!DateTimeException(Date.fromISOExtString("20100a04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010a704")); + + assertThrown!DateTimeException(Date.fromISOExtString("99-07-04")); + assertThrown!DateTimeException(Date.fromISOExtString("010-07-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0 ")); + assertThrown!DateTimeException(Date.fromISOExtString("12010-07-04")); + assertThrown!DateTimeException(Date.fromISOExtString("-010-07-04")); + assertThrown!DateTimeException(Date.fromISOExtString("+010-07-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-07-0a")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-0a-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-a7-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010/07/04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010/7/04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010/7/4")); + assertThrown!DateTimeException(Date.fromISOExtString("2010/07/4")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-7-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-7-4")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-07-4")); + + assertThrown!DateTimeException(Date.fromISOExtString("99Jul04")); + assertThrown!DateTimeException(Date.fromISOExtString("010Jul04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0 ")); + assertThrown!DateTimeException(Date.fromISOExtString("12010Jul04")); + assertThrown!DateTimeException(Date.fromISOExtString("-010Jul04")); + assertThrown!DateTimeException(Date.fromISOExtString("+010Jul04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0a")); + assertThrown!DateTimeException(Date.fromISOExtString("2010Jua04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010aul04")); + + assertThrown!DateTimeException(Date.fromISOExtString("99-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtString("010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0")); + assertThrown!DateTimeException(Date.fromISOExtString("2010Jul0 ")); + assertThrown!DateTimeException(Date.fromISOExtString("12010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtString("-010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtString("+010-Jul-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-0a")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jua-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jal-04")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-aul-04")); + + assertThrown!DateTimeException(Date.fromISOExtString("20100704")); + assertThrown!DateTimeException(Date.fromISOExtString("2010-Jul-04")); + + assert(Date.fromISOExtString("1999-07-06") == Date(1999, 7, 6)); + assert(Date.fromISOExtString("-1999-07-06") == Date(-1999, 7, 6)); + assert(Date.fromISOExtString("+01999-07-06") == Date(1999, 7, 6)); + assert(Date.fromISOExtString("1999-07-06 ") == Date(1999, 7, 6)); + assert(Date.fromISOExtString(" 1999-07-06") == Date(1999, 7, 6)); + assert(Date.fromISOExtString(" 1999-07-06 ") == Date(1999, 7, 6)); } @@ -13141,17 +12379,8 @@ assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4)); Throws: $(LREF DateTimeException) if the given string is not in the correct format or if the resulting $(LREF Date) would not be valid. - - Examples: --------------------- -assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4)); -assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25)); -assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5)); -assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5)); -assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4)); --------------------- +/ - static Date fromSimpleString(S)(in S simpleString) + static Date fromSimpleString(S)(in S simpleString) @safe pure if(isSomeString!(S)) { auto dstr = to!dstring(strip(simpleString)); @@ -13164,111 +12393,104 @@ assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4)); enforce(dstr[$-3] == '-', new DateTimeException(format("Invalid string format: %s", simpleString))); enforce(dstr[$-7] == '-', new DateTimeException(format("Invalid string format: %s", simpleString))); - enforce(!canFind!(not!isDigit)(day), new DateTimeException(format("Invalid string format: %s", simpleString))); + enforce(all!isDigit(day), new DateTimeException(format("Invalid string format: %s", simpleString))); if(year.length > 4) { enforce(year.startsWith("-") || year.startsWith("+"), new DateTimeException(format("Invalid string format: %s", simpleString))); - enforce(!canFind!(not!isDigit)(year[1..$]), + enforce(all!isDigit(year[1..$]), new DateTimeException(format("Invalid string format: %s", simpleString))); } else - enforce(!canFind!(not!isDigit)(year), + enforce(all!isDigit(year), new DateTimeException(format("Invalid string format: %s", simpleString))); return Date(to!short(year), month, to!ubyte(day)); } + /// + unittest + { + assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4)); + assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25)); + assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5)); + assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5)); + assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4)); + } + unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException(Date.fromSimpleString("")); - assertThrown!DateTimeException(Date.fromSimpleString("990704")); - assertThrown!DateTimeException(Date.fromSimpleString("0100704")); - assertThrown!DateTimeException(Date.fromSimpleString("2010070")); - assertThrown!DateTimeException(Date.fromSimpleString("2010070 ")); - assertThrown!DateTimeException(Date.fromSimpleString("120100704")); - assertThrown!DateTimeException(Date.fromSimpleString("-0100704")); - assertThrown!DateTimeException(Date.fromSimpleString("+0100704")); - assertThrown!DateTimeException(Date.fromSimpleString("2010070a")); - assertThrown!DateTimeException(Date.fromSimpleString("20100a04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010a704")); - - assertThrown!DateTimeException(Date.fromSimpleString("99-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("010-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0 ")); - assertThrown!DateTimeException(Date.fromSimpleString("12010-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("-010-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("+010-07-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0a")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-0a-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-a7-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010/07/04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010/7/04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010/7/4")); - assertThrown!DateTimeException(Date.fromSimpleString("2010/07/4")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-7-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-7-4")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-4")); - - assertThrown!DateTimeException(Date.fromSimpleString("99Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("010Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0")); - assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0 ")); - assertThrown!DateTimeException(Date.fromSimpleString("12010Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("-010Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("+010Jul04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0a")); - assertThrown!DateTimeException(Date.fromSimpleString("2010Jua04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010aul04")); - - assertThrown!DateTimeException(Date.fromSimpleString("99-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("010-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0 ")); - assertThrown!DateTimeException(Date.fromSimpleString("12010-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("-010-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("+010-Jul-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0a")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jua-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-Jal-04")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-aul-04")); - - assertThrown!DateTimeException(Date.fromSimpleString("20100704")); - assertThrown!DateTimeException(Date.fromSimpleString("2010-07-04")); - - _assertPred!"=="(Date.fromSimpleString("1999-Jul-06"), Date(1999, 7, 6)); - _assertPred!"=="(Date.fromSimpleString("-1999-Jul-06"), Date(-1999, 7, 6)); - _assertPred!"=="(Date.fromSimpleString("+01999-Jul-06"), Date(1999, 7, 6)); - _assertPred!"=="(Date.fromSimpleString("1999-Jul-06 "), Date(1999, 7, 6)); - _assertPred!"=="(Date.fromSimpleString(" 1999-Jul-06"), Date(1999, 7, 6)); - _assertPred!"=="(Date.fromSimpleString(" 1999-Jul-06 "), Date(1999, 7, 6)); - - //Verify Examples. - assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4)); - assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25)); - assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5)); - assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5)); - assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4)); - } - } - - - //TODO Add function which takes a user-specified time format and produces a Date - - //TODO Add function which takes pretty much any time-string and produces a Date - // Obviously, it will be less efficient, and it probably won't manage _every_ - // possible date format, but a smart conversion function would be nice. + assertThrown!DateTimeException(Date.fromSimpleString("")); + assertThrown!DateTimeException(Date.fromSimpleString("990704")); + assertThrown!DateTimeException(Date.fromSimpleString("0100704")); + assertThrown!DateTimeException(Date.fromSimpleString("2010070")); + assertThrown!DateTimeException(Date.fromSimpleString("2010070 ")); + assertThrown!DateTimeException(Date.fromSimpleString("120100704")); + assertThrown!DateTimeException(Date.fromSimpleString("-0100704")); + assertThrown!DateTimeException(Date.fromSimpleString("+0100704")); + assertThrown!DateTimeException(Date.fromSimpleString("2010070a")); + assertThrown!DateTimeException(Date.fromSimpleString("20100a04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010a704")); + + assertThrown!DateTimeException(Date.fromSimpleString("99-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("010-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0 ")); + assertThrown!DateTimeException(Date.fromSimpleString("12010-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("-010-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("+010-07-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-0a")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-0a-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-a7-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010/07/04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010/7/04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010/7/4")); + assertThrown!DateTimeException(Date.fromSimpleString("2010/07/4")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-7-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-7-4")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-4")); + + assertThrown!DateTimeException(Date.fromSimpleString("99Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("010Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0")); + assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0 ")); + assertThrown!DateTimeException(Date.fromSimpleString("12010Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("-010Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("+010Jul04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010Jul0a")); + assertThrown!DateTimeException(Date.fromSimpleString("2010Jua04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010aul04")); + + assertThrown!DateTimeException(Date.fromSimpleString("99-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("010-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0 ")); + assertThrown!DateTimeException(Date.fromSimpleString("12010-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("-010-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("+010-Jul-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jul-0a")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jua-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-Jal-04")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-aul-04")); + + assertThrown!DateTimeException(Date.fromSimpleString("20100704")); + assertThrown!DateTimeException(Date.fromSimpleString("2010-07-04")); + + assert(Date.fromSimpleString("1999-Jul-06") == Date(1999, 7, 6)); + assert(Date.fromSimpleString("-1999-Jul-06") == Date(-1999, 7, 6)); + assert(Date.fromSimpleString("+01999-Jul-06") == Date(1999, 7, 6)); + assert(Date.fromSimpleString("1999-Jul-06 ") == Date(1999, 7, 6)); + assert(Date.fromSimpleString(" 1999-Jul-06") == Date(1999, 7, 6)); + assert(Date.fromSimpleString(" 1999-Jul-06 ") == Date(1999, 7, 6)); + } /++ Returns the $(LREF Date) farthest in the past which is representable by $(LREF Date). +/ - @property static Date min() pure nothrow + @property static Date min() @safe pure nothrow { auto date = Date.init; date._year = short.min; @@ -13280,11 +12502,8 @@ assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4)); unittest { - version(testStdDateTime) - { - assert(Date.min.year < 0); - assert(Date.min < Date.max); - } + assert(Date.min.year < 0); + assert(Date.min < Date.max); } @@ -13292,7 +12511,7 @@ assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4)); Returns the $(LREF Date) farthest in the future which is representable by $(LREF Date). +/ - @property static Date max() pure nothrow + @property static Date max() @safe pure nothrow { auto date = Date.init; date._year = short.max; @@ -13304,11 +12523,8 @@ assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4)); unittest { - version(testStdDateTime) - { - assert(Date.max.year > 0); - assert(Date.max > Date.min); - } + assert(Date.max.year > 0); + assert(Date.max > Date.min); } @@ -13322,7 +12538,7 @@ private: month = The month of the Gregorian Calendar to test. day = The day of the month to test. +/ - static bool _valid(int year, int month, int day) pure nothrow + static bool _valid(int year, int month, int day) @safe pure nothrow { if(!valid!"months"(month)) return false; @@ -13340,197 +12556,183 @@ private: decrease) to the month would cause it to overflow (or underflow) the current year. - $(D addDays(numDays)) is effectively equivalent to + $(D _addDays(numDays)) is effectively equivalent to $(D date.dayOfGregorianCal = date.dayOfGregorianCal + days). Params: days = The number of days to add to this Date. +/ - ref Date addDays(long days) pure nothrow + ref Date _addDays(long days) @safe pure nothrow { dayOfGregorianCal = cast(int)(dayOfGregorianCal + days); - return this; } unittest { - version(testStdDateTime) + //Test A.D. { - //Test A.D. - { - auto date = Date(1999, 2, 28); - date.addDays(1); - _assertPred!"=="(date, Date(1999, 3, 1)); - date.addDays(-1); - _assertPred!"=="(date, Date(1999, 2, 28)); - } - - { - auto date = Date(2000, 2, 28); - date.addDays(1); - _assertPred!"=="(date, Date(2000, 2, 29)); - date.addDays(1); - _assertPred!"=="(date, Date(2000, 3, 1)); - date.addDays(-1); - _assertPred!"=="(date, Date(2000, 2, 29)); - } + auto date = Date(1999, 2, 28); + date._addDays(1); + assert(date == Date(1999, 3, 1)); + date._addDays(-1); + assert(date == Date(1999, 2, 28)); + } - { - auto date = Date(1999, 6, 30); - date.addDays(1); - _assertPred!"=="(date, Date(1999, 7, 1)); - date.addDays(-1); - _assertPred!"=="(date, Date(1999, 6, 30)); - } + { + auto date = Date(2000, 2, 28); + date._addDays(1); + assert(date == Date(2000, 2, 29)); + date._addDays(1); + assert(date == Date(2000, 3, 1)); + date._addDays(-1); + assert(date == Date(2000, 2, 29)); + } - { - auto date = Date(1999, 7, 31); - date.addDays(1); - _assertPred!"=="(date, Date(1999, 8, 1)); - date.addDays(-1); - _assertPred!"=="(date, Date(1999, 7, 31)); - } + { + auto date = Date(1999, 6, 30); + date._addDays(1); + assert(date == Date(1999, 7, 1)); + date._addDays(-1); + assert(date == Date(1999, 6, 30)); + } - { - auto date = Date(1999, 1, 1); - date.addDays(-1); - _assertPred!"=="(date, Date(1998, 12, 31)); - date.addDays(1); - _assertPred!"=="(date, Date(1999, 1, 1)); - } + { + auto date = Date(1999, 7, 31); + date._addDays(1); + assert(date == Date(1999, 8, 1)); + date._addDays(-1); + assert(date == Date(1999, 7, 31)); + } - { - auto date = Date(1999, 7, 6); - date.addDays(9); - _assertPred!"=="(date, Date(1999, 7, 15)); - date.addDays(-11); - _assertPred!"=="(date, Date(1999, 7, 4)); - date.addDays(30); - _assertPred!"=="(date, Date(1999, 8, 3)); - date.addDays(-3); - _assertPred!"=="(date, Date(1999, 7, 31)); - } + { + auto date = Date(1999, 1, 1); + date._addDays(-1); + assert(date == Date(1998, 12, 31)); + date._addDays(1); + assert(date == Date(1999, 1, 1)); + } - { - auto date = Date(1999, 7, 6); - date.addDays(365); - _assertPred!"=="(date, Date(2000, 7, 5)); - date.addDays(-365); - _assertPred!"=="(date, Date(1999, 7, 6)); - date.addDays(366); - _assertPred!"=="(date, Date(2000, 7, 6)); - date.addDays(730); - _assertPred!"=="(date, Date(2002, 7, 6)); - date.addDays(-1096); - _assertPred!"=="(date, Date(1999, 7, 6)); - } + { + auto date = Date(1999, 7, 6); + date._addDays(9); + assert(date == Date(1999, 7, 15)); + date._addDays(-11); + assert(date == Date(1999, 7, 4)); + date._addDays(30); + assert(date == Date(1999, 8, 3)); + date._addDays(-3); + assert(date == Date(1999, 7, 31)); + } - //Test B.C. - { - auto date = Date(-1999, 2, 28); - date.addDays(1); - _assertPred!"=="(date, Date(-1999, 3, 1)); - date.addDays(-1); - _assertPred!"=="(date, Date(-1999, 2, 28)); - } + { + auto date = Date(1999, 7, 6); + date._addDays(365); + assert(date == Date(2000, 7, 5)); + date._addDays(-365); + assert(date == Date(1999, 7, 6)); + date._addDays(366); + assert(date == Date(2000, 7, 6)); + date._addDays(730); + assert(date == Date(2002, 7, 6)); + date._addDays(-1096); + assert(date == Date(1999, 7, 6)); + } - { - auto date = Date(-2000, 2, 28); - date.addDays(1); - _assertPred!"=="(date, Date(-2000, 2, 29)); - date.addDays(1); - _assertPred!"=="(date, Date(-2000, 3, 1)); - date.addDays(-1); - _assertPred!"=="(date, Date(-2000, 2, 29)); - } + //Test B.C. + { + auto date = Date(-1999, 2, 28); + date._addDays(1); + assert(date == Date(-1999, 3, 1)); + date._addDays(-1); + assert(date == Date(-1999, 2, 28)); + } - { - auto date = Date(-1999, 6, 30); - date.addDays(1); - _assertPred!"=="(date, Date(-1999, 7, 1)); - date.addDays(-1); - _assertPred!"=="(date, Date(-1999, 6, 30)); - } + { + auto date = Date(-2000, 2, 28); + date._addDays(1); + assert(date == Date(-2000, 2, 29)); + date._addDays(1); + assert(date == Date(-2000, 3, 1)); + date._addDays(-1); + assert(date == Date(-2000, 2, 29)); + } - { - auto date = Date(-1999, 7, 31); - date.addDays(1); - _assertPred!"=="(date, Date(-1999, 8, 1)); - date.addDays(-1); - _assertPred!"=="(date, Date(-1999, 7, 31)); - } + { + auto date = Date(-1999, 6, 30); + date._addDays(1); + assert(date == Date(-1999, 7, 1)); + date._addDays(-1); + assert(date == Date(-1999, 6, 30)); + } - { - auto date = Date(-1999, 1, 1); - date.addDays(-1); - _assertPred!"=="(date, Date(-2000, 12, 31)); - date.addDays(1); - _assertPred!"=="(date, Date(-1999, 1, 1)); - } + { + auto date = Date(-1999, 7, 31); + date._addDays(1); + assert(date == Date(-1999, 8, 1)); + date._addDays(-1); + assert(date == Date(-1999, 7, 31)); + } - { - auto date = Date(-1999, 7, 6); - date.addDays(9); - _assertPred!"=="(date, Date(-1999, 7, 15)); - date.addDays(-11); - _assertPred!"=="(date, Date(-1999, 7, 4)); - date.addDays(30); - _assertPred!"=="(date, Date(-1999, 8, 3)); - date.addDays(-3); - } + { + auto date = Date(-1999, 1, 1); + date._addDays(-1); + assert(date == Date(-2000, 12, 31)); + date._addDays(1); + assert(date == Date(-1999, 1, 1)); + } - { - auto date = Date(-1999, 7, 6); - date.addDays(365); - _assertPred!"=="(date, Date(-1998, 7, 6)); - date.addDays(-365); - _assertPred!"=="(date, Date(-1999, 7, 6)); - date.addDays(366); - _assertPred!"=="(date, Date(-1998, 7, 7)); - date.addDays(730); - _assertPred!"=="(date, Date(-1996, 7, 6)); - date.addDays(-1096); - _assertPred!"=="(date, Date(-1999, 7, 6)); - } + { + auto date = Date(-1999, 7, 6); + date._addDays(9); + assert(date == Date(-1999, 7, 15)); + date._addDays(-11); + assert(date == Date(-1999, 7, 4)); + date._addDays(30); + assert(date == Date(-1999, 8, 3)); + date._addDays(-3); + } - //Test Both - { - auto date = Date(1, 7, 6); - date.addDays(-365); - _assertPred!"=="(date, Date(0, 7, 6)); - date.addDays(365); - _assertPred!"=="(date, Date(1, 7, 6)); - date.addDays(-731); - _assertPred!"=="(date, Date(-1, 7, 6)); - date.addDays(730); - _assertPred!"=="(date, Date(1, 7, 5)); - } + { + auto date = Date(-1999, 7, 6); + date._addDays(365); + assert(date == Date(-1998, 7, 6)); + date._addDays(-365); + assert(date == Date(-1999, 7, 6)); + date._addDays(366); + assert(date == Date(-1998, 7, 7)); + date._addDays(730); + assert(date == Date(-1996, 7, 6)); + date._addDays(-1096); + assert(date == Date(-1999, 7, 6)); + } - const cdate = Date(1999, 7, 6); - immutable idate = Date(1999, 7, 6); - static assert(!__traits(compiles, cdate.addDays(12))); - static assert(!__traits(compiles, idate.addDays(12))); + //Test Both + { + auto date = Date(1, 7, 6); + date._addDays(-365); + assert(date == Date(0, 7, 6)); + date._addDays(365); + assert(date == Date(1, 7, 6)); + date._addDays(-731); + assert(date == Date(-1, 7, 6)); + date._addDays(730); + assert(date == Date(1, 7, 5)); } + + const cdate = Date(1999, 7, 6); + immutable idate = Date(1999, 7, 6); + static assert(!__traits(compiles, cdate._addDays(12))); + static assert(!__traits(compiles, idate._addDays(12))); } - pure invariant() + @safe pure invariant() { - assert(valid!"months"(_month), "Invariant Failure: year [" ~ - numToString(_year) ~ - "] month [" ~ - numToString(_month) ~ - "] day [" ~ - numToString(_day) ~ - "]"); - assert(valid!"days"(_year, _month, _day), "Invariant Failure: year [" ~ - numToString(_year) ~ - "] month [" ~ - numToString(_month) ~ - "] day [" ~ - numToString(_day) ~ - "]"); + assert(valid!"months"(_month), + format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day)); + assert(valid!"days"(_year, _month, _day), + format("Invariant Failure: year [%s] month [%s] day [%s]", _year, _month, _day)); } @@ -13559,7 +12761,7 @@ public: $(LREF DateTimeException) if the resulting $(LREF TimeOfDay) would be not be valid. +/ - this(int hour, int minute, int second = 0) pure + this(int hour, int minute, int second = 0) @safe pure { enforceValid!"hours"(hour); enforceValid!"minutes"(minute); @@ -13572,35 +12774,32 @@ public: unittest { - version(testStdDateTime) - { - assert(TimeOfDay(0, 0) == TimeOfDay.init); - - { - auto tod = TimeOfDay(0, 0); - _assertPred!"=="(tod._hour, 0); - _assertPred!"=="(tod._minute, 0); - _assertPred!"=="(tod._second, 0); - } + assert(TimeOfDay(0, 0) == TimeOfDay.init); - { - auto tod = TimeOfDay(12, 30, 33); - _assertPred!"=="(tod._hour, 12); - _assertPred!"=="(tod._minute, 30); - _assertPred!"=="(tod._second, 33); - } + { + auto tod = TimeOfDay(0, 0); + assert(tod._hour == 0); + assert(tod._minute == 0); + assert(tod._second == 0); + } - { - auto tod = TimeOfDay(23, 59, 59); - _assertPred!"=="(tod._hour, 23); - _assertPred!"=="(tod._minute, 59); - _assertPred!"=="(tod._second, 59); - } + { + auto tod = TimeOfDay(12, 30, 33); + assert(tod._hour == 12); + assert(tod._minute == 30); + assert(tod._second == 33); + } - assertThrown!DateTimeException(TimeOfDay(24, 0, 0)); - assertThrown!DateTimeException(TimeOfDay(0, 60, 0)); - assertThrown!DateTimeException(TimeOfDay(0, 0, 60)); + { + auto tod = TimeOfDay(23, 59, 59); + assert(tod._hour == 23); + assert(tod._minute == 59); + assert(tod._second == 59); } + + assertThrown!DateTimeException(TimeOfDay(24, 0, 0)); + assertThrown!DateTimeException(TimeOfDay(0, 60, 0)); + assertThrown!DateTimeException(TimeOfDay(0, 0, 60)); } @@ -13614,7 +12813,7 @@ public: $(TR $(TD this > rhs) $(TD > 0)) ) +/ - int opCmp(in TimeOfDay rhs) const pure nothrow + int opCmp(in TimeOfDay rhs) @safe const pure nothrow { if(_hour < rhs._hour) return -1; @@ -13636,64 +12835,58 @@ public: unittest { - version(testStdDateTime) - { - _assertPred!("opCmp", "==")(TimeOfDay(0, 0, 0), TimeOfDay.init); + assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay.init) == 0); - _assertPred!("opCmp", "==")(TimeOfDay(0, 0, 0), TimeOfDay(0, 0, 0)); - _assertPred!("opCmp", "==")(TimeOfDay(12, 0, 0), TimeOfDay(12, 0, 0)); - _assertPred!("opCmp", "==")(TimeOfDay(0, 30, 0), TimeOfDay(0, 30, 0)); - _assertPred!("opCmp", "==")(TimeOfDay(0, 0, 33), TimeOfDay(0, 0, 33)); + assert(TimeOfDay(0, 0, 0).opCmp(TimeOfDay(0, 0, 0)) == 0); + assert(TimeOfDay(12, 0, 0).opCmp(TimeOfDay(12, 0, 0)) == 0); + assert(TimeOfDay(0, 30, 0).opCmp(TimeOfDay(0, 30, 0)) == 0); + assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0); - _assertPred!("opCmp", "==")(TimeOfDay(12, 30, 0), TimeOfDay(12, 30, 0)); - _assertPred!("opCmp", "==")(TimeOfDay(12, 30, 33), TimeOfDay(12, 30, 33)); + assert(TimeOfDay(12, 30, 0).opCmp(TimeOfDay(12, 30, 0)) == 0); + assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 33)) == 0); - _assertPred!("opCmp", "==")(TimeOfDay(0, 30, 33), TimeOfDay(0, 30, 33)); - _assertPred!("opCmp", "==")(TimeOfDay(0, 0, 33), TimeOfDay(0, 0, 33)); + assert(TimeOfDay(0, 30, 33).opCmp(TimeOfDay(0, 30, 33)) == 0); + assert(TimeOfDay(0, 0, 33).opCmp(TimeOfDay(0, 0, 33)) == 0); - _assertPred!("opCmp", "<")(TimeOfDay(12, 30, 33), TimeOfDay(13, 30, 33)); - _assertPred!("opCmp", ">")(TimeOfDay(13, 30, 33), TimeOfDay(12, 30, 33)); - _assertPred!("opCmp", "<")(TimeOfDay(12, 30, 33), TimeOfDay(12, 31, 33)); - _assertPred!("opCmp", ">")(TimeOfDay(12, 31, 33), TimeOfDay(12, 30, 33)); - _assertPred!("opCmp", "<")(TimeOfDay(12, 30, 33), TimeOfDay(12, 30, 34)); - _assertPred!("opCmp", ">")(TimeOfDay(12, 30, 34), TimeOfDay(12, 30, 33)); + assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(13, 30, 33)) < 0); + assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 33)) > 0); + assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 31, 33)) < 0); + assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 33)) > 0); + assert(TimeOfDay(12, 30, 33).opCmp(TimeOfDay(12, 30, 34)) < 0); + assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 30, 33)) > 0); - _assertPred!("opCmp", ">")(TimeOfDay(13, 30, 33), TimeOfDay(12, 30, 34)); - _assertPred!("opCmp", "<")(TimeOfDay(12, 30, 34), TimeOfDay(13, 30, 33)); - _assertPred!("opCmp", ">")(TimeOfDay(13, 30, 33), TimeOfDay(12, 31, 33)); - _assertPred!("opCmp", "<")(TimeOfDay(12, 31, 33), TimeOfDay(13, 30, 33)); + assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 30, 34)) > 0); + assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(13, 30, 33)) < 0); + assert(TimeOfDay(13, 30, 33).opCmp(TimeOfDay(12, 31, 33)) > 0); + assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(13, 30, 33)) < 0); - _assertPred!("opCmp", ">")(TimeOfDay(12, 31, 33), TimeOfDay(12, 30, 34)); - _assertPred!("opCmp", "<")(TimeOfDay(12, 30, 34), TimeOfDay(12, 31, 33)); + assert(TimeOfDay(12, 31, 33).opCmp(TimeOfDay(12, 30, 34)) > 0); + assert(TimeOfDay(12, 30, 34).opCmp(TimeOfDay(12, 31, 33)) < 0); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - static assert(__traits(compiles, ctod.opCmp(itod))); - static assert(__traits(compiles, itod.opCmp(ctod))); - } + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + static assert(__traits(compiles, ctod.opCmp(itod))); + static assert(__traits(compiles, itod.opCmp(ctod))); } /++ Hours passed midnight. +/ - @property ubyte hour() const pure nothrow + @property ubyte hour() @safe const pure nothrow { return _hour; } unittest { - version(testStdDateTime) - { - _assertPred!"=="(TimeOfDay.init.hour, 0); - _assertPred!"=="(TimeOfDay(12, 0, 0).hour, 12); + assert(TimeOfDay.init.hour == 0); + assert(TimeOfDay(12, 0, 0).hour == 12); - const ctod = TimeOfDay(12, 0, 0); - immutable itod = TimeOfDay(12, 0, 0); - static assert(__traits(compiles, ctod.hour == 12)); - static assert(__traits(compiles, itod.hour == 12)); - } + const ctod = TimeOfDay(12, 0, 0); + immutable itod = TimeOfDay(12, 0, 0); + static assert(__traits(compiles, ctod.hour == 12)); + static assert(__traits(compiles, itod.hour == 12)); } @@ -13707,7 +12900,7 @@ public: $(LREF DateTimeException) if the given hour would result in an invalid $(LREF TimeOfDay). +/ - @property void hour(int hour) pure + @property void hour(int hour) @safe pure { enforceValid!"hours"(hour); _hour = cast(ubyte)hour; @@ -13715,42 +12908,36 @@ public: unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}()); + assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).hour = 24;}()); - auto tod = TimeOfDay(0, 0, 0); - tod.hour = 12; - _assertPred!"=="(tod, TimeOfDay(12, 0, 0)); + auto tod = TimeOfDay(0, 0, 0); + tod.hour = 12; + assert(tod == TimeOfDay(12, 0, 0)); - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.hour = 12)); - static assert(!__traits(compiles, itod.hour = 12)); - } + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.hour = 12)); + static assert(!__traits(compiles, itod.hour = 12)); } /++ Minutes passed the hour. +/ - @property ubyte minute() const pure nothrow + @property ubyte minute() @safe const pure nothrow { return _minute; } unittest { - version(testStdDateTime) - { - _assertPred!"=="(TimeOfDay.init.minute, 0); - _assertPred!"=="(TimeOfDay(0, 30, 0).minute, 30); + assert(TimeOfDay.init.minute == 0); + assert(TimeOfDay(0, 30, 0).minute == 30); - const ctod = TimeOfDay(0, 30, 0); - immutable itod = TimeOfDay(0, 30, 0); - static assert(__traits(compiles, ctod.minute == 30)); - static assert(__traits(compiles, itod.minute == 30)); - } + const ctod = TimeOfDay(0, 30, 0); + immutable itod = TimeOfDay(0, 30, 0); + static assert(__traits(compiles, ctod.minute == 30)); + static assert(__traits(compiles, itod.minute == 30)); } @@ -13764,7 +12951,7 @@ public: $(LREF DateTimeException) if the given minute would result in an invalid $(LREF TimeOfDay). +/ - @property void minute(int minute) pure + @property void minute(int minute) @safe pure { enforceValid!"minutes"(minute); _minute = cast(ubyte)minute; @@ -13772,42 +12959,36 @@ public: unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}()); + assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).minute = 60;}()); - auto tod = TimeOfDay(0, 0, 0); - tod.minute = 30; - _assertPred!"=="(tod, TimeOfDay(0, 30, 0)); + auto tod = TimeOfDay(0, 0, 0); + tod.minute = 30; + assert(tod == TimeOfDay(0, 30, 0)); - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.minute = 30)); - static assert(!__traits(compiles, itod.minute = 30)); - } + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.minute = 30)); + static assert(!__traits(compiles, itod.minute = 30)); } /++ Seconds passed the minute. +/ - @property ubyte second() const pure nothrow + @property ubyte second() @safe const pure nothrow { return _second; } unittest { - version(testStdDateTime) - { - _assertPred!"=="(TimeOfDay.init.second, 0); - _assertPred!"=="(TimeOfDay(0, 0, 33).second, 33); + assert(TimeOfDay.init.second == 0); + assert(TimeOfDay(0, 0, 33).second == 33); - const ctod = TimeOfDay(0, 0, 33); - immutable itod = TimeOfDay(0, 0, 33); - static assert(__traits(compiles, ctod.second == 33)); - static assert(__traits(compiles, itod.second == 33)); - } + const ctod = TimeOfDay(0, 0, 33); + immutable itod = TimeOfDay(0, 0, 33); + static assert(__traits(compiles, ctod.second == 33)); + static assert(__traits(compiles, itod.second == 33)); } @@ -13821,7 +13002,7 @@ public: $(LREF DateTimeException) if the given second would result in an invalid $(LREF TimeOfDay). +/ - @property void second(int second) pure + @property void second(int second) @safe pure { enforceValid!"seconds"(second); _second = cast(ubyte)second; @@ -13829,19 +13010,16 @@ public: unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}()); + assertThrown!DateTimeException((){TimeOfDay(0, 0, 0).second = 60;}()); - auto tod = TimeOfDay(0, 0, 0); - tod.second = 33; - _assertPred!"=="(tod, TimeOfDay(0, 0, 33)); + auto tod = TimeOfDay(0, 0, 0); + tod.second = 33; + assert(tod == TimeOfDay(0, 0, 33)); - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.second = 33)); - static assert(!__traits(compiles, itod.second = 33)); - } + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.second = 33)); + static assert(!__traits(compiles, itod.second = 33)); } @@ -13860,97 +13038,62 @@ public: units = The units to add. value = The number of $(D_PARAM units) to add to this $(LREF TimeOfDay). - - Examples: --------------------- -auto tod1 = TimeOfDay(7, 12, 0); -tod1.roll!"hours"(1); -assert(tod1 == TimeOfDay(8, 12, 0)); - -auto tod2 = TimeOfDay(7, 12, 0); -tod2.roll!"hours"(-1); -assert(tod2 == TimeOfDay(6, 12, 0)); - -auto tod3 = TimeOfDay(23, 59, 0); -tod3.roll!"minutes"(1); -assert(tod3 == TimeOfDay(23, 0, 0)); - -auto tod4 = TimeOfDay(0, 0, 0); -tod4.roll!"minutes"(-1); -assert(tod4 == TimeOfDay(0, 59, 0)); - -auto tod5 = TimeOfDay(23, 59, 59); -tod5.roll!"seconds"(1); -assert(tod5 == TimeOfDay(23, 59, 0)); - -auto tod6 = TimeOfDay(0, 0, 0); -tod6.roll!"seconds"(-1); -assert(tod6 == TimeOfDay(0, 0, 59)); --------------------- +/ - /+ref TimeOfDay+/ void roll(string units)(long value) pure nothrow + ref TimeOfDay roll(string units)(long value) @safe pure nothrow if(units == "hours") { - this += dur!"hours"(value); + return this += dur!"hours"(value); } - //Verify Examples. + /// unittest { - version(testStdDateTime) - { - auto tod1 = TimeOfDay(7, 12, 0); - tod1.roll!"hours"(1); - assert(tod1 == TimeOfDay(8, 12, 0)); + auto tod1 = TimeOfDay(7, 12, 0); + tod1.roll!"hours"(1); + assert(tod1 == TimeOfDay(8, 12, 0)); - auto tod2 = TimeOfDay(7, 12, 0); - tod2.roll!"hours"(-1); - assert(tod2 == TimeOfDay(6, 12, 0)); + auto tod2 = TimeOfDay(7, 12, 0); + tod2.roll!"hours"(-1); + assert(tod2 == TimeOfDay(6, 12, 0)); - auto tod3 = TimeOfDay(23, 59, 0); - tod3.roll!"minutes"(1); - assert(tod3 == TimeOfDay(23, 0, 0)); + auto tod3 = TimeOfDay(23, 59, 0); + tod3.roll!"minutes"(1); + assert(tod3 == TimeOfDay(23, 0, 0)); - auto tod4 = TimeOfDay(0, 0, 0); - tod4.roll!"minutes"(-1); - assert(tod4 == TimeOfDay(0, 59, 0)); + auto tod4 = TimeOfDay(0, 0, 0); + tod4.roll!"minutes"(-1); + assert(tod4 == TimeOfDay(0, 59, 0)); - auto tod5 = TimeOfDay(23, 59, 59); - tod5.roll!"seconds"(1); - assert(tod5 == TimeOfDay(23, 59, 0)); + auto tod5 = TimeOfDay(23, 59, 59); + tod5.roll!"seconds"(1); + assert(tod5 == TimeOfDay(23, 59, 0)); - auto tod6 = TimeOfDay(0, 0, 0); - tod6.roll!"seconds"(-1); - assert(tod6 == TimeOfDay(0, 0, 59)); - } + auto tod6 = TimeOfDay(0, 0, 0); + tod6.roll!"seconds"(-1); + assert(tod6 == TimeOfDay(0, 0, 59)); } unittest { - version(testStdDateTime) - { - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.roll!"hours"(53))); - static assert(!__traits(compiles, itod.roll!"hours"(53))); - } + auto tod = TimeOfDay(12, 27, 2); + tod.roll!"hours"(22).roll!"hours"(-7); + assert(tod == TimeOfDay(3, 27, 2)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.roll!"hours"(53))); + static assert(!__traits(compiles, itod.roll!"hours"(53))); } //Shares documentation with "hours" version. - /+ref TimeOfDay+/ void roll(string units)(long value) pure nothrow + ref TimeOfDay roll(string units)(long value) @safe pure nothrow if(units == "minutes" || units == "seconds") { - static if(units == "minutes") - enum memberVarStr = "minute"; - else static if(units == "seconds") - enum memberVarStr = "second"; - else - static assert(0); - + enum memberVarStr = units[0 .. $ - 1]; value %= 60; - mixin("auto newVal = cast(ubyte)(_" ~ memberVarStr ~ ") + value;"); + mixin(format("auto newVal = cast(ubyte)(_%s) + value;", memberVarStr)); if(value < 0) { @@ -13960,195 +13103,165 @@ assert(tod6 == TimeOfDay(0, 0, 59)); else if(newVal >= 60) newVal -= 60; - mixin("_" ~ memberVarStr ~ " = cast(ubyte)newVal;"); + mixin(format("_%s = cast(ubyte)newVal;", memberVarStr)); + return this; } //Test roll!"minutes"(). unittest { - version(testStdDateTime) - { - static void testTOD(TimeOfDay orig, int minutes, in TimeOfDay expected, size_t line = __LINE__) - { - orig.roll!"minutes"(minutes); - _assertPred!"=="(orig, expected, "", __FILE__, line); - } - - testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33)); - testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33)); - testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33)); - testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33)); - testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33)); - testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33)); - testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33)); - testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33)); - testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33)); - testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33)); - - testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33)); - testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33)); - - testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33)); - testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33)); - testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33)); - testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33)); - testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33)); - testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33)); - testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33)); - testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33)); - testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33)); - - testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33)); - testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33)); - - testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33)); - testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33)); - testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33)); - - testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33)); - testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33)); - testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33)); - - testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33)); - testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33)); - testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33)); - - testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33)); - testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33)); - testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.roll!"minutes"(7))); - static assert(!__traits(compiles, itod.roll!"minutes"(7))); - - //Verify Examples. - auto tod1 = TimeOfDay(7, 12, 0); - tod1.roll!"minutes"(1); - assert(tod1 == TimeOfDay(7, 13, 0)); - - auto tod2 = TimeOfDay(7, 12, 0); - tod2.roll!"minutes"(-1); - assert(tod2 == TimeOfDay(7, 11, 0)); - - auto tod3 = TimeOfDay(23, 59, 0); - tod3.roll!"minutes"(1); - assert(tod3 == TimeOfDay(23, 0, 0)); - - auto tod4 = TimeOfDay(0, 0, 0); - tod4.roll!"minutes"(-1); - assert(tod4 == TimeOfDay(0, 59, 0)); - - auto tod5 = TimeOfDay(7, 32, 12); - tod5.roll!"seconds"(1); - assert(tod5 == TimeOfDay(7, 32, 13)); - - auto tod6 = TimeOfDay(7, 32, 12); - tod6.roll!"seconds"(-1); - assert(tod6 == TimeOfDay(7, 32, 11)); - - auto tod7 = TimeOfDay(23, 59, 59); - tod7.roll!"seconds"(1); - assert(tod7 == TimeOfDay(23, 59, 0)); - - auto tod8 = TimeOfDay(0, 0, 0); - tod8.roll!"seconds"(-1); - assert(tod8 == TimeOfDay(0, 0, 59)); - } + static void testTOD(TimeOfDay orig, int minutes, in TimeOfDay expected, size_t line = __LINE__) + { + orig.roll!"minutes"(minutes); + assert(orig == expected); + } + + testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 32, 33)); + testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 33, 33)); + testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 34, 33)); + testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 35, 33)); + testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 40, 33)); + testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 45, 33)); + testTOD(TimeOfDay(12, 30, 33), 29, TimeOfDay(12, 59, 33)); + testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), 45, TimeOfDay(12, 15, 33)); + testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 75, TimeOfDay(12, 45, 33)); + testTOD(TimeOfDay(12, 30, 33), 90, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), 100, TimeOfDay(12, 10, 33)); + + testTOD(TimeOfDay(12, 30, 33), 689, TimeOfDay(12, 59, 33)); + testTOD(TimeOfDay(12, 30, 33), 690, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), 691, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 30, 33), 960, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1439, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), 1440, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1441, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), 2880, TimeOfDay(12, 30, 33)); + + testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 28, 33)); + testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 27, 33)); + testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 26, 33)); + testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 25, 33)); + testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 20, 33)); + testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 15, 33)); + testTOD(TimeOfDay(12, 30, 33), -29, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 30, 33), -30, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), -45, TimeOfDay(12, 45, 33)); + testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -75, TimeOfDay(12, 15, 33)); + testTOD(TimeOfDay(12, 30, 33), -90, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), -100, TimeOfDay(12, 50, 33)); + + testTOD(TimeOfDay(12, 30, 33), -749, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 30, 33), -750, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 30, 33), -751, TimeOfDay(12, 59, 33)); + testTOD(TimeOfDay(12, 30, 33), -960, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -1439, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), -1440, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -1441, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), -2880, TimeOfDay(12, 30, 33)); + + testTOD(TimeOfDay(12, 0, 33), 1, TimeOfDay(12, 1, 33)); + testTOD(TimeOfDay(12, 0, 33), 0, TimeOfDay(12, 0, 33)); + testTOD(TimeOfDay(12, 0, 33), -1, TimeOfDay(12, 59, 33)); + + testTOD(TimeOfDay(11, 59, 33), 1, TimeOfDay(11, 0, 33)); + testTOD(TimeOfDay(11, 59, 33), 0, TimeOfDay(11, 59, 33)); + testTOD(TimeOfDay(11, 59, 33), -1, TimeOfDay(11, 58, 33)); + + testTOD(TimeOfDay(0, 0, 33), 1, TimeOfDay(0, 1, 33)); + testTOD(TimeOfDay(0, 0, 33), 0, TimeOfDay(0, 0, 33)); + testTOD(TimeOfDay(0, 0, 33), -1, TimeOfDay(0, 59, 33)); + + testTOD(TimeOfDay(23, 59, 33), 1, TimeOfDay(23, 0, 33)); + testTOD(TimeOfDay(23, 59, 33), 0, TimeOfDay(23, 59, 33)); + testTOD(TimeOfDay(23, 59, 33), -1, TimeOfDay(23, 58, 33)); + + auto tod = TimeOfDay(12, 27, 2); + tod.roll!"minutes"(97).roll!"minutes"(-102); + assert(tod == TimeOfDay(12, 22, 2)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.roll!"minutes"(7))); + static assert(!__traits(compiles, itod.roll!"minutes"(7))); } //Test roll!"seconds"(). unittest { - version(testStdDateTime) - { - static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) - { - orig.roll!"seconds"(seconds); - _assertPred!"=="(orig, expected, "", __FILE__, line); - } - - testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); - testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); - testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); - testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); - testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); - testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); - testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3)); - testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34)); - - testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1)); - testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33)); - - testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); - testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); - testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); - testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); - testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); - testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); - testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58)); - testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32)); - - testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); - testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59)); - - testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); - testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); - testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59)); - - testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); - testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); - testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59)); - - testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0)); - testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); - testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.roll!"seconds"(7))); - static assert(!__traits(compiles, itod.roll!"seconds"(7))); - } + static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) + { + orig.roll!"seconds"(seconds); + assert(orig == expected); + } + + testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); + testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); + testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); + testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); + testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); + testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); + testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 30, 3)); + testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 30, 34)); + + testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(12, 30, 1)); + testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(12, 30, 33)); + + testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); + testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); + testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); + testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); + testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); + testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); + testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 30, 58)); + testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 30, 32)); + + testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); + testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 30, 59)); + + testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); + testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); + testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(12, 0, 59)); + + testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); + testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); + testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(0, 0, 59)); + + testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(23, 59, 0)); + testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); + testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); + + auto tod = TimeOfDay(12, 27, 2); + tod.roll!"seconds"(105).roll!"seconds"(-77); + assert(tod == TimeOfDay(12, 27, 30)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod.roll!"seconds"(7))); + static assert(!__traits(compiles, itod.roll!"seconds"(7))); } @@ -14167,7 +13280,7 @@ assert(tod6 == TimeOfDay(0, 0, 59)); duration = The duration to add to or subtract from this $(LREF TimeOfDay). +/ - TimeOfDay opBinary(string op, D)(in D duration) const pure nothrow + TimeOfDay opBinary(string op, D)(in D duration) @safe const pure nothrow if((op == "+" || op == "-") && (is(Unqual!D == Duration) || is(Unqual!D == TickDuration))) @@ -14179,81 +13292,67 @@ assert(tod6 == TimeOfDay(0, 0, 59)); else static if(is(Unqual!D == TickDuration)) immutable hnsecs = duration.hnsecs; - //Ideally, this would just be - //return retval.addSeconds(convert!("hnsecs", "seconds")(unaryFun!(op ~ "a")(hnsecs))); - //But there isn't currently a pure version of unaryFun!(). - - static if(op == "+") - immutable signedHNSecs = hnsecs; - else static if(op == "-") - immutable signedHNSecs = -hnsecs; - else - static assert(0); - - return retval.addSeconds(convert!("hnsecs", "seconds")(signedHNSecs)); + mixin(format(`return retval._addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op)); } unittest { - version(testStdDateTime) - { - auto tod = TimeOfDay(12, 30, 33); + auto tod = TimeOfDay(12, 30, 33); - _assertPred!"=="(tod + dur!"hours"(7), TimeOfDay(19, 30, 33)); - _assertPred!"=="(tod + dur!"hours"(-7), TimeOfDay(5, 30, 33)); - _assertPred!"=="(tod + dur!"minutes"(7), TimeOfDay(12, 37, 33)); - _assertPred!"=="(tod + dur!"minutes"(-7), TimeOfDay(12, 23, 33)); - _assertPred!"=="(tod + dur!"seconds"(7), TimeOfDay(12, 30, 40)); - _assertPred!"=="(tod + dur!"seconds"(-7), TimeOfDay(12, 30, 26)); - - _assertPred!"=="(tod + dur!"msecs"(7000), TimeOfDay(12, 30, 40)); - _assertPred!"=="(tod + dur!"msecs"(-7000), TimeOfDay(12, 30, 26)); - _assertPred!"=="(tod + dur!"usecs"(7_000_000), TimeOfDay(12, 30, 40)); - _assertPred!"=="(tod + dur!"usecs"(-7_000_000), TimeOfDay(12, 30, 26)); - _assertPred!"=="(tod + dur!"hnsecs"(70_000_000), TimeOfDay(12, 30, 40)); - _assertPred!"=="(tod + dur!"hnsecs"(-70_000_000), TimeOfDay(12, 30, 26)); - - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if(TickDuration.ticksPerSec == 1_000_000) - { - _assertPred!"=="(tod + TickDuration.from!"usecs"(7_000_000), TimeOfDay(12, 30, 40)); - _assertPred!"=="(tod + TickDuration.from!"usecs"(-7_000_000), TimeOfDay(12, 30, 26)); - } + assert(tod + dur!"hours"(7) == TimeOfDay(19, 30, 33)); + assert(tod + dur!"hours"(-7) == TimeOfDay(5, 30, 33)); + assert(tod + dur!"minutes"(7) == TimeOfDay(12, 37, 33)); + assert(tod + dur!"minutes"(-7) == TimeOfDay(12, 23, 33)); + assert(tod + dur!"seconds"(7) == TimeOfDay(12, 30, 40)); + assert(tod + dur!"seconds"(-7) == TimeOfDay(12, 30, 26)); - _assertPred!"=="(tod - dur!"hours"(-7), TimeOfDay(19, 30, 33)); - _assertPred!"=="(tod - dur!"hours"(7), TimeOfDay(5, 30, 33)); - _assertPred!"=="(tod - dur!"minutes"(-7), TimeOfDay(12, 37, 33)); - _assertPred!"=="(tod - dur!"minutes"(7), TimeOfDay(12, 23, 33)); - _assertPred!"=="(tod - dur!"seconds"(-7), TimeOfDay(12, 30, 40)); - _assertPred!"=="(tod - dur!"seconds"(7), TimeOfDay(12, 30, 26)); - - _assertPred!"=="(tod - dur!"msecs"(-7000), TimeOfDay(12, 30, 40)); - _assertPred!"=="(tod - dur!"msecs"(7000), TimeOfDay(12, 30, 26)); - _assertPred!"=="(tod - dur!"usecs"(-7_000_000), TimeOfDay(12, 30, 40)); - _assertPred!"=="(tod - dur!"usecs"(7_000_000), TimeOfDay(12, 30, 26)); - _assertPred!"=="(tod - dur!"hnsecs"(-70_000_000), TimeOfDay(12, 30, 40)); - _assertPred!"=="(tod - dur!"hnsecs"(70_000_000), TimeOfDay(12, 30, 26)); - - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if(TickDuration.ticksPerSec == 1_000_000) - { - _assertPred!"=="(tod - TickDuration.from!"usecs"(-7_000_000), TimeOfDay(12, 30, 40)); - _assertPred!"=="(tod - TickDuration.from!"usecs"(7_000_000), TimeOfDay(12, 30, 26)); - } + assert(tod + dur!"msecs"(7000) == TimeOfDay(12, 30, 40)); + assert(tod + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26)); + assert(tod + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); + assert(tod + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); + assert(tod + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40)); + assert(tod + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26)); + + //This probably only runs in cases where gettimeofday() is used, but it's + //hard to do this test correctly with variable ticksPerSec. + if(TickDuration.ticksPerSec == 1_000_000) + { + assert(tod + TickDuration.from!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); + assert(tod + TickDuration.from!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); + } - auto duration = dur!"hours"(11); - const ctod = TimeOfDay(12, 33, 30); - immutable itod = TimeOfDay(12, 33, 30); - static assert(__traits(compiles, tod + duration)); - static assert(__traits(compiles, ctod + duration)); - static assert(__traits(compiles, itod + duration)); + assert(tod - dur!"hours"(-7) == TimeOfDay(19, 30, 33)); + assert(tod - dur!"hours"(7) == TimeOfDay(5, 30, 33)); + assert(tod - dur!"minutes"(-7) == TimeOfDay(12, 37, 33)); + assert(tod - dur!"minutes"(7) == TimeOfDay(12, 23, 33)); + assert(tod - dur!"seconds"(-7) == TimeOfDay(12, 30, 40)); + assert(tod - dur!"seconds"(7) == TimeOfDay(12, 30, 26)); - static assert(__traits(compiles, tod - duration)); - static assert(__traits(compiles, ctod - duration)); - static assert(__traits(compiles, itod - duration)); + assert(tod - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40)); + assert(tod - dur!"msecs"(7000) == TimeOfDay(12, 30, 26)); + assert(tod - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); + assert(tod - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); + assert(tod - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40)); + assert(tod - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26)); + + //This probably only runs in cases where gettimeofday() is used, but it's + //hard to do this test correctly with variable ticksPerSec. + if(TickDuration.ticksPerSec == 1_000_000) + { + assert(tod - TickDuration.from!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); + assert(tod - TickDuration.from!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); } + + auto duration = dur!"hours"(11); + const ctod = TimeOfDay(12, 33, 30); + immutable itod = TimeOfDay(12, 33, 30); + static assert(__traits(compiles, tod + duration)); + static assert(__traits(compiles, ctod + duration)); + static assert(__traits(compiles, itod + duration)); + + static assert(__traits(compiles, tod - duration)); + static assert(__traits(compiles, ctod - duration)); + static assert(__traits(compiles, itod - duration)); } @@ -14273,7 +13372,7 @@ assert(tod6 == TimeOfDay(0, 0, 59)); duration = The duration to add to or subtract from this $(LREF TimeOfDay). +/ - /+ref+/ TimeOfDay opOpAssign(string op, D)(in D duration) pure nothrow + ref TimeOfDay opOpAssign(string op, D)(in D duration) @safe pure nothrow if((op == "+" || op == "-") && (is(Unqual!D == Duration) || is(Unqual!D == TickDuration))) @@ -14283,61 +13382,51 @@ assert(tod6 == TimeOfDay(0, 0, 59)); else static if(is(Unqual!D == TickDuration)) immutable hnsecs = duration.hnsecs; - //Ideally, this would just be - //return addSeconds(convert!("hnsecs", "seconds")(unaryFun!(op ~ "a")(hnsecs))); - //But there isn't currently a pure version of unaryFun!(). - - static if(op == "+") - immutable signedHNSecs = hnsecs; - else static if(op == "-") - immutable signedHNSecs = -hnsecs; - else - static assert(0); - - return addSeconds(convert!("hnsecs", "seconds")(signedHNSecs)); + mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op)); } unittest { - version(testStdDateTime) - { - auto duration = dur!"hours"(12); + auto duration = dur!"hours"(12); - _assertPred!"+="(TimeOfDay(12, 30, 33), dur!"hours"(7), TimeOfDay(19, 30, 33)); - _assertPred!"+="(TimeOfDay(12, 30, 33), dur!"hours"(-7), TimeOfDay(5, 30, 33)); - _assertPred!"+="(TimeOfDay(12, 30, 33), dur!"minutes"(7), TimeOfDay(12, 37, 33)); - _assertPred!"+="(TimeOfDay(12, 30, 33), dur!"minutes"(-7), TimeOfDay(12, 23, 33)); - _assertPred!"+="(TimeOfDay(12, 30, 33), dur!"seconds"(7), TimeOfDay(12, 30, 40)); - _assertPred!"+="(TimeOfDay(12, 30, 33), dur!"seconds"(-7), TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) + dur!"hours"(7) == TimeOfDay(19, 30, 33)); + assert(TimeOfDay(12, 30, 33) + dur!"hours"(-7) == TimeOfDay(5, 30, 33)); + assert(TimeOfDay(12, 30, 33) + dur!"minutes"(7) == TimeOfDay(12, 37, 33)); + assert(TimeOfDay(12, 30, 33) + dur!"minutes"(-7) == TimeOfDay(12, 23, 33)); + assert(TimeOfDay(12, 30, 33) + dur!"seconds"(7) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) + dur!"seconds"(-7) == TimeOfDay(12, 30, 26)); - _assertPred!"+="(TimeOfDay(12, 30, 33), dur!"msecs"(7000), TimeOfDay(12, 30, 40)); - _assertPred!"+="(TimeOfDay(12, 30, 33), dur!"msecs"(-7000), TimeOfDay(12, 30, 26)); - _assertPred!"+="(TimeOfDay(12, 30, 33), dur!"usecs"(7_000_000), TimeOfDay(12, 30, 40)); - _assertPred!"+="(TimeOfDay(12, 30, 33), dur!"usecs"(-7_000_000), TimeOfDay(12, 30, 26)); - _assertPred!"+="(TimeOfDay(12, 30, 33), dur!"hnsecs"(70_000_000), TimeOfDay(12, 30, 40)); - _assertPred!"+="(TimeOfDay(12, 30, 33), dur!"hnsecs"(-70_000_000), TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) + dur!"msecs"(7000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) + dur!"msecs"(-7000) == TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) + dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) + dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) + dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 26)); - _assertPred!"-="(TimeOfDay(12, 30, 33), dur!"hours"(-7), TimeOfDay(19, 30, 33)); - _assertPred!"-="(TimeOfDay(12, 30, 33), dur!"hours"(7), TimeOfDay(5, 30, 33)); - _assertPred!"-="(TimeOfDay(12, 30, 33), dur!"minutes"(-7), TimeOfDay(12, 37, 33)); - _assertPred!"-="(TimeOfDay(12, 30, 33), dur!"minutes"(7), TimeOfDay(12, 23, 33)); - _assertPred!"-="(TimeOfDay(12, 30, 33), dur!"seconds"(-7), TimeOfDay(12, 30, 40)); - _assertPred!"-="(TimeOfDay(12, 30, 33), dur!"seconds"(7), TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) - dur!"hours"(-7) == TimeOfDay(19, 30, 33)); + assert(TimeOfDay(12, 30, 33) - dur!"hours"(7) == TimeOfDay(5, 30, 33)); + assert(TimeOfDay(12, 30, 33) - dur!"minutes"(-7) == TimeOfDay(12, 37, 33)); + assert(TimeOfDay(12, 30, 33) - dur!"minutes"(7) == TimeOfDay(12, 23, 33)); + assert(TimeOfDay(12, 30, 33) - dur!"seconds"(-7) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) - dur!"seconds"(7) == TimeOfDay(12, 30, 26)); - _assertPred!"-="(TimeOfDay(12, 30, 33), dur!"msecs"(-7000), TimeOfDay(12, 30, 40)); - _assertPred!"-="(TimeOfDay(12, 30, 33), dur!"msecs"(7000), TimeOfDay(12, 30, 26)); - _assertPred!"-="(TimeOfDay(12, 30, 33), dur!"usecs"(-7_000_000), TimeOfDay(12, 30, 40)); - _assertPred!"-="(TimeOfDay(12, 30, 33), dur!"usecs"(7_000_000), TimeOfDay(12, 30, 26)); - _assertPred!"-="(TimeOfDay(12, 30, 33), dur!"hnsecs"(-70_000_000), TimeOfDay(12, 30, 40)); - _assertPred!"-="(TimeOfDay(12, 30, 33), dur!"hnsecs"(70_000_000), TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) - dur!"msecs"(-7000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) - dur!"msecs"(7000) == TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) - dur!"usecs"(-7_000_000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) - dur!"usecs"(7_000_000) == TimeOfDay(12, 30, 26)); + assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(-70_000_000) == TimeOfDay(12, 30, 40)); + assert(TimeOfDay(12, 30, 33) - dur!"hnsecs"(70_000_000) == TimeOfDay(12, 30, 26)); - const ctod = TimeOfDay(12, 33, 30); - immutable itod = TimeOfDay(12, 33, 30); - static assert(!__traits(compiles, ctod += duration)); - static assert(!__traits(compiles, itod += duration)); - static assert(!__traits(compiles, ctod -= duration)); - static assert(!__traits(compiles, itod -= duration)); - } + auto tod = TimeOfDay(19, 17, 22); + (tod += dur!"seconds"(9)) += dur!"seconds"(-7292); + assert(tod == TimeOfDay(17, 15, 59)); + + const ctod = TimeOfDay(12, 33, 30); + immutable itod = TimeOfDay(12, 33, 30); + static assert(!__traits(compiles, ctod += duration)); + static assert(!__traits(compiles, itod += duration)); + static assert(!__traits(compiles, ctod -= duration)); + static assert(!__traits(compiles, itod -= duration)); } @@ -14353,7 +13442,7 @@ assert(tod6 == TimeOfDay(0, 0, 59)); Params: rhs = The $(LREF TimeOfDay) to subtract from this one. +/ - Duration opBinary(string op)(in TimeOfDay rhs) const pure nothrow + Duration opBinary(string op)(in TimeOfDay rhs) @safe const pure nothrow if(op == "-") { immutable lhsSec = _hour * 3600 + _minute * 60 + _second; @@ -14364,46 +13453,37 @@ assert(tod6 == TimeOfDay(0, 0, 59)); unittest { - version(testStdDateTime) - { - auto tod = TimeOfDay(12, 30, 33); + auto tod = TimeOfDay(12, 30, 33); - _assertPred!"=="(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33), dur!"seconds"(-19_061)); - _assertPred!"=="(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52), dur!"seconds"(19_061)); - _assertPred!"=="(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33), dur!"seconds"(-7200)); - _assertPred!"=="(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33), dur!"seconds"(7200)); - _assertPred!"=="(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33), dur!"seconds"(-240)); - _assertPred!"=="(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33), dur!"seconds"(240)); - _assertPred!"=="(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34), dur!"seconds"(-1)); - _assertPred!"=="(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33), dur!"seconds"(1)); + assert(TimeOfDay(7, 12, 52) - TimeOfDay(12, 30, 33) == dur!"seconds"(-19_061)); + assert(TimeOfDay(12, 30, 33) - TimeOfDay(7, 12, 52) == dur!"seconds"(19_061)); + assert(TimeOfDay(12, 30, 33) - TimeOfDay(14, 30, 33) == dur!"seconds"(-7200)); + assert(TimeOfDay(14, 30, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(7200)); + assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 34, 33) == dur!"seconds"(-240)); + assert(TimeOfDay(12, 34, 33) - TimeOfDay(12, 30, 33) == dur!"seconds"(240)); + assert(TimeOfDay(12, 30, 33) - TimeOfDay(12, 30, 34) == dur!"seconds"(-1)); + assert(TimeOfDay(12, 30, 34) - TimeOfDay(12, 30, 33) == dur!"seconds"(1)); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - static assert(__traits(compiles, tod - tod)); - static assert(__traits(compiles, ctod - tod)); - static assert(__traits(compiles, itod - tod)); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + static assert(__traits(compiles, tod - tod)); + static assert(__traits(compiles, ctod - tod)); + static assert(__traits(compiles, itod - tod)); - static assert(__traits(compiles, tod - ctod)); - static assert(__traits(compiles, ctod - ctod)); - static assert(__traits(compiles, itod - ctod)); + static assert(__traits(compiles, tod - ctod)); + static assert(__traits(compiles, ctod - ctod)); + static assert(__traits(compiles, itod - ctod)); - static assert(__traits(compiles, tod - itod)); - static assert(__traits(compiles, ctod - itod)); - static assert(__traits(compiles, itod - itod)); - } + static assert(__traits(compiles, tod - itod)); + static assert(__traits(compiles, ctod - itod)); + static assert(__traits(compiles, itod - itod)); } /++ Converts this $(LREF TimeOfDay) to a string with the format HHMMSS. - - Examples: --------------------- -assert(TimeOfDay(0, 0, 0).toISOString() == "000000"); -assert(TimeOfDay(12, 30, 33).toISOString() == "123033"); --------------------- +/ - string toISOString() const nothrow + string toISOString() @safe const pure nothrow { try return format("%02d%02d%02d", _hour, _minute, _second); @@ -14411,34 +13491,28 @@ assert(TimeOfDay(12, 30, 33).toISOString() == "123033"); assert(0, "format() threw."); } + /// unittest { - version(testStdDateTime) - { - auto tod = TimeOfDay(12, 30, 33); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - static assert(__traits(compiles, tod.toISOString())); - static assert(__traits(compiles, ctod.toISOString())); - static assert(__traits(compiles, itod.toISOString())); + assert(TimeOfDay(0, 0, 0).toISOString() == "000000"); + assert(TimeOfDay(12, 30, 33).toISOString() == "123033"); + } - //Verify Examples. - assert(TimeOfDay(0, 0, 0).toISOString() == "000000"); - assert(TimeOfDay(12, 30, 33).toISOString() == "123033"); - } + unittest + { + auto tod = TimeOfDay(12, 30, 33); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + static assert(__traits(compiles, tod.toISOString())); + static assert(__traits(compiles, ctod.toISOString())); + static assert(__traits(compiles, itod.toISOString())); } /++ Converts this $(LREF TimeOfDay) to a string with the format HH:MM:SS. - - Examples: --------------------- -assert(TimeOfDay(0, 0, 0).toISOExtString() == "000000"); -assert(TimeOfDay(12, 30, 33).toISOExtString() == "123033"); --------------------- +/ - string toISOExtString() const nothrow + string toISOExtString() @safe const pure nothrow { try return format("%02d:%02d:%02d", _hour, _minute, _second); @@ -14446,63 +13520,42 @@ assert(TimeOfDay(12, 30, 33).toISOExtString() == "123033"); assert(0, "format() threw."); } + /// unittest { - version(testStdDateTime) - { - auto tod = TimeOfDay(12, 30, 33); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - static assert(__traits(compiles, tod.toISOExtString())); - static assert(__traits(compiles, ctod.toISOExtString())); - static assert(__traits(compiles, itod.toISOExtString())); - - //Verify Examples. - assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00"); - assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33"); - } + assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00"); + assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33"); } - - /+ - Converts this $(LREF TimeOfDay) to a string. - +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() + unittest { - return toISOExtString(); + auto tod = TimeOfDay(12, 30, 33); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + static assert(__traits(compiles, tod.toISOExtString())); + static assert(__traits(compiles, ctod.toISOExtString())); + static assert(__traits(compiles, itod.toISOExtString())); } /++ Converts this TimeOfDay to a string. +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() const nothrow + string toString() @safe const pure nothrow { return toISOExtString(); } unittest { - version(testStdDateTime) - { - auto tod = TimeOfDay(12, 30, 33); - const ctod = TimeOfDay(12, 30, 33); - immutable itod = TimeOfDay(12, 30, 33); - static assert(__traits(compiles, tod.toString())); - static assert(__traits(compiles, ctod.toString())); - static assert(__traits(compiles, itod.toString())); - } + auto tod = TimeOfDay(12, 30, 33); + const ctod = TimeOfDay(12, 30, 33); + immutable itod = TimeOfDay(12, 30, 33); + static assert(__traits(compiles, tod.toString())); + static assert(__traits(compiles, ctod.toString())); + static assert(__traits(compiles, itod.toString())); } - //TODO Add a function which returns a string in a user-specified format. - - /++ Creates a $(LREF TimeOfDay) from a string with the format HHMMSS. @@ -14514,15 +13567,8 @@ assert(TimeOfDay(12, 30, 33).toISOExtString() == "123033"); Throws: $(LREF DateTimeException) if the given string is not in the ISO format or if the resulting $(LREF TimeOfDay) would not be valid. - - Examples: --------------------- -assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0)); -assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33)); -assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33)); --------------------- +/ - static TimeOfDay fromISOString(S)(in S isoString) + static TimeOfDay fromISOString(S)(in S isoString) @safe pure if(isSomeString!S) { auto dstr = to!dstring(strip(isoString)); @@ -14533,82 +13579,82 @@ assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33)); auto minutes = dstr[2 .. 4]; auto seconds = dstr[4 .. $]; - enforce(!canFind!(not!isDigit)(hours), new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(!canFind!(not!isDigit)(minutes), new DateTimeException(format("Invalid ISO String: %s", isoString))); - enforce(!canFind!(not!isDigit)(seconds), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(all!isDigit(hours), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(all!isDigit(minutes), new DateTimeException(format("Invalid ISO String: %s", isoString))); + enforce(all!isDigit(seconds), new DateTimeException(format("Invalid ISO String: %s", isoString))); return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); } + /// + unittest + { + assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0)); + assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33)); + assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33)); + } + unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException(TimeOfDay.fromISOString("")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("000")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0000")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00000")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("13033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1277")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12707")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12070")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOString("0::")); - assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("::0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am")); - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33")); - - _assertPred!"=="(TimeOfDay.fromISOString("011217"), TimeOfDay(1, 12, 17)); - _assertPred!"=="(TimeOfDay.fromISOString("001412"), TimeOfDay(0, 14, 12)); - _assertPred!"=="(TimeOfDay.fromISOString("000007"), TimeOfDay(0, 0, 7)); - _assertPred!"=="(TimeOfDay.fromISOString("011217 "), TimeOfDay(1, 12, 17)); - _assertPred!"=="(TimeOfDay.fromISOString(" 011217"), TimeOfDay(1, 12, 17)); - _assertPred!"=="(TimeOfDay.fromISOString(" 011217 "), TimeOfDay(1, 12, 17)); - - //Verify Examples. - assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0)); - assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33)); - assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33)); - } + assertThrown!DateTimeException(TimeOfDay.fromISOString("")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("000")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0000")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00000")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("13033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1277")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12707")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12070")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12303a")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1230a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("123a33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12a033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1a0033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("a20033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1200330")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("-120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("+120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("120033am")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("120033pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOString("0::")); + assertThrown!DateTimeException(TimeOfDay.fromISOString(":0:")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("::0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("0:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("00:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("13:0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:7")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:7:07")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:07:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:3a")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:30:a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:3a:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:a0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("1a:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("a2:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:003:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("120:03:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("012:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("01:200:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("-12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("+12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33am")); + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOString("12:00:33")); + + assert(TimeOfDay.fromISOString("011217") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOString("001412") == TimeOfDay(0, 14, 12)); + assert(TimeOfDay.fromISOString("000007") == TimeOfDay(0, 0, 7)); + assert(TimeOfDay.fromISOString("011217 ") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOString(" 011217") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOString(" 011217 ") == TimeOfDay(1, 12, 17)); } @@ -14623,15 +13669,8 @@ assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33)); $(LREF DateTimeException) if the given string is not in the ISO Extended format or if the resulting $(LREF TimeOfDay) would not be valid. - - Examples: --------------------- -assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0)); -assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33)); -assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33)); --------------------- +/ - static TimeOfDay fromISOExtString(S)(in S isoExtString) + static TimeOfDay fromISOExtString(S)(in S isoExtString) @safe pure if(isSomeString!S) { auto dstr = to!dstring(strip(isoExtString)); @@ -14644,119 +13683,109 @@ assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33)); enforce(dstr[2] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); enforce(dstr[5] == ':', new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(!canFind!(not!isDigit)(hours), + enforce(all!isDigit(hours), new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(!canFind!(not!isDigit)(minutes), + enforce(all!isDigit(minutes), new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); - enforce(!canFind!(not!isDigit)(seconds), + enforce(all!isDigit(seconds), new DateTimeException(format("Invalid ISO Extended String: %s", isoExtString))); return TimeOfDay(to!int(hours), to!int(minutes), to!int(seconds)); } + /// + unittest + { + assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0)); + assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33)); + assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33)); + } + unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("000")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0000")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00000")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1277")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12707")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12070")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12303a")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1230a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("123a33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12a033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a0033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a20033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1200330")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+120033")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033am")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0::")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString(":0:")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("::0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:00:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:00")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13:0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:7")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:07")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:07:0")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:3a")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:a3")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:3a:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:a0:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a2:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:003:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120:03:30")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("012:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("01:200:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+12:00:33")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33am")); - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33pm")); - - assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033")); - - _assertPred!"=="(TimeOfDay.fromISOExtString("01:12:17"), TimeOfDay(1, 12, 17)); - _assertPred!"=="(TimeOfDay.fromISOExtString("00:14:12"), TimeOfDay(0, 14, 12)); - _assertPred!"=="(TimeOfDay.fromISOExtString("00:00:07"), TimeOfDay(0, 0, 7)); - _assertPred!"=="(TimeOfDay.fromISOExtString("01:12:17 "), TimeOfDay(1, 12, 17)); - _assertPred!"=="(TimeOfDay.fromISOExtString(" 01:12:17"), TimeOfDay(1, 12, 17)); - _assertPred!"=="(TimeOfDay.fromISOExtString(" 01:12:17 "), TimeOfDay(1, 12, 17)); - - //Verify Examples. - assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0)); - assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33)); - assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33)); - } - } - - - //TODO Add function which takes a user-specified time format and produces a TimeOfDay - - //TODO Add function which takes pretty much any time-string and produces a TimeOfDay - // Obviously, it will be less efficient, and it probably won't manage _every_ - // possible date format, but a smart conversion function would be nice. + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("000")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0000")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00000")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1277")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12707")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12070")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12303a")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1230a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("123a33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12a033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a0033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a20033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1200330")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+120033")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033am")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0::")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString(":0:")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("::0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("0:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:00:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("00:0:00")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("13:0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:7")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:7:07")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:07:0")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:3a")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:30:a3")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:3a:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:a0:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("1a:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("a2:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:003:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120:03:30")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("012:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("01:200:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("-12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("+12:00:33")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33am")); + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("12:00:33pm")); + + assertThrown!DateTimeException(TimeOfDay.fromISOExtString("120033")); + + assert(TimeOfDay.fromISOExtString("01:12:17") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOExtString("00:14:12") == TimeOfDay(0, 14, 12)); + assert(TimeOfDay.fromISOExtString("00:00:07") == TimeOfDay(0, 0, 7)); + assert(TimeOfDay.fromISOExtString("01:12:17 ") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOExtString(" 01:12:17") == TimeOfDay(1, 12, 17)); + assert(TimeOfDay.fromISOExtString(" 01:12:17 ") == TimeOfDay(1, 12, 17)); + } /++ Returns midnight. +/ - @property static TimeOfDay min() pure nothrow + @property static TimeOfDay min() @safe pure nothrow { return TimeOfDay.init; } unittest { - version(testStdDateTime) - { - assert(TimeOfDay.min.hour == 0); - assert(TimeOfDay.min.minute == 0); - assert(TimeOfDay.min.second == 0); - assert(TimeOfDay.min < TimeOfDay.max); - } + assert(TimeOfDay.min.hour == 0); + assert(TimeOfDay.min.minute == 0); + assert(TimeOfDay.min.second == 0); + assert(TimeOfDay.min < TimeOfDay.max); } /++ Returns one second short of midnight. +/ - @property static TimeOfDay max() pure nothrow + @property static TimeOfDay max() @safe pure nothrow { auto tod = TimeOfDay.init; tod._hour = maxHour; @@ -14768,13 +13797,10 @@ assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33)); unittest { - version(testStdDateTime) - { - assert(TimeOfDay.max.hour == 23); - assert(TimeOfDay.max.minute == 59); - assert(TimeOfDay.max.second == 59); - assert(TimeOfDay.max > TimeOfDay.min); - } + assert(TimeOfDay.max.hour == 23); + assert(TimeOfDay.max.minute == 59); + assert(TimeOfDay.max.second == 59); + assert(TimeOfDay.max > TimeOfDay.min); } @@ -14791,7 +13817,7 @@ private: Params: seconds = The number of seconds to add to this TimeOfDay. +/ - ref TimeOfDay addSeconds(long seconds) pure nothrow + ref TimeOfDay _addSeconds(long seconds) @safe pure nothrow { long hnsecs = convert!("seconds", "hnsecs")(seconds); hnsecs += convert!("hours", "hnsecs")(_hour); @@ -14816,102 +13842,93 @@ private: unittest { - version(testStdDateTime) - { - static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) - { - orig.addSeconds(seconds); - _assertPred!"=="(orig, expected, "", __FILE__, line); - } - - testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); - testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); - testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); - testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); - testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); - testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); - testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); - testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0)); - testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3)); - testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32)); - testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33)); - testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34)); - - testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59)); - testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0)); - testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1)); - testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0)); - testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34)); - testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33)); - - testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); - testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); - testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); - testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); - testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); - testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); - testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59)); - testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58)); - testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34)); - testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33)); - testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32)); - - testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0)); - testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59)); - testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33)); - testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32)); - testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59)); - testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33)); - - testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); - testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); - testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59)); - - testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); - testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); - testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59)); - - testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); - testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); - testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59)); - - testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0)); - testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); - testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); - - const ctod = TimeOfDay(0, 0, 0); - immutable itod = TimeOfDay(0, 0, 0); - static assert(!__traits(compiles, ctod.addSeconds(7))); - static assert(!__traits(compiles, itod.addSeconds(7))); - } + static void testTOD(TimeOfDay orig, int seconds, in TimeOfDay expected, size_t line = __LINE__) + { + orig._addSeconds(seconds); + assert(orig == expected); + } + + testTOD(TimeOfDay(12, 30, 33), 0, TimeOfDay(12, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 1, TimeOfDay(12, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 2, TimeOfDay(12, 30, 35)); + testTOD(TimeOfDay(12, 30, 33), 3, TimeOfDay(12, 30, 36)); + testTOD(TimeOfDay(12, 30, 33), 4, TimeOfDay(12, 30, 37)); + testTOD(TimeOfDay(12, 30, 33), 5, TimeOfDay(12, 30, 38)); + testTOD(TimeOfDay(12, 30, 33), 10, TimeOfDay(12, 30, 43)); + testTOD(TimeOfDay(12, 30, 33), 15, TimeOfDay(12, 30, 48)); + testTOD(TimeOfDay(12, 30, 33), 26, TimeOfDay(12, 30, 59)); + testTOD(TimeOfDay(12, 30, 33), 27, TimeOfDay(12, 31, 0)); + testTOD(TimeOfDay(12, 30, 33), 30, TimeOfDay(12, 31, 3)); + testTOD(TimeOfDay(12, 30, 33), 59, TimeOfDay(12, 31, 32)); + testTOD(TimeOfDay(12, 30, 33), 60, TimeOfDay(12, 31, 33)); + testTOD(TimeOfDay(12, 30, 33), 61, TimeOfDay(12, 31, 34)); + + testTOD(TimeOfDay(12, 30, 33), 1766, TimeOfDay(12, 59, 59)); + testTOD(TimeOfDay(12, 30, 33), 1767, TimeOfDay(13, 0, 0)); + testTOD(TimeOfDay(12, 30, 33), 1768, TimeOfDay(13, 0, 1)); + testTOD(TimeOfDay(12, 30, 33), 2007, TimeOfDay(13, 4, 0)); + testTOD(TimeOfDay(12, 30, 33), 3599, TimeOfDay(13, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), 3600, TimeOfDay(13, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), 3601, TimeOfDay(13, 30, 34)); + testTOD(TimeOfDay(12, 30, 33), 7200, TimeOfDay(14, 30, 33)); + + testTOD(TimeOfDay(12, 30, 33), -1, TimeOfDay(12, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), -2, TimeOfDay(12, 30, 31)); + testTOD(TimeOfDay(12, 30, 33), -3, TimeOfDay(12, 30, 30)); + testTOD(TimeOfDay(12, 30, 33), -4, TimeOfDay(12, 30, 29)); + testTOD(TimeOfDay(12, 30, 33), -5, TimeOfDay(12, 30, 28)); + testTOD(TimeOfDay(12, 30, 33), -10, TimeOfDay(12, 30, 23)); + testTOD(TimeOfDay(12, 30, 33), -15, TimeOfDay(12, 30, 18)); + testTOD(TimeOfDay(12, 30, 33), -33, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 33), -34, TimeOfDay(12, 29, 59)); + testTOD(TimeOfDay(12, 30, 33), -35, TimeOfDay(12, 29, 58)); + testTOD(TimeOfDay(12, 30, 33), -59, TimeOfDay(12, 29, 34)); + testTOD(TimeOfDay(12, 30, 33), -60, TimeOfDay(12, 29, 33)); + testTOD(TimeOfDay(12, 30, 33), -61, TimeOfDay(12, 29, 32)); + + testTOD(TimeOfDay(12, 30, 33), -1833, TimeOfDay(12, 0, 0)); + testTOD(TimeOfDay(12, 30, 33), -1834, TimeOfDay(11, 59, 59)); + testTOD(TimeOfDay(12, 30, 33), -3600, TimeOfDay(11, 30, 33)); + testTOD(TimeOfDay(12, 30, 33), -3601, TimeOfDay(11, 30, 32)); + testTOD(TimeOfDay(12, 30, 33), -5134, TimeOfDay(11, 4, 59)); + testTOD(TimeOfDay(12, 30, 33), -7200, TimeOfDay(10, 30, 33)); + + testTOD(TimeOfDay(12, 30, 0), 1, TimeOfDay(12, 30, 1)); + testTOD(TimeOfDay(12, 30, 0), 0, TimeOfDay(12, 30, 0)); + testTOD(TimeOfDay(12, 30, 0), -1, TimeOfDay(12, 29, 59)); + + testTOD(TimeOfDay(12, 0, 0), 1, TimeOfDay(12, 0, 1)); + testTOD(TimeOfDay(12, 0, 0), 0, TimeOfDay(12, 0, 0)); + testTOD(TimeOfDay(12, 0, 0), -1, TimeOfDay(11, 59, 59)); + + testTOD(TimeOfDay(0, 0, 0), 1, TimeOfDay(0, 0, 1)); + testTOD(TimeOfDay(0, 0, 0), 0, TimeOfDay(0, 0, 0)); + testTOD(TimeOfDay(0, 0, 0), -1, TimeOfDay(23, 59, 59)); + + testTOD(TimeOfDay(23, 59, 59), 1, TimeOfDay(0, 0, 0)); + testTOD(TimeOfDay(23, 59, 59), 0, TimeOfDay(23, 59, 59)); + testTOD(TimeOfDay(23, 59, 59), -1, TimeOfDay(23, 59, 58)); + + const ctod = TimeOfDay(0, 0, 0); + immutable itod = TimeOfDay(0, 0, 0); + static assert(!__traits(compiles, ctod._addSeconds(7))); + static assert(!__traits(compiles, itod._addSeconds(7))); } /+ Whether the given values form a valid $(LREF TimeOfDay). +/ - static bool _valid(int hour, int minute, int second) pure nothrow + static bool _valid(int hour, int minute, int second) @safe pure nothrow { return valid!"hours"(hour) && valid!"minutes"(minute) && valid!"seconds"(second); } - pure invariant() + @safe pure invariant() { assert(_valid(_hour, _minute, _second), - "Invariant Failure: hour [" ~ - numToString(_hour) ~ - "] minute [" ~ - numToString(_minute) ~ - "] second [" ~ - numToString(_second) ~ - "]"); + format("Invariant Failure: hour [%s] minute [%s] second [%s]", _hour, _minute, _second)); } ubyte _hour; @@ -14942,7 +13959,7 @@ public: date = The date portion of $(LREF DateTime). tod = The time portion of $(LREF DateTime). +/ - this(in Date date, in TimeOfDay tod = TimeOfDay.init) pure nothrow + this(in Date date, in TimeOfDay tod = TimeOfDay.init) @safe pure nothrow { _date = date; _tod = tod; @@ -14950,25 +13967,22 @@ public: unittest { - version(testStdDateTime) { - { - auto dt = DateTime.init; - _assertPred!"=="(dt._date, Date.init); - _assertPred!"=="(dt._tod, TimeOfDay.init); - } + auto dt = DateTime.init; + assert(dt._date == Date.init); + assert(dt._tod == TimeOfDay.init); + } - { - auto dt = DateTime(Date(1999, 7 ,6)); - _assertPred!"=="(dt._date, Date(1999, 7, 6)); - _assertPred!"=="(dt._tod, TimeOfDay.init); - } + { + auto dt = DateTime(Date(1999, 7 ,6)); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay.init); + } - { - auto dt = DateTime(Date(1999, 7 ,6), TimeOfDay(12, 30, 33)); - _assertPred!"=="(dt._date, Date(1999, 7, 6)); - _assertPred!"=="(dt._tod, TimeOfDay(12, 30, 33)); - } + { + auto dt = DateTime(Date(1999, 7 ,6), TimeOfDay(12, 30, 33)); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay(12, 30, 33)); } } @@ -14982,8 +13996,7 @@ public: minute = The minute portion of the time; second = The second portion of the time; +/ - this(int year, int month, int day, - int hour = 0, int minute = 0, int second = 0) pure + this(int year, int month, int day, int hour = 0, int minute = 0, int second = 0) @safe pure { _date = Date(year, month, day); _tod = TimeOfDay(hour, minute, second); @@ -14991,19 +14004,16 @@ public: unittest { - version(testStdDateTime) { - { - auto dt = DateTime(1999, 7 ,6); - _assertPred!"=="(dt._date, Date(1999, 7, 6)); - _assertPred!"=="(dt._tod, TimeOfDay.init); - } + auto dt = DateTime(1999, 7 ,6); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay.init); + } - { - auto dt = DateTime(1999, 7 ,6, 12, 30, 33); - _assertPred!"=="(dt._date, Date(1999, 7, 6)); - _assertPred!"=="(dt._tod, TimeOfDay(12, 30, 33)); - } + { + auto dt = DateTime(1999, 7 ,6, 12, 30, 33); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay(12, 30, 33)); } } @@ -15018,7 +14028,7 @@ public: $(TR $(TD this > rhs) $(TD > 0)) ) +/ - int opCmp(in DateTime rhs) const pure nothrow + int opCmp(in DateTime rhs) @safe const pure nothrow { immutable dateResult = _date.opCmp(rhs._date); @@ -15030,231 +14040,225 @@ public: unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!("opCmp", "==")(DateTime(Date.init, TimeOfDay.init), DateTime.init); - - _assertPred!("opCmp", "==")(DateTime(Date(1999, 1, 1)), DateTime(Date(1999, 1, 1))); - _assertPred!("opCmp", "==")(DateTime(Date(1, 7, 1)), DateTime(Date(1, 7, 1))); - _assertPred!("opCmp", "==")(DateTime(Date(1, 1, 6)), DateTime(Date(1, 1, 6))); - - _assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 1)), DateTime(Date(1999, 7, 1))); - _assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6)), DateTime(Date(1999, 7, 6))); - - _assertPred!("opCmp", "==")(DateTime(Date(1, 7, 6)), DateTime(Date(1, 7, 6))); - - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6)), DateTime(Date(2000, 7, 6))); - _assertPred!("opCmp", ">")(DateTime(Date(2000, 7, 6)), DateTime(Date(1999, 7, 6))); - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6)), DateTime(Date(1999, 8, 6))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 8, 6)), DateTime(Date(1999, 7, 6))); - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6)), DateTime(Date(1999, 7, 7))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 7)), DateTime(Date(1999, 7, 6))); - - _assertPred!("opCmp", "<")(DateTime(Date(1999, 8, 7)), DateTime(Date(2000, 7, 6))); - _assertPred!("opCmp", ">")(DateTime(Date(2000, 8, 6)), DateTime(Date(1999, 7, 7))); - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 7)), DateTime(Date(2000, 7, 6))); - _assertPred!("opCmp", ">")(DateTime(Date(2000, 7, 6)), DateTime(Date(1999, 7, 7))); - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 7)), DateTime(Date(1999, 8, 6))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 8, 6)), DateTime(Date(1999, 7, 7))); - - - _assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))); - _assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))); - _assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)), - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))); - _assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))); - - _assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - _assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - _assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - _assertPred!("opCmp", "==")(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))); - - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - _assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)), - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - - _assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)), - DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)), - DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)), - DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)), - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)), - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)), - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)), - DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)), - DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", "<")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)), - DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - - //Test B.C. - _assertPred!("opCmp", "==")(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)), - DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", "==")(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)), - DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", "==")(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))); - - _assertPred!("opCmp", "==")(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)), - DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", "==")(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - _assertPred!("opCmp", "==")(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))); - - _assertPred!("opCmp", "<")(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", "<")(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", "<")(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)), - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - _assertPred!("opCmp", "<")(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)), - DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", "<")(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)), - DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", "<")(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)), - DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))); - - //Test Both - _assertPred!("opCmp", "<")(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - _assertPred!("opCmp", "<")(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))); - - _assertPred!("opCmp", "<")(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))); - - _assertPred!("opCmp", "<")(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))); - - _assertPred!("opCmp", "<")(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)), - DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33))); - _assertPred!("opCmp", ">")(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33)), - DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); - static assert(__traits(compiles, dt.opCmp(dt))); - static assert(__traits(compiles, dt.opCmp(cdt))); - static assert(__traits(compiles, dt.opCmp(idt))); - static assert(__traits(compiles, cdt.opCmp(dt))); - static assert(__traits(compiles, cdt.opCmp(cdt))); - static assert(__traits(compiles, cdt.opCmp(idt))); - static assert(__traits(compiles, idt.opCmp(dt))); - static assert(__traits(compiles, idt.opCmp(cdt))); - static assert(__traits(compiles, idt.opCmp(idt))); - } + //Test A.D. + assert(DateTime(Date.init, TimeOfDay.init).opCmp(DateTime.init) == 0); + + assert(DateTime(Date(1999, 1, 1)).opCmp(DateTime(Date(1999, 1, 1))) == 0); + assert(DateTime(Date(1, 7, 1)).opCmp(DateTime(Date(1, 7, 1))) == 0); + assert(DateTime(Date(1, 1, 6)).opCmp(DateTime(Date(1, 1, 6))) == 0); + + assert(DateTime(Date(1999, 7, 1)).opCmp(DateTime(Date(1999, 7, 1))) == 0); + assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) == 0); + + assert(DateTime(Date(1, 7, 6)).opCmp(DateTime(Date(1, 7, 6))) == 0); + + assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(2000, 7, 6))) < 0); + assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0); + assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 8, 6))) < 0); + assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 6))) > 0); + assert(DateTime(Date(1999, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) < 0); + assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 7, 6))) > 0); + + assert(DateTime(Date(1999, 8, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0); + assert(DateTime(Date(2000, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); + assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(2000, 7, 6))) < 0); + assert(DateTime(Date(2000, 7, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); + assert(DateTime(Date(1999, 7, 7)).opCmp(DateTime(Date(1999, 8, 6))) < 0); + assert(DateTime(Date(1999, 8, 6)).opCmp(DateTime(Date(1999, 7, 7))) > 0); + + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 0))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) == 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))) == 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))) == 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) < 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) < 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33)).opCmp( + DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)).opCmp( + DateTime(Date(1999, 7, 7), TimeOfDay(12, 31, 33))) < 0); + assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)).opCmp( + DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))) > 0); + + //Test B.C. + assert(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33))) == 0); + assert(DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))) == 0); + assert(DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1, 1, 6), TimeOfDay(12, 30, 33))) == 0); + + assert(DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 1), TimeOfDay(12, 30, 33))) == 0); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) == 0); + + assert(DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1, 7, 6), TimeOfDay(12, 30, 33))) == 0); + + assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-2000, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-2000, 7, 6), TimeOfDay(12, 30, 33))) > 0); + assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0); + + //Test Both + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 7), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 8, 7), TimeOfDay(12, 30, 33))) > 0); + + assert(DateTime(Date(-1999, 8, 6), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(1999, 6, 6), TimeOfDay(12, 30, 33))) < 0); + assert(DateTime(Date(1999, 6, 8), TimeOfDay(12, 30, 33)).opCmp( + DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))) > 0); + + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 30)); + static assert(__traits(compiles, dt.opCmp(dt))); + static assert(__traits(compiles, dt.opCmp(cdt))); + static assert(__traits(compiles, dt.opCmp(idt))); + static assert(__traits(compiles, cdt.opCmp(dt))); + static assert(__traits(compiles, cdt.opCmp(cdt))); + static assert(__traits(compiles, cdt.opCmp(idt))); + static assert(__traits(compiles, idt.opCmp(dt))); + static assert(__traits(compiles, idt.opCmp(cdt))); + static assert(__traits(compiles, idt.opCmp(idt))); } /++ The date portion of $(LREF DateTime). +/ - @property Date date() const pure nothrow + @property Date date() @safe const pure nothrow { return _date; } unittest { - version(testStdDateTime) { - { - auto dt = DateTime.init; - _assertPred!"=="(dt.date, Date.init); - } - - { - auto dt = DateTime(Date(1999, 7, 6)); - _assertPred!"=="(dt.date, Date(1999, 7, 6)); - } + auto dt = DateTime.init; + assert(dt.date == Date.init); + } - const cdt = DateTime(1999, 7, 6); - immutable idt = DateTime(1999, 7, 6); - static assert(__traits(compiles, cdt.date == Date(2010, 1, 1))); - static assert(__traits(compiles, idt.date == Date(2010, 1, 1))); + { + auto dt = DateTime(Date(1999, 7, 6)); + assert(dt.date == Date(1999, 7, 6)); } + + const cdt = DateTime(1999, 7, 6); + immutable idt = DateTime(1999, 7, 6); + static assert(__traits(compiles, cdt.date == Date(2010, 1, 1))); + static assert(__traits(compiles, idt.date == Date(2010, 1, 1))); } @@ -15264,55 +14268,49 @@ public: Params: date = The Date to set this $(LREF DateTime)'s date portion to. +/ - @property void date(in Date date) pure nothrow + @property void date(in Date date) @safe pure nothrow { _date = date; } unittest { - version(testStdDateTime) - { - auto dt = DateTime.init; - dt.date = Date(1999, 7, 6); - _assertPred!"=="(dt._date, Date(1999, 7, 6)); - _assertPred!"=="(dt._tod, TimeOfDay.init); + auto dt = DateTime.init; + dt.date = Date(1999, 7, 6); + assert(dt._date == Date(1999, 7, 6)); + assert(dt._tod == TimeOfDay.init); - const cdt = DateTime(1999, 7, 6); - immutable idt = DateTime(1999, 7, 6); - static assert(!__traits(compiles, cdt.date = Date(2010, 1, 1))); - static assert(!__traits(compiles, idt.date = Date(2010, 1, 1))); - } + const cdt = DateTime(1999, 7, 6); + immutable idt = DateTime(1999, 7, 6); + static assert(!__traits(compiles, cdt.date = Date(2010, 1, 1))); + static assert(!__traits(compiles, idt.date = Date(2010, 1, 1))); } /++ The time portion of $(LREF DateTime). +/ - @property TimeOfDay timeOfDay() const pure nothrow + @property TimeOfDay timeOfDay() @safe const pure nothrow { return _tod; } unittest { - version(testStdDateTime) { - { - auto dt = DateTime.init; - _assertPred!"=="(dt.timeOfDay, TimeOfDay.init); - } - - { - auto dt = DateTime(Date.init, TimeOfDay(12, 30, 33)); - _assertPred!"=="(dt.timeOfDay, TimeOfDay(12, 30, 33)); - } + auto dt = DateTime.init; + assert(dt.timeOfDay == TimeOfDay.init); + } - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.timeOfDay == TimeOfDay(12, 30, 33))); - static assert(__traits(compiles, idt.timeOfDay == TimeOfDay(12, 30, 33))); + { + auto dt = DateTime(Date.init, TimeOfDay(12, 30, 33)); + assert(dt.timeOfDay == TimeOfDay(12, 30, 33)); } + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.timeOfDay == TimeOfDay(12, 30, 33))); + static assert(__traits(compiles, idt.timeOfDay == TimeOfDay(12, 30, 33))); } @@ -15323,25 +14321,22 @@ public: tod = The $(LREF TimeOfDay) to set this $(LREF DateTime)'s time portion to. +/ - @property void timeOfDay(in TimeOfDay tod) pure nothrow + @property void timeOfDay(in TimeOfDay tod) @safe pure nothrow { _tod = tod; } unittest { - version(testStdDateTime) - { - auto dt = DateTime.init; - dt.timeOfDay = TimeOfDay(12, 30, 33); - _assertPred!"=="(dt._date, Date.init); - _assertPred!"=="(dt._tod, TimeOfDay(12, 30, 33)); + auto dt = DateTime.init; + dt.timeOfDay = TimeOfDay(12, 30, 33); + assert(dt._date == Date.init); + assert(dt._tod == TimeOfDay(12, 30, 33)); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.timeOfDay = TimeOfDay(12, 30, 33))); - static assert(!__traits(compiles, idt.timeOfDay = TimeOfDay(12, 30, 33))); - } + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.timeOfDay = TimeOfDay(12, 30, 33))); + static assert(!__traits(compiles, idt.timeOfDay = TimeOfDay(12, 30, 33))); } @@ -15349,24 +14344,21 @@ public: Year of the Gregorian Calendar. Positive numbers are A.D. Non-positive are B.C. +/ - @property short year() const pure nothrow + @property short year() @safe const pure nothrow { return _date.year; } unittest { - version(testStdDateTime) - { - _assertPred!"=="(Date.init.year, 1); - _assertPred!"=="(Date(1999, 7, 6).year, 1999); - _assertPred!"=="(Date(-1999, 7, 6).year, -1999); + assert(Date.init.year == 1); + assert(Date(1999, 7, 6).year == 1999); + assert(Date(-1999, 7, 6).year == -1999); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, idt.year)); - static assert(__traits(compiles, idt.year)); - } + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, idt.year)); + static assert(__traits(compiles, idt.year)); } @@ -15380,43 +14372,36 @@ public: Throws: $(LREF DateTimeException) if the new year is not a leap year and if the resulting date would be on February 29th. - - Examples: --------------------- -assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999); -assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010); -assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7); --------------------- +/ - @property void year(int year) pure + @property void year(int year) @safe pure { _date.year = year; } + /// unittest { - version(testStdDateTime) - { - static void testDT(DateTime dt, int year, in DateTime expected, size_t line = __LINE__) - { - dt.year = year; - _assertPred!"=="(dt, expected, "", __FILE__, line); - } + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999); + assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010); + assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7); + } - testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), 1999, DateTime(Date(1999, 1, 1), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), 0, DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), -1999, DateTime(Date(-1999, 1, 1), TimeOfDay(12, 30, 33))); + unittest + { + static void testDT(DateTime dt, int year, in DateTime expected, size_t line = __LINE__) + { + dt.year = year; + assert(dt == expected); + } - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.year = 7)); - static assert(!__traits(compiles, idt.year = 7)); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), 1999, DateTime(Date(1999, 1, 1), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), 0, DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), -1999, DateTime(Date(-1999, 1, 1), TimeOfDay(12, 30, 33))); - //Verify Examples. - assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999); - assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010); - assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7); - } + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.year = 7)); + static assert(!__traits(compiles, idt.year = 7)); } @@ -15425,37 +14410,30 @@ assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7); Throws: $(LREF DateTimeException) if $(D isAD) is true. - - Examples: --------------------- -assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1); -assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2); -assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101); --------------------- +/ - @property short yearBC() const pure + @property short yearBC() @safe const pure { return _date.yearBC; } + /// unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException((in DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1)))); + assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1); + assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2); + assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101); + } - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, dt.yearBC = 12)); - static assert(!__traits(compiles, cdt.yearBC = 12)); - static assert(!__traits(compiles, idt.yearBC = 12)); + unittest + { + assertThrown!DateTimeException((in DateTime dt){dt.yearBC;}(DateTime(Date(1, 1, 1)))); - //Verify Examples. - assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1); - assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2); - assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101); - } + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, dt.yearBC = 12)); + static assert(!__traits(compiles, cdt.yearBC = 12)); + static assert(!__traits(compiles, idt.yearBC = 12)); } @@ -15467,83 +14445,62 @@ assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101); Throws: $(LREF DateTimeException) if a non-positive value is given. - - Examples: --------------------- -auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0)); -dt.yearBC = 1; -assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0))); - -dt.yearBC = 10; -assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0))); --------------------- +/ - @property void yearBC(int year) pure + @property void yearBC(int year) @safe pure { _date.yearBC = year; } + /// unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException((DateTime dt){dt.yearBC = -1;}(DateTime(Date(1, 1, 1)))); + auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0)); + dt.yearBC = 1; + assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0))); - { - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, dt.yearBC = 12)); - static assert(!__traits(compiles, cdt.yearBC = 12)); - static assert(!__traits(compiles, idt.yearBC = 12)); - } + dt.yearBC = 10; + assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0))); + } - //Verify Examples. - { - auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0)); - dt.yearBC = 1; - assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0))); + unittest + { + assertThrown!DateTimeException((DateTime dt){dt.yearBC = -1;}(DateTime(Date(1, 1, 1)))); - dt.yearBC = 10; - assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0))); - } - } + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, dt.yearBC = 12)); + static assert(!__traits(compiles, cdt.yearBC = 12)); + static assert(!__traits(compiles, idt.yearBC = 12)); } /++ Month of a Gregorian Year. - - Examples: --------------------- -assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7); -assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10); -assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4); --------------------- +/ - @property Month month() const pure nothrow + @property Month month() @safe const pure nothrow { return _date.month; } + /// unittest { - version(testStdDateTime) - { - _assertPred!"=="(DateTime.init.month, 1); - _assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).month, 7); - _assertPred!"=="(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).month, 7); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7); + assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10); + assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4); + } - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.month)); - static assert(__traits(compiles, idt.month)); + unittest + { + assert(DateTime.init.month == 1); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7); + assert(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)).month == 7); - //Verify Examples. - assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7); - assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10); - assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4); - } + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.month)); + static assert(__traits(compiles, idt.month)); } @@ -15556,65 +14513,54 @@ assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4); Throws: $(LREF DateTimeException) if the given month is not a valid month. +/ - @property void month(Month month) pure + @property void month(Month month) @safe pure { _date.month = month; } unittest { - version(testStdDateTime) + static void testDT(DateTime dt, Month month, in DateTime expected = DateTime.init, size_t line = __LINE__) { - static void testDT(DateTime dt, Month month, in DateTime expected = DateTime.init, size_t line = __LINE__) - { - dt.month = month; - assert(expected != DateTime.init); - _assertPred!"=="(dt, expected, "", __FILE__, line); - } + dt.month = month; + assert(expected != DateTime.init); + assert(dt == expected); + } - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month)0)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month)13)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month)0)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month)13)); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month)7, DateTime(Date(1, 7, 1), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month)7, DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month)7, DateTime(Date(1, 7, 1), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(12, 30, 33)), cast(Month)7, DateTime(Date(-1, 7, 1), TimeOfDay(12, 30, 33))); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.month = 12)); - static assert(!__traits(compiles, idt.month = 12)); - } + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.month = 12)); + static assert(!__traits(compiles, idt.month = 12)); } /++ Day of a Gregorian Month. - - Examples: --------------------- -assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6); -assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4); -assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); --------------------- +/ - @property ubyte day() const pure nothrow + @property ubyte day() @safe const pure nothrow { return _date.day; } - //Verify Examples. - version(testStdDateTime) unittest + /// + unittest { assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6); assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4); assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); } - version(testStdDateTime) unittest + unittest { - static void test(DateTime dateTime, int expected, size_t line = __LINE__) + static void test(DateTime dateTime, int expected) { - _assertPred!"=="(dateTime.day, expected, - format("Value given: %s", dateTime), __FILE__, line); + assert(dateTime.day == expected, format("Value given: %s", dateTime)); } foreach(year; chain(testYearsBC, testYearsAD)) @@ -15643,118 +14589,112 @@ assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); $(LREF DateTimeException) if the given day is not a valid day of the current month. +/ - @property void day(int day) pure + @property void day(int day) @safe pure { _date.day = day; } unittest { - version(testStdDateTime) + static void testDT(DateTime dt, int day) { - static void testDT(DateTime dt, int day) - { - dt.day = day; - } - - //Test A.D. - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 0)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 29)); - assertThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 30)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 32)); - - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 28)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 29)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 31)); - - { - auto dt = DateTime(Date(1, 1, 1), TimeOfDay(7, 12, 22)); - dt.day = 6; - _assertPred!"=="(dt, DateTime(Date(1, 1, 6), TimeOfDay(7, 12, 22))); - } + dt.day = day; + } - //Test B.C. - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 0)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 29)); - assertThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 30)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 32)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 31)); - assertThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 32)); - - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 28)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 29)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 31)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 30)); - assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 31)); - - auto dt = DateTime(Date(-1, 1, 1), TimeOfDay(7, 12, 22)); + //Test A.D. + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 0)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 29)); + assertThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 30)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 32)); + + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 1, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 2, 1)), 28)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(4, 2, 1)), 29)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 3, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 4, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 5, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 6, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 7, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 8, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 9, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 10, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 11, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(1, 12, 1)), 31)); + + { + auto dt = DateTime(Date(1, 1, 1), TimeOfDay(7, 12, 22)); dt.day = 6; - _assertPred!"=="(dt, DateTime(Date(-1, 1, 6), TimeOfDay(7, 12, 22))); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.day = 27)); - static assert(!__traits(compiles, idt.day = 27)); + assert(dt == DateTime(Date(1, 1, 6), TimeOfDay(7, 12, 22))); } + + //Test B.C. + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 0)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 29)); + assertThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 30)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 32)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 31)); + assertThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 32)); + + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 1, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 2, 1)), 28)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(0, 2, 1)), 29)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 3, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 4, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 5, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 6, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 7, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 8, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 9, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 10, 1)), 31)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 11, 1)), 30)); + assertNotThrown!DateTimeException(testDT(DateTime(Date(-1, 12, 1)), 31)); + + auto dt = DateTime(Date(-1, 1, 1), TimeOfDay(7, 12, 22)); + dt.day = 6; + assert(dt == DateTime(Date(-1, 1, 6), TimeOfDay(7, 12, 22))); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.day = 27)); + static assert(!__traits(compiles, idt.day = 27)); } /++ Hours passed midnight. +/ - @property ubyte hour() const pure nothrow + @property ubyte hour() @safe const pure nothrow { return _tod.hour; } unittest { - version(testStdDateTime) - { - _assertPred!"=="(DateTime.init.hour, 0); - _assertPred!"=="(DateTime(Date.init, TimeOfDay(12, 0, 0)).hour, 12); + assert(DateTime.init.hour == 0); + assert(DateTime(Date.init, TimeOfDay(12, 0, 0)).hour == 12); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.hour)); - static assert(__traits(compiles, idt.hour)); - } + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.hour)); + static assert(__traits(compiles, idt.hour)); } @@ -15768,49 +14708,43 @@ assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); $(LREF DateTimeException) if the given hour would result in an invalid $(LREF DateTime). +/ - @property void hour(int hour) pure + @property void hour(int hour) @safe pure { _tod.hour = hour; } unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException((){DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).hour = 24;}()); + assertThrown!DateTimeException((){DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)).hour = 24;}()); - auto dt = DateTime.init; - dt.hour = 12; - _assertPred!"=="(dt, DateTime(1, 1, 1, 12, 0, 0)); + auto dt = DateTime.init; + dt.hour = 12; + assert(dt == DateTime(1, 1, 1, 12, 0, 0)); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.hour = 27)); - static assert(!__traits(compiles, idt.hour = 27)); - } + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.hour = 27)); + static assert(!__traits(compiles, idt.hour = 27)); } /++ Minutes passed the hour. +/ - @property ubyte minute() const pure nothrow + @property ubyte minute() @safe const pure nothrow { return _tod.minute; } unittest { - version(testStdDateTime) - { - _assertPred!"=="(DateTime.init.minute, 0); - _assertPred!"=="(DateTime(1, 1, 1, 0, 30, 0).minute, 30); + assert(DateTime.init.minute == 0); + assert(DateTime(1, 1, 1, 0, 30, 0).minute == 30); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.minute)); - static assert(__traits(compiles, idt.minute)); - } + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.minute)); + static assert(__traits(compiles, idt.minute)); } @@ -15824,49 +14758,43 @@ assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); $(LREF DateTimeException) if the given minute would result in an invalid $(LREF DateTime). +/ - @property void minute(int minute) pure + @property void minute(int minute) @safe pure { _tod.minute = minute; } unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException((){DateTime.init.minute = 60;}()); + assertThrown!DateTimeException((){DateTime.init.minute = 60;}()); - auto dt = DateTime.init; - dt.minute = 30; - _assertPred!"=="(dt, DateTime(1, 1, 1, 0, 30, 0)); + auto dt = DateTime.init; + dt.minute = 30; + assert(dt == DateTime(1, 1, 1, 0, 30, 0)); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.minute = 27)); - static assert(!__traits(compiles, idt.minute = 27)); - } + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.minute = 27)); + static assert(!__traits(compiles, idt.minute = 27)); } /++ Seconds passed the minute. +/ - @property ubyte second() const pure nothrow + @property ubyte second() @safe const pure nothrow { return _tod.second; } unittest { - version(testStdDateTime) - { - _assertPred!"=="(DateTime.init.second, 0); - _assertPred!"=="(DateTime(1, 1, 1, 0, 0, 33).second, 33); + assert(DateTime.init.second == 0); + assert(DateTime(1, 1, 1, 0, 0, 33).second == 33); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.second)); - static assert(__traits(compiles, idt.second)); - } + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.second)); + static assert(__traits(compiles, idt.second)); } @@ -15880,26 +14808,23 @@ assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); $(LREF DateTimeException) if the given seconds would result in an invalid $(LREF DateTime). +/ - @property void second(int second) pure + @property void second(int second) @safe pure { _tod.second = second; } unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException((){DateTime.init.second = 60;}()); + assertThrown!DateTimeException((){DateTime.init.second = 60;}()); - auto dt = DateTime.init; - dt.second = 33; - _assertPred!"=="(dt, DateTime(1, 1, 1, 0, 0, 33)); + auto dt = DateTime.init; + dt.second = 33; + assert(dt == DateTime(1, 1, 1, 0, 0, 33)); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.second = 27)); - static assert(!__traits(compiles, idt.second = 27)); - } + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.second = 27)); + static assert(!__traits(compiles, idt.second = 27)); } @@ -15921,67 +14846,48 @@ assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5); $(LREF DateTime). allowOverflow = Whether the days should be allowed to overflow, causing the month to increment. - - Examples: --------------------- -auto dt1 = DateTime(2010, 1, 1, 12, 30, 33); -dt1.add!"months"(11); -assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33)); - -auto dt2 = DateTime(2010, 1, 1, 12, 30, 33); -dt2.add!"months"(-11); -assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33)); - -auto dt3 = DateTime(2000, 2, 29, 12, 30, 33); -dt3.add!"years"(1); -assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33)); - -auto dt4 = DateTime(2000, 2, 29, 12, 30, 33); -dt4.add!"years"(1, AllowDayOverflow.no); -assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33)); --------------------- +/ - /+ref DateTime+/ void add(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) pure nothrow + ref DateTime add(string units) + (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow if(units == "years" || units == "months") { _date.add!units(value, allowOverflow); + return this; } - //Verify Examples. + /// unittest { - version(testStdDateTime) - { - auto dt1 = DateTime(2010, 1, 1, 12, 30, 33); - dt1.add!"months"(11); - assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33)); + auto dt1 = DateTime(2010, 1, 1, 12, 30, 33); + dt1.add!"months"(11); + assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33)); - auto dt2 = DateTime(2010, 1, 1, 12, 30, 33); - dt2.add!"months"(-11); - assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33)); + auto dt2 = DateTime(2010, 1, 1, 12, 30, 33); + dt2.add!"months"(-11); + assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33)); - auto dt3 = DateTime(2000, 2, 29, 12, 30, 33); - dt3.add!"years"(1); - assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33)); + auto dt3 = DateTime(2000, 2, 29, 12, 30, 33); + dt3.add!"years"(1); + assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33)); - auto dt4 = DateTime(2000, 2, 29, 12, 30, 33); - dt4.add!"years"(1, AllowDayOverflow.no); - assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33)); - } + auto dt4 = DateTime(2000, 2, 29, 12, 30, 33); + dt4.add!"years"(1, AllowDayOverflow.no); + assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33)); } unittest { - version(testStdDateTime) - { - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.add!"years"(4))); - static assert(!__traits(compiles, idt.add!"years"(4))); - static assert(!__traits(compiles, cdt.add!"months"(4))); - static assert(!__traits(compiles, idt.add!"months"(4))); - } + auto dt = DateTime(2000, 1, 31); + dt.add!"years"(7).add!"months"(-4); + assert(dt == DateTime(2006, 10, 1)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.add!"years"(4))); + static assert(!__traits(compiles, idt.add!"years"(4))); + static assert(!__traits(compiles, cdt.add!"months"(4))); + static assert(!__traits(compiles, idt.add!"months"(4))); } @@ -16003,85 +14909,56 @@ assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33)); $(LREF DateTime). allowOverflow = Whether the days should be allowed to overflow, causing the month to increment. - - Examples: --------------------- -auto dt1 = DateTime(2010, 1, 1, 12, 33, 33); -dt1.roll!"months"(1); -assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33)); - -auto dt2 = DateTime(2010, 1, 1, 12, 33, 33); -dt2.roll!"months"(-1); -assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33)); - -auto dt3 = DateTime(1999, 1, 29, 12, 33, 33); -dt3.roll!"months"(1); -assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33)); - -auto dt4 = DateTime(1999, 1, 29, 12, 33, 33); -dt4.roll!"months"(1, AllowDayOverflow.no); -assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33)); - -auto dt5 = DateTime(2000, 2, 29, 12, 30, 33); -dt5.roll!"years"(1); -assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33)); - -auto dt6 = DateTime(2000, 2, 29, 12, 30, 33); -dt6.roll!"years"(1, AllowDayOverflow.no); -assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33)); --------------------- +/ - /+ref DateTime+/ void roll(string units)(long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) pure nothrow + ref DateTime roll(string units) + (long value, AllowDayOverflow allowOverflow = AllowDayOverflow.yes) @safe pure nothrow if(units == "years" || units == "months") { _date.roll!units(value, allowOverflow); + return this; } - //Verify Examples. + /// unittest { - version(testdStdDateTime) - { - auto dt1 = DateTime(2010, 1, 1, 12, 33, 33); - dt1.roll!"months"(1); - assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33)); + auto dt1 = DateTime(2010, 1, 1, 12, 33, 33); + dt1.roll!"months"(1); + assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33)); - auto dt2 = DateTime(2010, 1, 1, 12, 33, 33); - dt2.roll!"months"(-1); - assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33)); + auto dt2 = DateTime(2010, 1, 1, 12, 33, 33); + dt2.roll!"months"(-1); + assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33)); - auto dt3 = DateTime(1999, 1, 29, 12, 33, 33); - dt3.roll!"months"(1); - assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33)); + auto dt3 = DateTime(1999, 1, 29, 12, 33, 33); + dt3.roll!"months"(1); + assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33)); - auto dt4 = DateTime(1999, 1, 29, 12, 33, 33); - dt4.roll!"months"(1, AllowDayOverflow.no); - assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33)); + auto dt4 = DateTime(1999, 1, 29, 12, 33, 33); + dt4.roll!"months"(1, AllowDayOverflow.no); + assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33)); - auto dt5 = DateTime(2000, 2, 29, 12, 30, 33); - dt5.roll!"years"(1); - assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33)); + auto dt5 = DateTime(2000, 2, 29, 12, 30, 33); + dt5.roll!"years"(1); + assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33)); - auto dt6 = DateTime(2000, 2, 29, 12, 30, 33); - dt6.roll!"years"(1, AllowDayOverflow.no); - assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33)); - } + auto dt6 = DateTime(2000, 2, 29, 12, 30, 33); + dt6.roll!"years"(1, AllowDayOverflow.no); + assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33)); } unittest { - version(testStdDateTime) - { - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"years"(4))); - static assert(!__traits(compiles, idt.roll!"years"(4))); - static assert(!__traits(compiles, cdt.roll!"months"(4))); - static assert(!__traits(compiles, idt.roll!"months"(4))); - static assert(!__traits(compiles, cdt.roll!"days"(4))); - static assert(!__traits(compiles, idt.roll!"days"(4))); - } + auto dt = DateTime(2000, 1, 31); + dt.roll!"years"(7).roll!"months"(-4); + assert(dt == DateTime(2007, 10, 1)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"years"(4))); + static assert(!__traits(compiles, idt.roll!"years"(4))); + static assert(!__traits(compiles, cdt.roll!"months"(4))); + static assert(!__traits(compiles, idt.roll!"months"(4))); } @@ -16099,581 +14976,548 @@ assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33)); Params: units = The units to add. value = The number of $(D_PARAM units) to add to this $(LREF DateTime). - - Examples: --------------------- -auto dt1 = DateTime(2010, 1, 1, 11, 23, 12); -dt1.roll!"days"(1); -assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12)); -dt1.roll!"days"(365); -assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12)); -dt1.roll!"days"(-32); -assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12)); - -auto dt2 = DateTime(2010, 7, 4, 12, 0, 0); -dt2.roll!"hours"(1); -assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0)); - -auto dt3 = DateTime(2010, 1, 1, 0, 0, 0); -dt3.roll!"seconds"(-1); -assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); --------------------- +/ - /+ref DateTime+/ void roll(string units)(long value) pure nothrow + ref DateTime roll(string units)(long value) @safe pure nothrow if(units == "days") { _date.roll!"days"(value); + return this; } - //Verify Examples. + /// unittest { - version(testStdDateTime) - { - auto dt1 = DateTime(2010, 1, 1, 11, 23, 12); - dt1.roll!"days"(1); - assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12)); - dt1.roll!"days"(365); - assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12)); - dt1.roll!"days"(-32); - assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12)); + auto dt1 = DateTime(2010, 1, 1, 11, 23, 12); + dt1.roll!"days"(1); + assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12)); + dt1.roll!"days"(365); + assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12)); + dt1.roll!"days"(-32); + assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12)); - auto dt2 = DateTime(2010, 7, 4, 12, 0, 0); - dt2.roll!"hours"(1); - assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0)); + auto dt2 = DateTime(2010, 7, 4, 12, 0, 0); + dt2.roll!"hours"(1); + assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0)); - auto dt3 = DateTime(2010, 1, 1, 0, 0, 0); - dt3.roll!"seconds"(-1); - assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); - } + auto dt3 = DateTime(2010, 1, 1, 0, 0, 0); + dt3.roll!"seconds"(-1); + assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); } unittest { - version(testStdDateTime) - { - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"days"(4))); - static assert(!__traits(compiles, idt.roll!"days"(4))); - } + auto dt = DateTime(2000, 1, 31); + dt.roll!"days"(7).roll!"days"(-4); + assert(dt == DateTime(2000, 1, 3)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"days"(4))); + static assert(!__traits(compiles, idt.roll!"days"(4))); } //Shares documentation with "days" version. - /+ref DateTime+/ void roll(string units)(long value) pure nothrow + ref DateTime roll(string units)(long value) @safe pure nothrow if(units == "hours" || units == "minutes" || units == "seconds") { _tod.roll!units(value); + return this; } //Test roll!"hours"(). unittest { - version(testStdDateTime) + static void testDT(DateTime orig, int hours, in DateTime expected, size_t line = __LINE__) { - static void testDT(DateTime orig, int hours, in DateTime expected, size_t line = __LINE__) - { - orig.roll!"hours"(hours); - _assertPred!"=="(orig, expected, "", __FILE__, line); - } - - //Test A.D. - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 6, DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7, DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 8, DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 9, DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 11, DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 12, DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 13, DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 14, DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 16, DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 17, DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 18, DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 19, DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 20, DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 21, DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 22, DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 23, DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 24, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 25, DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -6, DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -7, DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -8, DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -9, DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -11, DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -12, DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -13, DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -14, DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -16, DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -17, DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -18, DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -19, DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -20, DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -21, DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -22, DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -23, DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -24, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -25, DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); - - testDT(DateTime(Date(1999, 7, 31), TimeOfDay(23, 30, 33)), 1, DateTime(Date(1999, 7, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 8, 1), TimeOfDay(0, 30, 33)), -1, DateTime(Date(1999, 8, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(1999, 12, 31), TimeOfDay(23, 30, 33)), 1, DateTime(Date(1999, 12, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(2000, 1, 1), TimeOfDay(0, 30, 33)), -1, DateTime(Date(2000, 1, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(1999, 2, 28), TimeOfDay(23, 30, 33)), 25, DateTime(Date(1999, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(1999, 3, 2), TimeOfDay(0, 30, 33)), -25, DateTime(Date(1999, 3, 2), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(2000, 2, 28), TimeOfDay(23, 30, 33)), 25, DateTime(Date(2000, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(2000, 3, 1), TimeOfDay(0, 30, 33)), -25, DateTime(Date(2000, 3, 1), TimeOfDay(23, 30, 33))); - - //Test B.C. - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 6, DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7, DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 8, DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 9, DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 11, DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 12, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 13, DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 14, DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 16, DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 17, DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 18, DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 19, DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 20, DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 21, DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 22, DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 23, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 24, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 25, DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -6, DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -7, DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -8, DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -9, DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -11, DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -12, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -13, DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -14, DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -16, DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -17, DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -18, DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -19, DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -20, DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -21, DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -22, DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -23, DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -24, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -25, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 31), TimeOfDay(23, 30, 33)), 1, DateTime(Date(-1999, 7, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-1999, 8, 1), TimeOfDay(0, 30, 33)), -1, DateTime(Date(-1999, 8, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-2001, 12, 31), TimeOfDay(23, 30, 33)), 1, DateTime(Date(-2001, 12, 31), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-2000, 1, 1), TimeOfDay(0, 30, 33)), -1, DateTime(Date(-2000, 1, 1), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-2001, 2, 28), TimeOfDay(23, 30, 33)), 25, DateTime(Date(-2001, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-2001, 3, 2), TimeOfDay(0, 30, 33)), -25, DateTime(Date(-2001, 3, 2), TimeOfDay(23, 30, 33))); - - testDT(DateTime(Date(-2000, 2, 28), TimeOfDay(23, 30, 33)), 25, DateTime(Date(-2000, 2, 28), TimeOfDay(0, 30, 33))); - testDT(DateTime(Date(-2000, 3, 1), TimeOfDay(0, 30, 33)), -25, DateTime(Date(-2000, 3, 1), TimeOfDay(23, 30, 33))); - - //Test Both - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 17_546, DateTime(Date(-1, 1, 1), TimeOfDay(13, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -17_546, DateTime(Date(1, 1, 1), TimeOfDay(11, 30, 33))); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"hours"(4))); - static assert(!__traits(compiles, idt.roll!"hours"(4))); - - //Verify Examples. - auto dt1 = DateTime(Date(2010, 7, 4), TimeOfDay(12, 0, 0)); - dt1.roll!"hours"(1); - assert(dt1 == DateTime(Date(2010, 7, 4), TimeOfDay(13, 0, 0))); - - auto dt2 = DateTime(Date(2010, 2, 12), TimeOfDay(12, 0, 0)); - dt2.roll!"hours"(-1); - assert(dt2 == DateTime(Date(2010, 2, 12), TimeOfDay(11, 0, 0))); - - auto dt3 = DateTime(Date(2009, 12, 31), TimeOfDay(23, 0, 0)); - dt3.roll!"hours"(1); - assert(dt3 == DateTime(Date(2009, 12, 31), TimeOfDay(0, 0, 0))); - - auto dt4 = DateTime(Date(2010, 1, 1), TimeOfDay(0, 0, 0)); - dt4.roll!"hours"(-1); - assert(dt4 == DateTime(Date(2010, 1, 1), TimeOfDay(23, 0, 0))); + orig.roll!"hours"(hours); + assert(orig == expected); } + + //Test A.D. + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 6, DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7, DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 8, DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 9, DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 11, DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 12, DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 13, DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 14, DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 16, DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 17, DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 18, DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 19, DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 20, DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 21, DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 22, DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 23, DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 24, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 25, DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -6, DateTime(Date(1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -7, DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -8, DateTime(Date(1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -9, DateTime(Date(1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -11, DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -12, DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -13, DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -14, DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -16, DateTime(Date(1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -17, DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -18, DateTime(Date(1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -19, DateTime(Date(1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -20, DateTime(Date(1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -21, DateTime(Date(1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -22, DateTime(Date(1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -23, DateTime(Date(1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -24, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -25, DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(23, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(22, 30, 33))); + + testDT(DateTime(Date(1999, 7, 31), TimeOfDay(23, 30, 33)), 1, DateTime(Date(1999, 7, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 8, 1), TimeOfDay(0, 30, 33)), -1, DateTime(Date(1999, 8, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(1999, 12, 31), TimeOfDay(23, 30, 33)), 1, DateTime(Date(1999, 12, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(2000, 1, 1), TimeOfDay(0, 30, 33)), -1, DateTime(Date(2000, 1, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(1999, 2, 28), TimeOfDay(23, 30, 33)), 25, DateTime(Date(1999, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(1999, 3, 2), TimeOfDay(0, 30, 33)), -25, DateTime(Date(1999, 3, 2), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(2000, 2, 28), TimeOfDay(23, 30, 33)), 25, DateTime(Date(2000, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(2000, 3, 1), TimeOfDay(0, 30, 33)), -25, DateTime(Date(2000, 3, 1), TimeOfDay(23, 30, 33))); + + //Test B.C. + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 6, DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7, DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 8, DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 9, DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 11, DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 12, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 13, DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 14, DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 16, DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 17, DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 18, DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 19, DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 20, DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 21, DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 22, DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 23, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 24, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 25, DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(-1999, 7, 6), TimeOfDay(10, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(-1999, 7, 6), TimeOfDay(9, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(-1999, 7, 6), TimeOfDay(8, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(-1999, 7, 6), TimeOfDay(7, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -6, DateTime(Date(-1999, 7, 6), TimeOfDay(6, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -7, DateTime(Date(-1999, 7, 6), TimeOfDay(5, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -8, DateTime(Date(-1999, 7, 6), TimeOfDay(4, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -9, DateTime(Date(-1999, 7, 6), TimeOfDay(3, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(-1999, 7, 6), TimeOfDay(2, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -11, DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -12, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -13, DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -14, DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(-1999, 7, 6), TimeOfDay(21, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -16, DateTime(Date(-1999, 7, 6), TimeOfDay(20, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -17, DateTime(Date(-1999, 7, 6), TimeOfDay(19, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -18, DateTime(Date(-1999, 7, 6), TimeOfDay(18, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -19, DateTime(Date(-1999, 7, 6), TimeOfDay(17, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -20, DateTime(Date(-1999, 7, 6), TimeOfDay(16, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -21, DateTime(Date(-1999, 7, 6), TimeOfDay(15, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -22, DateTime(Date(-1999, 7, 6), TimeOfDay(14, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -23, DateTime(Date(-1999, 7, 6), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -24, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -25, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(1, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(23, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(22, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 31), TimeOfDay(23, 30, 33)), 1, DateTime(Date(-1999, 7, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-1999, 8, 1), TimeOfDay(0, 30, 33)), -1, DateTime(Date(-1999, 8, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-2001, 12, 31), TimeOfDay(23, 30, 33)), 1, DateTime(Date(-2001, 12, 31), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-2000, 1, 1), TimeOfDay(0, 30, 33)), -1, DateTime(Date(-2000, 1, 1), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-2001, 2, 28), TimeOfDay(23, 30, 33)), 25, DateTime(Date(-2001, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-2001, 3, 2), TimeOfDay(0, 30, 33)), -25, DateTime(Date(-2001, 3, 2), TimeOfDay(23, 30, 33))); + + testDT(DateTime(Date(-2000, 2, 28), TimeOfDay(23, 30, 33)), 25, DateTime(Date(-2000, 2, 28), TimeOfDay(0, 30, 33))); + testDT(DateTime(Date(-2000, 3, 1), TimeOfDay(0, 30, 33)), -25, DateTime(Date(-2000, 3, 1), TimeOfDay(23, 30, 33))); + + //Test Both + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 17_546, DateTime(Date(-1, 1, 1), TimeOfDay(13, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -17_546, DateTime(Date(1, 1, 1), TimeOfDay(11, 30, 33))); + + auto dt = DateTime(2000, 1, 31, 9, 7, 6); + dt.roll!"hours"(27).roll!"hours"(-9); + assert(dt == DateTime(2000, 1, 31, 3, 7, 6)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"hours"(4))); + static assert(!__traits(compiles, idt.roll!"hours"(4))); } //Test roll!"minutes"(). unittest { - version(testStdDateTime) + static void testDT(DateTime orig, int minutes, in DateTime expected, size_t line = __LINE__) { - static void testDT(DateTime orig, int minutes, in DateTime expected, size_t line = __LINE__) - { - orig.roll!"minutes"(minutes); - _assertPred!"=="(orig, expected, "", __FILE__, line); - } - - //Test A.D. - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(1999, 7, 6), TimeOfDay(12, 32, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(1999, 7, 6), TimeOfDay(12, 34, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(1999, 7, 6), TimeOfDay(12, 35, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(1999, 7, 6), TimeOfDay(12, 40, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 29, DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 45, DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 75, DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 90, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 100, DateTime(Date(1999, 7, 6), TimeOfDay(12, 10, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 689, DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 690, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 691, DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 960, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(1999, 7, 6), TimeOfDay(12, 28, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(1999, 7, 6), TimeOfDay(12, 27, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(1999, 7, 6), TimeOfDay(12, 26, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(1999, 7, 6), TimeOfDay(12, 25, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(1999, 7, 6), TimeOfDay(12, 20, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -29, DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -30, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -45, DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -75, DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -90, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -100, DateTime(Date(1999, 7, 6), TimeOfDay(12, 50, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -749, DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -750, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -751, DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -960, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(11, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(11, 58, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 1, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 59, 33))); - - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 1, DateTime(Date(1999, 7, 5), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 0, DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), -1, DateTime(Date(1999, 7, 5), TimeOfDay(23, 58, 33))); - - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 1, DateTime(Date(1998, 12, 31), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 0, DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), -1, DateTime(Date(1998, 12, 31), TimeOfDay(23, 58, 33))); - - //Test B.C. - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 32, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 33, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 34, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 35, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 40, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 29, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 45, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 75, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 90, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 100, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 10, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 689, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 690, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 691, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 960, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 28, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 27, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 26, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 25, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 20, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -29, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -30, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -45, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -75, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -90, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -100, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 50, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -749, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -750, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -751, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -960, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 58, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 1, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 59, 33))); - - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 1, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 0, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), -1, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 58, 33))); - - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 1, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 0, 33))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 0, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), -1, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 58, 33))); - - //Test Both - testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(1, 1, 1), TimeOfDay(0, 59, 0))); - testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)), 1, DateTime(Date(0, 12, 31), TimeOfDay(23, 0, 0))); - - testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(0, 1, 1), TimeOfDay(0, 59, 0))); - testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)), 1, DateTime(Date(-1, 12, 31), TimeOfDay(23, 0, 0))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_760, DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -1_052_760, DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_782, DateTime(Date(-1, 1, 1), TimeOfDay(11, 52, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 52, 33)), -1_052_782, DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"minutes"(4))); - static assert(!__traits(compiles, idt.roll!"minutes"(4))); + orig.roll!"minutes"(minutes); + assert(orig == expected); } + + //Test A.D. + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(1999, 7, 6), TimeOfDay(12, 32, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(1999, 7, 6), TimeOfDay(12, 33, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(1999, 7, 6), TimeOfDay(12, 34, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(1999, 7, 6), TimeOfDay(12, 35, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(1999, 7, 6), TimeOfDay(12, 40, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 29, DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 45, DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 75, DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 90, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 100, DateTime(Date(1999, 7, 6), TimeOfDay(12, 10, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 689, DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 690, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 691, DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 960, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(1999, 7, 6), TimeOfDay(12, 28, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(1999, 7, 6), TimeOfDay(12, 27, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(1999, 7, 6), TimeOfDay(12, 26, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(1999, 7, 6), TimeOfDay(12, 25, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(1999, 7, 6), TimeOfDay(12, 20, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -29, DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -30, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -45, DateTime(Date(1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -75, DateTime(Date(1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -90, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -100, DateTime(Date(1999, 7, 6), TimeOfDay(12, 50, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -749, DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -750, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -751, DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -960, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, DateTime(Date(1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 59, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(11, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(11, 59, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(11, 58, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 1, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 59, 33))); + + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 1, DateTime(Date(1999, 7, 5), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), 0, DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 33)), -1, DateTime(Date(1999, 7, 5), TimeOfDay(23, 58, 33))); + + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 1, DateTime(Date(1998, 12, 31), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), 0, DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 33)), -1, DateTime(Date(1998, 12, 31), TimeOfDay(23, 58, 33))); + + //Test B.C. + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 32, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 33, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 34, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 35, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 40, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 29, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 45, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 75, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 90, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 100, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 10, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 689, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 690, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 691, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 960, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1439, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1440, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1441, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2880, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 28, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 27, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 26, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 25, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 20, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -29, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -30, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -45, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 45, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -75, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 15, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -90, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -100, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 50, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -749, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -750, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -751, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -960, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1439, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 31, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1440, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1441, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 29, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2880, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 59, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(11, 59, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(11, 58, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 1, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 59, 33))); + + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 1, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), 0, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 33)), -1, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 58, 33))); + + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 1, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 0, 33))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), 0, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 33)), -1, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 58, 33))); + + //Test Both + testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(1, 1, 1), TimeOfDay(0, 59, 0))); + testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0)), 1, DateTime(Date(0, 12, 31), TimeOfDay(23, 0, 0))); + + testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(0, 1, 1), TimeOfDay(0, 59, 0))); + testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0)), 1, DateTime(Date(-1, 12, 31), TimeOfDay(23, 0, 0))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_760, DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -1_052_760, DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 1_052_782, DateTime(Date(-1, 1, 1), TimeOfDay(11, 52, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 52, 33)), -1_052_782, DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + auto dt = DateTime(2000, 1, 31, 9, 7, 6); + dt.roll!"minutes"(92).roll!"minutes"(-292); + assert(dt == DateTime(2000, 1, 31, 9, 47, 6)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"minutes"(4))); + static assert(!__traits(compiles, idt.roll!"minutes"(4))); } //Test roll!"seconds"(). unittest { - version(testStdDateTime) + static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) { - static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) - { - orig.roll!"seconds"(seconds); - _assertPred!"=="(orig, expected, "", __FILE__, line); - } - - //Test A.D. - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 35))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 36))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 37))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 38))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 43))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 48))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 26, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 27, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 3))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 59, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 61, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 31))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 30))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 29))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 28))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 23))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 18))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -33, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -34, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -35, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 58))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -59, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -61, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 59))); - - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 1))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))); - testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 59))); - - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 1, DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 0, DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), -1, DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 58))); - - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 0, DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), -1, DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 58))); - - //Test B.C. - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 35))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 36))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 37))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 38))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 43))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 48))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 26, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 27, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 3))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 59, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 61, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 31))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 30))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 29))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 28))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 23))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 18))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -33, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -34, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -35, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 58))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -59, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -61, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 59))); - - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 1))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0))); - testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 59))); - - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 1, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 0, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), -1, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 58))); - - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 0))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 0, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59))); - testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), -1, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 58))); - - //Test Both - testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 59))); - testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0))); - - testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 59))); - testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_600L, DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -63_165_600L, DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_617L, DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 50))); - testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 50)), -63_165_617L, DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.roll!"seconds"(4))); - static assert(!__traits(compiles, idt.roll!"seconds"(4))); + orig.roll!"seconds"(seconds); + assert(orig == expected); } + + //Test A.D. + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 35))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 36))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 37))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 38))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 43))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 48))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 26, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 27, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 30, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 3))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 59, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 60, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 61, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 31))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 30))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 29))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 28))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 23))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 18))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -33, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -34, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -35, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 58))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -59, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -60, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), -61, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 32))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 0)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 59))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 0)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(12, 0, 59))); + + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 1))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), 0, DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0))); + testDT(DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 0)), -1, DateTime(Date(1999, 7, 6), TimeOfDay(0, 0, 59))); + + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 1, DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), 0, DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 59)), -1, DateTime(Date(1999, 7, 5), TimeOfDay(23, 59, 58))); + + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), 0, DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 59)), -1, DateTime(Date(1998, 12, 31), TimeOfDay(23, 59, 58))); + + //Test B.C. + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 35))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 36))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 4, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 37))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 5, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 38))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 10, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 43))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 15, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 48))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 26, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 27, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 30, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 3))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 59, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 60, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 61, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1766, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1767, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 1768, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 2007, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3599, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3600, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 3601, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), 7200, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -2, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 31))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -3, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 30))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -4, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 29))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -5, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 28))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -10, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 23))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -15, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 18))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -33, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -34, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -35, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 58))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -59, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 34))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -60, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33)), -61, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 32))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 0)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 59))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 0)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(12, 0, 59))); + + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 1))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), 0, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0))); + testDT(DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 0)), -1, DateTime(Date(-1999, 7, 6), TimeOfDay(0, 0, 59))); + + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 1, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), 0, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 59)), -1, DateTime(Date(-1999, 7, 5), TimeOfDay(23, 59, 58))); + + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 0))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), 0, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59))); + testDT(DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 59)), -1, DateTime(Date(-2000, 12, 31), TimeOfDay(23, 59, 58))); + + //Test Both + testDT(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 59))); + testDT(DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 0))); + + testDT(DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 0)), -1, DateTime(Date(0, 1, 1), TimeOfDay(0, 0, 59))); + testDT(DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 59)), 1, DateTime(Date(-1, 12, 31), TimeOfDay(23, 59, 0))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_600L, DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33)), -63_165_600L, DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + testDT(DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 33)), 63_165_617L, DateTime(Date(-1, 1, 1), TimeOfDay(11, 30, 50))); + testDT(DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 50)), -63_165_617L, DateTime(Date(1, 1, 1), TimeOfDay(13, 30, 33))); + + auto dt = DateTime(2000, 1, 31, 9, 7, 6); + dt.roll!"seconds"(92).roll!"seconds"(-292); + assert(dt == DateTime(2000, 1, 31, 9, 7, 46)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt.roll!"seconds"(4))); + static assert(!__traits(compiles, idt.roll!"seconds"(4))); } @@ -16692,7 +15536,7 @@ assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); duration = The duration to add to or subtract from this $(LREF DateTime). +/ - DateTime opBinary(string op, D)(in D duration) const pure nothrow + DateTime opBinary(string op, D)(in D duration) @safe const pure nothrow if((op == "+" || op == "-") && (is(Unqual!D == Duration) || is(Unqual!D == TickDuration))) @@ -16704,86 +15548,72 @@ assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); else static if(is(Unqual!D == TickDuration)) immutable hnsecs = duration.hnsecs; - //Ideally, this would just be - //return retval.addSeconds(convert!("hnsecs", "seconds")(unaryFun!(op ~ "a")(hnsecs))); - //But there isn't currently a pure version of unaryFun!(). - - static if(op == "+") - immutable signedHNSecs = hnsecs; - else static if(op == "-") - immutable signedHNSecs = -hnsecs; - else - static assert(0); - - return retval.addSeconds(convert!("hnsecs", "seconds")(signedHNSecs)); + mixin(format(`return retval._addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op)); } unittest { - version(testStdDateTime) - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - _assertPred!"=="(dt + dur!"weeks"(7), DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - _assertPred!"=="(dt + dur!"weeks"(-7), DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - _assertPred!"=="(dt + dur!"days"(7), DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - _assertPred!"=="(dt + dur!"days"(-7), DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + assert(dt + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assert(dt + dur!"weeks"(-7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assert(dt + dur!"days"(7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assert(dt + dur!"days"(-7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - _assertPred!"=="(dt + dur!"hours"(7), DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - _assertPred!"=="(dt + dur!"hours"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - _assertPred!"=="(dt + dur!"minutes"(7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - _assertPred!"=="(dt + dur!"minutes"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - _assertPred!"=="(dt + dur!"seconds"(7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"=="(dt + dur!"seconds"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - _assertPred!"=="(dt + dur!"msecs"(7_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"=="(dt + dur!"msecs"(-7_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - _assertPred!"=="(dt + dur!"usecs"(7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"=="(dt + dur!"usecs"(-7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - _assertPred!"=="(dt + dur!"hnsecs"(70_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"=="(dt + dur!"hnsecs"(-70_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt + dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assert(dt + dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assert(dt + dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assert(dt + dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assert(dt + dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt + dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt + dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if(TickDuration.ticksPerSec == 1_000_000) - { - _assertPred!"=="(dt + TickDuration.from!"usecs"(7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"=="(dt + TickDuration.from!"usecs"(-7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - } + //This probably only runs in cases where gettimeofday() is used, but it's + //hard to do this test correctly with variable ticksPerSec. + if(TickDuration.ticksPerSec == 1_000_000) + { + assert(dt + TickDuration.from!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt + TickDuration.from!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + } - _assertPred!"=="(dt - dur!"weeks"(-7), DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - _assertPred!"=="(dt - dur!"weeks"(7), DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - _assertPred!"=="(dt - dur!"days"(-7), DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - _assertPred!"=="(dt - dur!"days"(7), DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - _assertPred!"=="(dt - dur!"hours"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - _assertPred!"=="(dt - dur!"hours"(7), DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - _assertPred!"=="(dt - dur!"minutes"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - _assertPred!"=="(dt - dur!"minutes"(7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - _assertPred!"=="(dt - dur!"seconds"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"=="(dt - dur!"seconds"(7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - _assertPred!"=="(dt - dur!"msecs"(-7_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"=="(dt - dur!"msecs"(7_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - _assertPred!"=="(dt - dur!"usecs"(-7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"=="(dt - dur!"usecs"(7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - _assertPred!"=="(dt - dur!"hnsecs"(-70_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"=="(dt - dur!"hnsecs"(70_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - //This probably only runs in cases where gettimeofday() is used, but it's - //hard to do this test correctly with variable ticksPerSec. - if(TickDuration.ticksPerSec == 1_000_000) - { - _assertPred!"=="(dt - TickDuration.from!"usecs"(-7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"=="(dt - TickDuration.from!"usecs"(7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - } + assert(dt - dur!"weeks"(-7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assert(dt - dur!"weeks"(7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assert(dt - dur!"days"(-7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assert(dt - dur!"days"(7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - auto duration = dur!"seconds"(12); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, cdt + duration)); - static assert(__traits(compiles, idt + duration)); - static assert(__traits(compiles, cdt - duration)); - static assert(__traits(compiles, idt - duration)); + assert(dt - dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assert(dt - dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assert(dt - dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assert(dt - dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assert(dt - dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt - dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt - dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(dt - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + //This probably only runs in cases where gettimeofday() is used, but it's + //hard to do this test correctly with variable ticksPerSec. + if(TickDuration.ticksPerSec == 1_000_000) + { + assert(dt - TickDuration.from!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(dt - TickDuration.from!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); } + + auto duration = dur!"seconds"(12); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, cdt + duration)); + static assert(__traits(compiles, idt + duration)); + static assert(__traits(compiles, cdt - duration)); + static assert(__traits(compiles, idt - duration)); } @@ -16802,7 +15632,7 @@ assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); duration = The duration to add to or subtract from this $(LREF DateTime). +/ - /+ref+/ DateTime opOpAssign(string op, D)(in D duration) pure nothrow + ref DateTime opOpAssign(string op, D)(in D duration) @safe pure nothrow if((op == "+" || op == "-") && (is(Unqual!D == Duration) || is(Unqual!D == TickDuration))) @@ -16814,68 +15644,58 @@ assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); else static if(is(Unqual!D == TickDuration)) immutable hnsecs = duration.hnsecs; - //Ideally, this would just be - //return addSeconds(convert!("hnsecs", "seconds")(unaryFun!(op ~ "a")(hnsecs))); - //But there isn't currently a pure version of unaryFun!(). - - static if(op == "+") - immutable signedHNSecs = hnsecs; - else static if(op == "-") - immutable signedHNSecs = -hnsecs; - else - static assert(0); - - return addSeconds(convert!("hnsecs", "seconds")(signedHNSecs)); + mixin(format(`return _addSeconds(convert!("hnsecs", "seconds")(%shnsecs));`, op)); } unittest { - version(testStdDateTime) - { - _assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"weeks"(7), DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - _assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"weeks"(-7), DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - _assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"days"(7), DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - _assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"days"(-7), DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - _assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"hours"(7), DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - _assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"hours"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - _assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"minutes"(7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - _assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"minutes"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - _assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"seconds"(7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"seconds"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - _assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"msecs"(7_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"msecs"(-7_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - _assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"usecs"(7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"usecs"(-7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - _assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"hnsecs"(70_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"+="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"hnsecs"(-70_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - _assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"weeks"(-7), DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); - _assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"weeks"(7), DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); - _assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"days"(-7), DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); - _assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"days"(7), DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); - - _assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"hours"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); - _assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"hours"(7), DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); - _assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"minutes"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); - _assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"minutes"(7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); - _assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"seconds"(-7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"seconds"(7), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - _assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"msecs"(-7_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"msecs"(7_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - _assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"usecs"(-7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"usecs"(7_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - _assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"hnsecs"(-70_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); - _assertPred!"-="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), dur!"hnsecs"(70_000_000), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); - - auto duration = dur!"seconds"(12); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(!__traits(compiles, cdt += duration)); - static assert(!__traits(compiles, idt += duration)); - static assert(!__traits(compiles, cdt -= duration)); - static assert(!__traits(compiles, idt -= duration)); - } + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"weeks"(-7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"days"(-7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) + dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(-7) == DateTime(Date(1999, 8, 24), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"weeks"(7) == DateTime(Date(1999, 5, 18), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(-7) == DateTime(Date(1999, 7, 13), TimeOfDay(12, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"days"(7) == DateTime(Date(1999, 6, 29), TimeOfDay(12, 30, 33))); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(19, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hours"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(5, 30, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 37, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"minutes"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 23, 33))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(-7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"seconds"(7) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(-7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"msecs"(7_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(-7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"usecs"(7_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(-70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 40))); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - dur!"hnsecs"(70_000_000) == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 26))); + + auto dt = DateTime(2000, 1, 31, 9, 7, 6); + (dt += dur!"seconds"(92)) -= dur!"days"(-500); + assert(dt == DateTime(2001, 6, 14, 9, 8, 38)); + + auto duration = dur!"seconds"(12); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(!__traits(compiles, cdt += duration)); + static assert(!__traits(compiles, idt += duration)); + static assert(!__traits(compiles, cdt -= duration)); + static assert(!__traits(compiles, idt -= duration)); } @@ -16888,7 +15708,7 @@ assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); $(TR $(TD DateTime) $(TD -) $(TD DateTime) $(TD -->) $(TD duration)) ) +/ - Duration opBinary(string op)(in DateTime rhs) const pure nothrow + Duration opBinary(string op)(in DateTime rhs) @safe const pure nothrow if(op == "-") { immutable dateResult = _date - rhs.date; @@ -16899,71 +15719,68 @@ assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); unittest { - version(testStdDateTime) - { - auto dt = DateTime(1999, 7, 6, 12, 30, 33); - - _assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)), - dur!"seconds"(31_536_000)); - _assertPred!"=="(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), - dur!"seconds"(-31_536_000)); - - _assertPred!"=="(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), - dur!"seconds"(26_78_400)); - _assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)), - dur!"seconds"(-26_78_400)); - - _assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)), - dur!"seconds"(86_400)); - _assertPred!"=="(DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), - dur!"seconds"(-86_400)); - - _assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)), - dur!"seconds"(3600)); - _assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), - dur!"seconds"(-3600)); - - _assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), - dur!"seconds"(60)); - _assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)), - dur!"seconds"(-60)); - - _assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)), - dur!"seconds"(1)); - _assertPred!"=="(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - - DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)), - dur!"seconds"(-1)); - - _assertPred!"=="(DateTime(1, 1, 1, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0), dur!"seconds"(45033)); - _assertPred!"=="(DateTime(1, 1, 1, 0, 0, 0) - DateTime(1, 1, 1, 12, 30, 33), dur!"seconds"(-45033)); - _assertPred!"=="(DateTime(0, 12, 31, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0), dur!"seconds"(-41367)); - _assertPred!"=="(DateTime(1, 1, 1, 0, 0, 0) - DateTime(0, 12, 31, 12, 30, 33), dur!"seconds"(41367)); - - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, dt - dt)); - static assert(__traits(compiles, cdt - dt)); - static assert(__traits(compiles, idt - dt)); - - static assert(__traits(compiles, dt - cdt)); - static assert(__traits(compiles, cdt - cdt)); - static assert(__traits(compiles, idt - cdt)); - - static assert(__traits(compiles, dt - idt)); - static assert(__traits(compiles, cdt - idt)); - static assert(__traits(compiles, idt - idt)); - } + auto dt = DateTime(1999, 7, 6, 12, 30, 33); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - + DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(31_536_000)); + assert(DateTime(Date(1998, 7, 6), TimeOfDay(12, 30, 33)) - + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(-31_536_000)); + + assert(DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) - + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(26_78_400)); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - + DateTime(Date(1999, 8, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(-26_78_400)); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - + DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) == + dur!"seconds"(86_400)); + assert(DateTime(Date(1999, 7, 5), TimeOfDay(12, 30, 33)) - + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(-86_400)); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - + DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) == + dur!"seconds"(3600)); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(11, 30, 33)) - + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(-3600)); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) - + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(60)); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - + DateTime(Date(1999, 7, 6), TimeOfDay(12, 31, 33)) == + dur!"seconds"(-60)); + + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) - + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) == + dur!"seconds"(1)); + assert(DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)) - + DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 34)) == + dur!"seconds"(-1)); + + assert(DateTime(1, 1, 1, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(45033)); + assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(1, 1, 1, 12, 30, 33) == dur!"seconds"(-45033)); + assert(DateTime(0, 12, 31, 12, 30, 33) - DateTime(1, 1, 1, 0, 0, 0) == dur!"seconds"(-41367)); + assert(DateTime(1, 1, 1, 0, 0, 0) - DateTime(0, 12, 31, 12, 30, 33) == dur!"seconds"(41367)); + + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, dt - dt)); + static assert(__traits(compiles, cdt - dt)); + static assert(__traits(compiles, idt - dt)); + + static assert(__traits(compiles, dt - cdt)); + static assert(__traits(compiles, cdt - cdt)); + static assert(__traits(compiles, idt - cdt)); + + static assert(__traits(compiles, dt - idt)); + static assert(__traits(compiles, cdt - idt)); + static assert(__traits(compiles, idt - idt)); } @@ -16987,130 +15804,109 @@ assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59)); Params: rhs = The $(LREF DateTime) to subtract from this one. - - Examples: --------------------- -assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths( - DateTime(1999, 1, 31, 23, 59, 59)) == 1); - -assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths( - DateTime(1999, 2, 1, 12, 3, 42)) == -1); - -assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths( - DateTime(1999, 1, 1, 2, 4, 7)) == 2); - -assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths( - DateTime(1999, 3, 31, 0, 30, 58)) == -2); --------------------- +/ - int diffMonths(in DateTime rhs) const pure nothrow + int diffMonths(in DateTime rhs) @safe const pure nothrow { return _date.diffMonths(rhs._date); } + /// unittest { - version(testStdDateTime) - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, dt.diffMonths(dt))); - static assert(__traits(compiles, cdt.diffMonths(dt))); - static assert(__traits(compiles, idt.diffMonths(dt))); + assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths( + DateTime(1999, 1, 31, 23, 59, 59)) == 1); - static assert(__traits(compiles, dt.diffMonths(cdt))); - static assert(__traits(compiles, cdt.diffMonths(cdt))); - static assert(__traits(compiles, idt.diffMonths(cdt))); + assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths( + DateTime(1999, 2, 1, 12, 3, 42)) == -1); - static assert(__traits(compiles, dt.diffMonths(idt))); - static assert(__traits(compiles, cdt.diffMonths(idt))); - static assert(__traits(compiles, idt.diffMonths(idt))); + assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths( + DateTime(1999, 1, 1, 2, 4, 7)) == 2); - //Verify Examples. - assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths(DateTime(1999, 1, 31, 23, 59, 59)) == 1); - assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths(DateTime(1999, 2, 1, 12, 3, 42)) == -1); - assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths(DateTime(1999, 1, 1, 2, 4, 7)) == 2); - assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths(DateTime(1999, 3, 31, 0, 30, 58)) == -2); - } + assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths( + DateTime(1999, 3, 31, 0, 30, 58)) == -2); + } + + unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, dt.diffMonths(dt))); + static assert(__traits(compiles, cdt.diffMonths(dt))); + static assert(__traits(compiles, idt.diffMonths(dt))); + + static assert(__traits(compiles, dt.diffMonths(cdt))); + static assert(__traits(compiles, cdt.diffMonths(cdt))); + static assert(__traits(compiles, idt.diffMonths(cdt))); + + static assert(__traits(compiles, dt.diffMonths(idt))); + static assert(__traits(compiles, cdt.diffMonths(idt))); + static assert(__traits(compiles, idt.diffMonths(idt))); } /++ Whether this $(LREF DateTime) is in a leap year. +/ - @property bool isLeapYear() const pure nothrow + @property bool isLeapYear() @safe const pure nothrow { return _date.isLeapYear; } unittest { - version(testStdDateTime) - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, dt.isLeapYear)); - static assert(__traits(compiles, cdt.isLeapYear)); - static assert(__traits(compiles, idt.isLeapYear)); - } + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, dt.isLeapYear)); + static assert(__traits(compiles, cdt.isLeapYear)); + static assert(__traits(compiles, idt.isLeapYear)); } /++ Day of the week this $(LREF DateTime) is on. +/ - @property DayOfWeek dayOfWeek() const pure nothrow + @property DayOfWeek dayOfWeek() @safe const pure nothrow { return _date.dayOfWeek; } unittest { - version(testStdDateTime) - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, dt.dayOfWeek)); - static assert(__traits(compiles, cdt.dayOfWeek)); - static assert(__traits(compiles, idt.dayOfWeek)); - } + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, dt.dayOfWeek)); + static assert(__traits(compiles, cdt.dayOfWeek)); + static assert(__traits(compiles, idt.dayOfWeek)); } /++ Day of the year this $(LREF DateTime) is on. - - Examples: --------------------- -assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1); -assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365); -assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366); --------------------- +/ - @property ushort dayOfYear() const pure nothrow + @property ushort dayOfYear() @safe const pure nothrow { return _date.dayOfYear; } + /// unittest { - version(testStdDateTime) - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, dt.dayOfYear)); - static assert(__traits(compiles, cdt.dayOfYear)); - static assert(__traits(compiles, idt.dayOfYear)); + assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1); + assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365); + assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366); + } - //Verify Examples. - assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1); - assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365); - assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366); - } + unittest + { + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, dt.dayOfYear)); + static assert(__traits(compiles, cdt.dayOfYear)); + static assert(__traits(compiles, idt.dayOfYear)); } @@ -17121,76 +15917,51 @@ assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366); day = The day of the year to set which day of the year this $(LREF DateTime) is on. +/ - @property void dayOfYear(int day) pure + @property void dayOfYear(int day) @safe pure { _date.dayOfYear = day; } unittest { - version(testStdDateTime) - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, dt.dayOfYear = 12)); - static assert(!__traits(compiles, cdt.dayOfYear = 12)); - static assert(!__traits(compiles, idt.dayOfYear = 12)); - } + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, dt.dayOfYear = 12)); + static assert(!__traits(compiles, cdt.dayOfYear = 12)); + static assert(!__traits(compiles, idt.dayOfYear = 12)); } /++ The Xth day of the Gregorian Calendar that this $(LREF DateTime) is on. - - Examples: --------------------- -assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == - 1); -assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == - 365); -assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == - 366); - -assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == - 0); -assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == - -365); -assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == - -366); - -assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == - 730_120); -assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == - 734_137); --------------------- +/ - @property int dayOfGregorianCal() const pure nothrow + @property int dayOfGregorianCal() @safe const pure nothrow { return _date.dayOfGregorianCal; } + /// unittest { - version(testStdDateTime) - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, cdt.dayOfGregorianCal)); - static assert(__traits(compiles, idt.dayOfGregorianCal)); + assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1); + assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365); + assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366); - //Verify Examples. - assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1); - assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365); - assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366); + assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0); + assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365); + assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366); - assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0); - assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365); - assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366); + assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120); + assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137); + } - assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120); - assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137); - } + unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, cdt.dayOfGregorianCal)); + static assert(__traits(compiles, idt.dayOfGregorianCal)); } @@ -17202,75 +15973,47 @@ assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == Params: days = The day of the Gregorian Calendar to set this $(LREF DateTime) to. - - Examples: --------------------- -auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0)); -dt.dayOfGregorianCal = 1; -assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0))); - -dt.dayOfGregorianCal = 365; -assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0))); - -dt.dayOfGregorianCal = 366; -assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0))); - -dt.dayOfGregorianCal = 0; -assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0))); - -dt.dayOfGregorianCal = -365; -assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0))); - -dt.dayOfGregorianCal = -366; -assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0))); - -dt.dayOfGregorianCal = 730_120; -assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0))); - -dt.dayOfGregorianCal = 734_137; -assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0))); --------------------- +/ - @property void dayOfGregorianCal(int days) pure nothrow + @property void dayOfGregorianCal(int days) @safe pure nothrow { _date.dayOfGregorianCal = days; } + /// unittest { - version(testStdDateTime) - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(!__traits(compiles, cdt.dayOfGregorianCal = 7)); - static assert(!__traits(compiles, idt.dayOfGregorianCal = 7)); + auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0)); + dt.dayOfGregorianCal = 1; + assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0))); - //Verify Examples. - auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0)); - dt.dayOfGregorianCal = 1; - assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0))); + dt.dayOfGregorianCal = 365; + assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0))); - dt.dayOfGregorianCal = 365; - assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0))); + dt.dayOfGregorianCal = 366; + assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0))); - dt.dayOfGregorianCal = 366; - assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0))); + dt.dayOfGregorianCal = 0; + assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0))); - dt.dayOfGregorianCal = 0; - assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0))); + dt.dayOfGregorianCal = -365; + assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0))); - dt.dayOfGregorianCal = -365; - assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0))); + dt.dayOfGregorianCal = -366; + assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0))); - dt.dayOfGregorianCal = -366; - assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0))); + dt.dayOfGregorianCal = 730_120; + assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0))); - dt.dayOfGregorianCal = 730_120; - assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0))); + dt.dayOfGregorianCal = 734_137; + assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0))); + } - dt.dayOfGregorianCal = 734_137; - assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0))); - } + unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(!__traits(compiles, cdt.dayOfGregorianCal = 7)); + static assert(!__traits(compiles, idt.dayOfGregorianCal = 7)); } @@ -17280,45 +16023,27 @@ assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0))); See_Also: $(WEB en.wikipedia.org/wiki/ISO_week_date, ISO Week Date) +/ - @property ubyte isoWeek() const pure nothrow + @property ubyte isoWeek() @safe const pure nothrow { return _date.isoWeek; } unittest { - version(testStdDateTime) - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, dt.isoWeek)); - static assert(__traits(compiles, cdt.isoWeek)); - static assert(__traits(compiles, idt.isoWeek)); - } + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, dt.isoWeek)); + static assert(__traits(compiles, cdt.isoWeek)); + static assert(__traits(compiles, idt.isoWeek)); } /++ $(LREF DateTime) for the last day in the month that this $(LREF DateTime) is in. The time portion of endOfMonth is always 23:59:59. - - Examples: --------------------- -assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth == - DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59))); - -assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth == - DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59))); - -assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth == - DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59))); - -assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth == - DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59))); --------------------- +/ - @property DateTime endOfMonth() const pure nothrow + @property DateTime endOfMonth() @safe const pure nothrow { try return DateTime(_date.endOfMonth, TimeOfDay(23, 59, 59)); @@ -17326,129 +16051,121 @@ assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth == assert(0, "DateTime constructor threw."); } + /// unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(DateTime(1999, 1, 1, 0, 13, 26).endOfMonth, DateTime(1999, 1, 31, 23, 59, 59)); - _assertPred!"=="(DateTime(1999, 2, 1, 1, 14, 27).endOfMonth, DateTime(1999, 2, 28, 23, 59, 59)); - _assertPred!"=="(DateTime(2000, 2, 1, 2, 15, 28).endOfMonth, DateTime(2000, 2, 29, 23, 59, 59)); - _assertPred!"=="(DateTime(1999, 3, 1, 3, 16, 29).endOfMonth, DateTime(1999, 3, 31, 23, 59, 59)); - _assertPred!"=="(DateTime(1999, 4, 1, 4, 17, 30).endOfMonth, DateTime(1999, 4, 30, 23, 59, 59)); - _assertPred!"=="(DateTime(1999, 5, 1, 5, 18, 31).endOfMonth, DateTime(1999, 5, 31, 23, 59, 59)); - _assertPred!"=="(DateTime(1999, 6, 1, 6, 19, 32).endOfMonth, DateTime(1999, 6, 30, 23, 59, 59)); - _assertPred!"=="(DateTime(1999, 7, 1, 7, 20, 33).endOfMonth, DateTime(1999, 7, 31, 23, 59, 59)); - _assertPred!"=="(DateTime(1999, 8, 1, 8, 21, 34).endOfMonth, DateTime(1999, 8, 31, 23, 59, 59)); - _assertPred!"=="(DateTime(1999, 9, 1, 9, 22, 35).endOfMonth, DateTime(1999, 9, 30, 23, 59, 59)); - _assertPred!"=="(DateTime(1999, 10, 1, 10, 23, 36).endOfMonth, DateTime(1999, 10, 31, 23, 59, 59)); - _assertPred!"=="(DateTime(1999, 11, 1, 11, 24, 37).endOfMonth, DateTime(1999, 11, 30, 23, 59, 59)); - _assertPred!"=="(DateTime(1999, 12, 1, 12, 25, 38).endOfMonth, DateTime(1999, 12, 31, 23, 59, 59)); + assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth == + DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59))); - //Test B.C. - _assertPred!"=="(DateTime(-1999, 1, 1, 0, 13, 26).endOfMonth, DateTime(-1999, 1, 31, 23, 59, 59)); - _assertPred!"=="(DateTime(-1999, 2, 1, 1, 14, 27).endOfMonth, DateTime(-1999, 2, 28, 23, 59, 59)); - _assertPred!"=="(DateTime(-2000, 2, 1, 2, 15, 28).endOfMonth, DateTime(-2000, 2, 29, 23, 59, 59)); - _assertPred!"=="(DateTime(-1999, 3, 1, 3, 16, 29).endOfMonth, DateTime(-1999, 3, 31, 23, 59, 59)); - _assertPred!"=="(DateTime(-1999, 4, 1, 4, 17, 30).endOfMonth, DateTime(-1999, 4, 30, 23, 59, 59)); - _assertPred!"=="(DateTime(-1999, 5, 1, 5, 18, 31).endOfMonth, DateTime(-1999, 5, 31, 23, 59, 59)); - _assertPred!"=="(DateTime(-1999, 6, 1, 6, 19, 32).endOfMonth, DateTime(-1999, 6, 30, 23, 59, 59)); - _assertPred!"=="(DateTime(-1999, 7, 1, 7, 20, 33).endOfMonth, DateTime(-1999, 7, 31, 23, 59, 59)); - _assertPred!"=="(DateTime(-1999, 8, 1, 8, 21, 34).endOfMonth, DateTime(-1999, 8, 31, 23, 59, 59)); - _assertPred!"=="(DateTime(-1999, 9, 1, 9, 22, 35).endOfMonth, DateTime(-1999, 9, 30, 23, 59, 59)); - _assertPred!"=="(DateTime(-1999, 10, 1, 10, 23, 36).endOfMonth, DateTime(-1999, 10, 31, 23, 59, 59)); - _assertPred!"=="(DateTime(-1999, 11, 1, 11, 24, 37).endOfMonth, DateTime(-1999, 11, 30, 23, 59, 59)); - _assertPred!"=="(DateTime(-1999, 12, 1, 12, 25, 38).endOfMonth, DateTime(-1999, 12, 31, 23, 59, 59)); + assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth == + DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59))); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, cdt.endOfMonth)); - static assert(__traits(compiles, idt.endOfMonth)); + assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth == + DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59))); - //Verify Examples. - assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth == DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59))); - assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth == DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59))); - assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth == DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59))); - assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth == DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59))); - } + assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth == + DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59))); } + unittest + { + //Test A.D. + assert(DateTime(1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(1999, 1, 31, 23, 59, 59)); + assert(DateTime(1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(1999, 2, 28, 23, 59, 59)); + assert(DateTime(2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(2000, 2, 29, 23, 59, 59)); + assert(DateTime(1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(1999, 3, 31, 23, 59, 59)); + assert(DateTime(1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(1999, 4, 30, 23, 59, 59)); + assert(DateTime(1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(1999, 5, 31, 23, 59, 59)); + assert(DateTime(1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(1999, 6, 30, 23, 59, 59)); + assert(DateTime(1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(1999, 7, 31, 23, 59, 59)); + assert(DateTime(1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(1999, 8, 31, 23, 59, 59)); + assert(DateTime(1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(1999, 9, 30, 23, 59, 59)); + assert(DateTime(1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(1999, 10, 31, 23, 59, 59)); + assert(DateTime(1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(1999, 11, 30, 23, 59, 59)); + assert(DateTime(1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(1999, 12, 31, 23, 59, 59)); + + //Test B.C. + assert(DateTime(-1999, 1, 1, 0, 13, 26).endOfMonth == DateTime(-1999, 1, 31, 23, 59, 59)); + assert(DateTime(-1999, 2, 1, 1, 14, 27).endOfMonth == DateTime(-1999, 2, 28, 23, 59, 59)); + assert(DateTime(-2000, 2, 1, 2, 15, 28).endOfMonth == DateTime(-2000, 2, 29, 23, 59, 59)); + assert(DateTime(-1999, 3, 1, 3, 16, 29).endOfMonth == DateTime(-1999, 3, 31, 23, 59, 59)); + assert(DateTime(-1999, 4, 1, 4, 17, 30).endOfMonth == DateTime(-1999, 4, 30, 23, 59, 59)); + assert(DateTime(-1999, 5, 1, 5, 18, 31).endOfMonth == DateTime(-1999, 5, 31, 23, 59, 59)); + assert(DateTime(-1999, 6, 1, 6, 19, 32).endOfMonth == DateTime(-1999, 6, 30, 23, 59, 59)); + assert(DateTime(-1999, 7, 1, 7, 20, 33).endOfMonth == DateTime(-1999, 7, 31, 23, 59, 59)); + assert(DateTime(-1999, 8, 1, 8, 21, 34).endOfMonth == DateTime(-1999, 8, 31, 23, 59, 59)); + assert(DateTime(-1999, 9, 1, 9, 22, 35).endOfMonth == DateTime(-1999, 9, 30, 23, 59, 59)); + assert(DateTime(-1999, 10, 1, 10, 23, 36).endOfMonth == DateTime(-1999, 10, 31, 23, 59, 59)); + assert(DateTime(-1999, 11, 1, 11, 24, 37).endOfMonth == DateTime(-1999, 11, 30, 23, 59, 59)); + assert(DateTime(-1999, 12, 1, 12, 25, 38).endOfMonth == DateTime(-1999, 12, 31, 23, 59, 59)); - /++ - The last day in the month that this $(LREF DateTime) is in. + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, cdt.endOfMonth)); + static assert(__traits(compiles, idt.endOfMonth)); + } - Examples: --------------------- -assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31); -assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28); -assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29); -assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30); --------------------- + + /++ + The last day in the month that this $(LREF DateTime) is in. +/ - @property ubyte daysInMonth() const pure nothrow + @property ubyte daysInMonth() @safe const pure nothrow { return _date.daysInMonth; } + /// unittest { - version(testStdDateTime) - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, cdt.daysInMonth)); - static assert(__traits(compiles, idt.daysInMonth)); + assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31); + assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28); + assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29); + assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30); + } - //Verify Examples. - assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31); - assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28); - assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29); - assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30); - } + unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, cdt.daysInMonth)); + static assert(__traits(compiles, idt.daysInMonth)); } /++ Whether the current year is a date in A.D. - - Examples: --------------------- -assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD); -assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD); -assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD); -assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD); --------------------- +/ - @property bool isAD() const pure nothrow + @property bool isAD() @safe const pure nothrow { return _date.isAD; } + /// unittest { - version(testStdDateTime) - { - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, cdt.isAD)); - static assert(__traits(compiles, idt.isAD)); + assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD); + assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD); + assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD); + assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD); + } - //Verify Examples. - assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD); - assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD); - assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD); - assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD); - } + unittest + { + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, cdt.isAD)); + static assert(__traits(compiles, idt.isAD)); } /++ - The $(WEB en.wikipedia.org/wiki/Julian_day, Julian day) for this $(LREF DateTime) at the given time. For example, - prior to noon, 1996-03-31 would be the Julian day number 2_450_173, so - this function returns 2_450_173, while from noon onward, the julian - day number would be 2_450_174, so this function returns 2_450_174. + The $(WEB en.wikipedia.org/wiki/Julian_day, Julian day) for this + $(LREF DateTime) at the given time. For example, prior to noon, + 1996-03-31 would be the Julian day number 2_450_173, so this function + returns 2_450_173, while from noon onward, the julian day number would + be 2_450_174, so this function returns 2_450_174. +/ - @property long julianDay() const pure nothrow + @property long julianDay() @safe const pure nothrow { if(_tod._hour < 12) return _date.julianDay - 1; @@ -17458,86 +16175,65 @@ assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD); unittest { - version(testStdDateTime) - { - _assertPred!"=="(DateTime(Date(-4713, 11, 24), TimeOfDay(0, 0, 0)).julianDay, -1); - _assertPred!"=="(DateTime(Date(-4713, 11, 24), TimeOfDay(12, 0, 0)).julianDay, 0); + assert(DateTime(Date(-4713, 11, 24), TimeOfDay(0, 0, 0)).julianDay == -1); + assert(DateTime(Date(-4713, 11, 24), TimeOfDay(12, 0, 0)).julianDay == 0); - _assertPred!"=="(DateTime(Date(0, 12, 31), TimeOfDay(0, 0, 0)).julianDay, 1_721_424); - _assertPred!"=="(DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)).julianDay, 1_721_425); + assert(DateTime(Date(0, 12, 31), TimeOfDay(0, 0, 0)).julianDay == 1_721_424); + assert(DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)).julianDay == 1_721_425); - _assertPred!"=="(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).julianDay, 1_721_425); - _assertPred!"=="(DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)).julianDay, 1_721_426); + assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).julianDay == 1_721_425); + assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)).julianDay == 1_721_426); - _assertPred!"=="(DateTime(Date(1582, 10, 15), TimeOfDay(0, 0, 0)).julianDay, 2_299_160); - _assertPred!"=="(DateTime(Date(1582, 10, 15), TimeOfDay(12, 0, 0)).julianDay, 2_299_161); + assert(DateTime(Date(1582, 10, 15), TimeOfDay(0, 0, 0)).julianDay == 2_299_160); + assert(DateTime(Date(1582, 10, 15), TimeOfDay(12, 0, 0)).julianDay == 2_299_161); - _assertPred!"=="(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).julianDay, 2_400_000); - _assertPred!"=="(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).julianDay, 2_400_001); + assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).julianDay == 2_400_000); + assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).julianDay == 2_400_001); - _assertPred!"=="(DateTime(Date(1982, 1, 4), TimeOfDay(0, 0, 0)).julianDay, 2_444_973); - _assertPred!"=="(DateTime(Date(1982, 1, 4), TimeOfDay(12, 0, 0)).julianDay, 2_444_974); + assert(DateTime(Date(1982, 1, 4), TimeOfDay(0, 0, 0)).julianDay == 2_444_973); + assert(DateTime(Date(1982, 1, 4), TimeOfDay(12, 0, 0)).julianDay == 2_444_974); - _assertPred!"=="(DateTime(Date(1996, 3, 31), TimeOfDay(0, 0, 0)).julianDay, 2_450_173); - _assertPred!"=="(DateTime(Date(1996, 3, 31), TimeOfDay(12, 0, 0)).julianDay, 2_450_174); + assert(DateTime(Date(1996, 3, 31), TimeOfDay(0, 0, 0)).julianDay == 2_450_173); + assert(DateTime(Date(1996, 3, 31), TimeOfDay(12, 0, 0)).julianDay == 2_450_174); - _assertPred!"=="(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).julianDay, 2_455_432); - _assertPred!"=="(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).julianDay, 2_455_433); + assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).julianDay == 2_455_432); + assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).julianDay == 2_455_433); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, cdt.julianDay)); - static assert(__traits(compiles, idt.julianDay)); - } + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, cdt.julianDay)); + static assert(__traits(compiles, idt.julianDay)); } /++ - The modified $(WEB en.wikipedia.org/wiki/Julian_day, Julian day) for any time on this date (since, the modified - Julian day changes at midnight). + The modified $(WEB en.wikipedia.org/wiki/Julian_day, Julian day) for any + time on this date (since, the modified Julian day changes at midnight). +/ - @property long modJulianDay() const pure nothrow + @property long modJulianDay() @safe const pure nothrow { return _date.modJulianDay; } unittest { - version(testStdDateTime) - { - _assertPred!"=="(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).modJulianDay, 0); - _assertPred!"=="(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).modJulianDay, 0); + assert(DateTime(Date(1858, 11, 17), TimeOfDay(0, 0, 0)).modJulianDay == 0); + assert(DateTime(Date(1858, 11, 17), TimeOfDay(12, 0, 0)).modJulianDay == 0); - _assertPred!"=="(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).modJulianDay, 55_432); - _assertPred!"=="(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).modJulianDay, 55_432); + assert(DateTime(Date(2010, 8, 24), TimeOfDay(0, 0, 0)).modJulianDay == 55_432); + assert(DateTime(Date(2010, 8, 24), TimeOfDay(12, 0, 0)).modJulianDay == 55_432); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, cdt.modJulianDay)); - static assert(__traits(compiles, idt.modJulianDay)); - } + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, cdt.modJulianDay)); + static assert(__traits(compiles, idt.modJulianDay)); } /++ Converts this $(LREF DateTime) to a string with the format YYYYMMDDTHHMMSS. - - Examples: --------------------- -assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() == - "20100704T070612"); - -assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() == - "19981225T021500"); - -assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() == - "00000105T230959"); - -assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() == - "-00040105T000002"); --------------------- +/ - string toISOString() const nothrow + string toISOString() @safe const pure nothrow { try return format("%sT%s", _date.toISOString(), _tod.toISOString()); @@ -17545,59 +16241,51 @@ assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() == assert(0, "format() threw."); } + /// unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOString(), "00091204T000000"); - _assertPred!"=="(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOString(), "00991204T050612"); - _assertPred!"=="(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOString(), "09991204T134459"); - _assertPred!"=="(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString(), "99990704T235959"); - _assertPred!"=="(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString(), "+100001020T010101"); + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() == + "20100704T070612"); - //Test B.C. - _assertPred!"=="(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOString(), "00001204T001204"); - _assertPred!"=="(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOString(), "-00091204T000000"); - _assertPred!"=="(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOString(), "-00991204T050612"); - _assertPred!"=="(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOString(), "-09991204T134459"); - _assertPred!"=="(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString(), "-99990704T235959"); - _assertPred!"=="(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString(), "-100001020T010101"); + assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() == + "19981225T021500"); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.toISOString())); - static assert(__traits(compiles, idt.toISOString())); + assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() == + "00000105T230959"); - //Verify Examples. - assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() == "20100704T070612"); - assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() == "19981225T021500"); - assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() == "00000105T230959"); - assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() == "-00040105T000002"); - } + assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() == + "-00040105T000002"); } + unittest + { + //Test A.D. + assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "00091204T000000"); + assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "00991204T050612"); + assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "09991204T134459"); + assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "99990704T235959"); + assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "+100001020T010101"); - /++ - Converts this $(LREF DateTime) to a string with the format - YYYY-MM-DDTHH:MM:SS. - - Examples: --------------------- -assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() == - "2010-07-04T07:06:12"); + //Test B.C. + assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOString() == "00001204T001204"); + assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOString() == "-00091204T000000"); + assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOString() == "-00991204T050612"); + assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOString() == "-09991204T134459"); + assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOString() == "-99990704T235959"); + assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOString() == "-100001020T010101"); -assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() == - "1998-12-25T02:15:00"); + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.toISOString())); + static assert(__traits(compiles, idt.toISOString())); + } -assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() == - "0000-01-05T23:09:59"); -assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() == - "-0004-01-05T00:00:02"); --------------------- + /++ + Converts this $(LREF DateTime) to a string with the format + YYYY-MM-DDTHH:MM:SS. +/ - string toISOExtString() const nothrow + string toISOExtString() @safe const pure nothrow { try return format("%sT%s", _date.toISOExtString(), _tod.toISOExtString()); @@ -17605,58 +16293,50 @@ assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() == assert(0, "format() threw."); } + /// unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString(), "0009-12-04T00:00:00"); - _assertPred!"=="(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString(), "0099-12-04T05:06:12"); - _assertPred!"=="(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString(), "0999-12-04T13:44:59"); - _assertPred!"=="(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString(), "9999-07-04T23:59:59"); - _assertPred!"=="(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString(), "+10000-10-20T01:01:01"); + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() == + "2010-07-04T07:06:12"); - //Test B.C. - _assertPred!"=="(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOExtString(), "0000-12-04T00:12:04"); - _assertPred!"=="(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString(), "-0009-12-04T00:00:00"); - _assertPred!"=="(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString(), "-0099-12-04T05:06:12"); - _assertPred!"=="(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString(), "-0999-12-04T13:44:59"); - _assertPred!"=="(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString(), "-9999-07-04T23:59:59"); - _assertPred!"=="(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString(), "-10000-10-20T01:01:01"); + assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() == + "1998-12-25T02:15:00"); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.toISOExtString())); - static assert(__traits(compiles, idt.toISOExtString())); + assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() == + "0000-01-05T23:09:59"); - //Verify Examples. - assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() == "2010-07-04T07:06:12"); - assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() == "1998-12-25T02:15:00"); - assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() == "0000-01-05T23:09:59"); - assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() == "-0004-01-05T00:00:02"); - } + assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() == + "-0004-01-05T00:00:02"); } - /++ - Converts this $(LREF DateTime) to a string with the format - YYYY-Mon-DD HH:MM:SS. - - Examples: --------------------- -assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() == - "2010-Jul-04 07:06:12"); + unittest + { + //Test A.D. + assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "0009-12-04T00:00:00"); + assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "0099-12-04T05:06:12"); + assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "0999-12-04T13:44:59"); + assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "9999-07-04T23:59:59"); + assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "+10000-10-20T01:01:01"); -assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() == - "1998-Dec-25 02:15:00"); + //Test B.C. + assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toISOExtString() == "0000-12-04T00:12:04"); + assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toISOExtString() == "-0009-12-04T00:00:00"); + assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toISOExtString() == "-0099-12-04T05:06:12"); + assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toISOExtString() == "-0999-12-04T13:44:59"); + assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toISOExtString() == "-9999-07-04T23:59:59"); + assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toISOExtString() == "-10000-10-20T01:01:01"); -assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() == - "0000-Jan-05 23:09:59"); + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.toISOExtString())); + static assert(__traits(compiles, idt.toISOExtString())); + } -assert(DateTime(Dte(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() == - "-0004-Jan-05 00:00:02"); --------------------- + /++ + Converts this $(LREF DateTime) to a string with the format + YYYY-Mon-DD HH:MM:SS. +/ - string toSimpleString() const nothrow + string toSimpleString() @safe const pure nothrow { try return format("%s %s", _date.toSimpleString(), _tod.toString()); @@ -17664,72 +16344,62 @@ assert(DateTime(Dte(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() == assert(0, "format() threw."); } + /// unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString(), "0009-Dec-04 00:00:00"); - _assertPred!"=="(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString(), "0099-Dec-04 05:06:12"); - _assertPred!"=="(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString(), "0999-Dec-04 13:44:59"); - _assertPred!"=="(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString(), "9999-Jul-04 23:59:59"); - _assertPred!"=="(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString(), "+10000-Oct-20 01:01:01"); + assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() == + "2010-Jul-04 07:06:12"); - //Test B.C. - _assertPred!"=="(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toSimpleString(), "0000-Dec-04 00:12:04"); - _assertPred!"=="(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString(), "-0009-Dec-04 00:00:00"); - _assertPred!"=="(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString(), "-0099-Dec-04 05:06:12"); - _assertPred!"=="(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString(), "-0999-Dec-04 13:44:59"); - _assertPred!"=="(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString(), "-9999-Jul-04 23:59:59"); - _assertPred!"=="(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString(), "-10000-Oct-20 01:01:01"); + assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() == + "1998-Dec-25 02:15:00"); - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(__traits(compiles, cdt.toSimpleString())); - static assert(__traits(compiles, idt.toSimpleString())); + assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() == + "0000-Jan-05 23:09:59"); - //Verify Examples. - assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() == "2010-Jul-04 07:06:12"); - assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() == "1998-Dec-25 02:15:00"); - assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() == "0000-Jan-05 23:09:59"); - assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() == "-0004-Jan-05 00:00:02"); - } + assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() == + "-0004-Jan-05 00:00:02"); } - - /+ - Converts this $(LREF DateTime) to a string. - +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() + unittest { - return toSimpleString(); + //Test A.D. + assert(DateTime(Date(9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "0009-Dec-04 00:00:00"); + assert(DateTime(Date(99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "0099-Dec-04 05:06:12"); + assert(DateTime(Date(999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "0999-Dec-04 13:44:59"); + assert(DateTime(Date(9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "9999-Jul-04 23:59:59"); + assert(DateTime(Date(10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "+10000-Oct-20 01:01:01"); + + //Test B.C. + assert(DateTime(Date(0, 12, 4), TimeOfDay(0, 12, 4)).toSimpleString() == "0000-Dec-04 00:12:04"); + assert(DateTime(Date(-9, 12, 4), TimeOfDay(0, 0, 0)).toSimpleString() == "-0009-Dec-04 00:00:00"); + assert(DateTime(Date(-99, 12, 4), TimeOfDay(5, 6, 12)).toSimpleString() == "-0099-Dec-04 05:06:12"); + assert(DateTime(Date(-999, 12, 4), TimeOfDay(13, 44, 59)).toSimpleString() == "-0999-Dec-04 13:44:59"); + assert(DateTime(Date(-9999, 7, 4), TimeOfDay(23, 59, 59)).toSimpleString() == "-9999-Jul-04 23:59:59"); + assert(DateTime(Date(-10000, 10, 20), TimeOfDay(1, 1, 1)).toSimpleString() == "-10000-Oct-20 01:01:01"); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(__traits(compiles, cdt.toSimpleString())); + static assert(__traits(compiles, idt.toSimpleString())); } + /++ Converts this $(LREF DateTime) to a string. +/ - //Due to bug http://d.puremagic.com/issues/show_bug.cgi?id=3715 , we can't - //have versions of toString() with extra modifiers, so we define one version - //with modifiers and one without. - string toString() const nothrow + string toString() @safe const pure nothrow { return toSimpleString(); } unittest { - version(testStdDateTime) - { - auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); - static assert(__traits(compiles, dt.toString())); - static assert(__traits(compiles, cdt.toString())); - static assert(__traits(compiles, idt.toString())); - } + auto dt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + const cdt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + immutable idt = DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33)); + static assert(__traits(compiles, dt.toString())); + static assert(__traits(compiles, cdt.toString())); + static assert(__traits(compiles, idt.toString())); } @@ -17744,26 +16414,8 @@ assert(DateTime(Dte(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() == Throws: $(LREF DateTimeException) if the given string is not in the ISO format or if the resulting $(LREF DateTime) would not be valid. - - Examples: --------------------- -assert(DateTime.fromISOString("20100704T070612") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - -assert(DateTime.fromISOString("19981225T021500") == - DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - -assert(DateTime.fromISOString("00000105T230959") == - DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - -assert(DateTime.fromISOString("-00040105T000002") == - DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - -assert(DateTime.fromISOString(" 20100704T070612 ") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); --------------------- +/ - static DateTime fromISOString(S)(in S isoString) + static DateTime fromISOString(S)(in S isoString) @safe pure if(isSomeString!S) { immutable dstr = to!dstring(strip(isoString)); @@ -17779,48 +16431,57 @@ assert(DateTime.fromISOString(" 20100704T070612 ") == return DateTime(date, tod); } + /// unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException(DateTime.fromISOString("")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); + assert(DateTime.fromISOString("20100704T070612") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); + assert(DateTime.fromISOString("19981225T021500") == + DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); + assert(DateTime.fromISOString("00000105T230959") == + DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01")); + assert(DateTime.fromISOString("-00040105T000002") == + DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - _assertPred!"=="(DateTime.fromISOString("20101222T172201"), DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); - _assertPred!"=="(DateTime.fromISOString("19990706T123033"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!"=="(DateTime.fromISOString("-19990706T123033"), DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!"=="(DateTime.fromISOString("+019990706T123033"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!"=="(DateTime.fromISOString("19990706T123033 "), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!"=="(DateTime.fromISOString(" 19990706T123033"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!"=="(DateTime.fromISOString(" 19990706T123033 "), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString(" 20100704T070612 ") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + } - //Verify Examples. - assert(DateTime.fromISOString("20100704T070612") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - assert(DateTime.fromISOString("19981225T021500") == DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - assert(DateTime.fromISOString("00000105T230959") == DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - assert(DateTime.fromISOString("-00040105T000002") == DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - assert(DateTime.fromISOString(" 20100704T070612 ") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - } + unittest + { + assertThrown!DateTimeException(DateTime.fromISOString("")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOString("2010-12-22T172201")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Dec-22 17:22:01")); + + assert(DateTime.fromISOString("20101222T172201") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); + assert(DateTime.fromISOString("19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString("-19990706T123033") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString("+019990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString("19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString(" 19990706T123033") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOString(" 19990706T123033 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); } @@ -17836,26 +16497,8 @@ assert(DateTime.fromISOString(" 20100704T070612 ") == $(LREF DateTimeException) if the given string is not in the ISO Extended format or if the resulting $(LREF DateTime) would not be valid. - - Examples: --------------------- -assert(DateTime.fromISOExtString("2010-07-04T07:06:12") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - -assert(DateTime.fromISOExtString("1998-12-25T02:15:00") == - DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - -assert(DateTime.fromISOExtString("0000-01-05T23:09:59") == - DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - -assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") == - DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - -assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); --------------------- +/ - static DateTime fromISOExtString(S)(in S isoExtString) + static DateTime fromISOExtString(S)(in S isoExtString) @safe pure if(isSomeString!(S)) { immutable dstr = to!dstring(strip(isoExtString)); @@ -17871,47 +16514,56 @@ assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") == return DateTime(date, tod); } + /// unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException(DateTime.fromISOExtString("")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704000000")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704 000000")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704t000000")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.")); - assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.0")); + assert(DateTime.fromISOExtString("2010-07-04T07:06:12") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07:0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.0")); + assert(DateTime.fromISOExtString("1998-12-25T02:15:00") == + DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.0")); + assert(DateTime.fromISOExtString("0000-01-05T23:09:59") == + DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201")); - assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01")); + assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") == + DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - _assertPred!"=="(DateTime.fromISOExtString("2010-12-22T17:22:01"), DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); - _assertPred!"=="(DateTime.fromISOExtString("1999-07-06T12:30:33"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!"=="(DateTime.fromISOExtString("-1999-07-06T12:30:33"), DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!"=="(DateTime.fromISOExtString("+01999-07-06T12:30:33"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!"=="(DateTime.fromISOExtString("1999-07-06T12:30:33 "), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!"=="(DateTime.fromISOExtString(" 1999-07-06T12:30:33"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!"=="(DateTime.fromISOExtString(" 1999-07-06T12:30:33 "), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + } - //Verify Examples. - assert(DateTime.fromISOExtString("2010-07-04T07:06:12") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - assert(DateTime.fromISOExtString("1998-12-25T02:15:00") == DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - assert(DateTime.fromISOExtString("0000-01-05T23:09:59") == DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") == DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - } + unittest + { + assertThrown!DateTimeException(DateTime.fromISOExtString("")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704000000")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704 000000")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704t000000")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.")); + assertThrown!DateTimeException(DateTime.fromISOExtString("20100704T000000.0")); + + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07:0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-07-04T00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Jul-04 00:00:00.0")); + + assertThrown!DateTimeException(DateTime.fromISOExtString("20101222T172201")); + assertThrown!DateTimeException(DateTime.fromISOExtString("2010-Dec-22 17:22:01")); + + assert(DateTime.fromISOExtString("2010-12-22T17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); + assert(DateTime.fromISOExtString("1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString("-1999-07-06T12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString("+01999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString("1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromISOExtString(" 1999-07-06T12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); } @@ -17926,22 +16578,8 @@ assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") == Throws: $(LREF DateTimeException) if the given string is not in the correct format or if the resulting $(LREF DateTime) would not be valid. - - Examples: --------------------- -assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); -assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") == - DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); -assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") == - DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); -assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") == - DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); -assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == - DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); --------------------- +/ - static DateTime fromSimpleString(S)(in S simpleString) + static DateTime fromSimpleString(S)(in S simpleString) @safe pure if(isSomeString!(S)) { immutable dstr = to!dstring(strip(simpleString)); @@ -17957,63 +16595,61 @@ assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == return DateTime(date, tod); } + /// unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException(DateTime.fromISOString("")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); - assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); + assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") == + DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); + assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") == + DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); + assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") == + DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); + assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == + DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); + } - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); + unittest + { + assertThrown!DateTimeException(DateTime.fromISOString("")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704 000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704t000000")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.")); + assertThrown!DateTimeException(DateTime.fromISOString("20100704T000000.0")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); - assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-07-04T00:00:00.0")); - assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201")); - assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-0400:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04t00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04T00:00:00")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.")); + assertThrown!DateTimeException(DateTime.fromISOString("2010-Jul-04 00:00:00.0")); - _assertPred!"=="(DateTime.fromSimpleString("2010-Dec-22 17:22:01"), DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); - _assertPred!"=="(DateTime.fromSimpleString("1999-Jul-06 12:30:33"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!"=="(DateTime.fromSimpleString("-1999-Jul-06 12:30:33"), DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!"=="(DateTime.fromSimpleString("+01999-Jul-06 12:30:33"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!"=="(DateTime.fromSimpleString("1999-Jul-06 12:30:33 "), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!"=="(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33"), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); - _assertPred!"=="(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 "), DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assertThrown!DateTimeException(DateTime.fromSimpleString("20101222T172201")); + assertThrown!DateTimeException(DateTime.fromSimpleString("2010-12-22T172201")); - //Verify Examples. - assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") == DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0))); - assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") == DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59))); - assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") == DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2))); - assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12))); - } + assert(DateTime.fromSimpleString("2010-Dec-22 17:22:01") == DateTime(Date(2010, 12, 22), TimeOfDay(17, 22, 01))); + assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString("-1999-Jul-06 12:30:33") == DateTime(Date(-1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString("+01999-Jul-06 12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString("1999-Jul-06 12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); + assert(DateTime.fromSimpleString(" 1999-Jul-06 12:30:33 ") == DateTime(Date(1999, 7, 6), TimeOfDay(12, 30, 33))); } - //TODO Add function which takes a user-specified time format and produces a DateTime - - //TODO Add function which takes pretty much any time-string and produces a DateTime - // Obviously, it will be less efficient, and it probably won't manage _every_ - // possible date format, but a smart conversion function would be nice. - - /++ Returns the $(LREF DateTime) farthest in the past which is representable by $(LREF DateTime). +/ - @property static DateTime min() pure nothrow + @property static DateTime min() @safe pure nothrow out(result) { assert(result._date == Date.min); @@ -18031,11 +16667,8 @@ assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == unittest { - version(testStdDateTime) - { - assert(DateTime.min.year < 0); - assert(DateTime.min < DateTime.max); - } + assert(DateTime.min.year < 0); + assert(DateTime.min < DateTime.max); } @@ -18043,7 +16676,7 @@ assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == Returns the $(LREF DateTime) farthest in the future which is representable by $(LREF DateTime). +/ - @property static DateTime max() pure nothrow + @property static DateTime max() @safe pure nothrow out(result) { assert(result._date == Date.max); @@ -18064,11 +16697,8 @@ assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") == unittest { - version(testStdDateTime) - { - assert(DateTime.max.year > 0); - assert(DateTime.max > DateTime.min); - } + assert(DateTime.max.year > 0); + assert(DateTime.max > DateTime.min); } @@ -18083,7 +16713,7 @@ private: Params: seconds = The number of seconds to add to this $(LREF DateTime). +/ - ref DateTime addSeconds(long seconds) pure nothrow + ref DateTime _addSeconds(long seconds) @safe pure nothrow { long hnsecs = convert!("seconds", "hnsecs")(seconds); hnsecs += convert!("hours", "hnsecs")(_tod._hour); @@ -18098,7 +16728,7 @@ private: --days; } - _date.addDays(days); + _date._addDays(days); immutable newHours = splitUnitsFromHNSecs!"hours"(hnsecs); immutable newMinutes = splitUnitsFromHNSecs!"minutes"(hnsecs); @@ -18113,175 +16743,172 @@ private: unittest { - version(testStdDateTime) + static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) { - static void testDT(DateTime orig, int seconds, in DateTime expected, size_t line = __LINE__) - { - orig.addSeconds(seconds); - _assertPred!"=="(orig, expected, "", __FILE__, line); - } - - //Test A.D. - testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34)); - - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33)); - testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3)); - - testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32)); - - testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32)); - testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59)); - testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57)); - - testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1)); - testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0)); - testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59)); - - testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1)); - testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0)); - testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59)); - - testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1)); - testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0)); - testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59)); - - testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0)); - testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59)); - testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58)); - - testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0)); - testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59)); - testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58)); - - testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1)); - testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0)); - testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59)); - - //Test B.C. - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33)); - testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59)); - testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33)); - testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57)); - - testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1)); - testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0)); - testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59)); - - testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1)); - testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0)); - testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59)); - - testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1)); - testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0)); - testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59)); - - testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0)); - testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59)); - testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58)); - - testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0)); - testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59)); - testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58)); - - testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1)); - testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0)); - testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59)); - - //Test Both - testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59)); - testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0)); - - testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59)); - testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0)); - - testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33)); - testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33)); - - testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50)); - testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33)); - - const cdt = DateTime(1999, 7, 6, 12, 30, 33); - immutable idt = DateTime(1999, 7, 6, 12, 30, 33); - static assert(!__traits(compiles, cdt.addSeconds(4))); - static assert(!__traits(compiles, idt.addSeconds(4))); + orig._addSeconds(seconds); + assert(orig == expected); } + + //Test A.D. + testDT(DateTime(1999, 7, 6, 12, 30, 33), 0, DateTime(1999, 7, 6, 12, 30, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1, DateTime(1999, 7, 6, 12, 30, 34)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 2, DateTime(1999, 7, 6, 12, 30, 35)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3, DateTime(1999, 7, 6, 12, 30, 36)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 4, DateTime(1999, 7, 6, 12, 30, 37)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 5, DateTime(1999, 7, 6, 12, 30, 38)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 10, DateTime(1999, 7, 6, 12, 30, 43)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 15, DateTime(1999, 7, 6, 12, 30, 48)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 26, DateTime(1999, 7, 6, 12, 30, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 27, DateTime(1999, 7, 6, 12, 31, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 30, DateTime(1999, 7, 6, 12, 31, 3)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 59, DateTime(1999, 7, 6, 12, 31, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 60, DateTime(1999, 7, 6, 12, 31, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 61, DateTime(1999, 7, 6, 12, 31, 34)); + + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1766, DateTime(1999, 7, 6, 12, 59, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1767, DateTime(1999, 7, 6, 13, 0, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 1768, DateTime(1999, 7, 6, 13, 0, 1)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 2007, DateTime(1999, 7, 6, 13, 4, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3599, DateTime(1999, 7, 6, 13, 30, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3600, DateTime(1999, 7, 6, 13, 30, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 3601, DateTime(1999, 7, 6, 13, 30, 34)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), 7200, DateTime(1999, 7, 6, 14, 30, 33)); + testDT(DateTime(1999, 7, 6, 23, 0, 0), 432_123, DateTime(1999, 7, 11, 23, 2, 3)); + + testDT(DateTime(1999, 7, 6, 12, 30, 33), -1, DateTime(1999, 7, 6, 12, 30, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -2, DateTime(1999, 7, 6, 12, 30, 31)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -3, DateTime(1999, 7, 6, 12, 30, 30)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -4, DateTime(1999, 7, 6, 12, 30, 29)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -5, DateTime(1999, 7, 6, 12, 30, 28)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -10, DateTime(1999, 7, 6, 12, 30, 23)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -15, DateTime(1999, 7, 6, 12, 30, 18)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -33, DateTime(1999, 7, 6, 12, 30, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -34, DateTime(1999, 7, 6, 12, 29, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -35, DateTime(1999, 7, 6, 12, 29, 58)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -59, DateTime(1999, 7, 6, 12, 29, 34)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -60, DateTime(1999, 7, 6, 12, 29, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -61, DateTime(1999, 7, 6, 12, 29, 32)); + + testDT(DateTime(1999, 7, 6, 12, 30, 33), -1833, DateTime(1999, 7, 6, 12, 0, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -1834, DateTime(1999, 7, 6, 11, 59, 59)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -3600, DateTime(1999, 7, 6, 11, 30, 33)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -3601, DateTime(1999, 7, 6, 11, 30, 32)); + testDT(DateTime(1999, 7, 6, 12, 30, 33), -5134, DateTime(1999, 7, 6, 11, 4, 59)); + testDT(DateTime(1999, 7, 6, 23, 0, 0), -432_123, DateTime(1999, 7, 1, 22, 57, 57)); + + testDT(DateTime(1999, 7, 6, 12, 30, 0), 1, DateTime(1999, 7, 6, 12, 30, 1)); + testDT(DateTime(1999, 7, 6, 12, 30, 0), 0, DateTime(1999, 7, 6, 12, 30, 0)); + testDT(DateTime(1999, 7, 6, 12, 30, 0), -1, DateTime(1999, 7, 6, 12, 29, 59)); + + testDT(DateTime(1999, 7, 6, 12, 0, 0), 1, DateTime(1999, 7, 6, 12, 0, 1)); + testDT(DateTime(1999, 7, 6, 12, 0, 0), 0, DateTime(1999, 7, 6, 12, 0, 0)); + testDT(DateTime(1999, 7, 6, 12, 0, 0), -1, DateTime(1999, 7, 6, 11, 59, 59)); + + testDT(DateTime(1999, 7, 6, 0, 0, 0), 1, DateTime(1999, 7, 6, 0, 0, 1)); + testDT(DateTime(1999, 7, 6, 0, 0, 0), 0, DateTime(1999, 7, 6, 0, 0, 0)); + testDT(DateTime(1999, 7, 6, 0, 0, 0), -1, DateTime(1999, 7, 5, 23, 59, 59)); + + testDT(DateTime(1999, 7, 5, 23, 59, 59), 1, DateTime(1999, 7, 6, 0, 0, 0)); + testDT(DateTime(1999, 7, 5, 23, 59, 59), 0, DateTime(1999, 7, 5, 23, 59, 59)); + testDT(DateTime(1999, 7, 5, 23, 59, 59), -1, DateTime(1999, 7, 5, 23, 59, 58)); + + testDT(DateTime(1998, 12, 31, 23, 59, 59), 1, DateTime(1999, 1, 1, 0, 0, 0)); + testDT(DateTime(1998, 12, 31, 23, 59, 59), 0, DateTime(1998, 12, 31, 23, 59, 59)); + testDT(DateTime(1998, 12, 31, 23, 59, 59), -1, DateTime(1998, 12, 31, 23, 59, 58)); + + testDT(DateTime(1998, 1, 1, 0, 0, 0), 1, DateTime(1998, 1, 1, 0, 0, 1)); + testDT(DateTime(1998, 1, 1, 0, 0, 0), 0, DateTime(1998, 1, 1, 0, 0, 0)); + testDT(DateTime(1998, 1, 1, 0, 0, 0), -1, DateTime(1997, 12, 31, 23, 59, 59)); + + //Test B.C. + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 0, DateTime(-1999, 7, 6, 12, 30, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1, DateTime(-1999, 7, 6, 12, 30, 34)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2, DateTime(-1999, 7, 6, 12, 30, 35)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3, DateTime(-1999, 7, 6, 12, 30, 36)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 4, DateTime(-1999, 7, 6, 12, 30, 37)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 5, DateTime(-1999, 7, 6, 12, 30, 38)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 10, DateTime(-1999, 7, 6, 12, 30, 43)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 15, DateTime(-1999, 7, 6, 12, 30, 48)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 26, DateTime(-1999, 7, 6, 12, 30, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 27, DateTime(-1999, 7, 6, 12, 31, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 30, DateTime(-1999, 7, 6, 12, 31, 3)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 59, DateTime(-1999, 7, 6, 12, 31, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 60, DateTime(-1999, 7, 6, 12, 31, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 61, DateTime(-1999, 7, 6, 12, 31, 34)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1766, DateTime(-1999, 7, 6, 12, 59, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1767, DateTime(-1999, 7, 6, 13, 0, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 1768, DateTime(-1999, 7, 6, 13, 0, 1)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 2007, DateTime(-1999, 7, 6, 13, 4, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3599, DateTime(-1999, 7, 6, 13, 30, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3600, DateTime(-1999, 7, 6, 13, 30, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 3601, DateTime(-1999, 7, 6, 13, 30, 34)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), 7200, DateTime(-1999, 7, 6, 14, 30, 33)); + testDT(DateTime(-1999, 7, 6, 23, 0, 0), 432_123, DateTime(-1999, 7, 11, 23, 2, 3)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1, DateTime(-1999, 7, 6, 12, 30, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -2, DateTime(-1999, 7, 6, 12, 30, 31)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3, DateTime(-1999, 7, 6, 12, 30, 30)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -4, DateTime(-1999, 7, 6, 12, 30, 29)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5, DateTime(-1999, 7, 6, 12, 30, 28)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -10, DateTime(-1999, 7, 6, 12, 30, 23)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -15, DateTime(-1999, 7, 6, 12, 30, 18)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -33, DateTime(-1999, 7, 6, 12, 30, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -34, DateTime(-1999, 7, 6, 12, 29, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -35, DateTime(-1999, 7, 6, 12, 29, 58)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -59, DateTime(-1999, 7, 6, 12, 29, 34)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -60, DateTime(-1999, 7, 6, 12, 29, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -61, DateTime(-1999, 7, 6, 12, 29, 32)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1833, DateTime(-1999, 7, 6, 12, 0, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -1834, DateTime(-1999, 7, 6, 11, 59, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3600, DateTime(-1999, 7, 6, 11, 30, 33)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -3601, DateTime(-1999, 7, 6, 11, 30, 32)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -5134, DateTime(-1999, 7, 6, 11, 4, 59)); + testDT(DateTime(-1999, 7, 6, 12, 30, 33), -7200, DateTime(-1999, 7, 6, 10, 30, 33)); + testDT(DateTime(-1999, 7, 6, 23, 0, 0), -432_123, DateTime(-1999, 7, 1, 22, 57, 57)); + + testDT(DateTime(-1999, 7, 6, 12, 30, 0), 1, DateTime(-1999, 7, 6, 12, 30, 1)); + testDT(DateTime(-1999, 7, 6, 12, 30, 0), 0, DateTime(-1999, 7, 6, 12, 30, 0)); + testDT(DateTime(-1999, 7, 6, 12, 30, 0), -1, DateTime(-1999, 7, 6, 12, 29, 59)); + + testDT(DateTime(-1999, 7, 6, 12, 0, 0), 1, DateTime(-1999, 7, 6, 12, 0, 1)); + testDT(DateTime(-1999, 7, 6, 12, 0, 0), 0, DateTime(-1999, 7, 6, 12, 0, 0)); + testDT(DateTime(-1999, 7, 6, 12, 0, 0), -1, DateTime(-1999, 7, 6, 11, 59, 59)); + + testDT(DateTime(-1999, 7, 6, 0, 0, 0), 1, DateTime(-1999, 7, 6, 0, 0, 1)); + testDT(DateTime(-1999, 7, 6, 0, 0, 0), 0, DateTime(-1999, 7, 6, 0, 0, 0)); + testDT(DateTime(-1999, 7, 6, 0, 0, 0), -1, DateTime(-1999, 7, 5, 23, 59, 59)); + + testDT(DateTime(-1999, 7, 5, 23, 59, 59), 1, DateTime(-1999, 7, 6, 0, 0, 0)); + testDT(DateTime(-1999, 7, 5, 23, 59, 59), 0, DateTime(-1999, 7, 5, 23, 59, 59)); + testDT(DateTime(-1999, 7, 5, 23, 59, 59), -1, DateTime(-1999, 7, 5, 23, 59, 58)); + + testDT(DateTime(-2000, 12, 31, 23, 59, 59), 1, DateTime(-1999, 1, 1, 0, 0, 0)); + testDT(DateTime(-2000, 12, 31, 23, 59, 59), 0, DateTime(-2000, 12, 31, 23, 59, 59)); + testDT(DateTime(-2000, 12, 31, 23, 59, 59), -1, DateTime(-2000, 12, 31, 23, 59, 58)); + + testDT(DateTime(-2000, 1, 1, 0, 0, 0), 1, DateTime(-2000, 1, 1, 0, 0, 1)); + testDT(DateTime(-2000, 1, 1, 0, 0, 0), 0, DateTime(-2000, 1, 1, 0, 0, 0)); + testDT(DateTime(-2000, 1, 1, 0, 0, 0), -1, DateTime(-2001, 12, 31, 23, 59, 59)); + + //Test Both + testDT(DateTime(1, 1, 1, 0, 0, 0), -1, DateTime(0, 12, 31, 23, 59, 59)); + testDT(DateTime(0, 12, 31, 23, 59, 59), 1, DateTime(1, 1, 1, 0, 0, 0)); + + testDT(DateTime(0, 1, 1, 0, 0, 0), -1, DateTime(-1, 12, 31, 23, 59, 59)); + testDT(DateTime(-1, 12, 31, 23, 59, 59), 1, DateTime(0, 1, 1, 0, 0, 0)); + + testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_600L, DateTime(1, 1, 1, 13, 30, 33)); + testDT(DateTime(1, 1, 1, 13, 30, 33), -63_165_600L, DateTime(-1, 1, 1, 11, 30, 33)); + + testDT(DateTime(-1, 1, 1, 11, 30, 33), 63_165_617L, DateTime(1, 1, 1, 13, 30, 50)); + testDT(DateTime(1, 1, 1, 13, 30, 50), -63_165_617L, DateTime(-1, 1, 1, 11, 30, 33)); + + const cdt = DateTime(1999, 7, 6, 12, 30, 33); + immutable idt = DateTime(1999, 7, 6, 12, 30, 33); + static assert(!__traits(compiles, cdt._addSeconds(4))); + static assert(!__traits(compiles, idt._addSeconds(4))); } @@ -18370,11 +16997,10 @@ assert(Interval!Date(Date(1996, 1, 2), dur!"years"(3)) == Params: rhs = The $(LREF2 .Interval, Interval) to assign to this one. +/ - /+ref+/ Interval opAssign(const ref Interval rhs) pure nothrow + ref Interval opAssign(const ref Interval rhs) pure nothrow { _begin = cast(TP)rhs._begin; _end = cast(TP)rhs._end; - return this; } @@ -18383,11 +17009,10 @@ assert(Interval!Date(Date(1996, 1, 2), dur!"years"(3)) == Params: rhs = The $(LREF2 .Interval, Interval) to assign to this one. +/ - /+ref+/ Interval opAssign(Interval rhs) pure nothrow + ref Interval opAssign(Interval rhs) pure nothrow { _begin = cast(TP)rhs._begin; _end = cast(TP)rhs._end; - return this; } @@ -19789,1487 +18414,1418 @@ private: //Test Interval's constructors. unittest { - version(testStdDateTime) - { - assertThrown!DateTimeException(Interval!Date(Date(2010, 1, 1), Date(1, 1, 1))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 1, 1), Date(1, 1, 1))); - Interval!Date(Date.init, Date.init); - Interval!TimeOfDay(TimeOfDay.init, TimeOfDay.init); - Interval!DateTime(DateTime.init, DateTime.init); - Interval!SysTime(SysTime(0), SysTime(0)); + Interval!Date(Date.init, Date.init); + Interval!TimeOfDay(TimeOfDay.init, TimeOfDay.init); + Interval!DateTime(DateTime.init, DateTime.init); + Interval!SysTime(SysTime(0), SysTime(0)); - Interval!DateTime(DateTime.init, dur!"days"(7)); + Interval!DateTime(DateTime.init, dur!"days"(7)); - //Verify Examples. - Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - assert(Interval!Date(Date(1996, 1, 2), dur!"weeks"(3)) == Interval!Date(Date(1996, 1, 2), Date(1996, 1, 23))); - assert(Interval!Date(Date(1996, 1, 2), dur!"days"(3)) == Interval!Date(Date(1996, 1, 2), Date(1996, 1, 5))); - assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"hours"(3)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 15, 0, 0))); - assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"minutes"(3)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 3, 0))); - assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"seconds"(3)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 0, 3))); - assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"msecs"(3000)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 0, 3))); - } + //Verify Examples. + Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + assert(Interval!Date(Date(1996, 1, 2), dur!"weeks"(3)) == Interval!Date(Date(1996, 1, 2), Date(1996, 1, 23))); + assert(Interval!Date(Date(1996, 1, 2), dur!"days"(3)) == Interval!Date(Date(1996, 1, 2), Date(1996, 1, 5))); + assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"hours"(3)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 15, 0, 0))); + assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"minutes"(3)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 3, 0))); + assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"seconds"(3)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 0, 3))); + assert(Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), dur!"msecs"(3000)) == Interval!DateTime(DateTime(1996, 1, 2, 12, 0, 0), DateTime(1996, 1, 2, 12, 0, 3))); } //Test Interval's begin. unittest { - version(testStdDateTime) - { - _assertPred!"=="(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).begin, Date(1, 1, 1)); - _assertPred!"=="(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).begin, Date(2010, 1, 1)); - _assertPred!"=="(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).begin, Date(1997, 12, 31)); + assert(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).begin == Date(1, 1, 1)); + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).begin == Date(2010, 1, 1)); + assert(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).begin == Date(1997, 12, 31)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, cInterval.begin)); - static assert(__traits(compiles, iInterval.begin)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, cInterval.begin)); + static assert(__traits(compiles, iInterval.begin)); - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).begin == Date(1996, 1, 2)); - } + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).begin == Date(1996, 1, 2)); } //Test Interval's end. unittest { - version(testStdDateTime) - { - _assertPred!"=="(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).end, Date(2010, 1, 1)); - _assertPred!"=="(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).end, Date(2010, 1, 1)); - _assertPred!"=="(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).end, Date(1998, 1, 1)); + assert(Interval!Date(Date(1, 1, 1), Date(2010, 1, 1)).end == Date(2010, 1, 1)); + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).end == Date(2010, 1, 1)); + assert(Interval!Date(Date(1997, 12, 31), Date(1998, 1, 1)).end == Date(1998, 1, 1)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, cInterval.end)); - static assert(__traits(compiles, iInterval.end)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, cInterval.end)); + static assert(__traits(compiles, iInterval.end)); - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).end == Date(2012, 3, 1)); - } + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).end == Date(2012, 3, 1)); } //Test Interval's length. unittest { - version(testStdDateTime) - { - _assertPred!"=="(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).length, dur!"days"(0)); - _assertPred!"=="(Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).length, dur!"days"(90)); - _assertPred!"=="(Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).length, dur!"seconds"(42_727)); - _assertPred!"=="(Interval!DateTime(DateTime(2010, 1, 1, 0, 30, 0), DateTime(2010, 1, 2, 12, 22, 7)).length, dur!"seconds"(129_127)); - _assertPred!"=="(Interval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0)), SysTime(DateTime(2010, 1, 2, 12, 22, 7))).length, dur!"seconds"(129_127)); + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).length == dur!"days"(0)); + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).length == dur!"days"(90)); + assert(Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).length == dur!"seconds"(42_727)); + assert(Interval!DateTime(DateTime(2010, 1, 1, 0, 30, 0), DateTime(2010, 1, 2, 12, 22, 7)).length == dur!"seconds"(129_127)); + assert(Interval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0)), SysTime(DateTime(2010, 1, 2, 12, 22, 7))).length == dur!"seconds"(129_127)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, cInterval.length)); - static assert(__traits(compiles, iInterval.length)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, cInterval.length)); + static assert(__traits(compiles, iInterval.length)); - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).length == dur!"days"(5903)); - } + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).length == dur!"days"(5903)); } //Test Interval's empty. unittest { - version(testStdDateTime) - { - assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).empty); - assert(!Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).empty); - assert(!Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).empty); - assert(!Interval!DateTime(DateTime(2010, 1, 1, 0, 30, 0), DateTime(2010, 1, 2, 12, 22, 7)).empty); - assert(!Interval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0)), SysTime(DateTime(2010, 1, 2, 12, 22, 7))).empty); + assert(Interval!Date(Date(2010, 1, 1), Date(2010, 1, 1)).empty); + assert(!Interval!Date(Date(2010, 1, 1), Date(2010, 4, 1)).empty); + assert(!Interval!TimeOfDay(TimeOfDay(0, 30, 0), TimeOfDay(12, 22, 7)).empty); + assert(!Interval!DateTime(DateTime(2010, 1, 1, 0, 30, 0), DateTime(2010, 1, 2, 12, 22, 7)).empty); + assert(!Interval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0)), SysTime(DateTime(2010, 1, 2, 12, 22, 7))).empty); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, cInterval.empty)); - static assert(__traits(compiles, iInterval.empty)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, cInterval.empty)); + static assert(__traits(compiles, iInterval.empty)); - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(1996, 1, 2)).empty); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).empty); - } + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(1996, 1, 2)).empty); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).empty); } //Test Interval's contains(time point). unittest { - version(testStdDateTime) - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(Date(2010, 7, 4))); + + assert(!interval.contains(Date(2009, 7, 4))); + assert(!interval.contains(Date(2010, 7, 3))); + assert(interval.contains(Date(2010, 7, 4))); + assert(interval.contains(Date(2010, 7, 5))); + assert(interval.contains(Date(2011, 7, 1))); + assert(interval.contains(Date(2012, 1, 6))); + assert(!interval.contains(Date(2012, 1, 7))); + assert(!interval.contains(Date(2012, 1, 8))); + assert(!interval.contains(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, interval.contains(cdate))); + static assert(__traits(compiles, cInterval.contains(cdate))); + static assert(__traits(compiles, iInterval.contains(cdate))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(Date(2010, 7, 4))); - - assert(!interval.contains(Date(2009, 7, 4))); - assert(!interval.contains(Date(2010, 7, 3))); - assert(interval.contains(Date(2010, 7, 4))); - assert(interval.contains(Date(2010, 7, 5))); - assert(interval.contains(Date(2011, 7, 1))); - assert(interval.contains(Date(2012, 1, 6))); - assert(!interval.contains(Date(2012, 1, 7))); - assert(!interval.contains(Date(2012, 1, 8))); - assert(!interval.contains(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, interval.contains(cdate))); - static assert(__traits(compiles, cInterval.contains(cdate))); - static assert(__traits(compiles, iInterval.contains(cdate))); - - //Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(1994, 12, 24))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2000, 1, 5))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2012, 3, 1))); - } + //Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(1994, 12, 24))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2000, 1, 5))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Date(2012, 3, 1))); } //Test Interval's contains(Interval). unittest { - version(testStdDateTime) - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.contains(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(interval.contains(interval)); + assert(!interval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(interval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(interval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(interval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!interval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!interval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!interval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).contains(interval)); + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).contains(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).contains(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).contains(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).contains(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).contains(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).contains(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).contains(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).contains(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).contains(interval)); + assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).contains(interval)); + assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).contains(interval)); + + assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, interval.contains(interval))); + static assert(__traits(compiles, interval.contains(cInterval))); + static assert(__traits(compiles, interval.contains(iInterval))); + static assert(__traits(compiles, interval.contains(posInfInterval))); + static assert(__traits(compiles, interval.contains(cPosInfInterval))); + static assert(__traits(compiles, interval.contains(iPosInfInterval))); + static assert(__traits(compiles, interval.contains(negInfInterval))); + static assert(__traits(compiles, interval.contains(cNegInfInterval))); + static assert(__traits(compiles, interval.contains(iNegInfInterval))); + static assert(__traits(compiles, cInterval.contains(interval))); + static assert(__traits(compiles, cInterval.contains(cInterval))); + static assert(__traits(compiles, cInterval.contains(iInterval))); + static assert(__traits(compiles, cInterval.contains(posInfInterval))); + static assert(__traits(compiles, cInterval.contains(cPosInfInterval))); + static assert(__traits(compiles, cInterval.contains(iPosInfInterval))); + static assert(__traits(compiles, cInterval.contains(negInfInterval))); + static assert(__traits(compiles, cInterval.contains(cNegInfInterval))); + static assert(__traits(compiles, cInterval.contains(iNegInfInterval))); + static assert(__traits(compiles, iInterval.contains(interval))); + static assert(__traits(compiles, iInterval.contains(cInterval))); + static assert(__traits(compiles, iInterval.contains(iInterval))); + static assert(__traits(compiles, iInterval.contains(posInfInterval))); + static assert(__traits(compiles, iInterval.contains(cPosInfInterval))); + static assert(__traits(compiles, iInterval.contains(iPosInfInterval))); + static assert(__traits(compiles, iInterval.contains(negInfInterval))); + static assert(__traits(compiles, iInterval.contains(cNegInfInterval))); + static assert(__traits(compiles, iInterval.contains(iNegInfInterval))); - assertThrown!DateTimeException(interval.contains(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).contains(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(interval.contains(interval)); - assert(!interval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!interval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(interval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(interval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(interval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!interval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!interval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!interval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).contains(interval)); - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).contains(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).contains(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).contains(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).contains(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).contains(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).contains(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).contains(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).contains(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).contains(interval)); - assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).contains(interval)); - assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).contains(interval)); - - assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, interval.contains(interval))); - static assert(__traits(compiles, interval.contains(cInterval))); - static assert(__traits(compiles, interval.contains(iInterval))); - static assert(__traits(compiles, interval.contains(posInfInterval))); - static assert(__traits(compiles, interval.contains(cPosInfInterval))); - static assert(__traits(compiles, interval.contains(iPosInfInterval))); - static assert(__traits(compiles, interval.contains(negInfInterval))); - static assert(__traits(compiles, interval.contains(cNegInfInterval))); - static assert(__traits(compiles, interval.contains(iNegInfInterval))); - static assert(__traits(compiles, cInterval.contains(interval))); - static assert(__traits(compiles, cInterval.contains(cInterval))); - static assert(__traits(compiles, cInterval.contains(iInterval))); - static assert(__traits(compiles, cInterval.contains(posInfInterval))); - static assert(__traits(compiles, cInterval.contains(cPosInfInterval))); - static assert(__traits(compiles, cInterval.contains(iPosInfInterval))); - static assert(__traits(compiles, cInterval.contains(negInfInterval))); - static assert(__traits(compiles, cInterval.contains(cNegInfInterval))); - static assert(__traits(compiles, cInterval.contains(iNegInfInterval))); - static assert(__traits(compiles, iInterval.contains(interval))); - static assert(__traits(compiles, iInterval.contains(cInterval))); - static assert(__traits(compiles, iInterval.contains(iInterval))); - static assert(__traits(compiles, iInterval.contains(posInfInterval))); - static assert(__traits(compiles, iInterval.contains(cPosInfInterval))); - static assert(__traits(compiles, iInterval.contains(iPosInfInterval))); - static assert(__traits(compiles, iInterval.contains(negInfInterval))); - static assert(__traits(compiles, iInterval.contains(cNegInfInterval))); - static assert(__traits(compiles, iInterval.contains(iNegInfInterval))); - - //Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); - } + //Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); } //Test Interval's isBefore(time point). unittest { - version(testStdDateTime) - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(Date(2010, 7, 4))); + + assert(!interval.isBefore(Date(2009, 7, 3))); + assert(!interval.isBefore(Date(2010, 7, 3))); + assert(!interval.isBefore(Date(2010, 7, 4))); + assert(!interval.isBefore(Date(2010, 7, 5))); + assert(!interval.isBefore(Date(2011, 7, 1))); + assert(!interval.isBefore(Date(2012, 1, 6))); + assert(interval.isBefore(Date(2012, 1, 7))); + assert(interval.isBefore(Date(2012, 1, 8))); + assert(interval.isBefore(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, interval.isBefore(cdate))); + static assert(__traits(compiles, cInterval.isBefore(cdate))); + static assert(__traits(compiles, iInterval.isBefore(cdate))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(Date(2010, 7, 4))); - - assert(!interval.isBefore(Date(2009, 7, 3))); - assert(!interval.isBefore(Date(2010, 7, 3))); - assert(!interval.isBefore(Date(2010, 7, 4))); - assert(!interval.isBefore(Date(2010, 7, 5))); - assert(!interval.isBefore(Date(2011, 7, 1))); - assert(!interval.isBefore(Date(2012, 1, 6))); - assert(interval.isBefore(Date(2012, 1, 7))); - assert(interval.isBefore(Date(2012, 1, 8))); - assert(interval.isBefore(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, interval.isBefore(cdate))); - static assert(__traits(compiles, cInterval.isBefore(cdate))); - static assert(__traits(compiles, iInterval.isBefore(cdate))); - - //Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); - } + //Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); } //Test Interval's isBefore(Interval). unittest { - version(testStdDateTime) - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.isBefore(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!interval.isBefore(interval)); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!interval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!interval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!interval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(interval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(interval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isBefore(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isBefore(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isBefore(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isBefore(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isBefore(interval)); + assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isBefore(interval)); + assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isBefore(interval)); + + assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(interval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(interval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, interval.isBefore(interval))); + static assert(__traits(compiles, interval.isBefore(cInterval))); + static assert(__traits(compiles, interval.isBefore(iInterval))); + static assert(__traits(compiles, interval.isBefore(posInfInterval))); + static assert(__traits(compiles, interval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, interval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, interval.isBefore(negInfInterval))); + static assert(__traits(compiles, interval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, interval.isBefore(iNegInfInterval))); + static assert(__traits(compiles, cInterval.isBefore(interval))); + static assert(__traits(compiles, cInterval.isBefore(cInterval))); + static assert(__traits(compiles, cInterval.isBefore(iInterval))); + static assert(__traits(compiles, cInterval.isBefore(posInfInterval))); + static assert(__traits(compiles, cInterval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, cInterval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, cInterval.isBefore(negInfInterval))); + static assert(__traits(compiles, cInterval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, cInterval.isBefore(iNegInfInterval))); + static assert(__traits(compiles, iInterval.isBefore(interval))); + static assert(__traits(compiles, iInterval.isBefore(cInterval))); + static assert(__traits(compiles, iInterval.isBefore(iInterval))); + static assert(__traits(compiles, iInterval.isBefore(posInfInterval))); + static assert(__traits(compiles, iInterval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, iInterval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, iInterval.isBefore(negInfInterval))); + static assert(__traits(compiles, iInterval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, iInterval.isBefore(iNegInfInterval))); - assertThrown!DateTimeException(interval.isBefore(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isBefore(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!interval.isBefore(interval)); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!interval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!interval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!interval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(interval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(interval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isBefore(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isBefore(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isBefore(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isBefore(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isBefore(interval)); - assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isBefore(interval)); - assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isBefore(interval)); - - assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(interval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(interval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, interval.isBefore(interval))); - static assert(__traits(compiles, interval.isBefore(cInterval))); - static assert(__traits(compiles, interval.isBefore(iInterval))); - static assert(__traits(compiles, interval.isBefore(posInfInterval))); - static assert(__traits(compiles, interval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, interval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, interval.isBefore(negInfInterval))); - static assert(__traits(compiles, interval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, interval.isBefore(iNegInfInterval))); - static assert(__traits(compiles, cInterval.isBefore(interval))); - static assert(__traits(compiles, cInterval.isBefore(cInterval))); - static assert(__traits(compiles, cInterval.isBefore(iInterval))); - static assert(__traits(compiles, cInterval.isBefore(posInfInterval))); - static assert(__traits(compiles, cInterval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, cInterval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, cInterval.isBefore(negInfInterval))); - static assert(__traits(compiles, cInterval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, cInterval.isBefore(iNegInfInterval))); - static assert(__traits(compiles, iInterval.isBefore(interval))); - static assert(__traits(compiles, iInterval.isBefore(cInterval))); - static assert(__traits(compiles, iInterval.isBefore(iInterval))); - static assert(__traits(compiles, iInterval.isBefore(posInfInterval))); - static assert(__traits(compiles, iInterval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, iInterval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, iInterval.isBefore(negInfInterval))); - static assert(__traits(compiles, iInterval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, iInterval.isBefore(iNegInfInterval))); - - //Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 1)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); - } + //Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 1)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); } //Test Interval's isAfter(time point). unittest { - version(testStdDateTime) - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(Date(2010, 7, 4))); + + assert(interval.isAfter(Date(2009, 7, 4))); + assert(interval.isAfter(Date(2010, 7, 3))); + assert(!interval.isAfter(Date(2010, 7, 4))); + assert(!interval.isAfter(Date(2010, 7, 5))); + assert(!interval.isAfter(Date(2011, 7, 1))); + assert(!interval.isAfter(Date(2012, 1, 6))); + assert(!interval.isAfter(Date(2012, 1, 7))); + assert(!interval.isAfter(Date(2012, 1, 8))); + assert(!interval.isAfter(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, interval.isAfter(cdate))); + static assert(__traits(compiles, cInterval.isAfter(cdate))); + static assert(__traits(compiles, iInterval.isAfter(cdate))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(Date(2010, 7, 4))); - - assert(interval.isAfter(Date(2009, 7, 4))); - assert(interval.isAfter(Date(2010, 7, 3))); - assert(!interval.isAfter(Date(2010, 7, 4))); - assert(!interval.isAfter(Date(2010, 7, 5))); - assert(!interval.isAfter(Date(2011, 7, 1))); - assert(!interval.isAfter(Date(2012, 1, 6))); - assert(!interval.isAfter(Date(2012, 1, 7))); - assert(!interval.isAfter(Date(2012, 1, 8))); - assert(!interval.isAfter(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, interval.isAfter(cdate))); - static assert(__traits(compiles, cInterval.isAfter(cdate))); - static assert(__traits(compiles, iInterval.isAfter(cdate))); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); - } + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); } //Test Interval's isAfter(Interval). unittest { - version(testStdDateTime) - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.isAfter(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!interval.isAfter(interval)); + assert(interval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!interval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!interval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!interval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!interval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!interval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isAfter(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isAfter(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isAfter(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isAfter(interval)); + assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isAfter(interval)); + assert(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isAfter(interval)); + + assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(interval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(interval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, interval.isAfter(interval))); + static assert(__traits(compiles, interval.isAfter(cInterval))); + static assert(__traits(compiles, interval.isAfter(iInterval))); + static assert(__traits(compiles, interval.isAfter(posInfInterval))); + static assert(__traits(compiles, interval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, interval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, interval.isAfter(negInfInterval))); + static assert(__traits(compiles, interval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, interval.isAfter(iNegInfInterval))); + static assert(__traits(compiles, cInterval.isAfter(interval))); + static assert(__traits(compiles, cInterval.isAfter(cInterval))); + static assert(__traits(compiles, cInterval.isAfter(iInterval))); + static assert(__traits(compiles, cInterval.isAfter(posInfInterval))); + static assert(__traits(compiles, cInterval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, cInterval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, cInterval.isAfter(negInfInterval))); + static assert(__traits(compiles, cInterval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, cInterval.isAfter(iNegInfInterval))); + static assert(__traits(compiles, iInterval.isAfter(interval))); + static assert(__traits(compiles, iInterval.isAfter(cInterval))); + static assert(__traits(compiles, iInterval.isAfter(iInterval))); + static assert(__traits(compiles, iInterval.isAfter(posInfInterval))); + static assert(__traits(compiles, iInterval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, iInterval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, iInterval.isAfter(negInfInterval))); + static assert(__traits(compiles, iInterval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, iInterval.isAfter(iNegInfInterval))); - assertThrown!DateTimeException(interval.isAfter(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).isAfter(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!interval.isAfter(interval)); - assert(interval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!interval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!interval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!interval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!interval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!interval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isAfter(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isAfter(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isAfter(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isAfter(interval)); - assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isAfter(interval)); - assert(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isAfter(interval)); - - assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(interval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(interval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, interval.isAfter(interval))); - static assert(__traits(compiles, interval.isAfter(cInterval))); - static assert(__traits(compiles, interval.isAfter(iInterval))); - static assert(__traits(compiles, interval.isAfter(posInfInterval))); - static assert(__traits(compiles, interval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, interval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, interval.isAfter(negInfInterval))); - static assert(__traits(compiles, interval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, interval.isAfter(iNegInfInterval))); - static assert(__traits(compiles, cInterval.isAfter(interval))); - static assert(__traits(compiles, cInterval.isAfter(cInterval))); - static assert(__traits(compiles, cInterval.isAfter(iInterval))); - static assert(__traits(compiles, cInterval.isAfter(posInfInterval))); - static assert(__traits(compiles, cInterval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, cInterval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, cInterval.isAfter(negInfInterval))); - static assert(__traits(compiles, cInterval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, cInterval.isAfter(iNegInfInterval))); - static assert(__traits(compiles, iInterval.isAfter(interval))); - static assert(__traits(compiles, iInterval.isAfter(cInterval))); - static assert(__traits(compiles, iInterval.isAfter(iInterval))); - static assert(__traits(compiles, iInterval.isAfter(posInfInterval))); - static assert(__traits(compiles, iInterval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, iInterval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, iInterval.isAfter(negInfInterval))); - static assert(__traits(compiles, iInterval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, iInterval.isAfter(iNegInfInterval))); - - //Verify Examples. - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); - } + //Verify Examples. + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); } //Test Interval's intersects(). unittest { - version(testStdDateTime) - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.intersects(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersects(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersects(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(interval.intersects(interval)); + assert(!interval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(interval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(interval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(interval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!interval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!interval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).intersects(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).intersects(interval)); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).intersects(interval)); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).intersects(interval)); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).intersects(interval)); + assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).intersects(interval)); + assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).intersects(interval)); + + assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(interval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!interval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(interval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, interval.intersects(interval))); + static assert(__traits(compiles, interval.intersects(cInterval))); + static assert(__traits(compiles, interval.intersects(iInterval))); + static assert(__traits(compiles, interval.intersects(posInfInterval))); + static assert(__traits(compiles, interval.intersects(cPosInfInterval))); + static assert(__traits(compiles, interval.intersects(iPosInfInterval))); + static assert(__traits(compiles, interval.intersects(negInfInterval))); + static assert(__traits(compiles, interval.intersects(cNegInfInterval))); + static assert(__traits(compiles, interval.intersects(iNegInfInterval))); + static assert(__traits(compiles, cInterval.intersects(interval))); + static assert(__traits(compiles, cInterval.intersects(cInterval))); + static assert(__traits(compiles, cInterval.intersects(iInterval))); + static assert(__traits(compiles, cInterval.intersects(posInfInterval))); + static assert(__traits(compiles, cInterval.intersects(cPosInfInterval))); + static assert(__traits(compiles, cInterval.intersects(iPosInfInterval))); + static assert(__traits(compiles, cInterval.intersects(negInfInterval))); + static assert(__traits(compiles, cInterval.intersects(cNegInfInterval))); + static assert(__traits(compiles, cInterval.intersects(iNegInfInterval))); + static assert(__traits(compiles, iInterval.intersects(interval))); + static assert(__traits(compiles, iInterval.intersects(cInterval))); + static assert(__traits(compiles, iInterval.intersects(iInterval))); + static assert(__traits(compiles, iInterval.intersects(posInfInterval))); + static assert(__traits(compiles, iInterval.intersects(cPosInfInterval))); + static assert(__traits(compiles, iInterval.intersects(iPosInfInterval))); + static assert(__traits(compiles, iInterval.intersects(negInfInterval))); + static assert(__traits(compiles, iInterval.intersects(cNegInfInterval))); + static assert(__traits(compiles, iInterval.intersects(iNegInfInterval))); - assertThrown!DateTimeException(interval.intersects(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersects(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersects(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(interval.intersects(interval)); - assert(!interval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(interval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(interval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(interval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!interval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!interval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).intersects(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).intersects(interval)); - assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).intersects(interval)); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).intersects(interval)); - assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).intersects(interval)); - assert(!Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).intersects(interval)); - assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).intersects(interval)); - - assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(interval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(interval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!interval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(interval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(interval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, interval.intersects(interval))); - static assert(__traits(compiles, interval.intersects(cInterval))); - static assert(__traits(compiles, interval.intersects(iInterval))); - static assert(__traits(compiles, interval.intersects(posInfInterval))); - static assert(__traits(compiles, interval.intersects(cPosInfInterval))); - static assert(__traits(compiles, interval.intersects(iPosInfInterval))); - static assert(__traits(compiles, interval.intersects(negInfInterval))); - static assert(__traits(compiles, interval.intersects(cNegInfInterval))); - static assert(__traits(compiles, interval.intersects(iNegInfInterval))); - static assert(__traits(compiles, cInterval.intersects(interval))); - static assert(__traits(compiles, cInterval.intersects(cInterval))); - static assert(__traits(compiles, cInterval.intersects(iInterval))); - static assert(__traits(compiles, cInterval.intersects(posInfInterval))); - static assert(__traits(compiles, cInterval.intersects(cPosInfInterval))); - static assert(__traits(compiles, cInterval.intersects(iPosInfInterval))); - static assert(__traits(compiles, cInterval.intersects(negInfInterval))); - static assert(__traits(compiles, cInterval.intersects(cNegInfInterval))); - static assert(__traits(compiles, cInterval.intersects(iNegInfInterval))); - static assert(__traits(compiles, iInterval.intersects(interval))); - static assert(__traits(compiles, iInterval.intersects(cInterval))); - static assert(__traits(compiles, iInterval.intersects(iInterval))); - static assert(__traits(compiles, iInterval.intersects(posInfInterval))); - static assert(__traits(compiles, iInterval.intersects(cPosInfInterval))); - static assert(__traits(compiles, iInterval.intersects(iPosInfInterval))); - static assert(__traits(compiles, iInterval.intersects(negInfInterval))); - static assert(__traits(compiles, iInterval.intersects(cNegInfInterval))); - static assert(__traits(compiles, iInterval.intersects(iNegInfInterval))); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2000, 1, 2)))); - } + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2000, 1, 2)))); } //Test Interval's intersection(). unittest { - version(testStdDateTime) - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersection(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersection(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).intersection(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).intersection(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).intersection(interval)); + assertThrown!DateTimeException(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).intersection(interval)); + + assertThrown!DateTimeException(interval.intersection(PosInfInterval!Date(Date(2012, 1, 7)))); + assertThrown!DateTimeException(interval.intersection(PosInfInterval!Date(Date(2012, 1, 8)))); + + assertThrown!DateTimeException(interval.intersection(NegInfInterval!Date(Date(2010, 7, 3)))); + assertThrown!DateTimeException(interval.intersection(NegInfInterval!Date(Date(2010, 7, 4)))); + + assert(interval.intersection(interval) == interval); + assert(interval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); + assert(interval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assert(interval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + assert(interval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).intersection(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).intersection(interval) == + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).intersection(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).intersection(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).intersection(interval) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).intersection(interval) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).intersection(interval) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).intersection(interval) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + + assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assert(interval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + + assert(interval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 6))); + assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, interval.intersection(interval))); + static assert(__traits(compiles, interval.intersection(cInterval))); + static assert(__traits(compiles, interval.intersection(iInterval))); + static assert(__traits(compiles, interval.intersection(posInfInterval))); + static assert(__traits(compiles, interval.intersection(cPosInfInterval))); + static assert(__traits(compiles, interval.intersection(iPosInfInterval))); + static assert(__traits(compiles, interval.intersection(negInfInterval))); + static assert(__traits(compiles, interval.intersection(cNegInfInterval))); + static assert(__traits(compiles, interval.intersection(iNegInfInterval))); + static assert(__traits(compiles, cInterval.intersection(interval))); + static assert(__traits(compiles, cInterval.intersection(cInterval))); + static assert(__traits(compiles, cInterval.intersection(iInterval))); + static assert(__traits(compiles, cInterval.intersection(posInfInterval))); + static assert(__traits(compiles, cInterval.intersection(cPosInfInterval))); + static assert(__traits(compiles, cInterval.intersection(iPosInfInterval))); + static assert(__traits(compiles, cInterval.intersection(negInfInterval))); + static assert(__traits(compiles, cInterval.intersection(cNegInfInterval))); + static assert(__traits(compiles, cInterval.intersection(iNegInfInterval))); + static assert(__traits(compiles, iInterval.intersection(interval))); + static assert(__traits(compiles, iInterval.intersection(cInterval))); + static assert(__traits(compiles, iInterval.intersection(iInterval))); + static assert(__traits(compiles, iInterval.intersection(posInfInterval))); + static assert(__traits(compiles, iInterval.intersection(cPosInfInterval))); + static assert(__traits(compiles, iInterval.intersection(iPosInfInterval))); + static assert(__traits(compiles, iInterval.intersection(negInfInterval))); + static assert(__traits(compiles, iInterval.intersection(cNegInfInterval))); + static assert(__traits(compiles, iInterval.intersection(iNegInfInterval))); - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersection(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 4), dur!"days"(0)).intersection(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assertThrown!DateTimeException(interval.intersection(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).intersection(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).intersection(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).intersection(interval)); - assertThrown!DateTimeException(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).intersection(interval)); - - assertThrown!DateTimeException(interval.intersection(PosInfInterval!Date(Date(2012, 1, 7)))); - assertThrown!DateTimeException(interval.intersection(PosInfInterval!Date(Date(2012, 1, 8)))); - - assertThrown!DateTimeException(interval.intersection(NegInfInterval!Date(Date(2010, 7, 3)))); - assertThrown!DateTimeException(interval.intersection(NegInfInterval!Date(Date(2010, 7, 4)))); - - _assertPred!"=="(interval.intersection(interval), interval); - _assertPred!"=="(interval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - _assertPred!"=="(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(interval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(interval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); - _assertPred!"=="(interval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - _assertPred!"=="(interval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - _assertPred!"=="(interval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - - _assertPred!"=="(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).intersection(interval), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).intersection(interval), - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).intersection(interval), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).intersection(interval), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).intersection(interval), - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).intersection(interval), - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).intersection(interval), - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).intersection(interval), - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - - _assertPred!"=="(interval.intersection(PosInfInterval!Date(Date(2010, 7, 3))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(interval.intersection(PosInfInterval!Date(Date(2010, 7, 4))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(interval.intersection(PosInfInterval!Date(Date(2010, 7, 5))), - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - _assertPred!"=="(interval.intersection(PosInfInterval!Date(Date(2012, 1, 6))), - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - - _assertPred!"=="(interval.intersection(NegInfInterval!Date(Date(2010, 7, 5))), - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - _assertPred!"=="(interval.intersection(NegInfInterval!Date(Date(2012, 1, 6))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 6))); - _assertPred!"=="(interval.intersection(NegInfInterval!Date(Date(2012, 1, 7))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(interval.intersection(NegInfInterval!Date(Date(2012, 1, 8))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, interval.intersection(interval))); - static assert(__traits(compiles, interval.intersection(cInterval))); - static assert(__traits(compiles, interval.intersection(iInterval))); - static assert(__traits(compiles, interval.intersection(posInfInterval))); - static assert(__traits(compiles, interval.intersection(cPosInfInterval))); - static assert(__traits(compiles, interval.intersection(iPosInfInterval))); - static assert(__traits(compiles, interval.intersection(negInfInterval))); - static assert(__traits(compiles, interval.intersection(cNegInfInterval))); - static assert(__traits(compiles, interval.intersection(iNegInfInterval))); - static assert(__traits(compiles, cInterval.intersection(interval))); - static assert(__traits(compiles, cInterval.intersection(cInterval))); - static assert(__traits(compiles, cInterval.intersection(iInterval))); - static assert(__traits(compiles, cInterval.intersection(posInfInterval))); - static assert(__traits(compiles, cInterval.intersection(cPosInfInterval))); - static assert(__traits(compiles, cInterval.intersection(iPosInfInterval))); - static assert(__traits(compiles, cInterval.intersection(negInfInterval))); - static assert(__traits(compiles, cInterval.intersection(cNegInfInterval))); - static assert(__traits(compiles, cInterval.intersection(iNegInfInterval))); - static assert(__traits(compiles, iInterval.intersection(interval))); - static assert(__traits(compiles, iInterval.intersection(cInterval))); - static assert(__traits(compiles, iInterval.intersection(iInterval))); - static assert(__traits(compiles, iInterval.intersection(posInfInterval))); - static assert(__traits(compiles, iInterval.intersection(cPosInfInterval))); - static assert(__traits(compiles, iInterval.intersection(iPosInfInterval))); - static assert(__traits(compiles, iInterval.intersection(negInfInterval))); - static assert(__traits(compiles, iInterval.intersection(cNegInfInterval))); - static assert(__traits(compiles, iInterval.intersection(iNegInfInterval))); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); - } + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == Interval!Date(Date(1996, 1 , 2), Date(2012, 3, 1))); } //Test Interval's isAdjacent(). unittest { - version(testStdDateTime) - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testInterval(in Interval!Date interval1, in Interval!Date interval2) + { + interval1.isAdjacent(interval2); + } + + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!interval.isAdjacent(interval)); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(interval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isAdjacent(interval)); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isAdjacent(interval)); + assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isAdjacent(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isAdjacent(interval)); + assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isAdjacent(interval)); + assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isAdjacent(interval)); + assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isAdjacent(interval)); + + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, interval.isAdjacent(interval))); + static assert(__traits(compiles, interval.isAdjacent(cInterval))); + static assert(__traits(compiles, interval.isAdjacent(iInterval))); + static assert(__traits(compiles, interval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, interval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, interval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, interval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, interval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, interval.isAdjacent(iNegInfInterval))); + static assert(__traits(compiles, cInterval.isAdjacent(interval))); + static assert(__traits(compiles, cInterval.isAdjacent(cInterval))); + static assert(__traits(compiles, cInterval.isAdjacent(iInterval))); + static assert(__traits(compiles, cInterval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, cInterval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, cInterval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, cInterval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, cInterval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, cInterval.isAdjacent(iNegInfInterval))); + static assert(__traits(compiles, iInterval.isAdjacent(interval))); + static assert(__traits(compiles, iInterval.isAdjacent(cInterval))); + static assert(__traits(compiles, iInterval.isAdjacent(iInterval))); + static assert(__traits(compiles, iInterval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, iInterval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, iInterval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, iInterval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, iInterval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, iInterval.isAdjacent(iNegInfInterval))); - static void testInterval(in Interval!Date interval1, in Interval!Date interval2) - { - interval1.isAdjacent(interval2); - } - - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!interval.isAdjacent(interval)); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!interval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(interval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!interval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).isAdjacent(interval)); - assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).isAdjacent(interval)); - assert(!Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).isAdjacent(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).isAdjacent(interval)); - assert(!Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).isAdjacent(interval)); - assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).isAdjacent(interval)); - assert(!Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).isAdjacent(interval)); - - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!interval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, interval.isAdjacent(interval))); - static assert(__traits(compiles, interval.isAdjacent(cInterval))); - static assert(__traits(compiles, interval.isAdjacent(iInterval))); - static assert(__traits(compiles, interval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, interval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, interval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, interval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, interval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, interval.isAdjacent(iNegInfInterval))); - static assert(__traits(compiles, cInterval.isAdjacent(interval))); - static assert(__traits(compiles, cInterval.isAdjacent(cInterval))); - static assert(__traits(compiles, cInterval.isAdjacent(iInterval))); - static assert(__traits(compiles, cInterval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, cInterval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, cInterval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, cInterval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, cInterval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, cInterval.isAdjacent(iNegInfInterval))); - static assert(__traits(compiles, iInterval.isAdjacent(interval))); - static assert(__traits(compiles, iInterval.isAdjacent(cInterval))); - static assert(__traits(compiles, iInterval.isAdjacent(iInterval))); - static assert(__traits(compiles, iInterval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, iInterval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, iInterval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, iInterval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, iInterval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, iInterval.isAdjacent(iNegInfInterval))); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1990, 7, 6), Date(1996, 1, 2)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2012, 3, 1), Date(2013, 9, 17)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1989, 3, 1), Date(2012, 3, 1)))); - - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(2000, 1, 2)))); - } + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1990, 7, 6), Date(1996, 1, 2)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2012, 3, 1), Date(2013, 9, 17)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1989, 3, 1), Date(2012, 3, 1)))); + + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(!Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(2000, 1, 2)))); } //Test Interval's merge(). unittest { - version(testStdDateTime) - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testInterval(I)(in Interval!Date interval1, in I interval2) + { + interval1.merge(interval2); + } + + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)), interval)); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)), interval)); + + assertThrown!DateTimeException(testInterval(interval, PosInfInterval!Date(Date(2012, 1, 8)))); + + assertThrown!DateTimeException(testInterval(interval, NegInfInterval!Date(Date(2010, 7, 3)))); + + assert(interval.merge(interval) == interval); + assert(interval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); + assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); + assert(interval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(interval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).merge(interval) == + Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).merge(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).merge(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).merge(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).merge(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).merge(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).merge(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).merge(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).merge(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).merge(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + + assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 4))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.merge(PosInfInterval!Date(Date(2010, 7, 5))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.merge(PosInfInterval!Date(Date(2012, 1, 6))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.merge(PosInfInterval!Date(Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + + assert(interval.merge(NegInfInterval!Date(Date(2010, 7, 4))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.merge(NegInfInterval!Date(Date(2010, 7, 5))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 6))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.merge(NegInfInterval!Date(Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, interval.merge(interval))); + static assert(__traits(compiles, interval.merge(cInterval))); + static assert(__traits(compiles, interval.merge(iInterval))); + static assert(__traits(compiles, interval.merge(posInfInterval))); + static assert(__traits(compiles, interval.merge(cPosInfInterval))); + static assert(__traits(compiles, interval.merge(iPosInfInterval))); + static assert(__traits(compiles, interval.merge(negInfInterval))); + static assert(__traits(compiles, interval.merge(cNegInfInterval))); + static assert(__traits(compiles, interval.merge(iNegInfInterval))); + static assert(__traits(compiles, cInterval.merge(interval))); + static assert(__traits(compiles, cInterval.merge(cInterval))); + static assert(__traits(compiles, cInterval.merge(iInterval))); + static assert(__traits(compiles, cInterval.merge(posInfInterval))); + static assert(__traits(compiles, cInterval.merge(cPosInfInterval))); + static assert(__traits(compiles, cInterval.merge(iPosInfInterval))); + static assert(__traits(compiles, cInterval.merge(negInfInterval))); + static assert(__traits(compiles, cInterval.merge(cNegInfInterval))); + static assert(__traits(compiles, cInterval.merge(iNegInfInterval))); + static assert(__traits(compiles, iInterval.merge(interval))); + static assert(__traits(compiles, iInterval.merge(cInterval))); + static assert(__traits(compiles, iInterval.merge(iInterval))); + static assert(__traits(compiles, iInterval.merge(posInfInterval))); + static assert(__traits(compiles, iInterval.merge(cPosInfInterval))); + static assert(__traits(compiles, iInterval.merge(iPosInfInterval))); + static assert(__traits(compiles, iInterval.merge(negInfInterval))); + static assert(__traits(compiles, iInterval.merge(cNegInfInterval))); + static assert(__traits(compiles, iInterval.merge(iNegInfInterval))); - static void testInterval(I)(in Interval!Date interval1, in I interval2) - { - interval1.merge(interval2); - } - - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)), interval)); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)), interval)); - - assertThrown!DateTimeException(testInterval(interval, PosInfInterval!Date(Date(2012, 1, 8)))); - - assertThrown!DateTimeException(testInterval(interval, NegInfInterval!Date(Date(2010, 7, 3)))); - - _assertPred!"=="(interval.merge(interval), interval); - _assertPred!"=="(interval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), - Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); - _assertPred!"=="(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - _assertPred!"=="(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - _assertPred!"=="(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - _assertPred!"=="(interval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); - _assertPred!"=="(interval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(interval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(interval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(interval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - _assertPred!"=="(interval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - - _assertPred!"=="(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).merge(interval), - Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).merge(interval), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).merge(interval), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).merge(interval), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).merge(interval), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).merge(interval), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).merge(interval), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).merge(interval), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).merge(interval), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - _assertPred!"=="(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).merge(interval), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - - _assertPred!"=="(interval.merge(PosInfInterval!Date(Date(2010, 7, 3))), - PosInfInterval!Date(Date(2010, 7, 3))); - _assertPred!"=="(interval.merge(PosInfInterval!Date(Date(2010, 7, 4))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(interval.merge(PosInfInterval!Date(Date(2010, 7, 5))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(interval.merge(PosInfInterval!Date(Date(2012, 1, 6))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(interval.merge(PosInfInterval!Date(Date(2012, 1, 7))), - PosInfInterval!Date(Date(2010, 7, 4))); - - _assertPred!"=="(interval.merge(NegInfInterval!Date(Date(2010, 7, 4))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(interval.merge(NegInfInterval!Date(Date(2010, 7, 5))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(interval.merge(NegInfInterval!Date(Date(2012, 1, 6))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(interval.merge(NegInfInterval!Date(Date(2012, 1, 7))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(interval.merge(NegInfInterval!Date(Date(2012, 1, 8))), - NegInfInterval!Date(Date(2012, 1, 8))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, interval.merge(interval))); - static assert(__traits(compiles, interval.merge(cInterval))); - static assert(__traits(compiles, interval.merge(iInterval))); - static assert(__traits(compiles, interval.merge(posInfInterval))); - static assert(__traits(compiles, interval.merge(cPosInfInterval))); - static assert(__traits(compiles, interval.merge(iPosInfInterval))); - static assert(__traits(compiles, interval.merge(negInfInterval))); - static assert(__traits(compiles, interval.merge(cNegInfInterval))); - static assert(__traits(compiles, interval.merge(iNegInfInterval))); - static assert(__traits(compiles, cInterval.merge(interval))); - static assert(__traits(compiles, cInterval.merge(cInterval))); - static assert(__traits(compiles, cInterval.merge(iInterval))); - static assert(__traits(compiles, cInterval.merge(posInfInterval))); - static assert(__traits(compiles, cInterval.merge(cPosInfInterval))); - static assert(__traits(compiles, cInterval.merge(iPosInfInterval))); - static assert(__traits(compiles, cInterval.merge(negInfInterval))); - static assert(__traits(compiles, cInterval.merge(cNegInfInterval))); - static assert(__traits(compiles, cInterval.merge(iNegInfInterval))); - static assert(__traits(compiles, iInterval.merge(interval))); - static assert(__traits(compiles, iInterval.merge(cInterval))); - static assert(__traits(compiles, iInterval.merge(iInterval))); - static assert(__traits(compiles, iInterval.merge(posInfInterval))); - static assert(__traits(compiles, iInterval.merge(cPosInfInterval))); - static assert(__traits(compiles, iInterval.merge(iPosInfInterval))); - static assert(__traits(compiles, iInterval.merge(negInfInterval))); - static assert(__traits(compiles, iInterval.merge(cNegInfInterval))); - static assert(__traits(compiles, iInterval.merge(iNegInfInterval))); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(PosInfInterval!Date(Date(2012, 3, 1))) == PosInfInterval!Date(Date(1996, 1 , 2))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1996, 1, 2))) == NegInfInterval!Date(Date(2012, 3 , 1))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); - } + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(PosInfInterval!Date(Date(2012, 3, 1))) == PosInfInterval!Date(Date(1996, 1 , 2))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1996, 1, 2))) == NegInfInterval!Date(Date(2012, 3 , 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); } //Test Interval's span(). unittest { - version(testStdDateTime) - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + + static void testInterval(in Interval!Date interval1, in Interval!Date interval2) + { + interval1.span(interval2); + } + + assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); + assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(interval.span(interval) == interval); + assert(interval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == + Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); + assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); + assert(interval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(interval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(interval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(interval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); + + assert(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).span(interval) == + Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).span(interval) == + Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).span(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).span(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).span(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).span(interval) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).span(interval) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); + + assert(interval.span(PosInfInterval!Date(Date(2010, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(interval.span(PosInfInterval!Date(Date(2010, 7, 4))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2010, 7, 5))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2012, 1, 6))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(interval.span(PosInfInterval!Date(Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 4))); + + assert(interval.span(NegInfInterval!Date(Date(2010, 7, 3))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2010, 7, 4))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2010, 7, 5))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2012, 1, 6))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(interval.span(NegInfInterval!Date(Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, interval.span(interval))); + static assert(__traits(compiles, interval.span(cInterval))); + static assert(__traits(compiles, interval.span(iInterval))); + static assert(__traits(compiles, interval.span(posInfInterval))); + static assert(__traits(compiles, interval.span(cPosInfInterval))); + static assert(__traits(compiles, interval.span(iPosInfInterval))); + static assert(__traits(compiles, interval.span(negInfInterval))); + static assert(__traits(compiles, interval.span(cNegInfInterval))); + static assert(__traits(compiles, interval.span(iNegInfInterval))); + static assert(__traits(compiles, cInterval.span(interval))); + static assert(__traits(compiles, cInterval.span(cInterval))); + static assert(__traits(compiles, cInterval.span(iInterval))); + static assert(__traits(compiles, cInterval.span(posInfInterval))); + static assert(__traits(compiles, cInterval.span(cPosInfInterval))); + static assert(__traits(compiles, cInterval.span(iPosInfInterval))); + static assert(__traits(compiles, cInterval.span(negInfInterval))); + static assert(__traits(compiles, cInterval.span(cNegInfInterval))); + static assert(__traits(compiles, cInterval.span(iNegInfInterval))); + static assert(__traits(compiles, iInterval.span(interval))); + static assert(__traits(compiles, iInterval.span(cInterval))); + static assert(__traits(compiles, iInterval.span(iInterval))); + static assert(__traits(compiles, iInterval.span(posInfInterval))); + static assert(__traits(compiles, iInterval.span(cPosInfInterval))); + static assert(__traits(compiles, iInterval.span(iPosInfInterval))); + static assert(__traits(compiles, iInterval.span(negInfInterval))); + static assert(__traits(compiles, iInterval.span(cNegInfInterval))); + static assert(__traits(compiles, iInterval.span(iNegInfInterval))); - static void testInterval(in Interval!Date interval1, in Interval!Date interval2) - { - interval1.span(interval2); - } - - assertThrown!DateTimeException(testInterval(interval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), interval)); - assertThrown!DateTimeException(testInterval(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - _assertPred!"=="(interval.span(interval), interval); - _assertPred!"=="(interval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))), - Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); - _assertPred!"=="(interval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), - Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); - _assertPred!"=="(interval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - _assertPred!"=="(interval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - _assertPred!"=="(interval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - _assertPred!"=="(interval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); - _assertPred!"=="(interval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(interval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(interval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(interval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - _assertPred!"=="(interval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - _assertPred!"=="(interval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); - - _assertPred!"=="(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)).span(interval), - Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)).span(interval), - Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)).span(interval), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)).span(interval), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)).span(interval), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)).span(interval), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)).span(interval), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)).span(interval), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)).span(interval), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)).span(interval), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - _assertPred!"=="(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)).span(interval), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - _assertPred!"=="(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)).span(interval), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 9))); - - _assertPred!"=="(interval.span(PosInfInterval!Date(Date(2010, 7, 3))), - PosInfInterval!Date(Date(2010, 7, 3))); - _assertPred!"=="(interval.span(PosInfInterval!Date(Date(2010, 7, 4))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(interval.span(PosInfInterval!Date(Date(2010, 7, 5))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(interval.span(PosInfInterval!Date(Date(2012, 1, 6))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(interval.span(PosInfInterval!Date(Date(2012, 1, 7))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(interval.span(PosInfInterval!Date(Date(2012, 1, 8))), - PosInfInterval!Date(Date(2010, 7, 4))); - - _assertPred!"=="(interval.span(NegInfInterval!Date(Date(2010, 7, 3))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(interval.span(NegInfInterval!Date(Date(2010, 7, 4))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(interval.span(NegInfInterval!Date(Date(2010, 7, 5))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(interval.span(NegInfInterval!Date(Date(2012, 1, 6))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(interval.span(NegInfInterval!Date(Date(2012, 1, 7))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(interval.span(NegInfInterval!Date(Date(2012, 1, 8))), - NegInfInterval!Date(Date(2012, 1, 8))); - - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, interval.span(interval))); - static assert(__traits(compiles, interval.span(cInterval))); - static assert(__traits(compiles, interval.span(iInterval))); - static assert(__traits(compiles, interval.span(posInfInterval))); - static assert(__traits(compiles, interval.span(cPosInfInterval))); - static assert(__traits(compiles, interval.span(iPosInfInterval))); - static assert(__traits(compiles, interval.span(negInfInterval))); - static assert(__traits(compiles, interval.span(cNegInfInterval))); - static assert(__traits(compiles, interval.span(iNegInfInterval))); - static assert(__traits(compiles, cInterval.span(interval))); - static assert(__traits(compiles, cInterval.span(cInterval))); - static assert(__traits(compiles, cInterval.span(iInterval))); - static assert(__traits(compiles, cInterval.span(posInfInterval))); - static assert(__traits(compiles, cInterval.span(cPosInfInterval))); - static assert(__traits(compiles, cInterval.span(iPosInfInterval))); - static assert(__traits(compiles, cInterval.span(negInfInterval))); - static assert(__traits(compiles, cInterval.span(cNegInfInterval))); - static assert(__traits(compiles, cInterval.span(iNegInfInterval))); - static assert(__traits(compiles, iInterval.span(interval))); - static assert(__traits(compiles, iInterval.span(cInterval))); - static assert(__traits(compiles, iInterval.span(iInterval))); - static assert(__traits(compiles, iInterval.span(posInfInterval))); - static assert(__traits(compiles, iInterval.span(cPosInfInterval))); - static assert(__traits(compiles, iInterval.span(iPosInfInterval))); - static assert(__traits(compiles, iInterval.span(negInfInterval))); - static assert(__traits(compiles, iInterval.span(cNegInfInterval))); - static assert(__traits(compiles, iInterval.span(iNegInfInterval))); - - //Verify Examples. - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, 6), Date(1991, 1, 8))) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(PosInfInterval!Date(Date(2050, 1, 1))) == PosInfInterval!Date(Date(1996, 1 , 2))); - - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1602, 5, 21))) == NegInfInterval!Date(Date(2012, 3 , 1))); - assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); - } + //Verify Examples. + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, 6), Date(1991, 1, 8))) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(Interval!Date(Date(2012, 3, 1), Date(2013, 5, 7))) == Interval!Date(Date(1996, 1 , 2), Date(2013, 5, 7))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(PosInfInterval!Date(Date(2050, 1, 1))) == PosInfInterval!Date(Date(1996, 1 , 2))); + + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1602, 5, 21))) == NegInfInterval!Date(Date(2012, 3 , 1))); + assert(Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); } //Test Interval's shift(duration). unittest { - version(testStdDateTime) - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static void testIntervalFail(Interval!Date interval, in Duration duration) - { - interval.shift(duration); - } + static void testIntervalFail(Interval!Date interval, in Duration duration) + { + interval.shift(duration); + } - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.shift(duration); - _assertPred!"=="(interval, expected, "", __FILE__, line); - } + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.shift(duration); + assert(interval == expected); + } - testInterval(interval, dur!"days"(22), Interval!Date(Date(2010, 7, 26), Date(2012, 1, 29))); - testInterval(interval, dur!"days"(-22), Interval!Date(Date(2010, 6, 12), Date(2011, 12, 16))); + testInterval(interval, dur!"days"(22), Interval!Date(Date(2010, 7, 26), Date(2012, 1, 29))); + testInterval(interval, dur!"days"(-22), Interval!Date(Date(2010, 6, 12), Date(2011, 12, 16))); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); - //Verify Examples. - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); + //Verify Examples. + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 4, 5)); - interval1.shift(dur!"days"(50)); - assert(interval1 == Interval!Date(Date(1996, 2, 21), Date(2012, 5, 25))); + interval1.shift(dur!"days"(50)); + assert(interval1 == Interval!Date(Date(1996, 2, 21), Date(2012, 5, 25))); - interval2.shift(dur!"days"(-50)); - assert(interval2 == Interval!Date(Date(1995, 11, 13), Date(2012, 2, 15))); - } + interval2.shift(dur!"days"(-50)); + assert(interval2 == Interval!Date(Date(1995, 11, 13), Date(2012, 2, 15))); } //Test Interval's shift(int, int, AllowDayOverflow). unittest { - version(testStdDateTime) { - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static void testIntervalFail(Interval!Date interval, int years, int months) - { - interval.shift(years, months); - } + static void testIntervalFail(Interval!Date interval, int years, int months) + { + interval.shift(years, months); + } - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), 1, 0)); + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), 1, 0)); - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) - { - interval.shift(years, months, allow); - _assertPred!"=="(interval, expected, "", __FILE__, line); - } + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) + { + interval.shift(years, months, allow); + assert(interval == expected); + } - testInterval(interval, 5, 0, AllowDayOverflow.yes, Interval!Date(Date(2015, 7, 4), Date(2017, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, Interval!Date(Date(2005, 7, 4), Date(2007, 1, 7))); + testInterval(interval, 5, 0, AllowDayOverflow.yes, Interval!Date(Date(2015, 7, 4), Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, Interval!Date(Date(2005, 7, 4), Date(2007, 1, 7))); - auto interval2 = Interval!Date(Date(2000, 1, 29), Date(2010, 5, 31)); + auto interval2 = Interval!Date(Date(2000, 1, 29), Date(2010, 5, 31)); - testInterval(interval2, 1, 1, AllowDayOverflow.yes, Interval!Date(Date(2001, 3, 1), Date(2011, 7, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, Interval!Date(Date(2000, 12, 29), Date(2011, 5, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, Interval!Date(Date(1998, 12, 29), Date(2009, 5, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, Interval!Date(Date(1999, 3, 1), Date(2009, 7, 1))); + testInterval(interval2, 1, 1, AllowDayOverflow.yes, Interval!Date(Date(2001, 3, 1), Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, Interval!Date(Date(2000, 12, 29), Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, Interval!Date(Date(1998, 12, 29), Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, Interval!Date(Date(1999, 3, 1), Date(2009, 7, 1))); - testInterval(interval2, 1, 1, AllowDayOverflow.no, Interval!Date(Date(2001, 2, 28), Date(2011, 6, 30))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, Interval!Date(Date(2000, 12, 29), Date(2011, 4, 30))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, Interval!Date(Date(1998, 12, 29), Date(2009, 4, 30))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, Interval!Date(Date(1999, 2, 28), Date(2009, 6, 30))); - } + testInterval(interval2, 1, 1, AllowDayOverflow.no, Interval!Date(Date(2001, 2, 28), Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, Interval!Date(Date(2000, 12, 29), Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, Interval!Date(Date(1998, 12, 29), Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, Interval!Date(Date(1999, 2, 28), Date(2009, 6, 30))); + } - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.shift(5))); - static assert(!__traits(compiles, iInterval.shift(5))); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.shift(5))); + static assert(!__traits(compiles, iInterval.shift(5))); - //Verify Examples. - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + //Verify Examples. + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - interval1.shift(2); - assert(interval1 == Interval!Date(Date(1998, 1, 2), Date(2014, 3, 1))); + interval1.shift(2); + assert(interval1 == Interval!Date(Date(1998, 1, 2), Date(2014, 3, 1))); - interval2.shift(-2); - assert(interval2 == Interval!Date(Date(1994, 1, 2), Date(2010, 3, 1))); - } + interval2.shift(-2); + assert(interval2 == Interval!Date(Date(1994, 1, 2), Date(2010, 3, 1))); } //Test Interval's expand(Duration). unittest { - version(testStdDateTime) - { - auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); + auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); - static void testIntervalFail(I)(I interval, in Duration duration) - { - interval.expand(duration); - } + static void testIntervalFail(I)(I interval, in Duration duration) + { + interval.expand(duration); + } - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5)), dur!"days"(-5))); + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), dur!"days"(1))); + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5)), dur!"days"(-5))); - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.expand(duration); - _assertPred!"=="(interval, expected, "", __FILE__, line); - } + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.expand(duration); + assert(interval == expected); + } - testInterval(interval, dur!"days"(22), Interval!Date(Date(2000, 6, 12), Date(2012, 1, 29))); - testInterval(interval, dur!"days"(-22), Interval!Date(Date(2000, 7, 26), Date(2011, 12, 16))); + testInterval(interval, dur!"days"(22), Interval!Date(Date(2000, 6, 12), Date(2012, 1, 29))); + testInterval(interval, dur!"days"(-22), Interval!Date(Date(2000, 7, 26), Date(2011, 12, 16))); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); - //Verify Examples. - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + //Verify Examples. + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - interval1.expand(dur!"days"(2)); - assert(interval1 == Interval!Date(Date(1995, 12, 31), Date(2012, 3, 3))); + interval1.expand(dur!"days"(2)); + assert(interval1 == Interval!Date(Date(1995, 12, 31), Date(2012, 3, 3))); - interval2.expand(dur!"days"(-2)); - assert(interval2 == Interval!Date(Date(1996, 1, 4), Date(2012, 2, 28))); - } + interval2.expand(dur!"days"(-2)); + assert(interval2 == Interval!Date(Date(1996, 1, 4), Date(2012, 2, 28))); } //Test Interval's expand(int, int, AllowDayOverflow, Direction) unittest { - version(testStdDateTime) { - { - auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); + auto interval = Interval!Date(Date(2000, 7, 4), Date(2012, 1, 7)); - static void testIntervalFail(Interval!Date interval, int years, int months) - { - interval.expand(years, months); - } + static void testIntervalFail(Interval!Date interval, int years, int months) + { + interval.expand(years, months); + } - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), 1, 0)); - assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)), -5, 0)); + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), dur!"days"(0)), 1, 0)); + assertThrown!DateTimeException(testIntervalFail(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)), -5, 0)); - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, Direction dir, in I expected, size_t line = __LINE__) - { - interval.expand(years, months, allow, dir); - _assertPred!"=="(interval, expected, "", __FILE__, line); - } + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, Direction dir, in I expected, size_t line = __LINE__) + { + interval.expand(years, months, allow, dir); + assert(interval == expected); + } - testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(1995, 7, 4), Date(2017, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(2005, 7, 4), Date(2007, 1, 7))); + testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(1995, 7, 4), Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(2005, 7, 4), Date(2007, 1, 7))); - testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 7, 4), Date(2017, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 7, 4), Date(2007, 1, 7))); + testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 7, 4), Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 7, 4), Date(2007, 1, 7))); - testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(1995, 7, 4), Date(2012, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(2005, 7, 4), Date(2012, 1, 7))); + testInterval(interval, 5, 0, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(1995, 7, 4), Date(2012, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(2005, 7, 4), Date(2012, 1, 7))); - auto interval2 = Interval!Date(Date(2000, 1, 29), Date(2010, 5, 31)); + auto interval2 = Interval!Date(Date(2000, 1, 29), Date(2010, 5, 31)); - testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(1998, 12, 29), Date(2011, 7, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(1999, 3, 1), Date(2011, 5, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(2001, 3, 1), Date(2009, 5, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(2000, 12, 29), Date(2009, 7, 1))); + testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(1998, 12, 29), Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(1999, 3, 1), Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(2001, 3, 1), Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.both, Interval!Date(Date(2000, 12, 29), Date(2009, 7, 1))); - testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.both, Interval!Date(Date(1998, 12, 29), Date(2011, 6, 30))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.both, Interval!Date(Date(1999, 2, 28), Date(2011, 4, 30))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.both, Interval!Date(Date(2001, 2, 28), Date(2009, 4, 30))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.both, Interval!Date(Date(2000, 12, 29), Date(2009, 6, 30))); + testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.both, Interval!Date(Date(1998, 12, 29), Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.both, Interval!Date(Date(1999, 2, 28), Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.both, Interval!Date(Date(2001, 2, 28), Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.both, Interval!Date(Date(2000, 12, 29), Date(2009, 6, 30))); - testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2011, 7, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2011, 5, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2009, 5, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2009, 7, 1))); + testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2009, 7, 1))); - testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2011, 6, 30))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2011, 4, 30))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2009, 4, 30))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2009, 6, 30))); + testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.fwd, Interval!Date(Date(2000, 1, 29), Date(2009, 6, 30))); - testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(1998, 12, 29), Date(2010, 5, 31))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(1999, 3, 1), Date(2010, 5, 31))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(2001, 3, 1), Date(2010, 5, 31))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(2000, 12, 29), Date(2010, 5, 31))); + testInterval(interval2, 1, 1, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(1998, 12, 29), Date(2010, 5, 31))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(1999, 3, 1), Date(2010, 5, 31))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(2001, 3, 1), Date(2010, 5, 31))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, Direction.bwd, Interval!Date(Date(2000, 12, 29), Date(2010, 5, 31))); - testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.bwd, Interval!Date(Date(1998, 12, 29), Date(2010, 5, 31))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.bwd, Interval!Date(Date(1999, 2, 28), Date(2010, 5, 31))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.bwd, Interval!Date(Date(2001, 2, 28), Date(2010, 5, 31))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.bwd, Interval!Date(Date(2000, 12, 29), Date(2010, 5, 31))); - } + testInterval(interval2, 1, 1, AllowDayOverflow.no, Direction.bwd, Interval!Date(Date(1998, 12, 29), Date(2010, 5, 31))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, Direction.bwd, Interval!Date(Date(1999, 2, 28), Date(2010, 5, 31))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, Direction.bwd, Interval!Date(Date(2001, 2, 28), Date(2010, 5, 31))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, Direction.bwd, Interval!Date(Date(2000, 12, 29), Date(2010, 5, 31))); + } - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.expand(5))); - static assert(!__traits(compiles, iInterval.expand(5))); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.expand(5))); + static assert(!__traits(compiles, iInterval.expand(5))); - //Verify Examples. - auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + //Verify Examples. + auto interval1 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); + auto interval2 = Interval!Date(Date(1996, 1, 2), Date(2012, 3, 1)); - interval1.expand(2); - assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); + interval1.expand(2); + assert(interval1 == Interval!Date(Date(1994, 1, 2), Date(2014, 3, 1))); - interval2.expand(-2); - assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); - } + interval2.expand(-2); + assert(interval2 == Interval!Date(Date(1998, 1, 2), Date(2010, 3, 1))); } //Test Interval's fwdRange. unittest { - version(testStdDateTime) { - { - auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); - - static void testInterval1(Interval!Date interval) - { - interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - } + auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); - assertThrown!DateTimeException(testInterval1(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + static void testInterval1(Interval!Date interval) + { + interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + } - static void testInterval2(Interval!Date interval) - { - interval.fwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).popFront(); - } + assertThrown!DateTimeException(testInterval1(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(testInterval2(interval)); + static void testInterval2(Interval!Date interval) + { + interval.fwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).popFront(); + } - assert(!interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); - assert(interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).empty); + assertThrown!DateTimeException(testInterval2(interval)); - _assertPred!"=="(Interval!Date(Date(2010, 9, 12), Date(2010, 10, 1)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).front, - Date(2010, 9, 12)); + assert(!interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).empty); + assert(interval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).empty); - _assertPred!"=="(Interval!Date(Date(2010, 9, 12), Date(2010, 10, 1)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).front, - Date(2010, 9, 17)); - } + assert(Interval!Date(Date(2010, 9, 12), Date(2010, 10, 1)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).front == + Date(2010, 9, 12)); - //Verify Examples. - { - auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); - auto func = delegate (in Date date) - { - if((date.day & 1) == 0) - return date + dur!"days"(2); + assert(Interval!Date(Date(2010, 9, 12), Date(2010, 10, 1)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).front == + Date(2010, 9, 17)); + } - return date + dur!"days"(1); - }; - auto range = interval.fwdRange(func); + //Verify Examples. + { + auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); + auto func = delegate (in Date date) + { + if((date.day & 1) == 0) + return date + dur!"days"(2); - assert(range.front == Date(2010, 9, 1)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). + return date + dur!"days"(1); + }; + auto range = interval.fwdRange(func); - range.popFront(); - assert(range.front == Date(2010, 9, 2)); + assert(range.front == Date(2010, 9, 1)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). - range.popFront(); - assert(range.front == Date(2010, 9, 4)); + range.popFront(); + assert(range.front == Date(2010, 9, 2)); - range.popFront(); - assert(range.front == Date(2010, 9, 6)); + range.popFront(); + assert(range.front == Date(2010, 9, 4)); - range.popFront(); - assert(range.front == Date(2010, 9, 8)); + range.popFront(); + assert(range.front == Date(2010, 9, 6)); - range.popFront(); - assert(range.empty); - } + range.popFront(); + assert(range.front == Date(2010, 9, 8)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, cInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); - static assert(__traits(compiles, iInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); + range.popFront(); + assert(range.empty); } + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, cInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); + static assert(__traits(compiles, iInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); } //Test Interval's bwdRange. unittest { - version(testStdDateTime) { - { - auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); + auto interval = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)); - static void testInterval1(Interval!Date interval) - { - interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - } - - assertThrown!DateTimeException(testInterval1(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + static void testInterval1(Interval!Date interval) + { + interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + } - static void testInterval2(Interval!Date interval) - { - interval.bwdRange(everyDayOfWeek!(Date, Direction.fwd)(DayOfWeek.fri)).popFront(); - } + assertThrown!DateTimeException(testInterval1(Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - assertThrown!DateTimeException(testInterval2(interval)); + static void testInterval2(Interval!Date interval) + { + interval.bwdRange(everyDayOfWeek!(Date, Direction.fwd)(DayOfWeek.fri)).popFront(); + } - assert(!interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).empty); - assert(interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).empty); + assertThrown!DateTimeException(testInterval2(interval)); - _assertPred!"=="(Interval!Date(Date(2010, 9, 19), Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).front, - Date(2010, 10, 1)); + assert(!interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).empty); + assert(interval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).empty); - _assertPred!"=="(Interval!Date(Date(2010, 9, 19), Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).front, - Date(2010, 9, 24)); - } + assert(Interval!Date(Date(2010, 9, 19), Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).front == + Date(2010, 10, 1)); - //Verify Examples. - { - auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); - auto func = delegate (in Date date) - { - if((date.day & 1) == 0) - return date - dur!"days"(2); + assert(Interval!Date(Date(2010, 9, 19), Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).front == + Date(2010, 9, 24)); + } - return date - dur!"days"(1); - }; - auto range = interval.bwdRange(func); + //Verify Examples. + { + auto interval = Interval!Date(Date(2010, 9, 1), Date(2010, 9, 9)); + auto func = delegate (in Date date) + { + if((date.day & 1) == 0) + return date - dur!"days"(2); - assert(range.front == Date(2010, 9, 9)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). + return date - dur!"days"(1); + }; + auto range = interval.bwdRange(func); - range.popFront(); - assert(range.front == Date(2010, 9, 8)); + assert(range.front == Date(2010, 9, 9)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). - range.popFront(); - assert(range.front == Date(2010, 9, 6)); + range.popFront(); + assert(range.front == Date(2010, 9, 8)); - range.popFront(); - assert(range.front == Date(2010, 9, 4)); + range.popFront(); + assert(range.front == Date(2010, 9, 6)); - range.popFront(); - assert(range.front == Date(2010, 9, 2)); + range.popFront(); + assert(range.front == Date(2010, 9, 4)); - range.popFront(); - assert(range.empty); - } + range.popFront(); + assert(range.front == Date(2010, 9, 2)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, cInterval.bwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); - static assert(__traits(compiles, iInterval.bwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); + range.popFront(); + assert(range.empty); } + + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, cInterval.bwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); + static assert(__traits(compiles, iInterval.bwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); } //Test Interval's toString(). unittest { - version(testStdDateTime) - { - _assertPred!"=="(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).toString(), "[2010-Jul-04 - 2012-Jan-07)"); + assert(Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).toString() == "[2010-Jul-04 - 2012-Jan-07)"); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, cInterval.toString())); - static assert(__traits(compiles, iInterval.toString())); - } + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, cInterval.toString())); + static assert(__traits(compiles, iInterval.toString())); } @@ -21303,10 +19859,9 @@ auto interval = PosInfInterval!Date(Date(1996, 1, 2)); Params: rhs = The $(D PosInfInterval) to assign to this one. +/ - /+ref+/ PosInfInterval opAssign(const ref PosInfInterval rhs) pure nothrow + ref PosInfInterval opAssign(const ref PosInfInterval rhs) pure nothrow { _begin = cast(TP)rhs._begin; - return this; } @@ -21315,10 +19870,9 @@ auto interval = PosInfInterval!Date(Date(1996, 1, 2)); Params: rhs = The $(D PosInfInterval) to assign to this one. +/ - /+ref+/ PosInfInterval opAssign(PosInfInterval rhs) pure nothrow + ref PosInfInterval opAssign(PosInfInterval rhs) pure nothrow { _begin = cast(TP)rhs._begin; - return this; } @@ -22296,1225 +20850,1165 @@ private: //Test PosInfInterval's constructor. unittest { - version(testStdDateTime) - { - PosInfInterval!Date(Date.init); - PosInfInterval!TimeOfDay(TimeOfDay.init); - PosInfInterval!DateTime(DateTime.init); - PosInfInterval!SysTime(SysTime(0)); + PosInfInterval!Date(Date.init); + PosInfInterval!TimeOfDay(TimeOfDay.init); + PosInfInterval!DateTime(DateTime.init); + PosInfInterval!SysTime(SysTime(0)); - //Verify Examples. - auto interval = PosInfInterval!Date(Date(1996, 1, 2)); - } + //Verify Examples. + auto interval = PosInfInterval!Date(Date(1996, 1, 2)); } //Test PosInfInterval's begin. unittest { - version(testStdDateTime) - { - _assertPred!"=="(PosInfInterval!Date(Date(1, 1, 1)).begin, Date(1, 1, 1)); - _assertPred!"=="(PosInfInterval!Date(Date(2010, 1, 1)).begin, Date(2010, 1, 1)); - _assertPred!"=="(PosInfInterval!Date(Date(1997, 12, 31)).begin, Date(1997, 12, 31)); + assert(PosInfInterval!Date(Date(1, 1, 1)).begin == Date(1, 1, 1)); + assert(PosInfInterval!Date(Date(2010, 1, 1)).begin == Date(2010, 1, 1)); + assert(PosInfInterval!Date(Date(1997, 12, 31)).begin == Date(1997, 12, 31)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(__traits(compiles, cPosInfInterval.begin)); - static assert(__traits(compiles, iPosInfInterval.begin)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(__traits(compiles, cPosInfInterval.begin)); + static assert(__traits(compiles, iPosInfInterval.begin)); - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).begin == Date(1996, 1, 2)); - } + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).begin == Date(1996, 1, 2)); } //Test PosInfInterval's empty. unittest { - version(testStdDateTime) - { - assert(!PosInfInterval!Date(Date(2010, 1, 1)).empty); - assert(!PosInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); - assert(!PosInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); - assert(!PosInfInterval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0))).empty); + assert(!PosInfInterval!Date(Date(2010, 1, 1)).empty); + assert(!PosInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); + assert(!PosInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); + assert(!PosInfInterval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0))).empty); - const cPosInfInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iPosInfInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - static assert(__traits(compiles, cPosInfInterval.empty)); - static assert(__traits(compiles, iPosInfInterval.empty)); + const cPosInfInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iPosInfInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + static assert(__traits(compiles, cPosInfInterval.empty)); + static assert(__traits(compiles, iPosInfInterval.empty)); - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).empty); - } + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).empty); } //Test PosInfInterval's contains(time point). unittest { - version(testStdDateTime) - { - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + assert(!posInfInterval.contains(Date(2009, 7, 4))); + assert(!posInfInterval.contains(Date(2010, 7, 3))); + assert(posInfInterval.contains(Date(2010, 7, 4))); + assert(posInfInterval.contains(Date(2010, 7, 5))); + assert(posInfInterval.contains(Date(2011, 7, 1))); + assert(posInfInterval.contains(Date(2012, 1, 6))); + assert(posInfInterval.contains(Date(2012, 1, 7))); + assert(posInfInterval.contains(Date(2012, 1, 8))); + assert(posInfInterval.contains(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(__traits(compiles, posInfInterval.contains(cdate))); + static assert(__traits(compiles, cPosInfInterval.contains(cdate))); + static assert(__traits(compiles, iPosInfInterval.contains(cdate))); - assert(!posInfInterval.contains(Date(2009, 7, 4))); - assert(!posInfInterval.contains(Date(2010, 7, 3))); - assert(posInfInterval.contains(Date(2010, 7, 4))); - assert(posInfInterval.contains(Date(2010, 7, 5))); - assert(posInfInterval.contains(Date(2011, 7, 1))); - assert(posInfInterval.contains(Date(2012, 1, 6))); - assert(posInfInterval.contains(Date(2012, 1, 7))); - assert(posInfInterval.contains(Date(2012, 1, 8))); - assert(posInfInterval.contains(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(__traits(compiles, posInfInterval.contains(cdate))); - static assert(__traits(compiles, cPosInfInterval.contains(cdate))); - static assert(__traits(compiles, iPosInfInterval.contains(cdate))); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(1994, 12, 24))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(2000, 1, 5))); - } + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(1994, 12, 24))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Date(2000, 1, 5))); } //Test PosInfInterval's contains(Interval). unittest { - version(testStdDateTime) - { - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.contains(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(posInfInterval.contains(posInfInterval)); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(posInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(posInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).contains(posInfInterval)); + assert(PosInfInterval!Date(Date(2010, 7, 4)).contains(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 5)).contains(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 6)).contains(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 7)).contains(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 8)).contains(posInfInterval)); + + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, posInfInterval.contains(interval))); + static assert(__traits(compiles, posInfInterval.contains(cInterval))); + static assert(__traits(compiles, posInfInterval.contains(iInterval))); + static assert(__traits(compiles, posInfInterval.contains(posInfInterval))); + static assert(__traits(compiles, posInfInterval.contains(cPosInfInterval))); + static assert(__traits(compiles, posInfInterval.contains(iPosInfInterval))); + static assert(__traits(compiles, posInfInterval.contains(negInfInterval))); + static assert(__traits(compiles, posInfInterval.contains(cNegInfInterval))); + static assert(__traits(compiles, posInfInterval.contains(iNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.contains(interval))); + static assert(__traits(compiles, cPosInfInterval.contains(cInterval))); + static assert(__traits(compiles, cPosInfInterval.contains(iInterval))); + static assert(__traits(compiles, cPosInfInterval.contains(posInfInterval))); + static assert(__traits(compiles, cPosInfInterval.contains(cPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.contains(iPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.contains(negInfInterval))); + static assert(__traits(compiles, cPosInfInterval.contains(cNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.contains(iNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.contains(interval))); + static assert(__traits(compiles, iPosInfInterval.contains(cInterval))); + static assert(__traits(compiles, iPosInfInterval.contains(iInterval))); + static assert(__traits(compiles, iPosInfInterval.contains(posInfInterval))); + static assert(__traits(compiles, iPosInfInterval.contains(cPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.contains(iPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.contains(negInfInterval))); + static assert(__traits(compiles, iPosInfInterval.contains(cNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.contains(iNegInfInterval))); - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.contains(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(posInfInterval.contains(posInfInterval)); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!posInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(posInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(posInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(posInfInterval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(posInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).contains(posInfInterval)); - assert(PosInfInterval!Date(Date(2010, 7, 4)).contains(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 5)).contains(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 6)).contains(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 7)).contains(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 8)).contains(posInfInterval)); - - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, posInfInterval.contains(interval))); - static assert(__traits(compiles, posInfInterval.contains(cInterval))); - static assert(__traits(compiles, posInfInterval.contains(iInterval))); - static assert(__traits(compiles, posInfInterval.contains(posInfInterval))); - static assert(__traits(compiles, posInfInterval.contains(cPosInfInterval))); - static assert(__traits(compiles, posInfInterval.contains(iPosInfInterval))); - static assert(__traits(compiles, posInfInterval.contains(negInfInterval))); - static assert(__traits(compiles, posInfInterval.contains(cNegInfInterval))); - static assert(__traits(compiles, posInfInterval.contains(iNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.contains(interval))); - static assert(__traits(compiles, cPosInfInterval.contains(cInterval))); - static assert(__traits(compiles, cPosInfInterval.contains(iInterval))); - static assert(__traits(compiles, cPosInfInterval.contains(posInfInterval))); - static assert(__traits(compiles, cPosInfInterval.contains(cPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.contains(iPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.contains(negInfInterval))); - static assert(__traits(compiles, cPosInfInterval.contains(cNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.contains(iNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.contains(interval))); - static assert(__traits(compiles, iPosInfInterval.contains(cInterval))); - static assert(__traits(compiles, iPosInfInterval.contains(iInterval))); - static assert(__traits(compiles, iPosInfInterval.contains(posInfInterval))); - static assert(__traits(compiles, iPosInfInterval.contains(cPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.contains(iPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.contains(negInfInterval))); - static assert(__traits(compiles, iPosInfInterval.contains(cNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.contains(iNegInfInterval))); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1995, 7, 2)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); - } + assert(PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(PosInfInterval!Date(Date(1995, 7, 2)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); } //Test PosInfInterval's isBefore(time point). unittest { - version(testStdDateTime) - { - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + assert(!posInfInterval.isBefore(Date(2009, 7, 3))); + assert(!posInfInterval.isBefore(Date(2010, 7, 3))); + assert(!posInfInterval.isBefore(Date(2010, 7, 4))); + assert(!posInfInterval.isBefore(Date(2010, 7, 5))); + assert(!posInfInterval.isBefore(Date(2011, 7, 1))); + assert(!posInfInterval.isBefore(Date(2012, 1, 6))); + assert(!posInfInterval.isBefore(Date(2012, 1, 7))); + assert(!posInfInterval.isBefore(Date(2012, 1, 8))); + assert(!posInfInterval.isBefore(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(__traits(compiles, posInfInterval.isBefore(cdate))); + static assert(__traits(compiles, cPosInfInterval.isBefore(cdate))); + static assert(__traits(compiles, iPosInfInterval.isBefore(cdate))); - assert(!posInfInterval.isBefore(Date(2009, 7, 3))); - assert(!posInfInterval.isBefore(Date(2010, 7, 3))); - assert(!posInfInterval.isBefore(Date(2010, 7, 4))); - assert(!posInfInterval.isBefore(Date(2010, 7, 5))); - assert(!posInfInterval.isBefore(Date(2011, 7, 1))); - assert(!posInfInterval.isBefore(Date(2012, 1, 6))); - assert(!posInfInterval.isBefore(Date(2012, 1, 7))); - assert(!posInfInterval.isBefore(Date(2012, 1, 8))); - assert(!posInfInterval.isBefore(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(__traits(compiles, posInfInterval.isBefore(cdate))); - static assert(__traits(compiles, cPosInfInterval.isBefore(cdate))); - static assert(__traits(compiles, iPosInfInterval.isBefore(cdate))); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(1994, 12, 24))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); - } + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(1994, 12, 24))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Date(2000, 1, 5))); } //Test PosInfInterval's isBefore(Interval). unittest { - version(testStdDateTime) - { - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.isBefore(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!posInfInterval.isBefore(posInfInterval)); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!PosInfInterval!Date(Date(2010, 7, 3)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 4)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 5)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 6)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 7)).isBefore(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 8)).isBefore(posInfInterval)); + + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, posInfInterval.isBefore(interval))); + static assert(__traits(compiles, posInfInterval.isBefore(cInterval))); + static assert(__traits(compiles, posInfInterval.isBefore(iInterval))); + static assert(__traits(compiles, posInfInterval.isBefore(posInfInterval))); + static assert(__traits(compiles, posInfInterval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, posInfInterval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, posInfInterval.isBefore(negInfInterval))); + static assert(__traits(compiles, posInfInterval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, posInfInterval.isBefore(iNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(interval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(cInterval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(iInterval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(posInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(negInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isBefore(iNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(interval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(cInterval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(iInterval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(posInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(negInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isBefore(iNegInfInterval))); + + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.isBefore(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!posInfInterval.isBefore(posInfInterval)); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!posInfInterval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!PosInfInterval!Date(Date(2010, 7, 3)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 4)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 5)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 6)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 7)).isBefore(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 8)).isBefore(posInfInterval)); - - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(1992, 5, 4)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, posInfInterval.isBefore(interval))); - static assert(__traits(compiles, posInfInterval.isBefore(cInterval))); - static assert(__traits(compiles, posInfInterval.isBefore(iInterval))); - static assert(__traits(compiles, posInfInterval.isBefore(posInfInterval))); - static assert(__traits(compiles, posInfInterval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, posInfInterval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, posInfInterval.isBefore(negInfInterval))); - static assert(__traits(compiles, posInfInterval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, posInfInterval.isBefore(iNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(interval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(cInterval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(iInterval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(posInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(negInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isBefore(iNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(interval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(cInterval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(iInterval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(posInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(negInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isBefore(iNegInfInterval))); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(1992, 5, 4)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(PosInfInterval!Date(Date(2013, 3, 7)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); - } + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); } //Test PosInfInterval's isAfter(time point). unittest { - version(testStdDateTime) - { - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + assert(posInfInterval.isAfter(Date(2009, 7, 3))); + assert(posInfInterval.isAfter(Date(2010, 7, 3))); + assert(!posInfInterval.isAfter(Date(2010, 7, 4))); + assert(!posInfInterval.isAfter(Date(2010, 7, 5))); + assert(!posInfInterval.isAfter(Date(2011, 7, 1))); + assert(!posInfInterval.isAfter(Date(2012, 1, 6))); + assert(!posInfInterval.isAfter(Date(2012, 1, 7))); + assert(!posInfInterval.isAfter(Date(2012, 1, 8))); + assert(!posInfInterval.isAfter(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(__traits(compiles, posInfInterval.isAfter(cdate))); + static assert(__traits(compiles, cPosInfInterval.isAfter(cdate))); + static assert(__traits(compiles, iPosInfInterval.isAfter(cdate))); - assert(posInfInterval.isAfter(Date(2009, 7, 3))); - assert(posInfInterval.isAfter(Date(2010, 7, 3))); - assert(!posInfInterval.isAfter(Date(2010, 7, 4))); - assert(!posInfInterval.isAfter(Date(2010, 7, 5))); - assert(!posInfInterval.isAfter(Date(2011, 7, 1))); - assert(!posInfInterval.isAfter(Date(2012, 1, 6))); - assert(!posInfInterval.isAfter(Date(2012, 1, 7))); - assert(!posInfInterval.isAfter(Date(2012, 1, 8))); - assert(!posInfInterval.isAfter(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(__traits(compiles, posInfInterval.isAfter(cdate))); - static assert(__traits(compiles, cPosInfInterval.isAfter(cdate))); - static assert(__traits(compiles, iPosInfInterval.isAfter(cdate))); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(1994, 12, 24))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); - } + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(1994, 12, 24))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Date(2000, 1, 5))); } //Test PosInfInterval's isAfter(Interval). unittest { - version(testStdDateTime) - { - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.isAfter(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!posInfInterval.isAfter(posInfInterval)); + assert(posInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!PosInfInterval!Date(Date(2010, 7, 3)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 4)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 5)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 6)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 7)).isAfter(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 8)).isAfter(posInfInterval)); + + assert(posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, posInfInterval.isAfter(interval))); + static assert(__traits(compiles, posInfInterval.isAfter(cInterval))); + static assert(__traits(compiles, posInfInterval.isAfter(iInterval))); + static assert(__traits(compiles, posInfInterval.isAfter(posInfInterval))); + static assert(__traits(compiles, posInfInterval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, posInfInterval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, posInfInterval.isAfter(negInfInterval))); + static assert(__traits(compiles, posInfInterval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, posInfInterval.isAfter(iNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(interval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(cInterval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(iInterval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(posInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(negInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAfter(iNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(interval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(cInterval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(iInterval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(posInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(negInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAfter(iNegInfInterval))); - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.isAfter(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!posInfInterval.isAfter(posInfInterval)); - assert(posInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!posInfInterval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!PosInfInterval!Date(Date(2010, 7, 3)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 4)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 5)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 6)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 7)).isAfter(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 8)).isAfter(posInfInterval)); - - assert(posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); + //Verify Examples. + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, posInfInterval.isAfter(interval))); - static assert(__traits(compiles, posInfInterval.isAfter(cInterval))); - static assert(__traits(compiles, posInfInterval.isAfter(iInterval))); - static assert(__traits(compiles, posInfInterval.isAfter(posInfInterval))); - static assert(__traits(compiles, posInfInterval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, posInfInterval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, posInfInterval.isAfter(negInfInterval))); - static assert(__traits(compiles, posInfInterval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, posInfInterval.isAfter(iNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(interval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(cInterval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(iInterval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(posInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(negInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAfter(iNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(interval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(cInterval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(iInterval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(posInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(negInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAfter(iNegInfInterval))); - - //Verify Examples. - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1990, 1, 7)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(2000, 7, 1)))); - } + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1990, 1, 7)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAfter(NegInfInterval!Date(Date(2000, 7, 1)))); } //Test PosInfInterval's intersects(). unittest { - version(testStdDateTime) - { - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.intersects(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(posInfInterval.intersects(posInfInterval)); + assert(!posInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2010, 7, 4)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2010, 7, 5)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2012, 1, 6)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2012, 1, 7)).intersects(posInfInterval)); + assert(PosInfInterval!Date(Date(2012, 1, 8)).intersects(posInfInterval)); + + assert(!posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, posInfInterval.intersects(interval))); + static assert(__traits(compiles, posInfInterval.intersects(cInterval))); + static assert(__traits(compiles, posInfInterval.intersects(iInterval))); + static assert(__traits(compiles, posInfInterval.intersects(posInfInterval))); + static assert(__traits(compiles, posInfInterval.intersects(cPosInfInterval))); + static assert(__traits(compiles, posInfInterval.intersects(iPosInfInterval))); + static assert(__traits(compiles, posInfInterval.intersects(negInfInterval))); + static assert(__traits(compiles, posInfInterval.intersects(cNegInfInterval))); + static assert(__traits(compiles, posInfInterval.intersects(iNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersects(interval))); + static assert(__traits(compiles, cPosInfInterval.intersects(cInterval))); + static assert(__traits(compiles, cPosInfInterval.intersects(iInterval))); + static assert(__traits(compiles, cPosInfInterval.intersects(posInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersects(cPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersects(iPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersects(negInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersects(cNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersects(iNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersects(interval))); + static assert(__traits(compiles, iPosInfInterval.intersects(cInterval))); + static assert(__traits(compiles, iPosInfInterval.intersects(iInterval))); + static assert(__traits(compiles, iPosInfInterval.intersects(posInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersects(cPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersects(iPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersects(negInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersects(cNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersects(iNegInfInterval))); - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.intersects(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(posInfInterval.intersects(posInfInterval)); - assert(!posInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(posInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(posInfInterval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(posInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(PosInfInterval!Date(Date(2010, 7, 3)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2010, 7, 4)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2010, 7, 5)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2012, 1, 6)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2012, 1, 7)).intersects(posInfInterval)); - assert(PosInfInterval!Date(Date(2012, 1, 8)).intersects(posInfInterval)); - - assert(!posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(posInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(posInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, posInfInterval.intersects(interval))); - static assert(__traits(compiles, posInfInterval.intersects(cInterval))); - static assert(__traits(compiles, posInfInterval.intersects(iInterval))); - static assert(__traits(compiles, posInfInterval.intersects(posInfInterval))); - static assert(__traits(compiles, posInfInterval.intersects(cPosInfInterval))); - static assert(__traits(compiles, posInfInterval.intersects(iPosInfInterval))); - static assert(__traits(compiles, posInfInterval.intersects(negInfInterval))); - static assert(__traits(compiles, posInfInterval.intersects(cNegInfInterval))); - static assert(__traits(compiles, posInfInterval.intersects(iNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersects(interval))); - static assert(__traits(compiles, cPosInfInterval.intersects(cInterval))); - static assert(__traits(compiles, cPosInfInterval.intersects(iInterval))); - static assert(__traits(compiles, cPosInfInterval.intersects(posInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersects(cPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersects(iPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersects(negInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersects(cNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersects(iNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersects(interval))); - static assert(__traits(compiles, iPosInfInterval.intersects(cInterval))); - static assert(__traits(compiles, iPosInfInterval.intersects(iInterval))); - static assert(__traits(compiles, iPosInfInterval.intersects(posInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersects(cPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersects(iPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersects(negInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersects(cNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersects(iNegInfInterval))); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1990, 1, 7)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(2000, 7, 1)))); - } + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1990, 1, 7)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(!PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersects(NegInfInterval!Date(Date(2000, 7, 1)))); } //Test PosInfInterval's intersection(). unittest { - version(testStdDateTime) - { - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(I, J)(in I interval1, in J interval2) + { + interval1.intersection(interval2); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + + assertThrown!DateTimeException(testInterval(posInfInterval, NegInfInterval!Date(Date(2010, 7, 3)))); + assertThrown!DateTimeException(testInterval(posInfInterval, NegInfInterval!Date(Date(2010, 7, 4)))); + + assert(posInfInterval.intersection(posInfInterval) == + posInfInterval); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + Interval!Date(Date(2010, 7, 4), Date(2013, 7, 3))); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); + assert(posInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))); + assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))); + assert(posInfInterval.intersection(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == + Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))); + + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == + PosInfInterval!Date(Date(2010, 7, 5))); + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == + PosInfInterval!Date(Date(2012, 1, 6))); + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2012, 1, 7))); + assert(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2012, 1, 8))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).intersection(posInfInterval) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2010, 7, 4)).intersection(posInfInterval) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2010, 7, 5)).intersection(posInfInterval) == + PosInfInterval!Date(Date(2010, 7, 5))); + assert(PosInfInterval!Date(Date(2012, 1, 6)).intersection(posInfInterval) == + PosInfInterval!Date(Date(2012, 1, 6))); + assert(PosInfInterval!Date(Date(2012, 1, 7)).intersection(posInfInterval) == + PosInfInterval!Date(Date(2012, 1, 7))); + assert(PosInfInterval!Date(Date(2012, 1, 8)).intersection(posInfInterval) == + PosInfInterval!Date(Date(2012, 1, 8))); + + assert(posInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); + assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 6))); + assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); + assert(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, posInfInterval.intersection(interval))); + static assert(__traits(compiles, posInfInterval.intersection(cInterval))); + static assert(__traits(compiles, posInfInterval.intersection(iInterval))); + static assert(__traits(compiles, posInfInterval.intersection(posInfInterval))); + static assert(__traits(compiles, posInfInterval.intersection(cPosInfInterval))); + static assert(__traits(compiles, posInfInterval.intersection(iPosInfInterval))); + static assert(__traits(compiles, posInfInterval.intersection(negInfInterval))); + static assert(__traits(compiles, posInfInterval.intersection(cNegInfInterval))); + static assert(__traits(compiles, posInfInterval.intersection(iNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersection(interval))); + static assert(__traits(compiles, cPosInfInterval.intersection(cInterval))); + static assert(__traits(compiles, cPosInfInterval.intersection(iInterval))); + static assert(__traits(compiles, cPosInfInterval.intersection(posInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersection(cPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersection(iPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersection(negInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersection(cNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.intersection(iNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersection(interval))); + static assert(__traits(compiles, iPosInfInterval.intersection(cInterval))); + static assert(__traits(compiles, iPosInfInterval.intersection(iInterval))); + static assert(__traits(compiles, iPosInfInterval.intersection(posInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersection(cPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersection(iPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersection(negInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersection(cNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.intersection(iNegInfInterval))); - static void testInterval(I, J)(in I interval1, in J interval2) - { - interval1.intersection(interval2); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - - assertThrown!DateTimeException(testInterval(posInfInterval, NegInfInterval!Date(Date(2010, 7, 3)))); - assertThrown!DateTimeException(testInterval(posInfInterval, NegInfInterval!Date(Date(2010, 7, 4)))); - - _assertPred!"=="(posInfInterval.intersection(posInfInterval), - posInfInterval); - _assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), - Interval!Date(Date(2010, 7, 4), Date(2013, 7, 3))); - _assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - _assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); - _assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); - _assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - _assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - _assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))); - _assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))), - Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))); - _assertPred!"=="(posInfInterval.intersection(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))), - Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))); - - _assertPred!"=="(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 3))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 4))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 5))), - PosInfInterval!Date(Date(2010, 7, 5))); - _assertPred!"=="(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 6))), - PosInfInterval!Date(Date(2012, 1, 6))); - _assertPred!"=="(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 7))), - PosInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(posInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 8))), - PosInfInterval!Date(Date(2012, 1, 8))); - - _assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 3)).intersection(posInfInterval), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 4)).intersection(posInfInterval), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 5)).intersection(posInfInterval), - PosInfInterval!Date(Date(2010, 7, 5))); - _assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 6)).intersection(posInfInterval), - PosInfInterval!Date(Date(2012, 1, 6))); - _assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 7)).intersection(posInfInterval), - PosInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 8)).intersection(posInfInterval), - PosInfInterval!Date(Date(2012, 1, 8))); - - _assertPred!"=="(posInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 5))), - Interval!Date(Date(2010, 7, 4), Date(2010, 7, 5))); - _assertPred!"=="(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 6))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 6))); - _assertPred!"=="(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 7))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7))); - _assertPred!"=="(posInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 8))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1, 8))); + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, posInfInterval.intersection(interval))); - static assert(__traits(compiles, posInfInterval.intersection(cInterval))); - static assert(__traits(compiles, posInfInterval.intersection(iInterval))); - static assert(__traits(compiles, posInfInterval.intersection(posInfInterval))); - static assert(__traits(compiles, posInfInterval.intersection(cPosInfInterval))); - static assert(__traits(compiles, posInfInterval.intersection(iPosInfInterval))); - static assert(__traits(compiles, posInfInterval.intersection(negInfInterval))); - static assert(__traits(compiles, posInfInterval.intersection(cNegInfInterval))); - static assert(__traits(compiles, posInfInterval.intersection(iNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersection(interval))); - static assert(__traits(compiles, cPosInfInterval.intersection(cInterval))); - static assert(__traits(compiles, cPosInfInterval.intersection(iInterval))); - static assert(__traits(compiles, cPosInfInterval.intersection(posInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersection(cPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersection(iPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersection(negInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersection(cNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.intersection(iNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersection(interval))); - static assert(__traits(compiles, iPosInfInterval.intersection(cInterval))); - static assert(__traits(compiles, iPosInfInterval.intersection(iInterval))); - static assert(__traits(compiles, iPosInfInterval.intersection(posInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersection(cPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersection(iPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersection(negInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersection(cNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.intersection(iNegInfInterval))); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1996, 1 , 2), Date(2000, 8, 2))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == Interval!Date(Date(1999, 1 , 12), Date(2011, 9, 17))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1996, 1 , 2))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == PosInfInterval!Date(Date(1999, 1 , 12))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == Interval!Date(Date(1996, 1 , 2), Date(2013, 1, 12))); - } + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1996, 1 , 2))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == PosInfInterval!Date(Date(1999, 1 , 12))); + + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == Interval!Date(Date(1996, 1 , 2), Date(1999, 7, 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == Interval!Date(Date(1996, 1 , 2), Date(2013, 1, 12))); } //Test PosInfInterval's isAdjacent(). unittest { - version(testStdDateTime) - { - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.isAdjacent(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!posInfInterval.isAdjacent(posInfInterval)); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(!PosInfInterval!Date(Date(2010, 7, 3)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 4)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2010, 7, 5)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 6)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 7)).isAdjacent(posInfInterval)); + assert(!PosInfInterval!Date(Date(2012, 1, 8)).isAdjacent(posInfInterval)); + + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, posInfInterval.isAdjacent(interval))); + static assert(__traits(compiles, posInfInterval.isAdjacent(cInterval))); + static assert(__traits(compiles, posInfInterval.isAdjacent(iInterval))); + static assert(__traits(compiles, posInfInterval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, posInfInterval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, posInfInterval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, posInfInterval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, posInfInterval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, posInfInterval.isAdjacent(iNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(interval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(cInterval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(iInterval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.isAdjacent(iNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(interval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(cInterval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(iInterval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.isAdjacent(iNegInfInterval))); + + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); + assert(!PosInfInterval!Date(Date(1999, 1, 12)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.isAdjacent(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!posInfInterval.isAdjacent(posInfInterval)); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!posInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); - - assert(!PosInfInterval!Date(Date(2010, 7, 3)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 4)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2010, 7, 5)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 6)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 7)).isAdjacent(posInfInterval)); - assert(!PosInfInterval!Date(Date(2012, 1, 8)).isAdjacent(posInfInterval)); - - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!posInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1990, 1, 7)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1996, 1, 2)))); - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, posInfInterval.isAdjacent(interval))); - static assert(__traits(compiles, posInfInterval.isAdjacent(cInterval))); - static assert(__traits(compiles, posInfInterval.isAdjacent(iInterval))); - static assert(__traits(compiles, posInfInterval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, posInfInterval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, posInfInterval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, posInfInterval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, posInfInterval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, posInfInterval.isAdjacent(iNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(interval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(cInterval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(iInterval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.isAdjacent(iNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(interval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(cInterval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(iInterval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.isAdjacent(iNegInfInterval))); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(Interval!Date(Date(1989, 3, 1), Date(1996, 1, 2)))); - assert(!PosInfInterval!Date(Date(1999, 1, 12)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1990, 1, 7)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(PosInfInterval!Date(Date(1996, 1, 2)))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); - assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(2000, 7, 1)))); - } + assert(PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(1996, 1, 2)))); + assert(!PosInfInterval!Date(Date(1996, 1, 2)).isAdjacent(NegInfInterval!Date(Date(2000, 7, 1)))); } //Test PosInfInterval's merge(). unittest { - version(testStdDateTime) - { - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.merge(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + + assert(posInfInterval.merge(posInfInterval) == + posInfInterval); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 1))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == + PosInfInterval!Date(Date(2010, 7, 4))); + + assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 4))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 5))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 6))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 4))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).merge(posInfInterval) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(PosInfInterval!Date(Date(2010, 7, 4)).merge(posInfInterval) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2010, 7, 5)).merge(posInfInterval) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 6)).merge(posInfInterval) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 7)).merge(posInfInterval) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 8)).merge(posInfInterval) == + PosInfInterval!Date(Date(2010, 7, 4))); + + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 3))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 4))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 5))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 6))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 7))))); + static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 8))))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, posInfInterval.merge(interval))); + static assert(__traits(compiles, posInfInterval.merge(cInterval))); + static assert(__traits(compiles, posInfInterval.merge(iInterval))); + static assert(__traits(compiles, posInfInterval.merge(posInfInterval))); + static assert(__traits(compiles, posInfInterval.merge(cPosInfInterval))); + static assert(__traits(compiles, posInfInterval.merge(iPosInfInterval))); + static assert(!__traits(compiles, posInfInterval.merge(negInfInterval))); + static assert(!__traits(compiles, posInfInterval.merge(cNegInfInterval))); + static assert(!__traits(compiles, posInfInterval.merge(iNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.merge(interval))); + static assert(__traits(compiles, cPosInfInterval.merge(cInterval))); + static assert(__traits(compiles, cPosInfInterval.merge(iInterval))); + static assert(__traits(compiles, cPosInfInterval.merge(posInfInterval))); + static assert(__traits(compiles, cPosInfInterval.merge(cPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.merge(iPosInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.merge(negInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.merge(cNegInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.merge(iNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.merge(interval))); + static assert(__traits(compiles, iPosInfInterval.merge(cInterval))); + static assert(__traits(compiles, iPosInfInterval.merge(iInterval))); + static assert(__traits(compiles, iPosInfInterval.merge(posInfInterval))); + static assert(__traits(compiles, iPosInfInterval.merge(cPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.merge(iPosInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.merge(negInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.merge(cNegInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.merge(iNegInfInterval))); - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.merge(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - - _assertPred!"=="(posInfInterval.merge(posInfInterval), - posInfInterval); - _assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), - PosInfInterval!Date(Date(2010, 7, 1))); - _assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))), - PosInfInterval!Date(Date(2010, 7, 3))); - _assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), - PosInfInterval!Date(Date(2010, 7, 3))); - _assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), - PosInfInterval!Date(Date(2010, 7, 3))); - _assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), - PosInfInterval!Date(Date(2010, 7, 3))); - _assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.merge(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))), - PosInfInterval!Date(Date(2010, 7, 4))); - - _assertPred!"=="(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 3))), - PosInfInterval!Date(Date(2010, 7, 3))); - _assertPred!"=="(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 4))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 5))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 6))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 7))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 8))), - PosInfInterval!Date(Date(2010, 7, 4))); - - _assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 3)).merge(posInfInterval), - PosInfInterval!Date(Date(2010, 7, 3))); - _assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 4)).merge(posInfInterval), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 5)).merge(posInfInterval), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 6)).merge(posInfInterval), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 7)).merge(posInfInterval), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 8)).merge(posInfInterval), - PosInfInterval!Date(Date(2010, 7, 4))); - - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 3))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 4))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 5))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 6))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 7))))); - static assert(!__traits(compiles, posInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 8))))); + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == PosInfInterval!Date(Date(1990, 7 , 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == PosInfInterval!Date(Date(1996, 1 , 2))); - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, posInfInterval.merge(interval))); - static assert(__traits(compiles, posInfInterval.merge(cInterval))); - static assert(__traits(compiles, posInfInterval.merge(iInterval))); - static assert(__traits(compiles, posInfInterval.merge(posInfInterval))); - static assert(__traits(compiles, posInfInterval.merge(cPosInfInterval))); - static assert(__traits(compiles, posInfInterval.merge(iPosInfInterval))); - static assert(!__traits(compiles, posInfInterval.merge(negInfInterval))); - static assert(!__traits(compiles, posInfInterval.merge(cNegInfInterval))); - static assert(!__traits(compiles, posInfInterval.merge(iNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.merge(interval))); - static assert(__traits(compiles, cPosInfInterval.merge(cInterval))); - static assert(__traits(compiles, cPosInfInterval.merge(iInterval))); - static assert(__traits(compiles, cPosInfInterval.merge(posInfInterval))); - static assert(__traits(compiles, cPosInfInterval.merge(cPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.merge(iPosInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.merge(negInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.merge(cNegInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.merge(iNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.merge(interval))); - static assert(__traits(compiles, iPosInfInterval.merge(cInterval))); - static assert(__traits(compiles, iPosInfInterval.merge(iInterval))); - static assert(__traits(compiles, iPosInfInterval.merge(posInfInterval))); - static assert(__traits(compiles, iPosInfInterval.merge(cPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.merge(iPosInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.merge(negInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.merge(cNegInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.merge(iNegInfInterval))); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == PosInfInterval!Date(Date(1990, 7 , 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == PosInfInterval!Date(Date(1996, 1 , 2))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1999, 1, 12))) == PosInfInterval!Date(Date(1996, 1 , 2))); - } + assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).merge(PosInfInterval!Date(Date(1999, 1, 12))) == PosInfInterval!Date(Date(1996, 1 , 2))); } //Test PosInfInterval's span(). unittest { - version(testStdDateTime) - { - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + + static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) + { + posInfInterval.span(interval); + } + + assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(posInfInterval.span(posInfInterval) == + posInfInterval); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 1))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 1))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == + PosInfInterval!Date(Date(2010, 7, 4))); + + assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 3))) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 4))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 5))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 6))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 7))) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 8))) == + PosInfInterval!Date(Date(2010, 7, 4))); + + assert(PosInfInterval!Date(Date(2010, 7, 3)).span(posInfInterval) == + PosInfInterval!Date(Date(2010, 7, 3))); + assert(PosInfInterval!Date(Date(2010, 7, 4)).span(posInfInterval) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2010, 7, 5)).span(posInfInterval) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 6)).span(posInfInterval) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 7)).span(posInfInterval) == + PosInfInterval!Date(Date(2010, 7, 4))); + assert(PosInfInterval!Date(Date(2012, 1, 8)).span(posInfInterval) == + PosInfInterval!Date(Date(2010, 7, 4))); + + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 3))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 4))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 5))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 6))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 7))))); + static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 8))))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, posInfInterval.span(interval))); + static assert(__traits(compiles, posInfInterval.span(cInterval))); + static assert(__traits(compiles, posInfInterval.span(iInterval))); + static assert(__traits(compiles, posInfInterval.span(posInfInterval))); + static assert(__traits(compiles, posInfInterval.span(cPosInfInterval))); + static assert(__traits(compiles, posInfInterval.span(iPosInfInterval))); + static assert(!__traits(compiles, posInfInterval.span(negInfInterval))); + static assert(!__traits(compiles, posInfInterval.span(cNegInfInterval))); + static assert(!__traits(compiles, posInfInterval.span(iNegInfInterval))); + static assert(__traits(compiles, cPosInfInterval.span(interval))); + static assert(__traits(compiles, cPosInfInterval.span(cInterval))); + static assert(__traits(compiles, cPosInfInterval.span(iInterval))); + static assert(__traits(compiles, cPosInfInterval.span(posInfInterval))); + static assert(__traits(compiles, cPosInfInterval.span(cPosInfInterval))); + static assert(__traits(compiles, cPosInfInterval.span(iPosInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.span(negInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.span(cNegInfInterval))); + static assert(!__traits(compiles, cPosInfInterval.span(iNegInfInterval))); + static assert(__traits(compiles, iPosInfInterval.span(interval))); + static assert(__traits(compiles, iPosInfInterval.span(cInterval))); + static assert(__traits(compiles, iPosInfInterval.span(iInterval))); + static assert(__traits(compiles, iPosInfInterval.span(posInfInterval))); + static assert(__traits(compiles, iPosInfInterval.span(cPosInfInterval))); + static assert(__traits(compiles, iPosInfInterval.span(iPosInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.span(negInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.span(cNegInfInterval))); + static assert(!__traits(compiles, iPosInfInterval.span(iNegInfInterval))); - static void testInterval(in PosInfInterval!Date posInfInterval, in Interval!Date interval) - { - posInfInterval.span(interval); - } - - assertThrown!DateTimeException(testInterval(posInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - _assertPred!"=="(posInfInterval.span(posInfInterval), - posInfInterval); - _assertPred!"=="(posInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))), - PosInfInterval!Date(Date(2010, 7, 1))); - _assertPred!"=="(posInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), - PosInfInterval!Date(Date(2010, 7, 1))); - _assertPred!"=="(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))), - PosInfInterval!Date(Date(2010, 7, 3))); - _assertPred!"=="(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), - PosInfInterval!Date(Date(2010, 7, 3))); - _assertPred!"=="(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), - PosInfInterval!Date(Date(2010, 7, 3))); - _assertPred!"=="(posInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), - PosInfInterval!Date(Date(2010, 7, 3))); - _assertPred!"=="(posInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))), - PosInfInterval!Date(Date(2010, 7, 4))); - - _assertPred!"=="(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 3))), - PosInfInterval!Date(Date(2010, 7, 3))); - _assertPred!"=="(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 4))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.span(PosInfInterval!Date(Date(2010, 7, 5))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 6))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 7))), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(posInfInterval.span(PosInfInterval!Date(Date(2012, 1, 8))), - PosInfInterval!Date(Date(2010, 7, 4))); - - _assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 3)).span(posInfInterval), - PosInfInterval!Date(Date(2010, 7, 3))); - _assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 4)).span(posInfInterval), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 5)).span(posInfInterval), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 6)).span(posInfInterval), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 7)).span(posInfInterval), - PosInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(PosInfInterval!Date(Date(2012, 1, 8)).span(posInfInterval), - PosInfInterval!Date(Date(2010, 7, 4))); - - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 3))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 4))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2010, 7, 5))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 6))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 7))))); - static assert(!__traits(compiles, posInfInterval.span(NegInfInterval!Date(Date(2012, 1, 8))))); + //Verify Examples. + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(500, 8, 9), Date(1602, 1, 31))) == PosInfInterval!Date(Date(500, 8, 9))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == PosInfInterval!Date(Date(1990, 7 , 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == PosInfInterval!Date(Date(1996, 1 , 2))); - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, posInfInterval.span(interval))); - static assert(__traits(compiles, posInfInterval.span(cInterval))); - static assert(__traits(compiles, posInfInterval.span(iInterval))); - static assert(__traits(compiles, posInfInterval.span(posInfInterval))); - static assert(__traits(compiles, posInfInterval.span(cPosInfInterval))); - static assert(__traits(compiles, posInfInterval.span(iPosInfInterval))); - static assert(!__traits(compiles, posInfInterval.span(negInfInterval))); - static assert(!__traits(compiles, posInfInterval.span(cNegInfInterval))); - static assert(!__traits(compiles, posInfInterval.span(iNegInfInterval))); - static assert(__traits(compiles, cPosInfInterval.span(interval))); - static assert(__traits(compiles, cPosInfInterval.span(cInterval))); - static assert(__traits(compiles, cPosInfInterval.span(iInterval))); - static assert(__traits(compiles, cPosInfInterval.span(posInfInterval))); - static assert(__traits(compiles, cPosInfInterval.span(cPosInfInterval))); - static assert(__traits(compiles, cPosInfInterval.span(iPosInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.span(negInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.span(cNegInfInterval))); - static assert(!__traits(compiles, cPosInfInterval.span(iNegInfInterval))); - static assert(__traits(compiles, iPosInfInterval.span(interval))); - static assert(__traits(compiles, iPosInfInterval.span(cInterval))); - static assert(__traits(compiles, iPosInfInterval.span(iInterval))); - static assert(__traits(compiles, iPosInfInterval.span(posInfInterval))); - static assert(__traits(compiles, iPosInfInterval.span(cPosInfInterval))); - static assert(__traits(compiles, iPosInfInterval.span(iPosInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.span(negInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.span(cNegInfInterval))); - static assert(!__traits(compiles, iPosInfInterval.span(iNegInfInterval))); - - //Verify Examples. - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(500, 8, 9), Date(1602, 1, 31))) == PosInfInterval!Date(Date(500, 8, 9))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == PosInfInterval!Date(Date(1990, 7 , 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17))) == PosInfInterval!Date(Date(1996, 1 , 2))); - - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); - assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1999, 1, 12))) == PosInfInterval!Date(Date(1996, 1 , 2))); - } + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1990, 7, 6))) == PosInfInterval!Date(Date(1990, 7 , 6))); + assert(PosInfInterval!Date(Date(1996, 1, 2)).span(PosInfInterval!Date(Date(1999, 1, 12))) == PosInfInterval!Date(Date(1996, 1 , 2))); } //Test PosInfInterval's shift(). unittest { - version(testStdDateTime) - { - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.shift(duration); - _assertPred!"=="(interval, expected, "", __FILE__, line); - } + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.shift(duration); + assert(interval == expected); + } - testInterval(interval, dur!"days"(22), PosInfInterval!Date(Date(2010, 7, 26))); - testInterval(interval, dur!"days"(-22), PosInfInterval!Date(Date(2010, 6, 12))); + testInterval(interval, dur!"days"(22), PosInfInterval!Date(Date(2010, 7, 26))); + testInterval(interval, dur!"days"(-22), PosInfInterval!Date(Date(2010, 6, 12))); - const cInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); + const cInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); - //Verify Examples. - auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); - auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + //Verify Examples. + auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); + auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - interval1.shift(dur!"days"(50)); - assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); + interval1.shift(dur!"days"(50)); + assert(interval1 == PosInfInterval!Date(Date(1996, 2, 21))); - interval2.shift(dur!"days"(-50)); - assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); - } + interval2.shift(dur!"days"(-50)); + assert(interval2 == PosInfInterval!Date(Date(1995, 11, 13))); } //Test PosInfInterval's shift(int, int, AllowDayOverflow). unittest { - version(testStdDateTime) { - { - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) - { - interval.shift(years, months, allow); - _assertPred!"=="(interval, expected, "", __FILE__, line); - } + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) + { + interval.shift(years, months, allow); + assert(interval == expected); + } - testInterval(interval, 5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2015, 7, 4))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2005, 7, 4))); + testInterval(interval, 5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2015, 7, 4))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2005, 7, 4))); - auto interval2 = PosInfInterval!Date(Date(2000, 1, 29)); + auto interval2 = PosInfInterval!Date(Date(2000, 1, 29)); - testInterval(interval2, 1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2001, 3, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2000, 12, 29))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1998, 12, 29))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1999, 3, 1))); + testInterval(interval2, 1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2001, 3, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2000, 12, 29))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1998, 12, 29))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1999, 3, 1))); - testInterval(interval2, 1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(2001, 2, 28))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(2000, 12, 29))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(1998, 12, 29))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(1999, 2, 28))); - } + testInterval(interval2, 1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(2001, 2, 28))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(2000, 12, 29))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(1998, 12, 29))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(1999, 2, 28))); + } - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(!__traits(compiles, cPosInfInterval.shift(1))); - static assert(!__traits(compiles, iPosInfInterval.shift(1))); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(!__traits(compiles, cPosInfInterval.shift(1))); + static assert(!__traits(compiles, iPosInfInterval.shift(1))); - //Verify Examples. - auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); - auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + //Verify Examples. + auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); + auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - interval1.shift(2); - assert(interval1 == PosInfInterval!Date(Date(1998, 1, 2))); + interval1.shift(2); + assert(interval1 == PosInfInterval!Date(Date(1998, 1, 2))); - interval2.shift(-2); - assert(interval2 == PosInfInterval!Date(Date(1994, 1, 2))); - } + interval2.shift(-2); + assert(interval2 == PosInfInterval!Date(Date(1994, 1, 2))); } //Test PosInfInterval's expand(). unittest { - version(testStdDateTime) - { - auto interval = PosInfInterval!Date(Date(2000, 7, 4)); + auto interval = PosInfInterval!Date(Date(2000, 7, 4)); - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.expand(duration); - _assertPred!"=="(interval, expected, "", __FILE__, line); - } + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.expand(duration); + assert(interval == expected); + } - testInterval(interval, dur!"days"(22), PosInfInterval!Date(Date(2000, 6, 12))); - testInterval(interval, dur!"days"(-22), PosInfInterval!Date(Date(2000, 7, 26))); + testInterval(interval, dur!"days"(22), PosInfInterval!Date(Date(2000, 6, 12))); + testInterval(interval, dur!"days"(-22), PosInfInterval!Date(Date(2000, 7, 26))); - const cInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); + const cInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); - //Verify Examples. - auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); - auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + //Verify Examples. + auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); + auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - interval1.expand(dur!"days"(2)); - assert(interval1 == PosInfInterval!Date(Date(1995, 12, 31))); + interval1.expand(dur!"days"(2)); + assert(interval1 == PosInfInterval!Date(Date(1995, 12, 31))); - interval2.expand(dur!"days"(-2)); - assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); - } + interval2.expand(dur!"days"(-2)); + assert(interval2 == PosInfInterval!Date(Date(1996, 1, 4))); } //Test PosInfInterval's expand(int, int, AllowDayOverflow). unittest { - version(testStdDateTime) { - { - auto interval = PosInfInterval!Date(Date(2000, 7, 4)); + auto interval = PosInfInterval!Date(Date(2000, 7, 4)); - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) - { - interval.expand(years, months, allow); - _assertPred!"=="(interval, expected, "", __FILE__, line); - } + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) + { + interval.expand(years, months, allow); + assert(interval == expected); + } - testInterval(interval, 5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(1995, 7, 4))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2005, 7, 4))); + testInterval(interval, 5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(1995, 7, 4))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, PosInfInterval!Date(Date(2005, 7, 4))); - auto interval2 = PosInfInterval!Date(Date(2000, 1, 29)); + auto interval2 = PosInfInterval!Date(Date(2000, 1, 29)); - testInterval(interval2, 1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1998, 12, 29))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1999, 3, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2001, 3, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2000, 12, 29))); + testInterval(interval2, 1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1998, 12, 29))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(1999, 3, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2001, 3, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, PosInfInterval!Date(Date(2000, 12, 29))); - testInterval(interval2, 1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(1998, 12, 29))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(1999, 2, 28))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(2001, 2, 28))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(2000, 12, 29))); - } + testInterval(interval2, 1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(1998, 12, 29))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(1999, 2, 28))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, PosInfInterval!Date(Date(2001, 2, 28))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, PosInfInterval!Date(Date(2000, 12, 29))); + } - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(!__traits(compiles, cPosInfInterval.expand(1))); - static assert(!__traits(compiles, iPosInfInterval.expand(1))); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(!__traits(compiles, cPosInfInterval.expand(1))); + static assert(!__traits(compiles, iPosInfInterval.expand(1))); - //Verify Examples. - auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); - auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); + //Verify Examples. + auto interval1 = PosInfInterval!Date(Date(1996, 1, 2)); + auto interval2 = PosInfInterval!Date(Date(1996, 1, 2)); - interval1.expand(2); - assert(interval1 == PosInfInterval!Date(Date(1994, 1, 2))); + interval1.expand(2); + assert(interval1 == PosInfInterval!Date(Date(1994, 1, 2))); - interval2.expand(-2); - assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); - } + interval2.expand(-2); + assert(interval2 == PosInfInterval!Date(Date(1998, 1, 2))); } //Test PosInfInterval's fwdRange(). unittest { - version(testStdDateTime) - { - auto posInfInterval = PosInfInterval!Date(Date(2010, 9, 19)); - - static void testInterval(PosInfInterval!Date posInfInterval) - { - posInfInterval.fwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).popFront(); - } + auto posInfInterval = PosInfInterval!Date(Date(2010, 9, 19)); - assertThrown!DateTimeException(testInterval(posInfInterval)); + static void testInterval(PosInfInterval!Date posInfInterval) + { + posInfInterval.fwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).popFront(); + } - _assertPred!"=="(PosInfInterval!Date(Date(2010, 9, 12)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).front, - Date(2010, 9, 12)); + assertThrown!DateTimeException(testInterval(posInfInterval)); - _assertPred!"=="(PosInfInterval!Date(Date(2010, 9, 12)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).front, - Date(2010, 9, 17)); + assert(PosInfInterval!Date(Date(2010, 9, 12)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)).front == + Date(2010, 9, 12)); - //Verify Examples. - auto interval = PosInfInterval!Date(Date(2010, 9, 1)); - auto func = delegate (in Date date) - { - if((date.day & 1) == 0) - return date + dur!"days"(2); + assert(PosInfInterval!Date(Date(2010, 9, 12)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri), PopFirst.yes).front == + Date(2010, 9, 17)); - return date + dur!"days"(1); - }; - auto range = interval.fwdRange(func); + //Verify Examples. + auto interval = PosInfInterval!Date(Date(2010, 9, 1)); + auto func = delegate (in Date date) + { + if((date.day & 1) == 0) + return date + dur!"days"(2); - assert(range.front == Date(2010, 9, 1)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). + return date + dur!"days"(1); + }; + auto range = interval.fwdRange(func); - range.popFront(); - assert(range.front == Date(2010, 9, 2)); + assert(range.front == Date(2010, 9, 1)); //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 2). - range.popFront(); - assert(range.front == Date(2010, 9, 4)); + range.popFront(); + assert(range.front == Date(2010, 9, 2)); - range.popFront(); - assert(range.front == Date(2010, 9, 6)); + range.popFront(); + assert(range.front == Date(2010, 9, 4)); - range.popFront(); - assert(range.front == Date(2010, 9, 8)); + range.popFront(); + assert(range.front == Date(2010, 9, 6)); - range.popFront(); - assert(!range.empty); + range.popFront(); + assert(range.front == Date(2010, 9, 8)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(__traits(compiles, cPosInfInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); - static assert(__traits(compiles, iPosInfInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); - } + range.popFront(); + assert(!range.empty); + + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(__traits(compiles, cPosInfInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); + static assert(__traits(compiles, iPosInfInterval.fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)))); } //Test PosInfInterval's toString(). unittest { - version(testStdDateTime) - { - _assertPred!"=="(PosInfInterval!Date(Date(2010, 7, 4)).toString(), "[2010-Jul-04 - ∞)"); + assert(PosInfInterval!Date(Date(2010, 7, 4)).toString() == "[2010-Jul-04 - ∞)"); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - static assert(__traits(compiles, cPosInfInterval.toString())); - static assert(__traits(compiles, iPosInfInterval.toString())); - } + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + static assert(__traits(compiles, cPosInfInterval.toString())); + static assert(__traits(compiles, iPosInfInterval.toString())); } @@ -23550,10 +22044,9 @@ auto interval = PosInfInterval!Date(Date(1996, 1, 2)); Params: rhs = The $(D NegInfInterval) to assign to this one. +/ - /+ref+/ NegInfInterval opAssign(const ref NegInfInterval rhs) pure nothrow + ref NegInfInterval opAssign(const ref NegInfInterval rhs) pure nothrow { _end = cast(TP)rhs._end; - return this; } @@ -23562,10 +22055,9 @@ auto interval = PosInfInterval!Date(Date(1996, 1, 2)); Params: rhs = The $(D NegInfInterval) to assign to this one. +/ - /+ref+/ NegInfInterval opAssign(NegInfInterval rhs) pure nothrow + ref NegInfInterval opAssign(NegInfInterval rhs) pure nothrow { _end = cast(TP)rhs._end; - return this; } @@ -24556,1234 +23048,1174 @@ private: //Test NegInfInterval's constructor. unittest { - version(testStdDateTime) - { - NegInfInterval!Date(Date.init); - NegInfInterval!TimeOfDay(TimeOfDay.init); - NegInfInterval!DateTime(DateTime.init); - NegInfInterval!SysTime(SysTime(0)); - } + NegInfInterval!Date(Date.init); + NegInfInterval!TimeOfDay(TimeOfDay.init); + NegInfInterval!DateTime(DateTime.init); + NegInfInterval!SysTime(SysTime(0)); } //Test NegInfInterval's end. unittest { - version(testStdDateTime) - { - _assertPred!"=="(NegInfInterval!Date(Date(2010, 1, 1)).end, Date(2010, 1, 1)); - _assertPred!"=="(NegInfInterval!Date(Date(2010, 1, 1)).end, Date(2010, 1, 1)); - _assertPred!"=="(NegInfInterval!Date(Date(1998, 1, 1)).end, Date(1998, 1, 1)); + assert(NegInfInterval!Date(Date(2010, 1, 1)).end == Date(2010, 1, 1)); + assert(NegInfInterval!Date(Date(2010, 1, 1)).end == Date(2010, 1, 1)); + assert(NegInfInterval!Date(Date(1998, 1, 1)).end == Date(1998, 1, 1)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, cNegInfInterval.end)); - static assert(__traits(compiles, iNegInfInterval.end)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, cNegInfInterval.end)); + static assert(__traits(compiles, iNegInfInterval.end)); - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).end == Date(2012, 3, 1)); - } + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).end == Date(2012, 3, 1)); } //Test NegInfInterval's empty. unittest { - version(testStdDateTime) - { - assert(!NegInfInterval!Date(Date(2010, 1, 1)).empty); - assert(!NegInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); - assert(!NegInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); - assert(!NegInfInterval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0))).empty); + assert(!NegInfInterval!Date(Date(2010, 1, 1)).empty); + assert(!NegInfInterval!TimeOfDay(TimeOfDay(0, 30, 0)).empty); + assert(!NegInfInterval!DateTime(DateTime(2010, 1, 1, 0, 30, 0)).empty); + assert(!NegInfInterval!SysTime(SysTime(DateTime(2010, 1, 1, 0, 30, 0))).empty); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, cNegInfInterval.empty)); - static assert(__traits(compiles, iNegInfInterval.empty)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, cNegInfInterval.empty)); + static assert(__traits(compiles, iNegInfInterval.empty)); - //Verify Examples. - assert(!NegInfInterval!Date(Date(1996, 1, 2)).empty); - } + //Verify Examples. + assert(!NegInfInterval!Date(Date(1996, 1, 2)).empty); } //Test NegInfInterval's contains(time point). unittest { - version(testStdDateTime) - { - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + assert(negInfInterval.contains(Date(2009, 7, 4))); + assert(negInfInterval.contains(Date(2010, 7, 3))); + assert(negInfInterval.contains(Date(2010, 7, 4))); + assert(negInfInterval.contains(Date(2010, 7, 5))); + assert(negInfInterval.contains(Date(2011, 7, 1))); + assert(negInfInterval.contains(Date(2012, 1, 6))); + assert(!negInfInterval.contains(Date(2012, 1, 7))); + assert(!negInfInterval.contains(Date(2012, 1, 8))); + assert(!negInfInterval.contains(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.contains(cdate))); + static assert(__traits(compiles, cNegInfInterval.contains(cdate))); + static assert(__traits(compiles, iNegInfInterval.contains(cdate))); - assert(negInfInterval.contains(Date(2009, 7, 4))); - assert(negInfInterval.contains(Date(2010, 7, 3))); - assert(negInfInterval.contains(Date(2010, 7, 4))); - assert(negInfInterval.contains(Date(2010, 7, 5))); - assert(negInfInterval.contains(Date(2011, 7, 1))); - assert(negInfInterval.contains(Date(2012, 1, 6))); - assert(!negInfInterval.contains(Date(2012, 1, 7))); - assert(!negInfInterval.contains(Date(2012, 1, 8))); - assert(!negInfInterval.contains(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.contains(cdate))); - static assert(__traits(compiles, cNegInfInterval.contains(cdate))); - static assert(__traits(compiles, iNegInfInterval.contains(cdate))); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(1994, 12, 24))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2000, 1, 5))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2012, 3, 1))); - } + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(1994, 12, 24))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2000, 1, 5))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Date(2012, 3, 1))); } //Test NegInfInterval's contains(Interval). unittest { - version(testStdDateTime) - { - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.contains(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(negInfInterval.contains(negInfInterval)); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!negInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(negInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(!NegInfInterval!Date(Date(2010, 7, 3)).contains(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 4)).contains(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 5)).contains(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 6)).contains(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 7)).contains(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 8)).contains(negInfInterval)); + + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.contains(interval))); + static assert(__traits(compiles, negInfInterval.contains(cInterval))); + static assert(__traits(compiles, negInfInterval.contains(iInterval))); + static assert(__traits(compiles, negInfInterval.contains(posInfInterval))); + static assert(__traits(compiles, negInfInterval.contains(cPosInfInterval))); + static assert(__traits(compiles, negInfInterval.contains(iPosInfInterval))); + static assert(__traits(compiles, negInfInterval.contains(negInfInterval))); + static assert(__traits(compiles, negInfInterval.contains(cNegInfInterval))); + static assert(__traits(compiles, negInfInterval.contains(iNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.contains(interval))); + static assert(__traits(compiles, cNegInfInterval.contains(cInterval))); + static assert(__traits(compiles, cNegInfInterval.contains(iInterval))); + static assert(__traits(compiles, cNegInfInterval.contains(posInfInterval))); + static assert(__traits(compiles, cNegInfInterval.contains(cPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.contains(iPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.contains(negInfInterval))); + static assert(__traits(compiles, cNegInfInterval.contains(cNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.contains(iNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.contains(interval))); + static assert(__traits(compiles, iNegInfInterval.contains(cInterval))); + static assert(__traits(compiles, iNegInfInterval.contains(iInterval))); + static assert(__traits(compiles, iNegInfInterval.contains(posInfInterval))); + static assert(__traits(compiles, iNegInfInterval.contains(cPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.contains(iPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.contains(negInfInterval))); + static assert(__traits(compiles, iNegInfInterval.contains(cNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.contains(iNegInfInterval))); - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) - { - negInfInterval.contains(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(negInfInterval.contains(negInfInterval)); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!negInfInterval.contains(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!negInfInterval.contains(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(negInfInterval.contains(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(negInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!negInfInterval.contains(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(negInfInterval.contains(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.contains(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(!NegInfInterval!Date(Date(2010, 7, 3)).contains(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 4)).contains(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 5)).contains(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 6)).contains(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 7)).contains(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 8)).contains(negInfInterval)); - - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.contains(PosInfInterval!Date(Date(2012, 1, 8)))); + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.contains(interval))); - static assert(__traits(compiles, negInfInterval.contains(cInterval))); - static assert(__traits(compiles, negInfInterval.contains(iInterval))); - static assert(__traits(compiles, negInfInterval.contains(posInfInterval))); - static assert(__traits(compiles, negInfInterval.contains(cPosInfInterval))); - static assert(__traits(compiles, negInfInterval.contains(iPosInfInterval))); - static assert(__traits(compiles, negInfInterval.contains(negInfInterval))); - static assert(__traits(compiles, negInfInterval.contains(cNegInfInterval))); - static assert(__traits(compiles, negInfInterval.contains(iNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.contains(interval))); - static assert(__traits(compiles, cNegInfInterval.contains(cInterval))); - static assert(__traits(compiles, cNegInfInterval.contains(iInterval))); - static assert(__traits(compiles, cNegInfInterval.contains(posInfInterval))); - static assert(__traits(compiles, cNegInfInterval.contains(cPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.contains(iPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.contains(negInfInterval))); - static assert(__traits(compiles, cNegInfInterval.contains(cNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.contains(iNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.contains(interval))); - static assert(__traits(compiles, iNegInfInterval.contains(cInterval))); - static assert(__traits(compiles, iNegInfInterval.contains(iInterval))); - static assert(__traits(compiles, iNegInfInterval.contains(posInfInterval))); - static assert(__traits(compiles, iNegInfInterval.contains(cPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.contains(iPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.contains(negInfInterval))); - static assert(__traits(compiles, iNegInfInterval.contains(cNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.contains(iNegInfInterval))); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(Interval!Date(Date(1998, 2, 28), Date(2013, 5, 1)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(2013, 7, 9)))); - } + assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(PosInfInterval!Date(Date(1999, 5, 4)))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).contains(NegInfInterval!Date(Date(2013, 7, 9)))); } //Test NegInfInterval's isBefore(time point). unittest { - version(testStdDateTime) - { - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + assert(!negInfInterval.isBefore(Date(2009, 7, 4))); + assert(!negInfInterval.isBefore(Date(2010, 7, 3))); + assert(!negInfInterval.isBefore(Date(2010, 7, 4))); + assert(!negInfInterval.isBefore(Date(2010, 7, 5))); + assert(!negInfInterval.isBefore(Date(2011, 7, 1))); + assert(!negInfInterval.isBefore(Date(2012, 1, 6))); + assert(negInfInterval.isBefore(Date(2012, 1, 7))); + assert(negInfInterval.isBefore(Date(2012, 1, 8))); + assert(negInfInterval.isBefore(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.isBefore(cdate))); + static assert(__traits(compiles, cNegInfInterval.isBefore(cdate))); + static assert(__traits(compiles, iNegInfInterval.isBefore(cdate))); - assert(!negInfInterval.isBefore(Date(2009, 7, 4))); - assert(!negInfInterval.isBefore(Date(2010, 7, 3))); - assert(!negInfInterval.isBefore(Date(2010, 7, 4))); - assert(!negInfInterval.isBefore(Date(2010, 7, 5))); - assert(!negInfInterval.isBefore(Date(2011, 7, 1))); - assert(!negInfInterval.isBefore(Date(2012, 1, 6))); - assert(negInfInterval.isBefore(Date(2012, 1, 7))); - assert(negInfInterval.isBefore(Date(2012, 1, 8))); - assert(negInfInterval.isBefore(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.isBefore(cdate))); - static assert(__traits(compiles, cNegInfInterval.isBefore(cdate))); - static assert(__traits(compiles, iNegInfInterval.isBefore(cdate))); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); - } + //Verify Examples. + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(1994, 12, 24))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2000, 1, 5))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Date(2012, 3, 1))); } //Test NegInfInterval's isBefore(Interval). unittest { - version(testStdDateTime) - { - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.isBefore(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!negInfInterval.isBefore(negInfInterval)); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!negInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(negInfInterval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(negInfInterval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(!NegInfInterval!Date(Date(2010, 7, 3)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 4)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 5)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 6)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 7)).isBefore(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 8)).isBefore(negInfInterval)); + + assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.isBefore(interval))); + static assert(__traits(compiles, negInfInterval.isBefore(cInterval))); + static assert(__traits(compiles, negInfInterval.isBefore(iInterval))); + static assert(__traits(compiles, negInfInterval.isBefore(posInfInterval))); + static assert(__traits(compiles, negInfInterval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, negInfInterval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, negInfInterval.isBefore(negInfInterval))); + static assert(__traits(compiles, negInfInterval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, negInfInterval.isBefore(iNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(interval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(cInterval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(iInterval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(posInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(negInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isBefore(iNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(interval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(cInterval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(iInterval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(posInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(cPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(iPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(negInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(cNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isBefore(iNegInfInterval))); + + //Verify Examples. + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) - { - negInfInterval.isBefore(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!negInfInterval.isBefore(negInfInterval)); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!negInfInterval.isBefore(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(negInfInterval.isBefore(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(negInfInterval.isBefore(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isBefore(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(!NegInfInterval!Date(Date(2010, 7, 3)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 4)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 5)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 6)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 7)).isBefore(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 8)).isBefore(negInfInterval)); - - assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(negInfInterval.isBefore(PosInfInterval!Date(Date(2012, 1, 8)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2012, 3, 1)))); - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.isBefore(interval))); - static assert(__traits(compiles, negInfInterval.isBefore(cInterval))); - static assert(__traits(compiles, negInfInterval.isBefore(iInterval))); - static assert(__traits(compiles, negInfInterval.isBefore(posInfInterval))); - static assert(__traits(compiles, negInfInterval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, negInfInterval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, negInfInterval.isBefore(negInfInterval))); - static assert(__traits(compiles, negInfInterval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, negInfInterval.isBefore(iNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(interval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(cInterval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(iInterval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(posInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(negInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isBefore(iNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(interval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(cInterval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(iInterval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(posInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(cPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(iPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(negInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(cNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isBefore(iNegInfInterval))); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isBefore(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(2013, 7, 9)))); - } + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isBefore(NegInfInterval!Date(Date(2013, 7, 9)))); } //Test NegInfInterval's isAfter(time point). unittest { - version(testStdDateTime) - { - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - - assert(!negInfInterval.isAfter(Date(2009, 7, 4))); - assert(!negInfInterval.isAfter(Date(2010, 7, 3))); - assert(!negInfInterval.isAfter(Date(2010, 7, 4))); - assert(!negInfInterval.isAfter(Date(2010, 7, 5))); - assert(!negInfInterval.isAfter(Date(2011, 7, 1))); - assert(!negInfInterval.isAfter(Date(2012, 1, 6))); - assert(!negInfInterval.isAfter(Date(2012, 1, 7))); - assert(!negInfInterval.isAfter(Date(2012, 1, 8))); - assert(!negInfInterval.isAfter(Date(2013, 1, 7))); - - const cdate = Date(2010, 7, 6); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.isAfter(cdate))); - static assert(__traits(compiles, cNegInfInterval.isAfter(cdate))); - static assert(__traits(compiles, iNegInfInterval.isAfter(cdate))); - } + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + assert(!negInfInterval.isAfter(Date(2009, 7, 4))); + assert(!negInfInterval.isAfter(Date(2010, 7, 3))); + assert(!negInfInterval.isAfter(Date(2010, 7, 4))); + assert(!negInfInterval.isAfter(Date(2010, 7, 5))); + assert(!negInfInterval.isAfter(Date(2011, 7, 1))); + assert(!negInfInterval.isAfter(Date(2012, 1, 6))); + assert(!negInfInterval.isAfter(Date(2012, 1, 7))); + assert(!negInfInterval.isAfter(Date(2012, 1, 8))); + assert(!negInfInterval.isAfter(Date(2013, 1, 7))); + + const cdate = Date(2010, 7, 6); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.isAfter(cdate))); + static assert(__traits(compiles, cNegInfInterval.isAfter(cdate))); + static assert(__traits(compiles, iNegInfInterval.isAfter(cdate))); } //Test NegInfInterval's isAfter(Interval). unittest { - version(testStdDateTime) - { - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.isAfter(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!negInfInterval.isAfter(negInfInterval)); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(!NegInfInterval!Date(Date(2010, 7, 3)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 4)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 5)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 6)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 7)).isAfter(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 8)).isAfter(negInfInterval)); + + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.isAfter(interval))); + static assert(__traits(compiles, negInfInterval.isAfter(cInterval))); + static assert(__traits(compiles, negInfInterval.isAfter(iInterval))); + static assert(__traits(compiles, negInfInterval.isAfter(posInfInterval))); + static assert(__traits(compiles, negInfInterval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, negInfInterval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, negInfInterval.isAfter(negInfInterval))); + static assert(__traits(compiles, negInfInterval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, negInfInterval.isAfter(iNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(interval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(cInterval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(iInterval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(posInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(negInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAfter(iNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(interval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(cInterval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(iInterval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(posInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(cPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(iPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(negInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(cNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAfter(iNegInfInterval))); - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) - { - negInfInterval.isAfter(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!negInfInterval.isAfter(negInfInterval)); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!negInfInterval.isAfter(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(!NegInfInterval!Date(Date(2010, 7, 3)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 4)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 5)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 6)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 7)).isAfter(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 8)).isAfter(negInfInterval)); - - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isAfter(PosInfInterval!Date(Date(2012, 1, 8)))); + //Verify Examples. + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.isAfter(interval))); - static assert(__traits(compiles, negInfInterval.isAfter(cInterval))); - static assert(__traits(compiles, negInfInterval.isAfter(iInterval))); - static assert(__traits(compiles, negInfInterval.isAfter(posInfInterval))); - static assert(__traits(compiles, negInfInterval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, negInfInterval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, negInfInterval.isAfter(negInfInterval))); - static assert(__traits(compiles, negInfInterval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, negInfInterval.isAfter(iNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(interval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(cInterval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(iInterval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(posInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(negInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAfter(iNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(interval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(cInterval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(iInterval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(posInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(cPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(iPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(negInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(cNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAfter(iNegInfInterval))); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(1994, 12, 24))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2000, 1, 5))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Date(2012, 3, 1))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(2013, 7, 9)))); - } + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAfter(NegInfInterval!Date(Date(2013, 7, 9)))); } //Test NegInfInterval's intersects(). unittest { - version(testStdDateTime) - { - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.intersects(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(negInfInterval.intersects(negInfInterval)); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(negInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(negInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(!negInfInterval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!negInfInterval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(NegInfInterval!Date(Date(2010, 7, 3)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2010, 7, 4)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2010, 7, 5)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 6)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 7)).intersects(negInfInterval)); + assert(NegInfInterval!Date(Date(2012, 1, 8)).intersects(negInfInterval)); + + assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.intersects(interval))); + static assert(__traits(compiles, negInfInterval.intersects(cInterval))); + static assert(__traits(compiles, negInfInterval.intersects(iInterval))); + static assert(__traits(compiles, negInfInterval.intersects(posInfInterval))); + static assert(__traits(compiles, negInfInterval.intersects(cPosInfInterval))); + static assert(__traits(compiles, negInfInterval.intersects(iPosInfInterval))); + static assert(__traits(compiles, negInfInterval.intersects(negInfInterval))); + static assert(__traits(compiles, negInfInterval.intersects(cNegInfInterval))); + static assert(__traits(compiles, negInfInterval.intersects(iNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersects(interval))); + static assert(__traits(compiles, cNegInfInterval.intersects(cInterval))); + static assert(__traits(compiles, cNegInfInterval.intersects(iInterval))); + static assert(__traits(compiles, cNegInfInterval.intersects(posInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersects(cPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersects(iPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersects(negInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersects(cNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersects(iNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersects(interval))); + static assert(__traits(compiles, iNegInfInterval.intersects(cInterval))); + static assert(__traits(compiles, iNegInfInterval.intersects(iInterval))); + static assert(__traits(compiles, iNegInfInterval.intersects(posInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersects(cPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersects(iPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersects(negInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersects(cNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersects(iNegInfInterval))); - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) - { - negInfInterval.intersects(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(negInfInterval.intersects(negInfInterval)); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(negInfInterval.intersects(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(negInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(negInfInterval.intersects(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(!negInfInterval.intersects(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!negInfInterval.intersects(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(negInfInterval.intersects(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(NegInfInterval!Date(Date(2010, 7, 3)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2010, 7, 4)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2010, 7, 5)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 6)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 7)).intersects(negInfInterval)); - assert(NegInfInterval!Date(Date(2012, 1, 8)).intersects(negInfInterval)); - - assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(negInfInterval.intersects(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.intersects(PosInfInterval!Date(Date(2012, 1, 8)))); + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.intersects(interval))); - static assert(__traits(compiles, negInfInterval.intersects(cInterval))); - static assert(__traits(compiles, negInfInterval.intersects(iInterval))); - static assert(__traits(compiles, negInfInterval.intersects(posInfInterval))); - static assert(__traits(compiles, negInfInterval.intersects(cPosInfInterval))); - static assert(__traits(compiles, negInfInterval.intersects(iPosInfInterval))); - static assert(__traits(compiles, negInfInterval.intersects(negInfInterval))); - static assert(__traits(compiles, negInfInterval.intersects(cNegInfInterval))); - static assert(__traits(compiles, negInfInterval.intersects(iNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersects(interval))); - static assert(__traits(compiles, cNegInfInterval.intersects(cInterval))); - static assert(__traits(compiles, cNegInfInterval.intersects(iInterval))); - static assert(__traits(compiles, cNegInfInterval.intersects(posInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersects(cPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersects(iPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersects(negInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersects(cNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersects(iNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersects(interval))); - static assert(__traits(compiles, iNegInfInterval.intersects(cInterval))); - static assert(__traits(compiles, iNegInfInterval.intersects(iInterval))); - static assert(__traits(compiles, iNegInfInterval.intersects(posInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersects(cPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersects(iPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersects(negInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersects(cNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersects(iNegInfInterval))); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(1999, 1, 12), Date(2011, 9, 17)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2013, 7, 9)))); - } + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).intersects(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersects(NegInfInterval!Date(Date(2013, 7, 9)))); } //Test NegInfInterval's intersection(). unittest { - version(testStdDateTime) - { - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I, J)(in I interval1, in J interval2) + { + interval1.intersection(interval2); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assertThrown!DateTimeException(testInterval(negInfInterval, PosInfInterval!Date(Date(2012, 1, 7)))); + assertThrown!DateTimeException(testInterval(negInfInterval, PosInfInterval!Date(Date(2012, 1, 8)))); + + assert(negInfInterval.intersection(negInfInterval) == + negInfInterval); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == + Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); + assert(negInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); + assert(negInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + assert(negInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); + + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 3))) == + NegInfInterval!Date(Date(2010, 7, 3))); + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 4))) == + NegInfInterval!Date(Date(2010, 7, 4))); + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 5))) == + NegInfInterval!Date(Date(2010, 7, 5))); + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 6))) == + NegInfInterval!Date(Date(2012, 1, 6))); + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 7))); + + assert(NegInfInterval!Date(Date(2010, 7, 3)).intersection(negInfInterval) == + NegInfInterval!Date(Date(2010, 7, 3))); + assert(NegInfInterval!Date(Date(2010, 7, 4)).intersection(negInfInterval) == + NegInfInterval!Date(Date(2010, 7, 4))); + assert(NegInfInterval!Date(Date(2010, 7, 5)).intersection(negInfInterval) == + NegInfInterval!Date(Date(2010, 7, 5))); + assert(NegInfInterval!Date(Date(2012, 1, 6)).intersection(negInfInterval) == + NegInfInterval!Date(Date(2012, 1, 6))); + assert(NegInfInterval!Date(Date(2012, 1, 7)).intersection(negInfInterval) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 8)).intersection(negInfInterval) == + NegInfInterval!Date(Date(2012, 1, 7))); + + assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 3))) == + Interval!Date(Date(2010, 7, 3), Date(2012, 1 ,7))); + assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 4))) == + Interval!Date(Date(2010, 7, 4), Date(2012, 1 ,7))); + assert(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 5))) == + Interval!Date(Date(2010, 7, 5), Date(2012, 1 ,7))); + assert(negInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 6))) == + Interval!Date(Date(2012, 1, 6), Date(2012, 1 ,7))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.intersection(interval))); + static assert(__traits(compiles, negInfInterval.intersection(cInterval))); + static assert(__traits(compiles, negInfInterval.intersection(iInterval))); + static assert(__traits(compiles, negInfInterval.intersection(posInfInterval))); + static assert(__traits(compiles, negInfInterval.intersection(cPosInfInterval))); + static assert(__traits(compiles, negInfInterval.intersection(iPosInfInterval))); + static assert(__traits(compiles, negInfInterval.intersection(negInfInterval))); + static assert(__traits(compiles, negInfInterval.intersection(cNegInfInterval))); + static assert(__traits(compiles, negInfInterval.intersection(iNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersection(interval))); + static assert(__traits(compiles, cNegInfInterval.intersection(cInterval))); + static assert(__traits(compiles, cNegInfInterval.intersection(iInterval))); + static assert(__traits(compiles, cNegInfInterval.intersection(posInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersection(cPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersection(iPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersection(negInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersection(cNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.intersection(iNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersection(interval))); + static assert(__traits(compiles, iNegInfInterval.intersection(cInterval))); + static assert(__traits(compiles, iNegInfInterval.intersection(iInterval))); + static assert(__traits(compiles, iNegInfInterval.intersection(posInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersection(cPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersection(iPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersection(negInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersection(cNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.intersection(iNegInfInterval))); - static void testInterval(I, J)(in I interval1, in J interval2) - { - interval1.intersection(interval2); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assertThrown!DateTimeException(testInterval(negInfInterval, PosInfInterval!Date(Date(2012, 1, 7)))); - assertThrown!DateTimeException(testInterval(negInfInterval, PosInfInterval!Date(Date(2012, 1, 8)))); - - _assertPred!"=="(negInfInterval.intersection(negInfInterval), - negInfInterval); - _assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))), - Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))); - _assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), - Interval!Date(Date(2010, 7, 1), Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))), - Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))); - _assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), - Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))); - _assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), - Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))); - _assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), - Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.intersection(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), - Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))); - - _assertPred!"=="(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 3))), - NegInfInterval!Date(Date(2010, 7, 3))); - _assertPred!"=="(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 4))), - NegInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(negInfInterval.intersection(NegInfInterval!Date(Date(2010, 7, 5))), - NegInfInterval!Date(Date(2010, 7, 5))); - _assertPred!"=="(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 6))), - NegInfInterval!Date(Date(2012, 1, 6))); - _assertPred!"=="(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 7))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.intersection(NegInfInterval!Date(Date(2012, 1, 8))), - NegInfInterval!Date(Date(2012, 1, 7))); - - _assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 3)).intersection(negInfInterval), - NegInfInterval!Date(Date(2010, 7, 3))); - _assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 4)).intersection(negInfInterval), - NegInfInterval!Date(Date(2010, 7, 4))); - _assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 5)).intersection(negInfInterval), - NegInfInterval!Date(Date(2010, 7, 5))); - _assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 6)).intersection(negInfInterval), - NegInfInterval!Date(Date(2012, 1, 6))); - _assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 7)).intersection(negInfInterval), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 8)).intersection(negInfInterval), - NegInfInterval!Date(Date(2012, 1, 7))); - - _assertPred!"=="(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 3))), - Interval!Date(Date(2010, 7, 3), Date(2012, 1 ,7))); - _assertPred!"=="(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 4))), - Interval!Date(Date(2010, 7, 4), Date(2012, 1 ,7))); - _assertPred!"=="(negInfInterval.intersection(PosInfInterval!Date(Date(2010, 7, 5))), - Interval!Date(Date(2010, 7, 5), Date(2012, 1 ,7))); - _assertPred!"=="(negInfInterval.intersection(PosInfInterval!Date(Date(2012, 1, 6))), - Interval!Date(Date(2012, 1, 6), Date(2012, 1 ,7))); + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1990, 7 , 6), Date(2000, 8, 2))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.intersection(interval))); - static assert(__traits(compiles, negInfInterval.intersection(cInterval))); - static assert(__traits(compiles, negInfInterval.intersection(iInterval))); - static assert(__traits(compiles, negInfInterval.intersection(posInfInterval))); - static assert(__traits(compiles, negInfInterval.intersection(cPosInfInterval))); - static assert(__traits(compiles, negInfInterval.intersection(iPosInfInterval))); - static assert(__traits(compiles, negInfInterval.intersection(negInfInterval))); - static assert(__traits(compiles, negInfInterval.intersection(cNegInfInterval))); - static assert(__traits(compiles, negInfInterval.intersection(iNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersection(interval))); - static assert(__traits(compiles, cNegInfInterval.intersection(cInterval))); - static assert(__traits(compiles, cNegInfInterval.intersection(iInterval))); - static assert(__traits(compiles, cNegInfInterval.intersection(posInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersection(cPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersection(iPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersection(negInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersection(cNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.intersection(iNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersection(interval))); - static assert(__traits(compiles, iNegInfInterval.intersection(cInterval))); - static assert(__traits(compiles, iNegInfInterval.intersection(iInterval))); - static assert(__traits(compiles, iNegInfInterval.intersection(posInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersection(cPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersection(iPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersection(negInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersection(cNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.intersection(iNegInfInterval))); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == Interval!Date(Date(1990, 7 , 6), Date(2000, 8, 2))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == NegInfInterval!Date(Date(1999, 7 , 6))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2012, 3 , 1))); - } + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1990, 7, 6))) == Interval!Date(Date(1990, 7 , 6), Date(2012, 3, 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(PosInfInterval!Date(Date(1999, 1, 12))) == Interval!Date(Date(1999, 1 , 12), Date(2012, 3, 1))); + + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(1999, 7, 6))) == NegInfInterval!Date(Date(1999, 7 , 6))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).intersection(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2012, 3 , 1))); } //Test NegInfInterval's isAdjacent(). unittest { - version(testStdDateTime) - { - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) + { + negInfInterval.isAdjacent(interval); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(!negInfInterval.isAdjacent(negInfInterval)); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); + assert(negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); + assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); + + assert(!NegInfInterval!Date(Date(2010, 7, 3)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 4)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2010, 7, 5)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 6)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 7)).isAdjacent(negInfInterval)); + assert(!NegInfInterval!Date(Date(2012, 1, 8)).isAdjacent(negInfInterval)); + + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); + assert(negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); + assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.isAdjacent(interval))); + static assert(__traits(compiles, negInfInterval.isAdjacent(cInterval))); + static assert(__traits(compiles, negInfInterval.isAdjacent(iInterval))); + static assert(__traits(compiles, negInfInterval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, negInfInterval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, negInfInterval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, negInfInterval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, negInfInterval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, negInfInterval.isAdjacent(iNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(interval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(cInterval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(iInterval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.isAdjacent(iNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(interval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(cInterval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(iInterval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(posInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(cPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(iPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(negInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(cNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.isAdjacent(iNegInfInterval))); - static void testInterval(in NegInfInterval!Date negInfInterval, in Interval!Date interval) - { - negInfInterval.isAdjacent(interval); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assert(!negInfInterval.isAdjacent(negInfInterval)); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8)))); - assert(negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8)))); - assert(!negInfInterval.isAdjacent(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 6)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(NegInfInterval!Date(Date(2012, 1, 8)))); - - assert(!NegInfInterval!Date(Date(2010, 7, 3)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 4)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2010, 7, 5)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 6)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 7)).isAdjacent(negInfInterval)); - assert(!NegInfInterval!Date(Date(2012, 1, 8)).isAdjacent(negInfInterval)); - - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 3)))); - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 4)))); - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2010, 7, 5)))); - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 6)))); - assert(negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 7)))); - assert(!negInfInterval.isAdjacent(PosInfInterval!Date(Date(2012, 1, 8)))); + //Verify Examples. + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2012, 3, 1), Date(2019, 2, 2)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.isAdjacent(interval))); - static assert(__traits(compiles, negInfInterval.isAdjacent(cInterval))); - static assert(__traits(compiles, negInfInterval.isAdjacent(iInterval))); - static assert(__traits(compiles, negInfInterval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, negInfInterval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, negInfInterval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, negInfInterval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, negInfInterval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, negInfInterval.isAdjacent(iNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(interval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(cInterval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(iInterval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.isAdjacent(iNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(interval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(cInterval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(iInterval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(posInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(cPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(iPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(negInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(cNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.isAdjacent(iNegInfInterval))); - - //Verify Examples. - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(1999, 1, 12), Date(2012, 3, 1)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2012, 3, 1), Date(2019, 2, 2)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(Interval!Date(Date(2022, 10, 19), Date(2027, 6, 3)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); - - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 5, 4)))); - assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(2012, 3, 1)))); - } + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(1999, 5, 4)))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(PosInfInterval!Date(Date(2012, 3, 1)))); + + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(1996, 5, 4)))); + assert(!NegInfInterval!Date(Date(2012, 3, 1)).isAdjacent(NegInfInterval!Date(Date(2012, 3, 1)))); } //Test NegInfInterval's merge(). unittest { - version(testStdDateTime) - { - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I, J)(in I interval1, in J interval2) + { + interval1.merge(interval2); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); + + assert(negInfInterval.merge(negInfInterval) == + negInfInterval); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + NegInfInterval!Date(Date(2013, 7, 3))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + assert(negInfInterval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + + assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 3))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 4))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 5))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 6))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + + assert(NegInfInterval!Date(Date(2010, 7, 3)).merge(negInfInterval) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2010, 7, 4)).merge(negInfInterval) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2010, 7, 5)).merge(negInfInterval) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 6)).merge(negInfInterval) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 7)).merge(negInfInterval) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 8)).merge(negInfInterval) == + NegInfInterval!Date(Date(2012, 1, 8))); + + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 3))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 4))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 5))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 6))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 7))))); + static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 8))))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.merge(interval))); + static assert(__traits(compiles, negInfInterval.merge(cInterval))); + static assert(__traits(compiles, negInfInterval.merge(iInterval))); + static assert(!__traits(compiles, negInfInterval.merge(posInfInterval))); + static assert(!__traits(compiles, negInfInterval.merge(cPosInfInterval))); + static assert(!__traits(compiles, negInfInterval.merge(iPosInfInterval))); + static assert(__traits(compiles, negInfInterval.merge(negInfInterval))); + static assert(__traits(compiles, negInfInterval.merge(cNegInfInterval))); + static assert(__traits(compiles, negInfInterval.merge(iNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.merge(interval))); + static assert(__traits(compiles, cNegInfInterval.merge(cInterval))); + static assert(__traits(compiles, cNegInfInterval.merge(iInterval))); + static assert(!__traits(compiles, cNegInfInterval.merge(posInfInterval))); + static assert(!__traits(compiles, cNegInfInterval.merge(cPosInfInterval))); + static assert(!__traits(compiles, cNegInfInterval.merge(iPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.merge(negInfInterval))); + static assert(__traits(compiles, cNegInfInterval.merge(cNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.merge(iNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.merge(interval))); + static assert(__traits(compiles, iNegInfInterval.merge(cInterval))); + static assert(__traits(compiles, iNegInfInterval.merge(iInterval))); + static assert(!__traits(compiles, iNegInfInterval.merge(posInfInterval))); + static assert(!__traits(compiles, iNegInfInterval.merge(cPosInfInterval))); + static assert(!__traits(compiles, iNegInfInterval.merge(iPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.merge(negInfInterval))); + static assert(__traits(compiles, iNegInfInterval.merge(cNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.merge(iNegInfInterval))); - static void testInterval(I, J)(in I interval1, in J interval2) - { - interval1.merge(interval2); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9)))); - - _assertPred!"=="(negInfInterval.merge(negInfInterval), - negInfInterval); - _assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), - NegInfInterval!Date(Date(2013, 7, 3))); - _assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), - NegInfInterval!Date(Date(2012, 1, 8))); - _assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), - NegInfInterval!Date(Date(2012, 1, 8))); - _assertPred!"=="(negInfInterval.merge(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))), - NegInfInterval!Date(Date(2012, 1, 8))); - - _assertPred!"=="(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 3))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 4))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.merge(NegInfInterval!Date(Date(2010, 7, 5))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 6))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 7))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.merge(NegInfInterval!Date(Date(2012, 1, 8))), - NegInfInterval!Date(Date(2012, 1, 8))); - - _assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 3)).merge(negInfInterval), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 4)).merge(negInfInterval), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 5)).merge(negInfInterval), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 6)).merge(negInfInterval), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 7)).merge(negInfInterval), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 8)).merge(negInfInterval), - NegInfInterval!Date(Date(2012, 1, 8))); - - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 3))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 4))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2010, 7, 5))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 6))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 7))))); - static assert(!__traits(compiles, negInfInterval.merge(PosInfInterval!Date(Date(2012, 1, 8))))); + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == NegInfInterval!Date(Date(2012, 3 , 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == NegInfInterval!Date(Date(2015, 9 , 2))); - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.merge(interval))); - static assert(__traits(compiles, negInfInterval.merge(cInterval))); - static assert(__traits(compiles, negInfInterval.merge(iInterval))); - static assert(!__traits(compiles, negInfInterval.merge(posInfInterval))); - static assert(!__traits(compiles, negInfInterval.merge(cPosInfInterval))); - static assert(!__traits(compiles, negInfInterval.merge(iPosInfInterval))); - static assert(__traits(compiles, negInfInterval.merge(negInfInterval))); - static assert(__traits(compiles, negInfInterval.merge(cNegInfInterval))); - static assert(__traits(compiles, negInfInterval.merge(iNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.merge(interval))); - static assert(__traits(compiles, cNegInfInterval.merge(cInterval))); - static assert(__traits(compiles, cNegInfInterval.merge(iInterval))); - static assert(!__traits(compiles, cNegInfInterval.merge(posInfInterval))); - static assert(!__traits(compiles, cNegInfInterval.merge(cPosInfInterval))); - static assert(!__traits(compiles, cNegInfInterval.merge(iPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.merge(negInfInterval))); - static assert(__traits(compiles, cNegInfInterval.merge(cNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.merge(iNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.merge(interval))); - static assert(__traits(compiles, iNegInfInterval.merge(cInterval))); - static assert(__traits(compiles, iNegInfInterval.merge(iInterval))); - static assert(!__traits(compiles, iNegInfInterval.merge(posInfInterval))); - static assert(!__traits(compiles, iNegInfInterval.merge(cPosInfInterval))); - static assert(!__traits(compiles, iNegInfInterval.merge(iPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.merge(negInfInterval))); - static assert(__traits(compiles, iNegInfInterval.merge(cNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.merge(iNegInfInterval))); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == NegInfInterval!Date(Date(2012, 3 , 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == NegInfInterval!Date(Date(2015, 9 , 2))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1999, 7, 6))) == NegInfInterval!Date(Date(2012, 3 , 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); - } + assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(1999, 7, 6))) == NegInfInterval!Date(Date(2012, 3 , 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).merge(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); } //Test NegInfInterval's span(). unittest { - version(testStdDateTime) - { - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + + static void testInterval(I, J)(in I interval1, in J interval2) + { + interval1.span(interval2); + } + + assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); + + assert(negInfInterval.span(negInfInterval) == + negInfInterval); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))) == + NegInfInterval!Date(Date(2013, 7, 3))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + assert(negInfInterval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + assert(negInfInterval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))) == + NegInfInterval!Date(Date(2012, 1, 9))); + + assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 3))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 4))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 5))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 6))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 7))) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 8))) == + NegInfInterval!Date(Date(2012, 1, 8))); + + assert(NegInfInterval!Date(Date(2010, 7, 3)).span(negInfInterval) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2010, 7, 4)).span(negInfInterval) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2010, 7, 5)).span(negInfInterval) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 6)).span(negInfInterval) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 7)).span(negInfInterval) == + NegInfInterval!Date(Date(2012, 1, 7))); + assert(NegInfInterval!Date(Date(2012, 1, 8)).span(negInfInterval) == + NegInfInterval!Date(Date(2012, 1, 8))); + + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 3))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 4))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 5))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 6))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 7))))); + static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 8))))); + + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, negInfInterval.span(interval))); + static assert(__traits(compiles, negInfInterval.span(cInterval))); + static assert(__traits(compiles, negInfInterval.span(iInterval))); + static assert(!__traits(compiles, negInfInterval.span(posInfInterval))); + static assert(!__traits(compiles, negInfInterval.span(cPosInfInterval))); + static assert(!__traits(compiles, negInfInterval.span(iPosInfInterval))); + static assert(__traits(compiles, negInfInterval.span(negInfInterval))); + static assert(__traits(compiles, negInfInterval.span(cNegInfInterval))); + static assert(__traits(compiles, negInfInterval.span(iNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.span(interval))); + static assert(__traits(compiles, cNegInfInterval.span(cInterval))); + static assert(__traits(compiles, cNegInfInterval.span(iInterval))); + static assert(!__traits(compiles, cNegInfInterval.span(posInfInterval))); + static assert(!__traits(compiles, cNegInfInterval.span(cPosInfInterval))); + static assert(!__traits(compiles, cNegInfInterval.span(iPosInfInterval))); + static assert(__traits(compiles, cNegInfInterval.span(negInfInterval))); + static assert(__traits(compiles, cNegInfInterval.span(cNegInfInterval))); + static assert(__traits(compiles, cNegInfInterval.span(iNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.span(interval))); + static assert(__traits(compiles, iNegInfInterval.span(cInterval))); + static assert(__traits(compiles, iNegInfInterval.span(iInterval))); + static assert(!__traits(compiles, iNegInfInterval.span(posInfInterval))); + static assert(!__traits(compiles, iNegInfInterval.span(cPosInfInterval))); + static assert(!__traits(compiles, iNegInfInterval.span(iPosInfInterval))); + static assert(__traits(compiles, iNegInfInterval.span(negInfInterval))); + static assert(__traits(compiles, iNegInfInterval.span(cNegInfInterval))); + static assert(__traits(compiles, iNegInfInterval.span(iNegInfInterval))); - static void testInterval(I, J)(in I interval1, in J interval2) - { - interval1.span(interval2); - } - - assertThrown!DateTimeException(testInterval(negInfInterval, Interval!Date(Date(2010, 7, 4), dur!"days"(0)))); - - _assertPred!"=="(negInfInterval.span(negInfInterval), - negInfInterval); - _assertPred!"=="(negInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2010, 7, 3))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.span(Interval!Date(Date(2010, 7, 1), Date(2013, 7, 3))), - NegInfInterval!Date(Date(2013, 7, 3))); - _assertPred!"=="(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 4))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2010, 7, 5))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 7))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.span(Interval!Date(Date(2010, 7, 3), Date(2012, 1, 8))), - NegInfInterval!Date(Date(2012, 1, 8))); - _assertPred!"=="(negInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 6))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.span(Interval!Date(Date(2010, 7, 5), Date(2012, 1, 7))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 7))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.span(Interval!Date(Date(2012, 1, 6), Date(2012, 1, 8))), - NegInfInterval!Date(Date(2012, 1, 8))); - _assertPred!"=="(negInfInterval.span(Interval!Date(Date(2012, 1, 7), Date(2012, 1, 8))), - NegInfInterval!Date(Date(2012, 1, 8))); - _assertPred!"=="(negInfInterval.span(Interval!Date(Date(2012, 1, 8), Date(2012, 1, 9))), - NegInfInterval!Date(Date(2012, 1, 9))); - - _assertPred!"=="(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 3))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 4))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.span(NegInfInterval!Date(Date(2010, 7, 5))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 6))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 7))), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(negInfInterval.span(NegInfInterval!Date(Date(2012, 1, 8))), - NegInfInterval!Date(Date(2012, 1, 8))); - - _assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 3)).span(negInfInterval), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 4)).span(negInfInterval), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(NegInfInterval!Date(Date(2010, 7, 5)).span(negInfInterval), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 6)).span(negInfInterval), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 7)).span(negInfInterval), - NegInfInterval!Date(Date(2012, 1, 7))); - _assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 8)).span(negInfInterval), - NegInfInterval!Date(Date(2012, 1, 8))); - - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 3))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 4))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2010, 7, 5))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 6))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 7))))); - static assert(!__traits(compiles, negInfInterval.span(PosInfInterval!Date(Date(2012, 1, 8))))); + //Verify Examples. + assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == NegInfInterval!Date(Date(2012, 3 , 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == NegInfInterval!Date(Date(2015, 9 , 2))); + assert(NegInfInterval!Date(Date(1600, 1, 7)).span(Interval!Date(Date(2012, 3, 11), Date(2017, 7, 1))) == NegInfInterval!Date(Date(2017, 7 , 1))); - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - const cInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - immutable iInterval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - immutable iPosInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, negInfInterval.span(interval))); - static assert(__traits(compiles, negInfInterval.span(cInterval))); - static assert(__traits(compiles, negInfInterval.span(iInterval))); - static assert(!__traits(compiles, negInfInterval.span(posInfInterval))); - static assert(!__traits(compiles, negInfInterval.span(cPosInfInterval))); - static assert(!__traits(compiles, negInfInterval.span(iPosInfInterval))); - static assert(__traits(compiles, negInfInterval.span(negInfInterval))); - static assert(__traits(compiles, negInfInterval.span(cNegInfInterval))); - static assert(__traits(compiles, negInfInterval.span(iNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.span(interval))); - static assert(__traits(compiles, cNegInfInterval.span(cInterval))); - static assert(__traits(compiles, cNegInfInterval.span(iInterval))); - static assert(!__traits(compiles, cNegInfInterval.span(posInfInterval))); - static assert(!__traits(compiles, cNegInfInterval.span(cPosInfInterval))); - static assert(!__traits(compiles, cNegInfInterval.span(iPosInfInterval))); - static assert(__traits(compiles, cNegInfInterval.span(negInfInterval))); - static assert(__traits(compiles, cNegInfInterval.span(cNegInfInterval))); - static assert(__traits(compiles, cNegInfInterval.span(iNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.span(interval))); - static assert(__traits(compiles, iNegInfInterval.span(cInterval))); - static assert(__traits(compiles, iNegInfInterval.span(iInterval))); - static assert(!__traits(compiles, iNegInfInterval.span(posInfInterval))); - static assert(!__traits(compiles, iNegInfInterval.span(cPosInfInterval))); - static assert(!__traits(compiles, iNegInfInterval.span(iPosInfInterval))); - static assert(__traits(compiles, iNegInfInterval.span(negInfInterval))); - static assert(__traits(compiles, iNegInfInterval.span(cNegInfInterval))); - static assert(__traits(compiles, iNegInfInterval.span(iNegInfInterval))); - - //Verify Examples. - assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1990, 7, 6), Date(2000, 8, 2))) == NegInfInterval!Date(Date(2012, 3 , 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).span(Interval!Date(Date(1999, 1, 12), Date(2015, 9, 2))) == NegInfInterval!Date(Date(2015, 9 , 2))); - assert(NegInfInterval!Date(Date(1600, 1, 7)).span(Interval!Date(Date(2012, 3, 11), Date(2017, 7, 1))) == NegInfInterval!Date(Date(2017, 7 , 1))); - - assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1999, 7, 6))) == NegInfInterval!Date(Date(2012, 3 , 1))); - assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); - } + assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(1999, 7, 6))) == NegInfInterval!Date(Date(2012, 3 , 1))); + assert(NegInfInterval!Date(Date(2012, 3, 1)).span(NegInfInterval!Date(Date(2013, 1, 12))) == NegInfInterval!Date(Date(2013, 1 , 12))); } //Test NegInfInterval's shift(). unittest { - version(testStdDateTime) - { - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.shift(duration); - _assertPred!"=="(interval, expected, "", __FILE__, line); - } + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.shift(duration); + assert(interval == expected); + } - testInterval(interval, dur!"days"(22), NegInfInterval!Date(Date(2012, 1, 29))); - testInterval(interval, dur!"days"(-22), NegInfInterval!Date(Date(2011, 12, 16))); + testInterval(interval, dur!"days"(22), NegInfInterval!Date(Date(2012, 1, 29))); + testInterval(interval, dur!"days"(-22), NegInfInterval!Date(Date(2011, 12, 16))); - const cInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); + const cInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.shift(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.shift(dur!"days"(5)))); - //Verify Examples. - auto interval1 = NegInfInterval!Date(Date(2012, 4, 5)); - auto interval2 = NegInfInterval!Date(Date(2012, 4, 5)); + //Verify Examples. + auto interval1 = NegInfInterval!Date(Date(2012, 4, 5)); + auto interval2 = NegInfInterval!Date(Date(2012, 4, 5)); - interval1.shift(dur!"days"(50)); - assert(interval1 == NegInfInterval!Date(Date(2012, 5, 25))); + interval1.shift(dur!"days"(50)); + assert(interval1 == NegInfInterval!Date(Date(2012, 5, 25))); - interval2.shift(dur!"days"(-50)); - assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); - } + interval2.shift(dur!"days"(-50)); + assert(interval2 == NegInfInterval!Date( Date(2012, 2, 15))); } //Test NegInfInterval's shift(int, int, AllowDayOverflow). unittest { - version(testStdDateTime) { - { - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - static void testIntervalFail(I)(I interval, int years, int months) - { - interval.shift(years, months); - } + static void testIntervalFail(I)(I interval, int years, int months) + { + interval.shift(years, months); + } - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) - { - interval.shift(years, months, allow); - _assertPred!"=="(interval, expected, "", __FILE__, line); - } + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) + { + interval.shift(years, months, allow); + assert(interval == expected); + } - testInterval(interval, 5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2017, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2007, 1, 7))); + testInterval(interval, 5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2007, 1, 7))); - auto interval2 = NegInfInterval!Date(Date(2010, 5, 31)); + auto interval2 = NegInfInterval!Date(Date(2010, 5, 31)); - testInterval(interval2, 1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 7, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 5, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 5, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 7, 1))); + testInterval(interval2, 1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 7, 1))); - testInterval(interval2, 1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 6, 30))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 4, 30))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 4, 30))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 6, 30))); - } + testInterval(interval2, 1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 6, 30))); + } - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(!__traits(compiles, cNegInfInterval.shift(1))); - static assert(!__traits(compiles, iNegInfInterval.shift(1))); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(!__traits(compiles, cNegInfInterval.shift(1))); + static assert(!__traits(compiles, iNegInfInterval.shift(1))); - //Verify Examples. - auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); - auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + //Verify Examples. + auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); + auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - interval1.shift(2); - assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); + interval1.shift(2); + assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); - interval2.shift(-2); - assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); - } + interval2.shift(-2); + assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); } //Test NegInfInterval's expand(). unittest { - version(testStdDateTime) - { - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) - { - interval.expand(duration); - _assertPred!"=="(interval, expected, "", __FILE__, line); - } + static void testInterval(I)(I interval, in Duration duration, in I expected, size_t line = __LINE__) + { + interval.expand(duration); + assert(interval == expected); + } - testInterval(interval, dur!"days"(22), NegInfInterval!Date(Date(2012, 1, 29))); - testInterval(interval, dur!"days"(-22), NegInfInterval!Date(Date(2011, 12, 16))); + testInterval(interval, dur!"days"(22), NegInfInterval!Date(Date(2012, 1, 29))); + testInterval(interval, dur!"days"(-22), NegInfInterval!Date(Date(2011, 12, 16))); - const cInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); - static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); + const cInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(!__traits(compiles, cInterval.expand(dur!"days"(5)))); + static assert(!__traits(compiles, iInterval.expand(dur!"days"(5)))); - //Verify Examples. - auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); - auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + //Verify Examples. + auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); + auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - interval1.expand(dur!"days"(2)); - assert(interval1 == NegInfInterval!Date(Date(2012, 3, 3))); + interval1.expand(dur!"days"(2)); + assert(interval1 == NegInfInterval!Date(Date(2012, 3, 3))); - interval2.expand(dur!"days"(-2)); - assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); - } + interval2.expand(dur!"days"(-2)); + assert(interval2 == NegInfInterval!Date(Date(2012, 2, 28))); } //Test NegInfInterval's expand(int, int, AllowDayOverflow). unittest { - version(testStdDateTime) { - { - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) - { - interval.expand(years, months, allow); - _assertPred!"=="(interval, expected, "", __FILE__, line); - } + static void testInterval(I)(I interval, int years, int months, AllowDayOverflow allow, in I expected, size_t line = __LINE__) + { + interval.expand(years, months, allow); + assert(interval == expected); + } - testInterval(interval, 5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2017, 1, 7))); - testInterval(interval, -5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2007, 1, 7))); + testInterval(interval, 5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2017, 1, 7))); + testInterval(interval, -5, 0, AllowDayOverflow.yes, NegInfInterval!Date(Date(2007, 1, 7))); - auto interval2 = NegInfInterval!Date(Date(2010, 5, 31)); + auto interval2 = NegInfInterval!Date(Date(2010, 5, 31)); - testInterval(interval2, 1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 7, 1))); - testInterval(interval2, 1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 5, 1))); - testInterval(interval2, -1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 5, 1))); - testInterval(interval2, -1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 7, 1))); + testInterval(interval2, 1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 7, 1))); + testInterval(interval2, 1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2011, 5, 1))); + testInterval(interval2, -1, -1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 5, 1))); + testInterval(interval2, -1, 1, AllowDayOverflow.yes, NegInfInterval!Date(Date(2009, 7, 1))); - testInterval(interval2, 1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 6, 30))); - testInterval(interval2, 1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 4, 30))); - testInterval(interval2, -1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 4, 30))); - testInterval(interval2, -1, 1, AllowDayOverflow.no, NegInfInterval!Date( Date(2009, 6, 30))); - } + testInterval(interval2, 1, 1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 6, 30))); + testInterval(interval2, 1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2011, 4, 30))); + testInterval(interval2, -1, -1, AllowDayOverflow.no, NegInfInterval!Date(Date(2009, 4, 30))); + testInterval(interval2, -1, 1, AllowDayOverflow.no, NegInfInterval!Date( Date(2009, 6, 30))); + } - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(!__traits(compiles, cNegInfInterval.expand(1))); - static assert(!__traits(compiles, iNegInfInterval.expand(1))); + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(!__traits(compiles, cNegInfInterval.expand(1))); + static assert(!__traits(compiles, iNegInfInterval.expand(1))); - //Verify Examples. - auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); - auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); + //Verify Examples. + auto interval1 = NegInfInterval!Date(Date(2012, 3, 1)); + auto interval2 = NegInfInterval!Date(Date(2012, 3, 1)); - interval1.expand(2); - assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); + interval1.expand(2); + assert(interval1 == NegInfInterval!Date(Date(2014, 3, 1))); - interval2.expand(-2); - assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); - } + interval2.expand(-2); + assert(interval2 == NegInfInterval!Date(Date(2010, 3, 1))); } //Test NegInfInterval's bwdRange(). unittest { - version(testStdDateTime) - { - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static void testInterval(NegInfInterval!Date negInfInterval) - { - negInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.fwd)(DayOfWeek.fri)).popFront(); - } + static void testInterval(NegInfInterval!Date negInfInterval) + { + negInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.fwd)(DayOfWeek.fri)).popFront(); + } - assertThrown!DateTimeException(testInterval(negInfInterval)); + assertThrown!DateTimeException(testInterval(negInfInterval)); - _assertPred!"=="(NegInfInterval!Date(Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).front, - Date(2010, 10, 1)); + assert(NegInfInterval!Date(Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)).front == + Date(2010, 10, 1)); - _assertPred!"=="(NegInfInterval!Date(Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).front, - Date(2010, 9, 24)); + assert(NegInfInterval!Date(Date(2010, 10, 1)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri), PopFirst.yes).front == + Date(2010, 9, 24)); - //Verify Examples. - auto interval = NegInfInterval!Date(Date(2010, 9, 9)); - auto func = delegate (in Date date) - { - if((date.day & 1) == 0) - return date - dur!"days"(2); + //Verify Examples. + auto interval = NegInfInterval!Date(Date(2010, 9, 9)); + auto func = delegate (in Date date) + { + if((date.day & 1) == 0) + return date - dur!"days"(2); - return date - dur!"days"(1); - }; - auto range = interval.bwdRange(func); + return date - dur!"days"(1); + }; + auto range = interval.bwdRange(func); - //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). - assert(range.front == Date(2010, 9, 9)); + //An odd day. Using PopFirst.yes would have made this Date(2010, 9, 8). + assert(range.front == Date(2010, 9, 9)); - range.popFront(); - assert(range.front == Date(2010, 9, 8)); + range.popFront(); + assert(range.front == Date(2010, 9, 8)); - range.popFront(); - assert(range.front == Date(2010, 9, 6)); + range.popFront(); + assert(range.front == Date(2010, 9, 6)); - range.popFront(); - assert(range.front == Date(2010, 9, 4)); + range.popFront(); + assert(range.front == Date(2010, 9, 4)); - range.popFront(); - assert(range.front == Date(2010, 9, 2)); + range.popFront(); + assert(range.front == Date(2010, 9, 2)); - range.popFront(); - assert(!range.empty); + range.popFront(); + assert(!range.empty); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, cNegInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)))); - static assert(__traits(compiles, iNegInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)))); - } + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, cNegInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)))); + static assert(__traits(compiles, iNegInfInterval.bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)))); } //Test NegInfInterval's toString(). unittest { - version(testStdDateTime) - { - _assertPred!"=="(NegInfInterval!Date(Date(2012, 1, 7)).toString(), "[-∞ - 2012-Jan-07)"); + assert(NegInfInterval!Date(Date(2012, 1, 7)).toString() == "[-∞ - 2012-Jan-07)"); - const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - static assert(__traits(compiles, cNegInfInterval.toString())); - static assert(__traits(compiles, iNegInfInterval.toString())); - } + const cNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + immutable iNegInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + static assert(__traits(compiles, cNegInfInterval.toString())); + static assert(__traits(compiles, iNegInfInterval.toString())); } @@ -25803,28 +24235,6 @@ unittest $(D fwdRange), use $(D Direction.fwd). If passing it to $(D bwdRange), use $(D Direction.bwd). dayOfWeek = The week that each time point in the range will be. - - Examples: --------------------- -auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); -auto func = everyDayOfWeek!Date(DayOfWeek.mon); -auto range = interval.fwdRange(func); - -//A Thursday. Using PopFirst.yes would have made this Date(2010, 9, 6). -assert(range.front == Date(2010, 9, 2)); - -range.popFront(); -assert(range.front == Date(2010, 9, 6)); - -range.popFront(); -assert(range.front == Date(2010, 9, 13)); - -range.popFront(); -assert(range.front == Date(2010, 9, 20)); - -range.popFront(); -assert(range.empty); --------------------- +/ static TP delegate(in TP) everyDayOfWeek(TP, Direction dir = Direction.fwd)(DayOfWeek dayOfWeek) nothrow if(isTimePoint!TP && @@ -25849,61 +24259,61 @@ static TP delegate(in TP) everyDayOfWeek(TP, Direction dir = Direction.fwd)(DayO return &func; } +/// unittest { - version(testStdDateTime) - { - auto funcFwd = everyDayOfWeek!Date(DayOfWeek.mon); - auto funcBwd = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.mon); - - _assertPred!"=="(funcFwd(Date(2010, 8, 28)), Date(2010, 8, 30)); - _assertPred!"=="(funcFwd(Date(2010, 8, 29)), Date(2010, 8, 30)); - _assertPred!"=="(funcFwd(Date(2010, 8, 30)), Date(2010, 9, 6)); - _assertPred!"=="(funcFwd(Date(2010, 8, 31)), Date(2010, 9, 6)); - _assertPred!"=="(funcFwd(Date(2010, 9, 1)), Date(2010, 9, 6)); - _assertPred!"=="(funcFwd(Date(2010, 9, 2)), Date(2010, 9, 6)); - _assertPred!"=="(funcFwd(Date(2010, 9, 3)), Date(2010, 9, 6)); - _assertPred!"=="(funcFwd(Date(2010, 9, 4)), Date(2010, 9, 6)); - _assertPred!"=="(funcFwd(Date(2010, 9, 5)), Date(2010, 9, 6)); - _assertPred!"=="(funcFwd(Date(2010, 9, 6)), Date(2010, 9, 13)); - _assertPred!"=="(funcFwd(Date(2010, 9, 7)), Date(2010, 9, 13)); - - _assertPred!"=="(funcBwd(Date(2010, 8, 28)), Date(2010, 8, 23)); - _assertPred!"=="(funcBwd(Date(2010, 8, 29)), Date(2010, 8, 23)); - _assertPred!"=="(funcBwd(Date(2010, 8, 30)), Date(2010, 8, 23)); - _assertPred!"=="(funcBwd(Date(2010, 8, 31)), Date(2010, 8, 30)); - _assertPred!"=="(funcBwd(Date(2010, 9, 1)), Date(2010, 8, 30)); - _assertPred!"=="(funcBwd(Date(2010, 9, 2)), Date(2010, 8, 30)); - _assertPred!"=="(funcBwd(Date(2010, 9, 3)), Date(2010, 8, 30)); - _assertPred!"=="(funcBwd(Date(2010, 9, 4)), Date(2010, 8, 30)); - _assertPred!"=="(funcBwd(Date(2010, 9, 5)), Date(2010, 8, 30)); - _assertPred!"=="(funcBwd(Date(2010, 9, 6)), Date(2010, 8, 30)); - _assertPred!"=="(funcBwd(Date(2010, 9, 7)), Date(2010, 9, 6)); - - static assert(!__traits(compiles, everyDayOfWeek!(TimeOfDay)(DayOfWeek.mon))); - static assert(__traits(compiles, everyDayOfWeek!(DateTime)(DayOfWeek.mon))); - static assert(__traits(compiles, everyDayOfWeek!(SysTime)(DayOfWeek.mon))); - - //Verify Examples. - auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); - auto func = everyDayOfWeek!Date(DayOfWeek.mon); - auto range = interval.fwdRange(func); + auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); + auto func = everyDayOfWeek!Date(DayOfWeek.mon); + auto range = interval.fwdRange(func); - //A Thursday. Using PopFirst.yes would have made this Date(2010, 9, 6). - assert(range.front == Date(2010, 9, 2)); + //A Thursday. Using PopFirst.yes would have made this Date(2010, 9, 6). + assert(range.front == Date(2010, 9, 2)); - range.popFront(); - assert(range.front == Date(2010, 9, 6)); + range.popFront(); + assert(range.front == Date(2010, 9, 6)); - range.popFront(); - assert(range.front == Date(2010, 9, 13)); + range.popFront(); + assert(range.front == Date(2010, 9, 13)); - range.popFront(); - assert(range.front == Date(2010, 9, 20)); + range.popFront(); + assert(range.front == Date(2010, 9, 20)); - range.popFront(); - assert(range.empty); - } + range.popFront(); + assert(range.empty); +} + +unittest +{ + auto funcFwd = everyDayOfWeek!Date(DayOfWeek.mon); + auto funcBwd = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.mon); + + assert(funcFwd(Date(2010, 8, 28)) == Date(2010, 8, 30)); + assert(funcFwd(Date(2010, 8, 29)) == Date(2010, 8, 30)); + assert(funcFwd(Date(2010, 8, 30)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 8, 31)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 1)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 2)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 3)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 4)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 5)) == Date(2010, 9, 6)); + assert(funcFwd(Date(2010, 9, 6)) == Date(2010, 9, 13)); + assert(funcFwd(Date(2010, 9, 7)) == Date(2010, 9, 13)); + + assert(funcBwd(Date(2010, 8, 28)) == Date(2010, 8, 23)); + assert(funcBwd(Date(2010, 8, 29)) == Date(2010, 8, 23)); + assert(funcBwd(Date(2010, 8, 30)) == Date(2010, 8, 23)); + assert(funcBwd(Date(2010, 8, 31)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 1)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 2)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 3)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 4)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 5)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 6)) == Date(2010, 8, 30)); + assert(funcBwd(Date(2010, 9, 7)) == Date(2010, 9, 6)); + + static assert(!__traits(compiles, everyDayOfWeek!(TimeOfDay)(DayOfWeek.mon))); + static assert(__traits(compiles, everyDayOfWeek!(DateTime)(DayOfWeek.mon))); + static assert(__traits(compiles, everyDayOfWeek!(SysTime)(DayOfWeek.mon))); } @@ -25929,34 +24339,6 @@ unittest $(D fwdRange), use $(D Direction.fwd). If passing it to $(D bwdRange), use $(D Direction.bwd). month = The month that each time point in the range will be in. - - Examples: --------------------- -auto interval = Interval!Date(Date(2000, 1, 30), Date(2004, 8, 5)); -auto func = everyMonth!(Date)(Month.feb); -auto range = interval.fwdRange(func); - -//Using PopFirst.yes would have made this Date(2010, 2, 29). -assert(range.front == Date(2000, 1, 30)); - -range.popFront(); -assert(range.front == Date(2000, 2, 29)); - -range.popFront(); -assert(range.front == Date(2001, 2, 28)); - -range.popFront(); -assert(range.front == Date(2002, 2, 28)); - -range.popFront(); -assert(range.front == Date(2003, 2, 28)); - -range.popFront(); -assert(range.front == Date(2004, 2, 28)); - -range.popFront(); -assert(range.empty); --------------------- +/ static TP delegate(in TP) everyMonth(TP, Direction dir = Direction.fwd)(int month) if(isTimePoint!TP && @@ -25991,75 +24373,75 @@ static TP delegate(in TP) everyMonth(TP, Direction dir = Direction.fwd)(int mont return &func; } +/// unittest { - version(testStdDateTime) - { - auto funcFwd = everyMonth!Date(Month.jun); - auto funcBwd = everyMonth!(Date, Direction.bwd)(Month.jun); - - _assertPred!"=="(funcFwd(Date(2010, 5, 31)), Date(2010, 6, 30)); - _assertPred!"=="(funcFwd(Date(2010, 6, 30)), Date(2011, 6, 30)); - _assertPred!"=="(funcFwd(Date(2010, 7, 31)), Date(2011, 6, 30)); - _assertPred!"=="(funcFwd(Date(2010, 8, 31)), Date(2011, 6, 30)); - _assertPred!"=="(funcFwd(Date(2010, 9, 30)), Date(2011, 6, 30)); - _assertPred!"=="(funcFwd(Date(2010, 10, 31)), Date(2011, 6, 30)); - _assertPred!"=="(funcFwd(Date(2010, 11, 30)), Date(2011, 6, 30)); - _assertPred!"=="(funcFwd(Date(2010, 12, 31)), Date(2011, 6, 30)); - _assertPred!"=="(funcFwd(Date(2011, 1, 31)), Date(2011, 6, 30)); - _assertPred!"=="(funcFwd(Date(2011, 2, 28)), Date(2011, 6, 28)); - _assertPred!"=="(funcFwd(Date(2011, 3, 31)), Date(2011, 6, 30)); - _assertPred!"=="(funcFwd(Date(2011, 4, 30)), Date(2011, 6, 30)); - _assertPred!"=="(funcFwd(Date(2011, 5, 31)), Date(2011, 6, 30)); - _assertPred!"=="(funcFwd(Date(2011, 6, 30)), Date(2012, 6, 30)); - _assertPred!"=="(funcFwd(Date(2011, 7, 31)), Date(2012, 6, 30)); - - _assertPred!"=="(funcBwd(Date(2010, 5, 31)), Date(2009, 6, 30)); - _assertPred!"=="(funcBwd(Date(2010, 6, 30)), Date(2009, 6, 30)); - _assertPred!"=="(funcBwd(Date(2010, 7, 31)), Date(2010, 6, 30)); - _assertPred!"=="(funcBwd(Date(2010, 8, 31)), Date(2010, 6, 30)); - _assertPred!"=="(funcBwd(Date(2010, 9, 30)), Date(2010, 6, 30)); - _assertPred!"=="(funcBwd(Date(2010, 10, 31)), Date(2010, 6, 30)); - _assertPred!"=="(funcBwd(Date(2010, 11, 30)), Date(2010, 6, 30)); - _assertPred!"=="(funcBwd(Date(2010, 12, 31)), Date(2010, 6, 30)); - _assertPred!"=="(funcBwd(Date(2011, 1, 31)), Date(2010, 6, 30)); - _assertPred!"=="(funcBwd(Date(2011, 2, 28)), Date(2010, 6, 28)); - _assertPred!"=="(funcBwd(Date(2011, 3, 31)), Date(2010, 6, 30)); - _assertPred!"=="(funcBwd(Date(2011, 4, 30)), Date(2010, 6, 30)); - _assertPred!"=="(funcBwd(Date(2011, 5, 31)), Date(2010, 6, 30)); - _assertPred!"=="(funcBwd(Date(2011, 6, 30)), Date(2010, 6, 30)); - _assertPred!"=="(funcBwd(Date(2011, 7, 30)), Date(2011, 6, 30)); - - static assert(!__traits(compiles, everyMonth!(TimeOfDay)(Month.jan))); - static assert(__traits(compiles, everyMonth!(DateTime)(Month.jan))); - static assert(__traits(compiles, everyMonth!(SysTime)(Month.jan))); - - //Verify Examples. - auto interval = Interval!Date(Date(2000, 1, 30), Date(2004, 8, 5)); - auto func = everyMonth!(Date)(Month.feb); - auto range = interval.fwdRange(func); + auto interval = Interval!Date(Date(2000, 1, 30), Date(2004, 8, 5)); + auto func = everyMonth!(Date)(Month.feb); + auto range = interval.fwdRange(func); - //Using PopFirst.yes would have made this Date(2010, 2, 29). - assert(range.front == Date(2000, 1, 30)); + //Using PopFirst.yes would have made this Date(2010, 2, 29). + assert(range.front == Date(2000, 1, 30)); - range.popFront(); - assert(range.front == Date(2000, 2, 29)); + range.popFront(); + assert(range.front == Date(2000, 2, 29)); - range.popFront(); - assert(range.front == Date(2001, 2, 28)); + range.popFront(); + assert(range.front == Date(2001, 2, 28)); - range.popFront(); - assert(range.front == Date(2002, 2, 28)); + range.popFront(); + assert(range.front == Date(2002, 2, 28)); - range.popFront(); - assert(range.front == Date(2003, 2, 28)); + range.popFront(); + assert(range.front == Date(2003, 2, 28)); - range.popFront(); - assert(range.front == Date(2004, 2, 28)); + range.popFront(); + assert(range.front == Date(2004, 2, 28)); - range.popFront(); - assert(range.empty); - } + range.popFront(); + assert(range.empty); +} + +unittest +{ + auto funcFwd = everyMonth!Date(Month.jun); + auto funcBwd = everyMonth!(Date, Direction.bwd)(Month.jun); + + assert(funcFwd(Date(2010, 5, 31)) == Date(2010, 6, 30)); + assert(funcFwd(Date(2010, 6, 30)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 7, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 8, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 9, 30)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 10, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 11, 30)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2010, 12, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2011, 1, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2011, 2, 28)) == Date(2011, 6, 28)); + assert(funcFwd(Date(2011, 3, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2011, 4, 30)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2011, 5, 31)) == Date(2011, 6, 30)); + assert(funcFwd(Date(2011, 6, 30)) == Date(2012, 6, 30)); + assert(funcFwd(Date(2011, 7, 31)) == Date(2012, 6, 30)); + + assert(funcBwd(Date(2010, 5, 31)) == Date(2009, 6, 30)); + assert(funcBwd(Date(2010, 6, 30)) == Date(2009, 6, 30)); + assert(funcBwd(Date(2010, 7, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2010, 8, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2010, 9, 30)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2010, 10, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2010, 11, 30)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2010, 12, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 1, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 2, 28)) == Date(2010, 6, 28)); + assert(funcBwd(Date(2011, 3, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 4, 30)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 5, 31)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 6, 30)) == Date(2010, 6, 30)); + assert(funcBwd(Date(2011, 7, 30)) == Date(2011, 6, 30)); + + static assert(!__traits(compiles, everyMonth!(TimeOfDay)(Month.jan))); + static assert(__traits(compiles, everyMonth!(DateTime)(Month.jan))); + static assert(__traits(compiles, everyMonth!(SysTime)(Month.jan))); } @@ -26080,28 +24462,6 @@ unittest $(D bwdRange), use $(D Direction.bwd). duration = The duration which separates each successive time point in the range. - - Examples: --------------------- -auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); -auto func = everyDuration!Date(dur!"days"(8)); -auto range = interval.fwdRange(func); - -//Using PopFirst.yes would have made this Date(2010, 9, 10). -assert(range.front == Date(2010, 9, 2)); - -range.popFront(); -assert(range.front == Date(2010, 9, 10)); - -range.popFront(); -assert(range.front == Date(2010, 9, 18)); - -range.popFront(); -assert(range.front == Date(2010, 9, 26)); - -range.popFront(); -assert(range.empty); --------------------- +/ static TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D) (D duration) nothrow @@ -26120,48 +24480,48 @@ static TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D) return &func; } +/// unittest { - version(testStdDateTime) - { - auto funcFwd = everyDuration!Date(dur!"days"(27)); - auto funcBwd = everyDuration!(Date, Direction.bwd)(dur!"days"(27)); - - _assertPred!"=="(funcFwd(Date(2009, 12, 25)), Date(2010, 1, 21)); - _assertPred!"=="(funcFwd(Date(2009, 12, 26)), Date(2010, 1, 22)); - _assertPred!"=="(funcFwd(Date(2009, 12, 27)), Date(2010, 1, 23)); - _assertPred!"=="(funcFwd(Date(2009, 12, 28)), Date(2010, 1, 24)); + auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); + auto func = everyDuration!Date(dur!"days"(8)); + auto range = interval.fwdRange(func); - _assertPred!"=="(funcBwd(Date(2010, 1, 21)), Date(2009, 12, 25)); - _assertPred!"=="(funcBwd(Date(2010, 1, 22)), Date(2009, 12, 26)); - _assertPred!"=="(funcBwd(Date(2010, 1, 23)), Date(2009, 12, 27)); - _assertPred!"=="(funcBwd(Date(2010, 1, 24)), Date(2009, 12, 28)); + //Using PopFirst.yes would have made this Date(2010, 9, 10). + assert(range.front == Date(2010, 9, 2)); - static assert(__traits(compiles, everyDuration!Date(dur!"hnsecs"(1)))); - static assert(__traits(compiles, everyDuration!TimeOfDay(dur!"hnsecs"(1)))); - static assert(__traits(compiles, everyDuration!DateTime(dur!"hnsecs"(1)))); - static assert(__traits(compiles, everyDuration!SysTime(dur!"hnsecs"(1)))); - - //Verify Examples. - auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27)); - auto func = everyDuration!Date(dur!"days"(8)); - auto range = interval.fwdRange(func); - - //Using PopFirst.yes would have made this Date(2010, 9, 10). - assert(range.front == Date(2010, 9, 2)); + range.popFront(); + assert(range.front == Date(2010, 9, 10)); - range.popFront(); - assert(range.front == Date(2010, 9, 10)); + range.popFront(); + assert(range.front == Date(2010, 9, 18)); - range.popFront(); - assert(range.front == Date(2010, 9, 18)); + range.popFront(); + assert(range.front == Date(2010, 9, 26)); - range.popFront(); - assert(range.front == Date(2010, 9, 26)); + range.popFront(); + assert(range.empty); +} - range.popFront(); - assert(range.empty); - } +unittest +{ + auto funcFwd = everyDuration!Date(dur!"days"(27)); + auto funcBwd = everyDuration!(Date, Direction.bwd)(dur!"days"(27)); + + assert(funcFwd(Date(2009, 12, 25)) == Date(2010, 1, 21)); + assert(funcFwd(Date(2009, 12, 26)) == Date(2010, 1, 22)); + assert(funcFwd(Date(2009, 12, 27)) == Date(2010, 1, 23)); + assert(funcFwd(Date(2009, 12, 28)) == Date(2010, 1, 24)); + + assert(funcBwd(Date(2010, 1, 21)) == Date(2009, 12, 25)); + assert(funcBwd(Date(2010, 1, 22)) == Date(2009, 12, 26)); + assert(funcBwd(Date(2010, 1, 23)) == Date(2009, 12, 27)); + assert(funcBwd(Date(2010, 1, 24)) == Date(2009, 12, 28)); + + static assert(__traits(compiles, everyDuration!Date(dur!"hnsecs"(1)))); + static assert(__traits(compiles, everyDuration!TimeOfDay(dur!"hnsecs"(1)))); + static assert(__traits(compiles, everyDuration!DateTime(dur!"hnsecs"(1)))); + static assert(__traits(compiles, everyDuration!SysTime(dur!"hnsecs"(1)))); } @@ -26202,28 +24562,6 @@ unittest increment. duration = The duration to add to the time point passed to the delegate. - - Examples: --------------------- -auto interval = Interval!Date(Date(2010, 9, 2), Date(2025, 9, 27)); -auto func = everyDuration!Date(4, 1, AllowDayOverflow.yes, dur!"days"(2)); -auto range = interval.fwdRange(func); - -//Using PopFirst.yes would have made this Date(2014, 10, 12). -assert(range.front == Date(2010, 9, 2)); - -range.popFront(); -assert(range.front == Date(2014, 10, 4)); - -range.popFront(); -assert(range.front == Date(2018, 11, 6)); - -range.popFront(); -assert(range.front == Date(2022, 12, 8)); - -range.popFront(); -assert(range.empty); --------------------- +/ static TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D) (int years, @@ -26261,69 +24599,69 @@ static TP delegate(in TP) everyDuration(TP, Direction dir = Direction.fwd, D) return &func; } +/// unittest { - version(testStdDateTime) - { - { - auto funcFwd = everyDuration!Date(1, 2, AllowDayOverflow.yes, dur!"days"(3)); - auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, AllowDayOverflow.yes, dur!"days"(3)); + auto interval = Interval!Date(Date(2010, 9, 2), Date(2025, 9, 27)); + auto func = everyDuration!Date(4, 1, AllowDayOverflow.yes, dur!"days"(2)); + auto range = interval.fwdRange(func); - _assertPred!"=="(funcFwd(Date(2009, 12, 25)), Date(2011, 2, 28)); - _assertPred!"=="(funcFwd(Date(2009, 12, 26)), Date(2011, 3, 1)); - _assertPred!"=="(funcFwd(Date(2009, 12, 27)), Date(2011, 3, 2)); - _assertPred!"=="(funcFwd(Date(2009, 12, 28)), Date(2011, 3, 3)); - _assertPred!"=="(funcFwd(Date(2009, 12, 29)), Date(2011, 3, 4)); - - _assertPred!"=="(funcBwd(Date(2011, 2, 28)), Date(2009, 12, 25)); - _assertPred!"=="(funcBwd(Date(2011, 3, 1)), Date(2009, 12, 26)); - _assertPred!"=="(funcBwd(Date(2011, 3, 2)), Date(2009, 12, 27)); - _assertPred!"=="(funcBwd(Date(2011, 3, 3)), Date(2009, 12, 28)); - _assertPred!"=="(funcBwd(Date(2011, 3, 4)), Date(2010, 1, 1)); - } + //Using PopFirst.yes would have made this Date(2014, 10, 12). + assert(range.front == Date(2010, 9, 2)); - { - auto funcFwd = everyDuration!Date(1, 2, AllowDayOverflow.no, dur!"days"(3)); - auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, AllowDayOverflow.yes, dur!"days"(3)); + range.popFront(); + assert(range.front == Date(2014, 10, 4)); - _assertPred!"=="(funcFwd(Date(2009, 12, 25)), Date(2011, 2, 28)); - _assertPred!"=="(funcFwd(Date(2009, 12, 26)), Date(2011, 3, 1)); - _assertPred!"=="(funcFwd(Date(2009, 12, 27)), Date(2011, 3, 2)); - _assertPred!"=="(funcFwd(Date(2009, 12, 28)), Date(2011, 3, 3)); - _assertPred!"=="(funcFwd(Date(2009, 12, 29)), Date(2011, 3, 3)); + range.popFront(); + assert(range.front == Date(2018, 11, 6)); - _assertPred!"=="(funcBwd(Date(2011, 2, 28)), Date(2009, 12, 25)); - _assertPred!"=="(funcBwd(Date(2011, 3, 1)), Date(2009, 12, 26)); - _assertPred!"=="(funcBwd(Date(2011, 3, 2)), Date(2009, 12, 27)); - _assertPred!"=="(funcBwd(Date(2011, 3, 3)), Date(2009, 12, 28)); - _assertPred!"=="(funcBwd(Date(2011, 3, 4)), Date(2010, 1, 1)); - } + range.popFront(); + assert(range.front == Date(2022, 12, 8)); - static assert(__traits(compiles, everyDuration!Date(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); - static assert(!__traits(compiles, everyDuration!TimeOfDay(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); - static assert(__traits(compiles, everyDuration!DateTime(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); - static assert(__traits(compiles, everyDuration!SysTime(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); + range.popFront(); + assert(range.empty); +} - //Verify Examples. - auto interval = Interval!Date(Date(2010, 9, 2), Date(2025, 9, 27)); - auto func = everyDuration!Date(4, 1, AllowDayOverflow.yes, dur!"days"(2)); - auto range = interval.fwdRange(func); +unittest +{ + { + auto funcFwd = everyDuration!Date(1, 2, AllowDayOverflow.yes, dur!"days"(3)); + auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, AllowDayOverflow.yes, dur!"days"(3)); - //Using PopFirst.yes would have made this Date(2014, 10, 12). - assert(range.front == Date(2010, 9, 2)); + assert(funcFwd(Date(2009, 12, 25)) == Date(2011, 2, 28)); + assert(funcFwd(Date(2009, 12, 26)) == Date(2011, 3, 1)); + assert(funcFwd(Date(2009, 12, 27)) == Date(2011, 3, 2)); + assert(funcFwd(Date(2009, 12, 28)) == Date(2011, 3, 3)); + assert(funcFwd(Date(2009, 12, 29)) == Date(2011, 3, 4)); - range.popFront(); - assert(range.front == Date(2014, 10, 4)); + assert(funcBwd(Date(2011, 2, 28)) == Date(2009, 12, 25)); + assert(funcBwd(Date(2011, 3, 1)) == Date(2009, 12, 26)); + assert(funcBwd(Date(2011, 3, 2)) == Date(2009, 12, 27)); + assert(funcBwd(Date(2011, 3, 3)) == Date(2009, 12, 28)); + assert(funcBwd(Date(2011, 3, 4)) == Date(2010, 1, 1)); + } - range.popFront(); - assert(range.front == Date(2018, 11, 6)); + { + auto funcFwd = everyDuration!Date(1, 2, AllowDayOverflow.no, dur!"days"(3)); + auto funcBwd = everyDuration!(Date, Direction.bwd)(1, 2, AllowDayOverflow.yes, dur!"days"(3)); - range.popFront(); - assert(range.front == Date(2022, 12, 8)); + assert(funcFwd(Date(2009, 12, 25)) == Date(2011, 2, 28)); + assert(funcFwd(Date(2009, 12, 26)) == Date(2011, 3, 1)); + assert(funcFwd(Date(2009, 12, 27)) == Date(2011, 3, 2)); + assert(funcFwd(Date(2009, 12, 28)) == Date(2011, 3, 3)); + assert(funcFwd(Date(2009, 12, 29)) == Date(2011, 3, 3)); - range.popFront(); - assert(range.empty); + assert(funcBwd(Date(2011, 2, 28)) == Date(2009, 12, 25)); + assert(funcBwd(Date(2011, 3, 1)) == Date(2009, 12, 26)); + assert(funcBwd(Date(2011, 3, 2)) == Date(2009, 12, 27)); + assert(funcBwd(Date(2011, 3, 3)) == Date(2009, 12, 28)); + assert(funcBwd(Date(2011, 3, 4)) == Date(2010, 1, 1)); } + + static assert(__traits(compiles, everyDuration!Date(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); + static assert(!__traits(compiles, everyDuration!TimeOfDay(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); + static assert(__traits(compiles, everyDuration!DateTime(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); + static assert(__traits(compiles, everyDuration!SysTime(1, 2, AllowDayOverflow.yes, dur!"hnsecs"(1)))); } @@ -26391,17 +24729,16 @@ public: Params: rhs = The $(D IntervalRange) to assign to this one. +/ - /+ref+/ IntervalRange opAssign(ref IntervalRange rhs) pure nothrow + ref IntervalRange opAssign(ref IntervalRange rhs) pure nothrow { _interval = rhs._interval; _func = rhs._func; - return this; } /++ Ditto +/ - /+ref+/ IntervalRange opAssign(IntervalRange rhs) pure nothrow + ref IntervalRange opAssign(IntervalRange rhs) pure nothrow { return this = rhs; } @@ -26575,310 +24912,283 @@ private: //Test that IntervalRange satisfies the range predicates that it's supposed to satisfy. unittest { - version(testStdDateTime) - { - static assert(isInputRange!(IntervalRange!(Date, Direction.fwd))); - static assert(isForwardRange!(IntervalRange!(Date, Direction.fwd))); - - //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 - //static assert(!isOutputRange!(IntervalRange!(Date, Direction.fwd), Date)); - - static assert(!isBidirectionalRange!(IntervalRange!(Date, Direction.fwd))); - static assert(!isRandomAccessRange!(IntervalRange!(Date, Direction.fwd))); - static assert(!hasSwappableElements!(IntervalRange!(Date, Direction.fwd))); - static assert(!hasAssignableElements!(IntervalRange!(Date, Direction.fwd))); - static assert(!hasLength!(IntervalRange!(Date, Direction.fwd))); - static assert(!isInfinite!(IntervalRange!(Date, Direction.fwd))); - static assert(!hasSlicing!(IntervalRange!(Date, Direction.fwd))); - - static assert(is(ElementType!(IntervalRange!(Date, Direction.fwd)) == Date)); - static assert(is(ElementType!(IntervalRange!(TimeOfDay, Direction.fwd)) == TimeOfDay)); - static assert(is(ElementType!(IntervalRange!(DateTime, Direction.fwd)) == DateTime)); - static assert(is(ElementType!(IntervalRange!(SysTime, Direction.fwd)) == SysTime)); - } + static assert(isInputRange!(IntervalRange!(Date, Direction.fwd))); + static assert(isForwardRange!(IntervalRange!(Date, Direction.fwd))); + + //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 + //static assert(!isOutputRange!(IntervalRange!(Date, Direction.fwd), Date)); + + static assert(!isBidirectionalRange!(IntervalRange!(Date, Direction.fwd))); + static assert(!isRandomAccessRange!(IntervalRange!(Date, Direction.fwd))); + static assert(!hasSwappableElements!(IntervalRange!(Date, Direction.fwd))); + static assert(!hasAssignableElements!(IntervalRange!(Date, Direction.fwd))); + static assert(!hasLength!(IntervalRange!(Date, Direction.fwd))); + static assert(!isInfinite!(IntervalRange!(Date, Direction.fwd))); + static assert(!hasSlicing!(IntervalRange!(Date, Direction.fwd))); + + static assert(is(ElementType!(IntervalRange!(Date, Direction.fwd)) == Date)); + static assert(is(ElementType!(IntervalRange!(TimeOfDay, Direction.fwd)) == TimeOfDay)); + static assert(is(ElementType!(IntervalRange!(DateTime, Direction.fwd)) == DateTime)); + static assert(is(ElementType!(IntervalRange!(SysTime, Direction.fwd)) == SysTime)); } //Test construction of IntervalRange. unittest { - version(testStdDateTime) { + Date dateFunc(in Date date) { - Date dateFunc(in Date date) - { - return date; - } + return date; + } - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto ir = IntervalRange!(Date, Direction.fwd)(interval, &dateFunc); - } + auto ir = IntervalRange!(Date, Direction.fwd)(interval, &dateFunc); + } + { + TimeOfDay todFunc(in TimeOfDay tod) { - TimeOfDay todFunc(in TimeOfDay tod) - { - return tod; - } + return tod; + } - auto interval = Interval!TimeOfDay(TimeOfDay(12, 1, 7), TimeOfDay(14, 0, 0)); + auto interval = Interval!TimeOfDay(TimeOfDay(12, 1, 7), TimeOfDay(14, 0, 0)); - auto ir = IntervalRange!(TimeOfDay, Direction.fwd)(interval, &todFunc); - } + auto ir = IntervalRange!(TimeOfDay, Direction.fwd)(interval, &todFunc); + } + { + DateTime dtFunc(in DateTime dt) { - DateTime dtFunc(in DateTime dt) - { - return dt; - } + return dt; + } - auto interval = Interval!DateTime(DateTime(2010, 7, 4, 12, 1, 7), DateTime(2012, 1, 7, 14, 0, 0)); + auto interval = Interval!DateTime(DateTime(2010, 7, 4, 12, 1, 7), DateTime(2012, 1, 7, 14, 0, 0)); - auto ir = IntervalRange!(DateTime, Direction.fwd)(interval, &dtFunc); - } + auto ir = IntervalRange!(DateTime, Direction.fwd)(interval, &dtFunc); + } + { + SysTime stFunc(in SysTime st) { - SysTime stFunc(in SysTime st) - { - return cast(SysTime)st; - } + return cast(SysTime)st; + } - auto interval = Interval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7)), SysTime(DateTime(2012, 1, 7, 14, 0, 0))); + auto interval = Interval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7)), SysTime(DateTime(2012, 1, 7, 14, 0, 0))); - auto ir = IntervalRange!(SysTime, Direction.fwd)(interval, &stFunc); - } + auto ir = IntervalRange!(SysTime, Direction.fwd)(interval, &stFunc); } } //Test IntervalRange's empty(). unittest { - version(testStdDateTime) + //fwd { - //fwd - { - auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - assert(!range.empty); - range.popFront(); - assert(range.empty); + assert(!range.empty); + range.popFront(); + assert(range.empty); - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - static assert(__traits(compiles, cRange.empty)); + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + static assert(__traits(compiles, cRange.empty)); - //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if - //empty works with it. However, since an immutable range is pretty useless, it's no great loss. - } + //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if + //empty works with it. However, since an immutable range is pretty useless, it's no great loss. + } - //bwd - { - auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + //bwd + { + auto range = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 21)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - assert(!range.empty); - range.popFront(); - assert(range.empty); + assert(!range.empty); + range.popFront(); + assert(range.empty); - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - static assert(__traits(compiles, cRange.empty)); + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + static assert(__traits(compiles, cRange.empty)); - //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if - //empty works with it. However, since an immutable range is pretty useless, it's no great loss. - } + //Apparently, creating an immutable IntervalRange!Date doesn't work, so we can't test if + //empty works with it. However, since an immutable range is pretty useless, it's no great loss. } } //Test IntervalRange's front. unittest { - version(testStdDateTime) + //fwd { - //fwd - { - auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - assertThrown!DateTimeException((in IntervalRange!(Date, Direction.fwd) range){range.front;}(emptyRange)); + auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + assertThrown!DateTimeException((in IntervalRange!(Date, Direction.fwd) range){range.front;}(emptyRange)); - auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); - _assertPred!"=="(range.front, Date(2010, 7, 4)); + auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); + assert(range.front == Date(2010, 7, 4)); - auto poppedRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - _assertPred!"=="(poppedRange.front, Date(2010, 7, 7)); + auto poppedRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + assert(poppedRange.front == Date(2010, 7, 7)); - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - static assert(__traits(compiles, cRange.front)); - } + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + static assert(__traits(compiles, cRange.front)); + } - //bwd - { - auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - assertThrown!DateTimeException((in IntervalRange!(Date, Direction.bwd) range){range.front;}(emptyRange)); + //bwd + { + auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + assertThrown!DateTimeException((in IntervalRange!(Date, Direction.bwd) range){range.front;}(emptyRange)); - auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); - _assertPred!"=="(range.front, Date(2012, 1, 7)); + auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); + assert(range.front == Date(2012, 1, 7)); - auto poppedRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - _assertPred!"=="(poppedRange.front, Date(2012, 1, 4)); + auto poppedRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + assert(poppedRange.front == Date(2012, 1, 4)); - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - static assert(__traits(compiles, cRange.front)); - } + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + static assert(__traits(compiles, cRange.front)); } } //Test IntervalRange's popFront(). unittest { - version(testStdDateTime) + //fwd { - //fwd - { - auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - assertThrown!DateTimeException((IntervalRange!(Date, Direction.fwd) range){range.popFront();}(emptyRange)); + auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + assertThrown!DateTimeException((IntervalRange!(Date, Direction.fwd) range){range.popFront();}(emptyRange)); - auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - auto expected = range.front; - - foreach(date; range) - { - _assertPred!"=="(date, expected); - expected += dur!"days"(7); - } - - _assertPred!"=="(walkLength(range), 79); + auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + auto expected = range.front; - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - static assert(__traits(compiles, cRange.front)); + foreach(date; range) + { + assert(date == expected); + expected += dur!"days"(7); } - //bwd - { - auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - assertThrown!DateTimeException((IntervalRange!(Date, Direction.bwd) range){range.popFront();}(emptyRange)); + assert(walkLength(range) == 79); - auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - auto expected = range.front; + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + static assert(__traits(compiles, cRange.front)); + } - foreach(date; range) - { - _assertPred!"=="(date, expected); - expected += dur!"days"(-7); - } + //bwd + { + auto emptyRange = Interval!Date(Date(2010, 9, 19), Date(2010, 9, 20)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + assertThrown!DateTimeException((IntervalRange!(Date, Direction.bwd) range){range.popFront();}(emptyRange)); - _assertPred!"=="(walkLength(range), 79); + auto range = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + auto expected = range.front; - const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - static assert(!__traits(compiles, cRange.popFront())); + foreach(date; range) + { + assert(date == expected); + expected += dur!"days"(-7); } + + assert(walkLength(range) == 79); + + const cRange = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + static assert(!__traits(compiles, cRange.popFront())); } } //Test IntervalRange's save. unittest { - version(testStdDateTime) + //fwd { - //fwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); - assert(range.save == range); - } + assert(range.save == range); + } - //bwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); + //bwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); - assert(range.save == range); - } + assert(range.save == range); } } //Test IntervalRange's interval. unittest { - version(testStdDateTime) + //fwd { - //fwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); - assert(range.interval == interval); + assert(range.interval == interval); - const cRange = range; - static assert(__traits(compiles, cRange.interval)); - } + const cRange = range; + static assert(__traits(compiles, cRange.interval)); + } - //bwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); + //bwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); - assert(range.interval == interval); + assert(range.interval == interval); - const cRange = range; - static assert(__traits(compiles, cRange.interval)); - } + const cRange = range; + static assert(__traits(compiles, cRange.interval)); } } //Test IntervalRange's func. unittest { - version(testStdDateTime) + //fwd { - //fwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); - assert(range.func == func); - } + assert(range.func == func); + } - //bwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); + //bwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); - assert(range.func == func); - } + assert(range.func == func); } } //Test IntervalRange's direction. unittest { - version(testStdDateTime) + //fwd { - //fwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); - assert(range.direction == Direction.fwd); + assert(range.direction == Direction.fwd); - const cRange = range; - static assert(__traits(compiles, cRange.direction)); - } + const cRange = range; + static assert(__traits(compiles, cRange.direction)); + } - //bwd - { - auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); + //bwd + { + auto interval = Interval!Date(Date(2010, 7, 4), Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); - assert(range.direction == Direction.bwd); + assert(range.direction == Direction.bwd); - const cRange = range; - static assert(__traits(compiles, cRange.direction)); - } + const cRange = range; + static assert(__traits(compiles, cRange.direction)); } } @@ -26915,7 +25225,7 @@ public: Params: rhs = The $(D PosInfIntervalRange) to assign to this one. +/ - /+ref+/ PosInfIntervalRange opAssign(ref PosInfIntervalRange rhs) pure nothrow + ref PosInfIntervalRange opAssign(ref PosInfIntervalRange rhs) pure nothrow { _interval = rhs._interval; _func = rhs._func; @@ -26925,7 +25235,7 @@ public: /++ Ditto +/ - /+ref+/ PosInfIntervalRange opAssign(PosInfIntervalRange rhs) pure nothrow + ref PosInfIntervalRange opAssign(PosInfIntervalRange rhs) pure nothrow { return this = rhs; } @@ -27029,154 +25339,133 @@ private: //Test that PosInfIntervalRange satisfies the range predicates that it's supposed to satisfy. unittest { - version(testStdDateTime) - { - static assert(isInputRange!(PosInfIntervalRange!Date)); - static assert(isForwardRange!(PosInfIntervalRange!Date)); - static assert(isInfinite!(PosInfIntervalRange!Date)); - - //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 - //static assert(!isOutputRange!(PosInfIntervalRange!Date, Date)); - static assert(!isBidirectionalRange!(PosInfIntervalRange!Date)); - static assert(!isRandomAccessRange!(PosInfIntervalRange!Date)); - static assert(!hasSwappableElements!(PosInfIntervalRange!Date)); - static assert(!hasAssignableElements!(PosInfIntervalRange!Date)); - static assert(!hasLength!(PosInfIntervalRange!Date)); - static assert(!hasSlicing!(PosInfIntervalRange!Date)); - - static assert(is(ElementType!(PosInfIntervalRange!Date) == Date)); - static assert(is(ElementType!(PosInfIntervalRange!TimeOfDay) == TimeOfDay)); - static assert(is(ElementType!(PosInfIntervalRange!DateTime) == DateTime)); - static assert(is(ElementType!(PosInfIntervalRange!SysTime) == SysTime)); - } + static assert(isInputRange!(PosInfIntervalRange!Date)); + static assert(isForwardRange!(PosInfIntervalRange!Date)); + static assert(isInfinite!(PosInfIntervalRange!Date)); + + //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 + //static assert(!isOutputRange!(PosInfIntervalRange!Date, Date)); + static assert(!isBidirectionalRange!(PosInfIntervalRange!Date)); + static assert(!isRandomAccessRange!(PosInfIntervalRange!Date)); + static assert(!hasSwappableElements!(PosInfIntervalRange!Date)); + static assert(!hasAssignableElements!(PosInfIntervalRange!Date)); + static assert(!hasLength!(PosInfIntervalRange!Date)); + static assert(!hasSlicing!(PosInfIntervalRange!Date)); + + static assert(is(ElementType!(PosInfIntervalRange!Date) == Date)); + static assert(is(ElementType!(PosInfIntervalRange!TimeOfDay) == TimeOfDay)); + static assert(is(ElementType!(PosInfIntervalRange!DateTime) == DateTime)); + static assert(is(ElementType!(PosInfIntervalRange!SysTime) == SysTime)); } //Test construction of PosInfIntervalRange. unittest { - version(testStdDateTime) { + Date dateFunc(in Date date) { - Date dateFunc(in Date date) - { - return date; - } + return date; + } - auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); + auto posInfInterval = PosInfInterval!Date(Date(2010, 7, 4)); - auto ir = PosInfIntervalRange!Date(posInfInterval, &dateFunc); - } + auto ir = PosInfIntervalRange!Date(posInfInterval, &dateFunc); + } + { + TimeOfDay todFunc(in TimeOfDay tod) { - TimeOfDay todFunc(in TimeOfDay tod) - { - return tod; - } + return tod; + } - auto posInfInterval = PosInfInterval!TimeOfDay(TimeOfDay(12, 1, 7)); + auto posInfInterval = PosInfInterval!TimeOfDay(TimeOfDay(12, 1, 7)); - auto ir = PosInfIntervalRange!(TimeOfDay)(posInfInterval, &todFunc); - } + auto ir = PosInfIntervalRange!(TimeOfDay)(posInfInterval, &todFunc); + } + { + DateTime dtFunc(in DateTime dt) { - DateTime dtFunc(in DateTime dt) - { - return dt; - } + return dt; + } - auto posInfInterval = PosInfInterval!DateTime(DateTime(2010, 7, 4, 12, 1, 7)); + auto posInfInterval = PosInfInterval!DateTime(DateTime(2010, 7, 4, 12, 1, 7)); - auto ir = PosInfIntervalRange!(DateTime)(posInfInterval, &dtFunc); - } + auto ir = PosInfIntervalRange!(DateTime)(posInfInterval, &dtFunc); + } + { + SysTime stFunc(in SysTime st) { - SysTime stFunc(in SysTime st) - { - return cast(SysTime)st; - } + return cast(SysTime)st; + } - auto posInfInterval = PosInfInterval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7))); + auto posInfInterval = PosInfInterval!SysTime(SysTime(DateTime(2010, 7, 4, 12, 1, 7))); - auto ir = PosInfIntervalRange!(SysTime)(posInfInterval, &stFunc); - } + auto ir = PosInfIntervalRange!(SysTime)(posInfInterval, &stFunc); } } //Test PosInfIntervalRange's front. unittest { - version(testStdDateTime) - { - auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); - _assertPred!"=="(range.front, Date(2010, 7, 4)); + auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed)); + assert(range.front == Date(2010, 7, 4)); - auto poppedRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - _assertPred!"=="(poppedRange.front, Date(2010, 7, 7)); + auto poppedRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + assert(poppedRange.front == Date(2010, 7, 7)); - const cRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - static assert(__traits(compiles, cRange.front)); - } + const cRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + static assert(__traits(compiles, cRange.front)); } //Test PosInfIntervalRange's popFront(). unittest { - version(testStdDateTime) - { - auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); - auto expected = range.front; - - foreach(date; take(range, 79)) - { - _assertPred!"=="(date, expected); - expected += dur!"days"(7); - } + auto range = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.wed), PopFirst.yes); + auto expected = range.front; - const cRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); - static assert(!__traits(compiles, cRange.popFront())); + foreach(date; take(range, 79)) + { + assert(date == expected); + expected += dur!"days"(7); } + + const cRange = PosInfInterval!Date(Date(2010, 7, 4)).fwdRange(everyDayOfWeek!Date(DayOfWeek.fri)); + static assert(!__traits(compiles, cRange.popFront())); } //Test PosInfIntervalRange's save. unittest { - version(testStdDateTime) - { - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); - assert(range.save == range); - } + assert(range.save == range); } //Test PosInfIntervalRange's interval. unittest { - version(testStdDateTime) - { - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); - assert(range.interval == interval); + assert(range.interval == interval); - const cRange = range; - static assert(__traits(compiles, cRange.interval)); - } + const cRange = range; + static assert(__traits(compiles, cRange.interval)); } //Test PosInfIntervalRange's func. unittest { - version(testStdDateTime) - { - auto interval = PosInfInterval!Date(Date(2010, 7, 4)); - auto func = everyDayOfWeek!Date(DayOfWeek.fri); - auto range = interval.fwdRange(func); + auto interval = PosInfInterval!Date(Date(2010, 7, 4)); + auto func = everyDayOfWeek!Date(DayOfWeek.fri); + auto range = interval.fwdRange(func); - assert(range.func == func); - } + assert(range.func == func); } @@ -27221,7 +25510,7 @@ public: Params: rhs = The $(D NegInfIntervalRange) to assign to this one. +/ - /+ref+/ NegInfIntervalRange opAssign(ref NegInfIntervalRange rhs) pure nothrow + ref NegInfIntervalRange opAssign(ref NegInfIntervalRange rhs) pure nothrow { _interval = rhs._interval; _func = rhs._func; @@ -27231,7 +25520,7 @@ public: /++ Ditto +/ - /+ref+/ NegInfIntervalRange opAssign(NegInfIntervalRange rhs) pure nothrow + ref NegInfIntervalRange opAssign(NegInfIntervalRange rhs) pure nothrow { return this = rhs; } @@ -27335,153 +25624,132 @@ private: //Test that NegInfIntervalRange satisfies the range predicates that it's supposed to satisfy. unittest { - version(testStdDateTime) - { - static assert(isInputRange!(NegInfIntervalRange!Date)); - static assert(isForwardRange!(NegInfIntervalRange!Date)); - static assert(isInfinite!(NegInfIntervalRange!Date)); - - //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 - //static assert(!isOutputRange!(NegInfIntervalRange!Date, Date)); - static assert(!isBidirectionalRange!(NegInfIntervalRange!Date)); - static assert(!isRandomAccessRange!(NegInfIntervalRange!Date)); - static assert(!hasSwappableElements!(NegInfIntervalRange!Date)); - static assert(!hasAssignableElements!(NegInfIntervalRange!Date)); - static assert(!hasLength!(NegInfIntervalRange!Date)); - static assert(!hasSlicing!(NegInfIntervalRange!Date)); - - static assert(is(ElementType!(NegInfIntervalRange!Date) == Date)); - static assert(is(ElementType!(NegInfIntervalRange!TimeOfDay) == TimeOfDay)); - static assert(is(ElementType!(NegInfIntervalRange!DateTime) == DateTime)); - } + static assert(isInputRange!(NegInfIntervalRange!Date)); + static assert(isForwardRange!(NegInfIntervalRange!Date)); + static assert(isInfinite!(NegInfIntervalRange!Date)); + + //Commented out due to bug http://d.puremagic.com/issues/show_bug.cgi?id=4895 + //static assert(!isOutputRange!(NegInfIntervalRange!Date, Date)); + static assert(!isBidirectionalRange!(NegInfIntervalRange!Date)); + static assert(!isRandomAccessRange!(NegInfIntervalRange!Date)); + static assert(!hasSwappableElements!(NegInfIntervalRange!Date)); + static assert(!hasAssignableElements!(NegInfIntervalRange!Date)); + static assert(!hasLength!(NegInfIntervalRange!Date)); + static assert(!hasSlicing!(NegInfIntervalRange!Date)); + + static assert(is(ElementType!(NegInfIntervalRange!Date) == Date)); + static assert(is(ElementType!(NegInfIntervalRange!TimeOfDay) == TimeOfDay)); + static assert(is(ElementType!(NegInfIntervalRange!DateTime) == DateTime)); } //Test construction of NegInfIntervalRange. unittest { - version(testStdDateTime) { + Date dateFunc(in Date date) { - Date dateFunc(in Date date) - { - return date; - } + return date; + } - auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); + auto negInfInterval = NegInfInterval!Date(Date(2012, 1, 7)); - auto ir = NegInfIntervalRange!Date(negInfInterval, &dateFunc); - } + auto ir = NegInfIntervalRange!Date(negInfInterval, &dateFunc); + } + { + TimeOfDay todFunc(in TimeOfDay tod) { - TimeOfDay todFunc(in TimeOfDay tod) - { - return tod; - } + return tod; + } - auto negInfInterval = NegInfInterval!TimeOfDay(TimeOfDay(14, 0, 0)); + auto negInfInterval = NegInfInterval!TimeOfDay(TimeOfDay(14, 0, 0)); - auto ir = NegInfIntervalRange!(TimeOfDay)(negInfInterval, &todFunc); - } + auto ir = NegInfIntervalRange!(TimeOfDay)(negInfInterval, &todFunc); + } + { + DateTime dtFunc(in DateTime dt) { - DateTime dtFunc(in DateTime dt) - { - return dt; - } + return dt; + } - auto negInfInterval = NegInfInterval!DateTime(DateTime(2012, 1, 7, 14, 0, 0)); + auto negInfInterval = NegInfInterval!DateTime(DateTime(2012, 1, 7, 14, 0, 0)); - auto ir = NegInfIntervalRange!(DateTime)(negInfInterval, &dtFunc); - } + auto ir = NegInfIntervalRange!(DateTime)(negInfInterval, &dtFunc); + } + { + SysTime stFunc(in SysTime st) { - SysTime stFunc(in SysTime st) - { - return cast(SysTime)(st); - } + return cast(SysTime)(st); + } - auto negInfInterval = NegInfInterval!SysTime(SysTime(DateTime(2012, 1, 7, 14, 0, 0))); + auto negInfInterval = NegInfInterval!SysTime(SysTime(DateTime(2012, 1, 7, 14, 0, 0))); - auto ir = NegInfIntervalRange!(SysTime)(negInfInterval, &stFunc); - } + auto ir = NegInfIntervalRange!(SysTime)(negInfInterval, &stFunc); } } //Test NegInfIntervalRange's front. unittest { - version(testStdDateTime) - { - auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); - _assertPred!"=="(range.front, Date(2012, 1, 7)); + auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed)); + assert(range.front == Date(2012, 1, 7)); - auto poppedRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - _assertPred!"=="(poppedRange.front, Date(2012, 1, 4)); + auto poppedRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + assert(poppedRange.front == Date(2012, 1, 4)); - const cRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - static assert(__traits(compiles, cRange.front)); - } + const cRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + static assert(__traits(compiles, cRange.front)); } //Test NegInfIntervalRange's popFront(). unittest { - version(testStdDateTime) - { - auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); - auto expected = range.front; - - foreach(date; take(range, 79)) - { - _assertPred!"=="(date, expected); - expected += dur!"days"(-7); - } + auto range = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.wed), PopFirst.yes); + auto expected = range.front; - const cRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); - static assert(!__traits(compiles, cRange.popFront())); + foreach(date; take(range, 79)) + { + assert(date == expected); + expected += dur!"days"(-7); } + + const cRange = NegInfInterval!Date(Date(2012, 1, 7)).bwdRange(everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri)); + static assert(!__traits(compiles, cRange.popFront())); } //Test NegInfIntervalRange's save. unittest { - version(testStdDateTime) - { - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); - assert(range.save == range); - } + assert(range.save == range); } //Test NegInfIntervalRange's interval. unittest { - version(testStdDateTime) - { - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); - assert(range.interval == interval); + assert(range.interval == interval); - const cRange = range; - static assert(__traits(compiles, cRange.interval)); - } + const cRange = range; + static assert(__traits(compiles, cRange.interval)); } //Test NegInfIntervalRange's func. unittest { - version(testStdDateTime) - { - auto interval = NegInfInterval!Date(Date(2012, 1, 7)); - auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); - auto range = interval.bwdRange(func); + auto interval = NegInfInterval!Date(Date(2012, 1, 7)); + auto func = everyDayOfWeek!(Date, Direction.bwd)(DayOfWeek.fri); + auto range = interval.bwdRange(func); - assert(range.func == func); - } + assert(range.func == func); } @@ -27507,7 +25775,7 @@ public: $(WEB en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of Time Zones) +/ - @property string name() const nothrow + @property string name() @safe const nothrow { return _name; } @@ -27520,7 +25788,7 @@ public: However, on Windows, it may be the unabbreviated name (e.g. Pacific Standard Time). Regardless, it is not the same as name. +/ - @property string stdName() const nothrow + @property string stdName() @safe const nothrow { return _stdName; } @@ -27533,7 +25801,7 @@ public: However, on Windows, it may be the unabbreviated name (e.g. Pacific Daylight Time). Regardless, it is not the same as name. +/ - @property string dstName() const nothrow + @property string dstName() @safe const nothrow { return _dstName; } @@ -27545,7 +25813,7 @@ public: but will still return true for $(D hasDST) because the time zone did at some point have DST. +/ - @property abstract bool hasDST() const nothrow; + @property abstract bool hasDST() @safe const nothrow; /++ @@ -27557,7 +25825,7 @@ public: stdTime = The UTC time that needs to be checked for DST in this time zone. +/ - abstract bool dstInEffect(long stdTime) const nothrow; + abstract bool dstInEffect(long stdTime) @safe const nothrow; /++ @@ -27568,7 +25836,7 @@ public: stdTime = The UTC time that needs to be adjusted to this time zone's time. +/ - abstract long utcToTZ(long stdTime) const nothrow; + abstract long utcToTZ(long stdTime) @safe const nothrow; /++ @@ -27579,7 +25847,7 @@ public: adjTime = The time in this time zone that needs to be adjusted to UTC time. +/ - abstract long tzToUTC(long adjTime) const nothrow; + abstract long tzToUTC(long adjTime) @safe const nothrow; /++ @@ -27590,7 +25858,7 @@ public: stdTime = The UTC time for which to get the offset from UTC for this time zone. +/ - Duration utcOffsetAt(long stdTime) const nothrow + Duration utcOffsetAt(long stdTime) @safe const nothrow { return dur!"hnsecs"(utcToTZ(stdTime) - stdTime); } @@ -27629,7 +25897,7 @@ public: auto tz = TimeZone.getTimeZone("America/Los_Angeles"); -------------------- +/ - static immutable(TimeZone) getTimeZone(string name) + static immutable(TimeZone) getTimeZone(string name) @safe { version(Posix) return PosixTimeZone.getTimeZone(name); @@ -27640,7 +25908,7 @@ auto tz = TimeZone.getTimeZone("America/Los_Angeles"); //Since reading in the time zone files could be expensive, most unit tests //are consolidated into this one unittest block which minimizes how often it //reads a time zone file. - version(testStdDateTime) unittest + unittest { version(Posix) scope(exit) clearTZEnvVar(); @@ -27657,13 +25925,13 @@ auto tz = TimeZone.getTimeZone("America/Los_Angeles"); immutable hasDST = dstOffset != dur!"hnsecs"(0); version(Posix) - _assertPred!"=="(tz.name, tzName); + assert(tz.name == tzName); else version(Windows) - _assertPred!"=="(tz.name, stdName); + assert(tz.name == stdName); - //_assertPred!"=="(tz.stdName, stdName); //Locale-dependent - //_assertPred!"=="(tz.dstName, dstName); //Locale-dependent - _assertPred!"=="(tz.hasDST, hasDST); + //assert(tz.stdName == stdName); //Locale-dependent + //assert(tz.dstName == dstName); //Locale-dependent + assert(tz.hasDST == hasDST); immutable stdDate = DateTime(2010, north ? 1 : 7, 1, 6, 0, 0); immutable dstDate = DateTime(2010, north ? 7 : 1, 1, 6, 0, 0); @@ -27673,13 +25941,13 @@ auto tz = TimeZone.getTimeZone("America/Los_Angeles"); auto dstUTC = SysTime(stdDate - utcOffset + dstOffset, UTC()); assert(!std.dstInEffect); - _assertPred!"=="(dst.dstInEffect, hasDST); - _assertPred!"=="(tz.utcOffsetAt(std.stdTime), utcOffset); - _assertPred!"=="(tz.utcOffsetAt(dst.stdTime), utcOffset + dstOffset); + assert(dst.dstInEffect == hasDST); + assert(tz.utcOffsetAt(std.stdTime) == utcOffset); + assert(tz.utcOffsetAt(dst.stdTime) == utcOffset + dstOffset); - _assertPred!"=="(cast(DateTime)std, stdDate); - _assertPred!"=="(cast(DateTime)dst, dstDate); - _assertPred!"=="(std, stdUTC); + assert(cast(DateTime)std == stdDate); + assert(cast(DateTime)dst == dstDate); + assert(std == stdUTC); version(Posix) { @@ -27691,17 +25959,17 @@ auto tz = TimeZone.getTimeZone("America/Los_Angeles"); tm* osTimeInfo = localtime(&unixTime); tm ourTimeInfo = st.toTM(); - _assertPred!"=="(ourTimeInfo.tm_sec, osTimeInfo.tm_sec); - _assertPred!"=="(ourTimeInfo.tm_min, osTimeInfo.tm_min); - _assertPred!"=="(ourTimeInfo.tm_hour, osTimeInfo.tm_hour); - _assertPred!"=="(ourTimeInfo.tm_mday, osTimeInfo.tm_mday); - _assertPred!"=="(ourTimeInfo.tm_mon, osTimeInfo.tm_mon); - _assertPred!"=="(ourTimeInfo.tm_year, osTimeInfo.tm_year); - _assertPred!"=="(ourTimeInfo.tm_wday, osTimeInfo.tm_wday); - _assertPred!"=="(ourTimeInfo.tm_yday, osTimeInfo.tm_yday); - _assertPred!"=="(ourTimeInfo.tm_isdst, osTimeInfo.tm_isdst); - _assertPred!"=="(ourTimeInfo.tm_gmtoff, osTimeInfo.tm_gmtoff); - _assertPred!"=="(to!string(ourTimeInfo.tm_zone), + assert(ourTimeInfo.tm_sec == osTimeInfo.tm_sec); + assert(ourTimeInfo.tm_min == osTimeInfo.tm_min); + assert(ourTimeInfo.tm_hour == osTimeInfo.tm_hour); + assert(ourTimeInfo.tm_mday == osTimeInfo.tm_mday); + assert(ourTimeInfo.tm_mon == osTimeInfo.tm_mon); + assert(ourTimeInfo.tm_year == osTimeInfo.tm_year); + assert(ourTimeInfo.tm_wday == osTimeInfo.tm_wday); + assert(ourTimeInfo.tm_yday == osTimeInfo.tm_yday); + assert(ourTimeInfo.tm_isdst == osTimeInfo.tm_isdst); + assert(ourTimeInfo.tm_gmtoff == osTimeInfo.tm_gmtoff); + assert(to!string(ourTimeInfo.tm_zone) == to!string(osTimeInfo.tm_zone)); } @@ -27715,7 +25983,7 @@ auto tz = TimeZone.getTimeZone("America/Los_Angeles"); //be there, but since PosixTimeZone _does_ use leap seconds if //the time zone file does, we'll test that functionality if the //appropriate files exist. - if((PosixTimeZone.defaultTZDatabaseDir ~ "right/" ~ tzName).exists()) + if(buildPath(PosixTimeZone.defaultTZDatabaseDir, "right", tzName).exists) { auto leapTZ = PosixTimeZone.getTimeZone("right/" ~ tzName); @@ -27730,14 +25998,14 @@ auto tz = TimeZone.getTimeZone("America/Los_Angeles"); assert(!leapSTD.dstInEffect); assert(leapDST.dstInEffect == hasDST); - _assertPred!"=="(leapSTD.stdTime, std.stdTime); - _assertPred!"=="(leapDST.stdTime, dst.stdTime); + assert(leapSTD.stdTime == std.stdTime); + assert(leapDST.stdTime == dst.stdTime); //Whenever a leap second is added/removed, //this will have to be adjusted. //enum leapDiff = convert!("seconds", "hnsecs")(25); - //_assertPred!"=="(leapSTD.adjTime - leapDiff, std.adjTime); - //_assertPred!"=="(leapDST.adjTime - leapDiff, dst.adjTime); + //assert(leapSTD.adjTime - leapDiff == std.adjTime); + //assert(leapDST.adjTime - leapDiff == dst.adjTime); } } @@ -27935,7 +26203,7 @@ auto tz = TimeZone.getTimeZone("America/Los_Angeles"); $(LREF DateTimeException) on Windows systems if it fails to read the registry. +/ - static string[] getInstalledTZNames(string subName = "") + static string[] getInstalledTZNames(string subName = "") @safe { version(Posix) return PosixTimeZone.getInstalledTZNames(subName); @@ -27948,32 +26216,33 @@ auto tz = TimeZone.getTimeZone("America/Los_Angeles"); { auto tzName = windowsTZNameToTZDatabaseName(winName); - if(tzName.startsWith(subName)) + version(unittest) + assert(tzName !is null, format("TZName which is missing: %s", winName)); + + if(tzName !is null && tzName.startsWith(subName)) retval.put(tzName); } sort(retval.data); - return retval.data; } } unittest { - version(testStdDateTime) + static void testPZSuccess(string tzName) { - static void testPZSuccess(string tzName) - { - scope(failure) writefln("TZName which threw: %s", tzName); - - TimeZone.getTimeZone(tzName); - } + scope(failure) writefln("TZName which threw: %s", tzName); + TimeZone.getTimeZone(tzName); + } - auto tzNames = getInstalledTZNames(); + auto tzNames = getInstalledTZNames(); + // This was not previously tested, and it's currently failing, so I'm + // leaving it commented out until I can sort it out. + //assert(equal(tzNames, tzNames.uniq())); - foreach(tzName; tzNames) - assertNotThrown!DateTimeException(testPZSuccess(tzName)); - } + foreach(tzName; tzNames) + assertNotThrown!DateTimeException(testPZSuccess(tzName)); } @@ -27985,7 +26254,7 @@ private: stdName = The abbreviation for the time zone during std time. dstName = The abbreviation for the time zone during DST. +/ - this(string name, string stdName, string dstName) immutable pure + this(string name, string stdName, string dstName) @safe immutable pure { _name = name; _stdName = stdName; @@ -28017,9 +26286,9 @@ public: $(LREF LocalTime) is a singleton class. $(LREF LocalTime) returns its only instance. +/ - static immutable(LocalTime) opCall() pure nothrow + static immutable(LocalTime) opCall() @trusted pure nothrow { - alias pure nothrow immutable(LocalTime) function() FuncType; + alias @safe pure nothrow immutable(LocalTime) function() FuncType; return (cast(FuncType)&singleton)(); } @@ -28042,7 +26311,7 @@ public: $(WEB en.wikipedia.org/wiki/List_of_tz_database_time_zones, List of Time Zones) +/ - @property override string name() const nothrow; + @property override string name() @safe const nothrow; } @@ -28058,7 +26327,7 @@ public: dynamically rather than it being fixed like it would be with most time zones. +/ - @property override string stdName() const nothrow + @property override string stdName() @trusted const nothrow { version(Posix) { @@ -28100,21 +26369,18 @@ public: } unittest - { - version(testStdDateTime) - { - assert(LocalTime().stdName !is null); +{ + assert(LocalTime().stdName !is null); - version(Posix) - { - scope(exit) clearTZEnvVar(); + version(Posix) + { + scope(exit) clearTZEnvVar(); - setTZEnvVar("America/Los_Angeles"); - _assertPred!"=="(LocalTime().stdName, "PST"); + setTZEnvVar("America/Los_Angeles"); + assert(LocalTime().stdName == "PST"); - setTZEnvVar("America/New_York"); - _assertPred!"=="(LocalTime().stdName, "EST"); - } + setTZEnvVar("America/New_York"); + assert(LocalTime().stdName == "EST"); } } @@ -28131,7 +26397,7 @@ public: dynamically rather than it being fixed like it would be with most time zones. +/ - @property override string dstName() const nothrow + @property override string dstName() @trusted const nothrow { version(Posix) { @@ -28173,21 +26439,18 @@ public: } unittest - { - version(testStdDateTime) - { - assert(LocalTime().dstName !is null); +{ + assert(LocalTime().dstName !is null); - version(Posix) - { - scope(exit) clearTZEnvVar(); + version(Posix) + { + scope(exit) clearTZEnvVar(); - setTZEnvVar("America/Los_Angeles"); - _assertPred!"=="(LocalTime().dstName, "PDT"); + setTZEnvVar("America/Los_Angeles"); + assert(LocalTime().dstName == "PDT"); - setTZEnvVar("America/New_York"); - _assertPred!"=="(LocalTime().dstName, "EDT"); - } + setTZEnvVar("America/New_York"); + assert(LocalTime().dstName == "EDT"); } } @@ -28198,7 +26461,7 @@ public: dates but will still return true for $(D hasDST) because the time zone did at some point have DST. +/ - @property override bool hasDST() const nothrow + @property override bool hasDST() @trusted const nothrow { version(Posix) { @@ -28235,24 +26498,21 @@ public: } unittest - { - version(testStdDateTime) - { - LocalTime().hasDST; +{ + LocalTime().hasDST; - version(Posix) - { - scope(exit) clearTZEnvVar(); + version(Posix) + { + scope(exit) clearTZEnvVar(); - setTZEnvVar("America/Los_Angeles"); - assert(LocalTime().hasDST); + setTZEnvVar("America/Los_Angeles"); + assert(LocalTime().hasDST); - setTZEnvVar("America/New_York"); - assert(LocalTime().hasDST); + setTZEnvVar("America/New_York"); + assert(LocalTime().hasDST); - setTZEnvVar("UTC"); - assert(!LocalTime().hasDST); - } + setTZEnvVar("UTC"); + assert(!LocalTime().hasDST); } } @@ -28266,7 +26526,7 @@ public: stdTime = The UTC time that needs to be checked for DST in this time zone. +/ - override bool dstInEffect(long stdTime) const nothrow + override bool dstInEffect(long stdTime) @trusted const nothrow { time_t unixTime = stdTimeToUnixTime(stdTime); @@ -28300,11 +26560,8 @@ public: unittest { - version(testStdDateTime) - { - auto currTime = Clock.currStdTime; - LocalTime().dstInEffect(currTime); - } + auto currTime = Clock.currStdTime; + LocalTime().dstInEffect(currTime); } @@ -28320,7 +26577,7 @@ public: See_Also: $(D TimeZone.utcToTZ) +/ - override long utcToTZ(long stdTime) const nothrow + override long utcToTZ(long stdTime) @trusted const nothrow { version(Posix) { @@ -28344,10 +26601,7 @@ public: unittest { - version(testStdDateTime) - { - LocalTime().utcToTZ(0); - } + LocalTime().utcToTZ(0); } @@ -28363,7 +26617,7 @@ public: adjTime = The time in this time zone that needs to be adjusted to UTC time. +/ - override long tzToUTC(long adjTime) const nothrow + override long tzToUTC(long adjTime) @trusted const nothrow { version(Posix) { @@ -28403,142 +26657,139 @@ public: unittest { - version(testStdDateTime) - { - assert(LocalTime().tzToUTC(LocalTime().utcToTZ(0)) == 0); - assert(LocalTime().utcToTZ(LocalTime().tzToUTC(0)) == 0); + assert(LocalTime().tzToUTC(LocalTime().utcToTZ(0)) == 0); + assert(LocalTime().utcToTZ(LocalTime().tzToUTC(0)) == 0); - _assertPred!"=="(LocalTime().tzToUTC(LocalTime().utcToTZ(0)), 0); - _assertPred!"=="(LocalTime().utcToTZ(LocalTime().tzToUTC(0)), 0); + assert(LocalTime().tzToUTC(LocalTime().utcToTZ(0)) == 0); + assert(LocalTime().utcToTZ(LocalTime().tzToUTC(0)) == 0); - version(Posix) - { - scope(exit) clearTZEnvVar(); + version(Posix) + { + scope(exit) clearTZEnvVar(); - auto tzInfos = [tuple("America/Los_Angeles", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), - tuple("America/New_York", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), - //tuple("America/Santiago", DateTime(2011, 8, 21), DateTime(2011, 5, 8), 0, 0), - tuple("Atlantic/Azores", DateTime(2011, 3, 27), DateTime(2011, 10, 30), 0, 1), - tuple("Europe/London", DateTime(2012, 3, 25), DateTime(2012, 10, 28), 1, 2), - tuple("Europe/Paris", DateTime(2012, 3, 25), DateTime(2012, 10, 28), 2, 3), - tuple("Australia/Adelaide", DateTime(2012, 10, 7), DateTime(2012, 4, 1), 2, 3)]; + auto tzInfos = [tuple("America/Los_Angeles", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), + tuple("America/New_York", DateTime(2012, 3, 11), DateTime(2012, 11, 4), 2, 2), + //tuple("America/Santiago", DateTime(2011, 8, 21), DateTime(2011, 5, 8), 0, 0), + tuple("Atlantic/Azores", DateTime(2011, 3, 27), DateTime(2011, 10, 30), 0, 1), + tuple("Europe/London", DateTime(2012, 3, 25), DateTime(2012, 10, 28), 1, 2), + tuple("Europe/Paris", DateTime(2012, 3, 25), DateTime(2012, 10, 28), 2, 3), + tuple("Australia/Adelaide", DateTime(2012, 10, 7), DateTime(2012, 4, 1), 2, 3)]; - foreach(i; 0 .. tzInfos.length) + foreach(i; 0 .. tzInfos.length) + { + auto tzName = tzInfos[i][0]; + setTZEnvVar(tzName); + immutable spring = tzInfos[i][3]; + immutable fall = tzInfos[i][4]; + auto stdOffset = SysTime(tzInfos[i][1] + dur!"hours"(-12)).utcOffset; + auto dstOffset = stdOffset + dur!"hours"(1); + + //Verify that creating a SysTime in the given time zone results + //in a SysTime with the correct std time during and surrounding + //a DST switch. + foreach(hour; -12 .. 13) { - auto tzName = tzInfos[i][0]; - setTZEnvVar(tzName); - immutable spring = tzInfos[i][3]; - immutable fall = tzInfos[i][4]; - auto stdOffset = SysTime(tzInfos[i][1] + dur!"hours"(-12)).utcOffset; - auto dstOffset = stdOffset + dur!"hours"(1); - - //Verify that creating a SysTime in the given time zone results - //in a SysTime with the correct std time during and surrounding - //a DST switch. - foreach(hour; -12 .. 13) + auto st = SysTime(tzInfos[i][1] + dur!"hours"(hour)); + immutable targetHour = hour < 0 ? hour + 24 : hour; + + static void testHour(SysTime st, int hour, string tzName, size_t line = __LINE__) { - auto st = SysTime(tzInfos[i][1] + dur!"hours"(hour)); - immutable targetHour = hour < 0 ? hour + 24 : hour; + enforce(st.hour == hour, + new AssertError(format("[%s] [%s]: [%s] [%s]", st, tzName, st.hour, hour), + __FILE__, line)); + } - static void testHour(SysTime st, int hour, string tzName, size_t line = __LINE__) + void testOffset1(Duration offset, bool dstInEffect, size_t line = __LINE__) + { + AssertError msg(string tag) { - enforce(st.hour == hour, - new AssertError(format("[%s] [%s]: [%s] [%s]", st, tzName, st.hour, hour), - __FILE__, line)); + return new AssertError(format("%s [%s] [%s]: [%s] [%s] [%s]", + tag, st, tzName, st.utcOffset, stdOffset, dstOffset), + __FILE__, line); } - void testOffset1(Duration offset, bool dstInEffect, size_t line = __LINE__) - { - AssertError msg(string tag) - { - return new AssertError(format("%s [%s] [%s]: [%s] [%s] [%s]", - tag, st, tzName, st.utcOffset, stdOffset, dstOffset), - __FILE__, line); - } + enforce(st.dstInEffect == dstInEffect, msg("1")); + enforce(st.utcOffset == offset, msg("2")); + enforce((st + dur!"minutes"(1)).utcOffset == offset, msg("3")); + } - enforce(st.dstInEffect == dstInEffect, msg("1")); - enforce(st.utcOffset == offset, msg("2")); - enforce((st + dur!"minutes"(1)).utcOffset == offset, msg("3")); - } + if(hour == spring) + { + testHour(st, spring + 1, tzName); + testHour(st + dur!"minutes"(1), spring + 1, tzName); + } + else + { + testHour(st, targetHour, tzName); + testHour(st + dur!"minutes"(1), targetHour, tzName); + } - if(hour == spring) - { - testHour(st, spring + 1, tzName); - testHour(st + dur!"minutes"(1), spring + 1, tzName); - } - else - { - testHour(st, targetHour, tzName); - testHour(st + dur!"minutes"(1), targetHour, tzName); - } + if(hour < spring) + testOffset1(stdOffset, false); + else + testOffset1(dstOffset, true); - if(hour < spring) - testOffset1(stdOffset, false); - else - testOffset1(dstOffset, true); + st = SysTime(tzInfos[i][2] + dur!"hours"(hour)); + testHour(st, targetHour, tzName); - st = SysTime(tzInfos[i][2] + dur!"hours"(hour)); - testHour(st, targetHour, tzName); + //Verify that 01:00 is the first 01:00 (or whatever hour before the switch is). + if(hour == fall - 1) + testHour(st + dur!"hours"(1), targetHour, tzName); - //Verify that 01:00 is the first 01:00 (or whatever hour before the switch is). - if(hour == fall - 1) - testHour(st + dur!"hours"(1), targetHour, tzName); + if(hour < fall) + testOffset1(dstOffset, true); + else + testOffset1(stdOffset, false); + } - if(hour < fall) - testOffset1(dstOffset, true); - else - testOffset1(stdOffset, false); - } + //Verify that converting a time in UTC to a time in another + //time zone results in the correct time during and surrounding + //a DST switch. + bool first = true; + auto springSwitch = SysTime(tzInfos[i][1] + dur!"hours"(spring), UTC()) - stdOffset; + auto fallSwitch = SysTime(tzInfos[i][2] + dur!"hours"(fall), UTC()) - dstOffset; + //@@@BUG@@@ 3659 makes this necessary. + auto fallSwitchMinus1 = fallSwitch - dur!"hours"(1); - //Verify that converting a time in UTC to a time in another - //time zone results in the correct time during and surrounding - //a DST switch. - bool first = true; - auto springSwitch = SysTime(tzInfos[i][1] + dur!"hours"(spring), UTC()) - stdOffset; - auto fallSwitch = SysTime(tzInfos[i][2] + dur!"hours"(fall), UTC()) - dstOffset; - //@@@BUG@@@ 3659 makes this necessary. - auto fallSwitchMinus1 = fallSwitch - dur!"hours"(1); + foreach(hour; -24 .. 25) + { + auto utc = SysTime(tzInfos[i][1] + dur!"hours"(hour), UTC()); + auto local = utc.toLocalTime(); - foreach(hour; -24 .. 25) + void testOffset2(Duration offset, size_t line = __LINE__) { - auto utc = SysTime(tzInfos[i][1] + dur!"hours"(hour), UTC()); - auto local = utc.toLocalTime(); - - void testOffset2(Duration offset, size_t line = __LINE__) + AssertError msg(string tag) { - AssertError msg(string tag) - { - return new AssertError(format("%s [%s] [%s]: [%s] [%s]", tag, hour, tzName, utc, local), - __FILE__, line); - } - - enforce((utc + offset).hour == local.hour, msg("1")); - enforce((utc + offset + dur!"minutes"(1)).hour == local.hour, msg("2")); + return new AssertError(format("%s [%s] [%s]: [%s] [%s]", tag, hour, tzName, utc, local), + __FILE__, line); } - if(utc < springSwitch) - testOffset2(stdOffset); - else - testOffset2(dstOffset); + enforce((utc + offset).hour == local.hour, msg("1")); + enforce((utc + offset + dur!"minutes"(1)).hour == local.hour, msg("2")); + } - utc = SysTime(tzInfos[i][2] + dur!"hours"(hour), UTC()); - local = utc.toLocalTime(); + if(utc < springSwitch) + testOffset2(stdOffset); + else + testOffset2(dstOffset); + + utc = SysTime(tzInfos[i][2] + dur!"hours"(hour), UTC()); + local = utc.toLocalTime(); - if(utc == fallSwitch || utc == fallSwitchMinus1) + if(utc == fallSwitch || utc == fallSwitchMinus1) + { + if(first) { - if(first) - { - testOffset2(dstOffset); - first = false; - } - else - testOffset2(stdOffset); + testOffset2(dstOffset); + first = false; } - else if(utc > fallSwitch) - testOffset2(stdOffset); else - testOffset2(dstOffset); + testOffset2(stdOffset); } + else if(utc > fallSwitch) + testOffset2(stdOffset); + else + testOffset2(dstOffset); } } } @@ -28547,7 +26798,7 @@ public: private: - this() immutable + this() @safe immutable pure { super("", "", ""); } @@ -28561,7 +26812,7 @@ private: // This is done so that we can maintain purity in spite of doing an impure // operation the first time that LocalTime() is called. - static immutable(LocalTime) singleton() + static immutable(LocalTime) singleton() @trusted { if(!_lowLock) { @@ -28592,7 +26843,7 @@ public: /++ $(D UTC) is a singleton class. $(D UTC) returns its only instance. +/ - static immutable(UTC) opCall() pure nothrow + static immutable(UTC) opCall() @safe pure nothrow { return _utc; } @@ -28601,7 +26852,7 @@ public: /++ Always returns false. +/ - @property override bool hasDST() const nothrow + @property override bool hasDST() @safe const nothrow { return false; } @@ -28610,7 +26861,7 @@ public: /++ Always returns false. +/ - override bool dstInEffect(long stdTime) const nothrow + override bool dstInEffect(long stdTime) @safe const nothrow { return false; } @@ -28626,27 +26877,24 @@ public: See_Also: $(D TimeZone.utcToTZ) +/ - override long utcToTZ(long stdTime) const nothrow + override long utcToTZ(long stdTime) @safe const nothrow { return stdTime; } unittest { - version(testStdDateTime) - { - _assertPred!"=="(UTC().utcToTZ(0), 0); + assert(UTC().utcToTZ(0) == 0); - version(Posix) - { - scope(exit) clearTZEnvVar(); + version(Posix) + { + scope(exit) clearTZEnvVar(); - setTZEnvVar("UTC"); - auto std = SysTime(Date(2010, 1, 1)); - auto dst = SysTime(Date(2010, 7, 1)); - _assertPred!"=="(UTC().utcToTZ(std.stdTime), std.stdTime); - _assertPred!"=="(UTC().utcToTZ(dst.stdTime), dst.stdTime); - } + setTZEnvVar("UTC"); + auto std = SysTime(Date(2010, 1, 1)); + auto dst = SysTime(Date(2010, 7, 1)); + assert(UTC().utcToTZ(std.stdTime) == std.stdTime); + assert(UTC().utcToTZ(dst.stdTime) == dst.stdTime); } } @@ -28661,27 +26909,24 @@ public: adjTime = The time in this time zone that needs to be adjusted to UTC time. +/ - override long tzToUTC(long adjTime) const nothrow + override long tzToUTC(long adjTime) @safe const nothrow { return adjTime; } unittest { - version(testStdDateTime) - { - _assertPred!"=="(UTC().tzToUTC(0), 0); + assert(UTC().tzToUTC(0) == 0); - version(Posix) - { - scope(exit) clearTZEnvVar(); + version(Posix) + { + scope(exit) clearTZEnvVar(); - setTZEnvVar("UTC"); - auto std = SysTime(Date(2010, 1, 1)); - auto dst = SysTime(Date(2010, 7, 1)); - _assertPred!"=="(UTC().tzToUTC(std.stdTime), std.stdTime); - _assertPred!"=="(UTC().tzToUTC(dst.stdTime), dst.stdTime); - } + setTZEnvVar("UTC"); + auto std = SysTime(Date(2010, 1, 1)); + auto dst = SysTime(Date(2010, 7, 1)); + assert(UTC().tzToUTC(std.stdTime) == std.stdTime); + assert(UTC().tzToUTC(dst.stdTime) == dst.stdTime); } } @@ -28693,7 +26938,7 @@ public: stdTime = The UTC time for which to get the offset from UTC for this time zone. +/ - override Duration utcOffsetAt(long stdTime) const nothrow + override Duration utcOffsetAt(long stdTime) @safe const nothrow { return dur!"hnsecs"(0); } @@ -28701,7 +26946,7 @@ public: private: - this() immutable pure + this() @safe immutable pure { super("UTC", "UTC", "UTC"); } @@ -28730,7 +26975,7 @@ public: /++ Always returns false. +/ - @property override bool hasDST() const nothrow + @property override bool hasDST() @safe const nothrow { return false; } @@ -28739,7 +26984,7 @@ public: /++ Always returns false. +/ - override bool dstInEffect(long stdTime) const nothrow + override bool dstInEffect(long stdTime) @safe const nothrow { return false; } @@ -28753,12 +26998,12 @@ public: stdTime = The UTC time that needs to be adjusted to this time zone's time. +/ - override long utcToTZ(long stdTime) const nothrow + override long utcToTZ(long stdTime) @safe const nothrow { return stdTime + _utcOffset.total!"hnsecs"; } - version(testStdDateTime) unittest + unittest { auto west = new immutable SimpleTimeZone(dur!"hours"(-8)); auto east = new immutable SimpleTimeZone(dur!"hours"(8)); @@ -28781,12 +27026,12 @@ public: adjTime = The time in this time zone that needs to be adjusted to UTC time. +/ - override long tzToUTC(long adjTime) const nothrow + override long tzToUTC(long adjTime) @safe const nothrow { return adjTime - _utcOffset.total!"hnsecs"; } - version(testStdDateTime) unittest + unittest { auto west = new immutable SimpleTimeZone(dur!"hours"(-8)); auto east = new immutable SimpleTimeZone(dur!"hours"(8)); @@ -28808,7 +27053,7 @@ public: stdTime = The UTC time for which to get the offset from UTC for this time zone. +/ - override Duration utcOffsetAt(long stdTime) const nothrow + override Duration utcOffsetAt(long stdTime) @safe const nothrow { return _utcOffset; } @@ -28816,12 +27061,11 @@ public: /++ Params: - utcOffset = This time zone's offset from UTC in minutes with west of - UTC being negative (it is added to UTC to get the - adjusted time). + utcOffset = This time zone's offset from UTC with west of UTC being + negative (it is added to UTC to get the adjusted time). stdName = The $(D stdName) for this time zone. +/ - this(Duration utcOffset, string stdName = "") immutable + this(Duration utcOffset, string stdName = "") @safe immutable pure { //FIXME This probably needs to be changed to something like (-12 - 13). enforceEx!DateTimeException(abs(utcOffset) < dur!"minutes"(1440), @@ -28831,33 +27075,38 @@ public: this._utcOffset = utcOffset; } - /++ Ditto +/ - this(int utcOffset, string stdName = "") immutable + /++ + $(RED Deprecated. Please use the overload which takes a Duration. This + overload will be removed in December 2014). + + Params: + utcOffset = This time zone's offset from UTC in minutes with west of + negative (it is added to UTC to get the adjusted time). + stdName = The $(D stdName) for this time zone. + +/ + deprecated("Please use the overload which takes a Duration.") + this(int utcOffset, string stdName = "") @safe immutable pure { this(dur!"minutes"(utcOffset), stdName); } - version(testStdDateTime) unittest + unittest { - foreach(stz; [new immutable SimpleTimeZone(dur!"hours"(-8), "PST"), - new immutable SimpleTimeZone(-8 * 60, "PST")]) - - { - assert(stz.name == ""); - assert(stz.stdName == "PST"); - assert(stz.dstName == ""); - assert(stz.utcOffset == -8 * 60); - } + auto stz = new immutable SimpleTimeZone(dur!"hours"(-8), "PST"); + assert(stz.name == ""); + assert(stz.stdName == "PST"); + assert(stz.dstName == ""); + assert(stz.utcOffset == dur!"hours"(-8)); } /++ - The number of minutes the offset from UTC is (negative is west of UTC, + The amount of time the offset from UTC is (negative is west of UTC, positive is east). +/ - @property int utcOffset() @safe const pure nothrow + @property Duration utcOffset() @safe const pure nothrow { - return cast(int)_utcOffset.total!"minutes"; + return _utcOffset; } @@ -28872,19 +27121,18 @@ private: utcOffset = The number of minutes offset from UTC (negative means west). +/ - static string toISOString(Duration utcOffset) + static string toISOString(Duration utcOffset) @safe pure { immutable absOffset = abs(utcOffset); enforceEx!DateTimeException(absOffset < dur!"minutes"(1440), "Offset from UTC must be within range (-24:00 - 24:00)."); - - if(utcOffset < Duration.zero) - return format("-%02d:%02d", absOffset.hours, absOffset.minutes); - - return format("+%02d:%02d", absOffset.hours, absOffset.minutes); + int hours; + int minutes; + absOffset.split!("hours", "minutes")(hours, minutes); + return format(utcOffset < Duration.zero ? "-%02d:%02d" : "+%02d:%02d", hours, minutes); } - version(testStdDateTime) unittest + unittest { static string testSTZInvalid(Duration offset) { @@ -28925,7 +27173,7 @@ private: Params: isoString = A string which represents a time zone in the ISO format. +/ - static immutable(SimpleTimeZone) fromISOString(S)(S isoString) + static immutable(SimpleTimeZone) fromISOString(S)(S isoString) @safe pure if(isSomeString!S) { auto dstr = to!dstring(strip(isoString)); @@ -28951,8 +27199,8 @@ private: else hoursStr = dstr; - enforce(!canFind!(not!isDigit)(hoursStr), new DateTimeException(format("Invalid ISO String: %s", dstr))); - enforce(!canFind!(not!isDigit)(minutesStr), new DateTimeException(format("Invalid ISO String: %s", dstr))); + enforce(all!isDigit(hoursStr), new DateTimeException(format("Invalid ISO String: %s", dstr))); + enforce(all!isDigit(minutesStr), new DateTimeException(format("Invalid ISO String: %s", dstr))); immutable hours = to!int(hoursStr); immutable minutes = minutesStr.empty ? 0 : to!int(minutesStr); @@ -28960,7 +27208,7 @@ private: return new immutable SimpleTimeZone(sign * (dur!"hours"(hours) + dur!"minutes"(minutes))); } - version(testStdDateTime) unittest + unittest { assertThrown!DateTimeException(SimpleTimeZone.fromISOString("")); assertThrown!DateTimeException(SimpleTimeZone.fromISOString("Z")); @@ -29050,15 +27298,15 @@ private: } //Test that converting from an ISO string to a SimpleTimeZone to an ISO String works properly. - version(testStdDateTime) unittest + unittest { static void testSTZ(in string isoString, int expectedOffset, size_t line = __LINE__) { auto stz = SimpleTimeZone.fromISOString(isoString); - _assertPred!"=="(stz.utcOffset, expectedOffset, "", __FILE__, line); + assert(stz.utcOffset == dur!"minutes"(expectedOffset)); - auto result = SimpleTimeZone.toISOString(dur!"minutes"(stz.utcOffset)); - _assertPred!"=="(result, isoString, "", __FILE__, line); + auto result = SimpleTimeZone.toISOString(stz.utcOffset); + assert(result == isoString); } testSTZ("+00:00", 0); @@ -29127,7 +27375,7 @@ public: dates but will still return true for $(D hasDST) because the time zone did at some point have DST. +/ - @property override bool hasDST() const nothrow + @property override bool hasDST() @safe const nothrow { return _hasDST; } @@ -29142,24 +27390,19 @@ public: stdTime = The UTC time that needs to be checked for DST in this time zone. +/ - override bool dstInEffect(long stdTime) const nothrow + override bool dstInEffect(long stdTime) @safe const nothrow { assert(!_transitions.empty); - try - { - immutable unixTime = stdTimeToUnixTime(stdTime); - immutable found = countUntil!"b < a.timeT"(cast(Transition[])_transitions, unixTime); + immutable unixTime = stdTimeToUnixTime(stdTime); + immutable found = countUntil!"b < a.timeT"(_transitions, unixTime); - if(found == -1) - return _transitions.back.ttInfo.isDST; + if(found == -1) + return _transitions.back.ttInfo.isDST; - immutable transition = found == 0 ? _transitions[0] : _transitions[found - 1]; + immutable transition = found == 0 ? _transitions[0] : _transitions[found - 1]; - return transition.ttInfo.isDST; - } - catch(Exception e) - assert(0, format("Unexpected Exception: %s", e)); + return transition.ttInfo.isDST; } @@ -29171,25 +27414,20 @@ public: stdTime = The UTC time that needs to be adjusted to this time zone's time. +/ - override long utcToTZ(long stdTime) const nothrow + override long utcToTZ(long stdTime) @safe const nothrow { assert(!_transitions.empty); - try - { - immutable leapSecs = calculateLeapSeconds(stdTime); - immutable unixTime = stdTimeToUnixTime(stdTime); - immutable found = countUntil!"b < a.timeT"(cast(Transition[])_transitions, unixTime); + immutable leapSecs = calculateLeapSeconds(stdTime); + immutable unixTime = stdTimeToUnixTime(stdTime); + immutable found = countUntil!"b < a.timeT"(_transitions, unixTime); - if(found == -1) - return stdTime + convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); + if(found == -1) + return stdTime + convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); - immutable transition = found == 0 ? _transitions[0] : _transitions[found - 1]; + immutable transition = found == 0 ? _transitions[0] : _transitions[found - 1]; - return stdTime + convert!("seconds", "hnsecs")(transition.ttInfo.utcOffset + leapSecs); - } - catch(Exception e) - assert(0, format("Unexpected Exception: %s", e)); + return stdTime + convert!("seconds", "hnsecs")(transition.ttInfo.utcOffset + leapSecs); } @@ -29201,47 +27439,41 @@ public: adjTime = The time in this time zone that needs to be adjusted to UTC time. +/ - override long tzToUTC(long adjTime) const nothrow + override long tzToUTC(long adjTime) @safe const nothrow { assert(!_transitions.empty); - try - { - immutable leapSecs = calculateLeapSeconds(adjTime); - time_t unixTime = stdTimeToUnixTime(adjTime); - immutable past = unixTime - convert!("days", "seconds")(1); - immutable future = unixTime + convert!("days", "seconds")(1); + immutable leapSecs = calculateLeapSeconds(adjTime); + time_t unixTime = stdTimeToUnixTime(adjTime); + immutable past = unixTime - convert!("days", "seconds")(1); + immutable future = unixTime + convert!("days", "seconds")(1); - immutable pastFound = countUntil!"b < a.timeT"(cast(Transition[])_transitions, past); + immutable pastFound = countUntil!"b < a.timeT"(_transitions, past); - if(pastFound == -1) - return adjTime - convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); + if(pastFound == -1) + return adjTime - convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); - immutable futureFound = countUntil!"b < a.timeT"(cast(Transition[])_transitions[pastFound .. $], future); - immutable pastTrans = pastFound == 0 ? _transitions[0] : _transitions[pastFound - 1]; + immutable futureFound = countUntil!"b < a.timeT"(_transitions[pastFound .. $], future); + immutable pastTrans = pastFound == 0 ? _transitions[0] : _transitions[pastFound - 1]; - if(futureFound == 0) - return adjTime - convert!("seconds", "hnsecs")(pastTrans.ttInfo.utcOffset + leapSecs); + if(futureFound == 0) + return adjTime - convert!("seconds", "hnsecs")(pastTrans.ttInfo.utcOffset + leapSecs); - immutable futureTrans = futureFound == -1 ? _transitions.back - : _transitions[pastFound + futureFound - 1]; - immutable pastOffset = pastTrans.ttInfo.utcOffset; + immutable futureTrans = futureFound == -1 ? _transitions.back + : _transitions[pastFound + futureFound - 1]; + immutable pastOffset = pastTrans.ttInfo.utcOffset; - if(pastOffset < futureTrans.ttInfo.utcOffset) - unixTime -= convert!("hours", "seconds")(1); + if(pastOffset < futureTrans.ttInfo.utcOffset) + unixTime -= convert!("hours", "seconds")(1); - immutable found = countUntil!"b < a.timeT"(cast(Transition[])_transitions[pastFound .. $], - unixTime - pastOffset); + immutable found = countUntil!"b < a.timeT"(_transitions[pastFound .. $], unixTime - pastOffset); - if(found == -1) - return adjTime - convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); + if(found == -1) + return adjTime - convert!("seconds", "hnsecs")(_transitions.back.ttInfo.utcOffset + leapSecs); - immutable transition = found == 0 ? pastTrans : _transitions[pastFound + found - 1]; + immutable transition = found == 0 ? pastTrans : _transitions[pastFound + found - 1]; - return adjTime - convert!("seconds", "hnsecs")(transition.ttInfo.utcOffset + leapSecs); - } - catch(Exception e) - assert(0, format("Unexpected Exception: %s", e)); + return adjTime - convert!("seconds", "hnsecs")(transition.ttInfo.utcOffset + leapSecs); } @@ -29296,23 +27528,20 @@ assert(tz.dstName == "PDT"); +/ //TODO make it possible for tzDatabaseDir to be gzipped tar file rather than an uncompressed // directory. - static immutable(PosixTimeZone) getTimeZone(string name, string tzDatabaseDir = defaultTZDatabaseDir) + static immutable(PosixTimeZone) getTimeZone(string name, string tzDatabaseDir = defaultTZDatabaseDir) @trusted { name = strip(name); enforce(tzDatabaseDir.exists, new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir))); enforce(tzDatabaseDir.isDir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir))); - version(Posix) - auto file = tzDatabaseDir ~ name; - else version(Windows) - auto file = tzDatabaseDir ~ replace(strip(name), "/", dirSeparator); + immutable file = buildNormalizedPath(tzDatabaseDir, name); enforce(file.exists, new DateTimeException(format("File %s does not exist.", file))); enforce(file.isFile, new DateTimeException(format("%s is not a file.", file))); auto tzFile = File(file); - immutable gmtZone = file.canFind("GMT"); + immutable gmtZone = file.representation.canFind("GMT"); try { @@ -29613,16 +27842,13 @@ assert(tz.dstName == "PDT"); Throws: $(D FileException) if it fails to read from disk. +/ - static string[] getInstalledTZNames(string subName = "", string tzDatabaseDir = defaultTZDatabaseDir) + static string[] getInstalledTZNames(string subName = "", string tzDatabaseDir = defaultTZDatabaseDir) @trusted { version(Posix) subName = strip(subName); else version(Windows) subName = replace(strip(subName), "/", dirSeparator); - if(!tzDatabaseDir.endsWith(dirSeparator)) - tzDatabaseDir ~= dirSeparator; - enforce(tzDatabaseDir.exists, new DateTimeException(format("Directory %s does not exist.", tzDatabaseDir))); enforce(tzDatabaseDir.isDir, new DateTimeException(format("%s is not a directory.", tzDatabaseDir))); @@ -29650,41 +27876,35 @@ assert(tz.dstName == "PDT"); return timezones.data; } - unittest + version(Posix) unittest { - version(testStdDateTime) + static void testPTZSuccess(string tzName) { - version(Posix) - { - static void testPTZSuccess(string tzName) - { - scope(failure) writefln("TZName which threw: %s", tzName); + scope(failure) writefln("TZName which threw: %s", tzName); - PosixTimeZone.getTimeZone(tzName); - } + PosixTimeZone.getTimeZone(tzName); + } - static void testPTZFailure(string tzName) - { - scope(success) writefln("TZName which was supposed to throw: %s", tzName); + static void testPTZFailure(string tzName) + { + scope(success) writefln("TZName which was supposed to throw: %s", tzName); - PosixTimeZone.getTimeZone(tzName); - } + PosixTimeZone.getTimeZone(tzName); + } - auto tzNames = getInstalledTZNames(); + auto tzNames = getInstalledTZNames(); - foreach(tzName; tzNames) - assertNotThrown!DateTimeException(testPTZSuccess(tzName)); + foreach(tzName; tzNames) + assertNotThrown!DateTimeException(testPTZSuccess(tzName)); - foreach(DirEntry dentry; dirEntries(defaultTZDatabaseDir, SpanMode.depth)) - { - if(dentry.isFile) - { - auto tzName = dentry.name[defaultTZDatabaseDir.length .. $]; + foreach(DirEntry dentry; dirEntries(defaultTZDatabaseDir, SpanMode.depth)) + { + if(dentry.isFile) + { + auto tzName = dentry.name[defaultTZDatabaseDir.length .. $]; - if(!canFind(tzNames, tzName)) - assertThrown!DateTimeException(testPTZFailure(tzName)); - } - } + if(!canFind(tzNames, tzName)) + assertThrown!DateTimeException(testPTZFailure(tzName)); } } } @@ -29699,7 +27919,7 @@ private: +/ struct Transition { - this(long timeT, immutable (TTInfo)* ttInfo) + this(long timeT, immutable (TTInfo)* ttInfo) @safe pure { this.timeT = timeT; this.ttInfo = ttInfo; @@ -29715,7 +27935,7 @@ private: +/ struct LeapSecond { - this(long timeT, int total) + this(long timeT, int total) @safe pure { this.timeT = timeT; this.total = total; @@ -29731,7 +27951,7 @@ private: +/ struct TTInfo { - this(in TempTTInfo tempTTInfo, string abbrev) immutable + this(in TempTTInfo tempTTInfo, string abbrev) @safe immutable pure { utcOffset = tempTTInfo.tt_gmtoff; isDST = tempTTInfo.tt_isdst; @@ -29750,7 +27970,7 @@ private: +/ struct TempTTInfo { - this(int gmtOff, bool isDST, ubyte abbrInd) + this(int gmtOff, bool isDST, ubyte abbrInd) @safe pure { tt_gmtoff = gmtOff; tt_isdst = isDST; @@ -29770,7 +27990,7 @@ private: +/ struct TempTransition { - this(long timeT, immutable (TTInfo)* ttInfo, TransitionType* ttype) + this(long timeT, immutable (TTInfo)* ttInfo, TransitionType* ttype) @safe pure { this.timeT = timeT; this.ttInfo = ttInfo; @@ -29790,7 +28010,7 @@ private: +/ struct TransitionType { - this(bool isStd, bool inUTC) + this(bool isStd, bool inUTC) @safe pure { this.isStd = isStd; this.inUTC = inUTC; @@ -29807,7 +28027,7 @@ private: /+ Reads an int from a TZ file. +/ - static T readVal(T)(ref File tzFile) + static T readVal(T)(ref File tzFile) @trusted if((isIntegral!T || isSomeChar!T) || is(Unqual!T == bool)) { import std.bitmanip; @@ -29822,7 +28042,7 @@ private: /+ Reads an array of values from a TZ file. +/ - static T readVal(T)(ref File tzFile, size_t length) + static T readVal(T)(ref File tzFile, size_t length) @trusted if(isArray!T) { auto buff = new T(length); @@ -29837,7 +28057,7 @@ private: /+ Reads a $(D TempTTInfo) from a TZ file. +/ - static T readVal(T)(ref File tzFile) + static T readVal(T)(ref File tzFile) @safe if(is(T == TempTTInfo)) { return TempTTInfo(readVal!int(tzFile), @@ -29850,36 +28070,31 @@ private: Throws: $(LREF DateTimeException) if $(D result) is false. +/ - static void _enforceValidTZFile(bool result, size_t line = __LINE__) + static void _enforceValidTZFile(bool result, size_t line = __LINE__) @safe pure { if(!result) throw new DateTimeException("Not a valid tzdata file.", __FILE__, line); } - int calculateLeapSeconds(long stdTime) const nothrow + int calculateLeapSeconds(long stdTime) @safe const pure nothrow { - try - { - if(_leapSeconds.empty) - return 0; + if(_leapSeconds.empty) + return 0; - immutable unixTime = stdTimeToUnixTime(stdTime); + immutable unixTime = stdTimeToUnixTime(stdTime); - if(_leapSeconds.front.timeT >= unixTime) - return 0; + if(_leapSeconds.front.timeT >= unixTime) + return 0; - immutable found = countUntil!"b < a.timeT"(cast(LeapSecond[])_leapSeconds, unixTime); + immutable found = countUntil!"b < a.timeT"(_leapSeconds, unixTime); - if(found == -1) - return _leapSeconds.back.total; + if(found == -1) + return _leapSeconds.back.total; - immutable leapSecond = found == 0 ? _leapSeconds[0] : _leapSeconds[found - 1]; + immutable leapSecond = found == 0 ? _leapSeconds[0] : _leapSeconds[found - 1]; - return leapSecond.total; - } - catch(Exception e) - assert(0, format("Nothing in calculateLeapSeconds() should be throwing. Caught Exception: %s", e)); + return leapSecond.total; } @@ -29888,7 +28103,7 @@ private: string name, string stdName, string dstName, - bool hasDST) immutable + bool hasDST) @safe immutable pure { if(dstName.empty && !stdName.empty) dstName = stdName; @@ -29964,7 +28179,7 @@ version(StdDdoc) current dates but will still return true for $(D hasDST) because the time zone did at some point have DST. +/ - @property override bool hasDST() const nothrow; + @property override bool hasDST() @safe const nothrow; /++ @@ -29976,7 +28191,7 @@ version(StdDdoc) stdTime = The UTC time that needs to be checked for DST in this time zone. +/ - override bool dstInEffect(long stdTime) const nothrow; + override bool dstInEffect(long stdTime) @safe const nothrow; /++ @@ -29988,7 +28203,7 @@ version(StdDdoc) stdTime = The UTC time that needs to be adjusted to this time zone's time. +/ - override long utcToTZ(long stdTime) const nothrow; + override long utcToTZ(long stdTime) @safe const nothrow; /++ @@ -30000,7 +28215,7 @@ version(StdDdoc) adjTime = The time in this time zone that needs to be adjusted to UTC time. +/ - override long tzToUTC(long adjTime) const nothrow; + override long tzToUTC(long adjTime) @safe const nothrow; /++ @@ -30026,14 +28241,17 @@ version(StdDdoc) auto tz = TimeZone.getTimeZone("America/Los_Angeles"); -------------------- +/ - static immutable(WindowsTimeZone) getTimeZone(string name); + static immutable(WindowsTimeZone) getTimeZone(string name) @safe; /++ Returns a list of the names of the time zones installed on the - system. + system. The list returned by WindowsTimeZone contains the Windows + TZ names, not the TZ Database names. However, + $(D TimeZone.getinstalledTZNames) will return the TZ Database names + which are equivalent to the Windows TZ names. +/ - static string[] getInstalledTZNames(); + static string[] getInstalledTZNames() @safe; private: @@ -30058,31 +28276,31 @@ else version(Windows) { public: - @property override bool hasDST() const nothrow + @property override bool hasDST() @safe const nothrow { return _tzInfo.DaylightDate.wMonth != 0; } - override bool dstInEffect(long stdTime) const nothrow + override bool dstInEffect(long stdTime) @safe const nothrow { return _dstInEffect(&_tzInfo, stdTime); } - override long utcToTZ(long stdTime) const nothrow + override long utcToTZ(long stdTime) @safe const nothrow { return _utcToTZ(&_tzInfo, stdTime, hasDST); } - override long tzToUTC(long adjTime) const nothrow + override long tzToUTC(long adjTime) @safe const nothrow { return _tzToUTC(&_tzInfo, adjTime, hasDST); } - static immutable(WindowsTimeZone) getTimeZone(string name) + static immutable(WindowsTimeZone) getTimeZone(string name) @trusted { scope baseKey = Registry.localMachine.getKey(`Software\Microsoft\Windows NT\CurrentVersion\Time Zones`); @@ -30126,7 +28344,7 @@ else version(Windows) throw new DateTimeException(format("Failed to find time zone: %s", name)); } - static string[] getInstalledTZNames() + static string[] getInstalledTZNames() @trusted { auto timezones = appender!(string[])(); @@ -30143,26 +28361,23 @@ else version(Windows) unittest { - version(testStdDateTime) + static void testWTZSuccess(string tzName) { - static void testWTZSuccess(string tzName) - { - scope(failure) writefln("TZName which threw: %s", tzName); + scope(failure) writefln("TZName which threw: %s", tzName); - WindowsTimeZone.getTimeZone(tzName); - } + WindowsTimeZone.getTimeZone(tzName); + } - auto tzNames = getInstalledTZNames(); + auto tzNames = getInstalledTZNames(); - foreach(tzName; tzNames) - assertNotThrown!DateTimeException(testWTZSuccess(tzName)); - } + foreach(tzName; tzNames) + assertNotThrown!DateTimeException(testWTZSuccess(tzName)); } private: - static bool _dstInEffect(const TIME_ZONE_INFORMATION* tzInfo, long stdTime) nothrow + static bool _dstInEffect(const TIME_ZONE_INFORMATION* tzInfo, long stdTime) @trusted nothrow { try { @@ -30235,7 +28450,7 @@ else version(Windows) assert(0, "DateTime's constructor threw."); } - version(testStdDateTime) unittest + unittest { TIME_ZONE_INFORMATION tzInfo; GetTimeZoneInformation(&tzInfo); @@ -30245,7 +28460,7 @@ else version(Windows) } - static long _utcToTZ(const TIME_ZONE_INFORMATION* tzInfo, long stdTime, bool hasDST) nothrow + static long _utcToTZ(const TIME_ZONE_INFORMATION* tzInfo, long stdTime, bool hasDST) @safe nothrow { if(hasDST && WindowsTimeZone._dstInEffect(tzInfo, stdTime)) return stdTime - convert!("minutes", "hnsecs")(tzInfo.Bias + tzInfo.DaylightBias); @@ -30254,7 +28469,7 @@ else version(Windows) } - static long _tzToUTC(const TIME_ZONE_INFORMATION* tzInfo, long adjTime, bool hasDST) nothrow + static long _tzToUTC(const TIME_ZONE_INFORMATION* tzInfo, long adjTime, bool hasDST) @trusted nothrow { if(hasDST) { @@ -30356,28 +28571,9 @@ else version(Windows) } - this(string name, TIME_ZONE_INFORMATION tzInfo) immutable + this(string name, TIME_ZONE_INFORMATION tzInfo) @safe immutable pure { - //Cannot use to!string(wchar*), probably due to bug http://d.puremagic.com/issues/show_bug.cgi?id=5016 - static string conv(wchar* wcstr) - { - wcstr[31] = '\0'; - wstring retval; - - for(;; ++wcstr) - { - if(*wcstr == '\0') - break; - - retval ~= *wcstr; - } - - return to!string(retval); - } - - //super(name, to!string(tzInfo.StandardName.ptr), to!string(tzInfo.DaylightName.ptr)); - super(name, conv(tzInfo.StandardName.ptr), conv(tzInfo.DaylightName.ptr)); - + super(name, to!string(tzInfo.StandardName.ptr), to!string(tzInfo.DaylightName.ptr)); _tzInfo = tzInfo; } @@ -30398,7 +28594,7 @@ version(StdDdoc) Unfortunately, there is no way to do it on Windows using the TZ Database name, so this function only exists on Posix systems. +/ - void setTZEnvVar(string tzDatabaseName); + void setTZEnvVar(string tzDatabaseName) @safe nothrow; /++ @@ -30406,17 +28602,16 @@ version(StdDdoc) Clears the TZ environment variable. +/ - void clearTZEnvVar(); + void clearTZEnvVar() @safe nothrow; } else version(Posix) { - void setTZEnvVar(string tzDatabaseName) nothrow + void setTZEnvVar(string tzDatabaseName) @trusted nothrow { try { - auto value = PosixTimeZone.defaultTZDatabaseDir ~ tzDatabaseName ~ "\0"; - - setenv("TZ", value.ptr, 1); + immutable value = buildNormalizedPath(PosixTimeZone.defaultTZDatabaseDir, tzDatabaseName).toStringz(); + setenv("TZ", value, 1); tzset(); } catch(Exception e) @@ -30424,7 +28619,7 @@ else version(Posix) } - void clearTZEnvVar() nothrow + void clearTZEnvVar() @trusted nothrow { try { @@ -30450,149 +28645,457 @@ else version(Posix) of the more exotic TZ Database names don't have corresponding Windows time zone names. + Returns null if the given time zone name cannot be converted. + See_Also: $(WEB unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html, Windows <-> TZ Database Name Conversion Table) Params: tzName = The TZ Database name to convert. - - Throws: - $(LREF DateTimeException) if the given $(D_PARAM tzName) cannot be - converted. +/ -string tzDatabaseNameToWindowsTZName(string tzName) +string tzDatabaseNameToWindowsTZName(string tzName) @safe pure nothrow @nogc { switch(tzName) { - //Most of these come from the link in the documentation, but a few have - //been added because they were found in the Windows registry. + case "Africa/Abidjan": return "Greenwich Standard Time"; + case "Africa/Accra": return "Greenwich Standard Time"; + case "Africa/Addis_Ababa": return "E. Africa Standard Time"; + case "Africa/Algiers": return "W. Central Africa Standard Time"; + case "Africa/Asmera": return "E. Africa Standard Time"; + case "Africa/Bamako": return "Greenwich Standard Time"; + case "Africa/Bangui": return "W. Central Africa Standard Time"; + case "Africa/Banjul": return "Greenwich Standard Time"; + case "Africa/Bissau": return "Greenwich Standard Time"; + case "Africa/Blantyre": return "South Africa Standard Time"; + case "Africa/Bogota": return "Line Islands Standard Time"; + case "Africa/Brazzaville": return "W. Central Africa Standard Time"; + case "Africa/Bujumbura": return "South Africa Standard Time"; case "Africa/Cairo": return "Egypt Standard Time"; case "Africa/Casablanca": return "Morocco Standard Time"; + case "Africa/Ceuta": return "Romance Standard Time"; + case "Africa/Conakry": return "Greenwich Standard Time"; + case "Africa/Dakar": return "Greenwich Standard Time"; + case "Africa/Dar_es_Salaam": return "E. Africa Standard Time"; + case "Africa/Djibouti": return "E. Africa Standard Time"; + case "Africa/Douala": return "W. Central Africa Standard Time"; + case "Africa/El_Aaiun": return "Morocco Standard Time"; + case "Africa/Freetown": return "Greenwich Standard Time"; + case "Africa/Gaborone": return "South Africa Standard Time"; + case "Africa/Harare": return "South Africa Standard Time"; case "Africa/Johannesburg": return "South Africa Standard Time"; + case "Africa/Juba": return "E. Africa Standard Time"; + case "Africa/Kampala": return "E. Africa Standard Time"; + case "Africa/Khartoum": return "E. Africa Standard Time"; + case "Africa/Kigali": return "South Africa Standard Time"; + case "Africa/Kinshasa": return "W. Central Africa Standard Time"; case "Africa/Lagos": return "W. Central Africa Standard Time"; + case "Africa/Libreville": return "W. Central Africa Standard Time"; + case "Africa/Lome": return "Greenwich Standard Time"; + case "Africa/Luanda": return "W. Central Africa Standard Time"; + case "Africa/Lubumbashi": return "South Africa Standard Time"; + case "Africa/Lusaka": return "South Africa Standard Time"; + case "Africa/Malabo": return "W. Central Africa Standard Time"; + case "Africa/Maputo": return "South Africa Standard Time"; + case "Africa/Maseru": return "South Africa Standard Time"; + case "Africa/Mbabane": return "South Africa Standard Time"; + case "Africa/Mogadishu": return "E. Africa Standard Time"; + case "Africa/Monrovia": return "Greenwich Standard Time"; case "Africa/Nairobi": return "E. Africa Standard Time"; + case "Africa/Ndjamena": return "W. Central Africa Standard Time"; + case "Africa/Niamey": return "W. Central Africa Standard Time"; + case "Africa/Nouakchott": return "Greenwich Standard Time"; + case "Africa/Ouagadougou": return "Greenwich Standard Time"; + case "Africa/Porto-Novo": return "W. Central Africa Standard Time"; + case "Africa/Sao_Tome": return "Greenwich Standard Time"; case "Africa/Tripoli": return "Libya Standard Time"; + case "Africa/Tunis": return "W. Central Africa Standard Time"; case "Africa/Windhoek": return "Namibia Standard Time"; case "America/Anchorage": return "Alaskan Standard Time"; + case "America/Anguilla": return "SA Western Standard Time"; + case "America/Antigua": return "SA Western Standard Time"; + case "America/Araguaina": return "SA Eastern Standard Time"; + case "America/Argentina/La_Rioja": return "Argentina Standard Time"; + case "America/Argentina/Rio_Gallegos": return "Argentina Standard Time"; + case "America/Argentina/Salta": return "Argentina Standard Time"; + case "America/Argentina/San_Juan": return "Argentina Standard Time"; + case "America/Argentina/San_Luis": return "Argentina Standard Time"; + case "America/Argentina/Tucuman": return "Argentina Standard Time"; + case "America/Argentina/Ushuaia": return "Argentina Standard Time"; + case "America/Aruba": return "SA Western Standard Time"; case "America/Asuncion": return "Paraguay Standard Time"; case "America/Bahia": return "Bahia Standard Time"; + case "America/Bahia_Banderas": return "Central Standard Time (Mexico)"; + case "America/Barbados": return "SA Western Standard Time"; + case "America/Belem": return "SA Eastern Standard Time"; + case "America/Belize": return "Central America Standard Time"; + case "America/Blanc-Sablon": return "SA Western Standard Time"; + case "America/Boa_Vista": return "SA Western Standard Time"; case "America/Bogota": return "SA Pacific Standard Time"; + case "America/Boise": return "Mountain Standard Time"; case "America/Buenos_Aires": return "Argentina Standard Time"; + case "America/Cambridge_Bay": return "Mountain Standard Time"; + case "America/Campo_Grande": return "Central Brazilian Standard Time"; + case "America/Cancun": return "Central Standard Time (Mexico)"; case "America/Caracas": return "Venezuela Standard Time"; + case "America/Catamarca": return "Argentina Standard Time"; case "America/Cayenne": return "SA Eastern Standard Time"; + case "America/Cayman": return "SA Pacific Standard Time"; case "America/Chicago": return "Central Standard Time"; case "America/Chihuahua": return "Mountain Standard Time (Mexico)"; + case "America/Coral_Harbour": return "SA Pacific Standard Time"; + case "America/Cordoba": return "Argentina Standard Time"; + case "America/Costa_Rica": return "Central America Standard Time"; + case "America/Creston": return "US Mountain Standard Time"; case "America/Cuiaba": return "Central Brazilian Standard Time"; + case "America/Curacao": return "SA Western Standard Time"; + case "America/Danmarkshavn": return "UTC"; + case "America/Dawson": return "Pacific Standard Time"; + case "America/Dawson_Creek": return "US Mountain Standard Time"; case "America/Denver": return "Mountain Standard Time"; + case "America/Detroit": return "Eastern Standard Time"; + case "America/Dominica": return "SA Western Standard Time"; + case "America/Edmonton": return "Mountain Standard Time"; + case "America/Eirunepe": return "SA Pacific Standard Time"; + case "America/El_Salvador": return "Central America Standard Time"; + case "America/Fortaleza": return "SA Eastern Standard Time"; + case "America/Glace_Bay": return "Atlantic Standard Time"; case "America/Godthab": return "Greenland Standard Time"; + case "America/Goose_Bay": return "Atlantic Standard Time"; + case "America/Grand_Turk": return "Eastern Standard Time"; + case "America/Grenada": return "SA Western Standard Time"; + case "America/Guadeloupe": return "SA Western Standard Time"; case "America/Guatemala": return "Central America Standard Time"; + case "America/Guayaquil": return "SA Pacific Standard Time"; + case "America/Guyana": return "SA Western Standard Time"; case "America/Halifax": return "Atlantic Standard Time"; + case "America/Havana": return "Eastern Standard Time"; + case "America/Hermosillo": return "US Mountain Standard Time"; + case "America/Indiana/Knox": return "Central Standard Time"; + case "America/Indiana/Marengo": return "US Eastern Standard Time"; + case "America/Indiana/Petersburg": return "Eastern Standard Time"; + case "America/Indiana/Tell_City": return "Central Standard Time"; + case "America/Indiana/Vevay": return "US Eastern Standard Time"; + case "America/Indiana/Vincennes": return "Eastern Standard Time"; + case "America/Indiana/Winamac": return "Eastern Standard Time"; + case "America/Indianapolis": return "US Eastern Standard Time"; + case "America/Inuvik": return "Mountain Standard Time"; + case "America/Iqaluit": return "Eastern Standard Time"; + case "America/Jamaica": return "SA Pacific Standard Time"; + case "America/Jujuy": return "Argentina Standard Time"; + case "America/Juneau": return "Alaskan Standard Time"; + case "America/Kentucky/Monticello": return "Eastern Standard Time"; + case "America/Kralendijk": return "SA Western Standard Time"; case "America/La_Paz": return "SA Western Standard Time"; + case "America/Lima": return "SA Pacific Standard Time"; case "America/Los_Angeles": return "Pacific Standard Time"; + case "America/Louisville": return "Eastern Standard Time"; + case "America/Lower_Princes": return "SA Western Standard Time"; + case "America/Maceio": return "SA Eastern Standard Time"; + case "America/Managua": return "Central America Standard Time"; + case "America/Manaus": return "SA Western Standard Time"; + case "America/Marigot": return "SA Western Standard Time"; + case "America/Martinique": return "SA Western Standard Time"; + case "America/Matamoros": return "Central Standard Time"; + case "America/Mazatlan": return "Mountain Standard Time (Mexico)"; + case "America/Mendoza": return "Argentina Standard Time"; + case "America/Menominee": return "Central Standard Time"; + case "America/Merida": return "Central Standard Time (Mexico)"; case "America/Mexico_City": return "Central Standard Time (Mexico)"; + case "America/Moncton": return "Atlantic Standard Time"; + case "America/Monterrey": return "Central Standard Time (Mexico)"; case "America/Montevideo": return "Montevideo Standard Time"; + case "America/Montreal": return "Eastern Standard Time"; + case "America/Montserrat": return "SA Western Standard Time"; + case "America/Nassau": return "Eastern Standard Time"; case "America/New_York": return "Eastern Standard Time"; + case "America/Nipigon": return "Eastern Standard Time"; + case "America/Nome": return "Alaskan Standard Time"; + case "America/Noronha": return "UTC-02"; + case "America/North_Dakota/Beulah": return "Central Standard Time"; + case "America/North_Dakota/Center": return "Central Standard Time"; + case "America/North_Dakota/New_Salem": return "Central Standard Time"; + case "America/Ojinaga": return "Mountain Standard Time"; + case "America/Panama": return "SA Pacific Standard Time"; + case "America/Pangnirtung": return "Eastern Standard Time"; + case "America/Paramaribo": return "SA Eastern Standard Time"; case "America/Phoenix": return "US Mountain Standard Time"; + case "America/Port-au-Prince": return "Eastern Standard Time"; + case "America/Port_of_Spain": return "SA Western Standard Time"; + case "America/Porto_Velho": return "SA Western Standard Time"; + case "America/Puerto_Rico": return "SA Western Standard Time"; + case "America/Rainy_River": return "Central Standard Time"; + case "America/Rankin_Inlet": return "Central Standard Time"; + case "America/Recife": return "SA Eastern Standard Time"; case "America/Regina": return "Canada Central Standard Time"; + case "America/Resolute": return "Central Standard Time"; + case "America/Rio_Branco": return "SA Pacific Standard Time"; case "America/Santa_Isabel": return "Pacific Standard Time (Mexico)"; + case "America/Santarem": return "SA Eastern Standard Time"; case "America/Santiago": return "Pacific SA Standard Time"; + case "America/Santo_Domingo": return "SA Western Standard Time"; case "America/Sao_Paulo": return "E. South America Standard Time"; + case "America/Scoresbysund": return "Azores Standard Time"; + case "America/Sitka": return "Alaskan Standard Time"; + case "America/St_Barthelemy": return "SA Western Standard Time"; case "America/St_Johns": return "Newfoundland Standard Time"; + case "America/St_Kitts": return "SA Western Standard Time"; + case "America/St_Lucia": return "SA Western Standard Time"; + case "America/St_Thomas": return "SA Western Standard Time"; + case "America/St_Vincent": return "SA Western Standard Time"; + case "America/Swift_Current": return "Canada Central Standard Time"; + case "America/Tegucigalpa": return "Central America Standard Time"; + case "America/Thule": return "Atlantic Standard Time"; + case "America/Thunder_Bay": return "Eastern Standard Time"; + case "America/Tijuana": return "Pacific Standard Time"; + case "America/Toronto": return "Eastern Standard Time"; + case "America/Tortola": return "SA Western Standard Time"; + case "America/Vancouver": return "Pacific Standard Time"; + case "America/Whitehorse": return "Pacific Standard Time"; + case "America/Winnipeg": return "Central Standard Time"; + case "America/Yakutat": return "Alaskan Standard Time"; + case "America/Yellowknife": return "Mountain Standard Time"; + case "Antarctica/Casey": return "W. Australia Standard Time"; + case "Antarctica/Davis": return "SE Asia Standard Time"; + case "Antarctica/DumontDUrville": return "West Pacific Standard Time"; + case "Antarctica/Macquarie": return "Central Pacific Standard Time"; + case "Antarctica/Mawson": return "West Asia Standard Time"; + case "Antarctica/McMurdo": return "New Zealand Standard Time"; + case "Antarctica/Palmer": return "Pacific SA Standard Time"; + case "Antarctica/Rothera": return "SA Eastern Standard Time"; + case "Antarctica/Syowa": return "E. Africa Standard Time"; + case "Antarctica/Vostok": return "Central Asia Standard Time"; + case "Arctic/Longyearbyen": return "W. Europe Standard Time"; + case "Asia/Aden": return "Arab Standard Time"; case "Asia/Almaty": return "Central Asia Standard Time"; case "Asia/Amman": return "Jordan Standard Time"; + case "Asia/Anadyr": return "Magadan Standard Time"; + case "Asia/Aqtau": return "West Asia Standard Time"; + case "Asia/Aqtobe": return "West Asia Standard Time"; + case "Asia/Ashgabat": return "West Asia Standard Time"; case "Asia/Baghdad": return "Arabic Standard Time"; + case "Asia/Bahrain": return "Arab Standard Time"; case "Asia/Baku": return "Azerbaijan Standard Time"; case "Asia/Bangkok": return "SE Asia Standard Time"; case "Asia/Beirut": return "Middle East Standard Time"; + case "Asia/Bishkek": return "Central Asia Standard Time"; + case "Asia/Brunei": return "Singapore Standard Time"; case "Asia/Calcutta": return "India Standard Time"; + case "Asia/Chita": return "Yakutsk Standard Time"; + case "Asia/Choibalsan": return "Ulaanbaatar Standard Time"; case "Asia/Colombo": return "Sri Lanka Standard Time"; case "Asia/Damascus": return "Syria Standard Time"; case "Asia/Dhaka": return "Bangladesh Standard Time"; + case "Asia/Dili": return "Tokyo Standard Time"; case "Asia/Dubai": return "Arabian Standard Time"; + case "Asia/Dushanbe": return "West Asia Standard Time"; + case "Asia/Hong_Kong": return "China Standard Time"; + case "Asia/Hovd": return "SE Asia Standard Time"; case "Asia/Irkutsk": return "North Asia East Standard Time"; + case "Asia/Jakarta": return "SE Asia Standard Time"; + case "Asia/Jayapura": return "Tokyo Standard Time"; case "Asia/Jerusalem": return "Israel Standard Time"; case "Asia/Kabul": return "Afghanistan Standard Time"; - case "Asia/Kamchatka": return "Kamchatka Standard Time"; + case "Asia/Kamchatka": return "Magadan Standard Time"; case "Asia/Karachi": return "Pakistan Standard Time"; case "Asia/Katmandu": return "Nepal Standard Time"; + case "Asia/Khandyga": return "Yakutsk Standard Time"; case "Asia/Krasnoyarsk": return "North Asia Standard Time"; + case "Asia/Kuala_Lumpur": return "Singapore Standard Time"; + case "Asia/Kuching": return "Singapore Standard Time"; + case "Asia/Kuwait": return "Arab Standard Time"; + case "Asia/Macau": return "China Standard Time"; case "Asia/Magadan": return "Magadan Standard Time"; + case "Asia/Makassar": return "Singapore Standard Time"; + case "Asia/Manila": return "Singapore Standard Time"; + case "Asia/Muscat": return "Arabian Standard Time"; + case "Asia/Nicosia": return "GTB Standard Time"; + case "Asia/Novokuznetsk": return "N. Central Asia Standard Time"; case "Asia/Novosibirsk": return "N. Central Asia Standard Time"; + case "Asia/Omsk": return "N. Central Asia Standard Time"; + case "Asia/Oral": return "West Asia Standard Time"; + case "Asia/Phnom_Penh": return "SE Asia Standard Time"; + case "Asia/Pontianak": return "SE Asia Standard Time"; + case "Asia/Pyongyang": return "Korea Standard Time"; + case "Asia/Qatar": return "Arab Standard Time"; + case "Asia/Qyzylorda": return "Central Asia Standard Time"; case "Asia/Rangoon": return "Myanmar Standard Time"; case "Asia/Riyadh": return "Arab Standard Time"; + case "Asia/Saigon": return "SE Asia Standard Time"; + case "Asia/Sakhalin": return "Vladivostok Standard Time"; + case "Asia/Samarkand": return "West Asia Standard Time"; case "Asia/Seoul": return "Korea Standard Time"; case "Asia/Shanghai": return "China Standard Time"; case "Asia/Singapore": return "Singapore Standard Time"; + case "Asia/Srednekolymsk": return "Magadan Standard Time"; case "Asia/Taipei": return "Taipei Standard Time"; case "Asia/Tashkent": return "West Asia Standard Time"; case "Asia/Tbilisi": return "Georgian Standard Time"; case "Asia/Tehran": return "Iran Standard Time"; + case "Asia/Thimphu": return "Bangladesh Standard Time"; case "Asia/Tokyo": return "Tokyo Standard Time"; case "Asia/Ulaanbaatar": return "Ulaanbaatar Standard Time"; + case "Asia/Urumqi": return "Central Asia Standard Time"; + case "Asia/Ust-Nera": return "Vladivostok Standard Time"; + case "Asia/Vientiane": return "SE Asia Standard Time"; case "Asia/Vladivostok": return "Vladivostok Standard Time"; case "Asia/Yakutsk": return "Yakutsk Standard Time"; case "Asia/Yekaterinburg": return "Ekaterinburg Standard Time"; case "Asia/Yerevan": return "Caucasus Standard Time"; case "Atlantic/Azores": return "Azores Standard Time"; + case "Atlantic/Bermuda": return "Atlantic Standard Time"; + case "Atlantic/Canary": return "GMT Standard Time"; case "Atlantic/Cape_Verde": return "Cape Verde Standard Time"; + case "Atlantic/Faeroe": return "GMT Standard Time"; + case "Atlantic/Madeira": return "GMT Standard Time"; case "Atlantic/Reykjavik": return "Greenwich Standard Time"; + case "Atlantic/South_Georgia": return "UTC-02"; + case "Atlantic/St_Helena": return "Greenwich Standard Time"; + case "Atlantic/Stanley": return "SA Eastern Standard Time"; case "Australia/Adelaide": return "Cen. Australia Standard Time"; case "Australia/Brisbane": return "E. Australia Standard Time"; + case "Australia/Broken_Hill": return "Cen. Australia Standard Time"; + case "Australia/Currie": return "Tasmania Standard Time"; case "Australia/Darwin": return "AUS Central Standard Time"; case "Australia/Hobart": return "Tasmania Standard Time"; + case "Australia/Lindeman": return "E. Australia Standard Time"; + case "Australia/Melbourne": return "AUS Eastern Standard Time"; case "Australia/Perth": return "W. Australia Standard Time"; case "Australia/Sydney": return "AUS Eastern Standard Time"; - case "Etc/GMT-12": return "UTC+12"; + case "CST6CDT": return "Central Standard Time"; + case "EST5EDT": return "Eastern Standard Time"; case "Etc/GMT": return "UTC"; + case "Etc/GMT+1": return "Cape Verde Standard Time"; + case "Etc/GMT+10": return "Hawaiian Standard Time"; case "Etc/GMT+11": return "UTC-11"; case "Etc/GMT+12": return "Dateline Standard Time"; - case "Etc/GMT+2": return "Mid-Atlantic Standard Time"; - case "Etc/GMT+5": return "US Eastern Standard Time"; + case "Etc/GMT+2": return "UTC-02"; + case "Etc/GMT+3": return "SA Eastern Standard Time"; + case "Etc/GMT+4": return "SA Western Standard Time"; + case "Etc/GMT+5": return "SA Pacific Standard Time"; + case "Etc/GMT+6": return "Central America Standard Time"; + case "Etc/GMT+7": return "US Mountain Standard Time"; + case "Etc/GMT-1": return "W. Central Africa Standard Time"; + case "Etc/GMT-10": return "West Pacific Standard Time"; + case "Etc/GMT-11": return "Central Pacific Standard Time"; + case "Etc/GMT-12": return "UTC+12"; + case "Etc/GMT-13": return "Tonga Standard Time"; + case "Etc/GMT-2": return "South Africa Standard Time"; + case "Etc/GMT-3": return "E. Africa Standard Time"; + case "Etc/GMT-4": return "Arabian Standard Time"; + case "Etc/GMT-5": return "West Asia Standard Time"; + case "Etc/GMT-6": return "Central Asia Standard Time"; + case "Etc/GMT-7": return "SE Asia Standard Time"; + case "Etc/GMT-8": return "Singapore Standard Time"; + case "Etc/GMT-9": return "Tokyo Standard Time"; + case "Europe/Amsterdam": return "W. Europe Standard Time"; + case "Europe/Andorra": return "W. Europe Standard Time"; + case "Europe/Athens": return "GTB Standard Time"; + case "Europe/Belgrade": return "Central Europe Standard Time"; case "Europe/Berlin": return "W. Europe Standard Time"; + case "Europe/Bratislava": return "Central Europe Standard Time"; + case "Europe/Brussels": return "Romance Standard Time"; + case "Europe/Bucharest": return "GTB Standard Time"; case "Europe/Budapest": return "Central Europe Standard Time"; - //This should probably be Turkey Standard Time, but GTB Standard Time - //has been around longer and therefore will work on more systems. - case "Europe/Istanbul": return "GTB Standard Time"; + case "Europe/Busingen": return "W. Europe Standard Time"; + case "Europe/Chisinau": return "GTB Standard Time"; + case "Europe/Copenhagen": return "Romance Standard Time"; + case "Europe/Dublin": return "GMT Standard Time"; + case "Europe/Gibraltar": return "W. Europe Standard Time"; + case "Europe/Guernsey": return "GMT Standard Time"; + case "Europe/Helsinki": return "FLE Standard Time"; + case "Europe/Isle_of_Man": return "GMT Standard Time"; + case "Europe/Istanbul": return "Turkey Standard Time"; + case "Europe/Jersey": return "GMT Standard Time"; case "Europe/Kaliningrad": return "Kaliningrad Standard Time"; case "Europe/Kiev": return "FLE Standard Time"; + case "Europe/Lisbon": return "GMT Standard Time"; + case "Europe/Ljubljana": return "Central Europe Standard Time"; case "Europe/London": return "GMT Standard Time"; - case "Europe/Minsk": return "E. Europe Standard Time"; + case "Europe/Luxembourg": return "W. Europe Standard Time"; + case "Europe/Madrid": return "Romance Standard Time"; + case "Europe/Malta": return "W. Europe Standard Time"; + case "Europe/Mariehamn": return "FLE Standard Time"; + case "Europe/Minsk": return "Kaliningrad Standard Time"; + case "Europe/Monaco": return "W. Europe Standard Time"; case "Europe/Moscow": return "Russian Standard Time"; + case "Europe/Oslo": return "W. Europe Standard Time"; case "Europe/Paris": return "Romance Standard Time"; + case "Europe/Podgorica": return "Central Europe Standard Time"; + case "Europe/Prague": return "Central Europe Standard Time"; + case "Europe/Riga": return "FLE Standard Time"; + case "Europe/Rome": return "W. Europe Standard Time"; + case "Europe/Samara": return "Russian Standard Time"; + case "Europe/San_Marino": return "W. Europe Standard Time"; + case "Europe/Sarajevo": return "Central European Standard Time"; + case "Europe/Simferopol": return "Russian Standard Time"; + case "Europe/Skopje": return "Central European Standard Time"; + case "Europe/Sofia": return "FLE Standard Time"; + case "Europe/Stockholm": return "W. Europe Standard Time"; + case "Europe/Tallinn": return "FLE Standard Time"; + case "Europe/Tirane": return "Central Europe Standard Time"; + case "Europe/Uzhgorod": return "FLE Standard Time"; + case "Europe/Vaduz": return "W. Europe Standard Time"; + case "Europe/Vatican": return "W. Europe Standard Time"; + case "Europe/Vienna": return "W. Europe Standard Time"; + case "Europe/Vilnius": return "FLE Standard Time"; + case "Europe/Volgograd": return "Russian Standard Time"; case "Europe/Warsaw": return "Central European Standard Time"; + case "Europe/Zagreb": return "Central European Standard Time"; + case "Europe/Zaporozhye": return "FLE Standard Time"; + case "Europe/Zurich": return "W. Europe Standard Time"; + case "Indian/Antananarivo": return "E. Africa Standard Time"; + case "Indian/Chagos": return "Central Asia Standard Time"; + case "Indian/Christmas": return "SE Asia Standard Time"; + case "Indian/Cocos": return "Myanmar Standard Time"; + case "Indian/Comoro": return "E. Africa Standard Time"; + case "Indian/Kerguelen": return "West Asia Standard Time"; + case "Indian/Mahe": return "Mauritius Standard Time"; + case "Indian/Maldives": return "West Asia Standard Time"; case "Indian/Mauritius": return "Mauritius Standard Time"; + case "Indian/Mayotte": return "E. Africa Standard Time"; + case "Indian/Reunion": return "Mauritius Standard Time"; + case "MST7MDT": return "Mountain Standard Time"; + case "PST8PDT": return "Pacific Standard Time"; case "Pacific/Apia": return "Samoa Standard Time"; case "Pacific/Auckland": return "New Zealand Standard Time"; + case "Pacific/Efate": return "Central Pacific Standard Time"; + case "Pacific/Enderbury": return "Tonga Standard Time"; + case "Pacific/Fakaofo": return "Tonga Standard Time"; case "Pacific/Fiji": return "Fiji Standard Time"; + case "Pacific/Funafuti": return "UTC+12"; + case "Pacific/Galapagos": return "Central America Standard Time"; case "Pacific/Guadalcanal": return "Central Pacific Standard Time"; + case "Pacific/Guam": return "West Pacific Standard Time"; case "Pacific/Honolulu": return "Hawaiian Standard Time"; + case "Pacific/Johnston": return "Hawaiian Standard Time"; + case "Pacific/Kosrae": return "Central Pacific Standard Time"; + case "Pacific/Kwajalein": return "UTC+12"; + case "Pacific/Majuro": return "UTC+12"; + case "Pacific/Midway": return "UTC-11"; + case "Pacific/Nauru": return "UTC+12"; + case "Pacific/Niue": return "UTC-11"; + case "Pacific/Noumea": return "Central Pacific Standard Time"; + case "Pacific/Pago_Pago": return "UTC-11"; + case "Pacific/Palau": return "Tokyo Standard Time"; + case "Pacific/Ponape": return "Central Pacific Standard Time"; case "Pacific/Port_Moresby": return "West Pacific Standard Time"; + case "Pacific/Rarotonga": return "Hawaiian Standard Time"; + case "Pacific/Saipan": return "West Pacific Standard Time"; + case "Pacific/Tahiti": return "Hawaiian Standard Time"; + case "Pacific/Tarawa": return "UTC+12"; case "Pacific/Tongatapu": return "Tonga Standard Time"; - default: - throw new DateTimeException(format("Could not find Windows time zone name for: %s.", tzName)); + case "Pacific/Truk": return "West Pacific Standard Time"; + case "Pacific/Wake": return "UTC+12"; + case "Pacific/Wallis": return "UTC+12"; + default: return null; } } -unittest +version(Windows) unittest { - version(testStdDateTime) - { - version(Windows) - { - static void testTZSuccess(string tzName) - { - scope(failure) writefln("TZName which threw: %s", tzName); - - tzDatabaseNameToWindowsTZName(tzName); - } - - auto timeZones = TimeZone.getInstalledTZNames(); - - foreach(tzname; timeZones) - assertNotThrown!DateTimeException(testTZSuccess(tzname)); - } - } + foreach(tzName; TimeZone.getInstalledTZNames()) + assert(tzDatabaseNameToWindowsTZName(tzName) !is null, format("TZName which failed: %s", tzName)); } @@ -30600,23 +29103,19 @@ unittest Converts the given Windows time zone name to a corresponding TZ Database name. + Returns null if the given time zone name cannot be converted. + See_Also: $(WEB unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html, Windows <-> TZ Database Name Conversion Table) Params: tzName = The TZ Database name to convert. - - Throws: - $(LREF DateTimeException) if the given $(D_PARAM tzName) cannot be - converted. +/ -string windowsTZNameToTZDatabaseName(string tzName) +string windowsTZNameToTZDatabaseName(string tzName) @safe pure nothrow @nogc { switch(tzName) { - //Most of these come from the link in the documentation, but a few have - //been added because they were found in the Windows registry. case "AUS Central Standard Time": return "Australia/Darwin"; case "AUS Eastern Standard Time": return "Australia/Sydney"; case "Afghanistan Standard Time": return "Asia/Kabul"; @@ -30625,7 +29124,6 @@ string windowsTZNameToTZDatabaseName(string tzName) case "Arabian Standard Time": return "Asia/Dubai"; case "Arabic Standard Time": return "Asia/Baghdad"; case "Argentina Standard Time": return "America/Buenos_Aires"; - case "Armenian Standard Time": return "Asia/Yerevan"; case "Atlantic Standard Time": return "America/Halifax"; case "Azerbaijan Standard Time": return "Asia/Baku"; case "Azores Standard Time": return "Atlantic/Azores"; @@ -30647,6 +29145,9 @@ string windowsTZNameToTZDatabaseName(string tzName) case "Dateline Standard Time": return "Etc/GMT+12"; case "E. Africa Standard Time": return "Africa/Nairobi"; case "E. Australia Standard Time": return "Australia/Brisbane"; + // This doesn't appear to be in the current stuff from MS, but the autotester + // is failing without it (probably because its time zone data hasn't been + // updated recently enough). case "E. Europe Standard Time": return "Europe/Minsk"; case "E. South America Standard Time": return "America/Sao_Paulo"; case "Eastern Standard Time": return "America/New_York"; @@ -30655,7 +29156,7 @@ string windowsTZNameToTZDatabaseName(string tzName) case "FLE Standard Time": return "Europe/Kiev"; case "Fiji Standard Time": return "Pacific/Fiji"; case "GMT Standard Time": return "Europe/London"; - case "GTB Standard Time": return "Europe/Istanbul"; + case "GTB Standard Time": return "Europe/Athens"; case "Georgian Standard Time": return "Asia/Tbilisi"; case "Greenland Standard Time": return "America/Godthab"; case "Greenwich Standard Time": return "Atlantic/Reykjavik"; @@ -30665,13 +29166,18 @@ string windowsTZNameToTZDatabaseName(string tzName) case "Israel Standard Time": return "Asia/Jerusalem"; case "Jordan Standard Time": return "Asia/Amman"; case "Kaliningrad Standard Time": return "Europe/Kaliningrad"; + // Same as with E. Europe Standard Time. case "Kamchatka Standard Time": return "Asia/Kamchatka"; case "Korea Standard Time": return "Asia/Seoul"; case "Libya Standard Time": return "Africa/Tripoli"; + case "Line Islands Standard Time": return "Africa/Bogota"; case "Magadan Standard Time": return "Asia/Magadan"; case "Mauritius Standard Time": return "Indian/Mauritius"; + // Same as with E. Europe Standard Time. case "Mexico Standard Time": return "America/Mexico_City"; + // Same as with E. Europe Standard Time. case "Mexico Standard Time 2": return "America/Chihuahua"; + // Same as with E. Europe Standard Time. case "Mid-Atlantic Standard Time": return "Etc/GMT+2"; case "Middle East Standard Time": return "Asia/Beirut"; case "Montevideo Standard Time": return "America/Montevideo"; @@ -30707,7 +29213,7 @@ string windowsTZNameToTZDatabaseName(string tzName) case "Tokyo Standard Time": return "Asia/Tokyo"; case "Tonga Standard Time": return "Pacific/Tongatapu"; case "Turkey Standard Time": return "Europe/Istanbul"; - case "US Eastern Standard Time": return "Etc/GMT+5"; + case "US Eastern Standard Time": return "America/Indianapolis"; case "US Mountain Standard Time": return "America/Phoenix"; case "UTC": return "Etc/GMT"; case "UTC+12": return "Etc/GMT-12"; @@ -30722,30 +29228,14 @@ string windowsTZNameToTZDatabaseName(string tzName) case "West Asia Standard Time": return "Asia/Tashkent"; case "West Pacific Standard Time": return "Pacific/Port_Moresby"; case "Yakutsk Standard Time": return "Asia/Yakutsk"; - default: - throw new DateTimeException(format("Could not find TZ Database name for: %s.", tzName)); + default: return null; } } -unittest +version(Windows) unittest { - version(testStdDateTime) - { - version(Windows) - { - static void testTZSuccess(string tzName) - { - scope(failure) writefln("TZName which threw: %s", tzName); - - windowsTZNameToTZDatabaseName(tzName); - } - - auto timeZones = WindowsTimeZone.getInstalledTZNames(); - - foreach(tzname; timeZones) - assertNotThrown!DateTimeException(testTZSuccess(tzname)); - } - } + foreach(tzName; WindowsTimeZone.getInstalledTZNames()) + assert(windowsTZNameToTZDatabaseName(tzName) !is null, format("TZName which failed: %s", tzName)); } @@ -30839,7 +29329,7 @@ public: start(); } - version(testStdDateTime) @safe unittest + @safe unittest { auto sw = StopWatch(AutoStart.yes); sw.stop(); @@ -30879,7 +29369,7 @@ public: _timeMeasured.length = 0; } - version(testStdDateTime) @safe unittest + @safe unittest { StopWatch sw; sw.start(); @@ -30899,7 +29389,7 @@ public: _timeStart = Clock.currSystemTick; } - version(testStdDateTime) @trusted unittest + @trusted unittest { StopWatch sw; sw.start(); @@ -30925,7 +29415,7 @@ public: _timeMeasured += Clock.currSystemTick - _timeStart; } - version(testStdDateTime) @trusted unittest + @trusted unittest { StopWatch sw; sw.start(); @@ -30953,7 +29443,7 @@ public: return _timeMeasured; } - version(testStdDateTime) @safe unittest + @safe unittest { StopWatch sw; sw.start(); @@ -30976,7 +29466,7 @@ public: _timeMeasured = d; } - version(testStdDateTime) @safe unittest + @safe unittest { StopWatch sw; TickDuration t0; @@ -30995,7 +29485,7 @@ public: return _flagStarted; } - version(testStdDateTime) @safe unittest + @safe unittest { StopWatch sw1; assert(!sw1.running); @@ -31027,13 +29517,6 @@ private: } -// workaround for bug4886 -@safe size_t lengthof(aliases...)() pure nothrow -{ - return aliases.length; -} - - /++ Benchmarks code for speed assessment and comparison. @@ -31048,19 +29531,16 @@ private: that it took to call $(D fun[0]) $(D n) times. The second value is the length of time it took to call $(D fun[1]) $(D n) times. Etc. - Examples: --------------------- -int a; -void f0() {} -void f1() {auto b = a;} -void f2() {auto b = to!(string)(a);} -auto r = benchmark!(f0, f1, f2)(10_000); -writefln("Milliseconds to call fun[0] n times: %s", r[0].msecs); --------------------- + Note that casting the TickDurations to $(CXREF time, Duration)s will make + the results easier to deal with (and it may change in the future that + benchmark will return an array of Durations rather than TickDurations). + + See_Also: + $(LREF measureTime) +/ -TickDuration[lengthof!(fun)()] benchmark(fun...)(uint n) +TickDuration[fun.length] benchmark(fun...)(uint n) { - TickDuration[lengthof!(fun)()] result; + TickDuration[fun.length] result; StopWatch sw; sw.start(); @@ -31075,20 +29555,20 @@ TickDuration[lengthof!(fun)()] benchmark(fun...)(uint n) return result; } -//Verify Examples. -version(testStdDateTime) unittest +/// +unittest { - void writefln(S...)(S args){} - int a; void f0() {} void f1() {auto b = a;} - void f2() {auto b = to!(string)(a);} + void f2() {auto b = to!string(a);} auto r = benchmark!(f0, f1, f2)(10_000); - writefln("Milliseconds to call fun[0] n times: %s", r[0].msecs); + auto f0Result = to!Duration(r[0]); // time f0 took to run 10,000 times + auto f1Result = to!Duration(r[1]); // time f1 took to run 10,000 times + auto f2Result = to!Duration(r[2]); // time f2 took to run 10,000 times } -version(testStdDateTime) @safe unittest +@safe unittest { int a; void f0() {} @@ -31177,7 +29657,7 @@ ComparingBenchmarkResult comparingBenchmark(alias baseFunc, return ComparingBenchmarkResult(t[0], t[1]); } -version(testStdDateTime) @safe unittest +@safe unittest { void f1x() {} void f2x() {} @@ -31187,7 +29667,7 @@ version(testStdDateTime) @safe unittest //static auto b2 = comparingBenchmark!(f1x, f2x, 1); // NG } -version(testStdDateTime) unittest +unittest { void f1x() {} void f2x() {} @@ -31198,7 +29678,7 @@ version(testStdDateTime) unittest } //Bug# 8450 -version(testStdDateTime) unittest +unittest { @safe void safeFunc() {} @trusted void trustFunc() {} @@ -31232,21 +29712,18 @@ template isTimePoint(T) unittest { - version(testStdDateTime) - { - static assert(isTimePoint!(Date)); - static assert(isTimePoint!(DateTime)); - static assert(isTimePoint!(TimeOfDay)); - static assert(isTimePoint!(SysTime)); - static assert(isTimePoint!(const Date)); - static assert(isTimePoint!(const DateTime)); - static assert(isTimePoint!(const TimeOfDay)); - static assert(isTimePoint!(const SysTime)); - static assert(isTimePoint!(immutable Date)); - static assert(isTimePoint!(immutable DateTime)); - static assert(isTimePoint!(immutable TimeOfDay)); - static assert(isTimePoint!(immutable SysTime)); - } + static assert(isTimePoint!(Date)); + static assert(isTimePoint!(DateTime)); + static assert(isTimePoint!(TimeOfDay)); + static assert(isTimePoint!(SysTime)); + static assert(isTimePoint!(const Date)); + static assert(isTimePoint!(const DateTime)); + static assert(isTimePoint!(const TimeOfDay)); + static assert(isTimePoint!(const SysTime)); + static assert(isTimePoint!(immutable Date)); + static assert(isTimePoint!(immutable DateTime)); + static assert(isTimePoint!(immutable TimeOfDay)); + static assert(isTimePoint!(immutable SysTime)); } /++ @@ -31255,7 +29732,7 @@ unittest Params: year = The year to to be tested. +/ -static bool yearIsLeapYear(int year) pure nothrow +static bool yearIsLeapYear(int year) @safe pure nothrow { if(year % 400 == 0) return true; @@ -31266,7 +29743,7 @@ static bool yearIsLeapYear(int year) pure nothrow return year % 4 == 0; } -version(testStdDateTime) unittest +unittest { foreach(year; [1, 2, 3, 5, 6, 7, 100, 200, 300, 500, 600, 700, 1998, 1999, 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010, 2011]) @@ -31290,7 +29767,7 @@ version(testStdDateTime) unittest Params: unixTime = The $(D time_t) to convert. +/ -long unixTimeToStdTime(time_t unixTime) pure nothrow +long unixTimeToStdTime(time_t unixTime) @safe pure nothrow { return 621_355_968_000_000_000L + convert!("seconds", "hnsecs")(unixTime); @@ -31298,15 +29775,12 @@ long unixTimeToStdTime(time_t unixTime) pure nothrow unittest { - version(testStdDateTime) - { - _assertPred!"=="(unixTimeToStdTime(0), 621_355_968_000_000_000L); //Midnight, January 1st, 1970 - _assertPred!"=="(unixTimeToStdTime(86_400), 621_355_968_000_000_000L + 864_000_000_000L); //Midnight, January 2nd, 1970 - _assertPred!"=="(unixTimeToStdTime(-86_400), 621_355_968_000_000_000L - 864_000_000_000L); //Midnight, December 31st, 1969 + assert(unixTimeToStdTime(0) == 621_355_968_000_000_000L); //Midnight, January 1st, 1970 + assert(unixTimeToStdTime(86_400) == 621_355_968_000_000_000L + 864_000_000_000L); //Midnight, January 2nd, 1970 + assert(unixTimeToStdTime(-86_400) == 621_355_968_000_000_000L - 864_000_000_000L); //Midnight, December 31st, 1969 - _assertPred!"=="(unixTimeToStdTime(0), (Date(1970, 1, 1) - Date(1, 1, 1)).total!"hnsecs"); - _assertPred!"=="(unixTimeToStdTime(0), (DateTime(1970, 1, 1) - DateTime(1, 1, 1)).total!"hnsecs"); - } + assert(unixTimeToStdTime(0) == (Date(1970, 1, 1) - Date(1, 1, 1)).total!"hnsecs"); + assert(unixTimeToStdTime(0) == (DateTime(1970, 1, 1) - DateTime(1, 1, 1)).total!"hnsecs"); } @@ -31330,7 +29804,7 @@ unittest Params: stdTime = The std time to convert. +/ -time_t stdTimeToUnixTime(long stdTime) pure nothrow +time_t stdTimeToUnixTime(long stdTime) @safe pure nothrow { immutable unixTime = convert!("hnsecs", "seconds")(stdTime - 621_355_968_000_000_000L); @@ -31354,15 +29828,12 @@ time_t stdTimeToUnixTime(long stdTime) pure nothrow unittest { - version(testStdDateTime) - { - _assertPred!"=="(stdTimeToUnixTime(621_355_968_000_000_000L), 0); //Midnight, January 1st, 1970 - _assertPred!"=="(stdTimeToUnixTime(621_355_968_000_000_000L + 864_000_000_000L), 86_400); //Midnight, January 2nd, 1970 - _assertPred!"=="(stdTimeToUnixTime(621_355_968_000_000_000L - 864_000_000_000L), -86_400); //Midnight, December 31st, 1969 + assert(stdTimeToUnixTime(621_355_968_000_000_000L) == 0); //Midnight, January 1st, 1970 + assert(stdTimeToUnixTime(621_355_968_000_000_000L + 864_000_000_000L) == 86_400); //Midnight, January 2nd, 1970 + assert(stdTimeToUnixTime(621_355_968_000_000_000L - 864_000_000_000L) == -86_400); //Midnight, December 31st, 1969 - _assertPred!"=="(stdTimeToUnixTime((Date(1970, 1, 1) - Date(1, 1, 1)).total!"hnsecs"), 0); - _assertPred!"=="(stdTimeToUnixTime((DateTime(1970, 1, 1) - DateTime(1, 1, 1)).total!"hnsecs"), 0); - } + assert(stdTimeToUnixTime((Date(1970, 1, 1) - Date(1, 1, 1)).total!"hnsecs") == 0); + assert(stdTimeToUnixTime((DateTime(1970, 1, 1) - DateTime(1, 1, 1)).total!"hnsecs") == 0); } @@ -31393,7 +29864,7 @@ version(StdDdoc) $(D SysTime.max) is in 29,228 A.D. and the maximum $(D SYSTEMTIME) is in 30,827 A.D. +/ - SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()); + SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe; /++ @@ -31413,7 +29884,7 @@ version(StdDdoc) $(D SYSTEMTIME). This will only happen if the $(LREF SysTime)'s date is prior to 1601 A.D. +/ - SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime); + SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime) @safe; /++ @@ -31429,7 +29900,7 @@ version(StdDdoc) $(LREF DateTimeException) if the given $(D FILETIME) cannot be represented as the return value. +/ - long FILETIMEToStdTime(const FILETIME* ft); + long FILETIMEToStdTime(const FILETIME* ft) @safe; /++ @@ -31446,7 +29917,7 @@ version(StdDdoc) $(LREF DateTimeException) if the given $(D FILETIME) will not fit in a $(LREF SysTime). +/ - SysTime FILETIMEToSysTime(const FILETIME* ft, immutable TimeZone tz = LocalTime()); + SysTime FILETIMEToSysTime(const FILETIME* ft, immutable TimeZone tz = LocalTime()) @safe; /++ @@ -31462,7 +29933,7 @@ version(StdDdoc) $(LREF DateTimeException) if the given value will not fit in a $(D FILETIME). +/ - FILETIME stdTimeToFILETIME(long stdTime); + FILETIME stdTimeToFILETIME(long stdTime) @safe; /++ @@ -31479,11 +29950,11 @@ version(StdDdoc) $(LREF DateTimeException) if the given $(LREF SysTime) will not fit in a $(D FILETIME). +/ - FILETIME SysTimeToFILETIME(SysTime sysTime); + FILETIME SysTimeToFILETIME(SysTime sysTime) @safe; } else version(Windows) { - SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) + SysTime SYSTEMTIMEToSysTime(const SYSTEMTIME* st, immutable TimeZone tz = LocalTime()) @safe { const max = SysTime.max; @@ -31533,20 +30004,16 @@ else version(Windows) unittest { - version(testStdDateTime) - { - auto sysTime = Clock.currTime(UTC()); - SYSTEMTIME st = void; - GetSystemTime(&st); - auto converted = SYSTEMTIMEToSysTime(&st, UTC()); + auto sysTime = Clock.currTime(UTC()); + SYSTEMTIME st = void; + GetSystemTime(&st); + auto converted = SYSTEMTIMEToSysTime(&st, UTC()); - _assertPred!"<="(abs((converted - sysTime)), - dur!"seconds"(2)); - } + assert(abs((converted - sysTime)) <= dur!"seconds"(2)); } - SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime) + SYSTEMTIME SysTimeToSYSTEMTIME(in SysTime sysTime) @safe { immutable dt = cast(DateTime)sysTime; @@ -31564,213 +30031,932 @@ else version(Windows) st.wSecond = dt.second; st.wMilliseconds = cast(ushort)sysTime.fracSec.msecs; - return st; - } + return st; + } + + unittest + { + SYSTEMTIME st = void; + GetSystemTime(&st); + auto sysTime = SYSTEMTIMEToSysTime(&st, UTC()); + + SYSTEMTIME result = SysTimeToSYSTEMTIME(sysTime); + + assert(st.wYear == result.wYear); + assert(st.wMonth == result.wMonth); + assert(st.wDayOfWeek == result.wDayOfWeek); + assert(st.wDay == result.wDay); + assert(st.wHour == result.wHour); + assert(st.wMinute == result.wMinute); + assert(st.wSecond == result.wSecond); + assert(st.wMilliseconds == result.wMilliseconds); + } + + private enum hnsecsFrom1601 = 504_911_232_000_000_000L; + + long FILETIMEToStdTime(const FILETIME* ft) @safe + { + ULARGE_INTEGER ul; + ul.HighPart = ft.dwHighDateTime; + ul.LowPart = ft.dwLowDateTime; + ulong tempHNSecs = ul.QuadPart; + + if(tempHNSecs > long.max - hnsecsFrom1601) + throw new DateTimeException("The given FILETIME cannot be represented as a stdTime value."); + + return cast(long)tempHNSecs + hnsecsFrom1601; + } + + SysTime FILETIMEToSysTime(const FILETIME* ft, immutable TimeZone tz = LocalTime()) @safe + { + auto sysTime = SysTime(FILETIMEToStdTime(ft), UTC()); + sysTime.timezone = tz; + + return sysTime; + } + + unittest + { + auto sysTime = Clock.currTime(UTC()); + SYSTEMTIME st = void; + GetSystemTime(&st); + + FILETIME ft = void; + SystemTimeToFileTime(&st, &ft); + + auto converted = FILETIMEToSysTime(&ft); + + assert(abs((converted - sysTime)) <= dur!"seconds"(2)); + } + + + FILETIME stdTimeToFILETIME(long stdTime) @safe + { + if(stdTime < hnsecsFrom1601) + throw new DateTimeException("The given stdTime value cannot be represented as a FILETIME."); + + ULARGE_INTEGER ul; + ul.QuadPart = cast(ulong)stdTime - hnsecsFrom1601; + + FILETIME ft; + ft.dwHighDateTime = ul.HighPart; + ft.dwLowDateTime = ul.LowPart; + + return ft; + } + + FILETIME SysTimeToFILETIME(SysTime sysTime) @safe + { + return stdTimeToFILETIME(sysTime.stdTime); + } + + unittest + { + SYSTEMTIME st = void; + GetSystemTime(&st); + + FILETIME ft = void; + SystemTimeToFileTime(&st, &ft); + auto sysTime = FILETIMEToSysTime(&ft, UTC()); + + FILETIME result = SysTimeToFILETIME(sysTime); + + assert(ft.dwLowDateTime == result.dwLowDateTime); + assert(ft.dwHighDateTime == result.dwHighDateTime); + } +} + + +/++ + Type representing the DOS file date/time format. + +/ +alias uint DosFileTime; + +/++ + Converts from DOS file date/time to $(LREF SysTime). + + Params: + dft = The DOS file time to convert. + tz = The time zone which the DOS file time is assumed to be in. + + Throws: + $(LREF DateTimeException) if the $(D DosFileTime) is invalid. + +/ +SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime()) @safe +{ + uint dt = cast(uint)dft; + + if(dt == 0) + throw new DateTimeException("Invalid DosFileTime."); + + int year = ((dt >> 25) & 0x7F) + 1980; + int month = ((dt >> 21) & 0x0F); // 1..12 + int dayOfMonth = ((dt >> 16) & 0x1F); // 1..31 + int hour = (dt >> 11) & 0x1F; // 0..23 + int minute = (dt >> 5) & 0x3F; // 0..59 + int second = (dt << 1) & 0x3E; // 0..58 (in 2 second increments) + + try + return SysTime(DateTime(year, month, dayOfMonth, hour, minute, second), tz); + catch(DateTimeException dte) + throw new DateTimeException("Invalid DosFileTime", __FILE__, __LINE__, dte); +} + +unittest +{ + assert(DosFileTimeToSysTime(0b00000000001000010000000000000000) == + SysTime(DateTime(1980, 1, 1, 0, 0, 0))); + + assert(DosFileTimeToSysTime(0b11111111100111111011111101111101) == + SysTime(DateTime(2107, 12, 31, 23, 59, 58))); + + assert(DosFileTimeToSysTime(0x3E3F8456) == + SysTime(DateTime(2011, 1, 31, 16, 34, 44))); +} + + +/++ + Converts from $(LREF SysTime) to DOS file date/time. + + Params: + sysTime = The $(LREF SysTime) to convert. + + Throws: + $(LREF DateTimeException) if the given $(LREF SysTime) cannot be converted to + a $(D DosFileTime). + +/ +DosFileTime SysTimeToDosFileTime(SysTime sysTime) @safe +{ + auto dateTime = cast(DateTime)sysTime; + + if(dateTime.year < 1980) + throw new DateTimeException("DOS File Times cannot hold dates prior to 1980."); + + if(dateTime.year > 2107) + throw new DateTimeException("DOS File Times cannot hold dates passed 2107."); + + uint retval = 0; + retval = (dateTime.year - 1980) << 25; + retval |= (dateTime.month & 0x0F) << 21; + retval |= (dateTime.day & 0x1F) << 16; + retval |= (dateTime.hour & 0x1F) << 11; + retval |= (dateTime.minute & 0x3F) << 5; + retval |= (dateTime.second >> 1) & 0x1F; + + return cast(DosFileTime)retval; +} + +unittest +{ + assert(SysTimeToDosFileTime(SysTime(DateTime(1980, 1, 1, 0, 0, 0))) == + 0b00000000001000010000000000000000); + + assert(SysTimeToDosFileTime(SysTime(DateTime(2107, 12, 31, 23, 59, 58))) == + 0b11111111100111111011111101111101); + + assert(SysTimeToDosFileTime(SysTime(DateTime(2011, 1, 31, 16, 34, 44))) == + 0x3E3F8456); +} + + +/++ + The given array of $(D char) or random-access range of $(D char) or + $(D ubyte) is expected to be in the format specified in + $(WEB http://tools.ietf.org/html/rfc5322, RFC 5322) section 3.3 with the + grammar rule $(I date-time). It is the date-time format commonly used in + internet messages such as e-mail and HTTP. The corresponding + $(LREF SysTime) will be returned. + + RFC 822 was the original spec (hence the function's name), whereas RFC 5322 + is the current spec. + + The day of the week is ignored beyond verifying that it's a valid day of the + week, as the day of the week can be inferred from the date. It is not + checked whether the given day of the week matches the actual day of the week + of the given date (though it is technically invalid per the spec if the + day of the week doesn't match the actual day of the week of the given date). + + If the time zone is $(D "-0000") (or considered to be equivalent to + $(D "-0000") by section 4.3 of the spec), a $(LREF SimpleTimeZone) with a + utc offset of $(D 0) is used rather than $(LREF UTC), whereas $(D "+0000") + uses $(LREF UTC). + + Note that because $(LREF SysTime) does not currently support having a second + value of 60 (as is sometimes done for leap seconds), if the date-time value + does have a value of 60 for the seconds, it is treated as 59. + + The one area in which this function violates RFC 5322 is that it accepts + $(D "\n") in folding whitespace in the place of $(D "\r\n"), because the + HTTP spec requires it. + + Throws: + $(LREF DateTimeException) if the given string doesn't follow the grammar + for a date-time field or if the resulting $(LREF SysTime) is invalid. + +/ +SysTime parseRFC822DateTime()(in char[] value) @safe +{ + return parseRFC822DateTime(value.representation); +} + +/++ Ditto +/ +SysTime parseRFC822DateTime(R)(R value) @safe + if(isRandomAccessRange!R && hasSlicing!R && hasLength!R && + (is(Unqual!(ElementType!R) == char) || is(Unqual!(ElementType!R) == ubyte))) +{ + void stripAndCheckLen(R valueBefore, size_t minLen, size_t line = __LINE__) + { + value = _stripCFWS(valueBefore); + if(value.length < minLen) + throw new DateTimeException("date-time value too short", __FILE__, line); + } + stripAndCheckLen(value, "7Dec1200:00A".length); + + static if(isArray!R && (is(ElementEncodingType!R == char) || is(ElementEncodingType!R == ubyte))) + { + static string sliceAsString(R str) @trusted + { + return cast(string)str; + } + } + else + { + char[4] temp; + char[] sliceAsString(R str) @trusted + { + size_t i = 0; + foreach(c; str) + temp[i++] = cast(char)c; + return temp[0 .. str.length]; + } + } + + // day-of-week + if(std.ascii.isAlpha(value[0])) + { + auto dowStr = sliceAsString(value[0 .. 3]); + switch(dowStr) + { + foreach(dow; EnumMembers!DayOfWeek) + { + enum dowC = capitalize(to!string(dow)); + case dowC: + goto afterDoW; + } + default: throw new DateTimeException(format("Invalid day-of-week: %s", dowStr)); + } +afterDoW: stripAndCheckLen(value[3 .. value.length], ",7Dec1200:00A".length); + if(value[0] != ',') + throw new DateTimeException("day-of-week missing comma"); + stripAndCheckLen(value[1 .. value.length], "7Dec1200:00A".length); + } + + // day + immutable digits = std.ascii.isDigit(value[1]) ? 2 : 1; + immutable day = _convDigits!short(value[0 .. digits]); + if(day == -1) + throw new DateTimeException("Invalid day"); + stripAndCheckLen(value[digits .. value.length], "Dec1200:00A".length); + + // month + Month month; + { + auto monStr = sliceAsString(value[0 .. 3]); + switch(monStr) + { + foreach(mon; EnumMembers!Month) + { + enum monC = capitalize(to!string(mon)); + case monC: + { + month = mon; + goto afterMon; + } + } + default: throw new DateTimeException(format("Invalid month: %s", monStr)); + } +afterMon: stripAndCheckLen(value[3 .. value.length], "1200:00A".length); + } + + // year + auto found = value[2 .. value.length].find!(not!(std.ascii.isDigit))(); + size_t yearLen = value.length - found.length; + if(found.length == 0) + throw new DateTimeException("Invalid year"); + if(found[0] == ':') + yearLen -= 2; + auto year = _convDigits!short(value[0 .. yearLen]); + if(year < 1900) + { + if(year == -1) + throw new DateTimeException("Invalid year"); + if(yearLen < 4) + { + if(yearLen == 3) + year += 1900; + else if(yearLen == 2) + year += year < 50 ? 2000 : 1900; + else + throw new DateTimeException("Invalid year. Too few digits."); + } + else + throw new DateTimeException("Invalid year. Cannot be earlier than 1900."); + } + stripAndCheckLen(value[yearLen .. value.length], "00:00A".length); + + // hour + immutable hour = _convDigits!short(value[0 .. 2]); + stripAndCheckLen(value[2 .. value.length], ":00A".length); + if(value[0] != ':') + throw new DateTimeException("Invalid hour"); + stripAndCheckLen(value[1 .. value.length], "00A".length); + + // minute + immutable minute = _convDigits!short(value[0 .. 2]); + stripAndCheckLen(value[2 .. value.length], "A".length); + + // second + short second; + if(value[0] == ':') + { + stripAndCheckLen(value[1 .. value.length], "00A".length); + second = _convDigits!short(value[0 .. 2]); + // this is just if/until SysTime is sorted out to fully support leap seconds + if(second == 60) + second = 59; + stripAndCheckLen(value[2 .. value.length], "A".length); + } + + immutable(TimeZone) parseTZ(int sign) + { + if(value.length < 5) + throw new DateTimeException("Invalid timezone"); + immutable zoneHours = _convDigits!short(value[1 .. 3]); + immutable zoneMinutes = _convDigits!short(value[3 .. 5]); + if(zoneHours == -1 || zoneMinutes == -1 || zoneMinutes > 59) + throw new DateTimeException("Invalid timezone"); + value = value[5 .. value.length]; + immutable utcOffset = (dur!"hours"(zoneHours) + dur!"minutes"(zoneMinutes)) * sign; + if(utcOffset == Duration.zero) + { + return sign == 1 ? cast(immutable(TimeZone))UTC() + : cast(immutable(TimeZone))new immutable SimpleTimeZone(Duration.zero); + } + return new immutable(SimpleTimeZone)(utcOffset); + } + + // zone + Rebindable!(immutable TimeZone) tz; + if(value[0] == '-') + tz = parseTZ(-1); + else if(value[0] == '+') + tz = parseTZ(1); + else + { + // obs-zone + immutable tzLen = value.length - find(value, ' ', '\t', '(')[0].length; + switch(sliceAsString(value[0 .. tzLen <= 4 ? tzLen : 4])) + { + case "UT": case "GMT": tz = UTC(); break; + case "EST": tz = new immutable SimpleTimeZone(dur!"hours"(-5)); break; + case "EDT": tz = new immutable SimpleTimeZone(dur!"hours"(-4)); break; + case "CST": tz = new immutable SimpleTimeZone(dur!"hours"(-6)); break; + case "CDT": tz = new immutable SimpleTimeZone(dur!"hours"(-5)); break; + case "MST": tz = new immutable SimpleTimeZone(dur!"hours"(-7)); break; + case "MDT": tz = new immutable SimpleTimeZone(dur!"hours"(-6)); break; + case "PST": tz = new immutable SimpleTimeZone(dur!"hours"(-8)); break; + case "PDT": tz = new immutable SimpleTimeZone(dur!"hours"(-7)); break; + case "J": case "j": throw new DateTimeException("Invalid timezone"); + default: + { + if(all!(std.ascii.isAlpha)(value[0 .. tzLen])) + { + tz = new immutable SimpleTimeZone(Duration.zero); + break; + } + throw new DateTimeException("Invalid timezone"); + } + } + value = value[tzLen .. value.length]; + } + + // This is kind of arbitrary. Technically, nothing but CFWS is legal past + // the end of the timezone, but we don't want to be picky about that in a + // function that's just parsing rather than validating. So, the idea here is + // that if the next character is printable (and not part of CFWS), then it + // might be part of the timezone and thus affect what the timezone was + // supposed to be, so we'll throw, but otherwise, we'll just ignore it. + if(!value.empty && std.ascii.isPrintable(value[0]) && value[0] != ' ' && value[0] != '(') + throw new DateTimeException("Invalid timezone"); + + try + return SysTime(DateTime(year, month, day, hour, minute, second), tz); + catch(DateTimeException dte) + throw new DateTimeException("date-time format is correct, but the resulting SysTime is invalid.", dte); +} + +/// +unittest +{ + auto tz = new immutable SimpleTimeZone(hours(-8)); + assert(parseRFC822DateTime("Sat, 6 Jan 1990 12:14:19 -0800") == + SysTime(DateTime(1990, 1, 6, 12, 14, 19), tz)); + + assert(parseRFC822DateTime("9 Jul 2002 13:11 +0000") == + SysTime(DateTime(2002, 7, 9, 13, 11, 0), UTC())); + + auto badStr = "29 Feb 2001 12:17:16 +0200"; + assertThrown!DateTimeException(parseRFC822DateTime(badStr)); +} + +version(unittest) void testParse822(alias cr)(string str, SysTime expected, size_t line = __LINE__) +{ + auto value = cr(str); + auto result = parseRFC822DateTime(value); + if(result != expected) + throw new AssertError(format("wrong result. expected [%s], actual[%s]", expected, result), __FILE__, line); +} + +version(unittest) void testBadParse822(alias cr)(string str, size_t line = __LINE__) +{ + try + parseRFC822DateTime(cr(str)); + catch(DateTimeException) + return; + throw new AssertError("No DateTimeException was thrown", __FILE__, line); +} + +unittest +{ + import std.typetuple : TypeTuple; + + static struct Rand3Letters + { + enum empty = false; + @property auto front() { return _mon; } + void popFront() + { + import std.random; + alias std.ascii.letters letters; + _mon = rndGen.map!(a => letters[a % letters.length])().take(3).array().assumeUnique(); + } + string _mon; + static auto start() { Rand3Letters retval; retval.popFront(); return retval; } + } + + foreach(cr; TypeTuple!(function(string a){return cast(char[])a;}, + function(string a){return cast(ubyte[])a;}, + function(string a){return a;}, + function(string a){return map!(b => cast(char)b)(a.representation);})) + { + scope(failure) writeln(typeof(cr).stringof); + alias testParse822!cr test; + alias testBadParse822!cr testBad; + + immutable std1 = DateTime(2012, 12, 21, 13, 14, 15); + immutable std2 = DateTime(2012, 12, 21, 13, 14, 0); + immutable dst1 = DateTime(1976, 7, 4, 5, 4, 22); + immutable dst2 = DateTime(1976, 7, 4, 5, 4, 0); + + test("21 Dec 2012 13:14:15 +0000", SysTime(std1, UTC())); + test("21 Dec 2012 13:14 +0000", SysTime(std2, UTC())); + test("Fri, 21 Dec 2012 13:14 +0000", SysTime(std2, UTC())); + test("Fri, 21 Dec 2012 13:14:15 +0000", SysTime(std1, UTC())); + + test("04 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); + test("04 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); + test("Sun, 04 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); + test("Sun, 04 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); + + test("4 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); + test("4 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); + test("Sun, 4 Jul 1976 05:04 +0000", SysTime(dst2, UTC())); + test("Sun, 4 Jul 1976 05:04:22 +0000", SysTime(dst1, UTC())); + + auto badTZ = new immutable SimpleTimeZone(Duration.zero); + test("21 Dec 2012 13:14:15 -0000", SysTime(std1, badTZ)); + test("21 Dec 2012 13:14 -0000", SysTime(std2, badTZ)); + test("Fri, 21 Dec 2012 13:14 -0000", SysTime(std2, badTZ)); + test("Fri, 21 Dec 2012 13:14:15 -0000", SysTime(std1, badTZ)); + + test("04 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); + test("04 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); + test("Sun, 04 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); + test("Sun, 04 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); + + test("4 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); + test("4 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); + test("Sun, 4 Jul 1976 05:04 -0000", SysTime(dst2, badTZ)); + test("Sun, 4 Jul 1976 05:04:22 -0000", SysTime(dst1, badTZ)); - unittest - { - version(testStdDateTime) - { - SYSTEMTIME st = void; - GetSystemTime(&st); - auto sysTime = SYSTEMTIMEToSysTime(&st, UTC()); + auto pst = new immutable SimpleTimeZone(dur!"hours"(-8)); + auto pdt = new immutable SimpleTimeZone(dur!"hours"(-7)); + test("21 Dec 2012 13:14:15 -0800", SysTime(std1, pst)); + test("21 Dec 2012 13:14 -0800", SysTime(std2, pst)); + test("Fri, 21 Dec 2012 13:14 -0800", SysTime(std2, pst)); + test("Fri, 21 Dec 2012 13:14:15 -0800", SysTime(std1, pst)); + + test("04 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); + test("04 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); + test("Sun, 04 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); + test("Sun, 04 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); - SYSTEMTIME result = SysTimeToSYSTEMTIME(sysTime); + test("4 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); + test("4 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); + test("Sun, 4 Jul 1976 05:04 -0700", SysTime(dst2, pdt)); + test("Sun, 4 Jul 1976 05:04:22 -0700", SysTime(dst1, pdt)); - _assertPred!"=="(st.wYear, result.wYear); - _assertPred!"=="(st.wMonth, result.wMonth); - _assertPred!"=="(st.wDayOfWeek, result.wDayOfWeek); - _assertPred!"=="(st.wDay, result.wDay); - _assertPred!"=="(st.wHour, result.wHour); - _assertPred!"=="(st.wMinute, result.wMinute); - _assertPred!"=="(st.wSecond, result.wSecond); - _assertPred!"=="(st.wMilliseconds, result.wMilliseconds); - } - } + auto cet = new immutable SimpleTimeZone(dur!"hours"(1)); + auto cest = new immutable SimpleTimeZone(dur!"hours"(2)); + test("21 Dec 2012 13:14:15 +0100", SysTime(std1, cet)); + test("21 Dec 2012 13:14 +0100", SysTime(std2, cet)); + test("Fri, 21 Dec 2012 13:14 +0100", SysTime(std2, cet)); + test("Fri, 21 Dec 2012 13:14:15 +0100", SysTime(std1, cet)); - private enum hnsecsFrom1601 = 504_911_232_000_000_000L; + test("04 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); + test("04 Jul 1976 05:04 +0200", SysTime(dst2, cest)); + test("Sun, 04 Jul 1976 05:04 +0200", SysTime(dst2, cest)); + test("Sun, 04 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); - long FILETIMEToStdTime(const FILETIME* ft) - { - ULARGE_INTEGER ul; - ul.HighPart = ft.dwHighDateTime; - ul.LowPart = ft.dwLowDateTime; - ulong tempHNSecs = ul.QuadPart; + test("4 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); + test("4 Jul 1976 05:04 +0200", SysTime(dst2, cest)); + test("Sun, 4 Jul 1976 05:04 +0200", SysTime(dst2, cest)); + test("Sun, 4 Jul 1976 05:04:22 +0200", SysTime(dst1, cest)); - if(tempHNSecs > long.max - hnsecsFrom1601) - throw new DateTimeException("The given FILETIME cannot be represented as a stdTime value."); + // dst and std times are switched in the Southern Hemisphere which is why the + // time zone names and DateTime variables don't match. + auto cstStd = new immutable SimpleTimeZone(dur!"hours"(9) + dur!"minutes"(30)); + auto cstDST = new immutable SimpleTimeZone(dur!"hours"(10) + dur!"minutes"(30)); + test("21 Dec 2012 13:14:15 +1030", SysTime(std1, cstDST)); + test("21 Dec 2012 13:14 +1030", SysTime(std2, cstDST)); + test("Fri, 21 Dec 2012 13:14 +1030", SysTime(std2, cstDST)); + test("Fri, 21 Dec 2012 13:14:15 +1030", SysTime(std1, cstDST)); - return cast(long)tempHNSecs + hnsecsFrom1601; - } + test("04 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); + test("04 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); + test("Sun, 04 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); + test("Sun, 04 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - SysTime FILETIMEToSysTime(const FILETIME* ft, immutable TimeZone tz = LocalTime()) - { - auto sysTime = SysTime(FILETIMEToStdTime(ft), UTC()); - sysTime.timezone = tz; + test("4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); + test("4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); + test("Sun, 4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); + test("Sun, 4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); - return sysTime; - } + foreach(int i, mon; _monthNames) + { + test(format("17 %s 2012 00:05:02 +0000", mon), SysTime(DateTime(2012, i + 1, 17, 0, 5, 2), UTC())); + test(format("17 %s 2012 00:05 +0000", mon), SysTime(DateTime(2012, i + 1, 17, 0, 5, 0), UTC())); + } - unittest - { - version(testStdDateTime) + import std.uni; + foreach(mon; chain(_monthNames[].map!(a => toLower(a))(), + _monthNames[].map!(a => toUpper(a))(), + ["Jam", "Jen", "Fec", "Fdb", "Mas", "Mbr", "Aps", "Aqr", "Mai", "Miy", + "Jum", "Jbn", "Jup", "Jal", "Aur", "Apg", "Sem", "Sap", "Ocm", "Odt", + "Nom", "Nav", "Dem", "Dac"], + Rand3Letters.start().take(20))) { - auto sysTime = Clock.currTime(UTC()); - SYSTEMTIME st = void; - GetSystemTime(&st); + scope(failure) writefln("Month: %s", mon); + testBad(format("17 %s 2012 00:05:02 +0000", mon)); + testBad(format("17 %s 2012 00:05 +0000", mon)); + } - FILETIME ft = void; - SystemTimeToFileTime(&st, &ft); + immutable string[7] daysOfWeekNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; - auto converted = FILETIMEToSysTime(&ft); + { + auto start = SysTime(DateTime(2012, 11, 11, 9, 42, 0), UTC()); + int day = 11; - _assertPred!"<="(abs((converted - sysTime)), - dur!"seconds"(2)); - } - } + foreach(int i, dow; daysOfWeekNames) + { + auto curr = start + dur!"days"(i); + test(format("%s, %s Nov 2012 09:42:00 +0000", dow, day), curr); + test(format("%s, %s Nov 2012 09:42 +0000", dow, day++), curr); + // Whether the day of the week matches the date is ignored. + test(format("%s, 11 Nov 2012 09:42:00 +0000", dow), start); + test(format("%s, 11 Nov 2012 09:42 +0000", dow), start); + } + } - FILETIME stdTimeToFILETIME(long stdTime) - { - if(stdTime < hnsecsFrom1601) - throw new DateTimeException("The given stdTime value cannot be represented as a FILETIME."); + foreach(dow; chain(daysOfWeekNames[].map!(a => toLower(a))(), + daysOfWeekNames[].map!(a => toUpper(a))(), + ["Sum", "Spn", "Mom", "Man", "Tuf", "Tae", "Wem", "Wdd", "The", "Tur", + "Fro", "Fai", "San", "Sut"], + Rand3Letters.start().take(20))) + { + scope(failure) writefln("Day of Week: %s", dow); + testBad(format("%s, 11 Nov 2012 09:42:00 +0000", dow)); + testBad(format("%s, 11 Nov 2012 09:42 +0000", dow)); + } - ULARGE_INTEGER ul; - ul.QuadPart = cast(ulong)stdTime - hnsecsFrom1601; + testBad("31 Dec 1899 23:59:59 +0000"); + test("01 Jan 1900 00:00:00 +0000", SysTime(Date(1900, 1, 1), UTC())); + test("01 Jan 1900 00:00:00 -0000", SysTime(Date(1900, 1, 1), + new immutable SimpleTimeZone(Duration.zero))); + test("01 Jan 1900 00:00:00 -0700", SysTime(Date(1900, 1, 1), + new immutable SimpleTimeZone(dur!"hours"(-7)))); - FILETIME ft; - ft.dwHighDateTime = ul.HighPart; - ft.dwLowDateTime = ul.LowPart; + { + auto st1 = SysTime(Date(1900, 1, 1), UTC()); + auto st2 = SysTime(Date(1900, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-11))); + foreach(i; 1900 .. 2102) + { + test(format("1 Jan %05d 00:00 +0000", i), st1); + test(format("1 Jan %05d 00:00 -1100", i), st2); + st1.add!"years"(1); + st2.add!"years"(1); + } + st1.year = 9998; + st2.year = 9998; + foreach(i; 9998 .. 11_002) + { + test(format("1 Jan %05d 00:00 +0000", i), st1); + test(format("1 Jan %05d 00:00 -1100", i), st2); + st1.add!"years"(1); + st2.add!"years"(1); + } + } - return ft; - } + testBad("12 Feb 1907 23:17:09 0000"); + testBad("12 Feb 1907 23:17:09 +000"); + testBad("12 Feb 1907 23:17:09 -000"); + testBad("12 Feb 1907 23:17:09 +00000"); + testBad("12 Feb 1907 23:17:09 -00000"); + testBad("12 Feb 1907 23:17:09 +A"); + testBad("12 Feb 1907 23:17:09 +PST"); + testBad("12 Feb 1907 23:17:09 -A"); + testBad("12 Feb 1907 23:17:09 -PST"); - FILETIME SysTimeToFILETIME(SysTime sysTime) - { - return stdTimeToFILETIME(sysTime.stdTime); - } + // test trailing stuff that gets ignored + { + foreach(c; chain(iota(0, 33), ['('], iota(127, ubyte.max + 1))) + { + scope(failure) writefln("c: %d", c); + test(format("21 Dec 2012 13:14:15 +0000%c", cast(char)c), SysTime(std1, UTC())); + test(format("21 Dec 2012 13:14:15 +0000%c ", cast(char)c), SysTime(std1, UTC())); + test(format("21 Dec 2012 13:14:15 +0000%chello", cast(char)c), SysTime(std1, UTC())); + } + } - unittest - { - version(testStdDateTime) + // test trailing stuff that doesn't get ignored { - SYSTEMTIME st = void; - GetSystemTime(&st); + foreach(c; chain(iota(33, '('), iota('(' + 1, 127))) + { + scope(failure) writefln("c: %d", c); + testBad(format("21 Dec 2012 13:14:15 +0000%c", cast(char)c)); + testBad(format("21 Dec 2012 13:14:15 +0000%c ", cast(char)c)); + testBad(format("21 Dec 2012 13:14:15 +0000%chello", cast(char)c)); + } + } + + testBad("32 Jan 2012 12:13:14 -0800"); + testBad("31 Jan 2012 24:13:14 -0800"); + testBad("31 Jan 2012 12:60:14 -0800"); + testBad("31 Jan 2012 12:13:61 -0800"); + testBad("31 Jan 2012 12:13:14 -0860"); + test("31 Jan 2012 12:13:14 -0859", + SysTime(DateTime(2012, 1, 31, 12, 13, 14), + new immutable SimpleTimeZone(dur!"hours"(-8) + dur!"minutes"(-59)))); - FILETIME ft = void; - SystemTimeToFileTime(&st, &ft); - auto sysTime = FILETIMEToSysTime(&ft, UTC()); + // leap-seconds + test("21 Dec 2012 15:59:60 -0800", SysTime(DateTime(2012, 12, 21, 15, 59, 59), pst)); - FILETIME result = SysTimeToFILETIME(sysTime); + // FWS + test("Sun,4 Jul 1976 05:04 +0930", SysTime(dst2, cstStd)); + test("Sun,4 Jul 1976 05:04:22 +0930", SysTime(dst1, cstStd)); + test("Sun,4 Jul 1976 05:04 +0930 (foo)", SysTime(dst2, cstStd)); + test("Sun,4 Jul 1976 05:04:22 +0930 (foo)", SysTime(dst1, cstStd)); + test("Sun,4 \r\n Jul \r\n 1976 \r\n 05:04 \r\n +0930 \r\n (foo)", SysTime(dst2, cstStd)); + test("Sun,4 \r\n Jul \r\n 1976 \r\n 05:04:22 \r\n +0930 \r\n (foo)", SysTime(dst1, cstStd)); - _assertPred!"=="(ft.dwLowDateTime, result.dwLowDateTime); - _assertPred!"=="(ft.dwHighDateTime, result.dwHighDateTime); + auto str = "01 Jan 2012 12:13:14 -0800 "; + test(str, SysTime(DateTime(2012, 1, 1, 12, 13, 14), new immutable SimpleTimeZone(hours(-8)))); + foreach(i; 0 .. str.length) + { + auto currStr = str.dup; + currStr[i] = 'x'; + scope(failure) writefln("failed: %s", currStr); + testBad(cast(string)currStr); + } + foreach(i; 2 .. str.length) + { + auto currStr = str[0 .. $ - i]; + scope(failure) writefln("failed: %s", currStr); + testBad(cast(string)currStr); + testBad((cast(string)currStr) ~ " "); } } } +// Obsolete Format per section 4.3 of RFC 5322. +unittest +{ + import std.typetuple : TypeTuple; -/++ - Type representing the DOS file date/time format. - +/ -alias uint DosFileTime; + auto std1 = SysTime(DateTime(2012, 12, 21, 13, 14, 15), UTC()); + auto std2 = SysTime(DateTime(2012, 12, 21, 13, 14, 0), UTC()); + auto std3 = SysTime(DateTime(1912, 12, 21, 13, 14, 15), UTC()); + auto std4 = SysTime(DateTime(1912, 12, 21, 13, 14, 0), UTC()); + auto dst1 = SysTime(DateTime(1976, 7, 4, 5, 4, 22), UTC()); + auto dst2 = SysTime(DateTime(1976, 7, 4, 5, 4, 0), UTC()); + auto tooLate1 = SysTime(Date(10_000, 1, 1), UTC()); + auto tooLate2 = SysTime(DateTime(12_007, 12, 31, 12, 22, 19), UTC()); -/++ - Converts from DOS file date/time to $(LREF SysTime). + foreach(cr; TypeTuple!(function(string a){return cast(char[])a;}, + function(string a){return cast(ubyte[])a;}, + function(string a){return a;}, + function(string a){return map!(b => cast(char)b)(a.representation);})) + { + scope(failure) writeln(typeof(cr).stringof); + alias testParse822!cr test; + { + auto list = ["", " ", " \r\n\t", "\t\r\n (hello world( frien(dog)) silly \r\n ) \t\t \r\n ()", + " \n ", "\t\n\t", " \n\t (foo) \n (bar) \r\n (baz) \n "]; - Params: - dft = The DOS file time to convert. - tz = The time zone which the DOS file time is assumed to be in. + foreach(i, cfws; list) + { + scope(failure) writefln("i: %s", i); - Throws: - $(LREF DateTimeException) if the $(D DosFileTime) is invalid. - +/ -SysTime DosFileTimeToSysTime(DosFileTime dft, immutable TimeZone tz = LocalTime()) -{ - uint dt = cast(uint)dft; + test(format("%1$s21%1$sDec%1$s2012%1$s13:14:15%1$s+0000%1$s", cfws), std1); + test(format("%1$s21%1$sDec%1$s2012%1$s13:14%1$s+0000%1$s", cfws), std2); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s2012%1$s13:14%1$s+0000%1$s", cfws), std2); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s2012%1$s13:14:15%1$s+0000%1$s", cfws), std1); - if(dt == 0) - throw new DateTimeException("Invalid DosFileTime."); + test(format("%1$s04%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s04%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s1976%1$s05:04:22 +0000%1$s", cfws), dst1); - int year = ((dt >> 25) & 0x7F) + 1980; - int month = ((dt >> 21) & 0x0F); // 1..12 - int dayOfMonth = ((dt >> 16) & 0x1F); // 1..31 - int hour = (dt >> 11) & 0x1F; // 0..23 - int minute = (dt >> 5) & 0x3F; // 0..59 - int second = (dt << 1) & 0x3E; // 0..58 (in 2 second increments) + test(format("%1$s4%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s4%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s1976%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s1976%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - SysTime sysTime = void; + test(format("%1$s21%1$sDec%1$s12%1$s13:14:15%1$s+0000%1$s", cfws), std1); + test(format("%1$s21%1$sDec%1$s12%1$s13:14%1$s+0000%1$s", cfws), std2); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s12%1$s13:14%1$s+0000%1$s", cfws), std2); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s12%1$s13:14:15%1$s+0000%1$s", cfws), std1); - try - return SysTime(DateTime(year, month, dayOfMonth, hour, minute, second), tz); - catch(DateTimeException dte) - throw new DateTimeException("Invalid DosFileTime", __FILE__, __LINE__, dte); -} + test(format("%1$s04%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s04%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); -unittest -{ - version(testStdDateTime) - { - _assertPred!"=="(DosFileTimeToSysTime(0b00000000001000010000000000000000), - SysTime(DateTime(1980, 1, 1, 0, 0, 0))); + test(format("%1$s4%1$sJul%1$s76 05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s4%1$sJul%1$s76 05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s76%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s76%1$s05:04:22%1$s+0000%1$s", cfws), dst1); - _assertPred!"=="(DosFileTimeToSysTime(0b11111111100111111011111101111101), - SysTime(DateTime(2107, 12, 31, 23, 59, 58))); + test(format("%1$s21%1$sDec%1$s012%1$s13:14:15%1$s+0000%1$s", cfws), std3); + test(format("%1$s21%1$sDec%1$s012%1$s13:14%1$s+0000%1$s", cfws), std4); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s012%1$s13:14%1$s+0000%1$s", cfws), std4); + test(format("%1$sFri%1$s,%1$s21%1$sDec%1$s012%1$s13:14:15%1$s+0000%1$s", cfws), std3); - _assertPred!"=="(DosFileTimeToSysTime(0x3E3F8456), - SysTime(DateTime(2011, 1, 31, 16, 34, 44))); - } -} + test(format("%1$s04%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s04%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s04%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s4%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); + test(format("%1$s4%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s076%1$s05:04%1$s+0000%1$s", cfws), dst2); + test(format("%1$sSun%1$s,%1$s4%1$sJul%1$s076%1$s05:04:22%1$s+0000%1$s", cfws), dst1); -/++ - Converts from $(LREF SysTime) to DOS file date/time. + test(format("%1$s1%1$sJan%1$s10000%1$s00:00:00%1$s+0000%1$s", cfws), tooLate1); + test(format("%1$s31%1$sDec%1$s12007%1$s12:22:19%1$s+0000%1$s", cfws), tooLate2); + test(format("%1$sSat%1$s,%1$s1%1$sJan%1$s10000%1$s00:00:00%1$s+0000%1$s", cfws), tooLate1); + test(format("%1$sSun%1$s,%1$s31%1$sDec%1$s12007%1$s12:22:19%1$s+0000%1$s", cfws), tooLate2); + } + } - Params: - sysTime = The $(LREF SysTime) to convert. + // test years of 1, 2, and 3 digits. + { + auto st1 = SysTime(Date(2000, 1, 1), UTC()); + auto st2 = SysTime(Date(2000, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-12))); + foreach(i; 0 .. 50) + { + test(format("1 Jan %02d 00:00 GMT", i), st1); + test(format("1 Jan %02d 00:00 -1200", i), st2); + st1.add!"years"(1); + st2.add!"years"(1); + } + } - Throws: - $(LREF DateTimeException) if the given $(LREF SysTime) cannot be converted to - a $(D DosFileTime). - +/ -DosFileTime SysTimeToDosFileTime(SysTime sysTime) -{ - auto dateTime = cast(DateTime)sysTime; + { + auto st1 = SysTime(Date(1950, 1, 1), UTC()); + auto st2 = SysTime(Date(1950, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-12))); + foreach(i; 50 .. 100) + { + test(format("1 Jan %02d 00:00 GMT", i), st1); + test(format("1 Jan %02d 00:00 -1200", i), st2); + st1.add!"years"(1); + st2.add!"years"(1); + } + } - if(dateTime.year < 1980) - throw new DateTimeException("DOS File Times cannot hold dates prior to 1980."); + { + auto st1 = SysTime(Date(1900, 1, 1), UTC()); + auto st2 = SysTime(Date(1900, 1, 1), new immutable SimpleTimeZone(dur!"hours"(-11))); + foreach(i; 0 .. 1000) + { + test(format("1 Jan %03d 00:00 GMT", i), st1); + test(format("1 Jan %03d 00:00 -1100", i), st2); + st1.add!"years"(1); + st2.add!"years"(1); + } + } - if(dateTime.year > 2107) - throw new DateTimeException("DOS File Times cannot hold dates passed 2107."); + foreach(i; 0 .. 10) + { + auto str1 = cr(format("1 Jan %d 00:00 GMT", i)); + auto str2 = cr(format("1 Jan %d 00:00 -1200", i)); + assertThrown!DateTimeException(parseRFC822DateTime(str1)); + assertThrown!DateTimeException(parseRFC822DateTime(str1)); + } - uint retval = 0; - retval = (dateTime.year - 1980) << 25; - retval |= (dateTime.month & 0x0F) << 21; - retval |= (dateTime.day & 0x1F) << 16; - retval |= (dateTime.hour & 0x1F) << 11; - retval |= (dateTime.minute & 0x3F) << 5; - retval |= (dateTime.second >> 1) & 0x1F; + // test time zones + { + auto dt = DateTime(1982, 05, 03, 12, 22, 04); + test("Wed, 03 May 1982 12:22:04 UT", SysTime(dt, UTC())); + test("Wed, 03 May 1982 12:22:04 GMT", SysTime(dt, UTC())); + test("Wed, 03 May 1982 12:22:04 EST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-5)))); + test("Wed, 03 May 1982 12:22:04 EDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-4)))); + test("Wed, 03 May 1982 12:22:04 CST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-6)))); + test("Wed, 03 May 1982 12:22:04 CDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-5)))); + test("Wed, 03 May 1982 12:22:04 MST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-7)))); + test("Wed, 03 May 1982 12:22:04 MDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-6)))); + test("Wed, 03 May 1982 12:22:04 PST", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-8)))); + test("Wed, 03 May 1982 12:22:04 PDT", SysTime(dt, new immutable SimpleTimeZone(dur!"hours"(-7)))); - return cast(DosFileTime)retval; -} + auto badTZ = new immutable SimpleTimeZone(Duration.zero); + foreach(dchar c; filter!(a => a != 'j' && a != 'J')(letters)) + { + scope(failure) writefln("c: %s", c); + test(format("Wed, 03 May 1982 12:22:04 %s", c), SysTime(dt, badTZ)); + test(format("Wed, 03 May 1982 12:22:04%s", c), SysTime(dt, badTZ)); + } -unittest -{ - version(testStdDateTime) - { - _assertPred!"=="(SysTimeToDosFileTime(SysTime(DateTime(1980, 1, 1, 0, 0, 0))), - 0b00000000001000010000000000000000); + foreach(dchar c; ['j', 'J']) + { + scope(failure) writefln("c: %s", c); + assertThrown!DateTimeException(parseRFC822DateTime(cr(format("Wed, 03 May 1982 12:22:04 %s", c)))); + assertThrown!DateTimeException(parseRFC822DateTime(cr(format("Wed, 03 May 1982 12:22:04%s", c)))); + } + + foreach(string s; ["AAA", "GQW", "DDT", "PDA", "GT", "GM"]) + { + scope(failure) writefln("s: %s", s); + test(format("Wed, 03 May 1982 12:22:04 %s", s), SysTime(dt, badTZ)); + } + + // test trailing stuff that gets ignored + { + foreach(c; chain(iota(0, 33), ['('], iota(127, ubyte.max + 1))) + { + scope(failure) writefln("c: %d", c); + test(format("21Dec1213:14:15+0000%c", cast(char)c), std1); + test(format("21Dec1213:14:15+0000%c ", cast(char)c), std1); + test(format("21Dec1213:14:15+0000%chello", cast(char)c), std1); + } + } + + // test trailing stuff that doesn't get ignored + { + foreach(c; chain(iota(33, '('), iota('(' + 1, 127))) + { + scope(failure) writefln("c: %d", c); + assertThrown!DateTimeException(parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%c", cast(char)c)))); + assertThrown!DateTimeException(parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%c ", cast(char)c)))); + assertThrown!DateTimeException(parseRFC822DateTime(cr(format("21Dec1213:14:15+0000%chello", cast(char)c)))); + } + } + } - _assertPred!"=="(SysTimeToDosFileTime(SysTime(DateTime(2107, 12, 31, 23, 59, 58))), - 0b11111111100111111011111101111101); + // test that the checks for minimum length work correctly and avoid + // any RangeErrors. + test("7Dec1200:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), + new immutable SimpleTimeZone(Duration.zero))); + test("Fri,7Dec1200:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), + new immutable SimpleTimeZone(Duration.zero))); + test("7Dec1200:00:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), + new immutable SimpleTimeZone(Duration.zero))); + test("Fri,7Dec1200:00:00A", SysTime(DateTime(2012, 12, 7, 00, 00, 00), + new immutable SimpleTimeZone(Duration.zero))); - _assertPred!"=="(SysTimeToDosFileTime(SysTime(DateTime(2011, 1, 31, 16, 34, 44))), - 0x3E3F8456); + auto tooShortMsg = collectExceptionMsg!DateTimeException(parseRFC822DateTime("")); + foreach(str; ["Fri,7Dec1200:00:00", "7Dec1200:00:00"]) + { + foreach(i; 0 .. str.length) + { + auto value = str[0 .. $ - i]; + scope(failure) writeln(value); + assert(collectExceptionMsg!DateTimeException(parseRFC822DateTime(value)) == tooShortMsg); + } + } } } - /++ Whether all of the given strings are valid units of time. @@ -31778,11 +30964,11 @@ unittest can handle precision greater than hnsecs, and the few functions in core.time which deal with "nsecs" deal with it explicitly. +/ -bool validTimeUnits(string[] units...) +bool validTimeUnits(string[] units...) @safe pure nothrow { foreach(str; units) { - if(!canFind(timeStrings.dup, str)) + if(!canFind(timeStrings[], str)) return false; } @@ -31805,7 +30991,7 @@ bool validTimeUnits(string[] units...) $(LREF DateTimeException) if either of the given strings is not a valid time unit string. +/ -int cmpTimeUnits(string lhs, string rhs) +int cmpTimeUnits(string lhs, string rhs) @safe pure { auto tstrings = timeStrings.dup; immutable indexOfLHS = countUntil(tstrings, lhs); @@ -31824,22 +31010,19 @@ int cmpTimeUnits(string lhs, string rhs) unittest { - version(testStdDateTime) + foreach(i, outerUnits; timeStrings) { - foreach(i, outerUnits; timeStrings) - { - _assertPred!"=="(cmpTimeUnits(outerUnits, outerUnits), 0); + assert(cmpTimeUnits(outerUnits, outerUnits) == 0); - //For some reason, $ won't compile. - foreach(innerUnits; timeStrings[i+1 .. timeStrings.length]) - _assertPred!"=="(cmpTimeUnits(outerUnits, innerUnits), -1); - } + //For some reason, $ won't compile. + foreach(innerUnits; timeStrings[i+1 .. timeStrings.length]) + assert(cmpTimeUnits(outerUnits, innerUnits) == -1); + } - foreach(i, outerUnits; timeStrings) - { - foreach(innerUnits; timeStrings[0 .. i]) - _assertPred!"=="(cmpTimeUnits(outerUnits, innerUnits), 1); - } + foreach(i, outerUnits; timeStrings) + { + foreach(innerUnits; timeStrings[0 .. i]) + assert(cmpTimeUnits(outerUnits, innerUnits) == 1); } } @@ -31870,7 +31053,7 @@ template CmpTimeUnits(string lhs, string rhs) /+ Helper function for $(D CmpTimeUnits). +/ -private int cmpTimeUnitsCTFE(string lhs, string rhs) +private int cmpTimeUnitsCTFE(string lhs, string rhs) @safe pure nothrow { auto tstrings = timeStrings.dup; immutable indexOfLHS = countUntil(tstrings, lhs); @@ -31886,34 +31069,25 @@ private int cmpTimeUnitsCTFE(string lhs, string rhs) unittest { - version(testStdDateTime) - { - static string genTest(size_t index) - { - auto currUnits = timeStrings[index]; - auto test = `_assertPred!"=="(CmpTimeUnits!("` ~ currUnits ~ `", "` ~ currUnits ~ `"), 0); `; + import std.typetuple : TypeTuple; - //For some reason, $ won't compile. - foreach(units; timeStrings[index + 1 .. timeStrings.length]) - test ~= `_assertPred!"=="(CmpTimeUnits!("` ~ currUnits ~ `", "` ~ units ~ `"), -1); `; + static string genTest(size_t index) + { + auto currUnits = timeStrings[index]; + auto test = format(`assert(CmpTimeUnits!("%s", "%s") == 0);`, currUnits, currUnits); - foreach(units; timeStrings[0 .. index]) - test ~= `_assertPred!"=="(CmpTimeUnits!("` ~ currUnits ~ `", "` ~ units ~ `"), 1); `; + foreach(units; timeStrings[index + 1 .. $]) + test ~= format(`assert(CmpTimeUnits!("%s", "%s") == -1);`, currUnits, units); - return test; - } + foreach(units; timeStrings[0 .. index]) + test ~= format(`assert(CmpTimeUnits!("%s", "%s") == 1);`, currUnits, units); - mixin(genTest(0)); - mixin(genTest(1)); - mixin(genTest(2)); - mixin(genTest(3)); - mixin(genTest(4)); - mixin(genTest(5)); - mixin(genTest(6)); - mixin(genTest(7)); - mixin(genTest(8)); - mixin(genTest(9)); + return test; } + + static assert(timeStrings.length == 10); + foreach(n; TypeTuple!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)) + mixin(genTest(n)); } @@ -31926,16 +31100,8 @@ unittest Params: units = The units of time to validate. value = The number to validate. - - Examples: --------------------- -assert(valid!"hours"(12)); -assert(!valid!"hours"(32)); -assert(valid!"months"(12)); -assert(!valid!"months"(13)); --------------------- +/ -bool valid(string units)(int value) pure nothrow +bool valid(string units)(int value) @safe pure nothrow if(units == "months" || units == "hours" || units == "minutes" || @@ -31951,16 +31117,13 @@ bool valid(string units)(int value) pure nothrow return value >= 0 && value <= TimeOfDay.maxSecond; } +/// unittest { - version(testStdDateTime) - { - //Verify Examples. - assert(valid!"hours"(12)); - assert(!valid!"hours"(32)); - assert(valid!"months"(12)); - assert(!valid!"months"(13)); - } + assert(valid!"hours"(12)); + assert(!valid!"hours"(32)); + assert(valid!"months"(12)); + assert(!valid!"months"(13)); } @@ -31973,7 +31136,7 @@ unittest month = The month of the day to validate. day = The day to validate. +/ -bool valid(string units)(int year, int month, int day) pure nothrow +bool valid(string units)(int year, int month, int day) @safe pure nothrow if(units == "days") { return day > 0 && day <= maxDay(year, month); @@ -31991,7 +31154,7 @@ bool valid(string units)(int year, int month, int day) pure nothrow Throws: $(LREF DateTimeException) if $(D valid!units(value)) is false. +/ -void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) pure +void enforceValid(string units)(int value, string file = __FILE__, size_t line = __LINE__) @safe pure if(units == "months" || units == "hours" || units == "minutes" || @@ -32000,22 +31163,22 @@ void enforceValid(string units)(int value, string file = __FILE__, size_t line = static if(units == "months") { if(!valid!units(value)) - throw new DateTimeException(numToString(value) ~ " is not a valid month of the year.", file, line); + throw new DateTimeException(format("%s is not a valid month of the year.", value), file, line); } else static if(units == "hours") { if(!valid!units(value)) - throw new DateTimeException(numToString(value) ~ " is not a valid hour of the day.", file, line); + throw new DateTimeException(format("%s is not a valid hour of the day.", value), file, line); } else static if(units == "minutes") { if(!valid!units(value)) - throw new DateTimeException(numToString(value) ~ " is not a valid minute of an hour.", file, line); + throw new DateTimeException(format("%s is not a valid minute of an hour.", value), file, line); } else static if(units == "seconds") { if(!valid!units(value)) - throw new DateTimeException(numToString(value) ~ " is not a valid second of a minute.", file, line); + throw new DateTimeException(format("%s is not a valid second of a minute.", value), file, line); } } @@ -32033,17 +31196,12 @@ void enforceValid(string units)(int value, string file = __FILE__, size_t line = Throws: $(LREF DateTimeException) if $(D valid!"days"(year, month, day)) is false. +/ -void enforceValid(string units)(int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) pure +void enforceValid(string units) + (int year, Month month, int day, string file = __FILE__, size_t line = __LINE__) @safe pure if(units == "days") { if(!valid!"days"(year, month, day)) - { - throw new DateTimeException(numToString(day) ~ - " is not a valid day in " ~ - monthToString(month) ~ - " in " ~ - numToString(year), file, line); - } + throw new DateTimeException(format("%s is not a valid day in %s in %s", day, month, year), file, line); } @@ -32055,7 +31213,7 @@ void enforceValid(string units)(int year, Month month, int day, string file = __ currMonth = The current month of the year. month = The month of the year to get the number of months to. +/ -static int monthsToMonth(int currMonth, int month) pure +static int monthsToMonth(int currMonth, int month) @safe pure { enforceValid!"months"(currMonth); enforceValid!"months"(month); @@ -32071,60 +31229,57 @@ static int monthsToMonth(int currMonth, int month) pure unittest { - version(testStdDateTime) - { - _assertPred!"=="(monthsToMonth(Month.jan, Month.jan), 0); - _assertPred!"=="(monthsToMonth(Month.jan, Month.feb), 1); - _assertPred!"=="(monthsToMonth(Month.jan, Month.mar), 2); - _assertPred!"=="(monthsToMonth(Month.jan, Month.apr), 3); - _assertPred!"=="(monthsToMonth(Month.jan, Month.may), 4); - _assertPred!"=="(monthsToMonth(Month.jan, Month.jun), 5); - _assertPred!"=="(monthsToMonth(Month.jan, Month.jul), 6); - _assertPred!"=="(monthsToMonth(Month.jan, Month.aug), 7); - _assertPred!"=="(monthsToMonth(Month.jan, Month.sep), 8); - _assertPred!"=="(monthsToMonth(Month.jan, Month.oct), 9); - _assertPred!"=="(monthsToMonth(Month.jan, Month.nov), 10); - _assertPred!"=="(monthsToMonth(Month.jan, Month.dec), 11); - - _assertPred!"=="(monthsToMonth(Month.may, Month.jan), 8); - _assertPred!"=="(monthsToMonth(Month.may, Month.feb), 9); - _assertPred!"=="(monthsToMonth(Month.may, Month.mar), 10); - _assertPred!"=="(monthsToMonth(Month.may, Month.apr), 11); - _assertPred!"=="(monthsToMonth(Month.may, Month.may), 0); - _assertPred!"=="(monthsToMonth(Month.may, Month.jun), 1); - _assertPred!"=="(monthsToMonth(Month.may, Month.jul), 2); - _assertPred!"=="(monthsToMonth(Month.may, Month.aug), 3); - _assertPred!"=="(monthsToMonth(Month.may, Month.sep), 4); - _assertPred!"=="(monthsToMonth(Month.may, Month.oct), 5); - _assertPred!"=="(monthsToMonth(Month.may, Month.nov), 6); - _assertPred!"=="(monthsToMonth(Month.may, Month.dec), 7); - - _assertPred!"=="(monthsToMonth(Month.oct, Month.jan), 3); - _assertPred!"=="(monthsToMonth(Month.oct, Month.feb), 4); - _assertPred!"=="(monthsToMonth(Month.oct, Month.mar), 5); - _assertPred!"=="(monthsToMonth(Month.oct, Month.apr), 6); - _assertPred!"=="(monthsToMonth(Month.oct, Month.may), 7); - _assertPred!"=="(monthsToMonth(Month.oct, Month.jun), 8); - _assertPred!"=="(monthsToMonth(Month.oct, Month.jul), 9); - _assertPred!"=="(monthsToMonth(Month.oct, Month.aug), 10); - _assertPred!"=="(monthsToMonth(Month.oct, Month.sep), 11); - _assertPred!"=="(monthsToMonth(Month.oct, Month.oct), 0); - _assertPred!"=="(monthsToMonth(Month.oct, Month.nov), 1); - _assertPred!"=="(monthsToMonth(Month.oct, Month.dec), 2); - - _assertPred!"=="(monthsToMonth(Month.dec, Month.jan), 1); - _assertPred!"=="(monthsToMonth(Month.dec, Month.feb), 2); - _assertPred!"=="(monthsToMonth(Month.dec, Month.mar), 3); - _assertPred!"=="(monthsToMonth(Month.dec, Month.apr), 4); - _assertPred!"=="(monthsToMonth(Month.dec, Month.may), 5); - _assertPred!"=="(monthsToMonth(Month.dec, Month.jun), 6); - _assertPred!"=="(monthsToMonth(Month.dec, Month.jul), 7); - _assertPred!"=="(monthsToMonth(Month.dec, Month.aug), 8); - _assertPred!"=="(monthsToMonth(Month.dec, Month.sep), 9); - _assertPred!"=="(monthsToMonth(Month.dec, Month.oct), 10); - _assertPred!"=="(monthsToMonth(Month.dec, Month.nov), 11); - _assertPred!"=="(monthsToMonth(Month.dec, Month.dec), 0); - } + assert(monthsToMonth(Month.jan, Month.jan) == 0); + assert(monthsToMonth(Month.jan, Month.feb) == 1); + assert(monthsToMonth(Month.jan, Month.mar) == 2); + assert(monthsToMonth(Month.jan, Month.apr) == 3); + assert(monthsToMonth(Month.jan, Month.may) == 4); + assert(monthsToMonth(Month.jan, Month.jun) == 5); + assert(monthsToMonth(Month.jan, Month.jul) == 6); + assert(monthsToMonth(Month.jan, Month.aug) == 7); + assert(monthsToMonth(Month.jan, Month.sep) == 8); + assert(monthsToMonth(Month.jan, Month.oct) == 9); + assert(monthsToMonth(Month.jan, Month.nov) == 10); + assert(monthsToMonth(Month.jan, Month.dec) == 11); + + assert(monthsToMonth(Month.may, Month.jan) == 8); + assert(monthsToMonth(Month.may, Month.feb) == 9); + assert(monthsToMonth(Month.may, Month.mar) == 10); + assert(monthsToMonth(Month.may, Month.apr) == 11); + assert(monthsToMonth(Month.may, Month.may) == 0); + assert(monthsToMonth(Month.may, Month.jun) == 1); + assert(monthsToMonth(Month.may, Month.jul) == 2); + assert(monthsToMonth(Month.may, Month.aug) == 3); + assert(monthsToMonth(Month.may, Month.sep) == 4); + assert(monthsToMonth(Month.may, Month.oct) == 5); + assert(monthsToMonth(Month.may, Month.nov) == 6); + assert(monthsToMonth(Month.may, Month.dec) == 7); + + assert(monthsToMonth(Month.oct, Month.jan) == 3); + assert(monthsToMonth(Month.oct, Month.feb) == 4); + assert(monthsToMonth(Month.oct, Month.mar) == 5); + assert(monthsToMonth(Month.oct, Month.apr) == 6); + assert(monthsToMonth(Month.oct, Month.may) == 7); + assert(monthsToMonth(Month.oct, Month.jun) == 8); + assert(monthsToMonth(Month.oct, Month.jul) == 9); + assert(monthsToMonth(Month.oct, Month.aug) == 10); + assert(monthsToMonth(Month.oct, Month.sep) == 11); + assert(monthsToMonth(Month.oct, Month.oct) == 0); + assert(monthsToMonth(Month.oct, Month.nov) == 1); + assert(monthsToMonth(Month.oct, Month.dec) == 2); + + assert(monthsToMonth(Month.dec, Month.jan) == 1); + assert(monthsToMonth(Month.dec, Month.feb) == 2); + assert(monthsToMonth(Month.dec, Month.mar) == 3); + assert(monthsToMonth(Month.dec, Month.apr) == 4); + assert(monthsToMonth(Month.dec, Month.may) == 5); + assert(monthsToMonth(Month.dec, Month.jun) == 6); + assert(monthsToMonth(Month.dec, Month.jul) == 7); + assert(monthsToMonth(Month.dec, Month.aug) == 8); + assert(monthsToMonth(Month.dec, Month.sep) == 9); + assert(monthsToMonth(Month.dec, Month.oct) == 10); + assert(monthsToMonth(Month.dec, Month.nov) == 11); + assert(monthsToMonth(Month.dec, Month.dec) == 0); } @@ -32136,7 +31291,7 @@ unittest currDoW = The current day of the week. dow = The day of the week to get the number of days to. +/ -static int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) pure nothrow +static int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) @safe pure nothrow { if(currDoW == dow) return 0; @@ -32149,64 +31304,61 @@ static int daysToDayOfWeek(DayOfWeek currDoW, DayOfWeek dow) pure nothrow unittest { - version(testStdDateTime) - { - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun), 0); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon), 1); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue), 2); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed), 3); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu), 4); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri), 5); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat), 6); - - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun), 6); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon), 0); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue), 1); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed), 2); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu), 3); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri), 4); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat), 5); - - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun), 5); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon), 6); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue), 0); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed), 1); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu), 2); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri), 3); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat), 4); - - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun), 4); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon), 5); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue), 6); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed), 0); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu), 1); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri), 2); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat), 3); - - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun), 3); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon), 4); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue), 5); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed), 6); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu), 0); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri), 1); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat), 2); - - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun), 2); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon), 3); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue), 4); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed), 5); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu), 6); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri), 0); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat), 1); - - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun), 1); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon), 2); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue), 3); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed), 4); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu), 5); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri), 6); - _assertPred!"=="(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat), 0); - } + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sun) == 0); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.mon) == 1); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.tue) == 2); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.wed) == 3); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.thu) == 4); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.fri) == 5); + assert(daysToDayOfWeek(DayOfWeek.sun, DayOfWeek.sat) == 6); + + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.tue) == 1); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.thu) == 3); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.fri) == 4); + assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sat) == 5); + + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sun) == 5); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.mon) == 6); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.tue) == 0); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.wed) == 1); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.thu) == 2); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.fri) == 3); + assert(daysToDayOfWeek(DayOfWeek.tue, DayOfWeek.sat) == 4); + + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sun) == 4); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.mon) == 5); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.tue) == 6); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.wed) == 0); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.thu) == 1); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.fri) == 2); + assert(daysToDayOfWeek(DayOfWeek.wed, DayOfWeek.sat) == 3); + + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sun) == 3); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.mon) == 4); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.tue) == 5); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.wed) == 6); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.thu) == 0); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.fri) == 1); + assert(daysToDayOfWeek(DayOfWeek.thu, DayOfWeek.sat) == 2); + + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sun) == 2); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.mon) == 3); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.tue) == 4); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.wed) == 5); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.thu) == 6); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.fri) == 0); + assert(daysToDayOfWeek(DayOfWeek.fri, DayOfWeek.sat) == 1); + + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sun) == 1); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.mon) == 2); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.tue) == 3); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.wed) == 4); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.thu) == 5); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.fri) == 6); + assert(daysToDayOfWeek(DayOfWeek.sat, DayOfWeek.sat) == 0); } @@ -32222,13 +31374,29 @@ version(StdDdoc) Examples: -------------------- -writeln("benchmark start!"); { -auto mt = measureTime!((a){assert(a.seconds);}); -doSomething(); + auto mt = measureTime!((TickDuration a) + { /+ do something when the scope is exited +/ }); + // do something that needs to be timed +} +-------------------- + + which is functionally equivalent to + +-------------------- +{ + auto sw = StopWatch(AutoStart.yes); + scope(exit) + { + TickDuration a = sw.peek(); + /+ do something when the scope is exited +/ + } + // do something that needs to be timed } -writeln("benchmark end!"); -------------------- + + See_Also: + $(LREF benchmark) +/ auto measureTime(alias func)(); } @@ -32271,7 +31439,27 @@ else } } -version(testStdDateTime) @safe unittest +// Verify Example. +unittest +{ + { + auto mt = measureTime!((TickDuration a) + { /+ do something when the scope is exited +/ }); + // do something that needs to be timed + } + + { + auto sw = StopWatch(AutoStart.yes); + scope(exit) + { + TickDuration a = sw.peek(); + /+ do something when the scope is exited +/ + } + // do something that needs to be timed + } +} + +@safe unittest { @safe static void func(TickDuration td) { @@ -32289,7 +31477,7 @@ version(testStdDateTime) @safe unittest +/ } -version(testStdDateTime) unittest +unittest { static void func(TickDuration td) { @@ -32308,7 +31496,7 @@ version(testStdDateTime) unittest } //Bug# 8450 -version(testStdDateTime) unittest +unittest { @safe void safeFunc() {} @trusted void trustFunc() {} @@ -32327,11 +31515,37 @@ private: // Section with private enums and constants. //============================================================================== -enum daysInYear = 365; /// The number of days in a non-leap year. -enum daysInLeapYear = 366; /// The numbef or days in a leap year. +enum daysInYear = 365; // The number of days in a non-leap year. +enum daysInLeapYear = 366; // The numbef or days in a leap year. enum daysIn4Years = daysInYear * 3 + daysInLeapYear; /// Number of days in 4 years. -enum daysIn100Years = daysIn4Years * 25 - 1; /// The number of days in 100 years. -enum daysIn400Years = daysIn100Years * 4 + 1; /// The number of days in 400 years. +enum daysIn100Years = daysIn4Years * 25 - 1; // The number of days in 100 years. +enum daysIn400Years = daysIn100Years * 4 + 1; // The number of days in 400 years. + +/+ + Array of integers representing the last days of each month in a year. + +/ +immutable int[13] lastDayNonLeap = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]; + +/+ + Array of integers representing the last days of each month in a leap year. + +/ +immutable int[13] lastDayLeap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]; + +/+ + Array of the short (three letter) names of each month. + +/ +immutable string[12] _monthNames = ["Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec"]; //============================================================================== @@ -32375,20 +31589,8 @@ template hnsecsPer(string units) Returns: The number of the given units from converting hnsecs to those units. - - Examples: --------------------- -auto hnsecs = 2595000000007L; -immutable days = splitUnitsFromHNSecs!"days"(hnsecs); -assert(days == 3); -assert(hnsecs == 3000000007); - -immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs); -assert(minutes == 5); -assert(hnsecs == 7); --------------------- +/ -long splitUnitsFromHNSecs(string units)(ref long hnsecs) pure nothrow +long splitUnitsFromHNSecs(string units)(ref long hnsecs) @safe pure nothrow if(validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0) { @@ -32400,18 +31602,14 @@ long splitUnitsFromHNSecs(string units)(ref long hnsecs) pure nothrow unittest { - version(testStdDateTime) - { - //Verify Example. - auto hnsecs = 2595000000007L; - immutable days = splitUnitsFromHNSecs!"days"(hnsecs); - assert(days == 3); - assert(hnsecs == 3000000007); - - immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs); - assert(minutes == 5); - assert(hnsecs == 7); - } + auto hnsecs = 2595000000007L; + immutable days = splitUnitsFromHNSecs!"days"(hnsecs); + assert(days == 3); + assert(hnsecs == 3000000007); + + immutable minutes = splitUnitsFromHNSecs!"minutes"(hnsecs); + assert(minutes == 5); + assert(hnsecs == 7); } @@ -32428,16 +31626,8 @@ unittest Returns: The split out value. - - Examples: --------------------- -auto hnsecs = 2595000000007L; -immutable days = getUnitsFromHNSecs!"days"(hnsecs); -assert(days == 3); -assert(hnsecs == 2595000000007L); --------------------- +/ -long getUnitsFromHNSecs(string units)(long hnsecs) pure nothrow +long getUnitsFromHNSecs(string units)(long hnsecs) @safe pure nothrow if(validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0) { @@ -32446,14 +31636,10 @@ long getUnitsFromHNSecs(string units)(long hnsecs) pure nothrow unittest { - version(testStdDateTime) - { - //Verify Example. - auto hnsecs = 2595000000007L; - immutable days = getUnitsFromHNSecs!"days"(hnsecs); - assert(days == 3); - assert(hnsecs == 2595000000007L); - } + auto hnsecs = 2595000000007L; + immutable days = getUnitsFromHNSecs!"days"(hnsecs); + assert(days == 3); + assert(hnsecs == 2595000000007L); } @@ -32470,16 +31656,8 @@ unittest Returns: The remaining hnsecs. - - Examples: --------------------- -auto hnsecs = 2595000000007L; -auto returned = removeUnitsFromHNSecs!"days"(hnsecs); -assert(returned == 3000000007); -assert(hnsecs == 2595000000007L); --------------------- +/ -long removeUnitsFromHNSecs(string units)(long hnsecs) pure nothrow +long removeUnitsFromHNSecs(string units)(long hnsecs) @safe pure nothrow if(validTimeUnits(units) && CmpTimeUnits!(units, "months") < 0) { @@ -32490,14 +31668,10 @@ long removeUnitsFromHNSecs(string units)(long hnsecs) pure nothrow unittest { - version(testStdDateTime) - { - //Verify Example. - auto hnsecs = 2595000000007L; - auto returned = removeUnitsFromHNSecs!"days"(hnsecs); - assert(returned == 3000000007); - assert(hnsecs == 2595000000007L); - } + auto hnsecs = 2595000000007L; + auto returned = removeUnitsFromHNSecs!"days"(hnsecs); + assert(returned == 3000000007); + assert(hnsecs == 2595000000007L); } @@ -32508,7 +31682,7 @@ unittest year = The year to get the day for. month = The month of the Gregorian Calendar to get the day for. +/ -static ubyte maxDay(int year, int month) pure nothrow +static ubyte maxDay(int year, int month) @safe pure nothrow in { assert(valid!"months"(month)); @@ -32530,62 +31704,59 @@ body unittest { - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(maxDay(1999, 1), 31); - _assertPred!"=="(maxDay(1999, 2), 28); - _assertPred!"=="(maxDay(1999, 3), 31); - _assertPred!"=="(maxDay(1999, 4), 30); - _assertPred!"=="(maxDay(1999, 5), 31); - _assertPred!"=="(maxDay(1999, 6), 30); - _assertPred!"=="(maxDay(1999, 7), 31); - _assertPred!"=="(maxDay(1999, 8), 31); - _assertPred!"=="(maxDay(1999, 9), 30); - _assertPred!"=="(maxDay(1999, 10), 31); - _assertPred!"=="(maxDay(1999, 11), 30); - _assertPred!"=="(maxDay(1999, 12), 31); - - _assertPred!"=="(maxDay(2000, 1), 31); - _assertPred!"=="(maxDay(2000, 2), 29); - _assertPred!"=="(maxDay(2000, 3), 31); - _assertPred!"=="(maxDay(2000, 4), 30); - _assertPred!"=="(maxDay(2000, 5), 31); - _assertPred!"=="(maxDay(2000, 6), 30); - _assertPred!"=="(maxDay(2000, 7), 31); - _assertPred!"=="(maxDay(2000, 8), 31); - _assertPred!"=="(maxDay(2000, 9), 30); - _assertPred!"=="(maxDay(2000, 10), 31); - _assertPred!"=="(maxDay(2000, 11), 30); - _assertPred!"=="(maxDay(2000, 12), 31); - - //Test B.C. - _assertPred!"=="(maxDay(-1999, 1), 31); - _assertPred!"=="(maxDay(-1999, 2), 28); - _assertPred!"=="(maxDay(-1999, 3), 31); - _assertPred!"=="(maxDay(-1999, 4), 30); - _assertPred!"=="(maxDay(-1999, 5), 31); - _assertPred!"=="(maxDay(-1999, 6), 30); - _assertPred!"=="(maxDay(-1999, 7), 31); - _assertPred!"=="(maxDay(-1999, 8), 31); - _assertPred!"=="(maxDay(-1999, 9), 30); - _assertPred!"=="(maxDay(-1999, 10), 31); - _assertPred!"=="(maxDay(-1999, 11), 30); - _assertPred!"=="(maxDay(-1999, 12), 31); - - _assertPred!"=="(maxDay(-2000, 1), 31); - _assertPred!"=="(maxDay(-2000, 2), 29); - _assertPred!"=="(maxDay(-2000, 3), 31); - _assertPred!"=="(maxDay(-2000, 4), 30); - _assertPred!"=="(maxDay(-2000, 5), 31); - _assertPred!"=="(maxDay(-2000, 6), 30); - _assertPred!"=="(maxDay(-2000, 7), 31); - _assertPred!"=="(maxDay(-2000, 8), 31); - _assertPred!"=="(maxDay(-2000, 9), 30); - _assertPred!"=="(maxDay(-2000, 10), 31); - _assertPred!"=="(maxDay(-2000, 11), 30); - _assertPred!"=="(maxDay(-2000, 12), 31); - } + //Test A.D. + assert(maxDay(1999, 1) == 31); + assert(maxDay(1999, 2) == 28); + assert(maxDay(1999, 3) == 31); + assert(maxDay(1999, 4) == 30); + assert(maxDay(1999, 5) == 31); + assert(maxDay(1999, 6) == 30); + assert(maxDay(1999, 7) == 31); + assert(maxDay(1999, 8) == 31); + assert(maxDay(1999, 9) == 30); + assert(maxDay(1999, 10) == 31); + assert(maxDay(1999, 11) == 30); + assert(maxDay(1999, 12) == 31); + + assert(maxDay(2000, 1) == 31); + assert(maxDay(2000, 2) == 29); + assert(maxDay(2000, 3) == 31); + assert(maxDay(2000, 4) == 30); + assert(maxDay(2000, 5) == 31); + assert(maxDay(2000, 6) == 30); + assert(maxDay(2000, 7) == 31); + assert(maxDay(2000, 8) == 31); + assert(maxDay(2000, 9) == 30); + assert(maxDay(2000, 10) == 31); + assert(maxDay(2000, 11) == 30); + assert(maxDay(2000, 12) == 31); + + //Test B.C. + assert(maxDay(-1999, 1) == 31); + assert(maxDay(-1999, 2) == 28); + assert(maxDay(-1999, 3) == 31); + assert(maxDay(-1999, 4) == 30); + assert(maxDay(-1999, 5) == 31); + assert(maxDay(-1999, 6) == 30); + assert(maxDay(-1999, 7) == 31); + assert(maxDay(-1999, 8) == 31); + assert(maxDay(-1999, 9) == 30); + assert(maxDay(-1999, 10) == 31); + assert(maxDay(-1999, 11) == 30); + assert(maxDay(-1999, 12) == 31); + + assert(maxDay(-2000, 1) == 31); + assert(maxDay(-2000, 2) == 29); + assert(maxDay(-2000, 3) == 31); + assert(maxDay(-2000, 4) == 30); + assert(maxDay(-2000, 5) == 31); + assert(maxDay(-2000, 6) == 30); + assert(maxDay(-2000, 7) == 31); + assert(maxDay(-2000, 8) == 31); + assert(maxDay(-2000, 9) == 30); + assert(maxDay(-2000, 10) == 31); + assert(maxDay(-2000, 11) == 30); + assert(maxDay(-2000, 12) == 31); } @@ -32596,7 +31767,7 @@ unittest day = The day of the Gregorian Calendar for which to get the day of the week. +/ -DayOfWeek getDayOfWeek(int day) pure nothrow +DayOfWeek getDayOfWeek(int day) @safe pure nothrow { //January 1st, 1 A.D. was a Monday if(day >= 0) @@ -32612,121 +31783,73 @@ DayOfWeek getDayOfWeek(int day) pure nothrow } } -unittest -{ - version(testStdDateTime) - { - //Test A.D. - _assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 1)).dayOfGregorianCal), DayOfWeek.mon); - _assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 2)).dayOfGregorianCal), DayOfWeek.tue); - _assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 3)).dayOfGregorianCal), DayOfWeek.wed); - _assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 4)).dayOfGregorianCal), DayOfWeek.thu); - _assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 5)).dayOfGregorianCal), DayOfWeek.fri); - _assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 6)).dayOfGregorianCal), DayOfWeek.sat); - _assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 7)).dayOfGregorianCal), DayOfWeek.sun); - _assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 8)).dayOfGregorianCal), DayOfWeek.mon); - _assertPred!"=="(getDayOfWeek(SysTime(Date(1, 1, 9)).dayOfGregorianCal), DayOfWeek.tue); - _assertPred!"=="(getDayOfWeek(SysTime(Date(2, 1, 1)).dayOfGregorianCal), DayOfWeek.tue); - _assertPred!"=="(getDayOfWeek(SysTime(Date(3, 1, 1)).dayOfGregorianCal), DayOfWeek.wed); - _assertPred!"=="(getDayOfWeek(SysTime(Date(4, 1, 1)).dayOfGregorianCal), DayOfWeek.thu); - _assertPred!"=="(getDayOfWeek(SysTime(Date(5, 1, 1)).dayOfGregorianCal), DayOfWeek.sat); - _assertPred!"=="(getDayOfWeek(SysTime(Date(2000, 1, 1)).dayOfGregorianCal), DayOfWeek.sat); - _assertPred!"=="(getDayOfWeek(SysTime(Date(2010, 8, 22)).dayOfGregorianCal), DayOfWeek.sun); - _assertPred!"=="(getDayOfWeek(SysTime(Date(2010, 8, 23)).dayOfGregorianCal), DayOfWeek.mon); - _assertPred!"=="(getDayOfWeek(SysTime(Date(2010, 8, 24)).dayOfGregorianCal), DayOfWeek.tue); - _assertPred!"=="(getDayOfWeek(SysTime(Date(2010, 8, 25)).dayOfGregorianCal), DayOfWeek.wed); - _assertPred!"=="(getDayOfWeek(SysTime(Date(2010, 8, 26)).dayOfGregorianCal), DayOfWeek.thu); - _assertPred!"=="(getDayOfWeek(SysTime(Date(2010, 8, 27)).dayOfGregorianCal), DayOfWeek.fri); - _assertPred!"=="(getDayOfWeek(SysTime(Date(2010, 8, 28)).dayOfGregorianCal), DayOfWeek.sat); - _assertPred!"=="(getDayOfWeek(SysTime(Date(2010, 8, 29)).dayOfGregorianCal), DayOfWeek.sun); - - //Test B.C. - _assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 31)).dayOfGregorianCal), DayOfWeek.sun); - _assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 30)).dayOfGregorianCal), DayOfWeek.sat); - _assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 29)).dayOfGregorianCal), DayOfWeek.fri); - _assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 28)).dayOfGregorianCal), DayOfWeek.thu); - _assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 27)).dayOfGregorianCal), DayOfWeek.wed); - _assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 26)).dayOfGregorianCal), DayOfWeek.tue); - _assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 25)).dayOfGregorianCal), DayOfWeek.mon); - _assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 24)).dayOfGregorianCal), DayOfWeek.sun); - _assertPred!"=="(getDayOfWeek(SysTime(Date(0, 12, 23)).dayOfGregorianCal), DayOfWeek.sat); - } +unittest +{ + //Test A.D. + assert(getDayOfWeek(SysTime(Date(1, 1, 1)).dayOfGregorianCal) == DayOfWeek.mon); + assert(getDayOfWeek(SysTime(Date(1, 1, 2)).dayOfGregorianCal) == DayOfWeek.tue); + assert(getDayOfWeek(SysTime(Date(1, 1, 3)).dayOfGregorianCal) == DayOfWeek.wed); + assert(getDayOfWeek(SysTime(Date(1, 1, 4)).dayOfGregorianCal) == DayOfWeek.thu); + assert(getDayOfWeek(SysTime(Date(1, 1, 5)).dayOfGregorianCal) == DayOfWeek.fri); + assert(getDayOfWeek(SysTime(Date(1, 1, 6)).dayOfGregorianCal) == DayOfWeek.sat); + assert(getDayOfWeek(SysTime(Date(1, 1, 7)).dayOfGregorianCal) == DayOfWeek.sun); + assert(getDayOfWeek(SysTime(Date(1, 1, 8)).dayOfGregorianCal) == DayOfWeek.mon); + assert(getDayOfWeek(SysTime(Date(1, 1, 9)).dayOfGregorianCal) == DayOfWeek.tue); + assert(getDayOfWeek(SysTime(Date(2, 1, 1)).dayOfGregorianCal) == DayOfWeek.tue); + assert(getDayOfWeek(SysTime(Date(3, 1, 1)).dayOfGregorianCal) == DayOfWeek.wed); + assert(getDayOfWeek(SysTime(Date(4, 1, 1)).dayOfGregorianCal) == DayOfWeek.thu); + assert(getDayOfWeek(SysTime(Date(5, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat); + assert(getDayOfWeek(SysTime(Date(2000, 1, 1)).dayOfGregorianCal) == DayOfWeek.sat); + assert(getDayOfWeek(SysTime(Date(2010, 8, 22)).dayOfGregorianCal) == DayOfWeek.sun); + assert(getDayOfWeek(SysTime(Date(2010, 8, 23)).dayOfGregorianCal) == DayOfWeek.mon); + assert(getDayOfWeek(SysTime(Date(2010, 8, 24)).dayOfGregorianCal) == DayOfWeek.tue); + assert(getDayOfWeek(SysTime(Date(2010, 8, 25)).dayOfGregorianCal) == DayOfWeek.wed); + assert(getDayOfWeek(SysTime(Date(2010, 8, 26)).dayOfGregorianCal) == DayOfWeek.thu); + assert(getDayOfWeek(SysTime(Date(2010, 8, 27)).dayOfGregorianCal) == DayOfWeek.fri); + assert(getDayOfWeek(SysTime(Date(2010, 8, 28)).dayOfGregorianCal) == DayOfWeek.sat); + assert(getDayOfWeek(SysTime(Date(2010, 8, 29)).dayOfGregorianCal) == DayOfWeek.sun); + + //Test B.C. + assert(getDayOfWeek(SysTime(Date(0, 12, 31)).dayOfGregorianCal) == DayOfWeek.sun); + assert(getDayOfWeek(SysTime(Date(0, 12, 30)).dayOfGregorianCal) == DayOfWeek.sat); + assert(getDayOfWeek(SysTime(Date(0, 12, 29)).dayOfGregorianCal) == DayOfWeek.fri); + assert(getDayOfWeek(SysTime(Date(0, 12, 28)).dayOfGregorianCal) == DayOfWeek.thu); + assert(getDayOfWeek(SysTime(Date(0, 12, 27)).dayOfGregorianCal) == DayOfWeek.wed); + assert(getDayOfWeek(SysTime(Date(0, 12, 26)).dayOfGregorianCal) == DayOfWeek.tue); + assert(getDayOfWeek(SysTime(Date(0, 12, 25)).dayOfGregorianCal) == DayOfWeek.mon); + assert(getDayOfWeek(SysTime(Date(0, 12, 24)).dayOfGregorianCal) == DayOfWeek.sun); + assert(getDayOfWeek(SysTime(Date(0, 12, 23)).dayOfGregorianCal) == DayOfWeek.sat); } /+ Returns the string representation of the given month. - - Params: - useLongName = Whether the long or short version of the month name should - be used. - plural = Whether the string should be plural or not. - - Throws: - $(LREF DateTimeException) if the given month is not a valid month. +/ -string monthToString(Month month, bool useLongName = true) pure +string monthToString(Month month) @safe pure { - if (month < Month.jan || month > Month.dec) - { - throw new DateTimeException("Invalid month: " ~ numToString(month)); - } - - if(useLongName == true) - { - return longMonthNames[month - Month.jan]; - } - else - { - return shortMonthNames[month - Month.jan]; - } + assert(month >= Month.jan && month <= Month.dec, format("Invalid month: %s", month)); + return _monthNames[month - Month.jan]; } unittest { - version(testStdDateTime) - { - static void testMTSInvalid(Month month, bool useLongName) - { - monthToString(month, useLongName); - } - - assertThrown!DateTimeException(testMTSInvalid(cast(Month)0, true)); - assertThrown!DateTimeException(testMTSInvalid(cast(Month)0, false)); - assertThrown!DateTimeException(testMTSInvalid(cast(Month)13, true)); - assertThrown!DateTimeException(testMTSInvalid(cast(Month)13, false)); - - _assertPred!"=="(monthToString(Month.jan), "January"); - _assertPred!"=="(monthToString(Month.feb), "February"); - _assertPred!"=="(monthToString(Month.mar), "March"); - _assertPred!"=="(monthToString(Month.apr), "April"); - _assertPred!"=="(monthToString(Month.may), "May"); - _assertPred!"=="(monthToString(Month.jun), "June"); - _assertPred!"=="(monthToString(Month.jul), "July"); - _assertPred!"=="(monthToString(Month.aug), "August"); - _assertPred!"=="(monthToString(Month.sep), "September"); - _assertPred!"=="(monthToString(Month.oct), "October"); - _assertPred!"=="(monthToString(Month.nov), "November"); - _assertPred!"=="(monthToString(Month.dec), "December"); - - _assertPred!"=="(monthToString(Month.jan, false), "Jan"); - _assertPred!"=="(monthToString(Month.feb, false), "Feb"); - _assertPred!"=="(monthToString(Month.mar, false), "Mar"); - _assertPred!"=="(monthToString(Month.apr, false), "Apr"); - _assertPred!"=="(monthToString(Month.may, false), "May"); - _assertPred!"=="(monthToString(Month.jun, false), "Jun"); - _assertPred!"=="(monthToString(Month.jul, false), "Jul"); - _assertPred!"=="(monthToString(Month.aug, false), "Aug"); - _assertPred!"=="(monthToString(Month.sep, false), "Sep"); - _assertPred!"=="(monthToString(Month.oct, false), "Oct"); - _assertPred!"=="(monthToString(Month.nov, false), "Nov"); - _assertPred!"=="(monthToString(Month.dec, false), "Dec"); - } + assert(monthToString(Month.jan) == "Jan"); + assert(monthToString(Month.feb) == "Feb"); + assert(monthToString(Month.mar) == "Mar"); + assert(monthToString(Month.apr) == "Apr"); + assert(monthToString(Month.may) == "May"); + assert(monthToString(Month.jun) == "Jun"); + assert(monthToString(Month.jul) == "Jul"); + assert(monthToString(Month.aug) == "Aug"); + assert(monthToString(Month.sep) == "Sep"); + assert(monthToString(Month.oct) == "Oct"); + assert(monthToString(Month.nov) == "Nov"); + assert(monthToString(Month.dec) == "Dec"); } /+ - Returns the Month corresponding to the given string. Casing is ignored. + Returns the Month corresponding to the given string. Params: monthStr = The string representation of the month to get the Month for. @@ -32734,44 +31857,33 @@ unittest Throws: $(LREF DateTimeException) if the given month is not a valid month string. +/ -Month monthFromString(string monthStr) +Month monthFromString(string monthStr) @safe pure { - switch(toLower(monthStr)) + switch(monthStr) { - case "january": - case "jan": + case "Jan": return Month.jan; - case "february": - case "feb": + case "Feb": return Month.feb; - case "march": - case "mar": + case "Mar": return Month.mar; - case "april": - case "apr": + case "Apr": return Month.apr; - case "may": + case "May": return Month.may; - case "june": - case "jun": + case "Jun": return Month.jun; - case "july": - case "jul": + case "Jul": return Month.jul; - case "august": - case "aug": + case "Aug": return Month.aug; - case "september": - case "sep": + case "Sep": return Month.sep; - case "october": - case "oct": + case "Oct": return Month.oct; - case "november": - case "nov": + case "Nov": return Month.nov; - case "december": - case "dec": + case "Dec": return Month.dec; default: throw new DateTimeException(format("Invalid month %s", monthStr)); @@ -32780,65 +31892,23 @@ Month monthFromString(string monthStr) unittest { - version(testStdDateTime) - { - static void testMFSInvalid(string monthStr) - { - monthFromString(monthStr); - } - - assertThrown!DateTimeException(testMFSInvalid("Ja")); - assertThrown!DateTimeException(testMFSInvalid("Janu")); - assertThrown!DateTimeException(testMFSInvalid("Januar")); - assertThrown!DateTimeException(testMFSInvalid("Januarys")); - assertThrown!DateTimeException(testMFSInvalid("JJanuary")); - - _assertPred!"=="(monthFromString(monthToString(Month.jan)), Month.jan); - _assertPred!"=="(monthFromString(monthToString(Month.feb)), Month.feb); - _assertPred!"=="(monthFromString(monthToString(Month.mar)), Month.mar); - _assertPred!"=="(monthFromString(monthToString(Month.apr)), Month.apr); - _assertPred!"=="(monthFromString(monthToString(Month.may)), Month.may); - _assertPred!"=="(monthFromString(monthToString(Month.jun)), Month.jun); - _assertPred!"=="(monthFromString(monthToString(Month.jul)), Month.jul); - _assertPred!"=="(monthFromString(monthToString(Month.aug)), Month.aug); - _assertPred!"=="(monthFromString(monthToString(Month.sep)), Month.sep); - _assertPred!"=="(monthFromString(monthToString(Month.oct)), Month.oct); - _assertPred!"=="(monthFromString(monthToString(Month.nov)), Month.nov); - _assertPred!"=="(monthFromString(monthToString(Month.dec)), Month.dec); - - _assertPred!"=="(monthFromString(monthToString(Month.jan, false)), Month.jan); - _assertPred!"=="(monthFromString(monthToString(Month.feb, false)), Month.feb); - _assertPred!"=="(monthFromString(monthToString(Month.mar, false)), Month.mar); - _assertPred!"=="(monthFromString(monthToString(Month.apr, false)), Month.apr); - _assertPred!"=="(monthFromString(monthToString(Month.may, false)), Month.may); - _assertPred!"=="(monthFromString(monthToString(Month.jun, false)), Month.jun); - _assertPred!"=="(monthFromString(monthToString(Month.jul, false)), Month.jul); - _assertPred!"=="(monthFromString(monthToString(Month.aug, false)), Month.aug); - _assertPred!"=="(monthFromString(monthToString(Month.sep, false)), Month.sep); - _assertPred!"=="(monthFromString(monthToString(Month.oct, false)), Month.oct); - _assertPred!"=="(monthFromString(monthToString(Month.nov, false)), Month.nov); - _assertPred!"=="(monthFromString(monthToString(Month.dec, false)), Month.dec); - - _assertPred!"=="(monthFromString("JANUARY"), Month.jan); - _assertPred!"=="(monthFromString("JAN"), Month.jan); - _assertPred!"=="(monthFromString("january"), Month.jan); - _assertPred!"=="(monthFromString("jan"), Month.jan); - _assertPred!"=="(monthFromString("jaNuary"), Month.jan); - _assertPred!"=="(monthFromString("jaN"), Month.jan); - _assertPred!"=="(monthFromString("jaNuaRy"), Month.jan); - _assertPred!"=="(monthFromString("jAn"), Month.jan); + foreach(badStr; ["Ja", "Janu", "Januar", "Januarys", "JJanuary", "JANUARY", + "JAN", "january", "jaNuary", "jaN", "jaNuaRy", "jAn"]) + { + scope(failure) writeln(badStr); + assertThrown!DateTimeException(monthFromString(badStr)); + } + + foreach(month; EnumMembers!Month) + { + scope(failure) writeln(month); + assert(monthFromString(monthToString(month)) == month); } } /+ The time units which are one step smaller than the given units. - - Examples: --------------------- -assert(nextSmallerTimeUnits!"years" == "months"); -assert(nextSmallerTimeUnits!"usecs" == "hnsecs"); --------------------- +/ template nextSmallerTimeUnits(string units) if(validTimeUnits(units) && @@ -32849,33 +31919,21 @@ template nextSmallerTimeUnits(string units) unittest { - version(testStdDateTime) - { - _assertPred!"=="(nextSmallerTimeUnits!"months", "weeks"); - _assertPred!"=="(nextSmallerTimeUnits!"weeks", "days"); - _assertPred!"=="(nextSmallerTimeUnits!"days", "hours"); - _assertPred!"=="(nextSmallerTimeUnits!"hours", "minutes"); - _assertPred!"=="(nextSmallerTimeUnits!"minutes", "seconds"); - _assertPred!"=="(nextSmallerTimeUnits!"seconds", "msecs"); - _assertPred!"=="(nextSmallerTimeUnits!"msecs", "usecs"); - - static assert(!__traits(compiles, nextSmallerTimeUnits!"hnsecs")); - - //Verify Examples. - assert(nextSmallerTimeUnits!"years" == "months"); - assert(nextSmallerTimeUnits!"usecs" == "hnsecs"); - } + assert(nextSmallerTimeUnits!"years" == "months"); + assert(nextSmallerTimeUnits!"months" == "weeks"); + assert(nextSmallerTimeUnits!"weeks" == "days"); + assert(nextSmallerTimeUnits!"days" == "hours"); + assert(nextSmallerTimeUnits!"hours" == "minutes"); + assert(nextSmallerTimeUnits!"minutes" == "seconds"); + assert(nextSmallerTimeUnits!"seconds" == "msecs"); + assert(nextSmallerTimeUnits!"msecs" == "usecs"); + assert(nextSmallerTimeUnits!"usecs" == "hnsecs"); + static assert(!__traits(compiles, nextSmallerTimeUnits!"hnsecs")); } /+ The time units which are one step larger than the given units. - - Examples: --------------------- -assert(nextLargerTimeUnits!"months" == "years"); -assert(nextLargerTimeUnits!"hnsecs" == "usecs"); --------------------- +/ template nextLargerTimeUnits(string units) if(validTimeUnits(units) && @@ -32886,29 +31944,23 @@ template nextLargerTimeUnits(string units) unittest { - version(testStdDateTime) - { - _assertPred!"=="(nextLargerTimeUnits!"usecs", "msecs"); - _assertPred!"=="(nextLargerTimeUnits!"msecs", "seconds"); - _assertPred!"=="(nextLargerTimeUnits!"seconds", "minutes"); - _assertPred!"=="(nextLargerTimeUnits!"minutes", "hours"); - _assertPred!"=="(nextLargerTimeUnits!"hours", "days"); - _assertPred!"=="(nextLargerTimeUnits!"days", "weeks"); - _assertPred!"=="(nextLargerTimeUnits!"weeks", "months"); - - static assert(!__traits(compiles, nextLargerTimeUnits!"years")); - - //Verify Examples. - assert(nextLargerTimeUnits!"months" == "years"); - assert(nextLargerTimeUnits!"hnsecs" == "usecs"); - } + assert(nextLargerTimeUnits!"hnsecs" == "usecs"); + assert(nextLargerTimeUnits!"usecs" == "msecs"); + assert(nextLargerTimeUnits!"msecs" == "seconds"); + assert(nextLargerTimeUnits!"seconds" == "minutes"); + assert(nextLargerTimeUnits!"minutes" == "hours"); + assert(nextLargerTimeUnits!"hours" == "days"); + assert(nextLargerTimeUnits!"days" == "weeks"); + assert(nextLargerTimeUnits!"weeks" == "months"); + assert(nextLargerTimeUnits!"months" == "years"); + static assert(!__traits(compiles, nextLargerTimeUnits!"years")); } /+ Returns the given hnsecs as an ISO string of fractional seconds. +/ -static string fracSecToISOString(int hnsecs) nothrow +static string fracSecToISOString(int hnsecs) @safe pure nothrow in { assert(hnsecs >= 0); @@ -32933,32 +31985,29 @@ body unittest { - version(testStdDateTime) - { - _assertPred!"=="(fracSecToISOString(0), ""); - _assertPred!"=="(fracSecToISOString(1), ".0000001"); - _assertPred!"=="(fracSecToISOString(10), ".000001"); - _assertPred!"=="(fracSecToISOString(100), ".00001"); - _assertPred!"=="(fracSecToISOString(1000), ".0001"); - _assertPred!"=="(fracSecToISOString(10_000), ".001"); - _assertPred!"=="(fracSecToISOString(100_000), ".01"); - _assertPred!"=="(fracSecToISOString(1_000_000), ".1"); - _assertPred!"=="(fracSecToISOString(1_000_001), ".1000001"); - _assertPred!"=="(fracSecToISOString(1_001_001), ".1001001"); - _assertPred!"=="(fracSecToISOString(1_071_601), ".1071601"); - _assertPred!"=="(fracSecToISOString(1_271_641), ".1271641"); - _assertPred!"=="(fracSecToISOString(9_999_999), ".9999999"); - _assertPred!"=="(fracSecToISOString(9_999_990), ".999999"); - _assertPred!"=="(fracSecToISOString(9_999_900), ".99999"); - _assertPred!"=="(fracSecToISOString(9_999_000), ".9999"); - _assertPred!"=="(fracSecToISOString(9_990_000), ".999"); - _assertPred!"=="(fracSecToISOString(9_900_000), ".99"); - _assertPred!"=="(fracSecToISOString(9_000_000), ".9"); - _assertPred!"=="(fracSecToISOString(999), ".0000999"); - _assertPred!"=="(fracSecToISOString(9990), ".000999"); - _assertPred!"=="(fracSecToISOString(99_900), ".00999"); - _assertPred!"=="(fracSecToISOString(999_000), ".0999"); - } + assert(fracSecToISOString(0) == ""); + assert(fracSecToISOString(1) == ".0000001"); + assert(fracSecToISOString(10) == ".000001"); + assert(fracSecToISOString(100) == ".00001"); + assert(fracSecToISOString(1000) == ".0001"); + assert(fracSecToISOString(10_000) == ".001"); + assert(fracSecToISOString(100_000) == ".01"); + assert(fracSecToISOString(1_000_000) == ".1"); + assert(fracSecToISOString(1_000_001) == ".1000001"); + assert(fracSecToISOString(1_001_001) == ".1001001"); + assert(fracSecToISOString(1_071_601) == ".1071601"); + assert(fracSecToISOString(1_271_641) == ".1271641"); + assert(fracSecToISOString(9_999_999) == ".9999999"); + assert(fracSecToISOString(9_999_990) == ".999999"); + assert(fracSecToISOString(9_999_900) == ".99999"); + assert(fracSecToISOString(9_999_000) == ".9999"); + assert(fracSecToISOString(9_990_000) == ".999"); + assert(fracSecToISOString(9_900_000) == ".99"); + assert(fracSecToISOString(9_000_000) == ".9"); + assert(fracSecToISOString(999) == ".0000999"); + assert(fracSecToISOString(9990) == ".000999"); + assert(fracSecToISOString(99_900) == ".00999"); + assert(fracSecToISOString(999_000) == ".0999"); } @@ -32966,7 +32015,7 @@ unittest Returns a FracSec corresponding to to the given ISO string of fractional seconds. +/ -static FracSec fracSecFromISOString(S)(in S isoString) +static FracSec fracSecFromISOString(S)(in S isoString) @safe pure if(isSomeString!S) { if(isoString.empty) @@ -32978,7 +32027,7 @@ static FracSec fracSecFromISOString(S)(in S isoString) dstr.popFront(); enforce(!dstr.empty && dstr.length <= 7, new DateTimeException("Invalid ISO String")); - enforce(!canFind!(not!isDigit)(dstr), new DateTimeException("Invalid ISO String")); + enforce(all!isDigit(dstr), new DateTimeException("Invalid ISO String")); dchar[7] fullISOString; @@ -32995,56 +32044,268 @@ static FracSec fracSecFromISOString(S)(in S isoString) unittest { - version(testStdDateTime) - { - static void testFSInvalid(string isoString) - { - fracSecFromISOString(isoString); - } - - assertThrown!DateTimeException(testFSInvalid(".")); - assertThrown!DateTimeException(testFSInvalid("0.")); - assertThrown!DateTimeException(testFSInvalid("0")); - assertThrown!DateTimeException(testFSInvalid("0000000")); - assertThrown!DateTimeException(testFSInvalid(".00000000")); - assertThrown!DateTimeException(testFSInvalid(".00000001")); - assertThrown!DateTimeException(testFSInvalid("T")); - assertThrown!DateTimeException(testFSInvalid("T.")); - assertThrown!DateTimeException(testFSInvalid(".T")); - - _assertPred!"=="(fracSecFromISOString(""), FracSec.from!"hnsecs"(0)); - _assertPred!"=="(fracSecFromISOString(".0000001"), FracSec.from!"hnsecs"(1)); - _assertPred!"=="(fracSecFromISOString(".000001"), FracSec.from!"hnsecs"(10)); - _assertPred!"=="(fracSecFromISOString(".00001"), FracSec.from!"hnsecs"(100)); - _assertPred!"=="(fracSecFromISOString(".0001"), FracSec.from!"hnsecs"(1000)); - _assertPred!"=="(fracSecFromISOString(".001"), FracSec.from!"hnsecs"(10_000)); - _assertPred!"=="(fracSecFromISOString(".01"), FracSec.from!"hnsecs"(100_000)); - _assertPred!"=="(fracSecFromISOString(".1"), FracSec.from!"hnsecs"(1_000_000)); - _assertPred!"=="(fracSecFromISOString(".1000001"), FracSec.from!"hnsecs"(1_000_001)); - _assertPred!"=="(fracSecFromISOString(".1001001"), FracSec.from!"hnsecs"(1_001_001)); - _assertPred!"=="(fracSecFromISOString(".1071601"), FracSec.from!"hnsecs"(1_071_601)); - _assertPred!"=="(fracSecFromISOString(".1271641"), FracSec.from!"hnsecs"(1_271_641)); - _assertPred!"=="(fracSecFromISOString(".9999999"), FracSec.from!"hnsecs"(9_999_999)); - _assertPred!"=="(fracSecFromISOString(".9999990"), FracSec.from!"hnsecs"(9_999_990)); - _assertPred!"=="(fracSecFromISOString(".999999"), FracSec.from!"hnsecs"(9_999_990)); - _assertPred!"=="(fracSecFromISOString(".9999900"), FracSec.from!"hnsecs"(9_999_900)); - _assertPred!"=="(fracSecFromISOString(".99999"), FracSec.from!"hnsecs"(9_999_900)); - _assertPred!"=="(fracSecFromISOString(".9999000"), FracSec.from!"hnsecs"(9_999_000)); - _assertPred!"=="(fracSecFromISOString(".9999"), FracSec.from!"hnsecs"(9_999_000)); - _assertPred!"=="(fracSecFromISOString(".9990000"), FracSec.from!"hnsecs"(9_990_000)); - _assertPred!"=="(fracSecFromISOString(".999"), FracSec.from!"hnsecs"(9_990_000)); - _assertPred!"=="(fracSecFromISOString(".9900000"), FracSec.from!"hnsecs"(9_900_000)); - _assertPred!"=="(fracSecFromISOString(".9900"), FracSec.from!"hnsecs"(9_900_000)); - _assertPred!"=="(fracSecFromISOString(".99"), FracSec.from!"hnsecs"(9_900_000)); - _assertPred!"=="(fracSecFromISOString(".9000000"), FracSec.from!"hnsecs"(9_000_000)); - _assertPred!"=="(fracSecFromISOString(".9"), FracSec.from!"hnsecs"(9_000_000)); - _assertPred!"=="(fracSecFromISOString(".0000999"), FracSec.from!"hnsecs"(999)); - _assertPred!"=="(fracSecFromISOString(".0009990"), FracSec.from!"hnsecs"(9990)); - _assertPred!"=="(fracSecFromISOString(".000999"), FracSec.from!"hnsecs"(9990)); - _assertPred!"=="(fracSecFromISOString(".0099900"), FracSec.from!"hnsecs"(99_900)); - _assertPred!"=="(fracSecFromISOString(".00999"), FracSec.from!"hnsecs"(99_900)); - _assertPred!"=="(fracSecFromISOString(".0999000"), FracSec.from!"hnsecs"(999_000)); - _assertPred!"=="(fracSecFromISOString(".0999"), FracSec.from!"hnsecs"(999_000)); + static void testFSInvalid(string isoString) + { + fracSecFromISOString(isoString); + } + + assertThrown!DateTimeException(testFSInvalid(".")); + assertThrown!DateTimeException(testFSInvalid("0.")); + assertThrown!DateTimeException(testFSInvalid("0")); + assertThrown!DateTimeException(testFSInvalid("0000000")); + assertThrown!DateTimeException(testFSInvalid(".00000000")); + assertThrown!DateTimeException(testFSInvalid(".00000001")); + assertThrown!DateTimeException(testFSInvalid("T")); + assertThrown!DateTimeException(testFSInvalid("T.")); + assertThrown!DateTimeException(testFSInvalid(".T")); + + assert(fracSecFromISOString("") == FracSec.from!"hnsecs"(0)); + assert(fracSecFromISOString(".0000001") == FracSec.from!"hnsecs"(1)); + assert(fracSecFromISOString(".000001") == FracSec.from!"hnsecs"(10)); + assert(fracSecFromISOString(".00001") == FracSec.from!"hnsecs"(100)); + assert(fracSecFromISOString(".0001") == FracSec.from!"hnsecs"(1000)); + assert(fracSecFromISOString(".001") == FracSec.from!"hnsecs"(10_000)); + assert(fracSecFromISOString(".01") == FracSec.from!"hnsecs"(100_000)); + assert(fracSecFromISOString(".1") == FracSec.from!"hnsecs"(1_000_000)); + assert(fracSecFromISOString(".1000001") == FracSec.from!"hnsecs"(1_000_001)); + assert(fracSecFromISOString(".1001001") == FracSec.from!"hnsecs"(1_001_001)); + assert(fracSecFromISOString(".1071601") == FracSec.from!"hnsecs"(1_071_601)); + assert(fracSecFromISOString(".1271641") == FracSec.from!"hnsecs"(1_271_641)); + assert(fracSecFromISOString(".9999999") == FracSec.from!"hnsecs"(9_999_999)); + assert(fracSecFromISOString(".9999990") == FracSec.from!"hnsecs"(9_999_990)); + assert(fracSecFromISOString(".999999") == FracSec.from!"hnsecs"(9_999_990)); + assert(fracSecFromISOString(".9999900") == FracSec.from!"hnsecs"(9_999_900)); + assert(fracSecFromISOString(".99999") == FracSec.from!"hnsecs"(9_999_900)); + assert(fracSecFromISOString(".9999000") == FracSec.from!"hnsecs"(9_999_000)); + assert(fracSecFromISOString(".9999") == FracSec.from!"hnsecs"(9_999_000)); + assert(fracSecFromISOString(".9990000") == FracSec.from!"hnsecs"(9_990_000)); + assert(fracSecFromISOString(".999") == FracSec.from!"hnsecs"(9_990_000)); + assert(fracSecFromISOString(".9900000") == FracSec.from!"hnsecs"(9_900_000)); + assert(fracSecFromISOString(".9900") == FracSec.from!"hnsecs"(9_900_000)); + assert(fracSecFromISOString(".99") == FracSec.from!"hnsecs"(9_900_000)); + assert(fracSecFromISOString(".9000000") == FracSec.from!"hnsecs"(9_000_000)); + assert(fracSecFromISOString(".9") == FracSec.from!"hnsecs"(9_000_000)); + assert(fracSecFromISOString(".0000999") == FracSec.from!"hnsecs"(999)); + assert(fracSecFromISOString(".0009990") == FracSec.from!"hnsecs"(9990)); + assert(fracSecFromISOString(".000999") == FracSec.from!"hnsecs"(9990)); + assert(fracSecFromISOString(".0099900") == FracSec.from!"hnsecs"(99_900)); + assert(fracSecFromISOString(".00999") == FracSec.from!"hnsecs"(99_900)); + assert(fracSecFromISOString(".0999000") == FracSec.from!"hnsecs"(999_000)); + assert(fracSecFromISOString(".0999") == FracSec.from!"hnsecs"(999_000)); +} + + +/+ + Strips what RFC 5322, section 3.2.2 refers to as CFWS from the left-hand + side of the given range (it strips comments delimited by $(D '(') and + $(D ')') as well as folding whitespace). + + It is assumed that the given range contains the value of a header field and + no terminating CRLF for the line (though the CRLF for folding whitespace is + of course expected and stripped) and thus that the only case of CR or LF is + in folding whitespace. + + If a comment does not terminate correctly (e.g. mismatched parens) or if the + the FWS is malformed, then the range will be empty when stripCWFS is done. + However, only minimal validation of the content is done (e.g. quoted pairs + within a comment aren't validated beyond \$LPAREN or \$RPAREN, because + they're inside a comment, and thus their value doesn't matter anyway). It's + only when the content does not conform to the grammar rules for FWS and thus + literally cannot be parsed that content is considered invalid, and an empty + range is returned. + + Note that _stripCFWS is eager, not lazy. It does not create a new range. + Rather, it pops off the CFWS from the range and returns it. + +/ +R _stripCFWS(R)(R range) + if(isRandomAccessRange!R && hasSlicing!R && hasLength!R && + (is(Unqual!(ElementType!R) == char) || is(Unqual!(ElementType!R) == ubyte))) +{ + immutable e = range.length; + outer: for(size_t i = 0; i < e; ) + { + switch(range[i]) + { + case ' ': case '\t': + { + ++i; + break; + } + case '\r': + { + if(i + 2 < e && range[i + 1] == '\n' && (range[i + 2] == ' ' || range[i + 2] == '\t')) + { + i += 3; + break; + } + break outer; + } + case '\n': + { + if(i + 1 < e && (range[i + 1] == ' ' || range[i + 1] == '\t')) + { + i += 2; + break; + } + break outer; + } + case '(': + { + ++i; + size_t commentLevel = 1; + while(i < e) + { + if(range[i] == '(') + ++commentLevel; + else if(range[i] == ')') + { + ++i; + if(--commentLevel == 0) + continue outer; + continue; + } + else if(range[i] == '\\') + { + if(++i == e) + break outer; + } + ++i; + } + break outer; + } + default: return range[i .. e]; + } + } + return range[e .. e]; +} + +unittest +{ + import std.typetuple : TypeTuple; + + foreach(cr; TypeTuple!(function(string a){return cast(ubyte[])a;}, + function(string a){return map!(b => cast(char)b)(a.representation);})) + { + scope(failure) writeln(typeof(cr).stringof); + + assert(_stripCFWS(cr("")).empty); + assert(_stripCFWS(cr("\r")).empty); + assert(_stripCFWS(cr("\r\n")).empty); + assert(_stripCFWS(cr("\r\n ")).empty); + assert(_stripCFWS(cr(" \t\r\n")).empty); + assert(equal(_stripCFWS(cr(" \t\r\n hello")), cr("hello"))); + assert(_stripCFWS(cr(" \t\r\nhello")).empty); + assert(_stripCFWS(cr(" \t\r\n\v")).empty); + assert(equal(_stripCFWS(cr("\v \t\r\n\v")), cr("\v \t\r\n\v"))); + assert(_stripCFWS(cr("()")).empty); + assert(_stripCFWS(cr("(hello world)")).empty); + assert(_stripCFWS(cr("(hello world)(hello world)")).empty); + assert(_stripCFWS(cr("(hello world\r\n foo\r where's\nwaldo)")).empty); + assert(_stripCFWS(cr(" \t (hello \tworld\r\n foo\r where's\nwaldo)\t\t ")).empty); + assert(_stripCFWS(cr(" ")).empty); + assert(_stripCFWS(cr("\t\t\t")).empty); + assert(_stripCFWS(cr("\t \r\n\r \n")).empty); + assert(_stripCFWS(cr("(hello world) (can't find waldo) (he's lost)")).empty); + assert(_stripCFWS(cr("(hello\\) world) (can't \\(find waldo) (he's \\(\\)lost)")).empty); + assert(_stripCFWS(cr("(((((")).empty); + assert(_stripCFWS(cr("(((()))")).empty); + assert(_stripCFWS(cr("(((())))")).empty); + assert(equal(_stripCFWS(cr("(((()))))")), cr(")"))); + assert(equal(_stripCFWS(cr(")))))")), cr(")))))"))); + assert(equal(_stripCFWS(cr("()))))")), cr("))))"))); + assert(equal(_stripCFWS(cr(" hello hello ")), cr("hello hello "))); + assert(equal(_stripCFWS(cr("\thello (world)")), cr("hello (world)"))); + assert(equal(_stripCFWS(cr(" \r\n \\((\\)) foo")), cr("\\((\\)) foo"))); + assert(equal(_stripCFWS(cr(" \r\n (\\((\\))) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" \r\n (\\(())) foo")), cr(") foo"))); + assert(_stripCFWS(cr(" \r\n (((\\))) foo")).empty); + + assert(_stripCFWS(cr("(hello)(hello)")).empty); + assert(_stripCFWS(cr(" \r\n (hello)\r\n (hello)")).empty); + assert(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n ")).empty); + assert(_stripCFWS(cr("\t\t\t\t(hello)\t\t\t\t(hello)\t\t\t\t")).empty); + assert(equal(_stripCFWS(cr(" \r\n (hello)\r\n (hello) \r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr("\t\r\n\t(hello)\r\n\t(hello)\t\r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr("\t\r\n\t(hello)\t\r\n\t(hello)\t\r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n \r\n (hello) \r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \r\n (hello) \r\n (hello) \r\n \r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \r\n \r\n (hello)\t\r\n (hello) \r\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \r\n\t\r\n\t(hello)\t\r\n (hello) \r\n hello")), cr("hello"))); + + assert(equal(_stripCFWS(cr(" (\r\n ( \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\t\r\n ( \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n\t( \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n (\t\r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n (\r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n (\r\n\t) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n )\t\r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n )\r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n\t) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n ) \r\n foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n )\t\r\n foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n ( \r\n ) \r\n )\r\n foo")), cr("foo"))); + + assert(equal(_stripCFWS(cr(" ( \r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\t\r\n \r\n ( \r\n \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n \r\n\t( \r\n \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n \r\n( \r\n \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n\t) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n )\t\r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" (\r\n \r\n ( \r\n \r\n )\r\n ) foo")), cr("foo"))); + + assert(equal(_stripCFWS(cr(" ( \r\n bar \r\n ( \r\n bar \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n () \r\n ( \r\n () \r\n ) \r\n ) foo")), cr("foo"))); + assert(equal(_stripCFWS(cr(" ( \r\n \\\\ \r\n ( \r\n \\\\ \r\n ) \r\n ) foo")), cr("foo"))); + + assert(_stripCFWS(cr("(hello)(hello)")).empty); + assert(_stripCFWS(cr(" \n (hello)\n (hello) \n ")).empty); + assert(_stripCFWS(cr(" \n (hello) \n (hello) \n ")).empty); + assert(equal(_stripCFWS(cr(" \n (hello)\n (hello) \n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \n (hello) \n (hello) \n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr("\t\n\t(hello)\n\t(hello)\t\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr("\t\n\t(hello)\t\n\t(hello)\t\n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \n (hello) \n \n (hello) \n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \n (hello) \n (hello) \n \n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \n \n (hello)\t\n (hello) \n hello")), cr("hello"))); + assert(equal(_stripCFWS(cr(" \n\t\n\t(hello)\t\n (hello) \n hello")), cr("hello"))); + } +} + +// This is so that we don't have to worry about std.conv.to throwing. It also +// doesn't have to worry about quite as many cases as std.conv.to, since it +// doesn't have to worry about a sign on the value or about whether it fits. +T _convDigits(T, R)(R str) + if(isIntegral!T && isSigned!T) // The constraints on R were already covered by parseRFC822DateTime. +{ + assert(!str.empty); + T num = 0; + foreach(i; 0 .. str.length) + { + if(i != 0) + num *= 10; + if(!std.ascii.isDigit(str[i])) + return -1; + num += str[i] - '0'; + } + return num; +} + +unittest +{ + foreach(i; chain(iota(0, 101), [250, 999, 1000, 1001, 2345, 9999])) + { + scope(failure) writeln(i); + assert(_convDigits!int(to!string(i)) == i); + } + foreach(str; ["-42", "+42", "1a", "1 ", " ", " 42 "]) + { + scope(failure) writeln(str); + assert(_convDigits!int(str) == -1); } } @@ -33062,20 +32323,17 @@ template hasMin(T) unittest { - version(testStdDateTime) - { - static assert(hasMin!(Date)); - static assert(hasMin!(TimeOfDay)); - static assert(hasMin!(DateTime)); - static assert(hasMin!(SysTime)); - static assert(hasMin!(const Date)); - static assert(hasMin!(const TimeOfDay)); - static assert(hasMin!(const DateTime)); - static assert(hasMin!(const SysTime)); - static assert(hasMin!(immutable Date)); - static assert(hasMin!(immutable TimeOfDay)); - static assert(hasMin!(immutable SysTime)); - } + static assert(hasMin!(Date)); + static assert(hasMin!(TimeOfDay)); + static assert(hasMin!(DateTime)); + static assert(hasMin!(SysTime)); + static assert(hasMin!(const Date)); + static assert(hasMin!(const TimeOfDay)); + static assert(hasMin!(const DateTime)); + static assert(hasMin!(const SysTime)); + static assert(hasMin!(immutable Date)); + static assert(hasMin!(immutable TimeOfDay)); + static assert(hasMin!(immutable SysTime)); } @@ -33092,21 +32350,18 @@ template hasMax(T) unittest { - version(testStdDateTime) - { - static assert(hasMax!(Date)); - static assert(hasMax!(TimeOfDay)); - static assert(hasMax!(DateTime)); - static assert(hasMax!(SysTime)); - static assert(hasMax!(const Date)); - static assert(hasMax!(const TimeOfDay)); - static assert(hasMax!(const DateTime)); - static assert(hasMax!(const SysTime)); - static assert(hasMax!(immutable Date)); - static assert(hasMax!(immutable TimeOfDay)); - static assert(hasMax!(immutable DateTime)); - static assert(hasMax!(immutable SysTime)); - } + static assert(hasMax!(Date)); + static assert(hasMax!(TimeOfDay)); + static assert(hasMax!(DateTime)); + static assert(hasMax!(SysTime)); + static assert(hasMax!(const Date)); + static assert(hasMax!(const TimeOfDay)); + static assert(hasMax!(const DateTime)); + static assert(hasMax!(const SysTime)); + static assert(hasMax!(immutable Date)); + static assert(hasMax!(immutable TimeOfDay)); + static assert(hasMax!(immutable DateTime)); + static assert(hasMax!(immutable SysTime)); } @@ -33133,21 +32388,18 @@ template hasOverloadedOpBinaryWithDuration(T) unittest { - version(testStdDateTime) - { - static assert(hasOverloadedOpBinaryWithDuration!(Date)); - static assert(hasOverloadedOpBinaryWithDuration!(TimeOfDay)); - static assert(hasOverloadedOpBinaryWithDuration!(DateTime)); - static assert(hasOverloadedOpBinaryWithDuration!(SysTime)); - static assert(hasOverloadedOpBinaryWithDuration!(const Date)); - static assert(hasOverloadedOpBinaryWithDuration!(const TimeOfDay)); - static assert(hasOverloadedOpBinaryWithDuration!(const DateTime)); - static assert(hasOverloadedOpBinaryWithDuration!(const SysTime)); - static assert(hasOverloadedOpBinaryWithDuration!(immutable Date)); - static assert(hasOverloadedOpBinaryWithDuration!(immutable TimeOfDay)); - static assert(hasOverloadedOpBinaryWithDuration!(immutable DateTime)); - static assert(hasOverloadedOpBinaryWithDuration!(immutable SysTime)); - } + static assert(hasOverloadedOpBinaryWithDuration!(Date)); + static assert(hasOverloadedOpBinaryWithDuration!(TimeOfDay)); + static assert(hasOverloadedOpBinaryWithDuration!(DateTime)); + static assert(hasOverloadedOpBinaryWithDuration!(SysTime)); + static assert(hasOverloadedOpBinaryWithDuration!(const Date)); + static assert(hasOverloadedOpBinaryWithDuration!(const TimeOfDay)); + static assert(hasOverloadedOpBinaryWithDuration!(const DateTime)); + static assert(hasOverloadedOpBinaryWithDuration!(const SysTime)); + static assert(hasOverloadedOpBinaryWithDuration!(immutable Date)); + static assert(hasOverloadedOpBinaryWithDuration!(immutable TimeOfDay)); + static assert(hasOverloadedOpBinaryWithDuration!(immutable DateTime)); + static assert(hasOverloadedOpBinaryWithDuration!(immutable SysTime)); } @@ -33176,21 +32428,18 @@ template hasOverloadedOpAssignWithDuration(T) unittest { - version(testStdDateTime) - { - static assert(hasOverloadedOpAssignWithDuration!(Date)); - static assert(hasOverloadedOpAssignWithDuration!(TimeOfDay)); - static assert(hasOverloadedOpAssignWithDuration!(DateTime)); - static assert(hasOverloadedOpAssignWithDuration!(SysTime)); - static assert(hasOverloadedOpAssignWithDuration!(const Date)); - static assert(hasOverloadedOpAssignWithDuration!(const TimeOfDay)); - static assert(hasOverloadedOpAssignWithDuration!(const DateTime)); - static assert(hasOverloadedOpAssignWithDuration!(const SysTime)); - static assert(hasOverloadedOpAssignWithDuration!(immutable Date)); - static assert(hasOverloadedOpAssignWithDuration!(immutable TimeOfDay)); - static assert(hasOverloadedOpAssignWithDuration!(immutable DateTime)); - static assert(hasOverloadedOpAssignWithDuration!(immutable SysTime)); - } + static assert(hasOverloadedOpAssignWithDuration!(Date)); + static assert(hasOverloadedOpAssignWithDuration!(TimeOfDay)); + static assert(hasOverloadedOpAssignWithDuration!(DateTime)); + static assert(hasOverloadedOpAssignWithDuration!(SysTime)); + static assert(hasOverloadedOpAssignWithDuration!(const Date)); + static assert(hasOverloadedOpAssignWithDuration!(const TimeOfDay)); + static assert(hasOverloadedOpAssignWithDuration!(const DateTime)); + static assert(hasOverloadedOpAssignWithDuration!(const SysTime)); + static assert(hasOverloadedOpAssignWithDuration!(immutable Date)); + static assert(hasOverloadedOpAssignWithDuration!(immutable TimeOfDay)); + static assert(hasOverloadedOpAssignWithDuration!(immutable DateTime)); + static assert(hasOverloadedOpAssignWithDuration!(immutable SysTime)); } @@ -33210,59 +32459,18 @@ template hasOverloadedOpBinaryWithSelf(T) unittest { - version(testStdDateTime) - { - static assert(hasOverloadedOpBinaryWithSelf!(Date)); - static assert(hasOverloadedOpBinaryWithSelf!(TimeOfDay)); - static assert(hasOverloadedOpBinaryWithSelf!(DateTime)); - static assert(hasOverloadedOpBinaryWithSelf!(SysTime)); - static assert(hasOverloadedOpBinaryWithSelf!(const Date)); - static assert(hasOverloadedOpBinaryWithSelf!(const TimeOfDay)); - static assert(hasOverloadedOpBinaryWithSelf!(const DateTime)); - static assert(hasOverloadedOpBinaryWithSelf!(const SysTime)); - static assert(hasOverloadedOpBinaryWithSelf!(immutable Date)); - static assert(hasOverloadedOpBinaryWithSelf!(immutable TimeOfDay)); - static assert(hasOverloadedOpBinaryWithSelf!(immutable DateTime)); - static assert(hasOverloadedOpBinaryWithSelf!(immutable SysTime)); - } -} - -/+ - Unfortunately, to!string() is not pure, so here's a way to convert - a number to a string which is. Once to!string() is properly pure - (like it hopefully will be at some point), this function should - be removed in favor of using to!string(). - +/ -string numToString(long value) pure nothrow -{ - try - { - immutable negative = value < 0; - char[25] str; - size_t i = str.length; - - if(negative) - value = -value; - - while(1) - { - char digit = cast(char)('0' + value % 10); - value /= 10; - - str[--i] = digit; - assert(i > 0); - - if(value == 0) - break; - } - - if(negative) - return "-" ~ str[i .. $].idup; - else - return str[i .. $].idup; - } - catch(Exception e) - assert(0, "Something threw when nothing can throw."); + static assert(hasOverloadedOpBinaryWithSelf!(Date)); + static assert(hasOverloadedOpBinaryWithSelf!(TimeOfDay)); + static assert(hasOverloadedOpBinaryWithSelf!(DateTime)); + static assert(hasOverloadedOpBinaryWithSelf!(SysTime)); + static assert(hasOverloadedOpBinaryWithSelf!(const Date)); + static assert(hasOverloadedOpBinaryWithSelf!(const TimeOfDay)); + static assert(hasOverloadedOpBinaryWithSelf!(const DateTime)); + static assert(hasOverloadedOpBinaryWithSelf!(const SysTime)); + static assert(hasOverloadedOpBinaryWithSelf!(immutable Date)); + static assert(hasOverloadedOpBinaryWithSelf!(immutable TimeOfDay)); + static assert(hasOverloadedOpBinaryWithSelf!(immutable DateTime)); + static assert(hasOverloadedOpBinaryWithSelf!(immutable SysTime)); } @@ -33607,451 +32815,157 @@ version(unittest) } -//============================================================================== -// Unit testing functions. -//============================================================================== - -void _assertPred(string op, L, R) - (L lhs, R rhs, lazy string msg = null, string file = __FILE__, size_t line = __LINE__) - if((op == "<" || - op == "<=" || - op == "==" || - op == "!=" || - op == ">=" || - op == ">") && - __traits(compiles, mixin("lhs " ~ op ~ " rhs")) && - _isPrintable!L && - _isPrintable!R) -{ - immutable result = mixin("lhs " ~ op ~ " rhs"); - - if(!result) - { - if(msg.empty) - throw new AssertError(format(`_assertPred!"%s" failed: [%s] is not %s [%s].`, op, lhs, op, rhs), file, line); - else - throw new AssertError(format(`_assertPred!"%s" failed: [%s] is not %s [%s]: %s`, op, lhs, op, rhs, msg), file, line); - } -} - unittest { - version(testStdDateTime) - { - struct IntWrapper - { - int value; - - this(int value) - { - this.value = value; - } - - string toString() const - { - return to!string(value); - } - } - - //Test ==. - assertNotThrown!AssertError(_assertPred!"=="(6, 6)); - assertNotThrown!AssertError(_assertPred!"=="(6, 6.0)); - assertNotThrown!AssertError(_assertPred!"=="(IntWrapper(6), IntWrapper(6))); - - assertThrown!AssertError(_assertPred!"=="(6, 7)); - assertThrown!AssertError(_assertPred!"=="(6, 6.1)); - assertThrown!AssertError(_assertPred!"=="(IntWrapper(6), IntWrapper(7))); - assertThrown!AssertError(_assertPred!"=="(IntWrapper(7), IntWrapper(6))); - - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"=="(6, 7)), - `_assertPred!"==" failed: [6] is not == [7].`); - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"=="(6, 7, "It failed!")), - `_assertPred!"==" failed: [6] is not == [7]: It failed!`); - - //Test !=. - assertNotThrown!AssertError(_assertPred!"!="(6, 7)); - assertNotThrown!AssertError(_assertPred!"!="(6, 6.1)); - assertNotThrown!AssertError(_assertPred!"!="(IntWrapper(6), IntWrapper(7))); - assertNotThrown!AssertError(_assertPred!"!="(IntWrapper(7), IntWrapper(6))); - - assertThrown!AssertError(_assertPred!"!="(6, 6)); - assertThrown!AssertError(_assertPred!"!="(6, 6.0)); - assertThrown!AssertError(_assertPred!"!="(IntWrapper(6), IntWrapper(6))); - - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"!="(6, 6)), - `_assertPred!"!=" failed: [6] is not != [6].`); - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"!="(6, 6, "It failed!")), - `_assertPred!"!=" failed: [6] is not != [6]: It failed!`); - - //Test <, <=, >=, >. - assertNotThrown!AssertError(_assertPred!"<"(5, 7)); - assertNotThrown!AssertError(_assertPred!"<="(5, 7)); - assertNotThrown!AssertError(_assertPred!"<="(5, 5)); - assertNotThrown!AssertError(_assertPred!">="(7, 7)); - assertNotThrown!AssertError(_assertPred!">="(7, 5)); - assertNotThrown!AssertError(_assertPred!">"(7, 5)); - - assertThrown!AssertError(_assertPred!"<"(7, 5)); - assertThrown!AssertError(_assertPred!"<="(7, 5)); - assertThrown!AssertError(_assertPred!">="(5, 7)); - assertThrown!AssertError(_assertPred!">"(5, 7)); - - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"<"(7, 5)), - `_assertPred!"<" failed: [7] is not < [5].`); - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"<"(7, 5, "It failed!")), - `_assertPred!"<" failed: [7] is not < [5]: It failed!`); - - //Verify Examples. - - //Equivalent to assert(5 / 2 + 4 < 27); - _assertPred!"<"(5 / 2 + 4, 27); - - //Equivalent to assert(4 <= 5); - _assertPred!"<="(4, 5); - - //Equivalent to assert(1 * 2.1 == 2.1); - _assertPred!"=="(1 * 2.1, 2.1); + /* Issue 6642 */ + static assert(!hasUnsharedAliasing!Date); + static assert(!hasUnsharedAliasing!TimeOfDay); + static assert(!hasUnsharedAliasing!DateTime); + static assert(!hasUnsharedAliasing!SysTime); +} - //Equivalent to assert("hello " ~ "world" != "goodbye world"); - _assertPred!"!="("hello " ~ "world", "goodbye world"); - //Equivalent to assert(14.2 >= 14); - _assertPred!">="(14.2, 14); - //Equivalent to assert(15 > 2 + 1); - _assertPred!">"(15, 2 + 1); +// This script is for regenerating tzDatabaseNameToWindowsTZName and +// windowsTZNameToTZDatabaseName from +// http://unicode.org/cldr/data/common/supplemental/windowsZones.xml - assert(collectExceptionMsg!AssertError(_assertPred!"=="("hello", "goodbye")) == - `_assertPred!"==" failed: [hello] is not == [goodbye].`); +/+ +#!/bin/rdmd - assert(collectExceptionMsg!AssertError(_assertPred!"<"(5, 2, "My test failed!")) == - `_assertPred!"<" failed: [5] is not < [2]: My test failed!`); - } -} +import std.algorithm; +import std.array; +import std.conv; +import std.datetime; +import std.exception; +import std.path; +import std.stdio; +import std.string; -void _assertPred(string func, string expected, L, R) - (L lhs, R rhs, lazy string msg = null, string file = __FILE__, size_t line = __LINE__) - if(func == "opCmp" && - (expected == "<" || - expected == "==" || - expected == ">") && - __traits(compiles, lhs.opCmp(rhs)) && - _isPrintable!L && - _isPrintable!R) +int main(string[] args) { - immutable result = lhs.opCmp(rhs); - - static if(expected == "<") + if(args.length != 4 || args[1].baseName != "windowsZones.xml") { - if(result < 0) - return; - - if(result == 0) - { - if(msg.empty) - throw new AssertError(format(`_assertPred!("opCmp", "<") failed: [%s] == [%s].`, lhs, rhs), file, line); - else - throw new AssertError(format(`_assertPred!("opCmp", "<") failed: [%s] == [%s]: %s`, lhs, rhs, msg), file, line); - } - else - { - if(msg.empty) - throw new AssertError(format(`_assertPred!("opCmp", "<") failed: [%s] > [%s].`, lhs, rhs), file, line); - else - throw new AssertError(format(`_assertPred!("opCmp", "<") failed: [%s] > [%s]: %s`, lhs, rhs, msg), file, line); - } + stderr.writeln("genTZs.d windowsZones.xml "); + return -1; } - else static if(expected == "==") - { - if(result == 0) - return; - if(result < 0) - { - if(msg.empty) - throw new AssertError(format(`_assertPred!("opCmp", "==") failed: [%s] < [%s].`, lhs, rhs), file, line); - else - throw new AssertError(format(`_assertPred!("opCmp", "==") failed: [%s] < [%s]: %s`, lhs, rhs, msg), file, line); - } - else - { - if(msg.empty) - throw new AssertError(format(`_assertPred!("opCmp", "==") failed: [%s] > [%s].`, lhs, rhs), file, line); - else - throw new AssertError(format(`_assertPred!("opCmp", "==") failed: [%s] > [%s]: %s`, lhs, rhs, msg), file, line); - } - } - else static if(expected == ">") + string[][string] win2Nix; + string[][string] nix2Win; + immutable f1 = ` 0) - return; + line = line.find(f1); + if(line.empty) + continue; + line = line[f1.length .. $]; + auto next = line.find('"'); + auto win = to!string(line[0 .. $ - next.length]); + line = next.find(f2); + line = line[f2.length .. $]; + next = line.find('"'); + auto nixes = to!string(line[0 .. $ - next.length]).split(); - if(result < 0) - { - if(msg.empty) - throw new AssertError(format(`_assertPred!("opCmp", ">") failed: [%s] < [%s].`, lhs, rhs), file, line); - else - throw new AssertError(format(`_assertPred!("opCmp", ">") failed: [%s] < [%s]: %s`, lhs, rhs, msg), file, line); - } + if(auto l = win in win2Nix) + *l ~= nixes; else + win2Nix[win] = nixes; + foreach(nix; nixes) { - if(msg.empty) - throw new AssertError(format(`_assertPred!("opCmp", ">") failed: [%s] == [%s].`, lhs, rhs), file, line); + if(auto w = nix in nix2Win) + *w ~= win; else - throw new AssertError(format(`_assertPred!("opCmp", ">") failed: [%s] == [%s]: %s`, lhs, rhs, msg), file, line); + nix2Win[nix] = [win]; } } - else - static assert(0); -} -void _assertPred(string op, L, R, E) - (L lhs, R rhs, E expected, lazy string msg = null, string file = __FILE__, size_t line = __LINE__) - if((op == "+=" || - op == "-=" || - op == "*=" || - op == "/=" || - op == "%=" || - op == "^^=" || - op == "&=" || - op == "|=" || - op == "^=" || - op == "<<=" || - op == ">>=" || - op == ">>>=" || - op == "~=") && - __traits(compiles, mixin("lhs " ~ op ~ " rhs")) && - __traits(compiles, mixin("(lhs " ~ op ~ " rhs) == expected")) && - _isPrintable!L && - _isPrintable!R) -{ - immutable origLHSStr = to!string(lhs); - const result = mixin("lhs " ~ op ~ " rhs"); - - if(lhs != expected) + foreach(nix; nix2Win.byKey()) { - if(msg.empty) - { - throw new AssertError(format(`_assertPred!"%s" failed: After [%s] %s [%s], lhs was assigned to [%s] instead of [%s].`, - op, - origLHSStr, - op, - rhs, - lhs, - expected), - file, - line); - } - else - { - throw new AssertError(format(`_assertPred!"%s" failed: After [%s] %s [%s], lhs was assigned to [%s] instead of [%s]: %s`, - op, - origLHSStr, - op, - rhs, - lhs, - expected, - msg), - file, - line); - } + auto wins = nix2Win[nix]; + nix2Win[nix] = wins.sort().uniq().array(); } - if(result != expected) + foreach(win; win2Nix.byKey()) { - if(msg.empty) - { - throw new AssertError(format(`_assertPred!"%s" failed: Return value of [%s] %s [%s] was [%s] instead of [%s].`, - op, - origLHSStr, - op, - rhs, - result, - expected), - file, - line); - } - else - { - throw new AssertError(format(`_assertPred!"%s" failed: Return value of [%s] %s [%s] was [%s] instead of [%s]: %s`, - op, - origLHSStr, - op, - rhs, - result, - expected, - msg), - file, - line); - } + auto nixes = win2Nix[win]; + win2Nix[win] = nixes.sort().uniq().array(); } -} - -unittest -{ - version(testStdDateTime) - { - assertNotThrown!AssertError(_assertPred!"+="(7, 5, 12)); - assertNotThrown!AssertError(_assertPred!"-="(7, 5, 2)); - assertNotThrown!AssertError(_assertPred!"*="(7, 5, 35)); - assertNotThrown!AssertError(_assertPred!"/="(7, 5, 1)); - assertNotThrown!AssertError(_assertPred!"%="(7, 5, 2)); - assertNotThrown!AssertError(_assertPred!"^^="(7, 5, 16_807)); - assertNotThrown!AssertError(_assertPred!"&="(7, 5, 5)); - assertNotThrown!AssertError(_assertPred!"|="(7, 5, 7)); - assertNotThrown!AssertError(_assertPred!"^="(7, 5, 2)); - assertNotThrown!AssertError(_assertPred!"<<="(7, 1, 14)); - assertNotThrown!AssertError(_assertPred!">>="(7, 1, 3)); - assertNotThrown!AssertError(_assertPred!">>>="(-7, 1, 2_147_483_644)); - assertNotThrown!AssertError(_assertPred!"~="("hello ", "world", "hello world")); - - assertThrown!AssertError(_assertPred!"+="(7, 5, 0)); - assertThrown!AssertError(_assertPred!"-="(7, 5, 0)); - assertThrown!AssertError(_assertPred!"*="(7, 5, 0)); - assertThrown!AssertError(_assertPred!"/="(7, 5, 0)); - assertThrown!AssertError(_assertPred!"%="(7, 5, 0)); - assertThrown!AssertError(_assertPred!"^^="(7, 5, 0)); - assertThrown!AssertError(_assertPred!"&="(7, 5, 0)); - assertThrown!AssertError(_assertPred!"|="(7, 5, 0)); - assertThrown!AssertError(_assertPred!"^="(7, 5, 0)); - assertThrown!AssertError(_assertPred!"<<="(7, 1, 0)); - assertThrown!AssertError(_assertPred!">>="(7, 1, 0)); - assertThrown!AssertError(_assertPred!">>>="(-7, 1, 0)); - assertThrown!AssertError(_assertPred!"~="("hello ", "world", "goodbye world")); - - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"+="(7, 5, 11)), - `_assertPred!"+=" failed: After [7] += [5], lhs was assigned to [12] instead of [11].`); - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"+="(7, 5, 11, "It failed!")), - `_assertPred!"+=" failed: After [7] += [5], lhs was assigned to [12] instead of [11]: It failed!`); - - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"^^="(7, 5, 42)), - `_assertPred!"^^=" failed: After [7] ^^= [5], lhs was assigned to [16807] instead of [42].`); - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"^^="(7, 5, 42, "It failed!")), - `_assertPred!"^^=" failed: After [7] ^^= [5], lhs was assigned to [16807] instead of [42]: It failed!`); - - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"~="("hello ", "world", "goodbye world")), - `_assertPred!"~=" failed: After [hello ] ~= [world], lhs was assigned to [hello world] instead of [goodbye world].`); - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"~="("hello ", "world", "goodbye world", "It failed!")), - `_assertPred!"~=" failed: After [hello ] ~= [world], lhs was assigned to [hello world] instead of [goodbye world]: It failed!`); - - struct IntWrapper - { - int value; - - this(int value) - { - this.value = value; - } - - IntWrapper opOpAssign(string op)(IntWrapper rhs) - { - mixin("this.value " ~ op ~ "= rhs.value;"); - - return this; - } - string toString() const - { - return to!string(value); - } - } + // AFAIK, there should be no cases of a TZ Database time zone converting to + // multiple windows time zones. + foreach(nix, wins; nix2Win) + enforce(wins.length == 1, format("%s -> %s", nix, wins)); - struct IntWrapper_BadAssign + // We'll try to eliminate multiples by favoring a conversion if it's already + // in Phobos, but if it's new, then the correct one will have to be chosen + // manually from the results. + string[] haveMultiple; + foreach(win, nixes; win2Nix) + { + if(nixes.length > 1) + haveMultiple ~= win; + } + bool[string] haveConflicts; + foreach(win; haveMultiple) + { + if(auto curr = windowsTZNameToTZDatabaseName(win)) { - int value; - - this(int value) - { - this.value = value; - } - - IntWrapper_BadAssign opOpAssign(string op)(IntWrapper_BadAssign rhs) - { - auto old = this.value; - - mixin("this.value " ~ op ~ "= -rhs.value;"); - - return IntWrapper_BadAssign(mixin("old " ~ op ~ " rhs.value")); - } - - string toString() const + if(auto other = curr in nix2Win) { - return to!string(value); + if((*other)[0] == win) + { + win2Nix[win] = [curr]; + continue; + } } } + haveConflicts[win] = true; + writefln("Warning: %s -> %s", win, win2Nix[win]); + } - struct IntWrapper_BadReturn - { - int value; - - this(int value) - { - this.value = value; - } - - IntWrapper_BadReturn opOpAssign(string op)(IntWrapper_BadReturn rhs) - { - mixin("this.value " ~ op ~ "= rhs.value;"); - return IntWrapper_BadReturn(rhs.value); - } + string[] nix2WinLines = [ + `string tzDatabaseNameToWindowsTZName(string tzName) @safe pure nothrow @nogc`, + `{`, + ` switch(tzName)`, + ` {`]; - string toString() const - { - return to!string(value); - } - } + foreach(nix; nix2Win.keys.sort()) + nix2WinLines ~= format(` case "%s": return "%s";`, nix, nix2Win[nix][0]); - assertNotThrown!AssertError(_assertPred!"+="(IntWrapper(5), IntWrapper(2), IntWrapper(7))); - assertNotThrown!AssertError(_assertPred!"*="(IntWrapper(5), IntWrapper(2), IntWrapper(10))); + nix2WinLines ~= [ + ` default: return null;`, + ` }`, + `}`]; - assertThrown!AssertError(_assertPred!"+="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(7))); - assertThrown!AssertError(_assertPred!"+="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(7))); - assertThrown!AssertError(_assertPred!"*="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(10))); - assertThrown!AssertError(_assertPred!"*="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(10))); - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"+="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(7))), - `_assertPred!"+=" failed: After [5] += [2], lhs was assigned to [3] instead of [7].`); - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"+="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(7), "It failed!")), - `_assertPred!"+=" failed: After [5] += [2], lhs was assigned to [3] instead of [7]: It failed!`); + string[] win2NixLines = [ + `string windowsTZNameToTZDatabaseName(string tzName) @safe pure nothrow @nogc`, + `{`, + ` switch(tzName)`, + ` {`]; + foreach(win; win2Nix.keys.sort()) + { + immutable hasMultiple = cast(bool)(win in haveConflicts); + foreach(nix; win2Nix[win]) + win2NixLines ~= format(` case "%s": return "%s";%s`, win, nix, hasMultiple ? " FIXME" : ""); + } - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"+="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(7))), - `_assertPred!"+=" failed: Return value of [5] += [2] was [2] instead of [7].`); - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"+="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(7), "It failed!")), - `_assertPred!"+=" failed: Return value of [5] += [2] was [2] instead of [7]: It failed!`); + win2NixLines ~= [ + ` default: return null;`, + ` }`, + `}`]; - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"*="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(10))), - `_assertPred!"*=" failed: After [5] *= [2], lhs was assigned to [-10] instead of [10].`); - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"*="(IntWrapper_BadAssign(5), IntWrapper_BadAssign(2), IntWrapper_BadAssign(10), "It failed!")), - `_assertPred!"*=" failed: After [5] *= [2], lhs was assigned to [-10] instead of [10]: It failed!`); - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"*="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(10))), - `_assertPred!"*=" failed: Return value of [5] *= [2] was [2] instead of [10].`); - _assertPred!"=="(collectExceptionMsg!AssertError(_assertPred!"*="(IntWrapper_BadReturn(5), IntWrapper_BadReturn(2), IntWrapper_BadReturn(10), "It failed!")), - `_assertPred!"*=" failed: Return value of [5] *= [2] was [2] instead of [10]: It failed!`); - } -} + auto nix2WinFile = args[2]; + std.file.write(nix2WinFile, nix2WinLines.join("\n")); -unittest -{ - /* Issue 6642 */ - static assert(!hasUnsharedAliasing!Date); - static assert(!hasUnsharedAliasing!TimeOfDay); - static assert(!hasUnsharedAliasing!DateTime); - static assert(!hasUnsharedAliasing!SysTime); -} + auto win2NixFile = args[3]; + std.file.write(win2NixFile, win2NixLines.join("\n")); -template _isPrintable(T...) -{ - static if(T.length == 0) - enum _isPrintable = true; - else static if(T.length == 1) - { - enum _isPrintable = (!isArray!(T[0]) && __traits(compiles, to!string(T[0].init))) || - (isArray!(T[0]) && __traits(compiles, to!string(T[0].init[0]))); - } - else - { - enum _isPrintable = _isPrintable!(T[0]) && _isPrintable!(T[1 .. $]); - } + return 0; } ++/ diff --git a/libphobos/src/std/digest/crc.d b/libphobos/src/std/digest/crc.d index c900eb90b..5e74006df 100644 --- a/libphobos/src/std/digest/crc.d +++ b/libphobos/src/std/digest/crc.d @@ -296,7 +296,7 @@ unittest digest = crc32Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); assert(digest == cast(ubyte[])x"d2e6c21f"); - digest = crc32Of("1234567890123456789012345678901234567890" + digest = crc32Of("1234567890123456789012345678901234567890"~ "1234567890123456789012345678901234567890"); assert(digest == cast(ubyte[])x"724aa97c"); @@ -317,9 +317,9 @@ auto crc32Of(T...)(T data) * This is a convenience alias for $(XREF digest.digest, toHexString) producing the usual * CRC32 string output. */ -public alias toHexString!(Order.decreasing) crcHexString; +public alias crcHexString = toHexString!(Order.decreasing); ///ditto -public alias toHexString!(Order.decreasing, 16) crcHexString; +public alias crcHexString = toHexString!(Order.decreasing, 16); /// unittest @@ -335,7 +335,7 @@ unittest * This is an alias for $(XREF digest.digest, WrapperDigest)!CRC32, see * $(XREF digest.digest, WrapperDigest) for more information. */ -alias WrapperDigest!CRC32 CRC32Digest; +alias CRC32Digest = WrapperDigest!CRC32; /// unittest diff --git a/libphobos/src/std/digest/digest.d b/libphobos/src/std/digest/digest.d index 9f4b65c6d..2019ec65a 100644 --- a/libphobos/src/std/digest/digest.d +++ b/libphobos/src/std/digest/digest.d @@ -67,7 +67,7 @@ module std.digest.digest; import std.exception, std.range, std.traits; import std.algorithm : copy; import std.typetuple : allSatisfy; -import std.ascii : LetterCase; +public import std.ascii : LetterCase; /// unittest @@ -318,11 +318,12 @@ template DigestType(T) { static if(isDigest!T) { - alias ReturnType!(typeof( + alias DigestType = + ReturnType!(typeof( { T dig = void; return dig.finish(); - })) DigestType; + })); } else static assert(false, T.stringof ~ " is not a digest! (fails isDigest!T)"); @@ -883,7 +884,7 @@ class WrapperDigest(T) if(isDigest!T) : Digest } body { - enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " + enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " ~ "big, check " ~ typeof(this).stringof ~ ".length!"; asArray!(digestLength!T)(buf, msg) = _digest.finish(); return buf[0 .. digestLength!T]; @@ -919,7 +920,7 @@ class WrapperDigest(T) if(isDigest!T) : Digest } body { - enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " + enum string msg = "Buffer needs to be at least " ~ digestLength!(T).stringof ~ " bytes " ~ "big, check " ~ typeof(this).stringof ~ ".length!"; asArray!(digestLength!T)(buf, msg) = _digest.peek(); return buf[0 .. digestLength!T]; diff --git a/libphobos/src/std/digest/md.d b/libphobos/src/std/digest/md.d index 9f4dcbdd8..00def7958 100644 --- a/libphobos/src/std/digest/md.d +++ b/libphobos/src/std/digest/md.d @@ -467,7 +467,7 @@ unittest digest = md5Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); assert(digest == cast(ubyte[])x"d174ab98d277d9f5a5611c2c9f419d9f"); - digest = md5Of("1234567890123456789012345678901234567890" + digest = md5Of("1234567890123456789012345678901234567890"~ "1234567890123456789012345678901234567890"); assert(digest == cast(ubyte[])x"57edf4a22be3c955ac49da2e2107b67a"); @@ -508,7 +508,7 @@ unittest * This is an alias for $(XREF digest.digest, WrapperDigest)!MD5, see * $(XREF digest.digest, WrapperDigest) for more information. */ -alias WrapperDigest!MD5 MD5Digest; +alias MD5Digest = WrapperDigest!MD5; /// unittest diff --git a/libphobos/src/std/digest/ripemd.d b/libphobos/src/std/digest/ripemd.d index 67d471efe..1e59d4940 100644 --- a/libphobos/src/std/digest/ripemd.d +++ b/libphobos/src/std/digest/ripemd.d @@ -629,7 +629,7 @@ unittest digest = ripemd160Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); assert(digest == cast(ubyte[])x"b0e20b6e3116640286ed3a87a5713079b21f5189"); - digest = ripemd160Of("1234567890123456789012345678901234567890" + digest = ripemd160Of("1234567890123456789012345678901234567890"~ "1234567890123456789012345678901234567890"); assert(digest == cast(ubyte[])x"9b752e45573d4b39f4dbd3323cab82bf63326bfb"); @@ -670,7 +670,7 @@ unittest * This is an alias for $(XREF digest.digest, WrapperDigest)!RIPEMD160, see * $(XREF digest.digest, WrapperDigest) for more information. */ -alias WrapperDigest!RIPEMD160 RIPEMD160Digest; +alias RIPEMD160Digest = WrapperDigest!RIPEMD160; /// unittest diff --git a/libphobos/src/std/digest/sha.d b/libphobos/src/std/digest/sha.d index f7c418051..7bf0f54c8 100644 --- a/libphobos/src/std/digest/sha.d +++ b/libphobos/src/std/digest/sha.d @@ -14,8 +14,12 @@ $(TR $(TDNW Helpers) $(TD $(MYREF sha1Of)) ) ) - * Computes SHA1 hashes of arbitrary data. SHA1 hashes are 20 byte quantities - * that are like a checksum or CRC, but are more robust. + * Computes SHA1 and SHA2 hashes of arbitrary data. SHA hashes are 20 to 64 byte + * quantities (depending on the SHA algorithm) that are like a checksum or CRC, + * but are more robust. + * + * SHA2 comes in several different versions, all supported by this module: + * SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224 and SHA-512/256. * * This module conforms to the APIs defined in $(D std.digest.digest). To understand the * differences between the template and the OOP API, see $(D std.digest.digest). @@ -31,7 +35,7 @@ $(TR $(TDNW Helpers) $(TD $(MYREF sha1Of)) * Authors: * The routines and algorithms are derived from the * $(I Secure Hash Signature Standard (SHS) (FIPS PUB 180-2)). $(BR ) - * Kai Nacke, Johannes Pfau + * Kai Nacke, Johannes Pfau, Nick Sabalausky * * References: * $(UL @@ -60,17 +64,20 @@ unittest //Template API import std.digest.sha; - ubyte[20] hash = sha1Of("abc"); - assert(toHexString(hash) == "A9993E364706816ABA3E25717850C26C9CD0D89D"); + ubyte[20] hash1 = sha1Of("abc"); + assert(toHexString(hash1) == "A9993E364706816ABA3E25717850C26C9CD0D89D"); + + ubyte[28] hash224 = sha224Of("abc"); + assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7"); //Feeding data ubyte[1024] data; - SHA1 sha; - sha.start(); - sha.put(data[]); - sha.start(); //Start again - sha.put(data[]); - hash = sha.finish(); + SHA1 sha1; + sha1.start(); + sha1.put(data[]); + sha1.start(); //Start again + sha1.put(data[]); + hash1 = sha1.finish(); } /// @@ -79,22 +86,30 @@ unittest //OOP API import std.digest.sha; - auto sha = new SHA1Digest(); - ubyte[] hash = sha.digest("abc"); - assert(toHexString(hash) == "A9993E364706816ABA3E25717850C26C9CD0D89D"); + auto sha1 = new SHA1Digest(); + ubyte[] hash1 = sha1.digest("abc"); + assert(toHexString(hash1) == "A9993E364706816ABA3E25717850C26C9CD0D89D"); + + auto sha224 = new SHA224Digest(); + ubyte[] hash224 = sha224.digest("abc"); + assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7"); //Feeding data ubyte[1024] data; - sha.put(data[]); - sha.reset(); //Start again - sha.put(data[]); - hash = sha.finish(); + sha1.put(data[]); + sha1.reset(); //Start again + sha1.put(data[]); + hash1 = sha1.finish(); } version(D_PIC) { // Do not use (Bug9378). } +else version(Win64) +{ + // wrong calling convention +} else version(D_InlineAsm_X86) { private version = USE_SSSE3; @@ -141,6 +156,17 @@ private ubyte[4] nativeToBigEndian(uint val) @trusted pure nothrow return *cast(ubyte[4]*) &res; } +private ulong bigEndianToNative(ubyte[8] val) @trusted pure nothrow +{ + version(LittleEndian) + { + static import std.bitmanip; + return std.bitmanip.bigEndianToNative!ulong(val); + } + else + return *cast(ulong*) &val; +} + private uint bigEndianToNative(ubyte[4] val) @trusted pure nothrow { version(LittleEndian) @@ -157,49 +183,203 @@ private nothrow pure uint rotateLeft(uint x, uint n) return (x << n) | (x >> (32-n)); } +//rotateRight rotates x right n bits +private nothrow pure uint rotateRight(uint x, uint n) +{ + return (x >> n) | (x << (32-n)); +} +private nothrow pure ulong rotateRight(ulong x, uint n) +{ + return (x >> n) | (x << (64-n)); +} + /** - * Template API SHA1 implementation. + * Template API SHA1/SHA2 implementation. Supports: SHA-1, SHA-224, SHA-256, + * SHA-384, SHA-512, SHA-512/224 and SHA-512/256. + * + * The blockSize and digestSize are in bits. However, it's likely easier to + * simply use the convenience aliases: SHA1, SHA224, SHA256, SHA384, SHA512, + * SHA512_224 and SHA512_256. + * * See $(D std.digest.digest) for differences between template and OOP API. */ -struct SHA1 +struct SHA(int blockSize, int digestSize) { - version(USE_SSSE3) + static assert(blockSize == 512 || blockSize == 1024, + "Invalid SHA blockSize, must be 512 or 1024"); + static assert(digestSize == 160 || digestSize == 224 || digestSize == 256 || digestSize == 384 || digestSize == 512, + "Invalid SHA digestSize, must be 224, 256, 384 or 512"); + static assert(!(blockSize == 512 && digestSize > 256), + "Invalid SHA digestSize for a blockSize of 512. The digestSize must be 160, 224 or 256."); + static assert(!(blockSize == 1024 && digestSize < 224), + "Invalid SHA digestSize for a blockSize of 1024. The digestSize must be 224, 256, 384 or 512."); + + static if(digestSize==160) /* SHA-1 */ { - private __gshared immutable nothrow pure void function(uint[5]* state, const(ubyte[64])* block) transform; + version(USE_SSSE3) + { + private __gshared immutable nothrow pure void function(uint[5]* state, const(ubyte[64])* block) transform; - shared static this() + shared static this() + { + transform = hasSSSE3Support ? &transformSSSE3 : &transformX86; + } + } + else { - transform = hasSSSE3Support ? &transformSSSE3 : &transformX86; + alias transform = transformX86; } } + else static if(blockSize == 512) /* SHA-224, SHA-256 */ + alias transform = transformSHA2!uint; + else static if(blockSize == 1024) /* SHA-384, SHA-512, SHA-512/224, SHA-512/256 */ + alias transform = transformSHA2!ulong; else - { - alias transformX86 transform; - } + static assert(0); private: - uint[5] state = /* state (ABCDE) */ - /* magic initialization constants */ - [0x67452301,0xefcdab89,0x98badcfe,0x10325476,0xc3d2e1f0]; + /* magic initialization constants - state (ABCDEFGH) */ + static if(blockSize == 512 && digestSize == 160) /* SHA-1 */ + { + uint[5] state = + [0x67452301,0xefcdab89,0x98badcfe,0x10325476,0xc3d2e1f0]; + } + else static if(blockSize == 512 && digestSize == 224) /* SHA-224 */ + { + uint[8] state = [ + 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, + 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4, + ]; + } + else static if(blockSize == 512 && digestSize == 256) /* SHA-256 */ + { + uint[8] state = [ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, + ]; + } + else static if(blockSize == 1024 && digestSize == 224) /* SHA-512/224 */ + { + ulong[8] state = [ + 0x8C3D37C8_19544DA2, 0x73E19966_89DCD4D6, + 0x1DFAB7AE_32FF9C82, 0x679DD514_582F9FCF, + 0x0F6D2B69_7BD44DA8, 0x77E36F73_04C48942, + 0x3F9D85A8_6A1D36C8, 0x1112E6AD_91D692A1, + ]; + } + else static if(blockSize == 1024 && digestSize == 256) /* SHA-512/256 */ + { + ulong[8] state = [ + 0x22312194_FC2BF72C, 0x9F555FA3_C84C64C2, + 0x2393B86B_6F53B151, 0x96387719_5940EABD, + 0x96283EE2_A88EFFE3, 0xBE5E1E25_53863992, + 0x2B0199FC_2C85B8AA, 0x0EB72DDC_81C52CA2, + ]; + } + else static if(blockSize == 1024 && digestSize == 384) /* SHA-384 */ + { + ulong[8] state = [ + 0xcbbb9d5d_c1059ed8, 0x629a292a_367cd507, + 0x9159015a_3070dd17, 0x152fecd8_f70e5939, + 0x67332667_ffc00b31, 0x8eb44a87_68581511, + 0xdb0c2e0d_64f98fa7, 0x47b5481d_befa4fa4, + ]; + } + else static if(blockSize == 1024 && digestSize == 512) /* SHA-512 */ + { + ulong[8] state = [ + 0x6a09e667_f3bcc908, 0xbb67ae85_84caa73b, + 0x3c6ef372_fe94f82b, 0xa54ff53a_5f1d36f1, + 0x510e527f_ade682d1, 0x9b05688c_2b3e6c1f, + 0x1f83d9ab_fb41bd6b, 0x5be0cd19_137e2179, + ]; + } + else + static assert(0); - ulong count; /* number of bits, modulo 2^64 */ - ubyte[64] buffer; /* input buffer */ + /* constants */ + static if(blockSize == 512) + { + static immutable uint[64] constants = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, + ]; + } + else static if(blockSize == 1024) + { + static immutable ulong[80] constants = [ + 0x428a2f98_d728ae22, 0x71374491_23ef65cd, 0xb5c0fbcf_ec4d3b2f, 0xe9b5dba5_8189dbbc, + 0x3956c25b_f348b538, 0x59f111f1_b605d019, 0x923f82a4_af194f9b, 0xab1c5ed5_da6d8118, + 0xd807aa98_a3030242, 0x12835b01_45706fbe, 0x243185be_4ee4b28c, 0x550c7dc3_d5ffb4e2, + 0x72be5d74_f27b896f, 0x80deb1fe_3b1696b1, 0x9bdc06a7_25c71235, 0xc19bf174_cf692694, + 0xe49b69c1_9ef14ad2, 0xefbe4786_384f25e3, 0x0fc19dc6_8b8cd5b5, 0x240ca1cc_77ac9c65, + 0x2de92c6f_592b0275, 0x4a7484aa_6ea6e483, 0x5cb0a9dc_bd41fbd4, 0x76f988da_831153b5, + 0x983e5152_ee66dfab, 0xa831c66d_2db43210, 0xb00327c8_98fb213f, 0xbf597fc7_beef0ee4, + 0xc6e00bf3_3da88fc2, 0xd5a79147_930aa725, 0x06ca6351_e003826f, 0x14292967_0a0e6e70, + 0x27b70a85_46d22ffc, 0x2e1b2138_5c26c926, 0x4d2c6dfc_5ac42aed, 0x53380d13_9d95b3df, + 0x650a7354_8baf63de, 0x766a0abb_3c77b2a8, 0x81c2c92e_47edaee6, 0x92722c85_1482353b, + 0xa2bfe8a1_4cf10364, 0xa81a664b_bc423001, 0xc24b8b70_d0f89791, 0xc76c51a3_0654be30, + 0xd192e819_d6ef5218, 0xd6990624_5565a910, 0xf40e3585_5771202a, 0x106aa070_32bbd1b8, + 0x19a4c116_b8d2d0c8, 0x1e376c08_5141ab53, 0x2748774c_df8eeb99, 0x34b0bcb5_e19b48a8, + 0x391c0cb3_c5c95a63, 0x4ed8aa4a_e3418acb, 0x5b9cca4f_7763e373, 0x682e6ff3_d6b2b8a3, + 0x748f82ee_5defb2fc, 0x78a5636f_43172f60, 0x84c87814_a1f0ab72, 0x8cc70208_1a6439ec, + 0x90befffa_23631e28, 0xa4506ceb_de82bde9, 0xbef9a3f7_b2c67915, 0xc67178f2_e372532b, + 0xca273ece_ea26619c, 0xd186b8c7_21c0c207, 0xeada7dd6_cde0eb1e, 0xf57d4f7f_ee6ed178, + 0x06f067aa_72176fba, 0x0a637dc5_a2c898a6, 0x113f9804_bef90dae, 0x1b710b35_131c471b, + 0x28db77f5_23047d84, 0x32caab7b_40c72493, 0x3c9ebe0a_15c9bebc, 0x431d67c4_9c100d4c, + 0x4cc5d4be_cb3e42b6, 0x597f299c_fc657e2a, 0x5fcb6fab_3ad6faec, 0x6c44198c_4a475817, + ]; + } + else + static assert(0); + + /* + * number of bits, modulo 2^64 (ulong[1]) or 2^128 (ulong[2]), + * should just use ucent instead of ulong[2] once it's available + */ + ulong[blockSize/512] count; + ubyte[blockSize/8] buffer; /* input buffer */ - enum ubyte[64] padding = + enum ubyte[128] padding = [ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; /* - * Ch, Parity and Maj are basic SHA1 functions. + * Basic SHA1/SHA2 functions. */ static pure nothrow { - uint Ch(uint x, uint y, uint z) { return z ^ (x & (y ^ z)); } + /* All SHA1/SHA2 */ + T Ch(T)(T x, T y, T z) { return z ^ (x & (y ^ z)); } + T Maj(T)(T x, T y, T z) { return (x & y) | (z & (x ^ y)); } + + /* SHA-1 */ uint Parity(uint x, uint y, uint z) { return x ^ y ^ z; } - uint Maj(uint x, uint y, uint z) { return (x & y) | (z & (x ^ y)); } + + /* SHA-224, SHA-256 */ + uint BigSigma0(uint x) { return rotateRight(x, 2) ^ rotateRight(x, 13) ^ rotateRight(x, 22); } + uint BigSigma1(uint x) { return rotateRight(x, 6) ^ rotateRight(x, 11) ^ rotateRight(x, 25); } + uint SmSigma0(uint x) { return rotateRight(x, 7) ^ rotateRight(x, 18) ^ x >> 3; } + uint SmSigma1(uint x) { return rotateRight(x, 17) ^ rotateRight(x, 19) ^ x >> 10; } + + /* SHA-384, SHA-512, SHA-512/224, SHA-512/256 */ + ulong BigSigma0(ulong x) { return rotateRight(x, 28) ^ rotateRight(x, 34) ^ rotateRight(x, 39); } + ulong BigSigma1(ulong x) { return rotateRight(x, 14) ^ rotateRight(x, 18) ^ rotateRight(x, 41); } + ulong SmSigma0(ulong x) { return rotateRight(x, 1) ^ rotateRight(x, 8) ^ x >> 7; } + ulong SmSigma1(ulong x) { return rotateRight(x, 19) ^ rotateRight(x, 61) ^ x >> 6; } } /* @@ -346,12 +526,147 @@ struct SHA1 W[] = 0; } + /* + * SHA2 basic transformation. Transforms state based on block. + */ + static nothrow pure void T_SHA2_0_15(Word)(int i, const(ubyte[blockSize/8])* input, ref Word[16] W, + Word A, Word B, Word C, ref Word D, Word E, Word F, Word G, ref Word H, Word K) + { + Word Wi = W[i] = bigEndianToNative(*cast(ubyte[Word.sizeof]*)&((*input)[i*Word.sizeof])); + Word T1 = H + BigSigma1(E) + Ch(E, F, G) + K + Wi; + Word T2 = BigSigma0(A) + Maj(A, B, C); + D += T1; + H = T1 + T2; + } + + static nothrow pure void T_SHA2_16_79(Word)(int i, ref Word[16] W, + Word A, Word B, Word C, ref Word D, Word E, Word F, Word G, ref Word H, Word K) + { + W[i&15] = SmSigma1(W[(i-2)&15]) + W[(i-7)&15] + SmSigma0(W[(i-15)&15]) + W[i&15]; + Word T1 = H + BigSigma1(E) + Ch(E, F, G) + K + W[i&15]; + Word T2 = BigSigma0(A) + Maj(A, B, C); + D += T1; + H = T1 + T2; + } + + private static nothrow pure void transformSHA2(Word)(Word[8]* state, const(ubyte[blockSize/8])* block) + { + Word A, B, C, D, E, F, G, H; + Word[16] W = void; + + A = (*state)[0]; + B = (*state)[1]; + C = (*state)[2]; + D = (*state)[3]; + E = (*state)[4]; + F = (*state)[5]; + G = (*state)[6]; + H = (*state)[7]; + + T_SHA2_0_15!Word ( 0, block, W, A, B, C, D, E, F, G, H, constants[ 0]); + T_SHA2_0_15!Word ( 1, block, W, H, A, B, C, D, E, F, G, constants[ 1]); + T_SHA2_0_15!Word ( 2, block, W, G, H, A, B, C, D, E, F, constants[ 2]); + T_SHA2_0_15!Word ( 3, block, W, F, G, H, A, B, C, D, E, constants[ 3]); + T_SHA2_0_15!Word ( 4, block, W, E, F, G, H, A, B, C, D, constants[ 4]); + T_SHA2_0_15!Word ( 5, block, W, D, E, F, G, H, A, B, C, constants[ 5]); + T_SHA2_0_15!Word ( 6, block, W, C, D, E, F, G, H, A, B, constants[ 6]); + T_SHA2_0_15!Word ( 7, block, W, B, C, D, E, F, G, H, A, constants[ 7]); + T_SHA2_0_15!Word ( 8, block, W, A, B, C, D, E, F, G, H, constants[ 8]); + T_SHA2_0_15!Word ( 9, block, W, H, A, B, C, D, E, F, G, constants[ 9]); + T_SHA2_0_15!Word (10, block, W, G, H, A, B, C, D, E, F, constants[10]); + T_SHA2_0_15!Word (11, block, W, F, G, H, A, B, C, D, E, constants[11]); + T_SHA2_0_15!Word (12, block, W, E, F, G, H, A, B, C, D, constants[12]); + T_SHA2_0_15!Word (13, block, W, D, E, F, G, H, A, B, C, constants[13]); + T_SHA2_0_15!Word (14, block, W, C, D, E, F, G, H, A, B, constants[14]); + T_SHA2_0_15!Word (15, block, W, B, C, D, E, F, G, H, A, constants[15]); + T_SHA2_16_79!Word(16, W, A, B, C, D, E, F, G, H, constants[16]); + T_SHA2_16_79!Word(17, W, H, A, B, C, D, E, F, G, constants[17]); + T_SHA2_16_79!Word(18, W, G, H, A, B, C, D, E, F, constants[18]); + T_SHA2_16_79!Word(19, W, F, G, H, A, B, C, D, E, constants[19]); + T_SHA2_16_79!Word(20, W, E, F, G, H, A, B, C, D, constants[20]); + T_SHA2_16_79!Word(21, W, D, E, F, G, H, A, B, C, constants[21]); + T_SHA2_16_79!Word(22, W, C, D, E, F, G, H, A, B, constants[22]); + T_SHA2_16_79!Word(23, W, B, C, D, E, F, G, H, A, constants[23]); + T_SHA2_16_79!Word(24, W, A, B, C, D, E, F, G, H, constants[24]); + T_SHA2_16_79!Word(25, W, H, A, B, C, D, E, F, G, constants[25]); + T_SHA2_16_79!Word(26, W, G, H, A, B, C, D, E, F, constants[26]); + T_SHA2_16_79!Word(27, W, F, G, H, A, B, C, D, E, constants[27]); + T_SHA2_16_79!Word(28, W, E, F, G, H, A, B, C, D, constants[28]); + T_SHA2_16_79!Word(29, W, D, E, F, G, H, A, B, C, constants[29]); + T_SHA2_16_79!Word(30, W, C, D, E, F, G, H, A, B, constants[30]); + T_SHA2_16_79!Word(31, W, B, C, D, E, F, G, H, A, constants[31]); + T_SHA2_16_79!Word(32, W, A, B, C, D, E, F, G, H, constants[32]); + T_SHA2_16_79!Word(33, W, H, A, B, C, D, E, F, G, constants[33]); + T_SHA2_16_79!Word(34, W, G, H, A, B, C, D, E, F, constants[34]); + T_SHA2_16_79!Word(35, W, F, G, H, A, B, C, D, E, constants[35]); + T_SHA2_16_79!Word(36, W, E, F, G, H, A, B, C, D, constants[36]); + T_SHA2_16_79!Word(37, W, D, E, F, G, H, A, B, C, constants[37]); + T_SHA2_16_79!Word(38, W, C, D, E, F, G, H, A, B, constants[38]); + T_SHA2_16_79!Word(39, W, B, C, D, E, F, G, H, A, constants[39]); + T_SHA2_16_79!Word(40, W, A, B, C, D, E, F, G, H, constants[40]); + T_SHA2_16_79!Word(41, W, H, A, B, C, D, E, F, G, constants[41]); + T_SHA2_16_79!Word(42, W, G, H, A, B, C, D, E, F, constants[42]); + T_SHA2_16_79!Word(43, W, F, G, H, A, B, C, D, E, constants[43]); + T_SHA2_16_79!Word(44, W, E, F, G, H, A, B, C, D, constants[44]); + T_SHA2_16_79!Word(45, W, D, E, F, G, H, A, B, C, constants[45]); + T_SHA2_16_79!Word(46, W, C, D, E, F, G, H, A, B, constants[46]); + T_SHA2_16_79!Word(47, W, B, C, D, E, F, G, H, A, constants[47]); + T_SHA2_16_79!Word(48, W, A, B, C, D, E, F, G, H, constants[48]); + T_SHA2_16_79!Word(49, W, H, A, B, C, D, E, F, G, constants[49]); + T_SHA2_16_79!Word(50, W, G, H, A, B, C, D, E, F, constants[50]); + T_SHA2_16_79!Word(51, W, F, G, H, A, B, C, D, E, constants[51]); + T_SHA2_16_79!Word(52, W, E, F, G, H, A, B, C, D, constants[52]); + T_SHA2_16_79!Word(53, W, D, E, F, G, H, A, B, C, constants[53]); + T_SHA2_16_79!Word(54, W, C, D, E, F, G, H, A, B, constants[54]); + T_SHA2_16_79!Word(55, W, B, C, D, E, F, G, H, A, constants[55]); + T_SHA2_16_79!Word(56, W, A, B, C, D, E, F, G, H, constants[56]); + T_SHA2_16_79!Word(57, W, H, A, B, C, D, E, F, G, constants[57]); + T_SHA2_16_79!Word(58, W, G, H, A, B, C, D, E, F, constants[58]); + T_SHA2_16_79!Word(59, W, F, G, H, A, B, C, D, E, constants[59]); + T_SHA2_16_79!Word(60, W, E, F, G, H, A, B, C, D, constants[60]); + T_SHA2_16_79!Word(61, W, D, E, F, G, H, A, B, C, constants[61]); + T_SHA2_16_79!Word(62, W, C, D, E, F, G, H, A, B, constants[62]); + T_SHA2_16_79!Word(63, W, B, C, D, E, F, G, H, A, constants[63]); + + static if(is(Word==ulong)) + { + T_SHA2_16_79!Word(64, W, A, B, C, D, E, F, G, H, constants[64]); + T_SHA2_16_79!Word(65, W, H, A, B, C, D, E, F, G, constants[65]); + T_SHA2_16_79!Word(66, W, G, H, A, B, C, D, E, F, constants[66]); + T_SHA2_16_79!Word(67, W, F, G, H, A, B, C, D, E, constants[67]); + T_SHA2_16_79!Word(68, W, E, F, G, H, A, B, C, D, constants[68]); + T_SHA2_16_79!Word(69, W, D, E, F, G, H, A, B, C, constants[69]); + T_SHA2_16_79!Word(70, W, C, D, E, F, G, H, A, B, constants[70]); + T_SHA2_16_79!Word(71, W, B, C, D, E, F, G, H, A, constants[71]); + T_SHA2_16_79!Word(72, W, A, B, C, D, E, F, G, H, constants[72]); + T_SHA2_16_79!Word(73, W, H, A, B, C, D, E, F, G, constants[73]); + T_SHA2_16_79!Word(74, W, G, H, A, B, C, D, E, F, constants[74]); + T_SHA2_16_79!Word(75, W, F, G, H, A, B, C, D, E, constants[75]); + T_SHA2_16_79!Word(76, W, E, F, G, H, A, B, C, D, constants[76]); + T_SHA2_16_79!Word(77, W, D, E, F, G, H, A, B, C, constants[77]); + T_SHA2_16_79!Word(78, W, C, D, E, F, G, H, A, B, constants[78]); + T_SHA2_16_79!Word(79, W, B, C, D, E, F, G, H, A, constants[79]); + } + + (*state)[0] += A; + (*state)[1] += B; + (*state)[2] += C; + (*state)[3] += D; + (*state)[4] += E; + (*state)[5] += F; + (*state)[6] += G; + (*state)[7] += H; + + /* Zeroize sensitive information. */ + W[] = 0; + } + public: /** - * SHA1 initialization. Begins a SHA1 operation. + * SHA initialization. Begins an SHA1/SHA2 operation. * * Note: - * For this SHA1 Digest implementation calling start after default construction + * For this SHA Digest implementation calling start after default construction * is not necessary. Calling start is only necessary to reset the Digest. * * Generic code which deals with different Digest types should always call start though. @@ -365,7 +680,7 @@ struct SHA1 */ @trusted nothrow pure void start() { - this = SHA1.init; + this = typeof(this).init; } /** @@ -375,16 +690,28 @@ struct SHA1 */ @trusted nothrow pure void put(scope const(ubyte)[] input...) { + enum blockSizeInBytes = blockSize/8; uint i, index, partLen; auto inputLen = input.length; - /* Compute number of bytes mod 64 */ - index = (cast(uint)count >> 3) & (64 - 1); + /* Compute number of bytes mod block size (64 or 128 bytes) */ + index = (cast(uint)count[0] >> 3) & (blockSizeInBytes - 1); /* Update number of bits */ - count += inputLen * 8; + static if(blockSize==512) + count[0] += inputLen * 8; + else static if(blockSize==1024) + { + /* ugly hack to work around lack of ucent */ + auto oldCount0 = count[0]; + count[0] += inputLen * 8; + if(count[0] < oldCount0) + count[1]++; + } + else + static assert(0); - partLen = 64 - index; + partLen = blockSizeInBytes - index; /* Transform as many times as possible. */ if (inputLen >= partLen) @@ -392,8 +719,8 @@ struct SHA1 (&buffer[index])[0 .. partLen] = input.ptr[0 .. partLen]; transform (&state, &buffer); - for (i = partLen; i + 63 < inputLen; i += 64) - transform(&state, cast(ubyte[64]*)(input.ptr + i)); + for (i = partLen; i + blockSizeInBytes-1 < inputLen; i += blockSizeInBytes) + transform(&state, cast(ubyte[blockSizeInBytes]*)(input.ptr + i)); index = 0; } @@ -407,7 +734,7 @@ struct SHA1 /// unittest { - SHA1 dig; + typeof(this) dig; dig.put(cast(ubyte)0); //single ubyte dig.put(cast(ubyte)0, cast(ubyte)0); //variadic ubyte[10] buf; @@ -416,32 +743,63 @@ struct SHA1 /** - * Returns the finished SHA1 hash. This also calls $(LREF start) to + * Returns the finished SHA hash. This also calls $(LREF start) to * reset the internal state. */ - @trusted nothrow pure ubyte[20] finish() + @trusted nothrow pure ubyte[digestSize/8] finish() { - ubyte[20] data = void; - uint index, padLen; + static if(blockSize==512) + { + ubyte[32] data = void; + uint index, padLen; - /* Save number of bits */ - ubyte bits[8] = nativeToBigEndian(count); + /* Save number of bits */ + ubyte bits[8] = nativeToBigEndian(count[0]); - /* Pad out to 56 mod 64. */ - index = (cast(uint)count >> 3) & (64 - 1); - padLen = (index < 56) ? (56 - index) : (120 - index); - put(padding[0 .. padLen]); + /* Pad out to 56 mod 64. */ + index = (cast(uint)count[0] >> 3) & (64 - 1); + padLen = (index < 56) ? (56 - index) : (120 - index); + put(padding[0 .. padLen]); - /* Append length (before padding) */ - put(bits); + /* Append length (before padding) */ + put(bits); - /* Store state in digest */ - for (auto i = 0; i < 5; i++) - data[i*4..(i+1)*4] = nativeToBigEndian(state[i])[]; + /* Store state in digest */ + for (auto i = 0; i < ((digestSize == 160)? 5 : 8); i++) + data[i*4..(i+1)*4] = nativeToBigEndian(state[i])[]; - /* Zeroize sensitive information. */ - start(); - return data; + /* Zeroize sensitive information. */ + start(); + return data[0..digestSize/8]; + } + else static if(blockSize==1024) + { + ubyte[64] data = void; + uint index, padLen; + + /* Save number of bits */ + ubyte bits[16]; + bits[ 0..8] = nativeToBigEndian(count[1]); + bits[8..16] = nativeToBigEndian(count[0]); + + /* Pad out to 112 mod 128. */ + index = (cast(uint)count[0] >> 3) & (128 - 1); + padLen = (index < 112) ? (112 - index) : (240 - index); + put(padding[0 .. padLen]); + + /* Append length (before padding) */ + put(bits); + + /* Store state in digest */ + for (auto i = 0; i < 8; i++) + data[i*8..(i+1)*8] = nativeToBigEndian(state[i])[]; + + /* Zeroize sensitive information. */ + start(); + return data[0..digestSize/8]; + } + else + static assert(0); } /// unittest @@ -454,6 +812,14 @@ struct SHA1 } } +alias SHA1 = SHA!(512, 160); /// SHA alias for SHA-1, hash is ubyte[20] +alias SHA224 = SHA!(512, 224); /// SHA alias for SHA-224, hash is ubyte[28] +alias SHA256 = SHA!(512, 256); /// SHA alias for SHA-256, hash is ubyte[32] +alias SHA384 = SHA!(1024, 384); /// SHA alias for SHA-384, hash is ubyte[48] +alias SHA512 = SHA!(1024, 512); /// SHA alias for SHA-512, hash is ubyte[64] +alias SHA512_224 = SHA!(1024, 224); /// SHA alias for SHA-512/224, hash is ubyte[28] +alias SHA512_256 = SHA!(1024, 256); /// SHA alias for SHA-512/256, hash is ubyte[32] + /// unittest { @@ -461,6 +827,10 @@ unittest ubyte[20] hash = sha1Of("abc"); //Let's get a hash string assert(toHexString(hash) == "A9993E364706816ABA3E25717850C26C9CD0D89D"); + + //The same, but using SHA-224 + ubyte[28] hash224 = sha224Of("abc"); + assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7"); } /// @@ -479,7 +849,7 @@ unittest unittest { //Let's use the template features: - //Note: When passing a SHA1 to a function, it must be passed by referece! + //Note: When passing a SHA1 to a function, it must be passed by reference! void doSomething(T)(ref T hash) if(isDigest!T) { hash.put(cast(ubyte)0); @@ -493,6 +863,12 @@ unittest unittest { assert(isDigest!SHA1); + assert(isDigest!SHA224); + assert(isDigest!SHA256); + assert(isDigest!SHA384); + assert(isDigest!SHA512); + assert(isDigest!SHA512_224); + assert(isDigest!SHA512_256); } unittest @@ -500,6 +876,12 @@ unittest import std.range; ubyte[20] digest; + ubyte[28] digest224; + ubyte[32] digest256; + ubyte[48] digest384; + ubyte[64] digest512; + ubyte[28] digest512_224; + ubyte[32] digest512_256; SHA1 sha; sha.put(cast(ubyte[])"abcdef"); @@ -507,59 +889,269 @@ unittest sha.put(cast(ubyte[])""); assert(sha.finish() == cast(ubyte[])x"da39a3ee5e6b4b0d3255bfef95601890afd80709"); - digest = sha1Of(""); + SHA224 sha224; + sha224.put(cast(ubyte[])"abcdef"); + sha224.start(); + sha224.put(cast(ubyte[])""); + assert(sha224.finish() == cast(ubyte[])x"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + + SHA256 sha256; + sha256.put(cast(ubyte[])"abcdef"); + sha256.start(); + sha256.put(cast(ubyte[])""); + assert(sha256.finish() == cast(ubyte[])x"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + + SHA384 sha384; + sha384.put(cast(ubyte[])"abcdef"); + sha384.start(); + sha384.put(cast(ubyte[])""); + assert(sha384.finish() == cast(ubyte[])x"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"); + + SHA512 sha512; + sha512.put(cast(ubyte[])"abcdef"); + sha512.start(); + sha512.put(cast(ubyte[])""); + assert(sha512.finish() == cast(ubyte[])x"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); + + SHA512_224 sha512_224; + sha512_224.put(cast(ubyte[])"abcdef"); + sha512_224.start(); + sha512_224.put(cast(ubyte[])""); + assert(sha512_224.finish() == cast(ubyte[])x"6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"); + + SHA512_256 sha512_256; + sha512_256.put(cast(ubyte[])"abcdef"); + sha512_256.start(); + sha512_256.put(cast(ubyte[])""); + assert(sha512_256.finish() == cast(ubyte[])x"c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"); + + digest = sha1Of (""); + digest224 = sha224Of (""); + digest256 = sha256Of (""); + digest384 = sha384Of (""); + digest512 = sha512Of (""); + digest512_224 = sha512_224Of(""); + digest512_256 = sha512_256Of(""); assert(digest == cast(ubyte[])x"da39a3ee5e6b4b0d3255bfef95601890afd80709"); - - digest = sha1Of("a"); + assert(digest224 == cast(ubyte[])x"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f"); + assert(digest256 == cast(ubyte[])x"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + assert(digest384 == cast(ubyte[])x"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"); + assert(digest512 == cast(ubyte[])x"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"); + assert(digest512_224 == cast(ubyte[])x"6ed0dd02806fa89e25de060c19d3ac86cabb87d6a0ddd05c333b84f4"); + assert(digest512_256 == cast(ubyte[])x"c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a"); + + digest = sha1Of ("a"); + digest224 = sha224Of ("a"); + digest256 = sha256Of ("a"); + digest384 = sha384Of ("a"); + digest512 = sha512Of ("a"); + digest512_224 = sha512_224Of("a"); + digest512_256 = sha512_256Of("a"); assert(digest == cast(ubyte[])x"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"); - - digest = sha1Of("abc"); + assert(digest224 == cast(ubyte[])x"abd37534c7d9a2efb9465de931cd7055ffdb8879563ae98078d6d6d5"); + assert(digest256 == cast(ubyte[])x"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb"); + assert(digest384 == cast(ubyte[])x"54a59b9f22b0b80880d8427e548b7c23abd873486e1f035dce9cd697e85175033caa88e6d57bc35efae0b5afd3145f31"); + assert(digest512 == cast(ubyte[])x"1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75"); + assert(digest512_224 == cast(ubyte[])x"d5cdb9ccc769a5121d4175f2bfdd13d6310e0d3d361ea75d82108327"); + assert(digest512_256 == cast(ubyte[])x"455e518824bc0601f9fb858ff5c37d417d67c2f8e0df2babe4808858aea830f8"); + + digest = sha1Of ("abc"); + digest224 = sha224Of ("abc"); + digest256 = sha256Of ("abc"); + digest384 = sha384Of ("abc"); + digest512 = sha512Of ("abc"); + digest512_224 = sha512_224Of("abc"); + digest512_256 = sha512_256Of("abc"); assert(digest == cast(ubyte[])x"a9993e364706816aba3e25717850c26c9cd0d89d"); - - digest = sha1Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); + assert(digest224 == cast(ubyte[])x"23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7"); + assert(digest256 == cast(ubyte[])x"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); + assert(digest384 == cast(ubyte[])x"cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"); + assert(digest512 == cast(ubyte[])x"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"); + assert(digest512_224 == cast(ubyte[])x"4634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa"); + assert(digest512_256 == cast(ubyte[])x"53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23"); + + digest = sha1Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); + digest224 = sha224Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); + digest256 = sha256Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); + digest384 = sha384Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); + digest512 = sha512Of ("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); + digest512_224 = sha512_224Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); + digest512_256 = sha512_256Of("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); assert(digest == cast(ubyte[])x"84983e441c3bd26ebaae4aa1f95129e5e54670f1"); - - digest = sha1Of("message digest"); + assert(digest224 == cast(ubyte[])x"75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525"); + assert(digest256 == cast(ubyte[])x"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"); + assert(digest384 == cast(ubyte[])x"3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b"); + assert(digest512 == cast(ubyte[])x"204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445"); + assert(digest512_224 == cast(ubyte[])x"e5302d6d54bb242275d1e7622d68df6eb02dedd13f564c13dbda2174"); + assert(digest512_256 == cast(ubyte[])x"bde8e1f9f19bb9fd3406c90ec6bc47bd36d8ada9f11880dbc8a22a7078b6a461"); + + digest = sha1Of ("message digest"); + digest224 = sha224Of ("message digest"); + digest256 = sha256Of ("message digest"); + digest384 = sha384Of ("message digest"); + digest512 = sha512Of ("message digest"); + digest512_224 = sha512_224Of("message digest"); + digest512_256 = sha512_256Of("message digest"); assert(digest == cast(ubyte[])x"c12252ceda8be8994d5fa0290a47231c1d16aae3"); - - digest = sha1Of("abcdefghijklmnopqrstuvwxyz"); + assert(digest224 == cast(ubyte[])x"2cb21c83ae2f004de7e81c3c7019cbcb65b71ab656b22d6d0c39b8eb"); + assert(digest256 == cast(ubyte[])x"f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650"); + assert(digest384 == cast(ubyte[])x"473ed35167ec1f5d8e550368a3db39be54639f828868e9454c239fc8b52e3c61dbd0d8b4de1390c256dcbb5d5fd99cd5"); + assert(digest512 == cast(ubyte[])x"107dbf389d9e9f71a3a95f6c055b9251bc5268c2be16d6c13492ea45b0199f3309e16455ab1e96118e8a905d5597b72038ddb372a89826046de66687bb420e7c"); + assert(digest512_224 == cast(ubyte[])x"ad1a4db188fe57064f4f24609d2a83cd0afb9b398eb2fcaeaae2c564"); + assert(digest512_256 == cast(ubyte[])x"0cf471fd17ed69d990daf3433c89b16d63dec1bb9cb42a6094604ee5d7b4e9fb"); + + digest = sha1Of ("abcdefghijklmnopqrstuvwxyz"); + digest224 = sha224Of ("abcdefghijklmnopqrstuvwxyz"); + digest256 = sha256Of ("abcdefghijklmnopqrstuvwxyz"); + digest384 = sha384Of ("abcdefghijklmnopqrstuvwxyz"); + digest512 = sha512Of ("abcdefghijklmnopqrstuvwxyz"); + digest512_224 = sha512_224Of("abcdefghijklmnopqrstuvwxyz"); + digest512_256 = sha512_256Of("abcdefghijklmnopqrstuvwxyz"); assert(digest == cast(ubyte[])x"32d10c7b8cf96570ca04ce37f2a19d84240d3a89"); - - digest = sha1Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + assert(digest224 == cast(ubyte[])x"45a5f72c39c5cff2522eb3429799e49e5f44b356ef926bcf390dccc2"); + assert(digest256 == cast(ubyte[])x"71c480df93d6ae2f1efad1447c66c9525e316218cf51fc8d9ed832f2daf18b73"); + assert(digest384 == cast(ubyte[])x"feb67349df3db6f5924815d6c3dc133f091809213731fe5c7b5f4999e463479ff2877f5f2936fa63bb43784b12f3ebb4"); + assert(digest512 == cast(ubyte[])x"4dbff86cc2ca1bae1e16468a05cb9881c97f1753bce3619034898faa1aabe429955a1bf8ec483d7421fe3c1646613a59ed5441fb0f321389f77f48a879c7b1f1"); + assert(digest512_224 == cast(ubyte[])x"ff83148aa07ec30655c1b40aff86141c0215fe2a54f767d3f38743d8"); + assert(digest512_256 == cast(ubyte[])x"fc3189443f9c268f626aea08a756abe7b726b05f701cb08222312ccfd6710a26"); + + digest = sha1Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + digest224 = sha224Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + digest256 = sha256Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + digest384 = sha384Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + digest512 = sha512Of ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + digest512_224 = sha512_224Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + digest512_256 = sha512_256Of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); assert(digest == cast(ubyte[])x"761c457bf73b14d27e9e9265c46f4b4dda11f940"); - - digest = sha1Of("1234567890123456789012345678901234567890" - "1234567890123456789012345678901234567890"); + assert(digest224 == cast(ubyte[])x"bff72b4fcb7d75e5632900ac5f90d219e05e97a7bde72e740db393d9"); + assert(digest256 == cast(ubyte[])x"db4bfcbd4da0cd85a60c3c37d3fbd8805c77f15fc6b1fdfe614ee0a7c8fdb4c0"); + assert(digest384 == cast(ubyte[])x"1761336e3f7cbfe51deb137f026f89e01a448e3b1fafa64039c1464ee8732f11a5341a6f41e0c202294736ed64db1a84"); + assert(digest512 == cast(ubyte[])x"1e07be23c26a86ea37ea810c8ec7809352515a970e9253c26f536cfc7a9996c45c8370583e0a78fa4a90041d71a4ceab7423f19c71b9d5a3e01249f0bebd5894"); + assert(digest512_224 == cast(ubyte[])x"a8b4b9174b99ffc67d6f49be9981587b96441051e16e6dd036b140d3"); + assert(digest512_256 == cast(ubyte[])x"cdf1cc0effe26ecc0c13758f7b4a48e000615df241284185c39eb05d355bb9c8"); + + digest = sha1Of ("1234567890123456789012345678901234567890"~ + "1234567890123456789012345678901234567890"); + digest224 = sha224Of ("1234567890123456789012345678901234567890"~ + "1234567890123456789012345678901234567890"); + digest256 = sha256Of ("1234567890123456789012345678901234567890"~ + "1234567890123456789012345678901234567890"); + digest384 = sha384Of ("1234567890123456789012345678901234567890"~ + "1234567890123456789012345678901234567890"); + digest512 = sha512Of ("1234567890123456789012345678901234567890"~ + "1234567890123456789012345678901234567890"); + digest512_224 = sha512_224Of("1234567890123456789012345678901234567890"~ + "1234567890123456789012345678901234567890"); + digest512_256 = sha512_256Of("1234567890123456789012345678901234567890"~ + "1234567890123456789012345678901234567890"); assert(digest == cast(ubyte[])x"50abf5706a150990a08b2c5ea40fa0e585554732"); + assert(digest224 == cast(ubyte[])x"b50aecbe4e9bb0b57bc5f3ae760a8e01db24f203fb3cdcd13148046e"); + assert(digest256 == cast(ubyte[])x"f371bc4a311f2b009eef952dd83ca80e2b60026c8e935592d0f9c308453c813e"); + assert(digest384 == cast(ubyte[])x"b12932b0627d1c060942f5447764155655bd4da0c9afa6dd9b9ef53129af1b8fb0195996d2de9ca0df9d821ffee67026"); + assert(digest512 == cast(ubyte[])x"72ec1ef1124a45b047e8b7c75a932195135bb61de24ec0d1914042246e0aec3a2354e093d76f3048b456764346900cb130d2a4fd5dd16abb5e30bcb850dee843"); + assert(digest512_224 == cast(ubyte[])x"ae988faaa47e401a45f704d1272d99702458fea2ddc6582827556dd2"); + assert(digest512_256 == cast(ubyte[])x"2c9fdbc0c90bdd87612ee8455474f9044850241dc105b1e8b94b8ddf5fac9148"); ubyte[] onemilliona = new ubyte[1000000]; onemilliona[] = 'a'; digest = sha1Of(onemilliona); + digest224 = sha224Of(onemilliona); + digest256 = sha256Of(onemilliona); + digest384 = sha384Of(onemilliona); + digest512 = sha512Of(onemilliona); + digest512_224 = sha512_224Of(onemilliona); + digest512_256 = sha512_256Of(onemilliona); assert(digest == cast(ubyte[])x"34aa973cd4c4daa4f61eeb2bdbad27316534016f"); + assert(digest224 == cast(ubyte[])x"20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67"); + assert(digest256 == cast(ubyte[])x"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"); + assert(digest384 == cast(ubyte[])x"9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985"); + assert(digest512 == cast(ubyte[])x"e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"); + assert(digest512_224 == cast(ubyte[])x"37ab331d76f0d36de422bd0edeb22a28accd487b7a8453ae965dd287"); + assert(digest512_256 == cast(ubyte[])x"9a59a052930187a97038cae692f30708aa6491923ef5194394dc68d56c74fb21"); auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000); digest = sha1Of(oneMillionRange); + digest224 = sha224Of(oneMillionRange); + digest256 = sha256Of(oneMillionRange); + digest384 = sha384Of(oneMillionRange); + digest512 = sha512Of(oneMillionRange); + digest512_224 = sha512_224Of(oneMillionRange); + digest512_256 = sha512_256Of(oneMillionRange); assert(digest == cast(ubyte[])x"34aa973cd4c4daa4f61eeb2bdbad27316534016f"); + assert(digest224 == cast(ubyte[])x"20794655980c91d8bbb4c1ea97618a4bf03f42581948b2ee4ee7ad67"); + assert(digest256 == cast(ubyte[])x"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"); + assert(digest384 == cast(ubyte[])x"9d0e1809716474cb086e834e310a4a1ced149e9c00f248527972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985"); + assert(digest512 == cast(ubyte[])x"e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"); + assert(digest512_224 == cast(ubyte[])x"37ab331d76f0d36de422bd0edeb22a28accd487b7a8453ae965dd287"); + assert(digest512_256 == cast(ubyte[])x"9a59a052930187a97038cae692f30708aa6491923ef5194394dc68d56c74fb21"); assert(toHexString(cast(ubyte[20])x"a9993e364706816aba3e25717850c26c9cd0d89d") == "A9993E364706816ABA3E25717850C26C9CD0D89D"); } /** - * This is a convenience alias for $(XREF digest.digest, digest) using the - * SHA1 implementation. + * These are convenience aliases for $(XREF digest.digest, digest) using the + * SHA implementation. */ //simple alias doesn't work here, hope this gets inlined... auto sha1Of(T...)(T data) { return digest!(SHA1, T)(data); } +///ditto +auto sha224Of(T...)(T data) +{ + return digest!(SHA224, T)(data); +} +///ditto +auto sha256Of(T...)(T data) +{ + return digest!(SHA256, T)(data); +} +///ditto +auto sha384Of(T...)(T data) +{ + return digest!(SHA384, T)(data); +} +///ditto +auto sha512Of(T...)(T data) +{ + return digest!(SHA512, T)(data); +} +///ditto +auto sha512_224Of(T...)(T data) +{ + return digest!(SHA512_224, T)(data); +} +///ditto +auto sha512_256Of(T...)(T data) +{ + return digest!(SHA512_256, T)(data); +} /// unittest { ubyte[20] hash = sha1Of("abc"); assert(hash == digest!SHA1("abc")); + + ubyte[28] hash224 = sha224Of("abc"); + assert(hash224 == digest!SHA224("abc")); + + ubyte[32] hash256 = sha256Of("abc"); + assert(hash256 == digest!SHA256("abc")); + + ubyte[48] hash384 = sha384Of("abc"); + assert(hash384 == digest!SHA384("abc")); + + ubyte[64] hash512 = sha512Of("abc"); + assert(hash512 == digest!SHA512("abc")); + + ubyte[28] hash512_224 = sha512_224Of("abc"); + assert(hash512_224 == digest!SHA512_224("abc")); + + ubyte[32] hash512_256 = sha512_256Of("abc"); + assert(hash512_256 == digest!SHA512_256("abc")); } unittest @@ -574,13 +1166,19 @@ unittest } /** - * OOP API SHA1 implementation. + * OOP API SHA1 and SHA2 implementations. * See $(D std.digest.digest) for differences between template and OOP API. * * This is an alias for $(XREF digest.digest, WrapperDigest)!SHA1, see * $(XREF digest.digest, WrapperDigest) for more information. */ -alias WrapperDigest!SHA1 SHA1Digest; +alias SHA1Digest = WrapperDigest!SHA1; +alias SHA224Digest = WrapperDigest!SHA224; ///ditto +alias SHA256Digest = WrapperDigest!SHA256; ///ditto +alias SHA384Digest = WrapperDigest!SHA384; ///ditto +alias SHA512Digest = WrapperDigest!SHA512; ///ditto +alias SHA512_224Digest = WrapperDigest!SHA512_224; ///ditto +alias SHA512_256Digest = WrapperDigest!SHA512_256; ///ditto /// unittest @@ -590,6 +1188,12 @@ unittest ubyte[] hash = sha.digest("abc"); //Let's get a hash string assert(toHexString(hash) == "A9993E364706816ABA3E25717850C26C9CD0D89D"); + + //The same, but using SHA-224 + auto sha224 = new SHA224Digest(); + ubyte[] hash224 = sha224.digest("abc"); + //Let's get a hash string + assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7"); } /// diff --git a/libphobos/src/std/encoding.d b/libphobos/src/std/encoding.d index fd6b0b0c8..2f6c7d047 100644 --- a/libphobos/src/std/encoding.d +++ b/libphobos/src/std/encoding.d @@ -647,12 +647,12 @@ template EncoderInstance(E) /** Defines various character sets. */ enum AsciiChar : ubyte { init } /// Ditto -alias immutable(AsciiChar)[] AsciiString; +alias AsciiString = immutable(AsciiChar)[]; template EncoderInstance(CharType : AsciiChar) { - alias AsciiChar E; - alias AsciiString EString; + alias E = AsciiChar; + alias EString = AsciiString; @property string encodingName() { @@ -730,12 +730,12 @@ enum Latin1Char : ubyte { init } Defines an Latin1-encoded string (as an array of $(D immutable(Latin1Char))). */ -alias immutable(Latin1Char)[] Latin1String; /// +alias Latin1String = immutable(Latin1Char)[]; /// template EncoderInstance(CharType : Latin1Char) { - alias Latin1Char E; - alias Latin1String EString; + alias E = Latin1Char; + alias EString = Latin1String; @property string encodingName() { @@ -806,12 +806,12 @@ enum Windows1252Char : ubyte { init } Defines an Windows1252-encoded string (as an array of $(D immutable(Windows1252Char))). */ -alias immutable(Windows1252Char)[] Windows1252String; /// +alias Windows1252String = immutable(Windows1252Char)[]; /// template EncoderInstance(CharType : Windows1252Char) { - alias Windows1252Char E; - alias Windows1252String EString; + alias E = Windows1252Char; + alias EString = Windows1252String; @property string encodingName() { @@ -819,9 +819,9 @@ template EncoderInstance(CharType : Windows1252Char) } immutable wstring charMap = - "\u20AC\uFFFD\u201A\u0192\u201E\u2026\u2020\u2021" - "\u02C6\u2030\u0160\u2039\u0152\uFFFD\u017D\uFFFD" - "\uFFFD\u2018\u2019\u201C\u201D\u2022\u2103\u2014" + "\u20AC\uFFFD\u201A\u0192\u201E\u2026\u2020\u2021"~ + "\u02C6\u2030\u0160\u2039\u0152\uFFFD\u017D\uFFFD"~ + "\uFFFD\u2018\u2019\u201C\u201D\u2022\u2103\u2014"~ "\u02DC\u2122\u0161\u203A\u0153\uFFFD\u017E\u0178" ; @@ -907,8 +907,8 @@ template EncoderInstance(CharType : Windows1252Char) template EncoderInstance(CharType : char) { - alias char E; - alias immutable(char)[] EString; + alias E = char; + alias EString = immutable(char)[]; @property string encodingName() { @@ -1073,8 +1073,8 @@ template EncoderInstance(CharType : char) template EncoderInstance(CharType : wchar) { - alias wchar E; - alias immutable(wchar)[] EString; + alias E = wchar; + alias EString = immutable(wchar)[]; @property string encodingName() { @@ -1170,8 +1170,8 @@ template EncoderInstance(CharType : wchar) template EncoderInstance(CharType : dchar) { - alias dchar E; - alias immutable(dchar)[] EString; + alias E = dchar; + alias EString = immutable(dchar)[]; @property string encodingName() { @@ -1242,8 +1242,8 @@ Returns true if c is a valid code point since these are valid code points (even though they are not valid characters). - Supercedes: - This function supercedes $(D std.utf.startsValidDchar()). + Supersedes: + This function supersedes $(D std.utf.startsValidDchar()). Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 @@ -1284,7 +1284,7 @@ unittest } /** - Returns true iff it is possible to represent the specifed codepoint + Returns true iff it is possible to represent the specified codepoint in the encoding. The type of encoding cannot be deduced. Therefore, it is necessary to @@ -1341,10 +1341,10 @@ unittest /** Returns true if the string is encoded correctly - Supercedes: - This function supercedes std.utf.validate(), however note that this + Supersedes: + This function supersedes std.utf.validate(), however note that this function returns a bool indicating whether the input was valid or not, - wheras the older funtion would throw an exception. + whereas the older function would throw an exception. Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 @@ -1507,8 +1507,8 @@ unittest The input to this function MUST be validly encoded. This is enforced by the function's in-contract. - Supercedes: - This function supercedes std.utf.toUTFindex(). + Supersedes: + This function supersedes std.utf.toUTFindex(). Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 @@ -1543,9 +1543,9 @@ unittest The input to this function MUST be validly encoded. This is enforced by the function's in-contract. - Supercedes: - This function supercedes std.utf.decode(), however, note that the - function codePoints() supercedes it more conveniently. + Supersedes: + This function supersedes std.utf.decode(), however, note that the + function codePoints() supersedes it more conveniently. Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 @@ -1650,9 +1650,9 @@ body The type of the output cannot be deduced. Therefore, it is necessary to explicitly specify the encoding as a template parameter. - Supercedes: - This function supercedes std.utf.encode(), however, note that the - function codeUnits() supercedes it more conveniently. + Supersedes: + This function supersedes std.utf.encode(), however, note that the + function codeUnits() supersedes it more conveniently. Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 @@ -1682,9 +1682,9 @@ body The type of the output cannot be deduced. Therefore, it is necessary to explicitly specify the encoding as a template parameter. - Supercedes: - This function supercedes std.utf.encode(), however, note that the - function codeUnits() supercedes it more conveniently. + Supersedes: + This function supersedes std.utf.encode(), however, note that the + function codeUnits() supersedes it more conveniently. Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 @@ -1719,9 +1719,9 @@ body // * The type of the output cannot be deduced. Therefore, it is necessary to // * explicitly specify the encoding as a template parameter. // * -// * Supercedes: -// * This function supercedes std.utf.encode(), however, note that the -// * function codeUnits() supercedes it more conveniently. +// * Supersedes: +// * This function supersedes std.utf.encode(), however, note that the +// * function codeUnits() supersedes it more conveniently. // * // * Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 // * @@ -1820,9 +1820,9 @@ unittest The type of the output cannot be deduced. Therefore, it is necessary to explicitly specify the encoding as a template parameter. - Supercedes: - This function supercedes std.utf.encode(), however, note that the - function codeUnits() supercedes it more conveniently. + Supersedes: + This function supersedes std.utf.encode(), however, note that the + function codeUnits() supersedes it more conveniently. Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 @@ -1852,8 +1852,8 @@ body at each iteration with the offset into the string at which the code point begins. - Supercedes: - This function supercedes std.utf.decode(). + Supersedes: + This function supersedes std.utf.decode(). Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 @@ -1903,8 +1903,8 @@ unittest The type of the output cannot be deduced. Therefore, it is necessary to explicitly specify the encoding type in the template parameter. - Supercedes: - This function supercedes std.utf.encode(). + Supersedes: + This function supersedes std.utf.encode(). Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 @@ -1968,10 +1968,10 @@ size_t encode(Tgt, Src, R)(in Src[] s, R range) The input to this function MUST be validly encoded. This is enforced by the function's in-contract. - Supercedes: - This function supercedes std.utf.toUTF8(), std.utf.toUTF16() and + Supersedes: + This function supersedes std.utf.toUTF8(), std.utf.toUTF16() and std.utf.toUTF32() - (but note that to!() supercedes it more conveniently). + (but note that to!() supersedes it more conveniently). Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 @@ -2083,8 +2083,8 @@ unittest The input to this function MUST be validly encoded. This is enforced by the function's in-contract. - Supercedes: - This function supercedes std.utf.toUTF8(), std.utf.toUTF16() and + Supersedes: + This function supersedes std.utf.toUTF8(), std.utf.toUTF16() and std.utf.toUTF32(). Standards: Unicode 5.0, ASCII, ISO-8859-1, WINDOWS-1252 @@ -2463,7 +2463,7 @@ class EncodingSchemeASCII : EncodingScheme "ISO_646.irv:1991", "US-ASCII", "cp367", - "csASCII" + "csASCII", "iso-ir-6", "us" ]; diff --git a/libphobos/src/std/exception.d b/libphobos/src/std/exception.d index 3287c22f2..194ef098d 100644 --- a/libphobos/src/std/exception.d +++ b/libphobos/src/std/exception.d @@ -338,11 +338,12 @@ T enforce(T)(T value, lazy const(char)[] msg = null, string file = __FILE__, siz } /++ - $(RED Scheduled for deprecation in January 2013. If passing the file or line - number explicitly, please use the version of enforce which takes them as - function arguments. Taking them as template arguments causes - unnecessary template bloat.) + $(RED Deprecated. If passing the file or line number explicitly, please use + the overload of enforce which takes them as function arguments. Taking + them as template arguments causes unnecessary template bloat. This + overload will be removed in June 2015.) +/ +deprecated("Use the overload of enforce that takes file and line as function arguments.") T enforce(T, string file, size_t line = __LINE__) (T value, lazy const(char)[] msg = null) if (is(typeof({ if (!value) {} }))) @@ -410,11 +411,11 @@ unittest "delegate void() " ~ (EncloseSafe ? "@safe " : "") ~ (EnclosePure ? "pure " : "") ~ - "{ ""enforce(true, { " + "{ enforce(true, { " ~ "int n; " ~ (BodySafe ? "" : "auto p = &n + 10; " ) ~ // unsafe code (BodyPure ? "" : "static int g; g = 10; ") ~ // impure code - "}); " + "}); " ~ "}"; enum expect = (BodySafe || !EncloseSafe) && (!EnclosePure || BodyPure); @@ -446,13 +447,12 @@ unittest S s; enforce(s); - enforce!(S, __FILE__, __LINE__)(s, ""); // scheduled for deprecation enforce(s, {}); enforce(s, new Exception("")); errnoEnforce(s); - alias Exception E1; + alias E1 = Exception; static class E2 : Exception { this(string fn, size_t ln) { super("", fn, ln); } @@ -465,6 +465,23 @@ unittest enforceEx!E2(s); } +deprecated unittest +{ + struct S + { + static int g; + ~this() {} // impure & unsafe destructor + bool opCast(T:bool)() { + int* p = cast(int*)0; // unsafe operation + int n = g; // impure operation + return true; + } + } + S s; + + enforce!(S, __FILE__, __LINE__)(s, ""); +} + /++ If $(D !!value) is true, $(D value) is returned. Otherwise, $(D ex) is thrown. @@ -520,9 +537,10 @@ T errnoEnforce(T, string file = __FILE__, size_t line = __LINE__) enforceEx!DataCorruptionException(line.length); -------------------- +/ -template enforceEx(E) +template enforceEx(E : Throwable) if (is(typeof(new E("", __FILE__, __LINE__)))) { + /++ Ditto +/ T enforceEx(T)(T value, lazy string msg = "", string file = __FILE__, size_t line = __LINE__) { if (!value) throw new E(msg, file, line); @@ -530,9 +548,11 @@ template enforceEx(E) } } -template enforceEx(E) +/++ Ditto +/ +template enforceEx(E : Throwable) if (is(typeof(new E(__FILE__, __LINE__))) && !is(typeof(new E("", __FILE__, __LINE__)))) { + /++ Ditto +/ T enforceEx(T)(T value, string file = __FILE__, size_t line = __LINE__) { if (!value) throw new E(file, line); @@ -561,11 +581,29 @@ unittest assert(e.file == "file"); assert(e.line == 42); } + + { + auto e = collectException!Error(enforceEx!OutOfMemoryError(false)); + assert(e !is null); + assert(e.msg == "Memory allocation failed"); + assert(e.file == __FILE__); + assert(e.line == __LINE__ - 4); + } + + { + auto e = collectException!Error(enforceEx!OutOfMemoryError(false, "file", 42)); + assert(e !is null); + assert(e.msg == "Memory allocation failed"); + assert(e.file == "file"); + assert(e.line == 42); + } + + static assert(!is(typeof(enforceEx!int(true)))); } unittest { - alias enforceEx!Exception enf; + alias enf = enforceEx!Exception; assertNotThrown(enf(true)); assertThrown(enf(false, "blah")); } @@ -702,7 +740,7 @@ enum emptyExceptionMsg = ""; * but its name documents assumptions on the part of the * caller. $(D assumeUnique(arr)) should only be called when * there are no more active mutable aliases to elements of $(D - * arr). To strenghten this assumption, $(D assumeUnique(arr)) + * arr). To strengthen this assumption, $(D assumeUnique(arr)) * also clears $(D arr) before returning. Essentially $(D * assumeUnique(arr)) indicates commitment from the caller that there * is no more mutable access to any of $(D arr)'s elements @@ -720,14 +758,14 @@ enum emptyExceptionMsg = ""; * char[] result = new char['z' - 'a' + 1]; * foreach (i, ref e; result) * { - * e = 'a' + i; + * e = cast(char)('a' + i); * } * return assumeUnique(result); * } * ---- * * The use in the example above is correct because $(D result) - * was private to $(D letters) and is unaccessible in writing + * was private to $(D letters) and is inaccessible in writing * after the function returns. The following example shows an * incorrect use of $(D assumeUnique). * @@ -742,7 +780,7 @@ enum emptyExceptionMsg = ""; * sneaky.length = last - first + 1; * foreach (i, ref e; sneaky) * { - * e = 'a' + i; + * e = cast(char)('a' + i); * } * return assumeUnique(sneaky); // BAD * } @@ -758,14 +796,33 @@ enum emptyExceptionMsg = ""; * * The call will duplicate the array appropriately. * - * Checking for uniqueness during compilation is possible in certain - * cases (see the $(D unique) and $(D lent) keywords in - * the $(WEB archjava.fluid.cs.cmu.edu/papers/oopsla02.pdf, ArchJava) - * language), but complicates the language considerably. The downside - * of $(D assumeUnique)'s convention-based usage is that at this - * time there is no formal checking of the correctness of the - * assumption; on the upside, the idiomatic use of $(D - * assumeUnique) is simple and rare enough to be tolerable. + * Note that checking for uniqueness during compilation is + * possible in certain cases, especially when a function is + * marked as a pure function. The following example does not + * need to call assumeUnique because the compiler can infer the + * uniqueness of the array in the pure function: + * ---- + * string letters() pure + * { + * char[] result = new char['z' - 'a' + 1]; + * foreach (i, ref e; result) + * { + * e = cast(char)('a' + i); + * } + * return result; + * } + * ---- + * + * For more on infering uniqueness see the $(B unique) and + * $(B lent) keywords in the + * $(WEB archjava.fluid.cs.cmu.edu/papers/oopsla02.pdf, ArchJava) + * language. + * + * The downside of using $(D assumeUnique)'s + * convention-based usage is that at this time there is no + * formal checking of the correctness of the assumption; + * on the upside, the idiomatic use of $(D assumeUnique) is + * simple and rare enough to be tolerable. * */ immutable(T)[] assumeUnique(T)(T[] array) pure nothrow @@ -874,32 +931,43 @@ unittest } /** +The "pointsTo" functions, $(D doesPointTo) and $(D mayPointTo). + Returns $(D true) if $(D source)'s representation embeds a pointer that points to $(D target)'s representation or somewhere inside it. -If $(D source) is or contains a dynamic array, then, then pointsTo will check +If $(D source) is or contains a dynamic array, then, then these functions will check if there is overlap between the dynamic array and $(D target)'s representation. -If $(D source) is or contains a union, then every member of the union is -checked for embedded pointers. This may lead to false positives, depending on -which should be considered the "active" member of the union. - If $(D source) is a class, then pointsTo will handle it as a pointer. -If $(D target) is a pointer, a dynamic array or a class, then pointsTo will only +If $(D target) is a pointer, a dynamic array or a class, then these functions will only check if $(D source) points to $(D target), $(I not) what $(D target) references. +If $(D source) is or contains a union, then there may be either false positives or +false negatives: + +$(D doesPointTo) will return $(D true) if it is absolutly certain +$(D source) points to $(D target). It may produce false negatives, but never +false positives. This function should be prefered when trying to validate +input data. + +$(D mayPointTo) will return $(D false) if it is absolutly certain +$(D source) does not point to $(D target). It may produce false positives, but never +false negatives. This function should be prefered for defensively choosing a +code path. + Note: Evaluating $(D pointsTo(x, x)) checks whether $(D x) has internal pointers. This should only be done as an assertive test, as the language is free to assume objects don't have internal pointers (TDPL 7.1.3.5). */ -bool pointsTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @trusted pure nothrow +bool doesPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @trusted pure nothrow if (__traits(isRef, source) || isDynamicArray!S || isPointer!S || is(S == class)) { - static if (isPointer!S || is(S == class)) + static if (isPointer!S || is(S == class) || is(S == interface)) { const m = cast(void*) source, b = cast(void*) &target, e = b + target.sizeof; @@ -908,13 +976,14 @@ bool pointsTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @t else static if (is(S == struct) || is(S == union)) { foreach (i, Subobj; typeof(source.tupleof)) - if (pointsTo(source.tupleof[i], target)) return true; + static if (!isUnionAliased!(S, i)) + if (doesPointTo(source.tupleof[i], target)) return true; return false; } else static if (isStaticArray!S) { foreach (size_t i; 0 .. S.length) - if (pointsTo(source[i], target)) return true; + if (doesPointTo(source[i], target)) return true; return false; } else static if (isDynamicArray!S) @@ -926,10 +995,104 @@ bool pointsTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @t return false; } } + +bool mayPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @trusted pure nothrow + if (__traits(isRef, source) || isDynamicArray!S || + isPointer!S || is(S == class)) +{ + static if (isPointer!S || is(S == class) || is(S == interface)) + { + const m = cast(void*) source, + b = cast(void*) &target, e = b + target.sizeof; + return b <= m && m < e; + } + else static if (is(S == struct) || is(S == union)) + { + foreach (i, Subobj; typeof(source.tupleof)) + if (mayPointTo(source.tupleof[i], target)) return true; + return false; + } + else static if (isStaticArray!S) + { + foreach (size_t i; 0 .. S.length) + if (mayPointTo(source[i], target)) return true; + return false; + } + else static if (isDynamicArray!S) + { + return overlap(cast(void[])source, cast(void[])(&target)[0 .. 1]).length != 0; + } + else + { + return false; + } +} + // for shared objects -bool pointsTo(S, T)(auto ref const shared S source, ref const shared T target) @trusted pure nothrow +bool doesPointTo(S, T)(auto ref const shared S source, ref const shared T target) @trusted pure nothrow +{ + return doesPointTo!(shared S, shared T, void)(source, target); +} +bool mayPointTo(S, T)(auto ref const shared S source, ref const shared T target) @trusted pure nothrow +{ + return mayPointTo!(shared S, shared T, void)(source, target); +} + +deprecated ("pointsTo is ambiguous. Please use either of doesPointTo or mayPointTo") +alias pointsTo = doesPointTo; + +/+ +Returns true if the field at index $(D i) in ($D T) shares its address with another field. + +Note: This does not merelly check if the field is a member of an union, but also that +it is not a single child. ++/ +package enum isUnionAliased(T, size_t i) = isUnionAliasedImpl!T(T.tupleof[i].offsetof); +private bool isUnionAliasedImpl(T)(size_t offset) { - return pointsTo!(shared S, shared T, void)(source, target); + int count = 0; + foreach (i, U; typeof(T.tupleof)) + if (T.tupleof[i].offsetof == offset) + ++count; + return count >= 2; +} +// +unittest +{ + static struct S + { + int a0; //Not aliased + union + { + int a1; //Not aliased + } + union + { + int a2; //Aliased + int a3; //Aliased + } + union A4 + { + int b0; //Not aliased + }; + A4 a4; + union A5 + { + int b0; //Aliased + int b1; //Aliased + }; + A5 a5; + } + + static assert(!isUnionAliased!(S, 0)); //a0; + static assert(!isUnionAliased!(S, 1)); //a1; + static assert( isUnionAliased!(S, 2)); //a2; + static assert( isUnionAliased!(S, 3)); //a3; + static assert(!isUnionAliased!(S, 4)); //a4; + static assert(!isUnionAliased!(S.A4, 0)); //a4.b0; + static assert(!isUnionAliased!(S, 5)); //a5; + static assert( isUnionAliased!(S.A5, 0)); //a5.b0; + static assert( isUnionAliased!(S.A5, 1)); //a5.b1; } /// Pointers @@ -937,9 +1100,9 @@ unittest { int i = 0; int* p = null; - assert(!p.pointsTo(i)); + assert(!p.doesPointTo(i)); p = &i; - assert( p.pointsTo(i)); + assert( p.doesPointTo(i)); } /// Structs and Unions @@ -955,9 +1118,9 @@ unittest //structs and unions "own" their members //pointsTo will answer true if one of the members pointsTo. - assert(!s.pointsTo(s.v)); //s.v is just v member of s, so not pointed. - assert( s.p.pointsTo(i)); //i is pointed by s.p. - assert( s .pointsTo(i)); //which means i is pointed by s itself. + assert(!s.doesPointTo(s.v)); //s.v is just v member of s, so not pointed. + assert( s.p.doesPointTo(i)); //i is pointed by s.p. + assert( s .doesPointTo(i)); //which means i is pointed by s itself. //Unions will behave exactly the same. Points to will check each "member" //individually, even if they share the same memory @@ -973,23 +1136,23 @@ unittest int*[1] arrp = [&i]; //A slice points to all of its members: - assert( slice.pointsTo(slice[3])); - assert(!slice[0 .. 2].pointsTo(slice[3])); //Object 3 is outside of the slice [0 .. 2] + assert( slice.doesPointTo(slice[3])); + assert(!slice[0 .. 2].doesPointTo(slice[3])); //Object 3 is outside of the slice [0 .. 2] //Note that a slice will not take into account what its members point to. - assert( slicep[0].pointsTo(i)); - assert(!slicep .pointsTo(i)); + assert( slicep[0].doesPointTo(i)); + assert(!slicep .doesPointTo(i)); //static arrays are objects that own their members, just like structs: - assert(!arr.pointsTo(arr[0])); //arr[0] is just a member of arr, so not pointed. - assert( arrp[0].pointsTo(i)); //i is pointed by arrp[0]. - assert( arrp .pointsTo(i)); //which means i is pointed by arrp itslef. + assert(!arr.doesPointTo(arr[0])); //arr[0] is just a member of arr, so not pointed. + assert( arrp[0].doesPointTo(i)); //i is pointed by arrp[0]. + assert( arrp .doesPointTo(i)); //which means i is pointed by arrp itslef. //Notice the difference between static and dynamic arrays: - assert(!arr .pointsTo(arr[0])); - assert( arr[].pointsTo(arr[0])); - assert( arrp .pointsTo(i)); - assert(!arrp[].pointsTo(i)); + assert(!arr .doesPointTo(arr[0])); + assert( arr[].doesPointTo(arr[0])); + assert( arrp .doesPointTo(i)); + assert(!arrp[].doesPointTo(i)); } /// Classes @@ -1005,21 +1168,21 @@ unittest C b = a; //Classes are a bit particular, as they are treated like simple pointers //to a class payload. - assert( a.p.pointsTo(i)); //a.p points to i. - assert(!a .pointsTo(i)); //Yet a itself does not point i. + assert( a.p.doesPointTo(i)); //a.p points to i. + assert(!a .doesPointTo(i)); //Yet a itself does not point i. //To check the class payload itself, iterate on its members: () { foreach (index, _; FieldTypeTuple!C) - if (pointsTo(a.tupleof[index], i)) + if (doesPointTo(a.tupleof[index], i)) return; assert(0); }(); //To check if a class points a specific payload, a direct memmory check can be done: auto aLoc = cast(ubyte[__traits(classInstanceSize, C)]*) a; - assert(b.pointsTo(*aLoc)); //b points to where a is pointing + assert(b.doesPointTo(*aLoc)); //b points to where a is pointing } unittest @@ -1027,49 +1190,49 @@ unittest struct S1 { int a; S1 * b; } S1 a1; S1 * p = &a1; - assert(pointsTo(p, a1)); + assert(doesPointTo(p, a1)); S1 a2; a2.b = &a1; - assert(pointsTo(a2, a1)); + assert(doesPointTo(a2, a1)); struct S3 { int[10] a; } S3 a3; auto a4 = a3.a[2 .. 3]; - assert(pointsTo(a4, a3)); + assert(doesPointTo(a4, a3)); auto a5 = new double[4]; auto a6 = a5[1 .. 2]; - assert(!pointsTo(a5, a6)); + assert(!doesPointTo(a5, a6)); auto a7 = new double[3]; auto a8 = new double[][1]; a8[0] = a7; - assert(!pointsTo(a8[0], a8[0])); + assert(!doesPointTo(a8[0], a8[0])); // don't invoke postblit on subobjects { static struct NoCopy { this(this) { assert(0); } } static struct Holder { NoCopy a, b, c; } Holder h; - pointsTo(h, h); + cast(void)doesPointTo(h, h); } shared S3 sh3; shared sh3sub = sh3.a[]; - assert(pointsTo(sh3sub, sh3)); + assert(doesPointTo(sh3sub, sh3)); int[] darr = [1, 2, 3, 4]; //dynamic arrays don't point to each other, or slices of themselves - assert(!pointsTo(darr, darr)); - assert(!pointsTo(darr[0 .. 1], darr)); + assert(!doesPointTo(darr, darr)); + assert(!doesPointTo(darr[0 .. 1], darr)); //But they do point their elements foreach(i; 0 .. 4) - assert(pointsTo(darr, darr[i])); - assert(pointsTo(darr[0..3], darr[2])); - assert(!pointsTo(darr[0..3], darr[3])); + assert(doesPointTo(darr, darr[i])); + assert(doesPointTo(darr[0..3], darr[2])); + assert(!doesPointTo(darr[0..3], darr[3])); } unittest @@ -1081,22 +1244,22 @@ unittest //Standard array int[2] k; - assert(!pointsTo(k, k)); //an array doesn't point to itself + assert(!doesPointTo(k, k)); //an array doesn't point to itself //Technically, k doesn't point its elements, although it does alias them - assert(!pointsTo(k, k[0])); - assert(!pointsTo(k, k[1])); + assert(!doesPointTo(k, k[0])); + assert(!doesPointTo(k, k[1])); //But an extracted slice will point to the same array. - assert(pointsTo(k[], k)); - assert(pointsTo(k[], k[1])); + assert(doesPointTo(k[], k)); + assert(doesPointTo(k[], k[1])); //An array of pointers int*[2] pp; int a; int b; pp[0] = &a; - assert( pointsTo(pp, a)); //The array contains a pointer to a - assert(!pointsTo(pp, b)); //The array does NOT contain a pointer to b - assert(!pointsTo(pp, pp)); //The array does not point itslef + assert( doesPointTo(pp, a)); //The array contains a pointer to a + assert(!doesPointTo(pp, b)); //The array does NOT contain a pointer to b + assert(!doesPointTo(pp, pp)); //The array does not point itslef //A struct containing a static array of pointers static struct S @@ -1105,9 +1268,9 @@ unittest } S s; s.p[0] = &a; - assert( pointsTo(s, a)); //The struct contains an array that points a - assert(!pointsTo(s, b)); //But doesn't point b - assert(!pointsTo(s, s)); //The struct doesn't actually point itslef. + assert( doesPointTo(s, a)); //The struct contains an array that points a + assert(!doesPointTo(s, b)); //But doesn't point b + assert(!doesPointTo(s, s)); //The struct doesn't actually point itslef. //An array containing structs that have pointers static struct SS @@ -1115,9 +1278,9 @@ unittest int* p; } SS[2] ss = [SS(&a), SS(null)]; - assert( pointsTo(ss, a)); //The array contains a struct that points to a - assert(!pointsTo(ss, b)); //The array doesn't contains a struct that points to b - assert(!pointsTo(ss, ss)); //The array doesn't point itself. + assert( doesPointTo(ss, a)); //The array contains a struct that points to a + assert(!doesPointTo(ss, b)); //The array doesn't contains a struct that points to b + assert(!doesPointTo(ss, ss)); //The array doesn't point itself. } @@ -1140,18 +1303,24 @@ unittest //Unions U u; S s; - assert(!pointsTo(u, i)); - assert(!pointsTo(s, i)); + assert(!doesPointTo(u, i)); + assert(!doesPointTo(s, i)); + assert(!mayPointTo(u, i)); + assert(!mayPointTo(s, i)); u.asPointer = &i; s.asPointer = &i; - assert( pointsTo(u, i)); - assert( pointsTo(s, i)); + assert(!doesPointTo(u, i)); + assert(!doesPointTo(s, i)); + assert( mayPointTo(u, i)); + assert( mayPointTo(s, i)); u.asInt = cast(size_t)&i; s.asInt = cast(size_t)&i; - assert( pointsTo(u, i)); //logical false positive - assert( pointsTo(s, i)); //logical false positive + assert(!doesPointTo(u, i)); + assert(!doesPointTo(s, i)); + assert( mayPointTo(u, i)); + assert( mayPointTo(s, i)); } unittest //Classes @@ -1162,9 +1331,9 @@ unittest //Classes int* p; } A a = new A, b = a; - assert(!pointsTo(a, b)); //a does not point to b + assert(!doesPointTo(a, b)); //a does not point to b a.p = &i; - assert(!pointsTo(a, i)); //a does not point to i + assert(!doesPointTo(a, i)); //a does not point to i } unittest //alias this test { @@ -1178,10 +1347,10 @@ unittest //alias this test } assert(is(S : int*)); S s = S(&j); - assert(!pointsTo(s, i)); - assert( pointsTo(s, j)); - assert( pointsTo(cast(int*)s, i)); - assert(!pointsTo(cast(int*)s, j)); + assert(!doesPointTo(s, i)); + assert( doesPointTo(s, j)); + assert( doesPointTo(cast(int*)s, i)); + assert(!doesPointTo(cast(int*)s, j)); } /********************* @@ -1190,7 +1359,7 @@ unittest //alias this test class ErrnoException : Exception { uint errno; // operating system error code - this(string msg, string file = null, size_t line = 0) + this(string msg, string file = null, size_t line = 0) @trusted { errno = .errno; version (linux) @@ -1259,7 +1428,7 @@ class ErrnoException : Exception static assert(!__traits(compiles, (new Object()).ifThrown(1))); -------------------- - If you need to use the actual thrown expection, you can use a delegate. + If you need to use the actual thrown exception, you can use a delegate. Example: -------------------- //Use a lambda to get the thrown object. @@ -1381,6 +1550,6 @@ unittest version(unittest) package @property void assertCTFEable(alias dg)() { - static assert({ dg(); return true; }()); - dg(); + static assert({ cast(void)dg(); return true; }()); + cast(void)dg(); } diff --git a/libphobos/src/std/file.d b/libphobos/src/std/file.d index ab4531011..85c628bba 100644 --- a/libphobos/src/std/file.d +++ b/libphobos/src/std/file.d @@ -181,11 +181,11 @@ void[] read(in char[] name, size_t upTo = size_t.max) { version(Windows) { - alias TypeTuple!(GENERIC_READ, + alias defaults = + TypeTuple!(GENERIC_READ, FILE_SHARE_READ, (SECURITY_ATTRIBUTES*).init, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, - HANDLE.init) - defaults; + HANDLE.init); auto h = CreateFileW(std.utf.toUTF16z(name), defaults); cenforce(h != INVALID_HANDLE_VALUE, name); @@ -316,10 +316,10 @@ void write(in char[] name, const void[] buffer) { version(Windows) { - alias TypeTuple!(GENERIC_WRITE, 0, null, CREATE_ALWAYS, + alias defaults = + TypeTuple!(GENERIC_WRITE, 0, null, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, - HANDLE.init) - defaults; + HANDLE.init); auto h = CreateFileW(std.utf.toUTF16z(name), defaults); cenforce(h != INVALID_HANDLE_VALUE, name); @@ -355,9 +355,9 @@ void append(in char[] name, in void[] buffer) { version(Windows) { - alias TypeTuple!(GENERIC_WRITE,0,null,OPEN_ALWAYS, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,HANDLE.init) - defaults; + alias defaults = + TypeTuple!(GENERIC_WRITE,0,null,OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,HANDLE.init); auto h = CreateFileW(std.utf.toUTF16z(name), defaults); @@ -687,15 +687,15 @@ void setTimes(in char[] name, { const ta = SysTimeToFILETIME(accessTime); const tm = SysTimeToFILETIME(modificationTime); - alias TypeTuple!(GENERIC_WRITE, + alias defaults = + TypeTuple!(GENERIC_WRITE, 0, null, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY | FILE_FLAG_BACKUP_SEMANTICS, - HANDLE.init) - defaults; + HANDLE.init); auto h = CreateFileW(std.utf.toUTF16z(name), defaults); cenforce(h != INVALID_HANDLE_VALUE, name); @@ -1754,7 +1754,6 @@ version(StdDdoc) version (Windows) { - private this(string path, in WIN32_FIND_DATA* fd); private this(string path, in WIN32_FIND_DATAW *fd); } else version (Posix) @@ -1887,7 +1886,7 @@ assert(!de2.isFile); @property uint linkAttributes(); version(Windows) - alias void* stat_t; + alias stat_t = void*; /++ $(BLUE This function is Posix-Only.) @@ -1921,23 +1920,6 @@ else version(Windows) } } - private this(string path, in WIN32_FIND_DATA* fd) - { - auto clength = to!int(core.stdc.string.strlen(fd.cFileName.ptr)); - - // Convert cFileName[] to unicode - const wlength = MultiByteToWideChar(0, 0, fd.cFileName.ptr, clength, null, 0); - auto wbuf = new wchar[wlength]; - const n = MultiByteToWideChar(0, 0, fd.cFileName.ptr, clength, wbuf.ptr, wlength); - assert(n == wlength); - // toUTF8() returns a new buffer - _name = buildPath(path, std.utf.toUTF8(wbuf[0 .. wlength])); - _size = (cast(ulong)fd.nFileSizeHigh << 32) | fd.nFileSizeLow; - _timeCreated = std.datetime.FILETIMEToSysTime(&fd.ftCreationTime); - _timeLastAccessed = std.datetime.FILETIMEToSysTime(&fd.ftLastAccessTime); - _timeLastModified = std.datetime.FILETIMEToSysTime(&fd.ftLastWriteTime); - _attributes = fd.dwFileAttributes; - } private this(string path, in WIN32_FIND_DATAW *fd) { size_t clength = std.string.wcslen(fd.cFileName.ptr); @@ -2389,8 +2371,13 @@ void rmdirRecurse(ref DirEntry de) if(!de.isDir) throw new FileException(de.name, "Not a directory"); - if(de.isSymlink) - remove(de.name); + if (de.isSymlink) + { + version (Windows) + rmdir(de.name); + else + remove(de.name); + } else { // all children, recursively depth-first @@ -2550,27 +2537,6 @@ private struct DirIteratorImpl return true; } - bool toNext(bool fetch, WIN32_FIND_DATA* findinfo) - { - if(fetch) - { - if(FindNextFileA(_stack.data[$-1].h, findinfo) == FALSE) - { - popDirStack(); - return false; - } - } - while( core.stdc.string.strcmp(findinfo.cFileName.ptr, ".") == 0 - || core.stdc.string.strcmp(findinfo.cFileName.ptr, "..") == 0) - if(FindNextFileA(_stack.data[$-1].h, findinfo) == FALSE) - { - popDirStack(); - return false; - } - _cur = DirEntry(_stack.data[$-1].dirpath, findinfo); - return true; - } - void popDirStack() { assert(!_stack.data.empty); @@ -2834,7 +2800,7 @@ unittest path = The directory to iterate over. pattern = String with wildcards, such as $(RED "*.d"). The supported wildcard strings are described under - $(XREF path, globMatch). + $(XREF _path, globMatch). mode = Whether the directory's sub-directories should be iterated over depth-first ($(D_PARAM depth)), breadth-first ($(D_PARAM breadth)), or not at all ($(D_PARAM shallow)). @@ -2889,7 +2855,7 @@ unittest mkdir(path); Thread.sleep(dur!"seconds"(2)); - auto de = dirEntry(path); + auto de = DirEntry(path); assert(de.name == path); assert(de.isDir); assert(!de.isFile); @@ -2939,7 +2905,7 @@ unittest write(path, "hello world"); Thread.sleep(dur!"seconds"(2)); - auto de = dirEntry(path); + auto de = DirEntry(path); assert(de.name == path); assert(!de.isDir); assert(de.isFile); @@ -2992,7 +2958,7 @@ version(linux) unittest core.sys.posix.unistd.symlink((orig ~ "\0").ptr, (path ~ "\0").ptr); Thread.sleep(dur!"seconds"(2)); - auto de = dirEntry(path); + auto de = DirEntry(path); assert(de.name == path); assert(de.isDir); assert(!de.isFile); @@ -3037,7 +3003,7 @@ version(linux) unittest core.sys.posix.unistd.symlink((orig ~ "\0").ptr, (path ~ "\0").ptr); Thread.sleep(dur!"seconds"(2)); - auto de = dirEntry(path); + auto de = DirEntry(path); assert(de.name == path); assert(!de.isDir); assert(de.isFile); diff --git a/libphobos/src/std/format.d b/libphobos/src/std/format.d index d387624bb..9f8d818c0 100644 --- a/libphobos/src/std/format.d +++ b/libphobos/src/std/format.d @@ -5,6 +5,10 @@ I/O. It's comparable to C99's $(D vsprintf()) and uses a similar format encoding scheme. + For an introductory look at $(B std.format)'s capabilities and how to use + this module see the dedicated + $(LINK2 http://wiki.dlang.org/Defining_custom_print_format_specifiers, DWiki article). + Macros: WIKI = Phobos/StdFormat Copyright: Copyright Digital Mars 2000-2013. @@ -18,7 +22,7 @@ */ /* NOTE: This file has been patched from the original DMD distribution to - work with the GDC compiler. + * work with the GDC compiler. */ module std.format; @@ -30,7 +34,7 @@ import std.algorithm, std.ascii, std.bitmanip, std.conv, std.system, std.traits, std.typetuple, std.utf; version (Win64) { - import std.math : isnan; + import std.math : isnan, isInfinity; } version(unittest) { import std.math; @@ -196,16 +200,16 @@ $(I FormatChar):
$(I Width)
Specifies the minimum field width. - If the width is a $(B *), the next argument, which must be - of type $(B int), is taken as the width. + If the width is a $(B *), an additional argument of type $(B int), + preceding the actual argument, is taken as the width. If the width is negative, it is as if the $(B -) was given as a $(I Flags) character.
$(I Precision)
Gives the precision for numeric conversions. - If the precision is a $(B *), the next argument, which must be - of type $(B int), is taken as the precision. If it is negative, - it is as if there was no $(I Precision). + If the precision is a $(B *), an additional argument of type $(B int), + preceding the actual argument, is taken as the precision. + If it is negative, it is as if there was no $(I Precision) specifier.
$(I FormatChar)
@@ -224,15 +228,28 @@ $(I FormatChar):
The result is the string converted to UTF-8. A $(I Precision) specifies the maximum number of characters to use in the result. +
structs +
If the struct defines a $(B toString()) method the result is the + string returned from this function. Otherwise the result is + StructName(field0, field1, ...) where fieldn + is the nth element formatted with the default format.
classes derived from $(B Object)
The result is the string returned from the class instance's $(B .toString()) method. A $(I Precision) specifies the maximum number of characters to use in the result. +
unions +
If the union defines a $(B toString()) method the result is the + string returned from this function. Otherwise the result is + the name of the union, without its contents.
non-string static and dynamic arrays
The result is [s0, s1, ...] - where sk is the kth element + where sn is the nth element formatted with the default format. +
associative arrays +
The result is the equivalent of what the initializer + would look like for the contents of the associative array, + e.g.: ["red" : 10, "blue" : 20].
$(B 'c') @@ -576,7 +593,7 @@ uint formattedRead(R, Char, S...)(ref R r, const(Char)[] fmt, S args) // Input is empty, nothing to read return 0; } - alias typeof(*args[0]) A; + alias A = typeof(*args[0]); static if (isTuple!A) { foreach (i, T; A.Types) @@ -608,7 +625,7 @@ unittest template FormatSpec(Char) if (!is(Unqual!Char == Char)) { - alias FormatSpec!(Unqual!Char) FormatSpec; + alias FormatSpec = FormatSpec!(Unqual!Char); } /** @@ -1101,7 +1118,10 @@ struct FormatSpec(Char) put(w, '%'); if (indexStart != 0) - formatValue(w, indexStart, f), put(w, '$'); + { + formatValue(w, indexStart, f); + put(w, '$'); + } if (flDash) put(w, '-'); if (flZero) put(w, '0'); if (flSpace) put(w, ' '); @@ -1110,7 +1130,10 @@ struct FormatSpec(Char) if (width != 0) formatValue(w, width, f); if (precision != FormatSpec!Char.UNSPECIFIED) - put(w, '.'), formatValue(w, precision, f); + { + put(w, '.'); + formatValue(w, precision, f); + } put(w, spec); return w.data; } @@ -1302,8 +1325,7 @@ unittest $(D null) literal is formatted as $(D "null"). */ void formatValue(Writer, T, Char)(Writer w, T obj, ref FormatSpec!Char f) -if (is(Unqual!T == typeof(null)) && -!is(T == enum) && !hasToString!(T, Char)) +if (is(Unqual!T == typeof(null)) && !is(T == enum) && !hasToString!(T, Char)) { enforceFmt(f.spec == 's', "null"); @@ -1578,17 +1600,25 @@ if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) } enforceFmt(std.algorithm.find("fgFGaAeEs", fs.spec).length, "floating"); + version (Win64) { - if (isnan(val)) // snprintf writes 1.#QNAN + double tval = val; // convert early to get "inf" in case of overflow + string s; + if (isnan(tval)) + s = "nan"; // snprintf writes 1.#QNAN + else if (isInfinity(tval)) + s = val >= 0 ? "inf" : "-inf"; // snprintf writes 1.#INF + + if (s.length > 0) { version(none) { - return formatValue(w, "nan", f); + return formatValue(w, s, f); } else // FIXME:workaroun { - auto s = "nan"[0 .. f.precision < $ ? f.precision : $]; + s = s[0 .. f.precision < $ ? f.precision : $]; if (!f.flDash) { // right align @@ -1607,6 +1637,8 @@ if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) } } } + else + alias val tval; if (fs.spec == 's') fs.spec = 'g'; char[1 /*%*/ + 5 /*flags*/ + 3 /*width.prec*/ + 2 /*format*/ + 1 /*\0*/] sprintfSpec = void; @@ -1624,12 +1656,13 @@ if (is(FloatingPointTypeOf!T) && !is(T == enum) && !hasToString!(T, Char)) sprintfSpec[i] = 0; //printf("format: '%s'; geeba: %g\n", sprintfSpec.ptr, val); char[512] buf; + immutable n = snprintf(buf.ptr, buf.length, - sprintfSpec.ptr, - fs.width, - // negative precision is same as no precision specified - fs.precision == fs.UNSPECIFIED ? -1 : fs.precision, - val); + sprintfSpec.ptr, + fs.width, + // negative precision is same as no precision specified + fs.precision == fs.UNSPECIFIED ? -1 : fs.precision, + tval); enforceFmt(n >= 0, "floating point formatting failure"); put(w, buf[0 .. strlen(buf.ptr)]); @@ -1909,7 +1942,7 @@ if (is(DynamicArrayTypeOf!T) && !is(StringTypeOf!T) && !is(T == enum) && !hasToS } else static if (!isInputRange!T) { - alias Unqual!(ArrayTypeOf!T) U; + alias U = Unqual!(ArrayTypeOf!T); static assert(isInputRange!U); U val = obj; formatValue(w, val, f); @@ -1923,7 +1956,7 @@ if (is(DynamicArrayTypeOf!T) && !is(StringTypeOf!T) && !is(T == enum) && !hasToS // alias this, input range I/F, and toString() unittest { - struct S(uint flags) + struct S(int flags) { int[] arr; static if (flags & 1) @@ -2107,7 +2140,9 @@ if (isInputRange!T) // Formatting character ranges like string if (f.spec == 's') { - static if (is(CharTypeOf!(ElementType!T))) + alias E = ElementType!T; + + static if (!is(E == enum) && is(CharTypeOf!E)) { static if (is(StringTypeOf!T)) { @@ -2268,7 +2303,10 @@ private void formatChar(Writer)(Writer w, in dchar c, in char quote) if (std.uni.isGraphical(c)) { if (c == quote || c == '\\') - put(w, '\\'), put(w, c); + { + put(w, '\\'); + put(w, c); + } else put(w, c); } @@ -2331,17 +2369,17 @@ if (is(StringTypeOf!T) && !is(T == enum)) static if (is(typeof(str[0]) : const(char))) { enum postfix = 'c'; - alias const(ubyte)[] IntArr; + alias IntArr = const(ubyte)[]; } else static if (is(typeof(str[0]) : const(wchar))) { enum postfix = 'w'; - alias const(ushort)[] IntArr; + alias IntArr = const(ushort)[]; } else static if (is(typeof(str[0]) : const(dchar))) { enum postfix = 'd'; - alias const(uint)[] IntArr; + alias IntArr = const(uint)[]; } formattedWrite(w, "x\"%(%02X %)\"%s", cast(IntArr)str, postfix); } @@ -2481,6 +2519,15 @@ unittest formatTest( S2(['c':1, 'd':2]), "S" ); } +unittest // Issue 8921 +{ + enum E : char { A = 'a', B = 'b', C = 'c' } + E[3] e = [E.A, E.B, E.C]; + formatTest(e, "[A, B, C]"); + + E[] e2 = [E.A, E.B, E.C]; + formatTest(e2, "[A, B, C]"); +} template hasToString(T, Char) { @@ -2654,6 +2701,52 @@ if (is(T == class) && !is(T == enum)) } } +/++ + $(D formatValue) allows to reuse existing format specifiers: + +/ +unittest +{ + import std.format; + import std.string : format; + + struct Point + { + int x, y; + + void toString(scope void delegate(const(char)[]) sink, + FormatSpec!char fmt) const + { + sink("("); + sink.formatValue(x, fmt); + sink(","); + sink.formatValue(y, fmt); + sink(")"); + } + } + + auto p = Point(16,11); + assert(format("%03d", p) == "(016,011)"); + assert(format("%02x", p) == "(10,0b)"); +} + +/++ + The following code compares the use of $(D formatValue) and $(D formattedWrite). + +/ +unittest +{ + import std.format; + import std.array : appender; + + auto writer1 = appender!string(); + writer1.formattedWrite("%08b", 42); + + auto writer2 = appender!string(); + auto f = singleSpec("%08b"); + writer2.formatValue(42, f); + + assert(writer1.data == writer2.data && writer1.data == "00101010"); +} + unittest { // class range (issue 5154) @@ -2733,7 +2826,22 @@ if (is(T == interface) && (hasToString!(T, Char) || !is(BuiltinTypeOf!T)) && !is } else { - formatValue(w, cast(Object)val, f); + version (Windows) + { + import std.c.windows.com : IUnknown; + static if (is(T : IUnknown)) + { + formatValue(w, *cast(void**)&val, f); + } + else + { + formatValue(w, cast(Object)val, f); + } + } + else + { + formatValue(w, cast(Object)val, f); + } } } } @@ -2755,6 +2863,26 @@ unittest } Whatever val = new C; formatTest( val, "ab" ); + + // Issue 11175 + version (Windows) + { + import core.sys.windows.windows : HRESULT; + import std.c.windows.com : IUnknown, IID; + + interface IUnknown2 : IUnknown { } + + class D : IUnknown2 + { + extern(Windows) HRESULT QueryInterface(const(IID)* riid, void** pvObject) { return typeof(return).init; } + extern(Windows) uint AddRef() { return 0; } + extern(Windows) uint Release() { return 0; } + } + + IUnknown2 d = new D; + string expected = format("%X", cast(void*)d); + formatTest(d, expected); + } } /// ditto @@ -2944,31 +3072,39 @@ unittest void formatValue(Writer, T, Char)(Writer w, T val, ref FormatSpec!Char f) if (isPointer!T && !is(T == enum) && !hasToString!(T, Char)) { - if (val is null) - put(w, "null"); - else + static if (isInputRange!T) { - static if (isInputRange!T) + if (val !is null) { formatRange(w, *val, f); + return; } - else + } + + static if (is(typeof({ shared const void* p = val; }))) + alias SharedOf(T) = shared(T); + else + alias SharedOf(T) = T; + + const SharedOf!(void*) p = val; + const pnum = ()@trusted{ return cast(ulong) p; }(); + + if (f.spec == 's') + { + if (p is null) { - const p = val; - const pnum = ()@trusted{ return cast(ulong) p; }(); - if (f.spec == 's') - { - FormatSpec!Char fs = f; // fs is copy for change its values. - fs.spec = 'X'; - formatValue(w, pnum, fs); - } - else - { - enforceFmt(f.spec == 'X' || f.spec == 'x', - "Expected one of %s, %x or %X for pointer type."); - formatValue(w, pnum, f); - } + put(w, "null"); + return; } + FormatSpec!Char fs = f; // fs is copy for change its values. + fs.spec = 'X'; + formatValue(w, pnum, fs); + } + else + { + enforceFmt(f.spec == 'X' || f.spec == 'x', + "Expected one of %s, %x or %X for pointer type."); + formatValue(w, pnum, f); } } @@ -3019,6 +3155,21 @@ unittest format("%s", &i); } +unittest +{ + // Test for issue 11778 + int* p = null; + assertThrown(format("%d", p)); + assertThrown(format("%04d", p + 2)); +} + +@safe pure unittest +{ + // Test for issue 12505 + void* p = null; + formatTest( "%08X", p, "00000000" ); +} + /** Delegates are formatted by 'Attributes ReturnType delegate(Parameters)' */ @@ -3043,18 +3194,6 @@ unittest formatTest( &func, "void delegate()" ); } -/* - Formatting a $(D typedef) is deprecated but still kept around for a while. - */ -void formatValue(Writer, T, Char)(Writer w, T val, ref FormatSpec!Char f) -if (is(T == typedef)) -{ - static if (is(T U == typedef)) - { - formatValue(w, cast(U) val, f); - } -} - /* Formats an object of type 'D' according to 'f' and writes it to 'w'. The pointer 'arg' is assumed to point to an object of type @@ -3271,6 +3410,11 @@ unittest assert(stream.data == "1.67 -0XA.3D70A3D70A3D8P-3 nan", stream.data); } + else version (Win64) + { + assert(stream.data == "1.67 -0X1.47AE14P+0 nan", + stream.data); + } else { assert(stream.data == "1.67 -0X1.47AE147AE147BP+0 nan", @@ -3296,7 +3440,10 @@ unittest formattedWrite(stream, "%a %A", 1.32, 6.78f); //formattedWrite(stream, "%x %X", 1.32); - assert(stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB86P+2"); + version (Win64) + assert(stream.data == "0x1.51eb85p+0 0X1.B1EB86P+2"); + else + assert(stream.data == "0x1.51eb851eb851fp+0 0X1.B1EB86P+2"); stream.clear(); formattedWrite(stream, "%#06.*f",2,12.345); @@ -3463,12 +3610,6 @@ unittest //writeln(stream.data); assert(stream.data == "7"); -// assert(false); -// typedef int myint; -// myint m = -7; -// stream.clear(); formattedWrite(stream, "", m); -// assert(stream.data == "-7"); - stream.clear(); formattedWrite(stream, "%s", "abc"c); assert(stream.data == "abc"); stream.clear(); formattedWrite(stream, "%s", "def"w); @@ -3710,8 +3851,9 @@ unittest // } auto stream = appender!(char[])(); - alias TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, - float, double, real) AllNumerics; + alias AllNumerics = + TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, + float, double, real); foreach (T; AllNumerics) { T value = 1; @@ -3753,8 +3895,8 @@ void formatReflectTest(T)(ref T val, string fmt, string formatted, string fn = _ static if (isAssociativeArray!T) if (__ctfe) { - alias val aa1; - alias val2 aa2; + alias aa1 = val; + alias aa2 = val2; //assert(aa1 == aa2); assert(aa1.length == aa2.length); @@ -3800,8 +3942,8 @@ void formatReflectTest(T)(ref T val, string fmt, string[] formatted, string fn = static if (isAssociativeArray!T) if (__ctfe) { - alias val aa1; - alias val2 aa2; + alias aa1 = val; + alias aa2 = val2; //assert(aa1 == aa2); assert(aa1.length == aa2.length); @@ -4150,7 +4292,7 @@ unittest Reads a string and returns it. */ T unformatValue(T, Range, Char)(ref Range input, ref FormatSpec!Char spec) - if (isInputRange!Range && isSomeString!T && !is(T == enum)) + if (isInputRange!Range && is(StringTypeOf!T) && !isAggregateType!T && !is(T == enum)) { if (spec.spec == '(') { @@ -4226,7 +4368,7 @@ unittest Reads an array (except for string types) and returns it. */ T unformatValue(T, Range, Char)(ref Range input, ref FormatSpec!Char spec) - if (isInputRange!Range && isArray!T && !isSomeString!T && !is(T == enum)) + if (isInputRange!Range && isArray!T && !is(StringTypeOf!T) && !isAggregateType!T && !is(T == enum)) { if (spec.spec == '(') { @@ -4421,8 +4563,9 @@ body enforce(i <= T.length); } - auto sep = - spec.sep ? fmt.readUpToNextSpec(input), spec.sep + if (spec.sep) + fmt.readUpToNextSpec(input); + auto sep = spec.sep ? spec.sep : fmt.trailing; debug (unformatRange) { if (!sep.empty && !input.empty) printf("-> %c, sep = %.*s\n", input.front, sep); @@ -4797,12 +4940,12 @@ $(I FormatChar): Example: ------------------------- -import std.c.stdio; +import core.stdc.stdio; import std.format; void myPrint(...) { - void putc(char c) + void putc(dchar c) { fputc(c, stdout); } @@ -4810,11 +4953,13 @@ void myPrint(...) std.format.doFormat(&putc, _arguments, _argptr); } -... +void main() +{ + int x = 27; -int x = 27; -// prints 'The answer is 27:6' -myPrint("The answer is %s:", x, 6); + // prints 'The answer is 27:6' + myPrint("The answer is %s:", x, 6); +} ------------------------ */ void doFormat(void delegate(dchar) putc, TypeInfo[] arguments, va_list argptr) @@ -4974,11 +5119,13 @@ void doFormat(void delegate(dchar) putc, TypeInfo[] arguments, va_list argptr) int n; version (Win64) { - if(isnan(v)) // snprintf writes 1.#QNAN + if (isnan(v)) // snprintf writes 1.#QNAN n = snprintf(fbuf.ptr, sl, "nan"); + else if(isInfinity(v)) // snprintf writes 1.#INF + n = snprintf(fbuf.ptr, sl, v < 0 ? "-inf" : "inf"); else n = snprintf(fbuf.ptr, sl, format.ptr, field_width, - precision, cast(double)v); + precision, cast(double)v); } else n = snprintf(fbuf.ptr, sl, format.ptr, field_width, @@ -5041,10 +5188,10 @@ void doFormat(void delegate(dchar) putc, TypeInfo[] arguments, va_list argptr) if (tsize > 8 && m != Mangle.Tsarray) { q = p; - argptr = &q; + argptr = cast(va_list)&q; } else - argptr = p; + argptr = cast(va_list)p; formatArg('s'); p += tsize; } @@ -5052,14 +5199,14 @@ void doFormat(void delegate(dchar) putc, TypeInfo[] arguments, va_list argptr) else { version (X86) - argptr = cast(va_list) p; + argptr = cast(va_list)p; else version(X86_64) { __va_list va; va.stack_args = p; - argptr = *cast(va_list*) &va; + argptr = *cast(va_list*)&va; } else version (ARM) - *cast(void**) &argptr = p; + *cast(void**)&argptr = p; else static assert(false, "unsupported platform"); formatArg('s'); @@ -5104,33 +5251,33 @@ void doFormat(void delegate(dchar) putc, TypeInfo[] arguments, va_list argptr) //doFormat(putc, (&keyti)[0..1], pkey); m = getMan(keyti); version (X86) - argptr = cast(va_list) pkey; + argptr = cast(va_list)pkey; else version (Win64) { version (GNU) { __va_list va; va.stack_args = pkey; - argptr = *cast(va_list*) &va; + argptr = *cast(va_list*)&va; } else { void* q = void; if (keysize > 8 && m != Mangle.Tsarray) { q = pkey; - argptr = &q; + argptr = cast(va_list)&q; } else - argptr = pkey; + argptr = cast(va_list)pkey; } } else version (X86_64) { __va_list va; va.stack_args = pkey; - argptr = *cast(va_list*) &va; + argptr = *cast(va_list*)&va; } else version (ARM) - *cast(void**) &argptr = pkey; + *cast(void**)&argptr = pkey; else static assert(false, "unsupported platform"); ti = keyti; @@ -5140,14 +5287,14 @@ void doFormat(void delegate(dchar) putc, TypeInfo[] arguments, va_list argptr) //doFormat(putc, (&valti)[0..1], pvalue); m = getMan(valti); version (X86) - argptr = cast(va_list) pvalue; + argptr = cast(va_list)pvalue; else version (Win64) { version (GNU) { __va_list va2; va2.stack_args = pvalue; - argptr = *cast(va_list*) &va2; + argptr = *cast(va_list*)&va2; } else { @@ -5155,19 +5302,19 @@ void doFormat(void delegate(dchar) putc, TypeInfo[] arguments, va_list argptr) auto valuesize = valti.tsize; if (valuesize > 8 && m != Mangle.Tsarray) { q2 = pvalue; - argptr = &q2; + argptr = cast(va_list)&q2; } else - argptr = pvalue; + argptr = cast(va_list)pvalue; } } else version (X86_64) { __va_list va2; va2.stack_args = pvalue; - argptr = *cast(va_list*) &va2; + argptr = *cast(va_list*)&va2; } else version (ARM) - *cast(void**) &argptr = pvalue; + *cast(void**)&argptr = pvalue; else static assert(false, "unsupported platform"); ti = valti; @@ -5840,6 +5987,8 @@ unittest //else version (MinGW) assert(s == "1.67 -0XA.3D70A3D70A3D8P-3 nan", s); + else version (Win64) + assert(s == "1.67 -0X1.47AE14P+0 nan", s); else assert(s == "1.67 -0X1.47AE147AE147BP+0 nan", s); @@ -5998,11 +6147,6 @@ unittest r = std.string.format("%.*d", -3, 7); assert(r == "7"); - //typedef int myint; - //myint m = -7; - //r = std.string.format(m); - //assert(r == "-7"); - r = std.string.format("abc"c); assert(r == "abc"); r = std.string.format("def"w); diff --git a/libphobos/src/std/functional.d b/libphobos/src/std/functional.d index d336b8c09..f51618c86 100644 --- a/libphobos/src/std/functional.d +++ b/libphobos/src/std/functional.d @@ -20,13 +20,14 @@ Distributed under the Boost Software License, Version 1.0. */ module std.functional; -import std.traits, std.typecons, std.typetuple; -// for making various functions visible in *naryFun -import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; +import std.traits, std.typetuple; /** Transforms a string representing an expression into a unary -function. The string must use symbol name $(D a) as the parameter. +function. The string must either use symbol name $(D a) as +the parameter or provide the symbol via the $(D parmName) argument. +If $(D fun) is not a string, $(D unaryFun) aliases itself away to +$(D fun). Example: @@ -36,33 +37,31 @@ assert(isEven(2) && !isEven(1)); ---- */ -template unaryFun(alias fun, bool byRef = false, string parmName = "a") +template unaryFun(alias fun, string parmName = "a") { static if (is(typeof(fun) : string)) { - static if (byRef) + import std.traits, std.typecons, std.typetuple; + import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; + auto unaryFun(ElementType)(auto ref ElementType __a) { - auto unaryFun(ElementType)(ref ElementType __a) - { - mixin("alias __a "~parmName~";"); - mixin("return (" ~ fun ~ ");"); - } - } - else - { - auto unaryFun(ElementType)(ElementType __a) - { - mixin("alias __a "~parmName~";"); - mixin("return (" ~ fun ~ ");"); - } + mixin("alias " ~ parmName ~ " = __a ;"); + return mixin(fun); } } else { - alias fun unaryFun; + alias unaryFun = fun; } } +/+ Undocumented, will be removed December 2014+/ +deprecated("Parameter byRef is obsolete. Please call unaryFun!(fun, parmName) directly.") +template unaryFun(alias fun, bool byRef, string parmName = "a") +{ + alias unaryFun = unaryFun!(fun, parmName); +} + unittest { static int f1(int a) { return a + 1; } @@ -80,15 +79,18 @@ unittest /** Transforms a string representing an expression into a Boolean binary -predicate. The string must use symbol names $(D a) and $(D b) as the -compared elements. +predicate. The string must either use symbol names $(D a) and $(D b) +as the parameters or provide the symbols via the $(D parm1Name) and +$(D parm2Name) arguments. +If $(D fun) is not a string, $(D binaryFun) aliases itself away to +$(D fun). Example: ---- -alias binaryFun!("a < b") less; +alias less = binaryFun!("a < b"); assert(less(1, 2) && !less(2, 1)); -alias binaryFun!("a > b") greater; +alias greater = binaryFun!("a > b"); assert(!greater("1", "2") && greater("2", "1")); ---- */ @@ -98,23 +100,25 @@ template binaryFun(alias fun, string parm1Name = "a", { static if (is(typeof(fun) : string)) { + import std.traits, std.typecons, std.typetuple; + import std.algorithm, std.conv, std.exception, std.math, std.range, std.string; auto binaryFun(ElementType1, ElementType2) - (ElementType1 __a, ElementType2 __b) + (auto ref ElementType1 __a, auto ref ElementType2 __b) { - mixin("alias __a "~parm1Name~";"); - mixin("alias __b "~parm2Name~";"); - mixin("return (" ~ fun ~ ");"); + mixin("alias "~parm1Name~" = __a ;"); + mixin("alias "~parm2Name~" = __b ;"); + return mixin(fun); } } else { - alias fun binaryFun; + alias binaryFun = fun; } } unittest { - alias binaryFun!(q{a < b}) less; + alias less = binaryFun!(q{a < b}); assert(less(1, 2) && !less(2, 1)); assert(less("1", "2") && !less("2", "1")); @@ -133,17 +137,54 @@ unittest Predicate that returns $(D_PARAM a < b). */ //bool less(T)(T a, T b) { return a < b; } -//alias binaryFun!(q{a < b}) less; +//alias less = binaryFun!(q{a < b}); /* Predicate that returns $(D_PARAM a > b). */ -//alias binaryFun!(q{a > b}) greater; +//alias greater = binaryFun!(q{a > b}); /* Predicate that returns $(D_PARAM a == b). */ -//alias binaryFun!(q{a == b}) equalTo; +//alias equalTo = binaryFun!(q{a == b}); + +/** + N-ary predicate that reverses the order of arguments, e.g., given + $(D pred(a, b, c)), returns $(D pred(c, b, a)). +*/ +template reverseArgs(alias pred) +{ + auto reverseArgs(Args...)(auto ref Args args) + if (is(typeof(pred(Reverse!args)))) + { + return pred(Reverse!args); + } +} + +unittest +{ + alias gt = reverseArgs!(binaryFun!("a < b")); + assert(gt(2, 1) && !gt(1, 1)); + int x = 42; + bool xyz(int a, int b) { return a * x < b / x; } + auto foo = &xyz; + foo(4, 5); + alias zyx = reverseArgs!(foo); + assert(zyx(5, 4) == foo(4, 5)); + + int abc(int a, int b, int c) { return a * b + c; } + alias cba = reverseArgs!abc; + assert(abc(91, 17, 32) == cba(32, 17, 91)); + + int a(int a) { return a * 2; } + alias _a = reverseArgs!a; + assert(a(2) == _a(2)); + + int b() { return 4; } + alias _b = reverseArgs!b; + assert(b() == _b()); +} /** Binary predicate that reverses the order of arguments, e.g., given @@ -160,13 +201,13 @@ template binaryReverseArgs(alias pred) unittest { - alias binaryReverseArgs!(binaryFun!("a < b")) gt; + alias gt = binaryReverseArgs!(binaryFun!("a < b")); assert(gt(2, 1) && !gt(1, 1)); int x = 42; bool xyz(int a, int b) { return a * x < b / x; } auto foo = &xyz; foo(4, 5); - alias binaryReverseArgs!(foo) zyx; + alias zyx = binaryReverseArgs!(foo); assert(zyx(5, 4) == foo(4, 5)); } @@ -189,37 +230,37 @@ template not(alias pred) else static if (T.length == 2) return !binaryFun!pred(args); else - static assert(false, "not unimplemented for multiple arguments"); + static assert(false, "not implemented for multiple arguments"); } } /** -Curries $(D fun) by tying its first argument to a particular value. +Partially evaluates $(D fun) by tying its first argument to a particular value. Example: ---- int fun(int a, int b) { return a + b; } -alias curry!(fun, 5) fun5; +alias partial!(fun, 5) fun5; assert(fun5(6) == 11); ---- Note that in most cases you'd use an alias instead of a value -assignment. Using an alias allows you to curry template functions -without committing to a particular type of the function. +assignment. Using an alias allows you to partially evaluate template +functions without committing to a particular type of the function. */ -template curry(alias fun, alias arg) +template partial(alias fun, alias arg) { static if (is(typeof(fun) == delegate) || is(typeof(fun) == function)) { - ReturnType!fun curry(ParameterTypeTuple!fun[1..$] args2) + ReturnType!fun partial(ParameterTypeTuple!fun[1..$] args2) { return fun(arg, args2); } } else { - auto curry(Ts...)(Ts args2) + auto partial(Ts...)(Ts args2) { static if (is(typeof(fun(arg, args2)))) { @@ -242,37 +283,44 @@ template curry(alias fun, alias arg) } } -// tests for currying callables +/** +Deprecated alias for $(D partial), kept for backwards compatibility + */ + +deprecated("Please use std.functional.partial instead") +alias curry = partial; + +// tests for partially evaluating callables unittest { static int f1(int a, int b) { return a + b; } - assert(curry!(f1, 5)(6) == 11); + assert(partial!(f1, 5)(6) == 11); int f2(int a, int b) { return a + b; } int x = 5; - assert(curry!(f2, x)(6) == 11); + assert(partial!(f2, x)(6) == 11); x = 7; - assert(curry!(f2, x)(6) == 13); - static assert(curry!(f2, 5)(6) == 11); + assert(partial!(f2, x)(6) == 13); + static assert(partial!(f2, 5)(6) == 11); auto dg = &f2; - auto f3 = &curry!(dg, x); + auto f3 = &partial!(dg, x); assert(f3(6) == 13); static int funOneArg(int a) { return a; } - assert(curry!(funOneArg, 1)() == 1); + assert(partial!(funOneArg, 1)() == 1); static int funThreeArgs(int a, int b, int c) { return a + b + c; } - alias curry!(funThreeArgs, 1) funThreeArgs1; + alias funThreeArgs1 = partial!(funThreeArgs, 1); assert(funThreeArgs1(2, 3) == 6); static assert(!is(typeof(funThreeArgs1(2)))); enum xe = 5; - alias curry!(f2, xe) fe; + alias fe = partial!(f2, xe); static assert(fe(6) == 11); } -// tests for currying templated/overloaded callables +// tests for partially evaluating templated/overloaded callables unittest { static auto add(A, B)(A x, B y) @@ -280,17 +328,17 @@ unittest return x + y; } - alias curry!(add, 5) add5; + alias add5 = partial!(add, 5); assert(add5(6) == 11); static assert(!is(typeof(add5()))); static assert(!is(typeof(add5(6, 7)))); - // taking address of templated curry needs explicit type + // taking address of templated partial evaluation needs explicit type auto dg = &add5!(int); assert(dg(6) == 11); int x = 5; - alias curry!(add, x) addX; + alias addX = partial!(add, x); assert(addX(6) == 11); static struct Callable @@ -300,9 +348,9 @@ unittest double opCall(double a, double b) { return a + b; } } Callable callable; - assert(curry!(Callable, "5")("6") == "56"); - assert(curry!(callable, 5)(6) == 30); - assert(curry!(callable, 7.0)(3.0) == 7.0 + 3.0); + assert(partial!(Callable, "5")("6") == "56"); + assert(partial!(callable, 5)(6) == 30); + assert(partial!(callable, 7.0)(3.0) == 7.0 + 3.0); static struct TCallable { @@ -312,15 +360,15 @@ unittest } } TCallable tcallable; - assert(curry!(tcallable, 5)(6) == 11); - static assert(!is(typeof(curry!(tcallable, "5")(6)))); + assert(partial!(tcallable, 5)(6) == 11); + static assert(!is(typeof(partial!(tcallable, "5")(6)))); static A funOneArg(A)(A a) { return a; } - alias curry!(funOneArg, 1) funOneArg1; + alias funOneArg1 = partial!(funOneArg, 1); assert(funOneArg1() == 1); static auto funThreeArgs(A, B, C)(A a, B b, C c) { return a + b + c; } - alias curry!(funThreeArgs, 1) funThreeArgs1; + alias funThreeArgs1 = partial!(funThreeArgs, 1); assert(funThreeArgs1(2, 3) == 6); static assert(!is(typeof(funThreeArgs1(1)))); @@ -340,43 +388,51 @@ $(XREF typecons, Tuple) with one element per passed-in function. Upon invocation, the returned tuple is the adjoined results of all functions. -Example: - ----- -static bool f1(int a) { return a != 0; } -static int f2(int a) { return a / 2; } -auto x = adjoin!(f1, f2)(5); -assert(is(typeof(x) == Tuple!(bool, int))); -assert(x[0] == true && x[1] == 2); ----- +Note: In the special case where where only a single function is provided +($(D F.length == 1)), adjoin simply aliases to the single passed function +($(D F[0])). */ -template adjoin(F...) if (F.length) +template adjoin(F...) if (F.length == 1) { - auto adjoin(V...)(V a) + alias adjoin = F[0]; +} +/// ditto +template adjoin(F...) if (F.length > 1) +{ + auto adjoin(V...)(auto ref V a) { - static if (F.length == 1) + import std.typecons : tuple; + static if (F.length == 2) { - return F[0](a); + return tuple(F[0](a), F[1](a)); } - else static if (F.length == 2) + else static if (F.length == 3) { - return tuple(F[0](a), F[1](a)); + return tuple(F[0](a), F[1](a), F[2](a)); } else { - alias typeof(F[0](a)) Head; - Tuple!(Head, typeof(.adjoin!(F[1..$])(a)).Types) result = void; - foreach (i, Unused; result.Types) - { - emplace(&result[i], F[i](a)); - } - return result; + import std.string : format; + import std.range : iota; + return mixin (q{tuple(%(F[%s](a)%|, %))}.format(iota(0, F.length))); } } } +/// +unittest +{ + import std.functional, std.typecons; + static bool f1(int a) { return a != 0; } + static int f2(int a) { return a / 2; } + auto x = adjoin!(f1, f2)(5); + assert(is(typeof(x) == Tuple!(bool, int))); + assert(x[0] == true && x[1] == 2); +} + unittest { + import std.typecons; static bool F1(int a) { return a != 0; } auto x1 = adjoin!(F1)(5); static int F2(int a) { return a / 2; } @@ -388,7 +444,7 @@ unittest assert(x3[0] && x3[1] == 2 && x3[2] == 2); bool F4(int a) { return a != x1; } - alias adjoin!(F4) eff4; + alias eff4 = adjoin!(F4); static struct S { bool delegate(int) store; @@ -400,6 +456,25 @@ unittest assert(x4 == 43); } +unittest +{ + import std.typetuple : staticMap; + import std.typecons : Tuple, tuple; + alias funs = staticMap!(unaryFun, "a", "a * 2", "a * 3", "a * a", "-a"); + alias afun = adjoin!funs; + assert(afun(5) == tuple(5, 10, 15, 25, -5)); + + static class C{} + alias IC = immutable(C); + IC foo(){return typeof(return).init;} + Tuple!(IC, IC, IC, IC) ret1 = adjoin!(foo, foo, foo, foo)(); + + static struct S{int* p;} + alias IS = immutable(S); + IS bar(){return typeof(return).init;} + enum Tuple!(IS, IS, IS, IS) ret2 = adjoin!(bar, bar, bar, bar)(); +} + // /*private*/ template NaryFun(string fun, string letter, V...) // { // static if (V.length == 0) @@ -412,7 +487,7 @@ unittest // ~NaryFun!(fun, [letter[0] + 1], V[1..$]).args; // enum code = args ~ "return "~fun~";"; // } -// alias void Result; +// alias Result = void; // } // unittest @@ -435,7 +510,7 @@ unittest // unittest // { -// alias naryFun!("a + b") test; +// alias test = naryFun!("a + b"); // test(1, 2); // } @@ -458,13 +533,13 @@ template compose(fun...) { static if (fun.length == 1) { - alias unaryFun!(fun[0]) compose; + alias compose = unaryFun!(fun[0]); } else static if (fun.length == 2) { // starch - alias unaryFun!(fun[0]) fun0; - alias unaryFun!(fun[1]) fun1; + alias fun0 = unaryFun!(fun[0]); + alias fun1 = unaryFun!(fun[1]); // protein: the core composition operation typeof({ E a; return fun0(fun1(a)); }()) compose(E)(E a) @@ -475,7 +550,7 @@ template compose(fun...) else { // protein: assembling operations - alias compose!(fun[0], compose!(fun[1 .. $])) compose; + alias compose = compose!(fun[0], compose!(fun[1 .. $])); } } @@ -494,13 +569,11 @@ template compose(fun...) int[] a = pipe!(readText, split, map!(to!(int)))("file.txt"); ---- */ -template pipe(fun...) -{ - alias compose!(Reverse!(fun)) pipe; -} +alias pipe(fun...) = compose!(Reverse!(fun)); unittest { + import std.conv : to; string foo(int a) { return to!(string)(a); } int bar(string a) { return to!(int)(a) + 1; } double baz(int a) { return a + 0.5; } @@ -529,7 +602,7 @@ double transmogrify(int a, string b) { ... expensive computation ... } -alias memoize!transmogrify fastTransmogrify; +alias fastTransmogrify = memoize!transmogrify; unittest { auto slow = transmogrify(2, "hello"); @@ -550,7 +623,7 @@ Example: ---- ulong fib(ulong n) { - alias memoize!fib mfib; + alias mfib = memoize!fib; return n < 2 ? 1 : mfib(n - 2) + mfib(n - 1); } ... @@ -563,7 +636,7 @@ Example: ---- ulong fact(ulong n) { - alias memoize!fact mfact; + alias mfact = memoize!fact; return n < 2 ? 1 : n * mfact(n - 1); } ... @@ -577,9 +650,9 @@ Example: ---- ulong factImpl(ulong n) { - return n < 2 ? 1 : n * mfact(n - 1); + return n < 2 ? 1 : n * factImpl(n - 1); } -alias memoize!factImpl fact; +alias fact = memoize!factImpl; ... assert(fact(10) == 3628800); ---- @@ -590,15 +663,17 @@ table is found to be $(D maxSize), the table is simply cleared. Example: ---- // Memoize no more than 128 values of transmogrify -alias memoize!(transmogrify, 128) fastTransmogrify; +alias fastTransmogrify = memoize!(transmogrify, 128); ---- */ template memoize(alias fun, uint maxSize = uint.max) { - ReturnType!fun memoize(ParameterTypeTuple!fun args) + private alias Args = ParameterTypeTuple!fun; + ReturnType!fun memoize(Args args) { - static ReturnType!fun[Tuple!(typeof(args))] memo; - auto t = tuple(args); + import std.typecons : Tuple, tuple; + static ReturnType!fun[Tuple!Args] memo; + auto t = Tuple!Args(args); auto p = t in memo; if (p) return *p; static if (maxSize != uint.max) @@ -614,21 +689,22 @@ template memoize(alias fun, uint maxSize = uint.max) unittest { - alias memoize!(function double(double x) { return sqrt(x); }) msqrt; + import core.math; + alias msqrt = memoize!(function double(double x) { return sqrt(x); }); auto y = msqrt(2.0); assert(y == msqrt(2.0)); y = msqrt(4.0); assert(y == sqrt(4.0)); - // alias memoize!rgb2cmyk mrgb2cmyk; + // alias mrgb2cmyk = memoize!rgb2cmyk; // auto z = mrgb2cmyk([43, 56, 76]); // assert(z == mrgb2cmyk([43, 56, 76])); - //alias memoize!fib mfib; + //alias mfib = memoize!fib; static ulong fib(ulong n) { - alias memoize!fib mfib; + alias mfib = memoize!fib; return n < 2 ? 1 : mfib(n - 2) + mfib(n - 1); } @@ -637,13 +713,25 @@ unittest static ulong fact(ulong n) { - alias memoize!fact mfact; + alias mfact = memoize!fact; return n < 2 ? 1 : n * mfact(n - 1); } assert(fact(10) == 3628800); + + // Issue 12568 + static uint len2(const string s) { // Error + alias mLen2 = memoize!len2; + if (s.length == 0) + return 0; + else + return 1 + mLen2(s[1 .. $]); + } } -private struct DelegateFaker(F) { +private struct DelegateFaker(F) +{ + import std.typecons; + // for @safe static F castToF(THIS)(THIS x) @trusted { @@ -688,7 +776,7 @@ private struct DelegateFaker(F) { } } // Type information used by the generated code. - alias FuncInfo!(F) FuncInfo_doIt; + alias FuncInfo_doIt = FuncInfo!(F); // Generate the member function doIt(). mixin( std.typecons.MemberFunctionGenerator!(GeneratingPolicy!()) @@ -732,7 +820,7 @@ auto toDelegate(F)(auto ref F fp) if (isCallable!(F)) } else { - alias typeof(&(new DelegateFaker!(F)).doIt) DelType; + alias DelType = typeof(&(new DelegateFaker!(F)).doIt); static struct DelegateFields { union { @@ -828,7 +916,7 @@ unittest { static assert(is(typeof(dg_trusted) == int delegate() @trusted)); static assert(is(typeof(dg_system) == int delegate() @system)); static assert(is(typeof(dg_pure_nothrow) == int delegate() pure nothrow)); - //static assert(is(typeof(dg_pure_nothrow_safe) == int delegate() pure nothrow @safe)); + //static assert(is(typeof(dg_pure_nothrow_safe) == int delegate() @safe pure nothrow)); assert(dg_ref() == refvar); assert(dg_pure() == 1); diff --git a/libphobos/src/std/getopt.d b/libphobos/src/std/getopt.d index e9ca17a79..8dfe61cac 100644 --- a/libphobos/src/std/getopt.d +++ b/libphobos/src/std/getopt.d @@ -30,10 +30,6 @@ Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ - -/* NOTE: This file has been patched from the original DMD distribution to - work with the GDC compiler. - */ module std.getopt; private import std.array, std.string, std.conv, std.traits, std.bitmanip, @@ -44,6 +40,20 @@ version (unittest) import std.stdio; // for testing only } +/** + * Thrown on one of the following conditions: + * - An unrecognized command-line argument is passed + * and $(D std.getopt.config.passThrough) was not present. + */ +class GetOptException : Exception +{ + @safe pure nothrow + this(string msg, string file = __FILE__, size_t line = __LINE__) + { + super(msg, file, line); + } +} + /** Parse and remove command line options from an string array. @@ -85,7 +95,10 @@ void main(string[] args) to their defaults and then invoke $(D getopt). If a command-line argument is recognized as an option with a parameter and the parameter cannot be parsed properly (e.g. a number is expected - but not present), a $(D Exception) exception is thrown. + but not present), a $(D ConvException) exception is thrown. + If $(D std.getopt.config.passThrough) was not passed to getopt + and an unrecognized command-line argument is found, a $(D GetOptException) + is thrown. Depending on the type of the pointer being bound, $(D getopt) recognizes the following kinds of options: @@ -114,7 +127,7 @@ void main(string[] args) --------- To set $(D timeout) to $(D 5), invoke the program with either $(D ---timeout=5) or $(D --timeout 5). +--timeout=5) or $(D --timeout 5).) $(UL $(LI $(I Incremental options.) If an option name has a "+" suffix and is bound to a numeric type, then the option's value tracks the number @@ -169,7 +182,18 @@ getopt(args, "output", &outputFiles); Invoking the program with "--output=myfile.txt --output=yourfile.txt" or "--output myfile.txt --output yourfile.txt" will set $(D - outputFiles) to [ "myfile.txt", "yourfile.txt" ] .) + outputFiles) to [ "myfile.txt", "yourfile.txt" ]. + + Alternatively you can set $(LREF arraySep) as the element separator: + +--------- +string[] outputFiles; +arraySep = ","; // defaults to "", separation by whitespace +getopt(args, "output", &outputFiles); +--------- + + With the above code you can invoke the program with + "--output=myfile.txt,yourfile.txt", or "--output myfile.txt,yourfile.txt".) $(LI $(I Hash options.) If an option is bound to an associative array, a string of the form "name=value" is expected as the next @@ -181,8 +205,20 @@ getopt(args, "tune", &tuningParms); --------- Invoking the program with e.g. "--tune=alpha=0.5 --tune beta=0.6" will -set $(D tuningParms) to [ "alpha" : 0.5, "beta" : 0.6 ]. In general, -keys and values can be of any parsable types.) +set $(D tuningParms) to [ "alpha" : 0.5, "beta" : 0.6 ]. + +Alternatively you can set $(LREF arraySep) as the element separator: + +--------- +double[string] tuningParms; +arraySep = ","; // defaults to "", separation by whitespace +getopt(args, "tune", &tuningParms); +--------- + +With the above code you can invoke the program with +"--tune=alpha=0.5,beta=0.6", or "--tune alpha=0.5,beta=0.6". + +In general, the keys and values can be of any parsable types.) $(LI $(I Callback options.) An option can be bound to a function or delegate with the signature $(D void function()), $(D void function(string option)), @@ -357,9 +393,9 @@ void getopt(T...)(ref string[] args, T opts) { } /** - Configuration options for $(D getopt). - - You can pass them to $(D getopt) in any position, except in between an option + Configuration options for $(D getopt). + + You can pass them to $(D getopt) in any position, except in between an option string and its bound pointer. */ enum config { @@ -424,7 +460,7 @@ private void getoptImpl(T...)(ref string[] args, } if (!cfg.passThrough) { - throw new Exception("Unrecognized option "~a); + throw new GetOptException("Unrecognized option "~a); } } } @@ -478,7 +514,7 @@ void handleOption(R)(string option, R receiver, ref string[] args, // parse '--b=true/false' if (val.length) { - *receiver = parse!(typeof(*receiver))(val); + *receiver = to!(typeof(*receiver))(val); break; } @@ -503,8 +539,7 @@ void handleOption(R)(string option, R receiver, ref string[] args, } static if (is(typeof(*receiver) == enum)) { - // enum receiver - *receiver = parse!(typeof(*receiver))(val); + *receiver = to!(typeof(*receiver))(val); } else static if (is(typeof(*receiver) : real)) { @@ -541,16 +576,46 @@ void handleOption(R)(string option, R receiver, ref string[] args, else static if (isArray!(typeof(*receiver))) { // array receiver - *receiver ~= [ to!(typeof((*receiver)[0]))(val) ]; + import std.range : ElementEncodingType; + alias E = ElementEncodingType!(typeof(*receiver)); + + if (arraySep == "") + { + *receiver ~= to!E(val); + } + else + { + foreach (elem; val.splitter(arraySep).map!(a => to!E(a))()) + *receiver ~= elem; + } } else static if (isAssociativeArray!(typeof(*receiver))) { // hash receiver - alias typeof(receiver.keys[0]) K; - alias typeof(receiver.values[0]) V; - auto j = std.string.indexOf(val, assignChar); - auto key = val[0 .. j], value = val[j + 1 .. $]; - (*receiver)[to!(K)(key)] = to!(V)(value); + alias K = typeof(receiver.keys[0]); + alias V = typeof(receiver.values[0]); + + import std.range : only; + import std.typecons : Tuple, tuple; + + static Tuple!(K, V) getter(string input) + { + auto j = std.string.indexOf(input, assignChar); + auto key = input[0 .. j]; + auto value = input[j + 1 .. $]; + return tuple(to!K(key), to!V(value)); + } + + static void setHash(Range)(R receiver, Range range) + { + foreach (k, v; range.map!getter) + (*receiver)[k] = v; + } + + if (arraySep == "") + setHash(receiver, val.only); + else + setHash(receiver, val.splitter(arraySep)); } else { @@ -561,28 +626,91 @@ void handleOption(R)(string option, R receiver, ref string[] args, } } +// 5316 - arrays with arraySep +unittest +{ + arraySep = ","; + scope (exit) arraySep = ""; + + string[] names; + auto args = ["program.name", "-nfoo,bar,baz"]; + getopt(args, "name|n", &names); + assert(names == ["foo", "bar", "baz"], to!string(names)); + + names = names.init; + args = ["program.name", "-n" "foo,bar,baz"].dup; + getopt(args, "name|n", &names); + assert(names == ["foo", "bar", "baz"], to!string(names)); + + names = names.init; + args = ["program.name", "--name=foo,bar,baz"].dup; + getopt(args, "name|n", &names); + assert(names == ["foo", "bar", "baz"], to!string(names)); + + names = names.init; + args = ["program.name", "--name", "foo,bar,baz"].dup; + getopt(args, "name|n", &names); + assert(names == ["foo", "bar", "baz"], to!string(names)); +} + +// 5316 - associative arrays with arraySep +unittest +{ + arraySep = ","; + scope (exit) arraySep = ""; + + int[string] values; + values = values.init; + auto args = ["program.name", "-vfoo=0,bar=1,baz=2"].dup; + getopt(args, "values|v", &values); + assert(values == ["foo":0, "bar":1, "baz":2], to!string(values)); + + values = values.init; + args = ["program.name", "-v", "foo=0,bar=1,baz=2"].dup; + getopt(args, "values|v", &values); + assert(values == ["foo":0, "bar":1, "baz":2], to!string(values)); + + values = values.init; + args = ["program.name", "--values=foo=0,bar=1,baz=2"]; + getopt(args, "values|t", &values); + assert(values == ["foo":0, "bar":1, "baz":2], to!string(values)); + + values = values.init; + args = ["program.name", "--values", "foo=0,bar=1,baz=2"].dup; + getopt(args, "values|v", &values); + assert(values == ["foo":0, "bar":1, "baz":2], to!string(values)); +} + /** - The option character (default '-'). + The option character (default '-'). Defaults to '-' but it can be assigned to prior to calling $(D getopt). */ dchar optionChar = '-'; /** - The string that conventionally marks the end of all options (default '--'). + The string that conventionally marks the end of all options (default '--'). - Defaults to "--" but can be assigned to prior to calling $(D getopt). Assigning an + Defaults to "--" but can be assigned to prior to calling $(D getopt). Assigning an empty string to $(D endOfOptions) effectively disables it. */ string endOfOptions = "--"; /** - The assignment character used in options with parameters (default '='). + The assignment character used in options with parameters (default '='). Defaults to '=' but can be assigned to prior to calling $(D getopt). */ dchar assignChar = '='; +/** + The string used to separate the elements of an array or associative array + (default is "" which means the elements are separated by whitespace). + + Defaults to "" but can be assigned to prior to calling $(D getopt). + */ +string arraySep = ""; + enum autoIncrementChar = '+'; private struct configuration @@ -703,16 +831,30 @@ unittest "--output", "yourfile.txt"]).dup; getopt(args, "output", &outputFiles); assert(outputFiles.length == 2 - && outputFiles[0] == "myfile.txt" && outputFiles[0] == "myfile.txt"); + && outputFiles[0] == "myfile.txt" && outputFiles[1] == "yourfile.txt"); - args = (["program.name", "--tune=alpha=0.5", - "--tune", "beta=0.6"]).dup; - double[string] tuningParms; - getopt(args, "tune", &tuningParms); - assert(args.length == 1); - assert(tuningParms.length == 2); - assert(approxEqual(tuningParms["alpha"], 0.5)); - assert(approxEqual(tuningParms["beta"], 0.6)); + outputFiles = []; + arraySep = ","; + args = (["program.name", "--output", "myfile.txt,yourfile.txt"]).dup; + getopt(args, "output", &outputFiles); + assert(outputFiles.length == 2 + && outputFiles[0] == "myfile.txt" && outputFiles[1] == "yourfile.txt"); + arraySep = ""; + + foreach (testArgs; + [["program.name", "--tune=alpha=0.5", "--tune", "beta=0.6"], + ["program.name", "--tune=alpha=0.5,beta=0.6"], + ["program.name", "--tune", "alpha=0.5,beta=0.6"]]) + { + arraySep = ","; + double[string] tuningParms; + getopt(testArgs, "tune", &tuningParms); + assert(testArgs.length == 1); + assert(tuningParms.length == 2); + assert(approxEqual(tuningParms["alpha"], 0.5)); + assert(approxEqual(tuningParms["beta"], 0.6)); + arraySep = ""; + } uint verbosityLevel = 1; void myHandler(string option) @@ -880,3 +1022,41 @@ unittest args.getopt(config.passThrough, "opt", &opt); assert(args == ["main", "-test"]); } + +unittest // 5228 +{ + auto args = ["prog", "--foo=bar"]; + int abc; + assertThrown!GetOptException(getopt(args, "abc", &abc)); + + args = ["prog", "--abc=string"]; + assertThrown!ConvException(getopt(args, "abc", &abc)); +} + +unittest // From bugzilla 7693 +{ + enum Foo { + bar, + baz + } + + auto args = ["prog", "--foo=barZZZ"]; + Foo foo; + assertThrown(getopt(args, "foo", &foo)); + args = ["prog", "--foo=bar"]; + assertNotThrown(getopt(args, "foo", &foo)); + args = ["prog", "--foo", "barZZZ"]; + assertThrown(getopt(args, "foo", &foo)); + args = ["prog", "--foo", "baz"]; + assertNotThrown(getopt(args, "foo", &foo)); +} + +unittest // same bug as 7693 only for bool +{ + auto args = ["prog", "--foo=truefoobar"]; + bool foo; + assertThrown(getopt(args, "foo", &foo)); + args = ["prog", "--foo"]; + getopt(args, "foo", &foo); + assert(foo); +} diff --git a/libphobos/src/std/internal/math/biguintcore.d b/libphobos/src/std/internal/math/biguintcore.d index 66e8f44aa..01d3f020c 100644 --- a/libphobos/src/std/internal/math/biguintcore.d +++ b/libphobos/src/std/internal/math/biguintcore.d @@ -42,8 +42,8 @@ else import std.internal.math.biguintnoasm; } -alias multibyteAddSub!('+') multibyteAdd; -alias multibyteAddSub!('-') multibyteSub; +alias multibyteAdd = multibyteAddSub!('+'); +alias multibyteSub = multibyteAddSub!('-'); private import core.cpuid; @@ -65,11 +65,11 @@ immutable size_t FASTDIVLIMIT; // crossover to recursive division static if (BigDigit.sizeof == int.sizeof) { enum { LG2BIGDIGITBITS = 5, BIGDIGITSHIFTMASK = 31 }; - alias ushort BIGHALFDIGIT; + alias BIGHALFDIGIT = ushort; } else static if (BigDigit.sizeof == long.sizeof) { - alias uint BIGHALFDIGIT; + alias BIGHALFDIGIT = uint; enum { LG2BIGDIGITBITS = 6, BIGDIGITSHIFTMASK = 63 }; } else static assert(0, "Unsupported BigDigit size"); @@ -99,17 +99,17 @@ private: assert( data.length >= 1 && (data.length == 1 || data[$-1] != 0 )); } immutable(BigDigit) [] data = ZERO; - this(immutable(BigDigit) [] x) pure + this(immutable(BigDigit) [] x) pure nothrow { data = x; } - this(T)(T x) pure if (isIntegral!T) + this(T)(T x) pure nothrow if (isIntegral!T) { opAssign(x); } public: // Length in uints - size_t uintLength() pure const + size_t uintLength() pure nothrow const { static if (BigDigit.sizeof == uint.sizeof) { @@ -121,7 +121,7 @@ public: ((data[$-1] & 0xFFFF_FFFF_0000_0000L) ? 1 : 0); } } - size_t ulongLength() pure const + size_t ulongLength() pure nothrow const { static if (BigDigit.sizeof == uint.sizeof) { @@ -134,7 +134,7 @@ public: } // The value at (cast(ulong[])data)[n] - ulong peekUlong(int n) pure const + ulong peekUlong(int n) pure nothrow const { static if (BigDigit.sizeof == int.sizeof) { @@ -146,7 +146,7 @@ public: return data[n]; } } - uint peekUint(int n) pure const + uint peekUint(int n) pure nothrow const { static if (BigDigit.sizeof == int.sizeof) { @@ -160,7 +160,7 @@ public: } public: /// - void opAssign(Tulong)(Tulong u) pure if (is (Tulong == ulong)) + void opAssign(Tulong)(Tulong u) pure nothrow if (is (Tulong == ulong)) { if (u == 0) data = ZERO; else if (u == 1) data = ONE; @@ -187,7 +187,7 @@ public: } } } - void opAssign(Tdummy = void)(BigUint y) pure + void opAssign(Tdummy = void)(BigUint y) pure nothrow { this.data = y.data; } @@ -337,7 +337,7 @@ public: } // return false if invalid character found - bool fromHexString(const(char)[] s) pure + bool fromHexString(const(char)[] s) pure nothrow { //Strip leading zeros int firstNonZero = 0; @@ -401,8 +401,10 @@ public: } auto predictlength = (18*2 + 2*(s.length-firstNonZero)) / 19; auto tmp = new BigDigit[predictlength]; + uint hi = biguintFromDecimal(tmp, s[firstNonZero..$]); tmp.length = hi; + data = assumeUnique(tmp); return true; } @@ -412,7 +414,7 @@ public: // All of these member functions create a new BigUint. // return x >> y - BigUint opShr(Tulong)(Tulong y) pure const if (is (Tulong == ulong)) + BigUint opShr(Tulong)(Tulong y) pure nothrow const if (is (Tulong == ulong)) { assert(y>0); uint bits = cast(uint)y & BIGDIGITSHIFTMASK; @@ -432,7 +434,7 @@ public: } // return x << y - BigUint opShl(Tulong)(Tulong y) pure const if (is (Tulong == ulong)) + BigUint opShl(Tulong)(Tulong y) pure nothrow const if (is (Tulong == ulong)) { assert(y>0); if (isZero()) return this; @@ -458,7 +460,7 @@ public: // If wantSub is false, return x + y, leaving sign unchanged // If wantSub is true, return abs(x - y), negating sign if x < y static BigUint addOrSubInt(Tulong)(const BigUint x, Tulong y, - bool wantSub, ref bool sign) pure if (is(Tulong == ulong)) + bool wantSub, ref bool sign) pure nothrow if (is(Tulong == ulong)) { BigUint r; if (wantSub) @@ -508,7 +510,7 @@ public: // If wantSub is false, return x + y, leaving sign unchanged. // If wantSub is true, return abs(x - y), negating sign if x>> 32); @@ -548,7 +550,7 @@ public: /* return x * y. */ - static BigUint mul(BigUint x, BigUint y) pure + static BigUint mul(BigUint x, BigUint y) pure nothrow { if (y==0 || x == 0) return BigUint(ZERO); @@ -569,7 +571,8 @@ public: } // return x / y - static BigUint divInt(T)(BigUint x, T y_) pure if ( is(Unqual!T == uint) ) + static BigUint divInt(T)(BigUint x, T y_) pure nothrow + if ( is(Unqual!T == uint) ) { uint y = y_; if (y == 1) @@ -615,7 +618,7 @@ public: } // return x / y - static BigUint div(BigUint x, BigUint y) pure + static BigUint div(BigUint x, BigUint y) pure nothrow { if (y.data.length > x.data.length) return BigUint(ZERO); @@ -627,7 +630,7 @@ public: } // return x % y - static BigUint mod(BigUint x, BigUint y) pure + static BigUint mod(BigUint x, BigUint y) pure nothrow { if (y.data.length > x.data.length) return x; if (y.data.length == 1) @@ -641,7 +644,8 @@ public: } // return x op y - static BigUint bitwiseOp(string op)(BigUint x, BigUint y, bool xSign, bool ySign, ref bool resultSign) pure if (op == "|" || op == "^" || op == "&") + static BigUint bitwiseOp(string op)(BigUint x, BigUint y, bool xSign, bool ySign, ref bool resultSign) + pure nothrow if (op == "|" || op == "^" || op == "&") { auto d1 = includeSign(x.data, y.uintLength, xSign); auto d2 = includeSign(y.data, x.uintLength, ySign); @@ -666,7 +670,7 @@ public: * exponentiation is used. * Memory allocation is minimized: at most one temporary BigUint is used. */ - static BigUint pow(BigUint x, ulong y) pure + static BigUint pow(BigUint x, ulong y) pure nothrow { // Deal with the degenerate cases first. if (y==0) return BigUint(ONE); @@ -888,10 +892,16 @@ unittest BigUint a = [1]; assert(a == 1); assert(a < 0x8000_0000_0000_0000UL); // bug 9548 + + // bug 12234 + BigUint z = [0]; + assert(z == 0UL); + assert(!(z > 0UL)); + assert(!(z < 0UL)); } // Remove leading zeros from x, to restore the BigUint invariant -inout(BigDigit) [] removeLeadingZeros(inout(BigDigit) [] x) pure +inout(BigDigit) [] removeLeadingZeros(inout(BigDigit) [] x) pure nothrow { size_t k = x.length; while(k>1 && x[k - 1]==0) --k; @@ -967,7 +977,7 @@ unittest private: -void twosComplement(const(BigDigit) [] x, BigDigit[] result) pure +void twosComplement(const(BigDigit) [] x, BigDigit[] result) pure nothrow { foreach (i; 0..x.length) { @@ -988,7 +998,8 @@ void twosComplement(const(BigDigit) [] x, BigDigit[] result) pure } // Encode BigInt as BigDigit array (sign and 2's complement) -BigDigit[] includeSign(const(BigDigit) [] x, size_t minSize, bool sign) pure +BigDigit[] includeSign(const(BigDigit) [] x, size_t minSize, bool sign) +pure nothrow { size_t length = (x.length > minSize) ? x.length : minSize; BigDigit [] result = new BigDigit[length]; @@ -1004,7 +1015,7 @@ BigDigit[] includeSign(const(BigDigit) [] x, size_t minSize, bool sign) pure } // works for any type -T intpow(T)(T x, ulong n) pure +T intpow(T)(T x, ulong n) pure nothrow { T p; @@ -1039,7 +1050,7 @@ T intpow(T)(T x, ulong n) pure // returns the maximum power of x that will fit in a uint. -int highestPowerBelowUintMax(uint x) pure +int highestPowerBelowUintMax(uint x) pure nothrow { assert(x>1); static immutable ubyte [22] maxpwr = [ 31, 20, 15, 13, 12, 11, 10, 10, 9, 9, @@ -1054,7 +1065,7 @@ int highestPowerBelowUintMax(uint x) pure } // returns the maximum power of x that will fit in a ulong. -int highestPowerBelowUlongMax(uint x) pure +int highestPowerBelowUlongMax(uint x) pure nothrow { assert(x>1); static immutable ubyte [39] maxpwr = [ 63, 40, 31, 27, 24, 22, 21, 20, 19, 18, @@ -1076,7 +1087,7 @@ int highestPowerBelowUlongMax(uint x) pure version(unittest) { -int slowHighestPowerBelowUintMax(uint x) pure +int slowHighestPowerBelowUintMax(uint x) pure nothrow { int pwr = 1; for (ulong q = x;x*q < cast(ulong)uint.max; ) { @@ -1097,7 +1108,8 @@ unittest /* General unsigned subtraction routine for bigints. * Sets result = x - y. If the result is negative, negative will be true. */ -BigDigit [] sub(const BigDigit [] x, const BigDigit [] y, bool *negative) pure +BigDigit [] sub(const BigDigit [] x, const BigDigit [] y, bool *negative) +pure nothrow { if (x.length == y.length) { @@ -1152,7 +1164,7 @@ BigDigit [] sub(const BigDigit [] x, const BigDigit [] y, bool *negative) pure // return a + b -BigDigit [] add(const BigDigit [] a, const BigDigit [] b) pure +BigDigit [] add(const BigDigit [] a, const BigDigit [] b) pure nothrow { const(BigDigit) [] x, y; if (a.length < b.length) @@ -1184,7 +1196,7 @@ BigDigit [] add(const BigDigit [] a, const BigDigit [] b) pure /** return x + y */ -BigDigit [] addInt(const BigDigit[] x, ulong y) pure +BigDigit [] addInt(const BigDigit[] x, ulong y) pure nothrow { uint hi = cast(uint)(y >>> 32); uint lo = cast(uint)(y& 0xFFFF_FFFF); @@ -1207,7 +1219,7 @@ BigDigit [] addInt(const BigDigit[] x, ulong y) pure /** Return x - y. * x must be greater than y. */ -BigDigit [] subInt(const BigDigit[] x, ulong y) pure +BigDigit [] subInt(const BigDigit[] x, ulong y) pure nothrow { uint hi = cast(uint)(y >>> 32); uint lo = cast(uint)(y & 0xFFFF_FFFF); @@ -1232,7 +1244,7 @@ BigDigit [] subInt(const BigDigit[] x, ulong y) pure * */ void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y) - pure + pure nothrow { assert( result.length == x.length + y.length ); assert( y.length > 0 ); @@ -1362,7 +1374,7 @@ void mulInternal(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y) * NOTE: If the highest half-digit of x is zero, the highest digit of result will * also be zero. */ -void squareInternal(BigDigit[] result, const BigDigit[] x) pure +void squareInternal(BigDigit[] result, const BigDigit[] x) pure nothrow { // Squaring is potentially half a multiply, plus add the squares of // the diagonal elements. @@ -1386,7 +1398,7 @@ import core.bitop : bsr; /// if remainder is null, only calculate quotient. void divModInternal(BigDigit [] quotient, BigDigit[] remainder, const BigDigit [] u, - const BigDigit [] v) pure + const BigDigit [] v) pure nothrow { assert(quotient.length == u.length - v.length + 1); assert(remainder == null || remainder.length == v.length); @@ -1657,7 +1669,7 @@ private: // Classic 'schoolbook' multiplication. void mulSimple(BigDigit[] result, const(BigDigit) [] left, - const(BigDigit)[] right) pure + const(BigDigit)[] right) pure nothrow in { assert(result.length == left.length + right.length); @@ -1670,7 +1682,7 @@ body } // Classic 'schoolbook' squaring -void squareSimple(BigDigit[] result, const(BigDigit) [] x) pure +void squareSimple(BigDigit[] result, const(BigDigit) [] x) pure nothrow in { assert(result.length == 2*x.length); @@ -1685,7 +1697,8 @@ body // add two uints of possibly different lengths. Result must be as long // as the larger length. // Returns carry (0 or 1). -uint addSimple(BigDigit[] result, const BigDigit [] left, const BigDigit [] right) pure +uint addSimple(BigDigit[] result, const BigDigit [] left, const BigDigit [] right) +pure nothrow in { assert(result.length == left.length); @@ -1707,7 +1720,7 @@ body // result = left - right // returns carry (0 or 1) BigDigit subSimple(BigDigit [] result,const(BigDigit) [] left, - const(BigDigit) [] right) pure + const(BigDigit) [] right) pure nothrow in { assert(result.length == left.length); @@ -1730,7 +1743,7 @@ body /* result = result - right * Returns carry = 1 if result was less than right. */ -BigDigit subAssignSimple(BigDigit [] result, const(BigDigit) [] right) pure +BigDigit subAssignSimple(BigDigit [] result, const(BigDigit) [] right) pure nothrow { assert(result.length >= right.length); uint c = multibyteSub(result[0..right.length], result[0..right.length], right, 0); @@ -1741,7 +1754,7 @@ BigDigit subAssignSimple(BigDigit [] result, const(BigDigit) [] right) pure /* result = result + right */ -BigDigit addAssignSimple(BigDigit [] result, const(BigDigit) [] right) pure +BigDigit addAssignSimple(BigDigit [] result, const(BigDigit) [] right) pure nothrow { assert(result.length >= right.length); uint c = multibyteAdd(result[0..right.length], result[0..right.length], right, 0); @@ -1753,7 +1766,7 @@ BigDigit addAssignSimple(BigDigit [] result, const(BigDigit) [] right) pure /* performs result += wantSub? - right : right; */ BigDigit addOrSubAssignSimple(BigDigit [] result, const(BigDigit) [] right, - bool wantSub) pure + bool wantSub) pure nothrow { if (wantSub) return subAssignSimple(result, right); @@ -1763,7 +1776,7 @@ BigDigit addOrSubAssignSimple(BigDigit [] result, const(BigDigit) [] right, // return true if x= y.length); auto k = x.length-1; @@ -1778,7 +1791,7 @@ bool less(const(BigDigit)[] x, const(BigDigit)[] y) pure // Set result = abs(x-y), return true if result is negative(x= y.length) ? x.length : y.length); @@ -1818,7 +1831,7 @@ bool inplaceSub(BigDigit[] result, const(BigDigit)[] x, const(BigDigit)[] y) /* Determine how much space is required for the temporaries * when performing a Karatsuba multiplication. */ -size_t karatsubaRequiredBuffSize(size_t xlen) pure +size_t karatsubaRequiredBuffSize(size_t xlen) pure nothrow { return xlen <= KARATSUBALIMIT ? 0 : 2*xlen; // - KARATSUBALIMIT+2; } @@ -1835,7 +1848,7 @@ size_t karatsubaRequiredBuffSize(size_t xlen) pure * scratchbuff An array long enough to store all the temporaries. Will be destroyed. */ void mulKaratsuba(BigDigit [] result, const(BigDigit) [] x, - const(BigDigit)[] y, BigDigit [] scratchbuff) pure + const(BigDigit)[] y, BigDigit [] scratchbuff) pure nothrow { assert(x.length >= y.length); assert(result.length < uint.max, "Operands too large"); @@ -1940,7 +1953,7 @@ void mulKaratsuba(BigDigit [] result, const(BigDigit) [] x, } void squareKaratsuba(BigDigit [] result, const BigDigit [] x, - BigDigit [] scratchbuff) pure + BigDigit [] scratchbuff) pure nothrow { // See mulKaratsuba for implementation comments. // Squaring is simpler, since it never gets asymmetric. @@ -1997,7 +2010,7 @@ void squareKaratsuba(BigDigit [] result, const BigDigit [] x, * u[0..v.length] holds the remainder. */ void schoolbookDivMod(BigDigit [] quotient, BigDigit [] u, in BigDigit [] v) - pure + pure nothrow { assert(quotient.length == u.length - v.length); assert(v.length > 1); @@ -2079,7 +2092,7 @@ again: private: // TODO: Replace with a library call -void itoaZeroPadded(char[] output, uint value, int radix = 10) pure +void itoaZeroPadded(char[] output, uint value, int radix = 10) pure nothrow { ptrdiff_t x = output.length - 1; for( ; x >= 0; --x) @@ -2089,7 +2102,7 @@ void itoaZeroPadded(char[] output, uint value, int radix = 10) pure } } -void toHexZeroPadded(char[] output, uint value) pure +void toHexZeroPadded(char[] output, uint value) pure nothrow { ptrdiff_t x = output.length - 1; static immutable string hexDigits = "0123456789ABCDEF"; @@ -2104,7 +2117,8 @@ private: // Returns the highest value of i for which left[i]!=right[i], // or 0 if left[] == right[] -size_t highestDifferentDigit(const BigDigit [] left, const BigDigit [] right) pure +size_t highestDifferentDigit(const BigDigit [] left, const BigDigit [] right) +pure nothrow { assert(left.length == right.length); for (ptrdiff_t i = left.length - 1; i>0; --i) @@ -2116,7 +2130,7 @@ size_t highestDifferentDigit(const BigDigit [] left, const BigDigit [] right) pu } // Returns the lowest value of i for which x[i]!=0. -int firstNonZeroDigit(const BigDigit [] x) pure +int firstNonZeroDigit(const BigDigit [] x) pure nothrow { int k = 0; while (x[k]==0) @@ -2152,7 +2166,7 @@ Returns: Max-Planck Institute fuer Informatik, (Oct 1998). */ void recursiveDivMod(BigDigit[] quotient, BigDigit[] u, const(BigDigit)[] v, - BigDigit[] scratch, bool mayOverflow = false) pure + BigDigit[] scratch, bool mayOverflow = false) pure nothrow in { // v must be normalized @@ -2242,7 +2256,7 @@ body // Needs (quot.length * k) scratch space to store the result of the multiply. void adjustRemainder(BigDigit[] quot, BigDigit[] rem, const(BigDigit)[] v, ptrdiff_t k, - BigDigit[] scratch, bool mayOverflow = false) pure + BigDigit[] scratch, bool mayOverflow = false) pure nothrow { assert(rem.length == v.length); mulInternal(scratch, quot, v[0 .. k]); @@ -2259,7 +2273,8 @@ void adjustRemainder(BigDigit[] quot, BigDigit[] rem, const(BigDigit)[] v, } // Cope with unbalanced division by performing block schoolbook division. -void blockDivMod(BigDigit [] quotient, BigDigit [] u, in BigDigit [] v) pure +void blockDivMod(BigDigit [] quotient, BigDigit [] u, in BigDigit [] v) +pure nothrow { assert(quotient.length == u.length - v.length); assert(v.length > 1); diff --git a/libphobos/src/std/internal/math/biguintnoasm.d b/libphobos/src/std/internal/math/biguintnoasm.d index 7f0d4f908..8d9a34bdb 100644 --- a/libphobos/src/std/internal/math/biguintnoasm.d +++ b/libphobos/src/std/internal/math/biguintnoasm.d @@ -17,8 +17,11 @@ module std.internal.math.biguintnoasm; +nothrow: +@safe: + public: -alias uint BigDigit; // A Bignum is an array of BigDigits. +alias BigDigit = uint; // A Bignum is an array of BigDigits. // Limits for when to switch between multiplication algorithms. enum : int { KARATSUBALIMIT = 10 }; // Minimum value for which Karatsuba is worthwhile. @@ -32,7 +35,7 @@ enum : int { KARATSUBASQUARELIMIT=12 }; // Minimum value for which square Karats * Set op == '+' for addition, '-' for subtraction. */ uint multibyteAddSub(char op)(uint[] dest, const(uint) [] src1, - const (uint) [] src2, uint carry) pure + const (uint) [] src2, uint carry) pure @nogc { ulong c = carry; for (size_t i = 0; i < src2.length; ++i) @@ -96,7 +99,7 @@ unittest * op must be '+' or '-' * Returns final carry or borrow (0 or 1) */ -uint multibyteIncrementAssign(char op)(uint[] dest, uint carry) pure +uint multibyteIncrementAssign(char op)(uint[] dest, uint carry) pure @nogc { static if (op=='+') { @@ -134,7 +137,7 @@ uint multibyteIncrementAssign(char op)(uint[] dest, uint carry) pure /** dest[] = src[] << numbits * numbits must be in the range 1..31 */ -uint multibyteShl(uint [] dest, const(uint) [] src, uint numbits) pure +uint multibyteShl(uint [] dest, const(uint) [] src, uint numbits) pure @nogc { ulong c = 0; for (size_t i = 0; i < dest.length; ++i) @@ -150,7 +153,7 @@ uint multibyteShl(uint [] dest, const(uint) [] src, uint numbits) pure /** dest[] = src[] >> numbits * numbits must be in the range 1..31 */ -void multibyteShr(uint [] dest, const(uint) [] src, uint numbits) pure +void multibyteShr(uint [] dest, const(uint) [] src, uint numbits) pure @nogc { ulong c = 0; for(ptrdiff_t i = dest.length; i!=0; --i) @@ -185,7 +188,7 @@ unittest * Returns carry. */ uint multibyteMul(uint[] dest, const(uint)[] src, uint multiplier, uint carry) - pure + pure @nogc { assert(dest.length == src.length); ulong c = carry; @@ -212,7 +215,7 @@ unittest * Returns carry out of MSB (0..FFFF_FFFF). */ uint multibyteMulAdd(char op)(uint [] dest, const(uint)[] src, - uint multiplier, uint carry) pure + uint multiplier, uint carry) pure @nogc { assert(dest.length == src.length); ulong c = carry; @@ -263,7 +266,7 @@ unittest ---- */ void multibyteMultiplyAccumulate(uint [] dest, const(uint)[] left, const(uint) - [] right) pure + [] right) pure @nogc { for (size_t i = 0; i < right.length; ++i) { @@ -275,7 +278,7 @@ void multibyteMultiplyAccumulate(uint [] dest, const(uint)[] left, const(uint) /** dest[] /= divisor. * overflow is the initial remainder, and must be in the range 0..divisor-1. */ -uint multibyteDivAssign(uint [] dest, uint divisor, uint overflow) pure +uint multibyteDivAssign(uint [] dest, uint divisor, uint overflow) pure @nogc { ulong c = cast(ulong)overflow; for(ptrdiff_t i = dest.length-1; i>= 0; --i) @@ -303,7 +306,7 @@ unittest } // Set dest[2*i..2*i+1]+=src[i]*src[i] -void multibyteAddDiagonalSquares(uint[] dest, const(uint)[] src) pure +void multibyteAddDiagonalSquares(uint[] dest, const(uint)[] src) pure @nogc { ulong c = 0; for(size_t i = 0; i < src.length; ++i) @@ -318,7 +321,7 @@ void multibyteAddDiagonalSquares(uint[] dest, const(uint)[] src) pure } // Does half a square multiply. (square = diagonal + 2*triangle) -void multibyteTriangleAccumulate(uint[] dest, const(uint)[] x) pure +void multibyteTriangleAccumulate(uint[] dest, const(uint)[] x) pure @nogc { // x[0]*x[1...$] + x[1]*x[2..$] + ... + x[$-2]x[$-1..$] dest[x.length] = multibyteMul(dest[1 .. x.length], x[1..$], x[0], 0); @@ -351,7 +354,7 @@ void multibyteTriangleAccumulate(uint[] dest, const(uint)[] x) pure dest[2*x.length-2] = cast(uint)c; } -void multibyteSquare(BigDigit[] result, const(BigDigit) [] x) pure +void multibyteSquare(BigDigit[] result, const(BigDigit) [] x) pure @nogc { multibyteTriangleAccumulate(result, x); result[$-1] = multibyteShl(result[1..$-1], result[1..$-1], 1); // mul by 2 diff --git a/libphobos/src/std/internal/math/biguintx86.d b/libphobos/src/std/internal/math/biguintx86.d index 60c944f9c..bbec4965b 100644 --- a/libphobos/src/std/internal/math/biguintx86.d +++ b/libphobos/src/std/internal/math/biguintx86.d @@ -51,6 +51,9 @@ module std.internal.math.biguintx86; @system: +pure: +nothrow: + /* Naked asm is used throughout, because: (a) it frees up the EBP register @@ -91,7 +94,7 @@ unittest public: -alias uint BigDigit; // A Bignum is an array of BigDigits. Usually the machine word size. +alias BigDigit = uint; // A Bignum is an array of BigDigits. Usually the machine word size. // Limits for when to switch between multiplication algorithms. enum : int { KARATSUBALIMIT = 18 }; // Minimum value for which Karatsuba is worthwhile. @@ -139,7 +142,7 @@ L_unrolled: ~ indexedLoopUnroll( 8, "mov EAX, [@*4-8*4+EDX+ECX*4];" ~ ( op == '+' ? "adc" : "sbb" ) ~ " EAX, [@*4-8*4+ESI+ECX*4];" - "mov [@*4-8*4+EDI+ECX*4], EAX;") + ~ "mov [@*4-8*4+EDI+ECX*4], EAX;") ~ "}"); asm { setc AL; // save carry @@ -156,7 +159,7 @@ L_residual: ~ indexedLoopUnroll( 1, "mov EAX, [@*4+EDX+ECX*4];" ~ ( op == '+' ? "adc" : "sbb" ) ~ " EAX, [@*4+ESI+ECX*4];" - "mov [@*4+EDI+ECX*4], EAX;") ~ "}"); + ~ "mov [@*4+EDI+ECX*4], EAX;") ~ "}"); asm { setc AL; // save carry add ECX, 1; diff --git a/libphobos/src/std/internal/math/errorfunction.d b/libphobos/src/std/internal/math/errorfunction.d index 9be6ac6c3..6c57a364d 100644 --- a/libphobos/src/std/internal/math/errorfunction.d +++ b/libphobos/src/std/internal/math/errorfunction.d @@ -25,6 +25,11 @@ module std.internal.math.errorfunction; import std.math; +pure: +nothrow: +@safe: +@nogc: + private { immutable real EXP_2 = 0.13533528323661269189L; /* exp(-2) */ enum real SQRT2PI = 2.50662827463100050242E0L; // sqrt(2pi) diff --git a/libphobos/src/std/internal/math/gammafunction.d b/libphobos/src/std/internal/math/gammafunction.d index d54e53922..28aafce93 100644 --- a/libphobos/src/std/internal/math/gammafunction.d +++ b/libphobos/src/std/internal/math/gammafunction.d @@ -22,6 +22,11 @@ module std.internal.math.gammafunction; import std.internal.math.errorfunction; import std.math; +pure: +nothrow: +@safe: +@nogc: + private { enum real SQRT2PI = 2.50662827463100050242E0L; // sqrt(2pi) @@ -490,7 +495,7 @@ unittest { assert(logGamma(-real.min_normal*real.epsilon) == real.infinity); // x, correct loggamma(x), correct d/dx loggamma(x). - static real[] testpoints = [ + immutable static real[] testpoints = [ 8.0L, 8.525146484375L + 1.48766904143001655310E-5, 2.01564147795560999654E0L, 8.99993896484375e-1L, 6.6375732421875e-2L + 5.11505711292524166220E-6L, -7.54938684259372234258E-1, 7.31597900390625e-1L, 2.2369384765625e-1 + 5.21506341809849792422E-6L, -1.13355566660398608343E0L, diff --git a/libphobos/src/std/internal/scopebuffer.d b/libphobos/src/std/internal/scopebuffer.d new file mode 100644 index 000000000..94ff18c6b --- /dev/null +++ b/libphobos/src/std/internal/scopebuffer.d @@ -0,0 +1,394 @@ +/* + * Copyright: 2014 by Digital Mars + * License: $(LINK2 http://boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: Walter Bright + * Source: $(PHOBOSSRC std/internal/_scopebuffer.d) + */ + +module std.internal.scopebuffer; + + +//debug=ScopeBuffer; + +private import core.exception; +private import core.stdc.stdlib : realloc; +private import std.traits; + +/************************************** + * ScopeBuffer encapsulates using a local array as a temporary buffer. + * It is initialized with the local array that should be large enough for + * most uses. If the need exceeds the size, ScopeBuffer will resize it + * using malloc() and friends. + * + * ScopeBuffer cannot contain more than (uint.max-16)/2 elements. + * + * ScopeBuffer is an OutputRange. + * + * Since ScopeBuffer potentially stores elements of type T in malloc'd memory, + * those elements are not scanned when the GC collects. This can cause + * memory corruption. Do not use ScopeBuffer when elements of type T point + * to the GC heap. + * + * Example: +--- +import core.stdc.stdio; +import std.internal.scopebuffer; +void main() +{ + char[2] buf = void; + auto textbuf = ScopeBuffer!char(buf); + scope(exit) textbuf.free(); // necessary for cleanup + + // Put characters and strings into textbuf, verify they got there + textbuf.put('a'); + textbuf.put('x'); + textbuf.put("abc"); + assert(textbuf.length == 5); + assert(textbuf[1..3] == "xa"); + assert(textbuf[3] == 'b'); + + // Can shrink it + textbuf.length = 3; + assert(textbuf[0..textbuf.length] == "axa"); + assert(textbuf[textbuf.length - 1] == 'a'); + assert(textbuf[1..3] == "xa"); + + textbuf.put('z'); + assert(textbuf[] == "axaz"); + + // Can shrink it to 0 size, and reuse same memory + textbuf.length = 0; +} +--- + * It is invalid to access ScopeBuffer's contents when ScopeBuffer goes out of scope. + * Hence, copying the contents are necessary to keep them around: +--- +import std.internal.scopebuffer; +string cat(string s1, string s2) +{ + char[10] tmpbuf = void; + auto textbuf = ScopeBuffer!char(tmpbuf); + scope(exit) textbuf.free(); + textbuf.put(s1); + textbuf.put(s2); + textbuf.put("even more"); + return textbuf[].idup; +} +--- + * ScopeBuffer is intended for high performance usages in $(D @system) and $(D @trusted) code. + * It is designed to fit into two 64 bit registers, again for high performance use. + * If used incorrectly, memory leaks and corruption can result. Be sure to use + * $(D scope(exit) textbuf.free();) for proper cleanup, and do not refer to a ScopeBuffer + * instance's contents after $(D ScopeBuffer.free()) has been called. + * + * The realloc parameter defaults to C's realloc(). Another can be supplied to override it. + * + * ScopeBuffer instances may be copied, as in: +--- +textbuf = doSomething(textbuf, args); +--- + * which can be very efficent, but these must be regarded as a move rather than a copy. + * Additionally, the code between passing and returning the instance must not throw + * exceptions, otherwise when ScopeBuffer.free() is called, memory may get corrupted. + */ + +@system +struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc) + if (isAssignable!T && + !hasElaborateDestructor!T && + !hasElaborateCopyConstructor!T && + !hasElaborateAssign!T) +{ + import core.stdc.string : memcpy; + + /************************** + * Initialize with buf to use as scratch buffer space. + * Params: + * buf = Scratch buffer space, must have length that is even + * Example: + * --- + * ubyte[10] tmpbuf = void; + * auto sbuf = ScopeBuffer!ubyte(tmpbuf); + * --- + * If buf was created by the same realloc passed as a parameter + * to ScopeBuffer, then the contents of ScopeBuffer can be extracted without needing + * to copy them, and ScopeBuffer.free() will not need to be called. + */ + this(T[] buf) + in + { + assert(!(buf.length & wasResized)); // assure even length of scratch buffer space + assert(buf.length <= uint.max); // because we cast to uint later + } + body + { + this.buf = buf.ptr; + this.bufLen = cast(uint)buf.length; + } + + unittest + { + ubyte[10] tmpbuf = void; + auto sbuf = ScopeBuffer!ubyte(tmpbuf); + } + + /************************** + * Releases any memory used. + * This will invalidate any references returned by the [] operator. + * A destructor is not used, because that would make it not POD + * (Plain Old Data) and it could not be placed in registers. + */ + void free() + { + debug(ScopeBuffer) buf[0 .. bufLen] = 0; + if (bufLen & wasResized) + realloc(buf, 0); + buf = null; + bufLen = 0; + used = 0; + } + + /**************************** + * Copying of ScopeBuffer is not allowed. + */ + //@disable this(this); + + /************************ + * Append element c to the buffer. + * This member function makes ScopeBuffer an OutputRange. + */ + void put(T c) + { + /* j will get enregistered, while used will not because resize() may change used + */ + const j = used; + if (j == bufLen) + { + assert(j <= (uint.max - 16) / 2); + resize(j * 2 + 16); + } + buf[j] = c; + used = j + 1; + } + + /************************ + * Append array s to the buffer. + * + * If $(D const(T)) can be converted to $(D T), then put will accept + * $(D const(T)[]) as input. It will accept a $(D T[]) otherwise. + */ + private alias CT = Select!(is(const(T) : T), const(T), T); + /// ditto + void put(CT[] s) + { + const newlen = used + s.length; + assert((cast(ulong)used + s.length) <= uint.max); + const len = bufLen; + if (newlen > len) + { + assert(len <= uint.max / 2); + resize(newlen <= len * 2 ? len * 2 : newlen); + } + buf[used .. newlen] = s[]; + used = cast(uint)newlen; + } + + /****** + * Retrieve a slice into the result. + * Returns: + * A slice into the temporary buffer that is only + * valid until the next put() or ScopeBuffer goes out of scope. + */ + @system T[] opSlice(size_t lower, size_t upper) + in + { + assert(lower <= bufLen); + assert(upper <= bufLen); + assert(lower <= upper); + } + body + { + return buf[lower .. upper]; + } + + /// ditto + @system T[] opSlice() + { + assert(used <= bufLen); + return buf[0 .. used]; + } + + /******* + * Returns: + * the element at index i. + */ + ref T opIndex(size_t i) + { + assert(i < bufLen); + return buf[i]; + } + + /*** + * Returns: + * the number of elements in the ScopeBuffer + */ + @property size_t length() const + { + return used; + } + + /*** + * Used to shrink the length of the buffer, + * typically to 0 so the buffer can be reused. + * Cannot be used to extend the length of the buffer. + */ + @property void length(size_t i) + in + { + assert(i <= this.used); + } + body + { + this.used = cast(uint)i; + } + + alias opDollar = length; + + private: + T* buf; + // Using uint instead of size_t so the struct fits in 2 registers in 64 bit code + uint bufLen; + enum wasResized = 1; // this bit is set in bufLen if we control the memory + uint used; + + void resize(size_t newsize) + in + { + assert(newsize <= uint.max); + } + body + { + //writefln("%s: oldsize %s newsize %s", id, buf.length, newsize); + newsize |= wasResized; + void *newBuf = realloc((bufLen & wasResized) ? buf : null, newsize * T.sizeof); + if (!newBuf) + core.exception.onOutOfMemoryError(); + if (!(bufLen & wasResized)) + { + memcpy(newBuf, buf, used * T.sizeof); + debug(ScopeBuffer) buf[0 .. bufLen] = 0; + } + buf = cast(T*)newBuf; + bufLen = cast(uint)newsize; + + /* This function is called only rarely, + * inlining results in poorer register allocation. + */ + version (DigitalMars) + /* With dmd, a fake loop will prevent inlining. + * Using a hack until a language enhancement is implemented. + */ + while (1) { break; } + } +} + +unittest +{ + import core.stdc.stdio; + import std.range; + + char[2] tmpbuf = void; + { + // Exercise all the lines of code except for assert(0)'s + auto textbuf = ScopeBuffer!char(tmpbuf); + scope(exit) textbuf.free(); + + static assert(isOutputRange!(ScopeBuffer!char, char)); + + textbuf.put('a'); + textbuf.put('x'); + textbuf.put("abc"); // tickle put([])'s resize + assert(textbuf.length == 5); + assert(textbuf[1..3] == "xa"); + assert(textbuf[3] == 'b'); + + textbuf.length = textbuf.length - 1; + assert(textbuf[0..textbuf.length] == "axab"); + + textbuf.length = 3; + assert(textbuf[0..textbuf.length] == "axa"); + assert(textbuf[textbuf.length - 1] == 'a'); + assert(textbuf[1..3] == "xa"); + + textbuf.put(cast(dchar)'z'); + assert(textbuf[] == "axaz"); + + textbuf.length = 0; // reset for reuse + assert(textbuf.length == 0); + + foreach (char c; "asdf;lasdlfaklsdjfalksdjfa;lksdjflkajsfdasdfkja;sdlfj") + { + textbuf.put(c); // tickle put(c)'s resize + } + assert(textbuf[] == "asdf;lasdlfaklsdjfalksdjfa;lksdjflkajsfdasdfkja;sdlfj"); + } // run destructor on textbuf here + +} + +unittest +{ + string cat(string s1, string s2) + { + char[10] tmpbuf = void; + auto textbuf = ScopeBuffer!char(tmpbuf); + scope(exit) textbuf.free(); + textbuf.put(s1); + textbuf.put(s2); + textbuf.put("even more"); + return textbuf[].idup; + } + + auto s = cat("hello", "betty"); + assert(s == "hellobettyeven more"); +} + +/********************************* + * This is a slightly simpler way to create a ScopeBuffer instance + * that uses type deduction. + * Params: + * tmpbuf = the initial buffer to use + * Returns: + * an instance of ScopeBuffer + * Example: +--- +ubyte[10] tmpbuf = void; +auto sb = scopeBuffer(tmpbuf); +scope(exit) sp.free(); +--- + */ + +auto scopeBuffer(T)(T[] tmpbuf) +{ + return ScopeBuffer!T(tmpbuf); +} + +unittest +{ + ubyte[10] tmpbuf = void; + auto sb = scopeBuffer(tmpbuf); + scope(exit) sb.free(); +} + +unittest +{ + ScopeBuffer!(int*) b; + int*[] s; + b.put(s); + + ScopeBuffer!char c; + string s1; + char[] s2; + c.put(s1); + c.put(s2); +} + diff --git a/libphobos/src/std/internal/uni.d b/libphobos/src/std/internal/uni.d deleted file mode 100644 index a0e381adc..000000000 --- a/libphobos/src/std/internal/uni.d +++ /dev/null @@ -1,805 +0,0 @@ -//Written in the D programming language -/** - Codepoint set and trie for efficient character class manipulation, - currently for internal use only. -*/ -module std.internal.uni; - -import std.algorithm, std.range, std.uni, std.format; -import std.internal.uni_tab; -import core.bitop; - -@safe: -public: - -//wrappers for CTFE -@trusted void insertInPlaceAlt(T)(ref T[] arr, size_t idx, T[] items...) -{ - if(__ctfe) - arr = arr[0..idx] ~ items ~ arr[idx..$]; - else - insertInPlace(arr, idx, items); -} - -//ditto + nothing better in std.algo for overlapping arrays anyway -@trusted void copyForwardAlt(T)(T[] src, T[] dest) -{ - for(size_t i = 0; i < src.length; i++) - dest[i] = src[i]; -} - -//ditto -@trusted void replaceInPlaceAlt(T)(ref T[] arr, size_t from, size_t to, T[] items...) -in -{ - assert(to >= from); -} -body -{ - if(__ctfe) - arr = arr[0..from]~items~arr[to..$]; - else //@@@BUG@@@ in replaceInPlace? symptoms being sudden ZEROs in array - { - //replaceInPlace(arr, from, to, items); - size_t window = to - from, ilen = items.length; - if(window >= ilen) - { - size_t delta = window - ilen; - arr[from .. from+ilen] = items[0..$]; - if(delta) - {//arrayops won't do - aliasing - for(size_t i = from+ilen; i < arr.length-delta; i++) - arr[i] = arr[i+delta]; - arr.length -= delta; - } - } - else - { - size_t delta = ilen - window, old = arr.length; - arr.length += delta; - //arrayops won't do - aliasing - for(size_t i = old - 1; i != to-1; i--) - arr[i+delta] = arr[i]; - arr[from .. from+ilen] = items[0..$]; - } - } -} - -//$(D Interval) represents an interval of codepoints: [a,b). -struct Interval -{ - uint begin, end; - - ///Create interval containig a single character $(D ch). - this(dchar ch) - { - begin = ch; - end = ch+1; - } - - /++ - Create Interval from inclusive range [$(D a),$(D b)]. Contrary to internal structure, inclusive is chosen for interface. - The reason for this is usability e.g. it's would force user to type the unwieldy Interval('a','z'+1) all over the place. - +/ - this(dchar a, dchar b) - { - assert(a <= b); - begin = a; - end = b+1; - } - - /// - @trusted string toString()const - { - auto s = appender!string(); - formattedWrite(s,"%s..%s", begin, end); - return s.data; - } - -} - -/+ - $(D CodepointSet) is a data structure for manipulating sets - of Unicode codepoints in an efficient manner. - Instances of CodepointSet have half-reference semantics akin to dynamic arrays, - to obtain a unique copy use $(D dup). -+/ -struct CodepointSet -{ - enum uint endOfRange = 0x110000; - uint[] ivals; - - //Add an $(D interval) of codepoints to this set. - @trusted ref CodepointSet add(Interval inter) - { - debug(fred_charset) writeln("Inserting ",inter); - if(ivals.empty) - { - insertInPlaceAlt(ivals, 0, inter.begin, inter.end); - return this; - }//assumeSorted is @system - auto svals = assumeSorted(ivals); - auto s = svals.lowerBound(inter.begin).length; - auto e = s+svals[s..svals.length].lowerBound(inter.end).length; - debug(fred_charset) writeln("Indexes: ", s," ", e); - if(s & 1) - { - inter.begin = ivals[s-1]; - s ^= 1; - } - if(e & 1) - { - inter.end = ivals[e]; - e += 1; - } - else //e % 2 == 0 - { - if(e < ivals.length && inter.end == ivals[e]) - { - inter.end = ivals[e+1]; - e+=2; - } - } - debug(fred_charset) - for(size_t i=1;i ", ivals, set.ivals); - for(size_t i=0; i b.front.end) - { - b.popFront(); - } - else //there is an intersection - { - if(a.front.begin < b.front.begin) - { - result ~= Interval(a.front.begin, b.front.begin-1); - if(a.front.end < b.front.end) - { - a.popFront(); - } - else if(a.front.end > b.front.end) - { - //adjust a in place - a.front.begin = b.front.end; - if(a.front.begin >= a.front.end) - a.popFront(); - b.popFront(); - } - else //== - { - a.popFront(); - b.popFront(); - } - } - else //a.front.begin > b.front.begin - {//adjust in place - if(a.front.end < b.front.end) - { - a.popFront(); - } - else - { - a.front.begin = b.front.end; - if(a.front.begin >= a.front.end) - a.popFront(); - b.popFront(); - } - } - } - } - result ~= a;//+ leftover of original - ivals = cast(uint[])result; - return this; - } - - //Make this set a symmetric difference with $(D set). - //Algebra: this = this ~ set (i.e. (this || set) -- (this && set)). - @trusted ref CodepointSet symmetricSub(in CodepointSet set) - { - auto a = CodepointSet(ivals.dup); - a.intersect(set); - this.add(set); - this.sub(a); - return this; - } - - //Intersect this set with $(D set). - //Algebra: this = this & set - @trusted ref CodepointSet intersect(in CodepointSet set) - { - if(empty || set.empty) - { - ivals = []; - return this; - } - Interval[] intersection; - auto a = cast(const(Interval)[])ivals; - auto b = cast(const(Interval)[])set.ivals; - for(;;) - { - if(a.front.end < b.front.begin) - { - a.popFront(); - if(a.empty) - break; - } - else if(a.front.begin > b.front.end) - { - b.popFront(); - if(b.empty) - break; - } - else //there is an intersection - { - if(a.front.end < b.front.end) - { - intersection ~= Interval(max(a.front.begin, b.front.begin), a.front.end); - a.popFront(); - if(a.empty) - break; - } - else if(a.front.end > b.front.end) - { - intersection ~= Interval(max(a.front.begin, b.front.begin), b.front.end); - b.popFront(); - if(b.empty) - break; - } - else //== - { - intersection ~= Interval(max(a.front.begin, b.front.begin), a.front.end); - a.popFront(); - b.popFront(); - if(a.empty || b.empty) - break; - } - } - } - ivals = cast(uint[])intersection; - return this; - } - - //this = !this (i.e. [^...] in regex syntax) - @trusted ref CodepointSet negate() - { - if(empty) - { - insertInPlaceAlt(ivals, 0, 0u, endOfRange); - return this; - } - if(ivals[0] != 0) - insertInPlaceAlt(ivals, 0, 0u); - else - { - for(size_t i=1; i= ivals[i-1]; - return false; - } - - /+ - Test if ch is present in this set, in $(BIGOH LogN) operations on number - of $(U intervals) in this set. - +/ - @trusted bool opIndex(dchar ch)const - { - auto svals = assumeSorted!"a <= b"(ivals); - auto s = svals.lowerBound(cast(uint)ch).length; - return s & 1; - } - - //Test if this set is empty. - @property bool empty() const pure nothrow { return ivals.empty; } - - //Write out in regular expression style [\uxxxx-\uyyyy...]. - @trusted void printUnicodeSet(R)(R sink) const - if(isOutputRange!(R, const(char)[])) - { - sink("["); - for(uint i=0;i= ivals[1]) - { - ivals = ivals[2..$]; - if(!empty) - j = ivals[0]; - } - } - @property ByCodepoint save() const { return this; } - } - static assert(isForwardRange!ByCodepoint); - - //Forward range of all codepoints in this set. - auto opSlice() const - { - return ByCodepoint(this); - } - - //Random access range of intervals in this set. - @trusted @property auto byInterval() const - { - const(uint)[] hack = ivals; - return cast(const(Interval)[])hack; - } - //eaten alive by @@@BUG@@@s - /+invariant() - { - assert(ivals.length % 2 == 0); - for(size_t i=1; i 4) -{ - static if(size_t.sizeof == 4) - enum unitBits = 2; - else static if(size_t.sizeof == 8) - enum unitBits = 3; - else - static assert(0); - enum prefixWordBits = prefixBits-unitBits, prefixSize=1< 500_000 ? (negative=true, s.dup.negate()) : s; - uint bound = 0;//set up on first iteration - ushort emptyBlock = ushort.max; - auto ivals = set.ivals; - size_t[prefixWordSize] page; - for(uint i=0; i ivals[bound] || emptyBlock == ushort.max)//avoid empty blocks if we have one already - { - bool flag = true; - L_Prefix_Loop: - for(uint j=0; j= ivals[bound+1]) - { - bound += 2; - if(bound == ivals.length) - { - bound = uint.max; - if(flag)//not a single one set so far - return; - //no more bits in the whole set, but need to add the last bucket - break L_Prefix_Loop; - } - } - if(i+j >= ivals[bound]) - { - enum mask = (1<<(3+unitBits))-1; - page[j>>(3+unitBits)] - |= cast(size_t)1<<(j & mask); - flag = false; - } - } - - debug(fred_trie) - { - printBlock(page); - } - uint npos; - for(npos=0;npos>prefixWordBits); - break; - } - if(npos == data.length) - { - indexes ~= cast(ushort)(data.length>>prefixWordBits); - data ~= page; - if(flag) - emptyBlock = indexes[$-1]; - } - if(bound == uint.max) - break; - page[] = 0; - } - else//fast reroute whole blocks to an empty one - { - indexes ~= emptyBlock; - } - } - } - - //Test if contains $(D ch). - @trusted bool opIndex(dchar ch) const - { - assert(ch < 0x110000); - uint ind = ch>>prefixBits; - if(ind >= indexes.length) - return negative; - return cast(bool)bt(data.ptr, (indexes[ind]<>3)]>>(ch&7))&1) ^ negative; - } - } - - //invert trie (trick internal for regular expressions, has aliasing problem) - @trusted private auto negated() const - { - CodepointTrie t = cast(CodepointTrie)this;//shallow copy, need to subvert type system? - t.negative = !negative; - return t; - } -} - - -unittest -{ - auto wordSet = - CodepointSet.init.add(unicodeAlphabetic).add(unicodeMn).add(unicodeMc) - .add(unicodeMe).add(unicodeNd).add(unicodePc); - auto t = CodepointTrie!8(wordSet); - assert(t['a']); - assert(!t[' ']); -} - -unittest -{ - CodepointSet set; - set.add(unicodeAlphabetic); - for(size_t i=1;i assert(trie[ch], text("on ch == ", ch, " seed was ", seed))); - test(neg, ch => assert(!trie[ch], text("negative on ch == ", ch, " seed was ", seed))); - } - - // test that negate is reversible - foreach(up; testCases.save) - { - auto neg = up.set.dup.negate().negate(); - assert(equal(up.set.ivals, neg.ivals)); - } - - // test codepoint forward iterator - auto set = testCases.front.set; - auto rng = set[]; - foreach (idx; 0 .. set.ivals.length / 2) - { - immutable lo = set.ivals[2*idx], hi = set.ivals[2*idx+1]; - foreach (val; lo .. hi) - { - assert(rng.front == val, text("on val == ", val, " seed was ", seed)); - rng.popFront(); - } - } -} - - -//fussy compare for unicode property names as per UTS-18 -int comparePropertyName(Char)(const(Char)[] a, const(Char)[] b) -{ - for(;;) - { - while(!a.empty && (isWhite(a.front) || a.front == '-' || a.front =='_')) - { - a.popFront(); - } - while(!b.empty && (isWhite(b.front) || b.front == '-' || b.front =='_')) - { - b.popFront(); - } - if(a.empty) - return b.empty ? 0 : -1; - if(b.empty) - return 1; - auto ca = toLower(a.front), cb = toLower(b.front); - if(ca > cb) - return 1; - else if( ca < cb) - return -1; - a.popFront(); - b.popFront(); - } -} - -//ditto (workaround for internal tools) -public bool propertyNameLess(Char)(const(Char)[] a, const(Char)[] b) -{ - return comparePropertyName(a, b) < 0; -} - -unittest -{ - assert(comparePropertyName("test","test") == 0); - assert(comparePropertyName("Al chemical Symbols", "Alphabetic Presentation Forms") == -1); - assert(comparePropertyName("Basic Latin","basic-LaTin") == 0); -} - -//Gets array of all of common case eqivalents of given codepoint -//(fills provided array & returns a slice of it) -@trusted dchar[] getCommonCasing(dchar ch, dchar[] range) -{ - CommonCaseEntry cs; - size_t i=1, j=0; - range[0] = ch; - while(j < i) - { - ch = range[j++]; - cs.start = ch; - cs.end = ch; - auto idx = assumeSorted!"a.end <= b.end"(commonCaseTable) - .lowerBound(cs).length; - immutable(CommonCaseEntry)[] slice = commonCaseTable[idx..$]; - idx = assumeSorted!"a.start <= b.start"(slice).lowerBound(cs).length; - slice = slice[0..idx]; - foreach(v; slice) - if(ch < v.end) - { - if(v.xor) - { - auto t = ch ^ v.delta; - if(countUntil(range[0..i], t) < 0) - range[i++] = t; - } - else - { - auto t = v.neg ? ch - v.delta : ch + v.delta; - if(countUntil(range[0..i], t) < 0) - range[i++] = t; - } - } - } - return range[0..i]; -} - -unittest -{ - dchar[6] data; - //these values give 100% code coverage for getCommonCasing - assert(getCommonCasing(0x01BC, data) == [0x01bc, 0x01bd]); - assert(getCommonCasing(0x03B9, data) == [0x03b9, 0x0399, 0x1fbe, 0x0345]); - assert(getCommonCasing(0x10402, data) == [0x10402, 0x1042a]); -} - -// -@trusted CodepointSet caseEnclose(in CodepointSet set) -{ - CodepointSet n; - for(size_t i=0;iBoost License 1.0. - * Authors: $(WEB digitalmars.com, Walter Bright), - * Don Clugston, Conversion of CEPHES math library to D by Iain Buclaw + * Authors: $(WEB digitalmars.com, Walter Bright), Don Clugston, + * Conversion of CEPHES math library to D by Iain Buclaw * Source: $(PHOBOSSRC std/_math.d) */ /* NOTE: This file has been patched from the original DMD distribution to - work with the GDC compiler. + * work with the GDC compiler. */ module std.math; @@ -74,11 +83,6 @@ version (Win64) import core.stdc.math; import std.traits; -version(unittest) -{ - import std.typetuple; -} - version(LDC) { import ldc.intrinsics; @@ -146,9 +150,13 @@ version(unittest) int ix; int iy; - ix = sprintf(bufx.ptr, "%.*Lg", ndigits, x); + version(Win64) + alias double real_t; + else + alias real real_t; + ix = sprintf(bufx.ptr, "%.*Lg", ndigits, cast(real_t) x); + iy = sprintf(bufy.ptr, "%.*Lg", ndigits, cast(real_t) y); assert(ix < bufx.length && ix > 0); - iy = sprintf(bufy.ptr, "%.*Lg", ndigits, y); assert(ix < bufy.length && ix > 0); return bufx[0 .. ix] == bufy[0 .. iy]; @@ -158,31 +166,34 @@ version(unittest) private: -/* - * The following IEEE 'real' formats are currently supported: - * 64 bit Big-endian 'double' (eg PowerPC) - * 128 bit Big-endian 'quadruple' (eg SPARC) - * 64 bit Little-endian 'double' (eg x86-SSE2) - * 80 bit Little-endian, with implied bit 'real80' (eg x87, Itanium). - * 128 bit Little-endian 'quadruple' (not implemented on any known processor!) - * - * Non-IEEE 128 bit Big-endian 'doubledouble' (eg PowerPC) has partial support - */ +// The following IEEE 'real' formats are currently supported. version(LittleEndian) { - static assert(real.mant_dig == 53 || real.mant_dig==64 + static assert(real.mant_dig == 53 || real.mant_dig == 64 || real.mant_dig == 113, - "Only 64-bit, 80-bit, and 128-bit reals" + "Only 64-bit, 80-bit, and 128-bit reals"~ " are supported for LittleEndian CPUs"); } else { - static assert(real.mant_dig == 53 || real.mant_dig==106 + static assert(real.mant_dig == 53 || real.mant_dig == 106 || real.mant_dig == 113, - "Only 64-bit and 128-bit reals are supported for BigEndian CPUs." + "Only 64-bit and 128-bit reals are supported for BigEndian CPUs."~ " double-double reals have partial support"); } +// Underlying format exposed through floatTraits +enum RealFormat +{ + ieeeHalf, + ieeeSingle, + ieeeDouble, + ieeeExtended, // x87 80-bit real + ieeeExtended53, // x87 real rounded to precision of double. + ibmExtended, // IBM 128-bit extended + ieeeQuadruple, +} + // Constants used for extracting the components of the representation. // They supplement the built-in floating point properties. template floatTraits(T) @@ -193,41 +204,71 @@ template floatTraits(T) // RECIP_EPSILON is the value such that (smallest_subnormal) * RECIP_EPSILON == T.min_normal enum T RECIP_EPSILON = (1/T.epsilon); static if (T.mant_dig == 24) - { // float + { + // Single precision float enum ushort EXPMASK = 0x7F80; enum ushort EXPBIAS = 0x3F00; enum uint EXPMASK_INT = 0x7F80_0000; enum uint MANTISSAMASK_INT = 0x007F_FFFF; + enum realFormat = RealFormat.ieeeSingle; version(LittleEndian) { enum EXPPOS_SHORT = 1; + enum SIGNPOS_BYTE = 3; } else { enum EXPPOS_SHORT = 0; + enum SIGNPOS_BYTE = 0; } } - else static if (T.mant_dig == 53) // double, or real==double + else static if (T.mant_dig == 53) { - enum ushort EXPMASK = 0x7FF0; - enum ushort EXPBIAS = 0x3FE0; - enum uint EXPMASK_INT = 0x7FF0_0000; - enum uint MANTISSAMASK_INT = 0x000F_FFFF; // for the MSB only - version(LittleEndian) + static if (T.sizeof == 8) { - enum EXPPOS_SHORT = 3; - enum SIGNPOS_BYTE = 7; + // Double precision float, or real == double + enum ushort EXPMASK = 0x7FF0; + enum ushort EXPBIAS = 0x3FE0; + enum uint EXPMASK_INT = 0x7FF0_0000; + enum uint MANTISSAMASK_INT = 0x000F_FFFF; // for the MSB only + enum realFormat = RealFormat.ieeeDouble; + version(LittleEndian) + { + enum EXPPOS_SHORT = 3; + enum SIGNPOS_BYTE = 7; + } + else + { + enum EXPPOS_SHORT = 0; + enum SIGNPOS_BYTE = 0; + } } - else + else static if (T.sizeof == 12) { - enum EXPPOS_SHORT = 0; - enum SIGNPOS_BYTE = 0; + // Intel extended real80 rounded to double + enum ushort EXPMASK = 0x7FFF; + enum ushort EXPBIAS = 0x3FFE; + enum realFormat = RealFormat.ieeeExtended53; + version(LittleEndian) + { + enum EXPPOS_SHORT = 4; + enum SIGNPOS_BYTE = 9; + } + else + { + enum EXPPOS_SHORT = 0; + enum SIGNPOS_BYTE = 0; + } } + else + static assert(false, "No traits support for " ~ T.stringof); } - else static if (T.mant_dig == 64) // real80 + else static if (T.mant_dig == 64) { + // Intel extended real80 enum ushort EXPMASK = 0x7FFF; enum ushort EXPBIAS = 0x3FFE; + enum realFormat = RealFormat.ieeeExtended; version(LittleEndian) { enum EXPPOS_SHORT = 4; @@ -239,9 +280,11 @@ template floatTraits(T) enum SIGNPOS_BYTE = 0; } } - else static if (T.mant_dig == 113) // quadruple + else static if (T.mant_dig == 113) { + // Quadruple precision float enum ushort EXPMASK = 0x7FFF; + enum realFormat = RealFormat.ieeeQuadruple; version(LittleEndian) { enum EXPPOS_SHORT = 7; @@ -253,9 +296,11 @@ template floatTraits(T) enum SIGNPOS_BYTE = 0; } } - else static if (T.mant_dig == 106) // doubledouble + else static if (T.mant_dig == 106) { + // IBM Extended doubledouble enum ushort EXPMASK = 0x7FF0; + enum realFormat = RealFormat.ibmExtended; // the exponent byte is not unique version(LittleEndian) { @@ -268,6 +313,8 @@ template floatTraits(T) enum SIGNPOS_BYTE = 0; } } + else + static assert(false, "No traits support for " ~ T.stringof); } // These apply to all floating-point types @@ -282,6 +329,92 @@ else enum MANTISSA_MSB = 0; } +// Common code for math implementations. + +// Helper for floor/ceil +T floorImpl(T)(T x) @trusted pure nothrow @nogc +{ + alias F = floatTraits!(T); + // Take care not to trigger library calls from the compiler, + // while ensuring that we don't get defeated by some optimizers. + union floatBits + { + T rv; + ushort[T.sizeof/2] vu; + } + floatBits y = void; + y.rv = x; + + // Find the exponent (power of 2) + static if (F.realFormat == RealFormat.ieeeSingle) + { + int exp = ((y.vu[F.EXPPOS_SHORT] >> 7) & 0xff) - 0x7f; + + version (LittleEndian) + int pos = 0; + else + int pos = 3; + } + else static if (F.realFormat == RealFormat.ieeeDouble) + { + int exp = ((y.vu[F.EXPPOS_SHORT] >> 4) & 0x7ff) - 0x3ff; + + version (LittleEndian) + int pos = 0; + else + int pos = 3; + } + else static if (F.realFormat == RealFormat.ieeeExtended) + { + int exp = (y.vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff; + + version (LittleEndian) + int pos = 0; + else + int pos = 4; + } + else static if (F.realFormat == RealFormat.ieeeQuadruple) + { + int exp = (y.vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff; + + version (LittleEndian) + int pos = 0; + else + int pos = 7; + } + else + static assert(false, "Not implemented for this architecture"); + + if (exp < 0) + { + if (x < 0.0) + return -1.0; + else + return 0.0; + } + + exp = (T.mant_dig - 1) - exp; + + // Zero 16 bits at a time. + while (exp >= 16) + { + version (LittleEndian) + y.vu[pos++] = 0; + else + y.vu[pos--] = 0; + exp -= 16; + } + + // Clear the remaining bits. + if (exp > 0) + y.vu[pos] &= 0xffff ^ ((1 << exp) - 1); + + if ((x < 0.0) && (x != y.rv)) + y.rv -= 1.0; + + return y.rv; +} + public: // Values obtained from Wolfram Alpha. 116 bits ought to be enough for anybody. @@ -321,7 +454,7 @@ Num abs(Num)(Num x) @safe pure nothrow return x>=0 ? x : -x; } -auto abs(Num)(Num z) @safe pure nothrow +auto abs(Num)(Num z) @safe pure nothrow @nogc if (is(Num* : const(cfloat*)) || is(Num* : const(cdouble*)) || is(Num* : const(creal*))) { @@ -329,7 +462,7 @@ auto abs(Num)(Num z) @safe pure nothrow } /** ditto */ -real abs(Num)(Num y) @safe pure nothrow +real abs(Num)(Num y) @safe pure nothrow @nogc if (is(Num* : const(ifloat*)) || is(Num* : const(idouble*)) || is(Num* : const(ireal*))) { @@ -357,13 +490,13 @@ unittest * Note that z * conj(z) = $(POWER z.re, 2) - $(POWER z.im, 2) * is always a real number */ -creal conj(creal z) @safe pure nothrow +creal conj(creal z) @safe pure nothrow @nogc { return z.re - z.im*1i; } /** ditto */ -ireal conj(ireal y) @safe pure nothrow +ireal conj(ireal y) @safe pure nothrow @nogc { return -y; } @@ -388,7 +521,7 @@ unittest * Results are undefined if |x| >= $(POWER 2,64). */ -real cos(real x) @safe pure nothrow; /* intrinsic */ +real cos(real x) @safe pure nothrow @nogc; /* intrinsic */ /*********************************** * Returns sine of x. x is in radians. @@ -403,7 +536,7 @@ real cos(real x) @safe pure nothrow; /* intrinsic */ * Results are undefined if |x| >= $(POWER 2,64). */ -real sin(real x) @safe pure nothrow; /* intrinsic */ +real sin(real x) @safe pure nothrow @nogc; /* intrinsic */ /*********************************** @@ -414,7 +547,7 @@ real sin(real x) @safe pure nothrow; /* intrinsic */ * If both sin($(THETA)) and cos($(THETA)) are required, * it is most efficient to use expi($(THETA)). */ -creal sin(creal z) @safe pure nothrow +creal sin(creal z) @safe pure nothrow @nogc { creal cs = expi(z.re); creal csh = coshisinh(z.im); @@ -422,7 +555,7 @@ creal sin(creal z) @safe pure nothrow } /** ditto */ -ireal sin(ireal y) @safe pure nothrow +ireal sin(ireal y) @safe pure nothrow @nogc { return cosh(y.im)*1i; } @@ -438,7 +571,7 @@ unittest * * cos(z) = cos(z.re)*cosh(z.im) - sin(z.re)*sinh(z.im)i */ -creal cos(creal z) @safe pure nothrow +creal cos(creal z) @safe pure nothrow @nogc { creal cs = expi(z.re); creal csh = coshisinh(z.im); @@ -446,7 +579,7 @@ creal cos(creal z) @safe pure nothrow } /** ditto */ -real cos(ireal y) @safe pure nothrow +real cos(ireal y) @safe pure nothrow @nogc { return cosh(y.im); } @@ -469,7 +602,7 @@ unittest * ) */ -real tan(real x) @trusted pure nothrow +real tan(real x) @trusted pure nothrow @nogc { version(D_InlineAsm_X86) { @@ -690,16 +823,16 @@ unittest * $(TR $(TD $(NAN)) $(TD $(NAN)) $(TD yes)) * ) */ -real acos(real x) @safe pure nothrow +real acos(real x) @safe pure nothrow @nogc { return atan2(sqrt(1-x*x), x); } /// ditto -double acos(double x) @safe pure nothrow { return acos(cast(real)x); } +double acos(double x) @safe pure nothrow @nogc { return acos(cast(real)x); } /// ditto -float acos(float x) @safe pure nothrow { return acos(cast(real)x); } +float acos(float x) @safe pure nothrow @nogc { return acos(cast(real)x); } unittest { @@ -717,16 +850,16 @@ unittest * $(TR $(TD $(LT)-1.0) $(TD $(NAN)) $(TD yes)) * ) */ -real asin(real x) @safe pure nothrow +real asin(real x) @safe pure nothrow @nogc { return atan2(x, sqrt(1-x*x)); } /// ditto -double asin(double x) @safe pure nothrow { return asin(cast(real)x); } +double asin(double x) @safe pure nothrow @nogc { return asin(cast(real)x); } /// ditto -float asin(float x) @safe pure nothrow { return asin(cast(real)x); } +float asin(float x) @safe pure nothrow @nogc { return asin(cast(real)x); } unittest { @@ -743,7 +876,7 @@ unittest * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(NAN)) $(TD yes)) * ) */ -real atan(real x) @safe pure nothrow +real atan(real x) @safe pure nothrow @nogc { version(InlineAsm_X86_Any) { @@ -811,10 +944,10 @@ real atan(real x) @safe pure nothrow } /// ditto -double atan(double x) @safe pure nothrow { return atan(cast(real)x); } +double atan(double x) @safe pure nothrow @nogc { return atan(cast(real)x); } /// ditto -float atan(float x) @safe pure nothrow { return atan(cast(real)x); } +float atan(float x) @safe pure nothrow @nogc { return atan(cast(real)x); } unittest { @@ -842,7 +975,7 @@ unittest * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD -$(INFIN)) $(TD $(PLUSMN)3$(PI)/4)) * ) */ -real atan2(real y, real x) @trusted pure nothrow +real atan2(real y, real x) @trusted pure nothrow @nogc { version(InlineAsm_X86_Any) { @@ -918,13 +1051,13 @@ real atan2(real y, real x) @trusted pure nothrow } /// ditto -double atan2(double y, double x) @safe pure nothrow +double atan2(double y, double x) @safe pure nothrow @nogc { return atan2(cast(real)y, cast(real)x); } /// ditto -float atan2(float y, float x) @safe pure nothrow +float atan2(float y, float x) @safe pure nothrow @nogc { return atan2(cast(real)y, cast(real)x); } @@ -942,7 +1075,7 @@ unittest * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)0.0) $(TD no) ) * ) */ -real cosh(real x) @safe pure nothrow +real cosh(real x) @safe pure nothrow @nogc { // cosh = (exp(x)+exp(-x))/2. // The naive implementation works correctly. @@ -951,10 +1084,10 @@ real cosh(real x) @safe pure nothrow } /// ditto -double cosh(double x) @safe pure nothrow { return cosh(cast(real)x); } +double cosh(double x) @safe pure nothrow @nogc { return cosh(cast(real)x); } /// ditto -float cosh(float x) @safe pure nothrow { return cosh(cast(real)x); } +float cosh(float x) @safe pure nothrow @nogc { return cosh(cast(real)x); } unittest { @@ -970,7 +1103,7 @@ unittest * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD no)) * ) */ -real sinh(real x) @safe pure nothrow +real sinh(real x) @safe pure nothrow @nogc { // sinh(x) = (exp(x)-exp(-x))/2; // Very large arguments could cause an overflow, but @@ -986,10 +1119,10 @@ real sinh(real x) @safe pure nothrow } /// ditto -double sinh(double x) @safe pure nothrow { return sinh(cast(real)x); } +double sinh(double x) @safe pure nothrow @nogc { return sinh(cast(real)x); } /// ditto -float sinh(float x) @safe pure nothrow { return sinh(cast(real)x); } +float sinh(float x) @safe pure nothrow @nogc { return sinh(cast(real)x); } unittest { @@ -1005,7 +1138,7 @@ unittest * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)1.0) $(TD no)) * ) */ -real tanh(real x) @safe pure nothrow +real tanh(real x) @safe pure nothrow @nogc { // tanh(x) = (exp(x) - exp(-x))/(exp(x)+exp(-x)) if (fabs(x) > real.mant_dig * LN2) @@ -1018,10 +1151,10 @@ real tanh(real x) @safe pure nothrow } /// ditto -double tanh(double x) @safe pure nothrow { return tanh(cast(real)x); } +double tanh(double x) @safe pure nothrow @nogc { return tanh(cast(real)x); } /// ditto -float tanh(float x) @safe pure nothrow { return tanh(cast(real)x); } +float tanh(float x) @safe pure nothrow @nogc { return tanh(cast(real)x); } unittest { @@ -1033,7 +1166,7 @@ package: /* Returns cosh(x) + I * sinh(x) * Only one call to exp() is performed. */ -creal coshisinh(real x) @safe pure nothrow +creal coshisinh(real x) @safe pure nothrow @nogc { // See comments for cosh, sinh. if (fabs(x) > real.mant_dig * LN2) @@ -1073,7 +1206,7 @@ public: * $(SV +$(INFIN),+$(INFIN)) * ) */ -real acosh(real x) @safe pure nothrow +real acosh(real x) @safe pure nothrow @nogc { if (x > 1/real.epsilon) return LN2 + log(x); @@ -1082,10 +1215,10 @@ real acosh(real x) @safe pure nothrow } /// ditto -double acosh(double x) @safe pure nothrow { return acosh(cast(real)x); } +double acosh(double x) @safe pure nothrow @nogc { return acosh(cast(real)x); } /// ditto -float acosh(float x) @safe pure nothrow { return acosh(cast(real)x); } +float acosh(float x) @safe pure nothrow @nogc { return acosh(cast(real)x); } unittest @@ -1114,7 +1247,7 @@ unittest * $(SV $(PLUSMN)$(INFIN),$(PLUSMN)$(INFIN)) * ) */ -real asinh(real x) @safe pure nothrow +real asinh(real x) @safe pure nothrow @nogc { return (fabs(x) > 1 / real.epsilon) // beyond this point, x*x + 1 == x*x @@ -1124,10 +1257,10 @@ real asinh(real x) @safe pure nothrow } /// ditto -double asinh(double x) @safe pure nothrow { return asinh(cast(real)x); } +double asinh(double x) @safe pure nothrow @nogc { return asinh(cast(real)x); } /// ditto -float asinh(float x) @safe pure nothrow { return asinh(cast(real)x); } +float asinh(float x) @safe pure nothrow @nogc { return asinh(cast(real)x); } unittest { @@ -1156,17 +1289,17 @@ unittest * $(SV -$(INFIN), -0) * ) */ -real atanh(real x) @safe pure nothrow +real atanh(real x) @safe pure nothrow @nogc { // log( (1+x)/(1-x) ) == log ( 1 + (2*x)/(1-x) ) return 0.5 * log1p( 2 * x / (1 - x) ); } /// ditto -double atanh(double x) @safe pure nothrow { return atanh(cast(real)x); } +double atanh(double x) @safe pure nothrow @nogc { return atanh(cast(real)x); } /// ditto -float atanh(float x) @safe pure nothrow { return atanh(cast(real)x); } +float atanh(float x) @safe pure nothrow @nogc { return atanh(cast(real)x); } unittest @@ -1185,7 +1318,7 @@ unittest * greater than long.max, the result is * indeterminate. */ -long rndtol(real x) @safe pure nothrow; /* intrinsic */ +long rndtol(real x) @nogc @safe pure nothrow; /* intrinsic */ /***************************************** @@ -1206,13 +1339,13 @@ extern (C) real rndtonl(real x); * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no)) * ) */ -float sqrt(float x) @safe pure nothrow; /* intrinsic */ +float sqrt(float x) @nogc @safe pure nothrow; /* intrinsic */ /// ditto -double sqrt(double x) @safe pure nothrow; /* intrinsic */ +double sqrt(double x) @nogc @safe pure nothrow; /* intrinsic */ /// ditto -real sqrt(real x) @safe pure nothrow; /* intrinsic */ +real sqrt(real x) @nogc @safe pure nothrow; /* intrinsic */ unittest { @@ -1222,7 +1355,7 @@ unittest enum ZX82 = sqrt(7.0L); } -creal sqrt(creal z) @safe pure nothrow +creal sqrt(creal z) @nogc @safe pure nothrow { creal c; real x,y,w,r; @@ -1273,7 +1406,7 @@ creal sqrt(creal z) @safe pure nothrow * $(TR $(TD $(NAN)) $(TD $(NAN)) ) * ) */ -real exp(real x) @trusted pure nothrow +real exp(real x) @trusted pure nothrow @nogc { version(D_InlineAsm_X86) { @@ -1342,10 +1475,10 @@ real exp(real x) @trusted pure nothrow } /// ditto -double exp(double x) @safe pure nothrow { return exp(cast(real)x); } +double exp(double x) @safe pure nothrow @nogc { return exp(cast(real)x); } /// ditto -float exp(float x) @safe pure nothrow { return exp(cast(real)x); } +float exp(float x) @safe pure nothrow @nogc { return exp(cast(real)x); } unittest { @@ -1367,7 +1500,7 @@ unittest * $(TR $(TD $(NAN)) $(TD $(NAN)) ) * ) */ -real expm1(real x) @trusted pure nothrow +real expm1(real x) @trusted pure nothrow @nogc { version(D_InlineAsm_X86) { @@ -1598,7 +1731,7 @@ L_largenegative: * $(TR $(TD $(NAN)) $(TD $(NAN)) ) * ) */ -real exp2(real x) @trusted pure nothrow +real exp2(real x) @nogc @trusted pure nothrow { version(D_InlineAsm_X86) { @@ -1837,15 +1970,18 @@ unittest assert(feqrel(exp2(0.5L), SQRT2) >= real.mant_dig -1); assert(exp2(8.0L) == 256.0); assert(exp2(-9.0L)== 1.0L/512.0); - assert( core.stdc.math.exp2f(0.0f) == 1 ); - assert( core.stdc.math.exp2 (0.0) == 1 ); - assert( core.stdc.math.exp2l(0.0L) == 1 ); + version(Win64) {} else // aexp2/exp2f/exp2l not implemented + { + assert( core.stdc.math.exp2f(0.0f) == 1 ); + assert( core.stdc.math.exp2 (0.0) == 1 ); + assert( core.stdc.math.exp2l(0.0L) == 1 ); + } } unittest { FloatingPointControl ctrl; - if(FloatingPointControl.hasExceptionTraps()) + if(FloatingPointControl.hasExceptionTraps) ctrl.disableExceptions(FloatingPointControl.allExceptions); ctrl.rounding = FloatingPointControl.roundToNearest; @@ -1916,7 +2052,7 @@ unittest * almost twice as fast as calculating sin(y) and cos(y) separately, * and is the preferred method when both are required. */ -creal expi(real y) @trusted pure nothrow +creal expi(real y) @trusted pure nothrow @nogc { version(InlineAsm_X86_Any) { @@ -1972,15 +2108,15 @@ unittest * $(TR $(TD $(PLUSMN)$(NAN)) $(TD $(PLUSMN)$(NAN)) $(TD int.min)) * ) */ -real frexp(real value, out int exp) @trusted pure nothrow +real frexp(real value, out int exp) @trusted pure nothrow @nogc { ushort* vu = cast(ushort*)&value; long* vl = cast(long*)&value; int ex; - alias floatTraits!(real) F; + alias F = floatTraits!(real); ex = vu[F.EXPPOS_SHORT] & F.EXPMASK; - static if (real.mant_dig == 64) // real80 + static if (F.realFormat == RealFormat.ieeeExtended) { if (ex) { // If exponent is non-zero @@ -2019,7 +2155,7 @@ real frexp(real value, out int exp) @trusted pure nothrow } return value; } - else static if (real.mant_dig == 113) // quadruple + else static if (F.realFormat == RealFormat.ieeeQuadruple) { if (ex) // If exponent is non-zero { @@ -2062,7 +2198,7 @@ real frexp(real value, out int exp) @trusted pure nothrow } return value; } - else static if (real.mant_dig==53) // real is double + else static if (F.realFormat == RealFormat.ieeeDouble) { if (ex) // If exponent is non-zero { @@ -2102,7 +2238,7 @@ real frexp(real value, out int exp) @trusted pure nothrow } return value; } - else // static if (real.mant_dig==106) // real is doubledouble + else // static if (F.realFormat == RealFormat.ibmExtended) { assert (0, "frexp not implemented"); } @@ -2139,7 +2275,7 @@ unittest } - static if (real.mant_dig == 64) + static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) { static real[3][] extendedvals = [ // x,frexp,exp [0x1.a5f1c2eb3fe4efp+73L, 0x1.A5F1C2EB3FE4EFp-1L, 74], // normal @@ -2189,7 +2325,7 @@ unittest * $(TR $(TD $(NAN)) $(TD FP_ILOGBNAN) $(TD no)) * ) */ -int ilogb(real x) @trusted nothrow +int ilogb(real x) @trusted nothrow @nogc { version (Win64_DMD_InlineAsm) { @@ -2227,8 +2363,8 @@ int ilogb(real x) @trusted nothrow return core.stdc.math.ilogbl(x); } -alias core.stdc.math.FP_ILOGB0 FP_ILOGB0; -alias core.stdc.math.FP_ILOGBNAN FP_ILOGBNAN; +alias FP_ILOGB0 = core.stdc.math.FP_ILOGB0; +alias FP_ILOGBNAN = core.stdc.math.FP_ILOGBNAN; /******************************************* @@ -2236,11 +2372,11 @@ alias core.stdc.math.FP_ILOGBNAN FP_ILOGBNAN; * References: frexp */ -real ldexp(real n, int exp) @safe pure nothrow; /* intrinsic */ +real ldexp(real n, int exp) @nogc @safe pure nothrow; /* intrinsic */ unittest { - static if(real.mant_dig == 64) + static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) { assert(ldexp(1, -16384) == 0x1p-16384L); assert(ldexp(1, -16382) == 0x1p-16382L); @@ -2250,7 +2386,7 @@ unittest assert(x==-16383); assert(ldexp(n, x)==0x1p-16384L); } - else static if(real.mant_dig == 53) + else static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble) { assert(ldexp(1, -1024) == 0x1p-1024L); assert(ldexp(1, -1022) == 0x1p-1022L); @@ -2316,7 +2452,7 @@ unittest * ) */ -real log(real x) @safe pure nothrow +real log(real x) @safe pure nothrow @nogc { version (INLINE_YL2X) return yl2x(x, LN2); @@ -2443,7 +2579,7 @@ unittest * ) */ -real log10(real x) @safe pure nothrow +real log10(real x) @safe pure nothrow @nogc { version (INLINE_YL2X) return yl2x(x, LOG2); @@ -2579,7 +2715,7 @@ unittest * ) */ -real log1p(real x) @safe pure nothrow +real log1p(real x) @safe pure nothrow @nogc { version(INLINE_YL2X) { @@ -2614,7 +2750,7 @@ real log1p(real x) @safe pure nothrow * $(TR $(TD +$(INFIN)) $(TD +$(INFIN)) $(TD no) $(TD no) ) * ) */ -real log2(real x) @safe pure nothrow +real log2(real x) @safe pure nothrow @nogc { version (INLINE_YL2X) return yl2x(x, 1); @@ -2739,7 +2875,7 @@ unittest * $(TR $(TD $(PLUSMN)0.0) $(TD -$(INFIN)) $(TD yes) ) * ) */ -real logb(real x) @trusted nothrow +real logb(real x) @trusted nothrow @nogc { version (Win64_DMD_InlineAsm) { @@ -2770,7 +2906,7 @@ real logb(real x) @trusted nothrow * $(TR $(TD !=$(PLUSMNINF)) $(TD $(PLUSMNINF)) $(TD x) $(TD no)) * ) */ -real fmod(real x, real y) @trusted nothrow +real fmod(real x, real y) @trusted nothrow @nogc { version (Win64) { @@ -2791,7 +2927,7 @@ real fmod(real x, real y) @trusted nothrow * $(TR $(TD $(PLUSMNINF)) $(TD anything) $(TD $(PLUSMN)0.0) $(TD $(PLUSMNINF))) * ) */ -real modf(real x, ref real i) @trusted nothrow +real modf(real x, ref real i) @trusted nothrow @nogc { version (Win64) { @@ -2814,7 +2950,7 @@ real modf(real x, ref real i) @trusted nothrow * $(TR $(TD $(PLUSMN)0.0) $(TD $(PLUSMN)0.0) ) * ) */ -real scalbn(real x, int n) @trusted nothrow +real scalbn(real x, int n) @trusted nothrow @nogc { version(InlineAsm_X86_Any) { // scalbnl is not supported on DMD-Windows, so use asm. @@ -2861,7 +2997,7 @@ unittest * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD $(PLUSMN)$(INFIN)) $(TD no) ) * ) */ -real cbrt(real x) @trusted nothrow +real cbrt(real x) @trusted nothrow @nogc { version (Win64) { @@ -2884,7 +3020,7 @@ real cbrt(real x) @trusted nothrow * $(TR $(TD $(PLUSMN)$(INFIN)) $(TD +$(INFIN)) ) * ) */ -real fabs(real x) @safe pure nothrow; /* intrinsic */ +real fabs(real x) @safe pure nothrow @nogc; /* intrinsic */ /*********************************************************************** @@ -2906,7 +3042,7 @@ real fabs(real x) @safe pure nothrow; /* intrinsic */ * ) */ -real hypot(real x, real y) @safe pure nothrow +real hypot(real x, real y) @safe pure nothrow @nogc { // Scale x and y to avoid underflow and overflow. // If one is huge and the other tiny, return the larger. @@ -2996,7 +3132,7 @@ unittest * Returns the value of x rounded upward to the next integer * (toward positive infinity). */ -real ceil(real x) @trusted pure nothrow +real ceil(real x) @trusted pure nothrow @nogc { version (Win64_DMD_InlineAsm) { @@ -3023,7 +3159,7 @@ real ceil(real x) @trusted pure nothrow if (isNaN(x) || isInfinity(x)) return x; - real y = floor(x); + real y = floorImpl(x); if (y < x) y += 1.0; @@ -3031,6 +3167,34 @@ real ceil(real x) @trusted pure nothrow } } +unittest +{ + assert(ceil(+123.456L) == +124); + assert(ceil(-123.456L) == -123); + assert(ceil(-1.234L) == -1); + assert(ceil(-0.123L) == 0); + assert(ceil(0.0L) == 0); + assert(ceil(+0.123L) == 1); + assert(ceil(+1.234L) == 2); + assert(ceil(real.infinity) == real.infinity); + assert(isNaN(ceil(real.nan))); + assert(isNaN(ceil(real.init))); +} + +// ditto +double ceil(double x) @trusted pure nothrow @nogc +{ + // Special cases. + if (isNaN(x) || isInfinity(x)) + return x; + + double y = floorImpl(x); + if (y < x) + y += 1.0; + + return y; +} + unittest { assert(ceil(+123.456) == +124); @@ -3040,16 +3204,44 @@ unittest assert(ceil(0.0) == 0); assert(ceil(+0.123) == 1); assert(ceil(+1.234) == 2); - assert(ceil(real.infinity) == real.infinity); - assert(isNaN(ceil(real.nan))); - assert(isNaN(ceil(real.init))); + assert(ceil(double.infinity) == double.infinity); + assert(isNaN(ceil(double.nan))); + assert(isNaN(ceil(double.init))); +} + +// ditto +float ceil(float x) @trusted pure nothrow @nogc +{ + // Special cases. + if (isNaN(x) || isInfinity(x)) + return x; + + float y = floorImpl(x); + if (y < x) + y += 1.0; + + return y; +} + +unittest +{ + assert(ceil(+123.456f) == +124); + assert(ceil(-123.456f) == -123); + assert(ceil(-1.234f) == -1); + assert(ceil(-0.123f) == 0); + assert(ceil(0.0f) == 0); + assert(ceil(+0.123f) == 1); + assert(ceil(+1.234f) == 2); + assert(ceil(float.infinity) == float.infinity); + assert(isNaN(ceil(float.nan))); + assert(isNaN(ceil(float.init))); } /************************************** * Returns the value of x rounded downward to the next integer * (toward negative infinity). */ -real floor(real x) @trusted pure nothrow +real floor(real x) @trusted pure nothrow @nogc { version (Win64_DMD_InlineAsm) { @@ -3072,84 +3264,36 @@ real floor(real x) @trusted pure nothrow } else { - // Bit clearing masks. - static immutable ushort[17] BMASK = [ - 0xffff, 0xfffe, 0xfffc, 0xfff8, - 0xfff0, 0xffe0, 0xffc0, 0xff80, - 0xff00, 0xfe00, 0xfc00, 0xf800, - 0xf000, 0xe000, 0xc000, 0x8000, - 0x0000, - ]; - // Special cases. if (isNaN(x) || isInfinity(x) || x == 0.0) return x; - alias floatTraits!(real) F; - auto vu = *cast(ushort[real.sizeof/2]*)(&x); - - // Find the exponent (power of 2) - static if (real.mant_dig == 53) - { - int exp = ((vu[F.EXPPOS_SHORT] >> 4) & 0x7ff) - 0x3ff; - - version (LittleEndian) - int pos = 0; - else - int pos = 3; - } - else static if (real.mant_dig == 64) - { - int exp = (vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff; - - version (LittleEndian) - int pos = 0; - else - int pos = 4; - } - else if (real.mant_dig == 113) - { - int exp = (vu[F.EXPPOS_SHORT] & 0x7fff) - 0x3fff; - - version (LittleEndian) - int pos = 0; - else - int pos = 7; - } - else - static assert(false, "Only 64-bit, 80-bit, and 128-bit reals are supported by floor()"); - - if (exp < 0) - { - if (x < 0.0) - return -1.0; - else - return 0.0; - } - - exp = (real.mant_dig - 1) - exp; - - // Clean out 16 bits at a time. - while (exp >= 16) - { - version (LittleEndian) - vu[pos++] = 0; - else - vu[pos--] = 0; - exp -= 16; - } - - // Clear the remaining bits. - if (exp > 0) - vu[pos] &= BMASK[exp]; + return floorImpl(x); + } +} - real y = *cast(real*)(&vu); +unittest +{ + assert(floor(+123.456L) == +123); + assert(floor(-123.456L) == -124); + assert(floor(-1.234L) == -2); + assert(floor(-0.123L) == -1); + assert(floor(0.0L) == 0); + assert(floor(+0.123L) == 0); + assert(floor(+1.234L) == 1); + assert(floor(real.infinity) == real.infinity); + assert(isNaN(floor(real.nan))); + assert(isNaN(floor(real.init))); +} - if ((x < 0.0) && (x != y)) - y -= 1.0; +// ditto +double floor(double x) @trusted pure nothrow @nogc +{ + // Special cases. + if (isNaN(x) || isInfinity(x) || x == 0.0) + return x; - return y; - } + return floorImpl(x); } unittest @@ -3161,9 +3305,33 @@ unittest assert(floor(0.0) == 0); assert(floor(+0.123) == 0); assert(floor(+1.234) == 1); - assert(floor(real.infinity) == real.infinity); - assert(isNaN(floor(real.nan))); - assert(isNaN(floor(real.init))); + assert(floor(double.infinity) == double.infinity); + assert(isNaN(floor(double.nan))); + assert(isNaN(floor(double.init))); +} + +// ditto +float floor(float x) @trusted pure nothrow @nogc +{ + // Special cases. + if (isNaN(x) || isInfinity(x) || x == 0.0) + return x; + + return floorImpl(x); +} + +unittest +{ + assert(floor(+123.456f) == +123); + assert(floor(-123.456f) == -124); + assert(floor(-1.234f) == -2); + assert(floor(-0.123f) == -1); + assert(floor(0.0f) == 0); + assert(floor(+0.123f) == 0); + assert(floor(+1.234f) == 1); + assert(floor(float.infinity) == float.infinity); + assert(isNaN(floor(float.nan))); + assert(isNaN(floor(float.init))); } /****************************************** @@ -3173,7 +3341,7 @@ unittest * Unlike the rint functions, nearbyint does not raise the * FE_INEXACT exception. */ -real nearbyint(real x) @trusted nothrow +real nearbyint(real x) @trusted nothrow @nogc { version (Win64) { @@ -3191,7 +3359,7 @@ real nearbyint(real x) @trusted nothrow * $(B nearbyint) performs * the same operation, but does not set the FE_INEXACT exception. */ -real rint(real x) @safe pure nothrow; /* intrinsic */ +real rint(real x) @safe pure nothrow @nogc; /* intrinsic */ /*************************************** * Rounds x to the nearest integer value, using the current rounding @@ -3203,7 +3371,7 @@ real rint(real x) @safe pure nothrow; /* intrinsic */ * If using the default rounding mode (ties round to even integers) * lrint(4.5) == 4, lrint(5.5)==6. */ -long lrint(real x) @trusted pure nothrow +long lrint(real x) @trusted pure nothrow @nogc { version(InlineAsm_X86_Any) { @@ -3213,7 +3381,7 @@ long lrint(real x) @trusted pure nothrow { naked; fld real ptr [RCX]; - fistp 8[RSP]; + fistp qword ptr 8[RSP]; mov RAX,8[RSP]; ret; } @@ -3231,7 +3399,8 @@ long lrint(real x) @trusted pure nothrow } else { - static if (real.mant_dig == 53) + alias F = floatTraits!(real); + static if (F.realFormat == RealFormat.ieeeDouble) { long result; @@ -3281,9 +3450,8 @@ long lrint(real x) @trusted pure nothrow return sign ? -result : result; } - else static if (real.mant_dig == 64) + else static if (F.realFormat == RealFormat.ieeeExtended) { - alias floatTraits!(real) F; long result; // Rounding limit when casting from real(80-bit) to ulong. @@ -3356,7 +3524,7 @@ unittest * If the fractional part of x is exactly 0.5, the return value is rounded to * the even integer. */ -real round(real x) @trusted nothrow +real round(real x) @trusted nothrow @nogc { version (Win64) { @@ -3376,7 +3544,7 @@ real round(real x) @trusted nothrow * If the fractional part of x is exactly 0.5, the return value is rounded * away from zero. */ -long lround(real x) @trusted nothrow +long lround(real x) @trusted nothrow @nogc { version (Posix) return core.stdc.math.llroundl(x); @@ -3399,7 +3567,7 @@ version(Posix) * * This is also known as "chop" rounding. */ -real trunc(real x) @trusted nothrow +real trunc(real x) @trusted nothrow @nogc { version (Win64_DMD_InlineAsm) { @@ -3446,7 +3614,7 @@ real trunc(real x) @trusted nothrow * * Note: remquo not supported on windows */ -real remainder(real x, real y) @trusted nothrow +real remainder(real x, real y) @trusted nothrow @nogc { version (Win64) { @@ -3457,7 +3625,7 @@ real remainder(real x, real y) @trusted nothrow return core.stdc.math.remainderl(x, y); } -real remquo(real x, real y, out int n) @trusted nothrow /// ditto +real remquo(real x, real y, out int n) @trusted nothrow @nogc /// ditto { version (Posix) return core.stdc.math.remquol(x, y, &n); @@ -3677,7 +3845,7 @@ private: public: version (IeeeFlagsSupport) { - /// The result cannot be represented exactly, so rounding occured. + /// The result cannot be represented exactly, so rounding occurred. /// (example: x = sin(0.1); ) @property bool inexact() { return (flags & INEXACT_MASK) != 0; } @@ -3764,7 +3932,7 @@ assert(rint(1.1) == 1); */ struct FloatingPointControl { - alias uint RoundingMode; + alias RoundingMode = uint; /** IEEE rounding modes. * The default mode is roundToNearest. @@ -3849,7 +4017,7 @@ private: public: /// Returns true if the current FPU supports exception trapping - @property static bool hasExceptionTraps() @safe nothrow + @property static bool hasExceptionTraps() @safe nothrow @nogc { version(X86) return true; @@ -3870,7 +4038,7 @@ public: } /// Enable (unmask) specific hardware exceptions. Multiple exceptions may be ORed together. - void enableExceptions(uint exceptions) + void enableExceptions(uint exceptions) @nogc { assert(hasExceptionTraps); initialize(); @@ -3881,7 +4049,7 @@ public: } /// Disable (mask) specific hardware exceptions. Multiple exceptions may be ORed together. - void disableExceptions(uint exceptions) + void disableExceptions(uint exceptions) @nogc { assert(hasExceptionTraps); initialize(); @@ -3892,14 +4060,14 @@ public: } //// Change the floating-point hardware rounding mode - @property void rounding(RoundingMode newMode) + @property void rounding(RoundingMode newMode) @nogc { initialize(); setControlState((getControlState() & ~ROUNDING_MASK) | (newMode & ROUNDING_MASK)); } /// Return the exceptions which are currently enabled (unmasked) - @property static uint enabledExceptions() + @property static uint enabledExceptions() @nogc { assert(hasExceptionTraps); version(ARM) @@ -3909,13 +4077,13 @@ public: } /// Return the currently active rounding mode - @property static RoundingMode rounding() + @property static RoundingMode rounding() @nogc { return cast(RoundingMode)(getControlState() & ROUNDING_MASK); } /// Clear all pending exceptions, then restore the original exception state and rounding mode. - ~this() + ~this() @nogc { clearExceptions(); if (initialized) @@ -3936,7 +4104,7 @@ private: alias ControlState = ushort; } - void initialize() + void initialize() @nogc { // BUG: This works around the absence of this() constructors. if (initialized) return; @@ -3946,7 +4114,7 @@ private: } // Clear all pending exceptions - static void clearExceptions() + static void clearExceptions() @nogc { version (InlineAsm_X86_Any) { @@ -3982,7 +4150,7 @@ private: } // Read from the control register - static ControlState getControlState() @trusted nothrow + static ushort getControlState() @trusted nothrow @nogc { version (D_InlineAsm_X86) { @@ -4035,7 +4203,7 @@ private: } // Set the control register - static void setControlState(ControlState newState) @trusted nothrow + static void setControlState(ushort newState) @trusted nothrow @nogc { version (InlineAsm_X86_Any) { @@ -4097,7 +4265,7 @@ unittest { assert(FloatingPointControl.rounding == FloatingPointControl.roundToNearest); - if(FloatingPointControl.hasExceptionTraps()) + if(FloatingPointControl.hasExceptionTraps) assert(FloatingPointControl.enabledExceptions == 0); } @@ -4133,64 +4301,120 @@ unittest * Returns !=0 if e is a NaN. */ -bool isNaN(real x) @trusted pure nothrow +bool isNaN(X)(X x) @nogc @trusted pure nothrow + if (isFloatingPoint!(X)) { - alias floatTraits!(real) F; - static if (real.mant_dig == 53) // double + alias F = floatTraits!(X); + static if (F.realFormat == RealFormat.ieeeSingle) + { + uint* p = cast(uint *)&x; + return ((*p & 0x7F80_0000) == 0x7F80_0000) + && *p & 0x007F_FFFF; // not infinity + } + else static if (F.realFormat == RealFormat.ieeeDouble) { ulong* p = cast(ulong *)&x; return ((*p & 0x7FF0_0000_0000_0000) == 0x7FF0_0000_0000_0000) - && *p & 0x000F_FFFF_FFFF_FFFF; + && *p & 0x000F_FFFF_FFFF_FFFF; // not infinity } - else static if (real.mant_dig == 64) // real80 + else static if (F.realFormat == RealFormat.ieeeExtended) { ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; ulong* ps = cast(ulong *)&x; return e == F.EXPMASK && - *ps & 0x7FFF_FFFF_FFFF_FFFF; // not infinity + *ps & 0x7FFF_FFFF_FFFF_FFFF; // not infinity } - else static if (real.mant_dig == 113) // quadruple + else static if (F.realFormat == RealFormat.ieeeQuadruple) { ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; ulong* ps = cast(ulong *)&x; return e == F.EXPMASK && - (ps[MANTISSA_LSB] | (ps[MANTISSA_MSB]& 0x0000_FFFF_FFFF_FFFF))!=0; + (ps[MANTISSA_LSB] | (ps[MANTISSA_MSB]& 0x0000_FFFF_FFFF_FFFF)) != 0; } else { - return x!=x; + return x != x; } } +deprecated("isNaN is not defined for integer types") +bool isNaN(X)(X x) @nogc @trusted pure nothrow + if (isIntegral!(X)) +{ + return isNaN(cast(float)x); +} unittest { - assert(isNaN(float.nan)); - assert(isNaN(-double.nan)); - assert(isNaN(real.nan)); + import std.typetuple; - assert(!isNaN(53.6)); - assert(!isNaN(float.infinity)); + foreach(T; TypeTuple!(float, double, real)) + { + // CTFE-able tests + assert(isNaN(T.init)); + assert(isNaN(-T.init)); + assert(isNaN(T.nan)); + assert(isNaN(-T.nan)); + assert(!isNaN(T.infinity)); + assert(!isNaN(-T.infinity)); + assert(!isNaN(cast(T)53.6)); + assert(!isNaN(cast(T)-53.6)); + + // Runtime tests + shared T f; + f = T.init; + assert(isNaN(f)); + assert(isNaN(-f)); + f = T.nan; + assert(isNaN(f)); + assert(isNaN(-f)); + f = T.infinity; + assert(!isNaN(f)); + assert(!isNaN(-f)); + f = cast(T)53.6; + assert(!isNaN(f)); + assert(!isNaN(-f)); + } } /********************************* * Returns !=0 if e is finite (not infinite or $(NAN)). */ -int isFinite(real e) @trusted pure nothrow +int isFinite(X)(X e) @trusted pure nothrow @nogc { - alias floatTraits!(real) F; + alias F = floatTraits!(X); ushort* pe = cast(ushort *)&e; return (pe[F.EXPPOS_SHORT] & F.EXPMASK) != F.EXPMASK; } unittest { + assert(isFinite(1.23f)); + assert(isFinite(float.max)); + assert(isFinite(float.min_normal)); + assert(!isFinite(float.nan)); + assert(!isFinite(float.infinity)); + assert(isFinite(1.23)); + assert(isFinite(double.max)); + assert(isFinite(double.min_normal)); + assert(!isFinite(double.nan)); assert(!isFinite(double.infinity)); - assert(!isFinite(float.nan)); + + assert(isFinite(1.23L)); + assert(isFinite(real.max)); + assert(isFinite(real.min_normal)); + assert(!isFinite(real.nan)); + assert(!isFinite(real.infinity)); } +deprecated("isFinite is not defined for integer types") +int isFinite(X)(X x) @trusted pure nothrow @nogc + if (isIntegral!(X)) +{ + return isFinite(cast(float)x); +} /********************************* * Returns !=0 if x is normalized (not zero, subnormal, infinite, or $(NAN)). @@ -4200,11 +4424,10 @@ unittest * be converted to normal reals. */ -int isNormal(X)(X x) @trusted pure nothrow +int isNormal(X)(X x) @trusted pure nothrow @nogc { - alias floatTraits!(X) F; - - static if(real.mant_dig == 106) // doubledouble + alias F = floatTraits!(X); + static if (F.realFormat == RealFormat.ibmExtended) { // doubledouble is normal if the least significant part is normal. return isNormal((cast(double*)&x)[MANTISSA_LSB]); @@ -4212,7 +4435,7 @@ int isNormal(X)(X x) @trusted pure nothrow else { ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; - return (e != F.EXPMASK && e!=0); + return (e != F.EXPMASK && e != 0); } } @@ -4245,123 +4468,171 @@ unittest * be converted to normal reals. */ -int isSubnormal(float f) @trusted pure nothrow -{ - uint *p = cast(uint *)&f; - return (*p & 0x7F80_0000) == 0 && *p & 0x007F_FFFF; -} - -unittest -{ - float f = 3.0; - - for (f = 1.0; !isSubnormal(f); f /= 2) - assert(f != 0); -} - -/// ditto - -int isSubnormal(double d) @trusted pure nothrow -{ - uint *p = cast(uint *)&d; - return (p[MANTISSA_MSB] & 0x7FF0_0000) == 0 - && (p[MANTISSA_LSB] || p[MANTISSA_MSB] & 0x000F_FFFF); -} - -unittest -{ - double f; - - for (f = 1; !isSubnormal(f); f /= 2) - assert(f != 0); -} - -/// ditto - -int isSubnormal(real x) @trusted pure nothrow +int isSubnormal(X)(X x) @trusted pure nothrow @nogc { - alias floatTraits!(real) F; - static if (real.mant_dig == 53) + alias F = floatTraits!(X); + static if (F.realFormat == RealFormat.ieeeSingle) + { + uint *p = cast(uint *)&x; + return (*p & F.EXPMASK_INT) == 0 && *p & F.MANTISSAMASK_INT; + } + else static if (F.realFormat == RealFormat.ieeeDouble) { - // double - return isSubnormal(cast(double)x); + uint *p = cast(uint *)&x; + return (p[MANTISSA_MSB] & F.EXPMASK_INT) == 0 + && (p[MANTISSA_LSB] || p[MANTISSA_MSB] & F.MANTISSAMASK_INT); } - else static if (real.mant_dig == 113) + else static if (F.realFormat == RealFormat.ieeeQuadruple) { - // quadruple ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; long* ps = cast(long *)&x; return (e == 0 && - (((ps[MANTISSA_LSB]|(ps[MANTISSA_MSB]& 0x0000_FFFF_FFFF_FFFF))) !=0)); + (((ps[MANTISSA_LSB]|(ps[MANTISSA_MSB]& 0x0000_FFFF_FFFF_FFFF))) != 0)); } - else static if (real.mant_dig==64) + else static if (F.realFormat == RealFormat.ieeeExtended) { - // real80 ushort* pe = cast(ushort *)&x; long* ps = cast(long *)&x; return (pe[F.EXPPOS_SHORT] & F.EXPMASK) == 0 && *ps > 0; } - else + else static if (F.realFormat == RealFormat.ibmExtended) { - // double double return isSubnormal((cast(double*)&x)[MANTISSA_MSB]); } + else + { + static assert(false, "Not implemented for this architecture"); + } } unittest { - real f; + import std.typetuple; + + foreach (T; TypeTuple!(float, double, real)) + { + T f; + for (f = 1.0; !isSubnormal(f); f /= 2) + assert(f != 0); + } +} - for (f = 1; !isSubnormal(f); f /= 2) - assert(f != 0); +deprecated("isSubnormal is not defined for integer types") +int isSubnormal(X)(X x) @trusted pure nothrow @nogc + if (isIntegral!X) +{ + return isSubnormal(cast(double)x); } /********************************* * Return !=0 if e is $(PLUSMN)$(INFIN). */ -bool isInfinity(real x) @trusted pure nothrow +bool isInfinity(X)(X x) @nogc @trusted pure nothrow + if (isFloatingPoint!(X)) { - alias floatTraits!(real) F; - static if (real.mant_dig == 53) + alias F = floatTraits!(X); + static if (F.realFormat == RealFormat.ieeeSingle) + { + return ((*cast(uint *)&x) & 0x7FFF_FFFF) == 0x7F80_0000; + } + else static if (F.realFormat == RealFormat.ieeeDouble) { - // double return ((*cast(ulong *)&x) & 0x7FFF_FFFF_FFFF_FFFF) - == 0x7FF0000000000000; + == 0x7FF0_0000_0000_0000; + } + else static if (F.realFormat == RealFormat.ieeeExtended) + { + ushort e = cast(ushort)(F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]); + ulong* ps = cast(ulong *)&x; + + // On Motorola 68K, infinity can have hidden bit = 1 or 0. On x86, it is always 1. + return e == F.EXPMASK && (*ps & 0x7FFF_FFFF_FFFF_FFFF) == 0; } - else static if(real.mant_dig == 106) + else static if (F.realFormat == RealFormat.ibmExtended) { - //doubledouble return (((cast(ulong *)&x)[MANTISSA_MSB]) & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FF8_0000_0000_0000; } - else static if (real.mant_dig == 113) + else static if (F.realFormat == RealFormat.ieeeQuadruple) { - // quadruple long* ps = cast(long *)&x; return (ps[MANTISSA_LSB] == 0) && (ps[MANTISSA_MSB] & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_0000_0000_0000; } else { - // real80 - ushort e = cast(ushort)(F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]); - ulong* ps = cast(ulong *)&x; - - // On Motorola 68K, infinity can have hidden bit = 1 or 0. On x86, it is always 1. - return e == F.EXPMASK && (*ps & 0x7FFF_FFFF_FFFF_FFFF) == 0; + return (x - 1) == x; } } unittest { - assert(isInfinity(float.infinity)); + // CTFE-able tests + assert(!isInfinity(float.init)); + assert(!isInfinity(-float.init)); assert(!isInfinity(float.nan)); - assert(isInfinity(double.infinity)); - assert(isInfinity(-real.infinity)); + assert(!isInfinity(-float.nan)); + assert(isInfinity(float.infinity)); + assert(isInfinity(-float.infinity)); + assert(isInfinity(-1.0f / 0.0f)); + assert(!isInfinity(double.init)); + assert(!isInfinity(-double.init)); + assert(!isInfinity(double.nan)); + assert(!isInfinity(-double.nan)); + assert(isInfinity(double.infinity)); + assert(isInfinity(-double.infinity)); assert(isInfinity(-1.0 / 0.0)); + + assert(!isInfinity(real.init)); + assert(!isInfinity(-real.init)); + assert(!isInfinity(real.nan)); + assert(!isInfinity(-real.nan)); + assert(isInfinity(real.infinity)); + assert(isInfinity(-real.infinity)); + assert(isInfinity(-1.0L / 0.0L)); + + // Runtime tests + shared float f; + f = float.init; + assert(!isInfinity(f)); + assert(!isInfinity(-f)); + f = float.nan; + assert(!isInfinity(f)); + assert(!isInfinity(-f)); + f = float.infinity; + assert(isInfinity(f)); + assert(isInfinity(-f)); + f = (-1.0f / 0.0f); + assert(isInfinity(f)); + + shared double d; + d = double.init; + assert(!isInfinity(d)); + assert(!isInfinity(-d)); + d = double.nan; + assert(!isInfinity(d)); + assert(!isInfinity(-d)); + d = double.infinity; + assert(isInfinity(d)); + assert(isInfinity(-d)); + d = (-1.0 / 0.0); + assert(isInfinity(d)); + + shared real e; + e = real.init; + assert(!isInfinity(e)); + assert(!isInfinity(-e)); + e = real.nan; + assert(!isInfinity(e)); + assert(!isInfinity(-e)); + e = real.infinity; + assert(isInfinity(e)); + assert(isInfinity(-e)); + e = (-1.0L / 0.0L); + assert(isInfinity(e)); } /********************************* @@ -4371,24 +4642,23 @@ unittest * and two $(NAN)s are identical if they have the same 'payload'. */ -bool isIdentical(real x, real y) @trusted pure nothrow +bool isIdentical(real x, real y) @trusted pure nothrow @nogc { // We're doing a bitwise comparison so the endianness is irrelevant. long* pxs = cast(long *)&x; long* pys = cast(long *)&y; - static if (real.mant_dig == 53) + alias F = floatTraits!(real); + static if (F.realFormat == RealFormat.ieeeDouble) { - //double return pxs[0] == pys[0]; } - else static if (real.mant_dig == 113 || real.mant_dig==106) + else static if (F.realFormat == RealFormat.ieeeQuadruple + || F.realFormat == RealFormat.ibmExtended) { - // quadruple or doubledouble return pxs[0] == pys[0] && pxs[1] == pys[1]; } else { - // real80 ushort* pxe = cast(ushort *)&x; ushort* pye = cast(ushort *)&y; return pxe[4] == pye[4] && pxs[0] == pys[0]; @@ -4399,9 +4669,10 @@ bool isIdentical(real x, real y) @trusted pure nothrow * Return 1 if sign bit of e is set, 0 if not. */ -int signbit(real x) @trusted pure nothrow +int signbit(X)(X x) @nogc @trusted pure nothrow { - return ((cast(ubyte *)&x)[floatTraits!(real).SIGNPOS_BYTE] & 0x80) != 0; + alias F = floatTraits!(X); + return ((cast(ubyte *)&x)[F.SIGNPOS_BYTE] & 0x80) != 0; } unittest @@ -4409,54 +4680,112 @@ unittest debug (math) printf("math.signbit.unittest\n"); assert(!signbit(float.nan)); assert(signbit(-float.nan)); + assert(!signbit(168.1234f)); + assert(signbit(-168.1234f)); + assert(!signbit(0.0f)); + assert(signbit(-0.0f)); + assert(signbit(-float.max)); + assert(!signbit(float.max)); + + assert(!signbit(double.nan)); + assert(signbit(-double.nan)); assert(!signbit(168.1234)); assert(signbit(-168.1234)); assert(!signbit(0.0)); assert(signbit(-0.0)); assert(signbit(-double.max)); assert(!signbit(double.max)); + + assert(!signbit(real.nan)); + assert(signbit(-real.nan)); + assert(!signbit(168.1234L)); + assert(signbit(-168.1234L)); + assert(!signbit(0.0L)); + assert(signbit(-0.0L)); + assert(signbit(-real.max)); + assert(!signbit(real.max)); +} + + +deprecated("signbit is not defined for integer types") +int signbit(X)(X x) @nogc @trusted pure nothrow + if (isIntegral!X) +{ + return signbit(cast(float)x); } /********************************* * Return a value composed of to with from's sign bit. */ -real copysign(real to, real from) @trusted pure nothrow +R copysign(R, X)(R to, X from) @trusted pure nothrow @nogc + if (isFloatingPoint!(R) && isFloatingPoint!(X)) { ubyte* pto = cast(ubyte *)&to; const ubyte* pfrom = cast(ubyte *)&from; - alias floatTraits!(real) F; - pto[F.SIGNPOS_BYTE] &= 0x7F; - pto[F.SIGNPOS_BYTE] |= pfrom[F.SIGNPOS_BYTE] & 0x80; + alias T = floatTraits!(R); + alias F = floatTraits!(X); + pto[T.SIGNPOS_BYTE] &= 0x7F; + pto[T.SIGNPOS_BYTE] |= pfrom[F.SIGNPOS_BYTE] & 0x80; return to; } +// ditto +R copysign(R, X)(X to, R from) @trusted pure nothrow @nogc + if (isIntegral!(X) && isFloatingPoint!(R)) +{ + return copysign(cast(R)to, from); +} + unittest { - real e; + import std.typetuple; - e = copysign(21, 23.8); - assert(e == 21); + foreach (X; TypeTuple!(float, double, real, int, long)) + { + foreach (Y; TypeTuple!(float, double, real)) + { + X x = 21; + Y y = 23.8; + Y e = void; + + e = copysign(x, y); + assert(e == 21.0); + + e = copysign(-x, y); + assert(e == 21.0); - e = copysign(-21, 23.8); - assert(e == 21); + e = copysign(x, -y); + assert(e == -21.0); - e = copysign(21, -23.8); - assert(e == -21); + e = copysign(-x, -y); + assert(e == -21.0); - e = copysign(-21, -23.8); - assert(e == -21); + static if (isFloatingPoint!X) + { + e = copysign(X.nan, y); + assert(isNaN(e) && !signbit(e)); + + e = copysign(X.nan, -y); + assert(isNaN(e) && signbit(e)); + } + } + } +} - e = copysign(real.nan, -23.8); - assert(isNaN(e) && signbit(e)); +deprecated("copysign : from can't be of integer type") +R copysign(R, X)(X to, R from) @trusted pure nothrow @nogc + if (isIntegral!R) +{ + return copysign(to, cast(float)from); } /********************************* Returns $(D -1) if $(D x < 0), $(D x) if $(D x == 0), $(D 1) if $(D x > 0), and $(NAN) if x==$(NAN). */ -F sgn(F)(F x) @safe pure nothrow +F sgn(F)(F x) @safe pure nothrow @nogc { // @@@TODO@@@: make this faster return x > 0 ? 1 : x < 0 ? -1 : x; @@ -4485,12 +4814,13 @@ unittest * For doubles, it is 0x3_FFFF_FFFF_FFFF. * For 80-bit or 128-bit reals, it is 0x3FFF_FFFF_FFFF_FFFF. */ -real NaN(ulong payload) @trusted pure nothrow +real NaN(ulong payload) @trusted pure nothrow @nogc { - static if (real.mant_dig == 64) + alias F = floatTraits!(real); + static if (F.realFormat == RealFormat.ieeeExtended) { - //real80 (in x86 real format, the implied bit is actually - //not implied but a real bit which is stored in the real) + // real80 (in x86 real format, the implied bit is actually + // not implied but a real bit which is stored in the real) ulong v = 3; // implied bit = 1, quiet bit = 1 } else @@ -4515,11 +4845,9 @@ real NaN(ulong payload) @trusted pure nothrow a -= w; a >>=29; - static if (real.mant_dig == 53) + static if (F.realFormat == RealFormat.ieeeDouble) { - // double - - v |=0x7FF0_0000_0000_0000; + v |= 0x7FF0_0000_0000_0000; real x; * cast(ulong *)(&x) = v; return x; @@ -4532,12 +4860,9 @@ real NaN(ulong payload) @trusted pure nothrow real x = real.nan; // Extended real bits - - static if (real.mant_dig == 113) + static if (F.realFormat == RealFormat.ieeeQuadruple) { - //quadruple - - v<<=1; // there's no implicit bit + v <<= 1; // there's no implicit bit version(LittleEndian) { @@ -4550,9 +4875,7 @@ real NaN(ulong payload) @trusted pure nothrow } else { - // real80 - - * cast(ulong *)(&x) = v; + *cast(ulong *)(&x) = v; } return x; } @@ -4560,7 +4883,7 @@ real NaN(ulong payload) @trusted pure nothrow unittest { - static if (real.mant_dig == 53) + static if (floatTraits!(real).realFormat == RealFormat.ieeeDouble) { auto x = NaN(1); auto xl = *cast(ulong*)&x; @@ -4579,10 +4902,11 @@ unittest * For doubles, it is 0x3_FFFF_FFFF_FFFF. * For 80-bit or 128-bit reals, it is 0x3FFF_FFFF_FFFF_FFFF. */ -ulong getNaNPayload(real x) @trusted pure nothrow +ulong getNaNPayload(real x) @trusted pure nothrow @nogc { // assert(isNaN(x)); - static if (real.mant_dig == 53) + alias F = floatTraits!(real); + static if (F.realFormat == RealFormat.ieeeDouble) { ulong m = *cast(ulong *)(&x); // Make it look like an 80-bit significand. @@ -4590,9 +4914,8 @@ ulong getNaNPayload(real x) @trusted pure nothrow m &= 0x0007_FFFF_FFFF_FFFF; m <<= 10; } - else static if (real.mant_dig==113) + else static if (F.realFormat == RealFormat.ieeeQuadruple) { - // quadruple version(LittleEndian) { ulong m = *cast(ulong*)(6+cast(ubyte*)(&x)); @@ -4624,7 +4947,8 @@ debug(UnitTest) unittest { real nan4 = NaN(0x789_ABCD_EF12_3456); - static if (real.mant_dig == 64 || real.mant_dig == 113) + static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended + || floatTraits!(real).realFormat == RealFormat.ieeeQuadruple) { assert (getNaNPayload(nan4) == 0x789_ABCD_EF12_3456); } @@ -4660,19 +4984,15 @@ debug(UnitTest) * $(SV $(NAN), $(NAN) ) * ) */ -real nextUp(real x) @trusted pure nothrow +real nextUp(real x) @trusted pure nothrow @nogc { - alias floatTraits!(real) F; - static if (real.mant_dig == 53) + alias F = floatTraits!(real); + static if (F.realFormat == RealFormat.ieeeDouble) { - // double - return nextUp(cast(double)x); } - else static if (real.mant_dig == 113) + else static if (F.realFormat == RealFormat.ieeeQuadruple) { - // quadruple - ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; if (e == F.EXPMASK) { @@ -4696,18 +5016,18 @@ real nextUp(real x) @trusted pure nothrow return x; } --*ps; - if (ps[MANTISSA_LSB]==0) --ps[MANTISSA_MSB]; + if (ps[MANTISSA_LSB] == 0) --ps[MANTISSA_MSB]; } else { // Positive number ++ps[MANTISSA_LSB]; - if (ps[MANTISSA_LSB]==0) ++ps[MANTISSA_MSB]; + if (ps[MANTISSA_LSB] == 0) ++ps[MANTISSA_MSB]; } return x; } - else static if(real.mant_dig==64) // real80 + else static if (F.realFormat == RealFormat.ieeeExtended) { // For 80-bit reals, the "implied bit" is a nuisance... ushort *pe = cast(ushort *)&x; @@ -4757,14 +5077,14 @@ real nextUp(real x) @trusted pure nothrow } return x; } - else // static if (real.mant_dig==106) // real is doubledouble + else // static if (F.realFormat == RealFormat.ibmExtended) { assert (0, "nextUp not implemented"); } } /** ditto */ -double nextUp(double x) @trusted pure nothrow +double nextUp(double x) @trusted pure nothrow @nogc { ulong *ps = cast(ulong *)&x; @@ -4791,7 +5111,7 @@ double nextUp(double x) @trusted pure nothrow } /** ditto */ -float nextUp(float x) @trusted pure nothrow +float nextUp(float x) @trusted pure nothrow @nogc { uint *ps = cast(uint *)&x; @@ -4835,19 +5155,19 @@ float nextUp(float x) @trusted pure nothrow * $(SV $(NAN), $(NAN) ) * ) */ -real nextDown(real x) @safe pure nothrow +real nextDown(real x) @safe pure nothrow @nogc { return -nextUp(-x); } /** ditto */ -double nextDown(double x) @safe pure nothrow +double nextDown(double x) @safe pure nothrow @nogc { return -nextUp(-x); } /** ditto */ -float nextDown(float x) @safe pure nothrow +float nextDown(float x) @safe pure nothrow @nogc { return -nextUp(-x); } @@ -4859,7 +5179,7 @@ unittest unittest { - static if (real.mant_dig == 64) + static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) { // Tests for 80-bit reals @@ -4940,9 +5260,9 @@ unittest * exceptions will be raised if the function value is subnormal, and x is * not equal to y. */ -T nextafter(T)(T x, T y) @safe pure nothrow +T nextafter(T)(T x, T y) @safe pure nothrow @nogc { - if (x==y) return y; + if (x == y) return y; return ((y>x) ? nextUp(x) : nextDown(x)); } @@ -4972,17 +5292,17 @@ unittest * $(TR $(TD x $(LT)= y) $(TD +0.0)) * ) */ -real fdim(real x, real y) @safe pure nothrow { return (x > y) ? x - y : +0.0; } +real fdim(real x, real y) @safe pure nothrow @nogc { return (x > y) ? x - y : +0.0; } /**************************************** * Returns the larger of x and y. */ -real fmax(real x, real y) @safe pure nothrow { return x > y ? x : y; } +real fmax(real x, real y) @safe pure nothrow @nogc { return x > y ? x : y; } /**************************************** * Returns the smaller of x and y. */ -real fmin(real x, real y) @safe pure nothrow { return x < y ? x : y; } +real fmin(real x, real y) @safe pure nothrow @nogc { return x < y ? x : y; } /************************************** * Returns (x * y) + z, rounding only once according to the @@ -4990,12 +5310,12 @@ real fmin(real x, real y) @safe pure nothrow { return x < y ? x : y; } * * BUGS: Not currently implemented - rounds twice. */ -real fma(real x, real y, real z) @safe pure nothrow { return (x * y) + z; } +real fma(real x, real y, real z) @safe pure nothrow @nogc { return (x * y) + z; } /******************************************************************* * Compute the value of x $(SUP n), where n is an integer */ -Unqual!F pow(F, G)(F x, G n) @trusted pure nothrow +Unqual!F pow(F, G)(F x, G n) @nogc @trusted pure nothrow if (isFloatingPoint!(F) && isIntegral!(G)) { real p = 1.0, v = void; @@ -5098,8 +5418,8 @@ unittest * regardless of the value of x. */ -typeof(Unqual!(F).init * Unqual!(G).init) pow(F, G)(F x, G n) @trusted pure nothrow -if (isIntegral!(F) && isIntegral!(G)) +typeof(Unqual!(F).init * Unqual!(G).init) pow(F, G)(F x, G n) @nogc @trusted pure nothrow + if (isIntegral!(F) && isIntegral!(G)) { if (n<0) return x/0; // Only support positive powers typeof(return) p, v = void; @@ -5153,7 +5473,7 @@ unittest } /**Computes integer to floating point powers.*/ -real pow(I, F)(I x, F y) @trusted pure nothrow +real pow(I, F)(I x, F y) @nogc @trusted pure nothrow if(isIntegral!I && isFloatingPoint!F) { return pow(cast(real) x, cast(Unqual!F) y); @@ -5202,12 +5522,12 @@ real pow(I, F)(I x, F y) @trusted pure nothrow * ) */ -Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @trusted pure nothrow +Unqual!(Largest!(F, G)) pow(F, G)(F x, G y) @nogc @trusted pure nothrow if (isFloatingPoint!(F) && isFloatingPoint!(G)) { - alias typeof(return) Float; + alias Float = typeof(return); - static real impl(real x, real y) pure nothrow + static real impl(real x, real y) @nogc pure nothrow { // Special cases. if (isNaN(y)) @@ -5458,12 +5778,13 @@ unittest * $(TR $(TD any) $(TD $(NAN)) $(TD 0)) * ) */ -int feqrel(X)(X x, X y) @trusted pure nothrow +int feqrel(X)(X x, X y) @trusted pure nothrow @nogc if (isFloatingPoint!(X)) { /* Public Domain. Author: Don Clugston, 18 Aug 2005. */ - static if (X.mant_dig == 106) // doubledouble + alias F = floatTraits!(X); + static if (F.realFormat == RealFormat.ibmExtended) { if (cast(double*)(&x)[MANTISSA_MSB] == cast(double*)(&y)[MANTISSA_MSB]) { @@ -5479,8 +5800,10 @@ int feqrel(X)(X x, X y) @trusted pure nothrow } else { - static assert( X.mant_dig == 64 || X.mant_dig == 113 - || X.mant_dig == double.mant_dig || X.mant_dig == float.mant_dig); + static assert (F.realFormat == RealFormat.ieeeSingle + || F.realFormat == RealFormat.ieeeDouble + || F.realFormat == RealFormat.ieeeExtended + || F.realFormat == RealFormat.ieeeQuadruple); if (x == y) return X.mant_dig; // ensure diff!=0, cope with INF. @@ -5491,7 +5814,6 @@ int feqrel(X)(X x, X y) @trusted pure nothrow ushort *pb = cast(ushort *)(&y); ushort *pd = cast(ushort *)(&diff); - alias floatTraits!(X) F; // The difference in abs(exponent) between x or y and abs(x-y) // is equal to the number of significand bits of x which are @@ -5503,20 +5825,21 @@ int feqrel(X)(X x, X y) @trusted pure nothrow // always 1 lower than we want, except that if bitsdiff==0, // they could have 0 or 1 bits in common. - static if (X.mant_dig == 64 || X.mant_dig == 113) - { // real80 or quadruple + static if (F.realFormat == RealFormat.ieeeExtended + || F.realFormat == RealFormat.ieeeQuadruple) + { int bitsdiff = ( ((pa[F.EXPPOS_SHORT] & F.EXPMASK) + (pb[F.EXPPOS_SHORT] & F.EXPMASK) - 1) >> 1) - pd[F.EXPPOS_SHORT]; } - else static if (X.mant_dig == double.mant_dig) - { // double + else static if (F.realFormat == RealFormat.ieeeDouble) + { int bitsdiff = (( ((pa[F.EXPPOS_SHORT]&0x7FF0) + (pb[F.EXPPOS_SHORT]&0x7FF0)-0x10)>>1) - (pd[F.EXPPOS_SHORT]&0x7FF0))>>4; } - else static if (X.mant_dig == float.mant_dig) - { // float + else static if (F.realFormat == RealFormat.ieeeSingle) + { int bitsdiff = (( ((pa[F.EXPPOS_SHORT]&0x7F80) + (pb[F.EXPPOS_SHORT]&0x7F80)-0x80)>>1) - (pd[F.EXPPOS_SHORT]&0x7F80))>>7; @@ -5534,11 +5857,13 @@ int feqrel(X)(X x, X y) @trusted pure nothrow return bitsdiff + 1; // add the 1 we subtracted before // Avoid out-by-1 errors when factor is almost 2. - static if (X.mant_dig == 64 || X.mant_dig == 113) - { // real80 or quadruple + static if (F.realFormat == RealFormat.ieeeExtended + || F.realFormat == RealFormat.ieeeQuadruple) + { return (bitsdiff == 0) ? (pa[F.EXPPOS_SHORT] == pb[F.EXPPOS_SHORT]) : 0; } - else static if (X.mant_dig == double.mant_dig || X.mant_dig == float.mant_dig) + else static if (F.realFormat == RealFormat.ieeeDouble + || F.realFormat == RealFormat.ieeeSingle) { if (bitsdiff == 0 && !((pa[F.EXPPOS_SHORT] ^ pb[F.EXPPOS_SHORT]) & F.EXPMASK)) @@ -5595,7 +5920,7 @@ unittest } assert(feqrel(7.1824L, 7.1824L) == real.mant_dig); - static if(real.mant_dig == 64) + static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) { assert(feqrel(real.min_normal / 8, real.min_normal / 17) == 3); } @@ -5621,12 +5946,12 @@ package: // Not public yet * ieeeMean(x, y) = sqrt(x * y). * */ -T ieeeMean(T)(T x, T y) @trusted pure nothrow +T ieeeMean(T)(T x, T y) @trusted pure nothrow @nogc in { // both x and y must have the same sign, and must not be NaN. assert(signbit(x) == signbit(y)); - assert(x==x && y==y); + assert(x == x && y == y); } body { @@ -5637,11 +5962,10 @@ body // The implementation is simple: cast x and y to integers, // average them (avoiding overflow), and cast the result back to a floating-point number. - alias floatTraits!(real) F; + alias F = floatTraits!(T); T u; - static if (T.mant_dig==64) - { // real80 - + static if (F.realFormat == RealFormat.ieeeExtended) + { // There's slight additional complexity because they are actually // 79-bit reals... ushort *ue = cast(ushort *)&u; @@ -5676,8 +6000,8 @@ body ue[4]= e | (xe[F.EXPPOS_SHORT]& 0x8000); // restore sign bit } - else static if(T.mant_dig == 113) - { //quadruple + else static if (F.realFormat == RealFormat.ieeeQuadruple) + { // This would be trivial if 'ucent' were implemented... ulong *ul = cast(ulong *)&u; ulong *xl = cast(ulong *)&x; @@ -5694,13 +6018,13 @@ body if (xl[MANTISSA_LSB] & yl[MANTISSA_LSB] & 1) { ++ml; - if (ml==0) ++mh; + if (ml == 0) ++mh; } mh >>>=1; ul[MANTISSA_MSB] = mh | (xl[MANTISSA_MSB] & 0x8000_0000_0000_0000); ul[MANTISSA_LSB] = ml; } - else static if (T.mant_dig == double.mant_dig) + else static if (F.realFormat == RealFormat.ieeeDouble) { ulong *ul = cast(ulong *)&u; ulong *xl = cast(ulong *)&x; @@ -5710,7 +6034,7 @@ body m |= ((*xl) & 0x8000_0000_0000_0000L); *ul = m; } - else static if (T.mant_dig == float.mant_dig) + else static if (F.realFormat == RealFormat.ieeeSingle) { uint *ul = cast(uint *)&u; uint *xl = cast(uint *)&x; @@ -5741,7 +6065,7 @@ unittest ==-1.5*(1+5*real.epsilon)); assert(ieeeMean(0x1p60,0x1p-10)==0x1p25); - static if (real.mant_dig == 64) + static if (floatTraits!(real).realFormat == RealFormat.ieeeExtended) { assert(ieeeMean(1.0L,real.infinity)==0x1p8192L); assert(ieeeMean(0.0L,real.infinity)==1.5); @@ -5763,7 +6087,7 @@ public: * x = the value to evaluate. * A = array of coefficients $(SUB a, 0), $(SUB a, 1), etc. */ -real poly(real x, const real[] A) @trusted pure nothrow +real poly(real x, const real[] A) @trusted pure nothrow @nogc in { assert(A.length > 0); @@ -5999,19 +6323,19 @@ unittest } // Included for backwards compatibility with Phobos1 -alias isNaN isnan; -alias isFinite isfinite; -alias isNormal isnormal; -alias isSubnormal issubnormal; -alias isInfinity isinf; +alias isnan = isNaN; +alias isfinite = isFinite; +alias isnormal = isNormal; +alias issubnormal = isSubnormal; +alias isinf = isInfinity; /* ********************************** * Building block functions, they * translate to a single x87 instruction. */ -real yl2x(real x, real y) @safe pure nothrow; // y * log2(x) -real yl2xp1(real x, real y) @safe pure nothrow; // y * log2(x + 1) +real yl2x(real x, real y) @nogc @safe pure nothrow; // y * log2(x) +real yl2xp1(real x, real y) @nogc @safe pure nothrow; // y * log2(x + 1) unittest { @@ -6093,7 +6417,7 @@ unittest assert(fabs(r - 2.18504f) < .00001); } -pure @safe nothrow unittest +@safe pure nothrow unittest { // issue 6381: floor/ceil should be usable in pure function. auto x = floor(1.2); diff --git a/libphobos/src/std/mathspecial.d b/libphobos/src/std/mathspecial.d index b67e3ff62..d8ba22ac6 100644 --- a/libphobos/src/std/mathspecial.d +++ b/libphobos/src/std/mathspecial.d @@ -62,7 +62,12 @@ private import std.internal.math.errorfunction; /* *********************************************** * GAMMA AND RELATED FUNCTIONS * * ***********************************************/ -//pure nothrow: + +pure: +nothrow: +@safe: +@nogc: + /** The Gamma function, $(GAMMA)(x) * * $(GAMMA)(x) is a generalisation of the factorial function diff --git a/libphobos/src/std/metastrings.d b/libphobos/src/std/metastrings.d deleted file mode 100644 index ad7bc54ba..000000000 --- a/libphobos/src/std/metastrings.d +++ /dev/null @@ -1,245 +0,0 @@ -// Written in the D programming language. - -/** -$(RED Deprecated. It will be removed in March 2014. - Please use $(XREF string, format), $(XREF conv, to), or - $(XREF conv, parse) instead of these templates (which one would depend on - which template is being replaced.) They now work in CTFE, and these - templates are very inefficient.) - -Templates with which to do compile-time manipulation of strings. - -Macros: - WIKI = Phobos/StdMetastrings - -Copyright: Copyright Digital Mars 2007 - 2013. -License: Boost License 1.0. -Authors: $(WEB digitalmars.com, Walter Bright), - Don Clugston -Source: $(PHOBOSSRC std/_metastrings.d) -*/ -module std.metastrings; - -/** -$(RED Deprecated. - Please use $(XREF string, format) instead. It now works in CTFE, - and this template is very inefficient.) - -Formats constants into a string at compile time. Analogous to $(XREF -string,format). - -Parameters: - -A = tuple of constants, which can be strings, characters, or integral - values. - -Formats: - * The formats supported are %s for strings, and %% - * for the % character. -Example: ---- -import std.metastrings; -import std.stdio; - -void main() -{ - string s = Format!("Arg %s = %s", "foo", 27); - writefln(s); // "Arg foo = 27" -} - * --- - */ - -deprecated("std.string.format now works in CTFE. Please use it instead.") -template Format(A...) -{ - static if (A.length == 0) - enum Format = ""; - else static if (is(typeof(A[0]) : const(char)[])) - enum Format = FormatString!(A[0], A[1..$]); - else - enum Format = toStringNow!(A[0]) ~ Format!(A[1..$]); -} - -deprecated("std.string.format now works in CTFE. Please use it instead.") -template FormatString(const(char)[] F, A...) -{ - static if (F.length == 0) - enum FormatString = Format!(A); - else static if (F.length == 1) - enum FormatString = F[0] ~ Format!(A); - else static if (F[0..2] == "%s") - enum FormatString - = toStringNow!(A[0]) ~ FormatString!(F[2..$],A[1..$]); - else static if (F[0..2] == "%%") - enum FormatString = "%" ~ FormatString!(F[2..$],A); - else - { - static assert(F[0] != '%', "unrecognized format %" ~ F[1]); - enum FormatString = F[0] ~ FormatString!(F[1..$],A); - } -} - -unittest -{ - auto s = Format!("hel%slo", "world", -138, 'c', true); - assert(s == "helworldlo-138ctrue", "[" ~ s ~ "]"); -} - -/** - * $(RED Deprecated. - * Please use $(XREF conv, format) instead. It now works in CTFE, - * and this template is very inefficient.) - * - * Convert constant argument to a string. - */ - -deprecated("std.conv.to now works in CTFE. Please use it instead.") -template toStringNow(ulong v) -{ - static if (v < 10) - enum toStringNow = "" ~ cast(char)(v + '0'); - else - enum toStringNow = toStringNow!(v / 10) ~ toStringNow!(v % 10); -} - -unittest -{ - static assert(toStringNow!(1uL << 62) == "4611686018427387904"); -} - -/// ditto -deprecated("std.conv.to!string now works in CTFE. Please use it instead.") -template toStringNow(long v) -{ - static if (v < 0) - enum toStringNow = "-" ~ toStringNow!(cast(ulong) -v); - else - enum toStringNow = toStringNow!(cast(ulong) v); -} - -unittest -{ - static assert(toStringNow!(0x100000000) == "4294967296"); - static assert(toStringNow!(-138L) == "-138"); -} - -/// ditto -deprecated("std.conv.to!string now works in CTFE. Please use it instead.") -template toStringNow(uint U) -{ - enum toStringNow = toStringNow!(cast(ulong)U); -} - -/// ditto -deprecated("std.conv.to!string now works in CTFE. Please use it instead.") -template toStringNow(int I) -{ - enum toStringNow = toStringNow!(cast(long)I); -} - -/// ditto -deprecated("std.conv.to!string now works in CTFE. Please use it instead.") -template toStringNow(bool B) -{ - enum toStringNow = B ? "true" : "false"; -} - -/// ditto -deprecated("std.conv.to!string now works in CTFE. Please use it instead.") -template toStringNow(string S) -{ - enum toStringNow = S; -} - -/// ditto -deprecated("std.conv.to!string now works in CTFE. Please use it instead.") -template toStringNow(char C) -{ - enum toStringNow = "" ~ C; -} - - -/******** - * $(RED Deprecated. - * Please use $(XREF conv, parse) instead. It now works in CTFE, - * and this template is very inefficient.) - * - * Parse unsigned integer literal from the start of string s. - * returns: - * .value = the integer literal as a string, - * .rest = the string following the integer literal - * Otherwise: - * .value = null, - * .rest = s - */ - -deprecated("to!string(std.conv.parse!uint(value)) now works in CTFE. Please use it instead.") -template parseUinteger(const(char)[] s) -{ - static if (s.length == 0) - { - enum value = ""; - enum rest = ""; - } - else static if (s[0] >= '0' && s[0] <= '9') - { - enum value = s[0] ~ parseUinteger!(s[1..$]).value; - enum rest = parseUinteger!(s[1..$]).rest; - } - else - { - enum value = ""; - enum rest = s; - } -} - -/******** -$(RED Deprecated. - Please use $(XREF conv, parse) instead. It now works in CTFE, - and this template is very inefficient.) - -Parse integer literal optionally preceded by $(D '-') from the start -of string $(D s). - -Returns: - .value = the integer literal as a string, - .rest = the string following the integer literal - -Otherwise: - .value = null, - .rest = s -*/ - -deprecated("to!string(std.conv.parse!int(value)) now works in CTFE. Please use it instead.") -template parseInteger(const(char)[] s) -{ - static if (s.length == 0) - { - enum value = ""; - enum rest = ""; - } - else static if (s[0] >= '0' && s[0] <= '9') - { - enum value = s[0] ~ parseUinteger!(s[1..$]).value; - enum rest = parseUinteger!(s[1..$]).rest; - } - else static if (s.length >= 2 && - s[0] == '-' && s[1] >= '0' && s[1] <= '9') - { - enum value = s[0..2] ~ parseUinteger!(s[2..$]).value; - enum rest = parseUinteger!(s[2..$]).rest; - } - else - { - enum value = ""; - enum rest = s; - } -} - -unittest -{ - assert(parseUinteger!("1234abc").value == "1234"); - assert(parseUinteger!("1234abc").rest == "abc"); - assert(parseInteger!("-1234abc").value == "-1234"); - assert(parseInteger!("-1234abc").rest == "abc"); -} diff --git a/libphobos/src/std/mmfile.d b/libphobos/src/std/mmfile.d index 533baa6a2..09705e774 100644 --- a/libphobos/src/std/mmfile.d +++ b/libphobos/src/std/mmfile.d @@ -127,7 +127,7 @@ class MmFile if (prot & PROT_WRITE && size > statbuf.st_size) { // Need to make the file size bytes big - lseek(fd, cast(int)(size - 1), SEEK_SET); + lseek(fd, cast(off_t)(size - 1), SEEK_SET); char c = 0; core.sys.posix.unistd.write(fd, &c, 1); } @@ -236,7 +236,7 @@ class MmFile if (hFile != INVALID_HANDLE_VALUE) { int hi = cast(int)(size>>32); - hFileMap = CreateFileMappingA(hFile, null, flProtect, + hFileMap = CreateFileMappingW(hFile, null, flProtect, hi, cast(uint)size, null); } if (hFileMap != null) // mapping didn't fail @@ -331,7 +331,7 @@ class MmFile if (prot & PROT_WRITE && size > statbuf.st_size) { // Need to make the file size bytes big - .lseek(fd, cast(int)(size - 1), SEEK_SET); + .lseek(fd, cast(off_t)(size - 1), SEEK_SET); char c = 0; core.sys.posix.unistd.write(fd, &c, 1); } @@ -341,6 +341,7 @@ class MmFile else { fd = -1; + version(linux) import core.sys.linux.sys.mman : MAP_ANON; flags |= MAP_ANON; } this.size = size; @@ -509,7 +510,7 @@ class MmFile p = MapViewOfFileEx(hFileMap, dwDesiredAccess, hi, cast(uint)start, len, address); errnoEnforce(p); } else { - p = mmap(address, len, prot, flags, fd, cast(int)start); + p = mmap(address, len, prot, flags, fd, cast(off_t)start); errnoEnforce(p != MAP_FAILED); } data = p[0 .. len]; diff --git a/libphobos/src/std/net/curl.d b/libphobos/src/std/net/curl.d index 7260466a7..5a356221a 100644 --- a/libphobos/src/std/net/curl.d +++ b/libphobos/src/std/net/curl.d @@ -20,6 +20,10 @@ Networking client functionality as provided by $(WEB _curl.haxx.se/libcurl, libcurl). The libcurl library must be installed on the system in order to use this module. +Windows x86 note: +A DMD compatible libcurl static library can be downloaded from the dlang.org +$(LINK2 http://dlang.org/download.html, download page). + Compared to using libcurl directly this module allows simpler client code for common uses, requires no unsafe operations, and integrates better with the rest of the language. Futhermore it provides $(D range) @@ -123,7 +127,7 @@ http.perform(); First, an instance of the reference-counted HTTP struct is created. Then the custom delegates are set. These will be called whenever the HTTP instance receives a header and a data buffer, respectively. In this simple example, the -headers are writting to stdout and the data is ignored. If the request should be +headers are written to stdout and the data is ignored. If the request should be stopped before it has finished then return something less than data.length from the onReceive callback. See $(LREF onReceiveHeader)/$(LREF onReceive) for more information. Finally the HTTP request is effected by calling perform(), which is @@ -139,7 +143,7 @@ License: Boost License 1.0. Authors: Jonas Drewsen. Some of the SMTP code contributed by Jimmy Cao. Credits: The functionally is based on $(WEB _curl.haxx.se/libcurl, libcurl). - LibCurl is licensed under an MIT/X derivate license. + LibCurl is licensed under an MIT/X derivative license. */ /* Copyright Jonas Drewsen 2011 - 2012. @@ -166,6 +170,8 @@ import std.traits; import std.typecons; import std.typetuple; +public import etc.c.curl : CurlOption; + version(unittest) { // Run unit test with the PHOBOS_TEST_ALLOW_NET=1 set in order to @@ -868,7 +874,7 @@ private auto _decodeContent(T)(ubyte[] content, string encoding) } } -alias std.string.KeepTerminator KeepTerminator; +alias KeepTerminator = std.string.KeepTerminator; /++ struct ByLineBuffer(Char) { @@ -1583,10 +1589,10 @@ private mixin template Protocol() /// Value to return from $(D onSend)/$(D onReceive) delegates in order to /// pause a request - alias CurlReadFunc.pause requestPause; + alias requestPause = CurlReadFunc.pause; /// Value to return from onSend delegate in order to abort a request - alias CurlReadFunc.abort requestAbort; + alias requestAbort = CurlReadFunc.abort; static uint defaultAsyncStringBufferSize = 100; @@ -1662,7 +1668,7 @@ private mixin template Protocol() } /// Type of proxy - alias etc.c.curl.CurlProxy CurlProxy; + alias CurlProxy = etc.c.curl.CurlProxy; /** Proxy type * See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type) @@ -2013,8 +2019,8 @@ private bool decodeLineInto(Terminator, Char = char)(ref const(ubyte)[] basesrc, * http.url = "http://upload.wikimedia.org/wikipedia/commons/" * "5/53/Wikipedia-logo-en-big.png"; * http.onReceive = (ubyte[] data) { return data.length; }; - * http.onProgress = (double dltotal, double dlnow, - * double ultotal, double ulnow) + * http.onProgress = (size_t dltotal, size_t dlnow, + * size_t ultotal, size_t ulnow) * { * writeln("Progress ", dltotal, ", ", dlnow, ", ", ultotal, ", ", ulnow); * return 0; @@ -2030,7 +2036,7 @@ struct HTTP mixin Protocol; /// Authentication method equal to $(ECXREF curl, CurlAuth) - alias CurlAuth AuthMethod; + alias AuthMethod = CurlAuth; static private uint defaultMaxRedirects = 10; @@ -2125,7 +2131,7 @@ struct HTTP $(WEB www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25, _RFC2616 Section 14.25) */ - alias CurlTimeCond TimeCond; + alias TimeCond = CurlTimeCond; /** Constructor taking the url as parameter. @@ -2247,10 +2253,10 @@ struct HTTP { /// Value to return from $(D onSend)/$(D onReceive) delegates in order to /// pause a request - alias CurlReadFunc.pause requestPause; + alias requestPause = CurlReadFunc.pause; /// Value to return from onSend delegate in order to abort a request - alias CurlReadFunc.abort requestAbort; + alias requestAbort = CurlReadFunc.abort; /** True if the instance is stopped. A stopped instance is not usable. @@ -2291,7 +2297,7 @@ struct HTTP @property void proxyPort(ushort port); /// Type of proxy - alias etc.c.curl.CurlProxy CurlProxy; + alias CurlProxy = etc.c.curl.CurlProxy; /** Proxy type * See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type) @@ -2894,10 +2900,10 @@ struct FTP { /// Value to return from $(D onSend)/$(D onReceive) delegates in order to /// pause a request - alias CurlReadFunc.pause requestPause; + alias requestPause = CurlReadFunc.pause; /// Value to return from onSend delegate in order to abort a request - alias CurlReadFunc.abort requestAbort; + alias requestAbort = CurlReadFunc.abort; /** True if the instance is stopped. A stopped instance is not usable. @@ -2938,7 +2944,7 @@ struct FTP @property void proxyPort(ushort port); /// Type of proxy - alias etc.c.curl.CurlProxy CurlProxy; + alias CurlProxy = etc.c.curl.CurlProxy; /** Proxy type * See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type) @@ -3234,10 +3240,10 @@ struct SMTP { /// Value to return from $(D onSend)/$(D onReceive) delegates in order to /// pause a request - alias CurlReadFunc.pause requestPause; + alias requestPause = CurlReadFunc.pause; /// Value to return from onSend delegate in order to abort a request - alias CurlReadFunc.abort requestAbort; + alias requestAbort = CurlReadFunc.abort; /** True if the instance is stopped. A stopped instance is not usable. @@ -3278,7 +3284,7 @@ struct SMTP @property void proxyPort(ushort port); /// Type of proxy - alias etc.c.curl.CurlProxy CurlProxy; + alias CurlProxy = etc.c.curl.CurlProxy; /** Proxy type * See: $(WEB curl.haxx.se/libcurl/c/curl_easy_setopt.html#CURLOPTPROXY, _proxy_type) @@ -3471,7 +3477,7 @@ class CurlTimeoutException : CurlException } /// Equal to $(ECXREF curl, CURLcode) -alias CURLcode CurlCode; +alias CurlCode = CURLcode; /** Wrapper to provide a better interface to libcurl than using the plain C API. @@ -3497,8 +3503,8 @@ struct Curl curl_global_cleanup(); } - alias void[] OutData; - alias ubyte[] InData; + alias OutData = void[]; + alias InData = ubyte[]; bool stopped; // A handle should not be used by two threads simultaneously @@ -3513,8 +3519,8 @@ struct Curl private int delegate(size_t dltotal, size_t dlnow, size_t ultotal, size_t ulnow) _onProgress; - alias CurlReadFunc.pause requestPause; - alias CurlReadFunc.abort requestAbort; + alias requestPause = CurlReadFunc.pause; + alias requestAbort = CurlReadFunc.abort; /** Initialize the instance by creating a working curl handle. @@ -3551,15 +3557,22 @@ struct Curl opensocketfunction, noprogress, progressdata, progressfunction, debugdata, debugfunction, - ssl_ctx_function, interleavedata, + interleavedata, interleavefunction, chunk_data, chunk_bgn_function, chunk_end_function, fnmatch_data, fnmatch_function, - ssh_keydata, cookiejar, postfields); + cookiejar, postfields); foreach(option; tt) copy.clear(option); } + // The options are only supported by libcurl when it has been built + // against certain versions of OpenSSL - if your libcurl uses an old + // OpenSSL, or uses an entirely different SSL engine, attempting to + // clear these normally will raise an exception + copy.clearIfSupported(CurlOption.ssl_ctx_function); + copy.clearIfSupported(CurlOption.ssh_keydata); + // Enable for curl version > 7.21.7 static if (LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 21 && @@ -3679,6 +3692,22 @@ struct Curl _check(curl_easy_setopt(this.handle, option, null)); } + /** + Clear a pointer option. Does not raise an exception if the underlying + libcurl does not support the option. Use sparingly. + Params: + option = A $(ECXREF curl, CurlOption) as found in the curl documentation + */ + void clearIfSupported(CurlOption option) + { + throwOnStopped(); + auto rval = curl_easy_setopt(this.handle, option, null); + if (rval != CurlError.unknown_telnet_option) + { + _check(rval); + } + } + /** perform the curl request by doing the HTTP,FTP etc. as it has been setup beforehand. @@ -3751,7 +3780,7 @@ struct Curl { _onReceiveHeader = (in char[] od) { - throwOnStopped("Receive header callback called on " + throwOnStopped("Receive header callback called on "~ "cleaned up Curl instance"); callback(od); }; @@ -3869,7 +3898,7 @@ struct Curl { _onSocketOption = (curl_socket_t sock, CurlSockType st) { - throwOnStopped("Socket option callback called on " + throwOnStopped("Socket option callback called on "~ "cleaned up Curl instance"); return callback(sock, st); }; @@ -3911,7 +3940,7 @@ struct Curl { _onProgress = (size_t dlt, size_t dln, size_t ult, size_t uln) { - throwOnStopped("Progress callback called on cleaned " + throwOnStopped("Progress callback called on cleaned "~ "up Curl instance"); return callback(dlt, dln, ult, uln); }; @@ -4189,9 +4218,9 @@ private static size_t _receiveAsyncLines(Terminator, Unit) // onReceive. Can be up to a max of 4 bytes. enforceEx!CurlException(data.length <= 4, format( - "Too many bytes left not decoded %s" - " > 4. Maybe the charset specified in" - " headers does not match " + "Too many bytes left not decoded %s"~ + " > 4. Maybe the charset specified in"~ + " headers does not match "~ "the actual content downloaded?", data.length)); leftOverBytes ~= data; diff --git a/libphobos/src/std/net/isemail.d b/libphobos/src/std/net/isemail.d index 225644de9..eae1c3c92 100644 --- a/libphobos/src/std/net/isemail.d +++ b/libphobos/src/std/net/isemail.d @@ -67,7 +67,7 @@ import std.uni; EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no, EmailStatusCode errorLevel = EmailStatusCode.none) if (isSomeChar!(Char)) { - alias const(Char)[] tstring; + alias tstring = const(Char)[]; enum defaultThreshold = 16; int threshold; @@ -229,7 +229,7 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no break; default: - throw new Exception("More text found where none is allowed, but unrecognised prior " + throw new Exception("More text found where none is allowed, but unrecognised prior " ~ "context: " ~ to!(string)(contextPrior)); } } @@ -282,7 +282,7 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no } endOrDie = false; - elementLength = 0, + elementLength = 0; elementCount++; //atomList[EmailPart.componentDomain][elementCount] = ""; @@ -345,7 +345,7 @@ EmailStatus isEmail (Char) (const(Char)[] email, CheckDns checkDNS = CheckDns.no break; default: - throw new Exception("More text found where none is allowed, but unrecognised prior " + throw new Exception("More text found where none is allowed, but unrecognised prior " ~ "context: " ~ to!(string)(contextPrior)); } @@ -851,19 +851,19 @@ unittest // EmailStatusCode.any).statusCode == EmailStatusCode.dnsWarningNoRecord); // DNS check is currently not implemented - assert(`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@abcdefghijklmnopqrstuvwxyz` - `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.` - `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij`.isEmail(CheckDns.no, + assert((`abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghiklm@abcdefghijklmnopqrstuvwxyz`~ + `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.`~ + `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij`).isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322TooLong); - assert(`a@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyz` - `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.` - `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.hij`.isEmail(CheckDns.no, + assert((`a@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyz`~ + `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.`~ + `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.hij`).isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322TooLong); - assert(`a@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyz` - `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.` - `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.hijk`.isEmail(CheckDns.no, + assert((`a@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyz`~ + `abcdefghijklmnopqrstuvwxyzabcdefghikl.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.`~ + `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg.hijk`).isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.rfc5322DomainTooLong); assert(`"test"@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == @@ -1000,7 +1000,7 @@ unittest EmailStatusCode.foldingWhitespace, `Folding whitespace`); assert("\u000D\u000A \u000D\u000A test@iana.org".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedFoldingWhitespace, `FWS with one line composed entirely of WSP` + EmailStatusCode.deprecatedFoldingWhitespace, `FWS with one line composed entirely of WSP`~ ` -- only allowed as obsolete FWS (someone might allow only non-obsolete FWS)`); assert(`(comment)test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.comment); @@ -1025,21 +1025,21 @@ unittest assert(`test@(comment)abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghikl.com`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt); - assert(`(comment)test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghik.abcdefghijklmnopqrstuvwxyz` - `abcdefghijklmnopqrstuvwxyzabcdefghik.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.` - `abcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstu`.isEmail(CheckDns.no, + assert((`(comment)test@abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghik.abcdefghijklmnopqrstuvwxyz`~ + `abcdefghijklmnopqrstuvwxyzabcdefghik.abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.`~ + `abcdefghijklmnopqrstuvwxyzabcdefghijk.abcdefghijklmnopqrstu`).isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.comment); assert("test@iana.org\u000A".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.errorExpectingText); assert(`test@xn--hxajbheg2az3al.xn--jxalpdlp`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == - EmailStatusCode.valid, `A valid IDN from ICANN's ` + EmailStatusCode.valid, `A valid IDN from ICANN's `~ `IDN TLD evaluation gateway`); assert(`xn--test@iana.org`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == EmailStatusCode.valid, - `RFC 3490: "unless the email standards are revised to invite the use of IDNA for local parts, a domain label` - ` that holds the local part of an email address SHOULD NOT begin with the ACE prefix, and even if it does,` + `RFC 3490: "unless the email standards are revised to invite the use of IDNA for local parts, a domain label`~ + ` that holds the local part of an email address SHOULD NOT begin with the ACE prefix, and even if it does,`~ ` it is to be interpreted literally as a local part that happens to begin with the ACE prefix"`); assert(`test@iana.org-`.isEmail(CheckDns.no, EmailStatusCode.any).statusCode == @@ -1179,7 +1179,7 @@ unittest EmailStatusCode.foldingWhitespace, `FWS`); assert("test@iana.org\u000D\u000A \u000D\u000A ".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == - EmailStatusCode.deprecatedFoldingWhitespace, `FWS with one line composed entirely of WSP -- ` + EmailStatusCode.deprecatedFoldingWhitespace, `FWS with one line composed entirely of WSP -- `~ `only allowed as obsolete FWS (someone might allow only non-obsolete FWS)`); assert("test@iana.org\u000D\u000A".isEmail(CheckDns.no, EmailStatusCode.any).statusCode == @@ -1316,13 +1316,13 @@ string statusCodeDescription (EmailStatusCode statusCode) case EmailStatusCode.dnsWarning: return "Address is valid but a DNS check was not successful"; case EmailStatusCode.rfc5321: return "Address is valid for SMTP but has unusual elements"; - case EmailStatusCode.cFoldingWhitespace: return "Address is valid within the message but cannot be used" + case EmailStatusCode.cFoldingWhitespace: return "Address is valid within the message but cannot be used"~ " unmodified for the envelope"; - case EmailStatusCode.deprecated_: return "Address contains deprecated elements but may still be valid in" + case EmailStatusCode.deprecated_: return "Address contains deprecated elements but may still be valid in"~ " restricted contexts"; - case EmailStatusCode.rfc5322: return "The address is only valid according to the broad definition of RFC 5322." + case EmailStatusCode.rfc5322: return "The address is only valid according to the broad definition of RFC 5322."~ " It is otherwise invalid"; case EmailStatusCode.any: return ""; @@ -1334,7 +1334,7 @@ string statusCodeDescription (EmailStatusCode statusCode) case EmailStatusCode.valid: return "Address is valid"; // Address is valid but a DNS check was not successful - case EmailStatusCode.dnsWarningNoMXRecord: return "Could not find an MX record for this domain but an A-record" + case EmailStatusCode.dnsWarningNoMXRecord: return "Could not find an MX record for this domain but an A-record"~ " does exist"; case EmailStatusCode.dnsWarningNoRecord: return "Could not find an MX record or an A-record for this domain"; @@ -1342,13 +1342,13 @@ string statusCodeDescription (EmailStatusCode statusCode) // Address is valid for SMTP but has unusual elements case EmailStatusCode.rfc5321TopLevelDomain: return "Address is valid but at a Top Level Domain"; - case EmailStatusCode.rfc5321TopLevelDomainNumeric: return "Address is valid but the Top Level Domain begins" + case EmailStatusCode.rfc5321TopLevelDomainNumeric: return "Address is valid but the Top Level Domain begins"~ " with a number"; case EmailStatusCode.rfc5321QuotedString: return "Address is valid but contains a quoted string"; case EmailStatusCode.rfc5321AddressLiteral: return "Address is valid but at a literal address not a domain"; - case EmailStatusCode.rfc5321IpV6Deprecated: return "Address is valid but contains a :: that only elides one" + case EmailStatusCode.rfc5321IpV6Deprecated: return "Address is valid but contains a :: that only elides one"~ " zero group"; @@ -1359,7 +1359,7 @@ string statusCodeDescription (EmailStatusCode statusCode) // Address contains deprecated elements but may still be valid in restricted contexts case EmailStatusCode.deprecatedLocalPart: return "The local part is in a deprecated form"; - case EmailStatusCode.deprecatedFoldingWhitespace: return "Address contains an obsolete form of" + case EmailStatusCode.deprecatedFoldingWhitespace: return "Address contains an obsolete form of"~ " Folding White Space"; case EmailStatusCode.deprecatedQuotedText: return "A quoted string contains a deprecated character"; @@ -1367,11 +1367,11 @@ string statusCodeDescription (EmailStatusCode statusCode) case EmailStatusCode.deprecatedComment: return "Address contains a comment in a position that is deprecated"; case EmailStatusCode.deprecatedCommentText: return "A comment contains a deprecated character"; - case EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt: return "Address contains a comment or" + case EmailStatusCode.deprecatedCommentFoldingWhitespaceNearAt: return "Address contains a comment or"~ " Folding White Space around the @ sign"; // The address is only valid according to the broad definition of RFC 5322 - case EmailStatusCode.rfc5322Domain: return "Address is RFC 5322 compliant but contains domain characters that" + case EmailStatusCode.rfc5322Domain: return "Address is RFC 5322 compliant but contains domain characters that"~ " are not allowed by DNS"; case EmailStatusCode.rfc5322TooLong: return "Address is too long"; @@ -1380,7 +1380,7 @@ string statusCodeDescription (EmailStatusCode statusCode) case EmailStatusCode.rfc5322LabelTooLong: return "The domain part contains an element that is too long"; case EmailStatusCode.rfc5322DomainLiteral: return "The domain literal is not a valid RFC 5321 address literal"; - case EmailStatusCode.rfc5322DomainLiteralObsoleteText: return "The domain literal is not a valid RFC 5321" + case EmailStatusCode.rfc5322DomainLiteralObsoleteText: return "The domain literal is not a valid RFC 5321"~ " address literal and it contains obsolete characters"; case EmailStatusCode.rfc5322IpV6GroupCount: @@ -1407,7 +1407,7 @@ string statusCodeDescription (EmailStatusCode statusCode) case EmailStatusCode.errorTextAfterQuotedString: return "Address contains text after a quoted string"; - case EmailStatusCode.errorTextAfterDomainLiteral: return "Extra characters were found after the end of" + case EmailStatusCode.errorTextAfterDomainLiteral: return "Extra characters were found after the end of"~ " the domain literal"; case EmailStatusCode.errorExpectingQuotedPair: diff --git a/libphobos/src/std/numeric.d b/libphobos/src/std/numeric.d index 44c044f1d..f2493743d 100644 --- a/libphobos/src/std/numeric.d +++ b/libphobos/src/std/numeric.d @@ -112,22 +112,23 @@ private template CustomFloatParams(uint bits) { enum CustomFloatFlags flags = CustomFloatFlags.ieee ^ ((bits == 80) ? CustomFloatFlags.storeNormalized : CustomFloatFlags.none); - static if (bits == 8) alias CustomFloatParams!( 4, 3, flags) CustomFloatParams; - static if (bits == 16) alias CustomFloatParams!(10, 5, flags) CustomFloatParams; - static if (bits == 32) alias CustomFloatParams!(23, 8, flags) CustomFloatParams; - static if (bits == 64) alias CustomFloatParams!(52, 11, flags) CustomFloatParams; - static if (bits == 80) alias CustomFloatParams!(64, 15, flags) CustomFloatParams; + static if (bits == 8) alias CustomFloatParams = CustomFloatParams!( 4, 3, flags); + static if (bits == 16) alias CustomFloatParams = CustomFloatParams!(10, 5, flags); + static if (bits == 32) alias CustomFloatParams = CustomFloatParams!(23, 8, flags); + static if (bits == 64) alias CustomFloatParams = CustomFloatParams!(52, 11, flags); + static if (bits == 80) alias CustomFloatParams = CustomFloatParams!(64, 15, flags); } private template CustomFloatParams(uint precision, uint exponentWidth, CustomFloatFlags flags) { - alias TypeTuple!( - precision, - exponentWidth, - flags, - (1 << (exponentWidth - ((flags & flags.probability) == 0))) - - ((flags & (flags.nan | flags.infinity)) != 0) - ((flags & flags.probability) != 0) - ) CustomFloatParams; // ((flags & CustomFloatFlags.probability) == 0) + alias CustomFloatParams = + TypeTuple!( + precision, + exponentWidth, + flags, + (1 << (exponentWidth - ((flags & flags.probability) == 0))) + - ((flags & (flags.nan | flags.infinity)) != 0) - ((flags & flags.probability) != 0) + ); // ((flags & CustomFloatFlags.probability) == 0) } /** @@ -149,26 +150,26 @@ private template CustomFloatParams(uint precision, uint exponentWidth, CustomFlo * writeln(w); * * // Functions calls require conversion - * z = sin(+x) + cos(+y); // Use uniary plus to concisely convert to a real + * z = sin(+x) + cos(+y); // Use unary plus to concisely convert to a real * z = sin(x.re) + cos(y.re); // Or use the .re property to convert to a real * z = sin(x.get!float) + cos(y.get!float); // Or use get!T * z = sin(cast(float)x) + cos(cast(float)y); // Or use cast(T) to explicitly convert * * // Define a 8-bit custom float for storing probabilities - * alias CustomFloat!(4, 4, CustomFloatFlags.ieee^CustomFloatFlags.probability^CustomFloatFlags.signed ) Probability; + * alias Probability = CustomFloat!(4, 4, CustomFloatFlags.ieee^CustomFloatFlags.probability^CustomFloatFlags.signed ); * auto p = Probability(0.5); * ---- */ template CustomFloat(uint bits) if (bits == 8 || bits == 16 || bits == 32 || bits == 64 || bits == 80) { - alias CustomFloat!(CustomFloatParams!(bits)) CustomFloat; + alias CustomFloat = CustomFloat!(CustomFloatParams!(bits)); } /// ditto template CustomFloat(uint precision, uint exponentWidth, CustomFloatFlags flags = CustomFloatFlags.ieee) if (((flags & flags.signed) + precision + exponentWidth) % 8 == 0 && precision + exponentWidth > 0) { - alias CustomFloat!(CustomFloatParams!(precision, exponentWidth, flags)) CustomFloat; + alias CustomFloat = CustomFloat!(CustomFloatParams!(precision, exponentWidth, flags)); } /// ditto struct CustomFloat( @@ -182,21 +183,21 @@ struct CustomFloat( private: // get the correct unsigned bitfield type to support > 32 bits template uType(uint bits) { - static if(bits <= size_t.sizeof*8) alias size_t uType; - else alias ulong uType; + static if(bits <= size_t.sizeof*8) alias uType = size_t; + else alias uType = ulong ; } // get the correct signed bitfield type to support > 32 bits template sType(uint bits) { - static if(bits <= ptrdiff_t.sizeof*8-1) alias ptrdiff_t sType; - else alias long sType; + static if(bits <= ptrdiff_t.sizeof*8-1) alias sType = ptrdiff_t; + else alias sType = long; } - alias uType!precision T_sig; - alias uType!exponentWidth T_exp; - alias sType!exponentWidth T_signed_exp; + alias T_sig = uType!precision; + alias T_exp = uType!exponentWidth; + alias T_signed_exp = sType!exponentWidth; - alias CustomFloatFlags Flags; + alias Flags = CustomFloatFlags; // Facilitate converting numeric types to custom float union ToBinary(F) @@ -556,13 +557,13 @@ struct CustomFloat( unittest { - alias TypeTuple!( - CustomFloat!(5, 10), - CustomFloat!(5, 11, CustomFloatFlags.ieee ^ CustomFloatFlags.signed), - CustomFloat!(1, 15, CustomFloatFlags.ieee ^ CustomFloatFlags.signed), - CustomFloat!(4, 3, CustomFloatFlags.ieee | CustomFloatFlags.probability ^ CustomFloatFlags.signed) - - ) FPTypes; + alias FPTypes = + TypeTuple!( + CustomFloat!(5, 10), + CustomFloat!(5, 11, CustomFloatFlags.ieee ^ CustomFloatFlags.signed), + CustomFloat!(1, 15, CustomFloatFlags.ieee ^ CustomFloatFlags.signed), + CustomFloat!(4, 3, CustomFloatFlags.ieee | CustomFloatFlags.probability ^ CustomFloatFlags.signed) + ); foreach (F; FPTypes) { @@ -627,7 +628,7 @@ on very many factors. */ template FPTemporary(F) if (isFloatingPoint!F) { - alias real FPTemporary; + alias FPTemporary = real; } /** @@ -1590,9 +1591,9 @@ unittest // { // ReturnType!(fun) tabulateFixed(ParameterTypeTuple!(fun) arg) // { -// alias ParameterTypeTuple!(fun)[0] num; +// alias num = ParameterTypeTuple!(fun)[0]; // static num[n] table; -// alias arg[0] x; +// alias x = arg[0]; // enforce(left <= x && x < right); // immutable i = cast(uint) (table.length // * ((x - left) / (right - left))); @@ -1618,7 +1619,7 @@ unittest // unittest // { // enum epsilon = 0.01; -// alias tabulateFixed!(tanh, 700, epsilon, 0.2, 3) fasttanh; +// alias fasttanh = tabulateFixed!(tanh, 700, epsilon, 0.2, 3); // uint testSize = 100000; // auto rnd = Random(unpredictableSeed); // foreach (i; 0 .. testSize) { @@ -2091,7 +2092,7 @@ unittest /** Computes the greatest common divisor of $(D a) and $(D b) by using -Euler's algorithm. +Euclid's algorithm. */ T gcd(T)(T a, T b) { static if (is(T == const) || is(T == immutable)) { @@ -2176,7 +2177,7 @@ unittest // though floats seem accurate enough for all practical purposes, since // they pass the "approxEqual(inverseFft(fft(arr)), arr)" test even for // size 2 ^^ 22. -private alias float lookup_t; +private alias lookup_t = float; /**A class for performing fast Fourier transforms of power of two sizes. * This class encapsulates a large amount of state that is reusable when @@ -2231,7 +2232,7 @@ private: assert(range.length >= 4); assert(isPowerOfTwo(range.length)); } body { - alias ElementType!R E; + alias E = ElementType!R; // Converts odd indices of range to the imaginary components of // a range half the size. The even indices become the real components. @@ -2244,7 +2245,7 @@ private: // source.length is even because it has to be a power of 2. static struct OddToImaginary { R source; - alias Complex!(CommonType!(E, typeof(buf[0].re))) C; + alias C = Complex!(CommonType!(E, typeof(buf[0].re))); @property { C front() { @@ -2506,7 +2507,7 @@ public: slowFourier2(range, buf); return; } else { - alias ElementType!R E; + alias E = ElementType!R; static if(is(E : real)) { return fftImplPureReal(range, buf); } else { @@ -2628,7 +2629,7 @@ unittest { assert(approxEqual(map!"a.re"(fft1), map!"a.re"(fft1Float))); assert(approxEqual(map!"a.im"(fft1), map!"a.im"(fft1Float))); - alias Complex!float C; + alias C = Complex!float; auto arr2 = [C(1,2), C(3,4), C(5,6), C(7,8), C(9,10), C(11,12), C(13,14), C(15,16)]; auto fft2 = fft(arr2); @@ -2689,7 +2690,7 @@ struct Stride(R) { Unqual!R range; size_t _nSteps; size_t _length; - alias ElementType!(R) E; + alias E = ElementType!(R); this(R range, size_t nStepsIn) { this.range = range; @@ -2765,7 +2766,7 @@ void slowFourier2(Ret, R)(R range, Ret buf) { // Hard-coded base case for FFT of size 4. Doesn't work as well as the size // 2 case. void slowFourier4(Ret, R)(R range, Ret buf) { - alias ElementType!Ret C; + alias C = ElementType!Ret; assert(range.length == 4); assert(buf.length == 4); diff --git a/libphobos/src/std/outbuffer.d b/libphobos/src/std/outbuffer.d index a17a70c4d..9a741164f 100644 --- a/libphobos/src/std/outbuffer.d +++ b/libphobos/src/std/outbuffer.d @@ -37,6 +37,7 @@ private * OutBuffer's byte order is the format native to the computer. * To control the byte order (endianness), use a class derived * from OutBuffer. + * OutBuffer's internal buffer is allocated with the GC. */ class OutBuffer @@ -89,6 +90,11 @@ class OutBuffer } } + /********************************** + * put enables OutBuffer to be used as an OutputRange. + */ + alias write put; + /************************************* * Append data to the internal buffer. */ @@ -377,3 +383,26 @@ unittest //printf("buf = '%.*s'\n", s.length, s.ptr); assert(cmp(buf.toString(), "hello world 6") == 0); } + +unittest +{ + import std.range; + static assert(isOutputRange!(OutBuffer, char)); + + import std.algorithm; + { + OutBuffer buf = new OutBuffer(); + "hello".copy(buf); + assert(buf.toBytes() == "hello"); + } + { + OutBuffer buf = new OutBuffer(); + "hello"w.copy(buf); + assert(buf.toBytes() == "h\x00e\x00l\x00l\x00o\x00"); + } + { + OutBuffer buf = new OutBuffer(); + "hello"d.copy(buf); + assert(buf.toBytes() == "h\x00\x00\x00e\x00\x00\x00l\x00\x00\x00l\x00\x00\x00o\x00\x00\x00"); + } +} diff --git a/libphobos/src/std/parallelism.d b/libphobos/src/std/parallelism.d index 674fd54ee..f830e4e07 100644 --- a/libphobos/src/std/parallelism.d +++ b/libphobos/src/std/parallelism.d @@ -207,20 +207,16 @@ private bool atomicCasUbyte(ref ubyte stuff, ubyte testVal, ubyte newVal) /*--------------------- Generic helper functions, etc.------------------------*/ private template MapType(R, functions...) { - static if(functions.length == 0) - { - alias typeof(unaryFun!(functions[0])(ElementType!R.init)) MapType; - } - else - { - alias typeof(adjoin!(staticMap!(unaryFun, functions)) - (ElementType!R.init)) MapType; - } + static assert(functions.length); + + ElementType!R e = void; + alias MapType = + typeof(adjoin!(staticMap!(unaryFun, functions))(e)); } private template ReduceType(alias fun, R, E) { - alias typeof(binaryFun!fun(E.init, ElementType!R.init)) ReduceType; + alias ReduceType = typeof(binaryFun!fun(E.init, ElementType!R.init)); } private template noUnsharedAliasing(T) @@ -242,17 +238,17 @@ private template isSafeTask(F) unittest { - alias void function() @safe F1; - alias void function() F2; - alias void function(uint, string) @trusted F3; - alias void function(uint, char[]) F4; + alias F1 = void function() @safe; + alias F2 = void function(); + alias F3 = void function(uint, string) @trusted; + alias F4 = void function(uint, char[]); static assert( isSafeTask!F1); static assert(!isSafeTask!F2); static assert( isSafeTask!F3); static assert(!isSafeTask!F4); - alias uint[] function(uint, string) pure @trusted F5; + alias F5 = uint[] function(uint, string) pure @trusted; static assert( isSafeTask!F5); } @@ -300,7 +296,7 @@ private enum TaskStatus : ubyte private template AliasReturn(alias fun, T...) { - alias typeof({ T args; return fun(args); }) AliasReturn; + alias AliasReturn = typeof({ T args; return fun(args); }); } // Should be private, but std.algorithm.reduce is used in the zero-thread case @@ -309,13 +305,13 @@ template reduceAdjoin(functions...) { static if(functions.length == 1) { - alias binaryFun!(functions[0]) reduceAdjoin; + alias reduceAdjoin = binaryFun!(functions[0]); } else { T reduceAdjoin(T, U)(T lhs, U rhs) { - alias staticMap!(binaryFun, functions) funs; + alias funs = staticMap!(binaryFun, functions); foreach(i, Unused; typeof(lhs.expand)) { @@ -331,13 +327,13 @@ private template reduceFinish(functions...) { static if(functions.length == 1) { - alias binaryFun!(functions[0]) reduceFinish; + alias reduceFinish = binaryFun!(functions[0]); } else { T reduceFinish(T)(T lhs, T rhs) { - alias staticMap!(binaryFun, functions) funs; + alias funs = staticMap!(binaryFun, functions); foreach(i, Unused; typeof(lhs.expand)) { @@ -349,15 +345,6 @@ private template reduceFinish(functions...) } } -private template isAssignable(T) -{ - enum isAssignable = is(typeof({ - T a; - T b; - a = b; - })); -} - private template isRoundRobin(R : RoundRobinBuffer!(C1, C2), C1, C2) { enum isRoundRobin = true; @@ -474,11 +461,11 @@ struct Task(alias fun, Args...) */ static if(__traits(isSame, fun, run)) { - alias _args[1..$] args; + alias args = _args[1..$]; } else { - alias _args args; + alias args = _args; } @@ -514,7 +501,7 @@ struct Task(alias fun, Args...) The return type of the function called by this $(D Task). This can be $(D void). */ - alias typeof(fun(_args)) ReturnType; + alias ReturnType = typeof(fun(_args)); static if(!is(ReturnType == void)) { @@ -545,9 +532,9 @@ struct Task(alias fun, Args...) enforce(this.pool !is null, "Job not submitted yet."); } - private this(Args args) + static if(Args.length > 0) { - static if(args.length > 0) + private this(Args args) { _args = args; } @@ -1405,7 +1392,7 @@ private: public: // This is used in parallel_algorithm but is too unstable to document // as public API. - size_t defaultWorkUnitSize(size_t rangeLen) const pure nothrow @safe + size_t defaultWorkUnitSize(size_t rangeLen) const @safe pure nothrow { if(this.size == 0) { @@ -1534,7 +1521,7 @@ public: ParallelForeach!R parallel(R)(R range, size_t workUnitSize) { enforce(workUnitSize > 0, "workUnitSize must be > 0."); - alias ParallelForeach!R RetType; + alias RetType = ParallelForeach!R; return RetType(this, range, workUnitSize); } @@ -1640,16 +1627,9 @@ public: auto amap(Args...)(Args args) if(isRandomAccessRange!(Args[0])) { - static if(functions.length == 1) - { - alias unaryFun!(functions[0]) fun; - } - else - { - alias adjoin!(staticMap!(unaryFun, functions)) fun; - } + alias fun = adjoin!(staticMap!(unaryFun, functions)); - alias args[0] range; + alias range = args[0]; immutable len = range.length; static if( @@ -1658,11 +1638,11 @@ public: is(MapType!(Args[0], functions) : ElementType!(Args[$ - 1])) ) { - alias args[$ - 1] buf; - alias args[0..$ - 1] args2; - alias Args[0..$ - 1] Args2; + alias buf = args[$ - 1]; + alias args2 = args[0..$ - 1]; + alias Args2 = Args[0..$ - 1]; enforce(buf.length == len, - text("Can't use a user supplied buffer that's the wrong " + text("Can't use a user supplied buffer that's the wrong ", "size. (Expected :", len, " Got: ", buf.length)); } else static if(randAssignable!(Args[$ - 1]) && Args.length > 1) @@ -1672,8 +1652,8 @@ public: else { auto buf = uninitializedArray!(MapType!(Args[0], functions)[])(len); - alias args args2; - alias Args Args2; + alias args2 = args; + alias Args2 = Args; } if(!len) return buf; @@ -1689,7 +1669,7 @@ public: auto workUnitSize = defaultWorkUnitSize(range.length); } - alias typeof(range) R; + alias R = typeof(range); if(workUnitSize > len) { @@ -1731,9 +1711,21 @@ public: immutable end = min(len, start + workUnitSize); - foreach(i; start..end) + static if (hasSlicing!R) { - buf[i] = fun(range[i]); + auto subrange = range[start..end]; + foreach(i; start..end) + { + buf[i] = fun(subrange.front); + subrange.popFront(); + } + } + else + { + foreach(i; start..end) + { + buf[i] = fun(range[i]); + } } } } @@ -1821,14 +1813,7 @@ public: { enforce(workUnitSize == size_t.max || workUnitSize <= bufSize, "Work unit size must be smaller than buffer size."); - static if(functions.length == 1) - { - alias unaryFun!(functions[0]) fun; - } - else - { - alias adjoin!(staticMap!(unaryFun, functions)) fun; - } + alias fun = adjoin!(staticMap!(unaryFun, functions)); static final class Map { @@ -1841,7 +1826,7 @@ public: is(typeof(source.bufPos)) && is(typeof(source.doBufSwap())); - alias MapType!(S, functions) E; + alias E = MapType!(S, functions); E[] buf1, buf2; S source; TaskPool pool; @@ -1852,7 +1837,7 @@ public: static if(isRandomAccessRange!S) { - alias S FromType; + alias FromType = S; void popSource() { @@ -1885,7 +1870,7 @@ public: ); } - alias typeof(source.buf1) FromType; + alias FromType = typeof(source.buf1); FromType from; // Just swap our input buffer with source's output buffer. @@ -1912,7 +1897,7 @@ public: } else { - alias ElementType!S[] FromType; + alias FromType = ElementType!S[]; // The temporary array that data is copied to before being // mapped. @@ -1937,7 +1922,7 @@ public: { size_t _length; - public @property size_t length() const pure nothrow @safe + public @property size_t length() const @safe pure nothrow { return _length; } @@ -2091,7 +2076,7 @@ public: Given a $(D source) range that is expensive to iterate over, returns an input range that asynchronously buffers the contents of $(D source) into a buffer of $(D bufSize) elements in a worker thread, - while making prevously buffered elements from a second buffer, also of size + while making previously buffered elements from a second buffer, also of size $(D bufSize), available via the range interface of the returned object. The returned range has a length iff $(D hasLength!S). $(D asyncBuf) is useful, for example, when performing expensive operations @@ -2104,7 +2089,7 @@ public: void main() { // Fetch lines of a file in a background thread - // while processing prevously fetched lines, + // while processing previously fetched lines, // dealing with byLine's buffer recycling by // eagerly duplicating every line. auto lines = File("foo.txt").byLine(); @@ -2138,7 +2123,7 @@ public: // the heap. // The element type of S. - alias ElementType!S E; // Needs to be here b/c of forward ref bugs. + alias E = ElementType!S; // Needs to be here b/c of forward ref bugs. private: E[] buf1, buf2; @@ -2153,7 +2138,7 @@ public: size_t _length; // Available if hasLength!S. - public @property size_t length() const pure nothrow @safe + public @property size_t length() const @safe pure nothrow { return _length; } @@ -2296,7 +2281,7 @@ public: Examples: --- // Fetch lines of a file in a background - // thread while processing prevously fetched + // thread while processing previously fetched // lines, without duplicating any lines. auto file = File("foo.txt"); @@ -2428,19 +2413,19 @@ public: /// auto reduce(Args...)(Args args) { - alias reduceAdjoin!functions fun; - alias reduceFinish!functions finishFun; + alias fun = reduceAdjoin!functions; + alias finishFun = reduceFinish!functions; static if(isIntegral!(Args[$ - 1])) { size_t workUnitSize = cast(size_t) args[$ - 1]; - alias args[0..$ - 1] args2; - alias Args[0..$ - 1] Args2; + alias args2 = args[0..$ - 1]; + alias Args2 = Args[0..$ - 1]; } else { - alias args args2; - alias Args Args2; + alias args2 = args; + alias Args2 = Args; } auto makeStartValue(Type)(Type e) @@ -2466,8 +2451,8 @@ public: static if(args2.length == 2) { static assert(isInputRange!(Args2[1])); - alias args2[1] range; - alias args2[0] seed; + alias range = args2[1]; + alias seed = args2[0]; enum explicitSeed = true; static if(!is(typeof(workUnitSize))) @@ -2478,7 +2463,7 @@ public: else { static assert(args2.length == 1); - alias args2[0] range; + alias range = args2[0]; static if(!is(typeof(workUnitSize))) { @@ -2493,8 +2478,8 @@ public: range.popFront(); } - alias typeof(seed) E; - alias typeof(range) R; + alias E = typeof(seed); + alias R = typeof(range); E reduceOnRange(R range, size_t lowerBound, size_t upperBound) { @@ -2596,7 +2581,7 @@ public: immutable size_t nWorkUnits = (len / workUnitSize) + ((len % workUnitSize == 0) ? 0 : 1); assert(nWorkUnits * workUnitSize >= len); - alias Task!(run, typeof(&reduceOnRange), R, size_t, size_t) RTask; + alias RTask = Task!(run, typeof(&reduceOnRange), R, size_t, size_t); RTask[] tasks; // Can't use alloca() due to Bug 3753. Use a fixed buffer @@ -2965,7 +2950,7 @@ public: The proper way to instantiate this object is to call $(D WorkerLocalStorage.toRange). Once instantiated, this object behaves - as a finite random-access range with assignable, lvalue elemends and + as a finite random-access range with assignable, lvalue elements and a length equal to the number of worker threads in the $(D TaskPool) that created it plus 1. */ @@ -3078,7 +3063,7 @@ public: a call to $(D Task.workForce), $(D Task.yieldForce) or $(D Task.spinForce) causes them to be executed. - Use only if you have waitied on every $(D Task) and therefore know the + Use only if you have waited on every $(D Task) and therefore know the queue is empty, or if you speculatively executed some tasks and no longer need the results. */ @@ -3366,7 +3351,7 @@ private void submitAndExecute( { immutable nThreads = pool.size + 1; - alias typeof(scopedTask(doIt)) PTask; + alias PTask = typeof(scopedTask(doIt)); import core.stdc.stdlib; import core.stdc.string : memcpy; @@ -3625,7 +3610,7 @@ enum string parallelApplyMixinInputRange = q{ static if(hasLvalueElements!R) { - alias ElementType!R*[] Temp; + alias Temp = ElementType!R*[]; Temp temp; // Returns: The previous value of nPopped. @@ -3655,7 +3640,7 @@ enum string parallelApplyMixinInputRange = q{ else { - alias ElementType!R[] Temp; + alias Temp = ElementType!R[]; Temp temp; // Returns: The previous value of nPopped. @@ -3788,17 +3773,17 @@ private struct ParallelForeach(R) TaskPool pool; R range; size_t workUnitSize; - alias ElementType!R E; + alias E = ElementType!R; static if(hasLvalueElements!R) { - alias int delegate(ref E) NoIndexDg; - alias int delegate(size_t, ref E) IndexDg; + alias NoIndexDg = int delegate(ref E); + alias IndexDg = int delegate(size_t, ref E); } else { - alias int delegate(E) NoIndexDg; - alias int delegate(size_t, E) IndexDg; + alias NoIndexDg = int delegate(E); + alias IndexDg = int delegate(size_t, E); } int opApply(scope NoIndexDg dg) @@ -3837,8 +3822,8 @@ private struct RoundRobinBuffer(C1, C2) { // No need for constraints because they're already checked for in asyncBuf. - alias ParameterTypeTuple!(C1.init)[0] Array; - alias typeof(Array.init[0]) T; + alias Array = ParameterTypeTuple!(C1.init)[0]; + alias T = typeof(Array.init[0]); T[][] bufs; size_t index; @@ -3898,7 +3883,7 @@ private struct RoundRobinBuffer(C1, C2) primed = false; } - bool empty() @property const pure nothrow @safe + bool empty() @property const @safe pure nothrow { return _empty; } diff --git a/libphobos/src/std/path.d b/libphobos/src/std/path.d index 4e2b15aff..93c34f869 100644 --- a/libphobos/src/std/path.d +++ b/libphobos/src/std/path.d @@ -39,7 +39,7 @@ Thomas Kühne, $(WEB erdani.org, Andrei Alexandrescu) Copyright: - Copyright (c) 2000–2011, the authors. All rights reserved. + Copyright (c) 2000-2014, the authors. All rights reserved. License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0) Source: @@ -95,7 +95,7 @@ else static assert (0, "unsupported platform"); On Windows, this includes both $(D `\`) and $(D `/`). On POSIX, it's just $(D `/`). */ -bool isDirSeparator(dchar c) @safe pure nothrow +bool isDirSeparator(dchar c) @safe pure nothrow @nogc { if (c == '/') return true; version(Windows) if (c == '\\') return true; @@ -109,7 +109,7 @@ bool isDirSeparator(dchar c) @safe pure nothrow the drive letter from the rest of the path. On POSIX, this always returns false. */ -private bool isDriveSeparator(dchar c) @safe pure nothrow +private bool isDriveSeparator(dchar c) @safe pure nothrow @nogc { version(Windows) return c == ':'; else return false; @@ -117,19 +117,20 @@ private bool isDriveSeparator(dchar c) @safe pure nothrow /* Combines the isDirSeparator and isDriveSeparator tests. */ -version(Windows) private bool isSeparator(dchar c) @safe pure nothrow +version(Windows) private bool isSeparator(dchar c) @safe pure nothrow @nogc { return isDirSeparator(c) || isDriveSeparator(c); } -version(Posix) private alias isDirSeparator isSeparator; +version(Posix) private alias isSeparator = isDirSeparator; /* Helper function that determines the position of the last drive/directory separator in a string. Returns -1 if none is found. */ -private ptrdiff_t lastSeparator(C)(in C[] path) @safe pure nothrow - if (isSomeChar!C) +private ptrdiff_t lastSeparator(R)(const R path) + if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + isNarrowString!R) { auto i = (cast(ptrdiff_t) path.length) - 1; while (i >= 0 && !isSeparator(path[i])) --i; @@ -139,13 +140,17 @@ private ptrdiff_t lastSeparator(C)(in C[] path) @safe pure nothrow version (Windows) { - private bool isUNC(C)(in C[] path) @safe pure nothrow if (isSomeChar!C) + private bool isUNC(R)(const R path) + if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + isNarrowString!R) { return path.length >= 3 && isDirSeparator(path[0]) && isDirSeparator(path[1]) && !isDirSeparator(path[2]); } - private ptrdiff_t uncRootLength(C)(in C[] path) @safe pure nothrow if (isSomeChar!C) + private ptrdiff_t uncRootLength(R)(const R path) + if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + isNarrowString!R) in { assert (isUNC(path)); } body { @@ -164,12 +169,16 @@ version (Windows) return i; } - private bool hasDrive(C)(in C[] path) @safe pure nothrow if (isSomeChar!C) + private bool hasDrive(R)(const R path) + if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + isNarrowString!R) { return path.length >= 2 && isDriveSeparator(path[1]); } - private bool isDriveRoot(C)(in C[] path) @safe pure nothrow if (isSomeChar!C) + private bool isDriveRoot(R)(const R path) + if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + isNarrowString!R) { return path.length >= 3 && isDriveSeparator(path[1]) && isDirSeparator(path[2]); @@ -180,24 +189,27 @@ version (Windows) /* Helper functions that strip leading/trailing slashes and backslashes from a path. */ -private inout(C)[] ltrimDirSeparators(C)(inout(C)[] path) @safe pure nothrow - if (isSomeChar!C) +private auto ltrimDirSeparators(R)(inout R path) + if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || + isNarrowString!R) { int i = 0; while (i < path.length && isDirSeparator(path[i])) ++i; - return path[i .. $]; + return path[i .. path.length]; } -private inout(C)[] rtrimDirSeparators(C)(inout(C)[] path) @safe pure nothrow - if (isSomeChar!C) +private auto rtrimDirSeparators(R)(inout R path) + if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || + isNarrowString!R) { auto i = (cast(ptrdiff_t) path.length) - 1; while (i >= 0 && isDirSeparator(path[i])) --i; return path[0 .. i+1]; } -private inout(C)[] trimDirSeparators(C)(inout(C)[] path) @safe pure nothrow - if (isSomeChar!C) +private auto trimDirSeparators(R)(inout R path) + if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + isNarrowString!R) { return ltrimDirSeparators(rtrimDirSeparators(path)); } @@ -257,10 +269,10 @@ else static assert (0); Note: This function $(I only) strips away the specified suffix, which - doesn't necessarily have to represent an extension. If you want - to remove the extension from a path, regardless of what the extension + doesn't necessarily have to represent an extension. + To remove the extension from a path, regardless of what the extension is, use $(LREF stripExtension). - If you want the filename without leading directories and without + To obtain the filename without leading directories and without an extension, combine the functions like this: --- assert (baseName(stripExtension("dir/file.ext")) == "file"); @@ -272,24 +284,27 @@ else static assert (0); the POSIX requirements for the 'basename' shell utility) (with suitable adaptations for Windows paths). */ -inout(C)[] baseName(C)(inout(C)[] path) - @trusted pure //TODO: nothrow (BUG 5700) - if (isSomeChar!C) +auto baseName(R)(R path) + if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || + is(StringTypeOf!R)) { - auto p1 = stripDrive(path); + auto p1 = stripDrive!(BaseOf!R)(path); if (p1.empty) { - version (Windows) if (isUNC(path)) + version (Windows) if (isUNC!(BaseOf!R)(path)) { - return cast(typeof(return)) dirSeparator.dup; + return path[0..1]; } - return null; + static if (is(StringTypeOf!R)) + return StringTypeOf!R.init[]; // which is null + else + return p1; // which is empty } auto p2 = rtrimDirSeparators(p1); if (p2.empty) return p1[0 .. 1]; - return p2[lastSeparator(p2)+1 .. $]; + return p2[lastSeparator(p2)+1 .. p2.length]; } /// ditto @@ -327,6 +342,13 @@ unittest assert (baseName!(CaseSensitive.yes)("file.ext", ".EXT") == "file.ext"); assert (baseName!(CaseSensitive.no)("file.ext", ".EXT") == "file"); + { + auto r = MockRange!(immutable(char))(`dir/file.ext`); + auto s = r.baseName(); + foreach (i, c; `file`) + assert(s[i] == c); + } + version (Windows) { assert (baseName(`dir\file.ext`) == `file.ext`); @@ -346,12 +368,20 @@ unittest assert (baseName(`\\server\share\file`) == `file`); assert (baseName(`\\server\share\`) == `\`); assert (baseName(`\\server\share`) == `\`); + + auto r = MockRange!(immutable(char))(`\\server\share`); + auto s = r.baseName(); + foreach (i, c; `\`) + assert(s[i] == c); } assert (baseName(stripExtension("dir/file.ext")) == "file"); static assert (baseName("dir/file.ext") == "file.ext"); static assert (baseName("dir/file.ext", ".ext") == "file"); + + static struct DirEntry { string s; alias s this; } + assert(baseName(DirEntry("dir/file.ext")) == "file.ext"); } @@ -479,7 +509,7 @@ unittest } --- */ -inout(C)[] rootName(C)(inout(C)[] path) @safe pure nothrow if (isSomeChar!C) +inout(C)[] rootName(C)(inout(C)[] path) @safe pure nothrow @nogc if (isSomeChar!C) { if (path.empty) return null; @@ -542,7 +572,7 @@ unittest } --- */ -inout(C)[] driveName(C)(inout(C)[] path) @safe pure nothrow +inout(C)[] driveName(C)(inout(C)[] path) @safe pure nothrow @nogc if (isSomeChar!C) { version (Windows) @@ -588,12 +618,14 @@ unittest } --- */ -inout(C)[] stripDrive(C)(inout(C)[] path) @safe pure nothrow if (isSomeChar!C) +auto stripDrive(R)(inout R path) + if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || + is(StringTypeOf!R)) { version(Windows) { - if (hasDrive(path)) return path[2 .. $]; - else if (isUNC(path)) return path[uncRootLength(path) .. $]; + if (hasDrive!(BaseOf!R)(path)) return path[2 .. path.length]; + else if (isUNC!(BaseOf!R)(path)) return path[uncRootLength!(BaseOf!R)(path) .. path.length]; } return path; } @@ -606,10 +638,20 @@ unittest assert (stripDrive(`d:\dir\file`) == `\dir\file`); assert (stripDrive(`\\server\share\dir\file`) == `\dir\file`); static assert (stripDrive(`d:\dir\file`) == `\dir\file`); + + auto r = MockRange!(immutable(char))(`d:\dir\file`); + auto s = r.stripDrive(); + foreach (i, c; `\dir\file`) + assert(s[i] == c); } version(Posix) { assert (stripDrive(`d:\dir\file`) == `d:\dir\file`); + + auto r = MockRange!(immutable(char))(`d:\dir\file`); + auto s = r.stripDrive(); + foreach (i, c; `d:\dir\file`) + assert(s[i] == c); } } @@ -619,8 +661,9 @@ unittest /* Helper function that returns the position of the filename/extension separator dot in path. If not found, returns -1. */ -private ptrdiff_t extSeparatorPos(C)(in C[] path) @safe pure nothrow - if (isSomeChar!C) +private ptrdiff_t extSeparatorPos(R)(const R path) + if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || + isNarrowString!R) { auto i = (cast(ptrdiff_t) path.length) - 1; while (i >= 0 && !isSeparator(path[i])) @@ -632,8 +675,6 @@ private ptrdiff_t extSeparatorPos(C)(in C[] path) @safe pure nothrow } - - /** Returns the _extension part of a file name, including the dot. If there is no _extension, $(D null) is returned. @@ -648,11 +689,19 @@ private ptrdiff_t extSeparatorPos(C)(in C[] path) @safe pure nothrow assert (extension(".file.ext") == ".ext"); --- */ -inout(C)[] extension(C)(inout(C)[] path) @safe pure nothrow if (isSomeChar!C) +auto extension(R)(R path) + if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) || + is(StringTypeOf!R)) { - auto i = extSeparatorPos(path); - if (i == -1) return null; - else return path[i .. $]; + auto i = extSeparatorPos!(BaseOf!R)(path); + if (i == -1) + { + static if (is(StringTypeOf!R)) + return StringTypeOf!R.init[]; // which is null + else + return path[0 .. 0]; + } + else return path[i .. path.length]; } @@ -691,12 +740,22 @@ unittest static assert (extension("file").empty); static assert (extension("file.ext") == ".ext"); + + { + auto r = MockRange!(immutable(char))(`file.ext1.ext2`); + auto s = r.extension(); + foreach (i, c; `.ext2`) + assert(s[i] == c); + } + + static struct DirEntry { string s; alias s this; } + assert (extension(DirEntry("file")).empty); } -/** Returns the path with the extension stripped off. +/** Returns slice of path[] with the extension stripped off. Examples: --- @@ -709,7 +768,7 @@ unittest assert (stripExtension("dir/file.ext") == "dir/file"); --- */ -inout(C)[] stripExtension(C)(inout(C)[] path) @safe pure nothrow +inout(C)[] stripExtension(C)(inout(C)[] path) @safe pure nothrow @nogc if (isSomeChar!C) { auto i = extSeparatorPos(path); @@ -1543,7 +1602,7 @@ auto pathSplitter(C)(const(C)[] path) @safe pure nothrow { static struct PathSplitter { - @safe pure nothrow: + @safe pure nothrow @nogc: @property bool empty() const { return _empty; } @property const(C)[] front() const @@ -1768,11 +1827,13 @@ unittest } --- */ -bool isRooted(C)(in C[] path) @safe pure nothrow if (isSomeChar!C) +bool isRooted(R)(inout R path) + if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + is(StringTypeOf!R)) { if (path.length >= 1 && isDirSeparator(path[0])) return true; version (Posix) return false; - else version (Windows) return isAbsolute(path); + else version (Windows) return isAbsolute!(BaseOf!R)(path); } @@ -1795,6 +1856,9 @@ unittest static assert (isRooted("/foo")); static assert (!isRooted("foo")); + + static struct DirEntry { string s; alias s this; } + assert (!isRooted(DirEntry("foo"))); } @@ -1831,16 +1895,25 @@ unittest } --- */ -version (StdDdoc) bool isAbsolute(C)(in C[] path) @safe pure nothrow - if (isSomeChar!C); - -else version (Windows) bool isAbsolute(C)(in C[] path) @safe pure nothrow - if (isSomeChar!C) +version (StdDdoc) { - return isDriveRoot(path) || isUNC(path); + bool isAbsolute(R)(const R path) @safe pure nothrow @nogc + if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + is(StringTypeOf!R)); +} +else version (Windows) +{ + bool isAbsolute(R)(const R path) @safe pure nothrow @nogc + if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + is(StringTypeOf!R)) + { + return isDriveRoot!(BaseOf!R)(path) || isUNC!(BaseOf!R)(path); + } +} +else version (Posix) +{ + alias isAbsolute = isRooted; } - -else version (Posix) alias isRooted isAbsolute; unittest @@ -1867,6 +1940,14 @@ unittest assert (!isAbsolute("d:foo")); static assert (isAbsolute(`d:\foo`)); } + + { + auto r = MockRange!(immutable(char))(`../foo`); + assert(!r.isAbsolute()); + } + + static struct DirEntry { string s; alias s this; } + assert(!isAbsolute(DirEntry("foo"))); } @@ -2489,7 +2570,9 @@ unittest On POSIX, $(D filename) may not contain a forward slash ($(D '/')) or the null character ($(D '\0')). */ -bool isValidFilename(C)(in C[] filename) @safe pure nothrow if (isSomeChar!C) +bool isValidFilename(R)(R filename) + if (isRandomAccessRange!R && isSomeChar!(ElementType!R) || + is(StringTypeOf!R)) { import core.stdc.stdio; if (filename.length == 0 || filename.length >= FILENAME_MAX) return false; @@ -2512,7 +2595,9 @@ bool isValidFilename(C)(in C[] filename) @safe pure nothrow if (isSomeChar!C) case '?': case '*': return false; + default: + break; } } else version (Posix) @@ -2523,7 +2608,8 @@ bool isValidFilename(C)(in C[] filename) @safe pure nothrow if (isSomeChar!C) } version (Windows) { - if (filename[$-1] == '.' || filename[$-1] == ' ') return false; + auto last = filename[filename.length - 1]; + if (last == '.' || last == ' ') return false; } // All criteria passed @@ -2549,6 +2635,14 @@ unittest foreach (fn; invalid) assert (!isValidFilename(to!T(fn))); } + + { + auto r = MockRange!(immutable(char))(`dir/file.d`); + assert(!isValidFilename(r)); + } + + static struct DirEntry { string s; alias s this; } + assert(isValidFilename(DirEntry("file.ext"))); } @@ -2924,3 +3018,42 @@ unittest assert(expandTilde("~Idontexist/hey") == "~Idontexist/hey"); } } + +version (unittest) +{ + /* Define a mock RandomAccessRange to use for unittesting. + */ + + struct MockRange(C) + { + this(C[] array) { this.array = array; } + const + { + @property size_t length() { return array.length; } + @property bool empty() { return array.length == 0; } + @property C front() { return array[0]; } + @property C back() { return array[$ - 1]; } + @property size_t opDollar() { return length; } + C opIndex(size_t i) { return array[i]; } + } + void popFront() { array = array[1 .. $]; } + void popBack() { array = array[0 .. $-1]; } + MockRange!C opSlice( size_t lwr, size_t upr) const + { + return MockRange!C(array[lwr .. upr]); + } + @property MockRange save() { return this; } + private: + C[] array; + } + + static assert( isRandomAccessRange!(MockRange!(const(char))) ); +} + +private template BaseOf(R) +{ + static if (isRandomAccessRange!R && isSomeChar!(ElementType!R)) + alias BaseOf = R; + else + alias BaseOf = StringTypeOf!R; +} diff --git a/libphobos/src/std/process.d b/libphobos/src/std/process.d index 30eab76a5..1dc42e087 100644 --- a/libphobos/src/std/process.d +++ b/libphobos/src/std/process.d @@ -124,26 +124,12 @@ version (Windows) version (DMC_RUNTIME) { } else { import core.stdc.stdint; - extern(C) - { - int _fileno(FILE* stream); - HANDLE _get_osfhandle(int fd); - int _open_osfhandle(HANDLE osfhandle, int flags); - FILE* _fdopen(int fd, const (char)* mode); - int _close(int fd); - } enum { STDIN_FILENO = 0, STDOUT_FILENO = 1, STDERR_FILENO = 2, } - enum - { - _O_RDONLY = 0x0000, - _O_APPEND = 0x0004, - _O_TEXT = 0x4000, - } } } @@ -285,6 +271,9 @@ stderr = The standard error stream of the child process. env = Additional environment variables for the child process. config = Flags that control process creation. See $(LREF Config) for an overview of available flags. +workDir = The working directory for the new process. + By default the child process inherits the parent's working + directory. Returns: A $(LREF Pid) object that corresponds to the spawned process. @@ -300,18 +289,20 @@ Pid spawnProcess(in char[][] args, File stdout = std.stdio.stdout, File stderr = std.stdio.stderr, const string[string] env = null, - Config config = Config.none) + Config config = Config.none, + in char[] workDir = null) @trusted // TODO: Should be @safe { version (Windows) auto args2 = escapeShellArguments(args); else version (Posix) alias args2 = args; - return spawnProcessImpl(args2, stdin, stdout, stderr, env, config); + return spawnProcessImpl(args2, stdin, stdout, stderr, env, config, workDir); } /// ditto Pid spawnProcess(in char[][] args, const string[string] env, - Config config = Config.none) + Config config = Config.none, + in char[] workDir = null) @trusted // TODO: Should be @safe { return spawnProcess(args, @@ -319,7 +310,8 @@ Pid spawnProcess(in char[][] args, std.stdio.stdout, std.stdio.stderr, env, - config); + config, + workDir); } /// ditto @@ -328,20 +320,22 @@ Pid spawnProcess(in char[] program, File stdout = std.stdio.stdout, File stderr = std.stdio.stderr, const string[string] env = null, - Config config = Config.none) + Config config = Config.none, + in char[] workDir = null) @trusted { return spawnProcess((&program)[0 .. 1], - stdin, stdout, stderr, env, config); + stdin, stdout, stderr, env, config, workDir); } /// ditto Pid spawnProcess(in char[] program, const string[string] env, - Config config = Config.none) + Config config = Config.none, + in char[] workDir = null) @trusted { - return spawnProcess((&program)[0 .. 1], env, config); + return spawnProcess((&program)[0 .. 1], env, config, workDir); } /* @@ -356,7 +350,8 @@ private Pid spawnProcessImpl(in char[][] args, File stdout, File stderr, const string[string] env, - Config config) + Config config, + in char[] workDir) @trusted // TODO: Should be @safe { import core.exception: RangeError; @@ -372,7 +367,7 @@ private Pid spawnProcessImpl(in char[][] args, { name = searchPathFor(name); if (name is null) - throw new ProcessException(text("Executable file not found: ", name)); + throw new ProcessException(text("Executable file not found: ", args[0])); } // Convert program name and arguments to C-style strings. @@ -384,6 +379,25 @@ private Pid spawnProcessImpl(in char[][] args, // Prepare environment. auto envz = createEnv(env, !(config & Config.newEnv)); + // Open the working directory. + // We use open in the parent and fchdir in the child + // so that most errors (directory doesn't exist, not a directory) + // can be propagated as exceptions before forking. + int workDirFD = 0; + scope(exit) if (workDirFD > 0) close(workDirFD); + if (workDir) + { + import core.sys.posix.fcntl; + workDirFD = open(toStringz(workDir), O_RDONLY); + if (workDirFD < 0) + throw ProcessException.newFromErrno("Failed to open working directory"); + stat_t s; + if (fstat(workDirFD, &s) < 0) + throw ProcessException.newFromErrno("Failed to stat working directory"); + if (!S_ISDIR(s.st_mode)) + throw new ProcessException("Not a directory: " ~ cast(string)workDir); + } + // Get the file descriptors of the streams. // These could potentially be invalid, but that is OK. If so, later calls // to dup2() and close() will just silently fail without causing any harm. @@ -398,6 +412,21 @@ private Pid spawnProcessImpl(in char[][] args, { // Child process + // Set the working directory. + if (workDirFD) + { + if (fchdir(workDirFD) < 0) + { + // Fail. It is dangerous to run a program + // in an unexpected working directory. + core.sys.posix.stdio.perror("spawnProcess(): " ~ + "Failed to set working directory"); + core.sys.posix.unistd._exit(1); + assert(0); + } + close(workDirFD); + } + // Redirect streams and close the old file descriptors. // In the case that stderr is redirected to stdout, we need // to backup the file descriptor since stdout may be redirected @@ -462,13 +491,15 @@ private Pid spawnProcessImpl(in char[] commandLine, File stdout, File stderr, const string[string] env, - Config config) + Config config, + in char[] workDir) @trusted { import core.exception: RangeError; if (commandLine.empty) throw new RangeError("Command line is empty"); auto commandz = toUTFz!(wchar*)(commandLine); + auto workDirz = workDir is null ? null : toUTFz!(wchar*)(workDir); // Prepare environment. auto envz = createEnv(env, !(config & Config.newEnv)); @@ -483,13 +514,9 @@ private Pid spawnProcessImpl(in char[] commandLine, static void prepareStream(ref File file, DWORD stdHandle, string which, out int fileDescriptor, out HANDLE handle) { - fileDescriptor = _fileno(file.getFP()); + fileDescriptor = file.isOpen ? file.fileno() : -1; if (fileDescriptor < 0) handle = GetStdHandle(stdHandle); - else - { - version (DMC_RUNTIME) handle = _fdToHandle(fileDescriptor); - else /* MSVCRT */ handle = _get_osfhandle(fileDescriptor); - } + else handle = file.windowsHandle; DWORD dwFlags; if (GetHandleInformation(handle, &dwFlags)) @@ -519,7 +546,7 @@ private Pid spawnProcessImpl(in char[] commandLine, CREATE_UNICODE_ENVIRONMENT | ((config & Config.suppressConsole) ? CREATE_NO_WINDOW : 0); if (!CreateProcessW(null, commandz, null, null, true, dwCreationFlags, - envz, null, &startinfo, &pi)) + envz, workDirz, &startinfo, &pi)) throw ProcessException.newFromLastError("Failed to spawn new process"); // figure out if we should close any of the streams @@ -808,6 +835,31 @@ unittest // Error handling in spawnProcess() assertThrown!ProcessException(spawnProcess("./rgiuhrifuheiohnmnvqweoijwf")); } +unittest // Specifying a working directory. +{ + TestScript prog = "echo foo>bar"; + + auto directory = uniqueTempPath(); + mkdir(directory); + scope(exit) rmdirRecurse(directory); + + auto pid = spawnProcess([prog.path], null, Config.none, directory); + wait(pid); + assert(exists(buildPath(directory, "bar"))); +} + +unittest // Specifying a bad working directory. +{ + TestScript prog = "echo"; + + auto directory = uniqueTempPath(); + assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory)); + + std.file.write(directory, "foo"); + scope(exit) remove(directory); + assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory)); +} + /** A variation on $(LREF spawnProcess) that runs the given _command through @@ -839,13 +891,18 @@ Pid spawnShell(in char[] command, File stdout = std.stdio.stdout, File stderr = std.stdio.stderr, const string[string] env = null, - Config config = Config.none) + Config config = Config.none, + in char[] workDir = null) @trusted // TODO: Should be @safe { version (Windows) { - auto args = escapeShellArguments(userShell, shellSwitch) - ~ " " ~ command; + // CMD does not parse its arguments like other programs. + // It does not use CommandLineToArgvW. + // Instead, it treats the first and last quote specially. + // See CMD.EXE /? for details. + auto args = escapeShellFileName(userShell) + ~ ` ` ~ shellSwitch ~ ` "` ~ command ~ `"`; } else version (Posix) { @@ -854,13 +911,14 @@ Pid spawnShell(in char[] command, args[1] = shellSwitch; args[2] = command; } - return spawnProcessImpl(args, stdin, stdout, stderr, env, config); + return spawnProcessImpl(args, stdin, stdout, stderr, env, config, workDir); } /// ditto Pid spawnShell(in char[] command, const string[string] env, - Config config = Config.none) + Config config = Config.none, + in char[] workDir = null) @trusted // TODO: Should be @safe { return spawnShell(command, @@ -868,7 +926,8 @@ Pid spawnShell(in char[] command, std.stdio.stdout, std.stdio.stderr, env, - config); + config, + workDir); } unittest @@ -884,12 +943,29 @@ unittest auto env = ["foo" : "bar"]; assert (wait(spawnShell(cmd~redir, env)) == 0); auto f = File(tmpFile, "a"); + version(Win64) f.seek(0, SEEK_END); // MSVCRT probably seeks to the end when writing, not before assert (wait(spawnShell(cmd, std.stdio.stdin, f, std.stdio.stderr, env)) == 0); f.close(); auto output = std.file.readText(tmpFile); assert (output == "bar\nbar\n" || output == "bar\r\nbar\r\n"); } +version (Windows) +unittest +{ + TestScript prog = "echo %0 %*"; + auto outputFn = uniqueTempPath(); + scope(exit) if (exists(outputFn)) remove(outputFn); + auto args = [`a b c`, `a\b\c\`, `a"b"c"`]; + auto result = executeShell( + escapeShellCommand([prog.path] ~ args) + ~ " > " ~ + escapeShellFileName(outputFn)); + assert(result.status == 0); + auto args2 = outputFn.readText().strip().parseCommandLine()[1..$]; + assert(args == args2, text(args2)); +} + /** Flags that control the behaviour of $(LREF spawnProcess) and @@ -1183,7 +1259,7 @@ A non-blocking version of $(LREF wait). If the process associated with $(D pid) has already terminated, $(D tryWait) has the exact same effect as $(D wait). -In this case, it returns a struct where the $(D terminated) field +In this case, it returns a tuple where the $(D terminated) field is set to $(D true) and the $(D status) field has the same interpretation as the return value of $(D wait). @@ -1197,10 +1273,7 @@ get the exit code, but also to avoid the process becoming a "zombie" when it finally terminates. (See $(LREF wait) for details). Returns: -A $(D struct) which contains the fields $(D bool terminated) -and $(D int status). (This will most likely change to become a -$(D std.typecons.Tuple!(bool,"terminated",int,"status")) in the future, -but a compiler bug currently prevents this.) +An $(D std.typecons.Tuple!(bool, "terminated", int, "status")). Throws: $(LREF ProcessException) on failure. @@ -1227,14 +1300,10 @@ by the time we reach the end of the scope. */ auto tryWait(Pid pid) @safe { - struct TryWaitResult - { - bool terminated; - int status; - } + import std.typecons : Tuple; assert(pid !is null, "Called tryWait on a null Pid."); auto code = pid.performWait(false); - return TryWaitResult(pid._processID == Pid.terminated, code); + return Tuple!(bool, "terminated", int, "status")(pid._processID == Pid.terminated, code); } // unittest: This function is tested together with kill() below. @@ -1304,13 +1373,10 @@ void kill(Pid pid, int codeOrSignal) version (Windows) { if (codeOrSignal < 0) throw new ProcessException("Invalid exit code"); - version (Win32) - { - // On Windows XP, TerminateProcess() appears to terminate the - // *current* process if it is passed an invalid handle... - if (pid.osHandle == INVALID_HANDLE_VALUE) - throw new ProcessException("Invalid process handle"); - } + // On Windows, TerminateProcess() appears to terminate the + // *current* process if it is passed an invalid handle... + if (pid.osHandle == INVALID_HANDLE_VALUE) + throw new ProcessException("Invalid process handle"); if (!TerminateProcess(pid.osHandle, codeOrSignal)) throw ProcessException.newFromLastError(); } @@ -1420,69 +1486,24 @@ Pipe pipe() @trusted //TODO: @safe 0); } - // Create file descriptors from the handles - version (DMC_RUNTIME) + scope(failure) { - auto readFD = _handleToFD(readHandle, FHND_DEVICE); - auto writeFD = _handleToFD(writeHandle, FHND_DEVICE); - } - else // MSVCRT - { - auto readFD = _open_osfhandle(readHandle, _O_RDONLY); - auto writeFD = _open_osfhandle(writeHandle, _O_APPEND); - } - version (DMC_RUNTIME) alias .close _close; - if (readFD == -1 || writeFD == -1) - { - // Close file descriptors, then throw. - if (readFD >= 0) _close(readFD); - else CloseHandle(readHandle); - if (writeFD >= 0) _close(writeFD); - else CloseHandle(writeHandle); - throw new StdioException("Error creating pipe"); + CloseHandle(readHandle); + CloseHandle(writeHandle); } - // Create FILE pointers from the file descriptors - Pipe p; - version (DMC_RUNTIME) - { - // This is a re-implementation of DMC's fdopen, but without the - // mucking with the file descriptor. POSIX standard requires the - // new fdopen'd file to retain the given file descriptor's - // position. - FILE * local_fdopen(int fd, const(char)* mode) - { - auto fp = core.stdc.stdio.fopen("NUL", mode); - if(!fp) return null; - FLOCK(fp); - auto iob = cast(_iobuf*)fp; - .close(iob._file); - iob._file = fd; - iob._flag &= ~_IOTRAN; - FUNLOCK(fp); - return fp; - } - - auto readFP = local_fdopen(readFD, "r"); - auto writeFP = local_fdopen(writeFD, "a"); - } - else // MSVCRT + try { - auto readFP = _fdopen(readFD, "r"); - auto writeFP = _fdopen(writeFD, "a"); + Pipe p; + p._read .windowsHandleOpen(readHandle , "r"); + p._write.windowsHandleOpen(writeHandle, "a"); + return p; } - if (readFP == null || writeFP == null) + catch (Exception e) { - // Close streams, then throw. - if (readFP != null) fclose(readFP); - else _close(readFD); - if (writeFP != null) fclose(writeFP); - else _close(writeFD); - throw new StdioException("Cannot open pipe"); + throw new StdioException("Error attaching pipe (" ~ e.msg ~ ")", + 0); } - p._read = File(readFP, null); - p._write = File(writeFP, null); - return p; } @@ -1563,6 +1584,9 @@ env = Additional environment variables for the child process. config = Flags that control process creation. See $(LREF Config) for an overview of available flags, and note that the $(D retainStd...) flags have no effect in this function. +workDir = The working directory for the new process. + By default the child process inherits the parent's working + directory. Returns: A $(LREF ProcessPipes) object which contains $(XREF stdio,File) @@ -1591,30 +1615,33 @@ foreach (line; pipes.stderr.byLine) errors ~= line.idup; ProcessPipes pipeProcess(in char[][] args, Redirect redirect = Redirect.all, const string[string] env = null, - Config config = Config.none) + Config config = Config.none, + in char[] workDir = null) @trusted //TODO: @safe { - return pipeProcessImpl!spawnProcess(args, redirect, env, config); + return pipeProcessImpl!spawnProcess(args, redirect, env, config, workDir); } /// ditto ProcessPipes pipeProcess(in char[] program, Redirect redirect = Redirect.all, const string[string] env = null, - Config config = Config.none) + Config config = Config.none, + in char[] workDir = null) @trusted { - return pipeProcessImpl!spawnProcess(program, redirect, env, config); + return pipeProcessImpl!spawnProcess(program, redirect, env, config, workDir); } /// ditto ProcessPipes pipeShell(in char[] command, Redirect redirect = Redirect.all, const string[string] env = null, - Config config = Config.none) + Config config = Config.none, + in char[] workDir = null) @safe { - return pipeProcessImpl!spawnShell(command, redirect, env, config); + return pipeProcessImpl!spawnShell(command, redirect, env, config, workDir); } // Implementation of the pipeProcess() family of functions. @@ -1622,7 +1649,8 @@ private ProcessPipes pipeProcessImpl(alias spawnFunc, Cmd) (Cmd command, Redirect redirectFlags, const string[string] env = null, - Config config = Config.none) + Config config = Config.none, + in char[] workDir = null) @trusted //TODO: @safe { File childStdin, childStdout, childStderr; @@ -1689,7 +1717,7 @@ private ProcessPipes pipeProcessImpl(alias spawnFunc, Cmd) config &= ~(Config.retainStdin | Config.retainStdout | Config.retainStderr); pipes._pid = spawnFunc(command, childStdin, childStdout, childStderr, - env, config); + env, config, workDir); return pipes; } @@ -1770,7 +1798,7 @@ unittest pp.stdin.flush(); assert (wait(pp.pid) == 1); - pp = pipeShell(prog.path~" AAAAA BBB", + pp = pipeShell(escapeShellCommand(prog.path, "AAAAA", "BBB"), Redirect.stdin | Redirect.stdoutToStderr | Redirect.stderr); pp.stdin.writeln("ab"); pp.stdin.flush(); @@ -1906,12 +1934,12 @@ config = Flags that control process creation. See $(LREF Config) $(D retainStd...) flags have no effect in this function. maxOutput = The maximum number of bytes of output that should be captured. +workDir = The working directory for the new process. + By default the child process inherits the parent's working + directory. Returns: -A $(D struct) which contains the fields $(D int status) and -$(D string output). (This will most likely change to become a -$(D std.typecons.Tuple!(int,"status",string,"output")) in the future, -but a compiler bug currently prevents this.) +An $(D std.typecons.Tuple!(int, "status", string, "output")). POSIX_specific: If the process is terminated by a signal, the $(D status) field of @@ -1925,30 +1953,33 @@ $(XREF stdio,StdioException) on failure to capture output. auto execute(in char[][] args, const string[string] env = null, Config config = Config.none, - size_t maxOutput = size_t.max) + size_t maxOutput = size_t.max, + in char[] workDir = null) @trusted //TODO: @safe { - return executeImpl!pipeProcess(args, env, config, maxOutput); + return executeImpl!pipeProcess(args, env, config, maxOutput, workDir); } /// ditto auto execute(in char[] program, const string[string] env = null, Config config = Config.none, - size_t maxOutput = size_t.max) + size_t maxOutput = size_t.max, + in char[] workDir = null) @trusted //TODO: @safe { - return executeImpl!pipeProcess(program, env, config, maxOutput); + return executeImpl!pipeProcess(program, env, config, maxOutput, workDir); } /// ditto auto executeShell(in char[] command, const string[string] env = null, Config config = Config.none, - size_t maxOutput = size_t.max) + size_t maxOutput = size_t.max, + in char[] workDir = null) @trusted //TODO: @safe { - return executeImpl!pipeShell(command, env, config, maxOutput); + return executeImpl!pipeShell(command, env, config, maxOutput, workDir); } // Does the actual work for execute() and executeShell(). @@ -1956,10 +1987,13 @@ private auto executeImpl(alias pipeFunc, Cmd)( Cmd commandLine, const string[string] env = null, Config config = Config.none, - size_t maxOutput = size_t.max) + size_t maxOutput = size_t.max, + in char[] workDir = null) { + import std.typecons : Tuple; + auto p = pipeFunc(commandLine, Redirect.stdout | Redirect.stderrToStdout, - env, config); + env, config, workDir); auto a = appender!(ubyte[])(); enum size_t defaultChunkSize = 4096; @@ -1980,8 +2014,7 @@ private auto executeImpl(alias pipeFunc, Cmd)( // Exhaust the stream, if necessary. foreach (ubyte[] chunk; p.stdout.byChunk(defaultChunkSize)) { } - struct ProcessOutput { int status; string output; } - return ProcessOutput(wait(p.pid), cast(string) a.data); + return Tuple!(int, "status", string, "output")(wait(p.pid), cast(string) a.data); } unittest @@ -2017,6 +2050,19 @@ unittest assert (r3.output.empty); } +unittest +{ + import std.typecons : Tuple; + void foo() //Just test the compilation + { + auto ret1 = execute(["dummy", "arg"]); + auto ret2 = executeShell("dummy arg"); + static assert(is(typeof(ret1) == typeof(ret2))); + + Tuple!(int, string) ret3 = execute(["dummy", "arg"]); + } +} + /// An exception that signals a problem with starting or waiting for a process. class ProcessException : Exception @@ -2144,7 +2190,9 @@ version (unittest) private string uniqueTempPath() { import std.file, std.uuid; - return buildPath(tempDir(), randomUUID().toString()); + // Path should contain spaces to test escaping whitespace + return buildPath(tempDir(), "std.process temporary file " ~ + randomUUID().toString()); } @@ -2198,7 +2246,25 @@ characters (NUL on all platforms, as well as CR and LF on Windows). string escapeShellCommand(in char[][] args...) //TODO: @safe pure nothrow { - return escapeShellCommandString(escapeShellArguments(args)); + if (args.empty) + return null; + version (Windows) + { + // Do not ^-escape the first argument (the program path), + // as the shell parses it differently from parameters. + // ^-escaping a program path that contains spaces will fail. + string result = escapeShellFileName(args[0]); + if (args.length > 1) + { + result ~= " " ~ escapeShellCommandString( + escapeShellArguments(args[1..$])); + } + return result; + } + version (Posix) + { + return escapeShellCommandString(escapeShellArguments(args)); + } } unittest @@ -2211,29 +2277,29 @@ unittest TestVector[] tests = [ { - args : ["foo"], - windows : `^"foo^"`, - posix : `'foo'` + args : ["foo bar"], + windows : `"foo bar"`, + posix : `'foo bar'` }, { - args : ["foo", "hello"], - windows : `^"foo^" ^"hello^"`, - posix : `'foo' 'hello'` + args : ["foo bar", "hello"], + windows : `"foo bar" ^"hello^"`, + posix : `'foo bar' 'hello'` }, { - args : ["foo", "hello world"], - windows : `^"foo^" ^"hello world^"`, - posix : `'foo' 'hello world'` + args : ["foo bar", "hello world"], + windows : `"foo bar" ^"hello world^"`, + posix : `'foo bar' 'hello world'` }, { - args : ["foo", "hello", "world"], - windows : `^"foo^" ^"hello^" ^"world^"`, - posix : `'foo' 'hello' 'world'` + args : ["foo bar", "hello", "world"], + windows : `"foo bar" ^"hello^" ^"world^"`, + posix : `'foo bar' 'hello' 'world'` }, { - args : ["foo", `'"^\`], - windows : `^"foo^" ^"'\^"^^\\^"`, - posix : `'foo' ''\''"^\'` + args : ["foo bar", `'"^\`], + windows : `"foo bar" ^"'\^"^^\\^"`, + posix : `'foo bar' ''\''"^\'` }, ]; @@ -2406,6 +2472,17 @@ version(Windows) version(unittest) extern (Windows) wchar_t** CommandLineToArgvW(wchar_t*, int*); extern (C) size_t wcslen(in wchar *); + string[] parseCommandLine(string line) + { + LPWSTR lpCommandLine = (to!(wchar[])(line) ~ "\0"w).ptr; + int numArgs; + LPWSTR* args = CommandLineToArgvW(lpCommandLine, &numArgs); + scope(exit) LocalFree(args); + return args[0..numArgs] + .map!(arg => to!string(arg[0..wcslen(arg)])) + .array(); + } + unittest { string[] testStrings = [ @@ -2427,13 +2504,9 @@ version(Windows) version(unittest) foreach (s; testStrings) { auto q = escapeWindowsArgument(s); - LPWSTR lpCommandLine = (to!(wchar[])("Dummy.exe " ~ q) ~ "\0"w).ptr; - int numArgs; - LPWSTR* args = CommandLineToArgvW(lpCommandLine, &numArgs); - scope(exit) LocalFree(args); - assert(numArgs==2, s ~ " => " ~ q ~ " #" ~ text(numArgs-1)); - auto arg = to!string(args[1][0..wcslen(args[1])]); - assert(arg == s, s ~ " => " ~ q ~ " => " ~ arg); + auto args = parseCommandLine("Dummy.exe " ~ q); + assert(args.length==2, s ~ " => " ~ q ~ " #" ~ text(args.length-1)); + assert(args[1] == s, s ~ " => " ~ q ~ " => " ~ args[1]); } } } @@ -2975,8 +3048,8 @@ private void toAStringz(in string[] a, const(char)**az) // Incorporating idea (for spawnvp() on Posix) from Dave Fladebo -alias std.c.process._P_WAIT P_WAIT; -alias std.c.process._P_NOWAIT P_NOWAIT; +alias P_WAIT = std.c.process._P_WAIT; +alias P_NOWAIT = std.c.process._P_NOWAIT; int spawnvp(int mode, string pathname, string[] argv) { @@ -3052,11 +3125,11 @@ Lerror: } // _spawnvp private { - alias WIFSTOPPED stopped; - alias WIFSIGNALED signaled; - alias WTERMSIG termsig; - alias WIFEXITED exited; - alias WEXITSTATUS exitstatus; + alias stopped = WIFSTOPPED; + alias signaled = WIFSIGNALED; + alias termsig = WTERMSIG; + alias exited = WIFEXITED; + alias exitstatus = WEXITSTATUS; } // private } // version (Posix) @@ -3176,7 +3249,7 @@ else * writefln("Current process id: %s", getpid()); * --- */ -alias core.thread.getpid getpid; +alias getpid = core.thread.getpid; /** Runs $(D_PARAM cmd) in a shell and returns its standard output. If @@ -3362,15 +3435,11 @@ version (Windows) { import core.sys.windows.windows; - extern (Windows) - HINSTANCE ShellExecuteA(HWND hwnd, LPCSTR lpOperation, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT nShowCmd); - - pragma(lib,"shell32.lib"); void browse(string url) { - ShellExecuteA(null, "open", toStringz(url), null, null, SW_SHOWNORMAL); + ShellExecuteW(null, "open", toUTF16z(url), null, null, SW_SHOWNORMAL); } } else version (OSX) diff --git a/libphobos/src/std/random.d b/libphobos/src/std/random.d index 05c7146c4..f86cf5cee 100644 --- a/libphobos/src/std/random.d +++ b/libphobos/src/std/random.d @@ -175,7 +175,7 @@ template isSeedable(Rng) })); } -unittest +@safe pure nothrow unittest { struct NoRng { @@ -271,7 +271,7 @@ The parameters of this distribution. The random number is $(D_PARAM x (cast(ulong)a * (m-1) + c) % m == (c < a ? c - a + m : c - a)); // Check for maximum range - private static ulong gcd(ulong a, ulong b) + private static ulong gcd(ulong a, ulong b) @safe pure nothrow { while (b) { @@ -282,7 +282,7 @@ The parameters of this distribution. The random number is $(D_PARAM x return a; } - private static ulong primeFactorsOnly(ulong n) + private static ulong primeFactorsOnly(ulong n) @safe pure nothrow { ulong result = 1; ulong iter = 2; @@ -298,7 +298,7 @@ The parameters of this distribution. The random number is $(D_PARAM x return result * n; } - unittest + @safe pure nothrow unittest { static assert(primeFactorsOnly(100) == 10); //writeln(primeFactorsOnly(11)); @@ -310,7 +310,7 @@ The parameters of this distribution. The random number is $(D_PARAM x } private static bool properLinearCongruentialParameters(ulong m, - ulong a, ulong c) + ulong a, ulong c) @safe pure nothrow { if (m == 0) { @@ -344,7 +344,7 @@ The parameters of this distribution. The random number is $(D_PARAM x Constructs a $(D_PARAM LinearCongruentialEngine) generator seeded with $(D x0). */ - this(UIntType x0) + this(UIntType x0) @safe pure { seed(x0); } @@ -352,7 +352,7 @@ $(D x0). /** (Re)seeds the generator. */ - void seed(UIntType x0 = 1) + void seed(UIntType x0 = 1) @safe pure { static if (c == 0) { @@ -366,7 +366,7 @@ $(D x0). /** Advances the random sequence. */ - void popFront() + void popFront() @safe pure nothrow { static if (m) { @@ -402,13 +402,13 @@ $(D x0). /** Returns the current number in the random sequence. */ - @property UIntType front() + @property UIntType front() const @safe pure nothrow { return _x; } /// - @property typeof(this) save() + @property typeof(this) save() @safe pure nothrow { return this; } @@ -421,7 +421,7 @@ Always $(D false) (random generators are infinite ranges). /** Compares against $(D_PARAM rhs) for equality. */ - bool opEquals(ref const LinearCongruentialEngine rhs) const + bool opEquals(ref const LinearCongruentialEngine rhs) const @safe pure nothrow { return _x == rhs._x; } @@ -449,9 +449,9 @@ rnd0.seed(unpredictableSeed); n = rnd0.front; // different across runs ---- */ -alias LinearCongruentialEngine!(uint, 16807, 0, 2147483647) MinstdRand0; +alias MinstdRand0 = LinearCongruentialEngine!(uint, 16807, 0, 2147483647); /// ditto -alias LinearCongruentialEngine!(uint, 48271, 0, 2147483647) MinstdRand; +alias MinstdRand = LinearCongruentialEngine!(uint, 48271, 0, 2147483647); unittest { @@ -526,41 +526,42 @@ struct MersenneTwisterEngine(UIntType, size_t w, size_t n, size_t m, size_t r, UIntType c, size_t l) if(isUnsigned!UIntType) { + static assert(0 < w && w <= UIntType.sizeof * 8); + static assert(1 <= m && m <= n); + static assert(0 <= r && 0 <= u && 0 <= s && 0 <= t && 0 <= l); + static assert(r <= w && u <= w && s <= w && t <= w && l <= w); + static assert(0 <= a && 0 <= b && 0 <= c); + ///Mark this as a Rng enum bool isUniformRandom = true; + /** -Parameter for the generator. +Parameters for the generator. */ - enum size_t wordSize = w; - enum size_t stateSize = n; - enum size_t shiftSize = m; - enum size_t maskBits = r; - enum UIntType xorMask = a; - enum UIntType temperingU = u; - enum size_t temperingS = s; - enum UIntType temperingB = b; - enum size_t temperingT = t; - enum UIntType temperingC = c; - enum size_t temperingL = l; + enum size_t wordSize = w; + enum size_t stateSize = n; /// ditto + enum size_t shiftSize = m; /// ditto + enum size_t maskBits = r; /// ditto + enum UIntType xorMask = a; /// ditto + enum UIntType temperingU = u; /// ditto + enum size_t temperingS = s; /// ditto + enum UIntType temperingB = b; /// ditto + enum size_t temperingT = t; /// ditto + enum UIntType temperingC = c; /// ditto + enum size_t temperingL = l; /// ditto /// Smallest generated value (0). enum UIntType min = 0; /// Largest generated value. - enum UIntType max = - w == UIntType.sizeof * 8 ? UIntType.max : (1u << w) - 1; + enum UIntType max = UIntType.max >> (UIntType.sizeof * 8u - w); + static assert(a <= max && b <= max && c <= max); /// The default seed value. enum UIntType defaultSeed = 5489u; - static assert(1 <= m && m <= n); - static assert(0 <= r && 0 <= u && 0 <= s && 0 <= t && 0 <= l); - static assert(r <= w && u <= w && s <= w && t <= w && l <= w); - static assert(0 <= a && 0 <= b && 0 <= c); - static assert(a <= max && b <= max && c <= max); - /** Constructs a MersenneTwisterEngine object. */ - this(UIntType value) + this(UIntType value) @safe pure nothrow { seed(value); } @@ -571,7 +572,7 @@ Parameter for the generator. This seed function gives 2^32 starting points. To allow the RNG to be started in any one of its internal states use the seed overload taking an InputRange. */ - void seed()(UIntType value = defaultSeed) + void seed()(UIntType value = defaultSeed) @safe pure nothrow { static if (w == UIntType.sizeof * 8) { @@ -621,7 +622,7 @@ Parameter for the generator. mti = n; if(range.empty && j < n) { - throw new Exception(format("MersenneTwisterEngine.seed: Input range didn't provide enough" + throw new Exception(format("MersenneTwisterEngine.seed: Input range didn't provide enough"~ " elements: Need %s elemnets.", n)); } @@ -631,7 +632,7 @@ Parameter for the generator. /** Advances the generator. */ - void popFront() + void popFront() @safe pure nothrow { if (mti == size_t.max) seed(); enum UIntType @@ -682,14 +683,14 @@ Parameter for the generator. /** Returns the current random value. */ - @property UIntType front() + @property UIntType front() @safe pure nothrow { if (mti == size_t.max) seed(); return _y; } /// - @property typeof(this) save() + @property typeof(this) save() @safe pure nothrow { return this; } @@ -723,11 +724,12 @@ gen.seed(unpredictableSeed); n = gen.front; // different across runs ---- */ -alias MersenneTwisterEngine!(uint, 32, 624, 397, 31, 0x9908b0df, 11, 7, - 0x9d2c5680, 15, 0xefc60000, 18) - Mt19937; +alias Mt19937 = MersenneTwisterEngine!(uint, 32, 624, 397, 31, + 0x9908b0df, 11, 7, + 0x9d2c5680, 15, + 0xefc60000, 18); -unittest +nothrow unittest { static assert(isUniformRNG!Mt19937); static assert(isUniformRNG!(Mt19937, uint)); @@ -750,7 +752,7 @@ unittest gen.seed(map!((a) => unpredictableSeed)(repeat(0))); } -unittest +@safe pure nothrow unittest { uint a, b; { @@ -780,6 +782,17 @@ unittest } } +@safe pure nothrow unittest //11690 +{ + alias MT(UIntType, uint w) = MersenneTwisterEngine!(UIntType, w, 624, 397, 31, + 0x9908b0df, 11, 7, + 0x9d2c5680, 15, + 0xefc60000, 18); + + foreach (R; TypeTuple!(MT!(uint, 32), MT!(ulong, 32), MT!(ulong, 48), MT!(ulong, 64))) + auto a = R(); +} + /** * Xorshift generator using 32bit algorithm. @@ -844,7 +857,7 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType * Constructs a $(D XorshiftEngine) generator seeded with $(D_PARAM x0). */ @safe - this(UIntType x0) + nothrow this(UIntType x0) pure { seed(x0); } @@ -854,7 +867,7 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType * (Re)seeds the generator. */ @safe - nothrow void seed(UIntType x0) + nothrow void seed(UIntType x0) pure { // Initialization routine from MersenneTwisterEngine. foreach (i, e; seeds_) @@ -871,7 +884,7 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType * Returns the current number in the random sequence. */ @property @safe - nothrow UIntType front() + nothrow UIntType front() const pure { static if (bits == 192) return value_; @@ -884,7 +897,7 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType * Advances the random sequence. */ @safe - nothrow void popFront() + nothrow void popFront() pure { UIntType temp; @@ -945,8 +958,8 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType /** * Captures a range state. */ - @property - typeof(this) save() + @property @safe + nothrow typeof(this) save() pure { return this; } @@ -956,7 +969,7 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType * Compares against $(D_PARAM rhs) for equality. */ @safe - nothrow bool opEquals(ref const XorshiftEngine rhs) const + nothrow bool opEquals(ref const XorshiftEngine rhs) const pure { return seeds_ == rhs.seeds_; } @@ -964,7 +977,7 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType private: @safe - static nothrow void sanitizeSeeds(ref UIntType[size] seeds) + static nothrow void sanitizeSeeds(ref UIntType[size] seeds) pure { for (uint i; i < seeds.length; i++) { @@ -974,7 +987,7 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType } - unittest + @safe pure nothrow unittest { static if (size == 4) // Other bits too { @@ -1003,13 +1016,13 @@ struct XorshiftEngine(UIntType, UIntType bits, UIntType a, UIntType b, UIntType * num = rnd.front; // different across runs * ----- */ -alias XorshiftEngine!(uint, 32, 13, 17, 15) Xorshift32; -alias XorshiftEngine!(uint, 64, 10, 13, 10) Xorshift64; /// ditto -alias XorshiftEngine!(uint, 96, 10, 5, 26) Xorshift96; /// ditto -alias XorshiftEngine!(uint, 128, 11, 8, 19) Xorshift128; /// ditto -alias XorshiftEngine!(uint, 160, 2, 1, 4) Xorshift160; /// ditto -alias XorshiftEngine!(uint, 192, 2, 1, 4) Xorshift192; /// ditto -alias Xorshift128 Xorshift; /// ditto +alias Xorshift32 = XorshiftEngine!(uint, 32, 13, 17, 15) ; +alias Xorshift64 = XorshiftEngine!(uint, 64, 10, 13, 10); /// ditto +alias Xorshift96 = XorshiftEngine!(uint, 96, 10, 5, 26); /// ditto +alias Xorshift128 = XorshiftEngine!(uint, 128, 11, 8, 19); /// ditto +alias Xorshift160 = XorshiftEngine!(uint, 160, 2, 1, 4); /// ditto +alias Xorshift192 = XorshiftEngine!(uint, 192, 2, 1, 4); /// ditto +alias Xorshift = Xorshift128; /// ditto unittest @@ -1030,7 +1043,7 @@ unittest [0UL, 246875399, 3690007200, 1264581005, 3906711041, 1866187943, 2481925219, 2464530826, 1604040631, 3653403911] ]; - alias TypeTuple!(Xorshift32, Xorshift64, Xorshift96, Xorshift128, Xorshift160, Xorshift192) XorshiftTypes; + alias XorshiftTypes = TypeTuple!(Xorshift32, Xorshift64, Xorshift96, Xorshift128, Xorshift160, Xorshift192); foreach (I, Type; XorshiftTypes) { @@ -1130,7 +1143,7 @@ nice random numbers, and (2) you don't care for the minutiae of the method being used. */ -alias Mt19937 Random; +alias Random = Mt19937; unittest { @@ -1218,9 +1231,9 @@ unittest auto uniform(string boundaries = "[)", T1, T2, UniformRandomNumberGenerator) (T1 a, T2 b, ref UniformRandomNumberGenerator urng) -if (isFloatingPoint!(CommonType!(T1, T2))) +if (isFloatingPoint!(CommonType!(T1, T2)) && isUniformRNG!UniformRandomNumberGenerator) { - alias Unqual!(CommonType!(T1, T2)) NumberType; + alias NumberType = Unqual!(CommonType!(T1, T2)); static if (boundaries[0] == '(') { NumberType _a = nextafter(cast(NumberType) a, NumberType.infinity); @@ -1312,7 +1325,8 @@ Hence, our condition to reroll is +/ auto uniform(string boundaries = "[)", T1, T2, RandomGen) (T1 a, T2 b, ref RandomGen rng) -if (isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) +if ((isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) && + isUniformRNG!RandomGen) { alias ResultType = Unqual!(CommonType!(T1, T2)); static if (boundaries[0] == '(') @@ -1331,10 +1345,16 @@ if (isIntegral!(CommonType!(T1, T2)) || isSomeChar!(CommonType!(T1, T2))) enforce(lower <= b, text("std.random.uniform(): invalid bounding interval ", boundaries[0], a, ", ", b, boundaries[1])); - if (b == ResultType.max && lower == ResultType.min) + /* Cannot use this next optimization with dchar, as dchar + * only partially uses its full bit range + */ + static if (!is(ResultType == dchar)) { - // Special case - all bits are occupied - return std.random.uniform!ResultType(rng); + if (b == ResultType.max && lower == ResultType.min) + { + // Special case - all bits are occupied + return std.random.uniform!ResultType(rng); + } } auto upperDist = unsigned(b - lower) + 1u; } @@ -1383,6 +1403,20 @@ unittest size_t i = 50; while (--i && uniform(lo, hi) == init) {} assert(i > 0); + + /* Test case with closed boundaries covering whole range + * of integral type + */ + static if (isIntegral!T || isSomeChar!T) + { + foreach (immutable _; 0 .. 100) + { + auto u = uniform!"[]"(T.min, T.max); + static assert(is(typeof(u) == T)); + assert(T.min <= u, "Lower bound violation for uniform!\"[]\" with " ~ T.stringof); + assert(u <= T.max, "Upper bound violation for uniform!\"[]\" with " ~ T.stringof); + } + } } auto reproRng = Xorshift(239842); @@ -1462,20 +1496,30 @@ passed, uses the default $(D rndGen). */ auto uniform(T, UniformRandomNumberGenerator) (ref UniformRandomNumberGenerator urng) -if (!is(T == enum) && (isIntegral!T || isSomeChar!T)) +if (!is(T == enum) && (isIntegral!T || isSomeChar!T) && isUniformRNG!UniformRandomNumberGenerator) { - auto r = urng.front; - urng.popFront(); - static if (T.sizeof <= r.sizeof) + /* dchar does not use its full bit range, so we must + * revert to the uniform with specified bounds + */ + static if (is(T == dchar)) { - return cast(T) r; + return uniform!"[]"(T.min, T.max); } else { - static assert(T.sizeof == 8 && r.sizeof == 4); - T r1 = urng.front | (cast(T)r << 32); + auto r = urng.front; urng.popFront(); - return r1; + static if (T.sizeof <= r.sizeof) + { + return cast(T) r; + } + else + { + static assert(T.sizeof == 8 && r.sizeof == 4); + T r1 = urng.front | (cast(T)r << 32); + urng.popFront(); + return r1; + } } } @@ -1495,6 +1539,14 @@ unittest size_t i = 50; while (--i && uniform!T() == init) {} assert(i > 0); + + foreach (immutable _; 0 .. 100) + { + auto u = uniform!T(); + static assert(is(typeof(u) == T)); + assert(T.min <= u, "Lower bound violation for uniform!" ~ T.stringof); + assert(u <= T.max, "Upper bound violation for uniform!" ~ T.stringof); + } } } @@ -1504,7 +1556,7 @@ generator is passed, uses the default $(D rndGen). */ auto uniform(E, UniformRandomNumberGenerator) (ref UniformRandomNumberGenerator urng) -if (is(E == enum)) +if (is(E == enum) && isUniformRNG!UniformRandomNumberGenerator) { static immutable E[EnumMembers!E.length] members = [EnumMembers!E]; return members[std.random.uniform(0, members.length, urng)]; @@ -1517,6 +1569,13 @@ if (is(E == enum)) return uniform!E(rndGen); } +/// +unittest +{ + enum Fruit { apple, mango, pear } + auto randFruit = uniform!Fruit(); +} + unittest { enum Fruit { Apple = 12, Mango = 29, Pear = 72 } @@ -1529,6 +1588,109 @@ unittest } } +/** + * Generates a uniformly-distributed floating point number of type + * $(D T) in the range [0, 1$(RPAREN). If no random number generator is + * specified, the default RNG $(D rndGen) will be used as the source + * of randomness. + * + * $(D uniform01) offers a faster generation of random variates than + * the equivalent $(D uniform!"[)"(0.0, 1.0)) and so may be preferred + * for some applications. + */ +T uniform01(T = double)() + if (isFloatingPoint!T) +{ + return uniform01!T(rndGen); +} + +/// ditto +T uniform01(T = double, UniformRNG)(ref UniformRNG rng) + if (isFloatingPoint!T && isUniformRNG!UniformRNG) +out (result) +{ + assert(0 <= result); + assert(result < 1); +} +body +{ + alias R = typeof(rng.front); + static if (isIntegral!R) + { + enum T factor = 1 / (T(1) + rng.max - rng.min); + } + else static if (isFloatingPoint!R) + { + enum T factor = 1 / (rng.max - rng.min); + } + else + { + static assert(false); + } + + while (true) + { + immutable T u = (rng.front - rng.min) * factor; + rng.popFront(); + static if (isIntegral!R) + { + /* if RNG variates are integral, we're guaranteed + * by the definition of factor that u < 1. + */ + return u; + } + else + { + /* Otherwise we have to check, just in case a + * floating-point RNG returns a variate that is + * exactly equal to its maximum + */ + if (u < 1) + { + return u; + } + } + } + + // Shouldn't ever get here. + assert(false); +} + +unittest +{ + import std.typetuple; + foreach (UniformRNG; PseudoRngTypes) + { + + foreach (T; TypeTuple!(float, double, real)) + { + UniformRNG rng = UniformRNG(unpredictableSeed); + + auto a = uniform01(); + assert(is(typeof(a) == double)); + assert(0 <= a && a < 1); + + auto b = uniform01(rng); + assert(is(typeof(a) == double)); + assert(0 <= b && b < 1); + + auto c = uniform01!T(); + assert(is(typeof(c) == T)); + assert(0 <= c && c < 1); + + auto d = uniform01!T(rng); + assert(is(typeof(d) == T)); + assert(0 <= d && d < 1); + + T init = uniform01!T(rng); + size_t i = 50; + while (--i && uniform01!T(rng) == init) {} + assert(i > 0); + assert(i < 50); + } + } +} + /** Generates a uniform probability distribution of size $(D n), i.e., an array of size $(D n) of positive numbers of type $(D F) that sum to @@ -1678,7 +1840,7 @@ if (isNumeric!Num) private size_t diceImpl(Rng, Range)(ref Rng rng, Range proportions) if (isForwardRange!Range && isNumeric!(ElementType!Range) && isForwardRange!Rng) { - double sum = reduce!("(assert(b >= 0), a + b)")(0.0, proportions.save); + double sum = reduce!((a,b) { assert(b >= 0); return a + b; })(0.0, proportions.save); enforce(sum > 0, "Proportions in a dice cannot sum to zero"); immutable point = uniform(0.0, sum, rng); assert(point < sum); @@ -2254,10 +2416,12 @@ Variable names are chosen to match those in Vitter's paper. while (true) { // Step D2: set values of x and u. - for (x = _available * (1-_Vprime), s = cast(size_t) trunc(x); - s >= qu1; - x = _available * (1-_Vprime), s = cast(size_t) trunc(x)) + while(1) { + x = _available * (1-_Vprime); + s = cast(size_t) trunc(x); + if (s < qu1) + break; _Vprime = newVprime(_toSelect); } diff --git a/libphobos/src/std/range.d b/libphobos/src/std/range.d index b2e6ea1f3..6bda76419 100644 --- a/libphobos/src/std/range.d +++ b/libphobos/src/std/range.d @@ -291,7 +291,6 @@ import std.algorithm : copy, count, equal, filter, filterBidirectional, findSplitBefore, group, isSorted, joiner, move, map, max, min, sort, swap, until; import std.traits; -import std.typecons : Tuple, tuple; import std.typetuple : allSatisfy, staticMap, TypeTuple; // For testing only. This code is included in a string literal to be included @@ -442,13 +441,13 @@ enum dummyRanges = q{ return arr.length; } - alias length opDollar; + alias opDollar = length; } } enum dummyLength = 10; - alias TypeTuple!( + alias AllDummyRanges = TypeTuple!( DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Forward), DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Bidirectional), DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random), @@ -461,7 +460,7 @@ enum dummyRanges = q{ DummyRange!(ReturnBy.Value, Length.No, RangeType.Input), DummyRange!(ReturnBy.Value, Length.No, RangeType.Forward), DummyRange!(ReturnBy.Value, Length.No, RangeType.Bidirectional) - ) AllDummyRanges; + ); }; @@ -522,7 +521,7 @@ template isInputRange(R) enum bool isInputRange = is(typeof( (inout int = 0) { - R r = void; // can define a range object + R r = R.init; // can define a range object if (r.empty) {} // can test for empty r.popFront(); // can invoke popFront() auto h = r.front; // can get the front of the range @@ -576,11 +575,15 @@ package void doPut(R, E)(ref R r, auto ref E e) r.front = e; r.popFront(); } + else static if (is(typeof(r(e)))) + { + r(e); + } else { - static assert(is(typeof(r(e))), + import std.string; + static assert (false, format("Cannot nativaly put a %s into a %s.", E.stringof, R.stringof)); - r(e); } } @@ -616,8 +619,9 @@ are attempted in order, and the first to compile "wins" and gets evaluated. In this table "doPut" is a method that places $(D e) into $(D r), using the -correct primitive: $(D r.put(e)) if $(D R) defines $(D put), $(D r.front = e) if $(D r) is an input -range (followed by $(D r.popFront())), or $(D r(e)) otherwise. +correct primitive: $(D r.put(e)) if $(D R) defines $(D put), $(D r.front = e) +if $(D r) is an input range (followed by $(D r.popFront())), or $(D r(e)) +otherwise. $(BOOKTABLE , $(TR @@ -643,7 +647,7 @@ $(BOOKTABLE , ) ) -Tip: $(D put) should $(I not) be used "UFCS-style", eg $(D r.put(e)). +Tip: $(D put) should $(I not) be used "UFCS-style", e.g. $(D r.put(e)). Doing this may call $(D R.put) directly, by-passing any transformation feature provided by $(D Range.put). $(D put(r, e)) is prefered. +/ @@ -696,7 +700,10 @@ void put(R, E)(ref R r, E e) } } else + { + import std.string; static assert (false, format("Cannot put a %s into a %s.", E.stringof, R.stringof)); + } } //Helper function to handle chars as quickly and as elegantly as possible @@ -743,7 +750,10 @@ if (isSomeChar!E) encode!(C, R)(e, r); } else + { + import std.string; static assert (false, format("Cannot put a %s into a %s.", E.stringof, R.stringof)); + } } pure unittest @@ -859,6 +869,7 @@ unittest unittest { import std.conv : to; + import std.typecons : tuple; static struct PutC(C) { @@ -1050,29 +1061,34 @@ template isOutputRange(R, E) enum bool isOutputRange = is(typeof( (inout int = 0) { - R r = void; - E e = void; + R r = R.init; + E e = E.init; put(r, e); })); } +/// unittest { - import std.stdio : writeln; - - void myprint(in char[] s) { writeln('[', s, ']'); } + void myprint(in char[] s) { } static assert(isOutputRange!(typeof(&myprint), char)); + static assert(!isOutputRange!(char[], char)); + static assert( isOutputRange!(dchar[], wchar)); + static assert( isOutputRange!(dchar[], dchar)); +} + +unittest +{ + import std.stdio : writeln; + auto app = appender!string(); string s; static assert( isOutputRange!(Appender!string, string)); static assert( isOutputRange!(Appender!string*, string)); static assert(!isOutputRange!(Appender!string, int)); - static assert(!isOutputRange!(char[], char)); static assert(!isOutputRange!(wchar[], wchar)); static assert( isOutputRange!(dchar[], char)); - static assert( isOutputRange!(dchar[], wchar)); - static assert( isOutputRange!(dchar[], dchar)); static assert( isOutputRange!(dchar[], string)); static assert( isOutputRange!(dchar[], wstring)); static assert( isOutputRange!(dchar[], dstring)); @@ -1119,7 +1135,7 @@ template isForwardRange(R) enum bool isForwardRange = isInputRange!R && is(typeof( (inout int = 0) { - R r1 = void; + R r1 = R.init; static assert (is(typeof(r1.save) == R)); })); } @@ -1159,7 +1175,7 @@ template isBidirectionalRange(R) enum bool isBidirectionalRange = isForwardRange!R && is(typeof( (inout int = 0) { - R r = void; + R r = R.init; r.popBack(); auto t = r.back; auto w = r.front; @@ -1242,7 +1258,7 @@ template isRandomAccessRange(R) { static assert(isBidirectionalRange!R || isForwardRange!R && isInfinite!R); - R r = void; + R r = R.init; auto e = r[1]; static assert(is(typeof(e) == typeof(r.front))); static assert(!isNarrowString!R); @@ -1285,7 +1301,7 @@ unittest void popBack(); ref int opIndex(uint); @property size_t length(); - alias length opDollar; + alias opDollar = length; //int opSlice(uint, uint); } static assert(!isRandomAccessRange!(A)); @@ -1303,8 +1319,6 @@ unittest { @disable this(); - @disable static @property R init(); - @property bool empty() const { return false; } @property int front() const { return 0; } void popFront() {} @@ -1316,7 +1330,7 @@ unittest int opIndex(size_t n) const { return 0; } @property size_t length() const { return 0; } - alias length opDollar; + alias opDollar = length; void put(int e){ } } @@ -1339,23 +1353,24 @@ template hasMobileElements(R) enum bool hasMobileElements = is(typeof( (inout int = 0) { - R r = void; + R r = R.init; return moveFront(r); })) && (!isBidirectionalRange!R || is(typeof( (inout int = 0) { - R r = void; + R r = R.init; return moveBack(r); }))) && (!isRandomAccessRange!R || is(typeof( (inout int = 0) { - R r = void; + R r = R.init; return moveAt(r, 0); }))); } +/// unittest { static struct HasPostblit @@ -1381,26 +1396,23 @@ $(D void). template ElementType(R) { static if (is(typeof(R.init.front.init) T)) - alias T ElementType; + alias ElementType = T; else - alias void ElementType; + alias ElementType = void; } /// unittest { // Standard arrays: returns the type of the elements of the array - static assert(is(ElementType!(byte[]) == byte)); static assert(is(ElementType!(int[]) == int)); // Accessing .front retrieves the decoded dchar static assert(is(ElementType!(char[]) == dchar)); // rvalue - static assert(is(ElementType!(wchar[]) == dchar)); // rvalue static assert(is(ElementType!(dchar[]) == dchar)); // lvalue // Ditto static assert(is(ElementType!(string) == dchar)); - static assert(is(ElementType!(wstring) == dchar)); static assert(is(ElementType!(dstring) == immutable(dchar))); // For ranges it gets the type of .front. @@ -1408,6 +1420,13 @@ unittest static assert(is(ElementType!(typeof(range)) == int)); } +unittest +{ + static assert(is(ElementType!(byte[]) == byte)); + static assert(is(ElementType!(wchar[]) == dchar)); // rvalue + static assert(is(ElementType!(wstring) == dchar)); +} + unittest { enum XYZ : string { a = "foo" } @@ -1460,10 +1479,10 @@ $(D ElementType). */ template ElementEncodingType(R) { - static if (isNarrowString!R) - alias typeof(*lvalueOf!R.ptr) ElementEncodingType; + static if (is(StringTypeOf!R) && is(R : E[], E)) + alias ElementEncodingType = E; else - alias ElementType!R ElementEncodingType; + alias ElementEncodingType = ElementType!R; } /// @@ -1471,21 +1490,24 @@ unittest { // internally the range stores the encoded type static assert(is(ElementEncodingType!(char[]) == char)); - static assert(is(ElementEncodingType!(wchar[]) == wchar)); - static assert(is(ElementEncodingType!(dchar[]) == dchar)); - // ditto - static assert(is(ElementEncodingType!(string) == immutable(char))); static assert(is(ElementEncodingType!(wstring) == immutable(wchar))); - static assert(is(ElementEncodingType!(dstring) == immutable(dchar))); static assert(is(ElementEncodingType!(byte[]) == byte)); - static assert(is(ElementEncodingType!(int[]) == int)); auto range = iota(0, 10); static assert(is(ElementEncodingType!(typeof(range)) == int)); } +unittest +{ + static assert(is(ElementEncodingType!(wchar[]) == wchar)); + static assert(is(ElementEncodingType!(dchar[]) == dchar)); + static assert(is(ElementEncodingType!(string) == immutable(char))); + static assert(is(ElementEncodingType!(dstring) == immutable(dchar))); + static assert(is(ElementEncodingType!(int[]) == int)); +} + unittest { enum XYZ : string { a = "foo" } @@ -1528,18 +1550,18 @@ template hasSwappableElements(R) enum bool hasSwappableElements = isForwardRange!R && is(typeof( (inout int = 0) { - R r = void; + R r = R.init; swap(r.front, r.front); // can swap elements of the range })); } +/// unittest { static assert(!hasSwappableElements!(const int[])); static assert(!hasSwappableElements!(const(int)[])); static assert(!hasSwappableElements!(inout(int)[])); static assert( hasSwappableElements!(int[])); - //static assert( hasSwappableElements!(char[])); } /** @@ -1559,13 +1581,14 @@ template hasAssignableElements(R) enum bool hasAssignableElements = isForwardRange!R && is(typeof( (inout int = 0) { - R r = void; + R r = R.init; static assert(isForwardRange!(R)); // range is forward auto e = r.front; r.front = e; // can assign elements of the range })); } +/// unittest { static assert(!hasAssignableElements!(const int[])); @@ -1584,11 +1607,12 @@ template hasLvalueElements(R) (inout int = 0) { void checkRef(ref ElementType!R stuff) {} - R r = void; + R r = R.init; static assert(is(typeof(checkRef(r.front)))); })); } +/// unittest { static assert( hasLvalueElements!(int[])); @@ -1599,7 +1623,10 @@ unittest auto c = chain([1, 2, 3], [4, 5, 6]); static assert( hasLvalueElements!(typeof(c))); +} +unittest +{ // bugfix 6336 struct S { immutable int value; } static assert( isInputRange!(S[])); @@ -1626,11 +1653,12 @@ template hasLength(R) enum bool hasLength = !isNarrowString!R && is(typeof( (inout int = 0) { - R r = void; + R r = R.init; static assert(is(typeof(r.length) : ulong)); })); } +/// unittest { static assert(!hasLength!(char[])); @@ -1668,6 +1696,7 @@ template isInfinite(R) enum bool isInfinite = false; } +/// unittest { static assert(!isInfinite!(int[])); @@ -1726,7 +1755,7 @@ template hasSlicing(R) enum bool hasSlicing = isForwardRange!R && !isNarrowString!R && is(typeof( (inout int = 0) { - R r = void; + R r = R.init; static if(isInfinite!R) typeof(take(r, 1)) s = r[1 .. 2]; @@ -1757,6 +1786,7 @@ template hasSlicing(R) })); } +/// unittest { static assert( hasSlicing!(int[])); @@ -1878,14 +1908,6 @@ unittest Iterates a bidirectional range backwards. The original range can be accessed by using the $(D source) property. Applying retro twice to the same range yields the original range. - -Example: ----- -int[] a = [ 1, 2, 3, 4, 5 ]; -assert(equal(retro(a), [ 5, 4, 3, 2, 1 ][])); -assert(retro(a).source is a); -assert(retro(retro(a)) is a); ----- */ auto retro(Range)(Range r) if (isBidirectionalRange!(Unqual!Range)) @@ -1899,14 +1921,14 @@ if (isBidirectionalRange!(Unqual!Range)) { static struct Result() { - private alias Unqual!Range R; + private alias R = Unqual!Range; // User code can get and set source, too R source; static if (hasLength!R) { - private alias CommonType!(size_t, typeof(source.length)) IndexType; + private alias IndexType = CommonType!(size_t, typeof(source.length)); IndexType retroIndex(IndexType n) { @@ -1915,7 +1937,7 @@ if (isBidirectionalRange!(Unqual!Range)) } public: - alias R Source; + alias Source = R; @property bool empty() { return source.empty; } @property auto save() @@ -1990,7 +2012,7 @@ if (isBidirectionalRange!(Unqual!Range)) return source.length; } - alias length opDollar; + alias opDollar = length; } } @@ -1998,6 +2020,16 @@ if (isBidirectionalRange!(Unqual!Range)) } } + +/// +unittest +{ + int[] a = [ 1, 2, 3, 4, 5 ]; + assert(equal(retro(a), [ 5, 4, 3, 2, 1 ][])); + assert(retro(a).source is a); + assert(retro(retro(a)) is a); +} + unittest { static assert(isBidirectionalRange!(typeof(retro("hello")))); @@ -2019,10 +2051,12 @@ unittest test([ 1, 2, 3, 4, 5 ], [ 5, 4, 3, 2, 1 ]); test([ 1, 2, 3, 4, 5, 6 ], [ 6, 5, 4, 3, 2, 1 ]); - // static assert(is(Retro!(immutable int[]))); immutable foo = [1,2,3].idup; - retro(foo); + auto r = retro(foo); +} +unittest +{ foreach(DummyType; AllDummyRanges) { static if (!isBidirectionalRange!DummyType) { static assert(!__traits(compiles, Retro!DummyType)); @@ -2112,7 +2146,7 @@ if (isInputRange!(Unqual!Range)) { static struct Result { - private alias Unqual!Range R; + private alias R = Unqual!Range; public R source; private size_t _n; @@ -2296,7 +2330,7 @@ if (isInputRange!(Unqual!Range)) return (source.length + _n - 1) / _n; } - alias length opDollar; + alias opDollar = length; } } return Result(r, n); @@ -2470,8 +2504,8 @@ if (Ranges.length > 0 && static struct Result { private: - alias staticMap!(Unqual, Ranges) R; - alias CommonType!(staticMap!(.ElementType, R)) RvalueElementType; + alias R = staticMap!(Unqual, Ranges); + alias RvalueElementType = CommonType!(staticMap!(.ElementType, R)); private template sameET(A) { enum sameET = is(.ElementType!A == RvalueElementType); @@ -2486,7 +2520,7 @@ if (Ranges.length > 0 && } else { - alias RvalueElementType ElementType; + alias ElementType = RvalueElementType; } static if (allSameType && allSatisfy!(hasLvalueElements, R)) { @@ -2661,7 +2695,7 @@ if (Ranges.length > 0 && return result; } - alias length opDollar; + alias opDollar = length; } static if (allSatisfy!(isRandomAccessRange, R)) @@ -2881,13 +2915,6 @@ continues again from $(D r1). For example, if two ranges are involved, it alternately yields elements off the two ranges. $(D roundRobin) stops after it has consumed all ranges (skipping over the ones that finish early). - -Example: ----- -int[] a = [ 1, 2, 3, 4]; -int[] b = [ 10, 20 ]; -assert(equal(roundRobin(a, b), [1, 10, 2, 20, 3, 4])); ----- */ auto roundRobin(Rs...)(Rs rs) if (Rs.length > 1 && allSatisfy!(isInputRange, staticMap!(Unqual, Rs))) @@ -2941,15 +2968,15 @@ if (Rs.length > 1 && allSatisfy!(isInputRange, staticMap!(Unqual, Rs))) static string makeSwitchIncrementCounter() { string result = - "auto next = _current == Rs.length - 1 ? 0 : _current + 1;\n" + "auto next = _current == Rs.length - 1 ? 0 : _current + 1;\n"~ "switch (next) {\n"; foreach (i, R; Rs) { auto si = to!string(i); auto si_1 = to!string(i ? i - 1 : Rs.length - 1); - result ~= "case "~si~": " - "if (!source["~si~"].empty) { _current = "~si~"; return; }\n" - "if ("~si~" == _current) { _current = _current.max; return; }\n" + result ~= "case "~si~": "~ + "if (!source["~si~"].empty) { _current = "~si~"; return; }\n"~ + "if ("~si~" == _current) { _current = _current.max; return; }\n"~ "goto case "~to!string((i + 1) % Rs.length)~";\n"; } return result ~ "default: assert(0); }"; @@ -2982,13 +3009,14 @@ if (Rs.length > 1 && allSatisfy!(isInputRange, staticMap!(Unqual, Rs))) return result; } - alias length opDollar; + alias opDollar = length; } } return Result(rs, 0); } +/// unittest { int[] a = [ 1, 2, 3 ]; @@ -3002,14 +3030,6 @@ Iterates a random-access range starting from a given point and progressively extending left and right from that point. If no initial point is given, iteration starts from the middle of the range. Iteration spans the entire range. - -Example: ----- -int[] a = [ 1, 2, 3, 4, 5 ]; -assert(equal(radial(a), [ 3, 4, 2, 5, 1 ])); -a = [ 1, 2, 3, 4 ]; -assert(equal(radial(a), [ 2, 3, 1, 4 ])); ----- */ auto radial(Range, I)(Range r, I startingIndex) if (isRandomAccessRange!(Unqual!Range) && hasLength!(Unqual!Range) && isIntegral!I) @@ -3025,6 +3045,15 @@ if (isRandomAccessRange!(Unqual!R) && hasLength!(Unqual!R)) return .radial(r, (r.length - !r.empty) / 2); } +/// +unittest +{ + int[] a = [ 1, 2, 3, 4, 5 ]; + assert(equal(radial(a), [ 3, 4, 2, 5, 1 ])); + a = [ 1, 2, 3, 4 ]; + assert(equal(radial(a), [ 2, 3, 1, 4 ])); +} + unittest { import std.conv : text; @@ -3073,15 +3102,6 @@ unittest Lazily takes only up to $(D n) elements of a range. This is particularly useful when using with infinite ranges. If the range offers random access and $(D length), $(D Take) offers them as well. - -Example: ----- -int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; -auto s = take(arr1, 5); -assert(s.length == 5); -assert(s[4] == 5); -assert(equal(s, [ 1, 2, 3, 4, 5 ][])); ----- */ struct Take(Range) if (isInputRange!(Unqual!Range) && @@ -3089,14 +3109,14 @@ if (isInputRange!(Unqual!Range) && //take for slicing infinite ranges. !((!isInfinite!(Unqual!Range) && hasSlicing!(Unqual!Range)) || is(Range T == Take!T))) { - private alias Unqual!Range R; + private alias R = Unqual!Range; // User accessible in read and write public R source; private size_t _maxAvailable; - alias R Source; + alias Source = R; @property bool empty() { @@ -3154,7 +3174,18 @@ if (isInputRange!(Unqual!Range) && return _maxAvailable; } - alias length opDollar; + alias opDollar = length; + + //Note: Due to Take/hasSlicing circular dependency, + //This needs to be a restrained template. + auto opSlice()(size_t i, size_t j) + if (hasSlicing!R) + { + assert(i <= j, "Invalid slice bounds"); + assert(j - i <= length, "Attempting to slice past the end of a " + ~ Take.stringof); + return source[i .. j - i]; + } } else static if (hasLength!R) { @@ -3163,7 +3194,7 @@ if (isInputRange!(Unqual!Range) && return min(_maxAvailable, source.length); } - alias length opDollar; + alias opDollar = length; } static if (isRandomAccessRange!R) @@ -3245,7 +3276,7 @@ template Take(R) if (isInputRange!(Unqual!R) && ((!isInfinite!(Unqual!R) && hasSlicing!(Unqual!R)) || is(R T == Take!T))) { - alias R Take; + alias Take = R; } // take for finite ranges with slicing @@ -3258,6 +3289,16 @@ if (isInputRange!(Unqual!R) && !isInfinite!(Unqual!R) && hasSlicing!(Unqual!R)) return input[0 .. min(n, input.length)]; } +/// +unittest +{ + int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]; + auto s = take(arr1, 5); + assert(s.length == 5); + assert(s[4] == 5); + assert(equal(s, [ 1, 2, 3, 4, 5 ][])); +} + // take(take(r, n1), n2) Take!R take(R)(R input, size_t n) if (is(R T == Take!T)) @@ -3300,7 +3341,7 @@ unittest foreach(DummyType; AllDummyRanges) { DummyType dummy; auto t = take(dummy, 5); - alias typeof(t) T; + alias T = typeof(t); static if (isRandomAccessRange!DummyType) { static assert(isRandomAccessRange!T); @@ -3357,6 +3398,13 @@ unittest static assert(isBidirectionalRange!TR2); } +unittest //12731 +{ + auto a = repeat(1); + auto s = a[1 .. 5]; + s = s[1 .. 3]; +} + /** Similar to $(LREF take), but assumes that $(D range) has at least $(D n) elements. Consequently, the result of $(D takeExactly(range, n)) @@ -3402,7 +3450,7 @@ if (isInputRange!R) } void popFront() { _input.popFront(); --_n; } @property size_t length() const { return _n; } - alias length opDollar; + alias opDollar = length; static if (isForwardRange!R) @property auto save() @@ -3437,6 +3485,7 @@ if (isInputRange!R) } } +/// unittest { auto a = [ 1, 2, 3, 4, 5 ]; @@ -3447,7 +3496,12 @@ unittest assert(b.length == 3); assert(b.front == 1); assert(b.back == 3); +} +unittest +{ + auto a = [ 1, 2, 3, 4, 5 ]; + auto b = takeExactly(a, 3); auto c = takeExactly(b, 2); auto d = filter!"a > 0"(a); @@ -3458,7 +3512,11 @@ unittest assert(e.front == 1); assert(equal(takeExactly(e, 3), [1, 2, 3])); +} +unittest +{ + auto a = [ 1, 2, 3, 4, 5 ]; //Test that take and takeExactly are the same for ranges which define length //but aren't sliceable. struct L @@ -3520,21 +3578,6 @@ Returns a range with at most one element; for example, $(D takeOne([42, 43, 44])) returns a range consisting of the integer $(D 42). Calling $(D popFront()) off that range renders it empty. ----- -auto s = takeOne([42, 43, 44]); -static assert(isRandomAccessRange!(typeof(s))); -assert(s.length == 1); -assert(!s.empty); -assert(s.front == 42); -s.front() = 43; -assert(s.front == 43); -assert(s.back == 43); -assert(s[0] == 43); -s.popFront(); -assert(s.length == 0); -assert(s.empty); ----- - In effect $(D takeOne(r)) is somewhat equivalent to $(D take(r, 1)) but in certain interfaces it is important to know statically that the range may only have at most one element. @@ -3562,7 +3605,7 @@ auto takeOne(R)(R source) if (isInputRange!R) @property auto save() { return Result(_source.save, empty); } @property auto ref back() { assert(!empty); return _source.front; } @property size_t length() const { return !empty; } - alias length opDollar; + alias opDollar = length; auto ref opIndex(size_t n) { assert(n < length); return _source.front; } auto opSlice(size_t m, size_t n) { @@ -3577,6 +3620,7 @@ auto takeOne(R)(R source) if (isInputRange!R) } } +/// unittest { auto s = takeOne([42, 43, 44]); @@ -3597,13 +3641,6 @@ unittest Returns an empty range which is statically known to be empty and is guaranteed to have $(D length) and be random access regardless of $(D R)'s capabilities. - - Examples: --------------------- -auto range = takeNone!(int[])(); -assert(range.length == 0); -assert(range.empty); --------------------- +/ auto takeNone(R)() if(isInputRange!R) @@ -3611,12 +3648,16 @@ auto takeNone(R)() return typeof(takeOne(R.init)).init; } +/// unittest { auto range = takeNone!(int[])(); assert(range.length == 0); assert(range.empty); +} +unittest +{ enum ctfe = takeNone!(int[])(); static assert(ctfe.length == 0); static assert(ctfe.empty); @@ -3627,13 +3668,6 @@ unittest Creates an empty range from the given range in $(BIGOH 1). If it can, it will return the same range type. If not, it will return $(D takeExactly(range, 0)). - - Examples: --------------------- -assert(takeNone([42, 27, 19]).empty); -assert(takeNone("dlang.org").empty); -assert(takeNone(filter!"true"([42, 27, 19])).empty); --------------------- +/ auto takeNone(R)(R range) if(isInputRange!R) @@ -3659,7 +3693,7 @@ auto takeNone(R)(R range) return retval; } -//Verify Examples. +/// unittest { assert(takeNone([42, 27, 19]).empty); @@ -3669,20 +3703,24 @@ unittest unittest { - string genInput() + struct Dummy { - return "@property bool empty() { return _arr.empty; }" ~ - "@property auto front() { return _arr.front; }" ~ - "void popFront() { _arr.popFront(); }" ~ - "static assert(isInputRange!(typeof(this)));"; + mixin template genInput() + { + @property bool empty() { return _arr.empty; } + @property auto front() { return _arr.front; } + void popFront() { _arr.popFront(); } + static assert(isInputRange!(typeof(this))); + } } + alias genInput = Dummy.genInput; static struct NormalStruct { //Disabled to make sure that the takeExactly version is used. @disable this(); this(int[] arr) { _arr = arr; } - mixin(genInput()); + mixin genInput; int[] _arr; } @@ -3690,7 +3728,7 @@ unittest { @disable this(); this(int[] arr) { _arr = arr; } - mixin(genInput()); + mixin genInput; @property auto save() { return this; } auto opSlice(size_t i, size_t j) { return typeof(this)(_arr[i .. j]); } @property size_t length() { return _arr.length; } @@ -3699,7 +3737,7 @@ unittest static struct InitStruct { - mixin(genInput()); + mixin genInput; int[] _arr; } @@ -3707,7 +3745,7 @@ unittest { this(int[] arr) { _arr = arr; } @disable this(); - mixin(genInput()); + mixin genInput; auto takeNone() { return typeof(this)(null); } int[] _arr; } @@ -3715,14 +3753,14 @@ unittest static class NormalClass { this(int[] arr) {_arr = arr;} - mixin(genInput()); + mixin genInput; int[] _arr; } static class SliceClass { this(int[] arr) { _arr = arr; } - mixin(genInput()); + mixin genInput; @property auto save() { return new typeof(this)(_arr); } auto opSlice(size_t i, size_t j) { return new typeof(this)(_arr[i .. j]); } @property size_t length() { return _arr.length; } @@ -3732,37 +3770,33 @@ unittest static class TakeNoneClass { this(int[] arr) { _arr = arr; } - mixin(genInput()); + mixin genInput; auto takeNone() { return new typeof(this)(null); } int[] _arr; } import std.string : format; - foreach(range; TypeTuple!(`[1, 2, 3, 4, 5]`, - `"hello world"`, - `"hello world"w`, - `"hello world"d`, - `SliceStruct([1, 2, 3])`, + foreach(range; TypeTuple!([1, 2, 3, 4, 5], + "hello world", + "hello world"w, + "hello world"d, + SliceStruct([1, 2, 3]), //@@@BUG@@@ 8339 forces this to be takeExactly - //`InitStruct([1, 2, 3])`, - `TakeNoneStruct([1, 2, 3])`)) + //`InitStruct([1, 2, 3]), + TakeNoneStruct([1, 2, 3]))) { - mixin(format("enum a = takeNone(%s).empty;", range)); - assert(a, typeof(range).stringof); - mixin(format("assert(takeNone(%s).empty);", range)); - mixin(format("static assert(is(typeof(%s) == typeof(takeNone(%s))), typeof(%s).stringof);", - range, range, range)); + static assert(takeNone(range).empty, typeof(range).stringof); + assert(takeNone(range).empty); + static assert(is(typeof(range) == typeof(takeNone(range))), typeof(range).stringof); } - foreach(range; TypeTuple!(`NormalStruct([1, 2, 3])`, - `InitStruct([1, 2, 3])`)) + foreach(range; TypeTuple!(NormalStruct([1, 2, 3]), + InitStruct([1, 2, 3]))) { - mixin(format("enum a = takeNone(%s).empty;", range)); - assert(a, typeof(range).stringof); - mixin(format("assert(takeNone(%s).empty);", range)); - mixin(format("static assert(is(typeof(takeExactly(%s, 0)) == typeof(takeNone(%s))), typeof(%s).stringof);", - range, range, range)); + static assert(takeNone(range).empty, typeof(range).stringof); + assert(takeNone(range).empty); + static assert(is(typeof(takeExactly(range, 0)) == typeof(takeNone(range))), typeof(range).stringof); } //Don't work in CTFE. @@ -3797,34 +3831,6 @@ unittest Note: $(D drop) and $(D dropBack) will only pop $(I up to) $(D n) elements but will stop if the range is empty first. - Examples: --------------------- -assert([0, 2, 1, 5, 0, 3].drop(3) == [5, 0, 3]); -assert("hello world".drop(6) == "world"); -assert("hello world".drop(50).empty); -assert("hello world".take(6).drop(3).equal("lo ")); --------------------- - --------------------- -//Remove all but the first two elements -auto a = DList!int(0, 1, 9, 9, 9); -a.remove(a[].drop(2)); -assert(a[].equal(a[].take(2))); --------------------- - --------------------- -assert([0, 2, 1, 5, 0, 3].dropBack(3) == [0, 2, 1]); -assert("hello world".dropBack(6) == "hello"); -assert("hello world".dropBack(50).empty); -assert("hello world".drop(4).dropBack(4).equal("o w")); --------------------- - --------------------- -//insert before the last two elements -auto a = DList!int(0, 1, 2, 5, 6); -a.insertAfter(a[].dropBack(2), [3, 4]); -assert(a[].equal(iota(0, 7))); --------------------- +/ R drop(R)(R range, size_t n) if(isInputRange!R) @@ -3840,7 +3846,7 @@ R dropBack(R)(R range, size_t n) return range; } -//Verify Examples +/// unittest { assert([0, 2, 1, 5, 0, 3].drop(3) == [5, 0, 3]); @@ -3848,6 +3854,15 @@ unittest assert("hello world".drop(50).empty); assert("hello world".take(6).drop(3).equal("lo ")); } + +unittest +{ + assert([0, 2, 1, 5, 0, 3].dropBack(3) == [0, 2, 1]); + assert("hello world".dropBack(6) == "hello"); + assert("hello world".dropBack(50).empty); + assert("hello world".drop(4).dropBack(4).equal("o w")); +} + unittest { import std.container : DList; @@ -3857,18 +3872,13 @@ unittest a.remove(a[].drop(2)); assert(a[].equal(a[].take(2))); } + unittest { assert(drop("", 5).empty); assert(equal(drop(filter!"true"([0, 2, 1, 5, 0, 3]), 3), [5, 0, 3])); } -unittest -{ - assert([0, 2, 1, 5, 0, 3].dropBack(3) == [0, 2, 1]); - assert("hello world".dropBack(6) == "hello"); - assert("hello world".dropBack(50).empty); - assert("hello world".drop(4).dropBack(4).equal("o w")); -} + unittest { import std.container : DList; @@ -3906,22 +3916,20 @@ R dropBackExactly(R)(R range, size_t n) return range; } +/// unittest { - //RA+slicing auto a = [1, 2, 3]; - assert(a.dropExactly(1) == [2, 3]); - assert(a.dropBackExactly(1) == [1, 2]); + assert(a.dropExactly(2) == [3]); + assert(a.dropBackExactly(2) == [1]); - //UTF string string s = "日本語"; - assert(s.dropExactly(1) == "本語"); - assert(s.dropBackExactly(1) == "日本"); + assert(s.dropExactly(2) == "語"); + assert(s.dropBackExactly(2) == "日"); - //Bidirectional auto bd = filterBidirectional!"true"([1, 2, 3]); - assert(bd.dropExactly(1).equal([2, 3])); - assert(bd.dropBackExactly(1).equal([1, 2])); + assert(bd.dropExactly(2).equal([3])); + assert(bd.dropBackExactly(2).equal([1])); } /++ @@ -3933,12 +3941,6 @@ unittest $(D dropBackOne) provides the same functionality but instead calls $(D range.popBack()). - - Example: ----- -auto dl = DList!int(9, 1, 2, 3, 9); -assert(dl[].dropOne().dropBackOne().equal([1, 2, 3])); ----- +/ R dropOne(R)(R range) if (isInputRange!R) @@ -3954,26 +3956,22 @@ R dropBackOne(R)(R range) return range; } +/// unittest { import std.container : DList; auto dl = DList!int(9, 1, 2, 3, 9); assert(dl[].dropOne().dropBackOne().equal([1, 2, 3])); -} -unittest -{ - //RA+slicing + auto a = [1, 2, 3]; assert(a.dropOne() == [2, 3]); assert(a.dropBackOne() == [1, 2]); - //UTF string string s = "日本語"; assert(s.dropOne() == "本語"); assert(s.dropBackOne() == "日本"); - //Bidirectional auto bd = filterBidirectional!"true"([1, 2, 3]); assert(bd.dropOne().equal([2, 3])); assert(bd.dropBackOne().equal([1, 2])); @@ -3992,23 +3990,6 @@ unittest $(D popBackN) will behave the same but instead removes elements from the back of the (bidirectional) range instead of the front. - - Example: ----- -int[] a = [ 1, 2, 3, 4, 5 ]; -a.popFrontN(2); -assert(a == [ 3, 4, 5 ]); -a.popFrontN(7); -assert(a == [ ]); ----- - ----- -int[] a = [ 1, 2, 3, 4, 5 ]; -a.popBackN(2); -assert(a == [ 1, 2, 3 ]); -a.popBackN(7); -assert(a == [ ]); ----- */ size_t popFrontN(Range)(ref Range r, size_t n) if (isInputRange!Range) @@ -4076,6 +4057,7 @@ size_t popBackN(Range)(ref Range r, size_t n) return n; } +/// unittest { int[] a = [ 1, 2, 3, 4, 5 ]; @@ -4084,6 +4066,8 @@ unittest a.popFrontN(7); assert(a == [ ]); } + +/// unittest { auto LL = iota(1L, 7L); @@ -4091,6 +4075,8 @@ unittest assert(equal(LL, [3L, 4L, 5L, 6L])); assert(r == 2); } + +/// unittest { int[] a = [ 1, 2, 3, 4, 5 ]; @@ -4099,6 +4085,8 @@ unittest a.popBackN(7); assert(a == [ ]); } + +/// unittest { auto LL = iota(1L, 7L); @@ -4155,23 +4143,21 @@ void popBackExactly(Range)(ref Range r, size_t n) r.popBack(); } +/// unittest { - //RA+slicing auto a = [1, 2, 3]; a.popFrontExactly(1); assert(a == [2, 3]); a.popBackExactly(1); assert(a == [2]); - //UTF string string s = "日本語"; s.popFrontExactly(1); assert(s == "本語"); s.popBackExactly(1); assert(s == "本"); - //Bidirectional auto bd = filterBidirectional!"true"([1, 2, 3]); bd.popFrontExactly(1); assert(bd.equal([2, 3])); @@ -4186,7 +4172,20 @@ Models an infinite bidirectional and random access range, with slicing. */ struct Repeat(T) { - private T _value; +private: + //Store a non-qualified T when possible: This is to make Repeat assignable + static if ((is(T == class) || is(T == interface)) && (is(T == const) || is(T == immutable))) + { + import std.typecons; + alias UT = Rebindable!T; + } + else static if (is(T : Unqual!T) && is(Unqual!T : T)) + alias UT = Unqual!T; + else + alias UT = T; + UT _value; + +public: @property inout(T) front() inout { return _value; } @property inout(T) back() inout { return _value; } enum bool empty = false; @@ -4250,6 +4249,21 @@ unittest assert(equal(5.repeat(4), 5.repeat().take(4))); } +unittest //12007 +{ + static class C{} + Repeat!(immutable int) ri; + ri = ri.save; + Repeat!(immutable C) rc; + rc = rc.save; + + import std.algorithm; + immutable int[] A = [1,2,3]; + immutable int[] B = [4,5,6]; + + auto AB = cartesianProduct(A,B); +} + /** Repeats the given forward range ad infinitum. If the original range is infinite (fact that would make $(D Cycle) the identity application), @@ -4259,11 +4273,6 @@ random access and also offers a constructor taking an initial position $(D index). $(D Cycle) works with static arrays in addition to ranges, mostly for performance reasons. -Example: ----- -assert(equal(take(cycle([1, 2][]), 5), [ 1, 2, 1, 2, 1 ][])); ----- - Tip: This is a great way to implement simple circular buffers. */ struct Cycle(R) @@ -4277,20 +4286,19 @@ struct Cycle(R) this(R input, size_t index = 0) { _original = input; - _index = index; + _index = index % _original.length; } @property auto ref front() { - return _original[_index % _original.length]; + return _original[_index]; } - static if (is(typeof((cast(const R)_original)[0])) && - is(typeof((cast(const R)_original).length))) + static if (is(typeof((cast(const R)_original)[_index]))) { @property auto ref front() const { - return _original[_index % _original.length]; + return _original[_index]; } } @@ -4298,7 +4306,7 @@ struct Cycle(R) { @property auto front(ElementType!R val) { - _original[_index % _original.length] = val; + _original[_index] = val; } } @@ -4307,6 +4315,8 @@ struct Cycle(R) void popFront() { ++_index; + if (_index >= _original.length) + _index = 0; } auto ref opIndex(size_t n) @@ -4314,7 +4324,7 @@ struct Cycle(R) return _original[(n + _index) % _original.length]; } - static if (is(typeof((cast(const R)_original)[0])) && + static if (is(typeof((cast(const R)_original)[_index])) && is(typeof((cast(const R)_original).length))) { auto ref opIndex(size_t n) const @@ -4393,7 +4403,8 @@ struct Cycle(R) void popFront() { _current.popFront(); - if (_current.empty) _current = _original; + if (_current.empty) + _current = _original.save; } @property Cycle save() @@ -4424,12 +4435,12 @@ nothrow: this(ref R input, size_t index = 0) { _ptr = input.ptr; - _index = index; + _index = index % R.length; } @property ref inout(ElementType) front() inout { - return _ptr[_index % R.length]; + return _ptr[_index]; } enum bool empty = false; @@ -4437,6 +4448,8 @@ nothrow: void popFront() { ++_index; + if (_index >= R.length) + _index = 0; } ref inout(ElementType) opIndex(size_t n) inout @@ -4465,7 +4478,8 @@ nothrow: inout(typeof(this)) opSlice(size_t i, DollarToken) inout { - return Cycle(*cast(R*)_ptr, _index + i); + // cast: Issue 12177 workaround + return cast(typeof(return))Cycle(*cast(R*)_ptr, _index + i); } } @@ -4476,6 +4490,12 @@ Cycle!R cycle(R)(R input) return Cycle!R(input); } +/// +unittest +{ + assert(equal(take(cycle([1, 2][]), 5), [ 1, 2, 1, 2, 1 ][])); +} + /// Ditto Cycle!R cycle(R)(R input, size_t index = 0) if (isRandomAccessRange!R && !isInfinite!R) @@ -4497,7 +4517,6 @@ Cycle!R cycle(R)(ref R input, size_t index = 0) unittest { - assert(equal(take(cycle([1, 2][]), 5), [ 1, 2, 1, 2, 1 ][])); static assert(isForwardRange!(Cycle!(uint[]))); // Make sure ref is getting propagated properly. @@ -4589,7 +4608,49 @@ unittest // For infinite ranges assert (c == i); } -private template lengthType(R) { alias typeof((inout int = 0){ R r = void; return r.length; }()) lengthType; } +unittest +{ + int[5] arr = [0, 1, 2, 3, 4]; + auto cleS = cycle(arr); //Static + auto cleD = cycle(arr[]); //Dynamic + assert(equal(cleS[5 .. 10], arr[])); + assert(equal(cleD[5 .. 10], arr[])); + + //n is a multiple of 5 worth about 3/4 of size_t.max + auto n = size_t.max/4 + size_t.max/2; + n -= n % 5; + + //Test index overflow + foreach (_ ; 0 .. 10) + { + cleS = cleS[n .. $]; + cleD = cleD[n .. $]; + assert(equal(cleS[5 .. 10], arr[])); + assert(equal(cleD[5 .. 10], arr[])); + } +} + +unittest +{ + int[1] arr = [0]; + auto cleS = cycle(arr); + cleS = cleS[10 .. $]; + assert(equal(cleS[5 .. 10], 0.repeat(5))); + assert(cleS.front == 0); +} + +unittest //10845 +{ + auto a = inputRangeObject(iota(3).filter!"true"); + assert(equal(cycle(a).take(10), [0, 1, 2, 0, 1, 2, 0, 1, 2, 0])); +} + +unittest // 12177 +{ + auto a = recurrence!q{a[n - 1] ~ a[n - 2]}("1", "0"); +} + +private alias lengthType(R) = typeof(R.init.length.init); /** Iterate several ranges in lockstep. The element type is a proxy tuple @@ -4613,21 +4674,16 @@ private template lengthType(R) { alias typeof((inout int = 0){ R r = void; retur Zip) is extremely powerful because it allows manipulating several ranges in lockstep. For example, the following code sorts two arrays in parallel: - - ---- - int[] a = [ 1, 2, 3 ]; - string[] b = [ "a", "b", "c" ]; - sort!("a[0] > b[0]")(zip(a, b)); - assert(a == [ 3, 2, 1 ]); - assert(b == [ "c", "b", "a" ]); - ---- */ struct Zip(Ranges...) if (Ranges.length && allSatisfy!(isInputRange, Ranges)) { + import std.string : format; //for generic mixins + import std.typecons : Tuple; + alias R = Ranges; R ranges; - alias Tuple!(staticMap!(.ElementType, R)) ElementType; + alias ElementType = Tuple!(staticMap!(.ElementType, R)); StoppingPolicy stoppingPolicy = StoppingPolicy.shortest; /** @@ -4636,11 +4692,8 @@ struct Zip(Ranges...) */ this(R rs, StoppingPolicy s = StoppingPolicy.shortest) { + ranges[] = rs[]; stoppingPolicy = s; - foreach (i, Unused; R) - { - ranges[i] = rs[i]; - } } /** @@ -4688,24 +4741,21 @@ struct Zip(Ranges...) } static if (allSatisfy!(isForwardRange, R)) + { @property Zip save() { - Zip result = this; - foreach (i, Unused; R) - { - result.ranges[i] = result.ranges[i].save; - } - return result; + //Zip(ranges[0].save, ranges[1].save, ..., stoppingPolicy) + return mixin (q{Zip(%(ranges[%s]%|, %), stoppingPolicy)}.format(iota(0, R.length))); } + } - private void emplaceIfCan(T)(T* addr) + private .ElementType!(R[i]) tryGetInit(size_t i)() { - import std.conv : emplace; - - static if(__traits(compiles, emplace(addr))) - emplace(addr); - else + alias E = .ElementType!(R[i]); + static if (!is(typeof({static E i;}))) throw new Exception("Range with non-default constructable elements exhausted."); + else + return E.init; } /** @@ -4713,29 +4763,16 @@ struct Zip(Ranges...) */ @property ElementType front() { - import std.conv : emplace; - - ElementType result = void; - foreach (i, Unused; R) - { - auto addr = cast(Unqual!(typeof(result[i]))*) &result[i]; - if (ranges[i].empty) - { - emplaceIfCan(addr); - } - else - { - emplace(addr, ranges[i].front); - } - } - return result; + @property tryGetFront(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].front;} + //ElementType(tryGetFront!0, tryGetFront!1, ...) + return mixin(q{ElementType(%(tryGetFront!%s, %))}.format(iota(0, R.length))); } - static if (allSatisfy!(hasAssignableElements, R)) - { /** Sets the front of all iterated ranges. */ + static if (allSatisfy!(hasAssignableElements, R)) + { @property void front(ElementType v) { foreach (i, Unused; R) @@ -4755,22 +4792,9 @@ struct Zip(Ranges...) { ElementType moveFront() { - import std.conv : emplace; - - ElementType result = void; - foreach (i, Unused; R) - { - auto addr = cast(Unqual!(typeof(result[i]))*) &result[i]; - if (!ranges[i].empty) - { - emplace(addr, .moveFront(ranges[i])); - } - else - { - emplaceIfCan(addr); - } - } - return result; + @property tryMoveFront(size_t i)(){return ranges[i].empty ? tryGetInit!i() : .moveFront(ranges[i]);} + //ElementType(tryMoveFront!0, tryMoveFront!1, ...) + return mixin(q{ElementType(%(tryMoveFront!%s, %))}.format(iota(0, R.length))); } } @@ -4781,22 +4805,11 @@ struct Zip(Ranges...) { @property ElementType back() { - import std.conv : emplace; + //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness - ElementType result = void; - foreach (i, Unused; R) - { - auto addr = cast(Unqual!(typeof(result[i]))*) &result[i]; - if (!ranges[i].empty) - { - emplace(addr, ranges[i].back); - } - else - { - emplaceIfCan(addr); - } - } - return result; + @property tryGetBack(size_t i)(){return ranges[i].empty ? tryGetInit!i() : ranges[i].back;} + //ElementType(tryGetBack!0, tryGetBack!1, ...) + return mixin(q{ElementType(%(tryGetBack!%s, %))}.format(iota(0, R.length))); } /** @@ -4806,22 +4819,11 @@ struct Zip(Ranges...) { ElementType moveBack() { - import std.conv : emplace; + //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness - ElementType result = void; - foreach (i, Unused; R) - { - auto addr = cast(Unqual!(typeof(result[i]))*) &result[i]; - if (!ranges[i].empty) - { - emplace(addr, .moveBack(ranges[i])); - } - else - { - emplaceIfCan(addr); - } - } - return result; + @property tryMoveBack(size_t i)(){return ranges[i].empty ? tryGetInit!i() : .moveFront(ranges[i]);} + //ElementType(tryMoveBack!0, tryMoveBack!1, ...) + return mixin(q{ElementType(%(tryMoveBack!%s, %))}.format(iota(0, R.length))); } } @@ -4832,6 +4834,9 @@ struct Zip(Ranges...) { @property void back(ElementType v) { + //TODO: Fixme! BackElement != back of all ranges in case of jagged-ness. + //Not sure the call is even legal for StoppingPolicy.longest + foreach (i, Unused; R) { if (!ranges[i].empty) @@ -4875,12 +4880,14 @@ struct Zip(Ranges...) } } - static if (allSatisfy!(isBidirectionalRange, R)) /** Calls $(D popBack) for all controlled ranges. */ + static if (allSatisfy!(isBidirectionalRange, R)) + { void popBack() { + //TODO: Fixme! In case of jaggedness, this is wrong. import std.exception : enforce; final switch (stoppingPolicy) @@ -4907,6 +4914,7 @@ struct Zip(Ranges...) break; } } + } /** Returns the length of this range. Defined only if all ranges define @@ -4916,25 +4924,22 @@ struct Zip(Ranges...) { @property auto length() { - CommonType!(staticMap!(lengthType, R)) result = ranges[0].length; - if (stoppingPolicy == StoppingPolicy.requireSameLength) - return result; - foreach (i, Unused; R[1 .. $]) + static if (Ranges.length == 1) + return ranges[0].length; + else { + if (stoppingPolicy == StoppingPolicy.requireSameLength) + return ranges[0].length; + + //[min|max](ranges[0].length, ranges[1].length, ...) if (stoppingPolicy == StoppingPolicy.shortest) - { - result = min(ranges[i + 1].length, result); - } + return mixin(q{min(%(ranges[%s].length%|, %))}.format(iota(0, R.length))); else - { - assert(stoppingPolicy == StoppingPolicy.longest); - result = max(ranges[i + 1].length, result); - } + return mixin(q{max(%(ranges[%s].length%|, %))}.format(iota(0, R.length))); } - return result; } - alias length opDollar; + alias opDollar = length; } /** @@ -4942,48 +4947,42 @@ struct Zip(Ranges...) slicing. */ static if (allSatisfy!(hasSlicing, R)) + { auto opSlice(size_t from, size_t to) { - import std.conv : emplace; - //Slicing an infinite range yields the type Take!R //For finite ranges, the type Take!R aliases to R - Zip!(staticMap!(Take, R)) result = void; - emplace(&result.stoppingPolicy, stoppingPolicy); - foreach (i, Unused; R) - { - emplace(&result.ranges[i], ranges[i][from .. to]); - } - return result; + alias ZipResult = Zip!(staticMap!(Take, R)); + + //ZipResult(ranges[0][from .. to], ranges[1][from .. to], ..., stoppingPolicy) + return mixin (q{ZipResult(%(ranges[%s][from .. to]%|, %), stoppingPolicy)}.format(iota(0, R.length))); } + } - static if (allSatisfy!(isRandomAccessRange, R)) - { /** Returns the $(D n)th element in the composite range. Defined if all ranges offer random access. */ + static if (allSatisfy!(isRandomAccessRange, R)) + { ElementType opIndex(size_t n) { - import std.conv : emplace; + //TODO: Fixme! This may create an out of bounds access + //for StoppingPolicy.longest - ElementType result = void; - foreach (i, Range; R) - { - auto addr = cast(Unqual!(typeof(result[i]))*) &result[i]; - emplace(addr, ranges[i][n]); - } - return result; + //ElementType(ranges[0][n], ranges[1][n], ...) + return mixin (q{ElementType(%(ranges[%s][n]%|, %))}.format(iota(0, R.length))); } - static if (allSatisfy!(hasAssignableElements, R)) - { /** Assigns to the $(D n)th element in the composite range. Defined if all ranges offer random access. */ + static if (allSatisfy!(hasAssignableElements, R)) + { void opIndexAssign(ElementType v, size_t n) { + //TODO: Fixme! Not sure the call is even legal for StoppingPolicy.longest foreach (i, Range; R) { ranges[i][n] = v[i]; @@ -4999,15 +4998,11 @@ struct Zip(Ranges...) { ElementType moveAt(size_t n) { - import std.conv : emplace; + //TODO: Fixme! This may create an out of bounds access + //for StoppingPolicy.longest - ElementType result = void; - foreach (i, Range; R) - { - auto addr = cast(Unqual!(typeof(result[i]))*) &result[i]; - emplace(addr, .moveAt(ranges[i], n)); - } - return result; + //ElementType(.moveAt(ranges[0], n), .moveAt(ranges[1], n), ..., ) + return mixin (q{ElementType(%(.moveAt(ranges[%s], n)%|, %))}.format(iota(0, R.length))); } } } @@ -5020,6 +5015,16 @@ auto zip(Ranges...)(Ranges ranges) return Zip!Ranges(ranges); } +/// +unittest +{ + int[] a = [ 1, 2, 3 ]; + string[] b = [ "a", "b", "c" ]; + sort!("a[0] > b[0]")(zip(a, b)); + assert(a == [ 3, 2, 1 ]); + assert(b == [ "c", "b", "a" ]); +} + /// Ditto auto zip(Ranges...)(StoppingPolicy sp, Ranges ranges) if (Ranges.length && allSatisfy!(isInputRange, Ranges)) @@ -5044,6 +5049,7 @@ enum StoppingPolicy unittest { import std.exception : assertThrown, assertNotThrown; + import std.typecons : tuple; int[] a = [ 1, 2, 3 ]; float[] b = [ 1.0, 2.0, 3.0 ]; @@ -5060,7 +5066,9 @@ unittest assert(b == [2.0, 1.0, 3.0]); z = zip(StoppingPolicy.requireSameLength, a, b); - assertNotThrown((z.popBack(), z.popBack(), z.popBack())); + assertNotThrown(z.popBack()); + assertNotThrown(z.popBack()); + assertNotThrown(z.popBack()); assert(z.empty); assertThrown(z.popBack()); @@ -5083,7 +5091,7 @@ unittest auto stuff = tuple(tuple(a1, a2), tuple(filter!"a"(a1), filter!"a"(a2))); - alias Zip!(immutable(int)[], immutable(float)[]) FOO; + alias FOO = Zip!(immutable(int)[], immutable(float)[]); foreach(t; stuff.expand) { auto arr1 = t[0]; @@ -5096,7 +5104,7 @@ unittest auto zSame = zip(StoppingPolicy.requireSameLength, arr1, arr2); foreach(elem; zSame) {} assert(0); - } catch { /* It's supposed to throw.*/ } + } catch (Throwable) { /* It's supposed to throw.*/ } auto zLongest = zip(StoppingPolicy.longest, arr1, arr2); assert(!zLongest.ranges[0].empty); @@ -5162,8 +5170,10 @@ unittest assert(a == [1, 2, 3, 4, 5]); assert(b == [6, 5, 2, 1, 3]); } -unittest + +@safe pure unittest { + import std.typecons : tuple; auto LL = iota(1L, 1000L); auto z = zip(LL, [4]); @@ -5175,7 +5185,7 @@ unittest } // Text for Issue 11196 -unittest +@safe pure unittest { import std.exception : assertThrown; @@ -5185,6 +5195,21 @@ unittest assertThrown(zip(StoppingPolicy.longest, cast(S[]) null, new int[1]).front); } +@safe pure unittest //12007 +{ + static struct R + { + enum empty = false; + void popFront(){} + int front(){return 1;} @property + R save(){return this;} @property + void opAssign(R) @disable; + } + R r; + auto z = zip(r, r); + auto zz = z.save; +} + /* Generate lockstep's opApply function as a mixin string. If withIndex is true prepend a size_t index to the delegate. @@ -5258,23 +5283,13 @@ private string lockstepMixin(Ranges...)(bool withIndex) $(D foreach) loop, it will be silently accepted but any modifications to the variable will not be propagated to the underlying range. - Examples: - --- - auto arr1 = [1,2,3,4,5]; - auto arr2 = [6,7,8,9,10]; - - foreach(ref a, ref b; lockstep(arr1, arr2)) - { - a += b; - } - - assert(arr1 == [7,9,11,13,15]); - // Lockstep also supports iterating with an index variable: + Example: + ------- foreach(index, a, b; lockstep(arr1, arr2)) { writefln("Index %s: a = %s, b = %s", index, a, b); } - --- + ------- */ struct Lockstep(Ranges...) if (Ranges.length > 1 && allSatisfy!(isInputRange, Ranges)) @@ -5302,7 +5317,7 @@ private: // single range. template Lockstep(Range) { - alias Range Lockstep; + alias Lockstep = Range; } /// Ditto @@ -5315,10 +5330,24 @@ Lockstep!(Ranges) lockstep(Ranges...)(Ranges ranges) Lockstep!(Ranges) lockstep(Ranges...)(Ranges ranges, StoppingPolicy s) if (allSatisfy!(isInputRange, Ranges)) { - static if (Ranges.length > 1) - return Lockstep!Ranges(ranges, s); - else - return ranges[0]; + static if (Ranges.length > 1) + return Lockstep!Ranges(ranges, s); + else + return ranges[0]; +} + +/// +unittest +{ + auto arr1 = [1,2,3,4,5]; + auto arr2 = [6,7,8,9,10]; + + foreach(ref a, ref b; lockstep(arr1, arr2)) + { + a += b; + } + + assert(arr1 == [7,9,11,13,15]); } unittest @@ -5370,7 +5399,7 @@ unittest try { foreach(a, b; ls) {} assert(0); - } catch {} + } catch (Exception) {} // Just make sure 1-range case instantiates. This hangs the compiler // when no explicit stopping policy is specified due to Bug 4652. @@ -5513,20 +5542,14 @@ unittest The state of the sequence is stored as a $(D Tuple) so it can be heterogeneous. - - Example: - ---- - // a[0] = 1, a[1] = 2, a[n] = a[0] + n * a[1] - auto odds = sequence!("a[0] + n * a[1]")(1, 2); - ---- */ struct Sequence(alias fun, State) { private: import std.functional : binaryFun; - alias binaryFun!(fun, "a", "n") compute; - alias typeof(compute(State.init, cast(size_t) 1)) ElementType; + alias compute = binaryFun!(fun, "a", "n"); + alias ElementType = typeof(compute(State.init, cast(size_t) 1)); State _state; size_t _n; ElementType _cache; @@ -5584,15 +5607,28 @@ public: } /// Ditto -Sequence!(fun, Tuple!(State)) sequence(alias fun, State...)(State args) +auto sequence(alias fun, State...)(State args) +{ + import std.typecons : Tuple, tuple; + alias Return = Sequence!(fun, Tuple!State); + return Return(tuple(args)); +} + +/// +unittest { - return typeof(return)(tuple(args)); + auto odds = sequence!("a[0] + n * a[1]")(1, 2); + assert(odds.front == 1); + odds.popFront(); + assert(odds.front == 3); + odds.popFront(); + assert(odds.front == 5); } unittest { - auto y = Sequence!("a[0] + n * a[1]", Tuple!(int, int)) - (tuple(0, 4)); + import std.typecons : Tuple, tuple; + auto y = Sequence!("a[0] + n * a[1]", Tuple!(int, int))(tuple(0, 4)); static assert(isForwardRange!(typeof(y))); //@@BUG @@ -5609,19 +5645,6 @@ unittest } } - - -unittest -{ - // documentation example - auto odds = sequence!("a[0] + n * a[1]")(1, 2); - assert(odds.front == 1); - odds.popFront(); - assert(odds.front == 3); - odds.popFront(); - assert(odds.front == 5); -} - unittest { auto odds = sequence!("a[0] + n * a[1]")(1, 2); @@ -5655,17 +5678,6 @@ unittest Throws: $(D Exception) if $(D begin != end && step == 0), an exception is thrown. - - Example: - ---- - auto r = iota(0, 10, 1); - assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][])); - r = iota(0, 11, 3); - assert(equal(r, [0, 3, 6, 9][])); - assert(r[2] == 6); - auto rf = iota(0.0, 0.5, 0.1); - assert(approxEqual(rf, [0.0, 0.1, 0.2, 0.3, 0.4])); - ---- */ auto iota(B, E, S)(B begin, E end, S step) if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) @@ -5673,9 +5685,9 @@ if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) { import std.conv : unsigned; - alias CommonType!(Unqual!B, Unqual!E) Value; - alias Unqual!S StepType; - alias typeof(unsigned((end - begin) / step)) IndexType; + alias Value = CommonType!(Unqual!B, Unqual!E); + alias StepType = Unqual!S; + alias IndexType = typeof(unsigned((end - begin) / step)); static struct Result { @@ -5750,7 +5762,7 @@ if ((isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) } } - alias length opDollar; + alias opDollar = length; } return Result(begin, end, step); @@ -5769,8 +5781,8 @@ if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) { import std.conv : unsigned; - alias CommonType!(Unqual!B, Unqual!E) Value; - alias typeof(unsigned(end - begin)) IndexType; + alias Value = CommonType!(Unqual!B, Unqual!E); + alias IndexType = typeof(unsigned(end - begin)); static struct Result { @@ -5820,7 +5832,7 @@ if (isIntegral!(CommonType!(B, E)) || isPointer!(CommonType!(B, E))) return unsigned(pastLast - current); } - alias length opDollar; + alias opDollar = length; } return Result(begin, end); @@ -5837,7 +5849,7 @@ auto iota(E)(E end) auto iota(B, E, S)(B begin, E end, S step) if (isFloatingPoint!(CommonType!(B, E, S))) { - alias Unqual!(CommonType!(B, E, S)) Value; + alias Value = Unqual!(CommonType!(B, E, S)); static struct Result { private Value start, step; @@ -5910,12 +5922,25 @@ if (isFloatingPoint!(CommonType!(B, E, S))) return count - index; } - alias length opDollar; + alias opDollar = length; } return Result(begin, end, step); } +/// +unittest +{ + import std.math : approxEqual; + auto r = iota(0, 10, 1); + assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9][])); + r = iota(0, 11, 3); + assert(equal(r, [0, 3, 6, 9][])); + assert(r[2] == 6); + auto rf = iota(0.0, 0.5, 0.1); + assert(approxEqual(rf, [0.0, 0.1, 0.2, 0.3, 0.4])); +} + unittest { import std.math : approxEqual, nextUp, nextDown; @@ -6119,22 +6144,13 @@ enum TransverseOptions /** Given a range of ranges, iterate transversally through the first elements of each of the enclosed ranges. - - Example: - ---- - int[][] x = new int[][2]; - x[0] = [1, 2]; - x[1] = [3, 4]; - auto ror = frontTransversal(x); - assert(equal(ror, [ 1, 3 ][])); - --- */ struct FrontTransversal(Ror, TransverseOptions opt = TransverseOptions.assumeJagged) { - alias Unqual!(Ror) RangeOfRanges; - alias .ElementType!RangeOfRanges RangeType; - alias .ElementType!RangeType ElementType; + alias RangeOfRanges = Unqual!(Ror); + alias RangeType = .ElementType!RangeOfRanges; + alias ElementType = .ElementType!RangeType; private void prime() { @@ -6333,6 +6349,16 @@ FrontTransversal!(RangeOfRanges, opt) frontTransversal( return typeof(return)(rr); } +/// +unittest +{ + int[][] x = new int[][2]; + x[0] = [1, 2]; + x[1] = [3, 4]; + auto ror = frontTransversal(x); + assert(equal(ror, [ 1, 3 ][])); +} + unittest { static assert(is(FrontTransversal!(immutable int[][]))); @@ -6396,22 +6422,13 @@ unittest { Given a range of ranges, iterate transversally through the the $(D n)th element of each of the enclosed ranges. All elements of the enclosing range must offer random access. - - Example: - ---- - int[][] x = new int[][2]; - x[0] = [1, 2]; - x[1] = [3, 4]; - auto ror = transversal(x, 1); - assert(equal(ror, [ 2, 4 ][])); - --- */ struct Transversal(Ror, TransverseOptions opt = TransverseOptions.assumeJagged) { - private alias Unqual!Ror RangeOfRanges; - private alias ElementType!RangeOfRanges InnerRange; - private alias ElementType!InnerRange E; + private alias RangeOfRanges = Unqual!Ror; + private alias InnerRange = ElementType!RangeOfRanges; + private alias E = ElementType!InnerRange; private void prime() { @@ -6592,7 +6609,7 @@ struct Transversal(Ror, return _input.length; } - alias length opDollar; + alias opDollar = length; } /** @@ -6623,6 +6640,16 @@ Transversal!(RangeOfRanges, opt) transversal return typeof(return)(rr, n); } +/// +unittest +{ + int[][] x = new int[][2]; + x[0] = [1, 2]; + x[1] = [3, 4]; + auto ror = transversal(x, 1); + assert(equal(ror, [ 2, 4 ][])); +} + unittest { int[][] x = new int[][2]; @@ -6663,7 +6690,7 @@ unittest } // Test w/o ref return. - alias DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random) D; + alias D = DummyRange!(ReturnBy.Value, Length.Yes, RangeType.Random); auto drs = [D.init, D.init]; foreach(num; 0..10) { auto t = transversal!(TransverseOptions.enforceNotJagged)(drs, num); @@ -6682,7 +6709,7 @@ unittest struct Transposed(RangeOfRanges) { - //alias typeof(map!"a.front"(RangeOfRanges.init)) ElementType; + //alias ElementType = typeof(map!"a.front"(RangeOfRanges.init)); this(RangeOfRanges input) { @@ -6731,6 +6758,7 @@ auto transposed(RangeOfRanges)(RangeOfRanges rr) return Transposed!RangeOfRanges(rr); } +/// unittest { int[][] x = new int[][2]; @@ -6755,20 +6783,6 @@ may also repeat elements. $(D Source) must be a random access range. The returned range will be bidirectional or random-access if $(D Indices) is bidirectional or random-access, respectively. - -Examples: ---- -auto source = [1, 2, 3, 4, 5]; -auto indices = [4, 3, 1, 2, 0, 4]; -auto ind = indexed(source, indices); -assert(equal(ind, [5, 4, 2, 3, 1, 5])); - -// When elements of indices are duplicated and Source has lvalue elements, -// these are aliased in ind. -ind[0]++; -assert(ind[0] == 6); -assert(ind[5] == 6); ---- */ struct Indexed(Source, Indices) if(isRandomAccessRange!Source && isInputRange!Indices && @@ -6884,7 +6898,7 @@ struct Indexed(Source, Indices) return _indices.length; } - alias length opDollar; + alias opDollar = length; } static if(isRandomAccessRange!Indices) @@ -6972,10 +6986,19 @@ Indexed!(Source, Indices) indexed(Source, Indices)(Source source, Indices indice return typeof(return)(source, indices); } +/// +unittest +{ + auto source = [1, 2, 3, 4, 5]; + auto indices = [4, 3, 1, 2, 0, 4]; + auto ind = indexed(source, indices); + assert(equal(ind, [5, 4, 2, 3, 1, 5])); + assert(equal(retro(ind), [5, 1, 3, 2, 4, 5])); +} + unittest { { - // Test examples. auto ind = indexed([1, 2, 3, 4, 5], [1, 3, 4]); assert(ind.physicalIndex(0) == 1); } @@ -6983,15 +7006,16 @@ unittest auto source = [1, 2, 3, 4, 5]; auto indices = [4, 3, 1, 2, 0, 4]; auto ind = indexed(source, indices); - assert(equal(ind, [5, 4, 2, 3, 1, 5])); - assert(equal(retro(ind), [5, 1, 3, 2, 4, 5])); // When elements of indices are duplicated and Source has lvalue elements, // these are aliased in ind. ind[0]++; assert(ind[0] == 6); assert(ind[5] == 6); +} +unittest +{ foreach(DummyType; AllDummyRanges) { auto d = DummyType.init; @@ -7008,18 +7032,6 @@ $(D source) range. $(D Source) must be a forward range. If $(D !isInfinite!Source) and $(D source.walkLength) is not evenly divisible by $(D chunkSize), the back element of this range will contain fewer than $(D chunkSize) elements. - -Examples: ---- -auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; -auto chunks = chunks(source, 4); -assert(chunks[0] == [1, 2, 3, 4]); -assert(chunks[1] == [5, 6, 7, 8]); -assert(chunks[2] == [9, 10]); -assert(chunks.back == chunks[2]); -assert(chunks.front == chunks[0]); -assert(chunks.length == 3); ---- */ struct Chunks(Source) if (isForwardRange!Source) @@ -7223,6 +7235,7 @@ if (isForwardRange!Source) return typeof(return)(source, chunkSize); } +/// unittest { auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; @@ -7234,7 +7247,12 @@ unittest assert(chunks.front == chunks[0]); assert(chunks.length == 3); assert(equal(retro(array(chunks)), array(retro(chunks)))); +} +unittest +{ + auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + auto chunks = chunks(source, 4); auto chunks2 = chunks.save; chunks.popFront(); assert(chunks[0] == [5, 6, 7, 8]); @@ -7501,12 +7519,12 @@ auto only(Values...)(auto ref Values values) /// unittest { + import std.uni; assert(equal(only('♡'), "♡")); assert([1, 2, 3, 4].findSplitBefore(only(3))[0] == [1, 2]); assert(only("one", "two", "three").joiner(" ").equal("one two three")); - import std.uni; string title = "The D Programming Language"; assert(filter!isUpper(title).map!only().join(".") == "T.D.P.L"); } @@ -7545,6 +7563,7 @@ unittest // Tests the single-element result unittest { + import std.typecons : tuple; foreach (x; tuple(1, '1', 1.0, "1", [1])) { auto a = only(x); @@ -7566,7 +7585,7 @@ unittest b.popBack(); assert(b.empty && b.length == 0 && b[].empty); - alias typeof(a) A; + alias A = typeof(a); static assert(isInputRange!A); static assert(isForwardRange!A); static assert(isBidirectionalRange!A); @@ -7672,7 +7691,7 @@ unittest static struct Test { int* a; } immutable(Test) test; - only(test, test); // Works with mutable indirection + cast(void)only(test, test); // Works with mutable indirection } /** @@ -7694,6 +7713,24 @@ ElementType!R moveFront(R)(R r) } } +/// +unittest +{ + auto a = [ 1, 2, 3 ]; + assert(moveFront(a) == 1); + + // define a perfunctory input range + struct InputRange + { + @property bool empty() { return false; } + @property int front() { return 42; } + void popFront() {} + int moveFront() { return 43; } + } + InputRange r; + assert(moveFront(r) == 43); +} + unittest { struct R @@ -7724,11 +7761,12 @@ ElementType!R moveBack(R)(R r) } } +/// unittest { struct TestRange { - int payload; + int payload = 5; @property bool empty() { return false; } @property TestRange save() { return this; } @property ref int front() { return payload; } @@ -7739,6 +7777,7 @@ unittest static assert(isBidirectionalRange!TestRange); TestRange r; auto x = moveBack(r); + assert(x == 5); } /** @@ -7760,21 +7799,18 @@ ElementType!R moveAt(R, I)(R r, I i) if (isIntegral!I) } } +/// unittest { - auto a = [ 1, 2, 3 ]; - assert(moveFront(a) == 1); - // define a perfunctory input range - struct InputRange + auto a = [1,2,3,4]; + foreach(idx, it; a) { - @property bool empty() { return false; } - @property int front() { return 42; } - void popFront() {} - int moveFront() { return 43; } + assert(it == moveAt(a, idx)); } - InputRange r; - assert(moveFront(r) == 43); +} +unittest +{ foreach(DummyType; AllDummyRanges) { auto d = DummyType.init; assert(moveFront(d) == 1); @@ -7891,7 +7927,7 @@ interface RandomAccessFinite(E) : BidirectionalRange!(E) { @property size_t length(); /// - alias length opDollar; + alias opDollar = length; // Can't support slicing until issues with requiring slicing for all // finite random access ranges are fully resolved. @@ -7984,33 +8020,33 @@ class OutputRangeObject(R, E...) : staticMap!(OutputRange, E) { /**Returns the interface type that best matches $(D R).*/ template MostDerivedInputRange(R) if (isInputRange!(Unqual!R)) { - private alias ElementType!R E; + private alias E = ElementType!R; static if (isRandomAccessRange!R) { static if (isInfinite!R) { - alias RandomAccessInfinite!E MostDerivedInputRange; + alias MostDerivedInputRange = RandomAccessInfinite!E; } else static if (hasAssignableElements!R) { - alias RandomFiniteAssignable!E MostDerivedInputRange; + alias MostDerivedInputRange = RandomFiniteAssignable!E; } else { - alias RandomAccessFinite!E MostDerivedInputRange; + alias MostDerivedInputRange = RandomAccessFinite!E; } } else static if (isBidirectionalRange!R) { static if (hasAssignableElements!R) { - alias BidirectionalAssignable!E MostDerivedInputRange; + alias MostDerivedInputRange = BidirectionalAssignable!E; } else { - alias BidirectionalRange!E MostDerivedInputRange; + alias MostDerivedInputRange = BidirectionalRange!E; } } else static if (isForwardRange!R) { static if (hasAssignableElements!R) { - alias ForwardAssignable!E MostDerivedInputRange; + alias MostDerivedInputRange = ForwardAssignable!E; } else { - alias ForwardRange!E MostDerivedInputRange; + alias MostDerivedInputRange = ForwardRange!E; } } else { static if (hasAssignableElements!R) { - alias InputAssignable!E MostDerivedInputRange; + alias MostDerivedInputRange = InputAssignable!E; } else { - alias InputRange!E MostDerivedInputRange; + alias MostDerivedInputRange = InputRange!E; } } } @@ -8021,15 +8057,15 @@ template MostDerivedInputRange(R) if (isInputRange!(Unqual!R)) { */ template InputRangeObject(R) if (isInputRange!(Unqual!R)) { static if (is(R : InputRange!(ElementType!R))) { - alias R InputRangeObject; + alias InputRangeObject = R; } else static if (!is(Unqual!R == R)) { - alias InputRangeObject!(Unqual!R) InputRangeObject; + alias InputRangeObject = InputRangeObject!(Unqual!R); } else { /// class InputRangeObject : MostDerivedInputRange!(R) { private R _range; - private alias ElementType!R E; + private alias E = ElementType!R; this(R range) { this._range = range; @@ -8092,7 +8128,7 @@ template InputRangeObject(R) if (isInputRange!(Unqual!R)) { return _range.length; } - alias length opDollar; + alias opDollar = length; // Can't support slicing until all the issues with // requiring slicing support for finite random access @@ -8238,6 +8274,11 @@ template isTwoWayCompatible(alias fn, T1, T2) */ enum SearchPolicy { + /** + Searches in a linear fashion. + */ + linear, + /** Searches with a step that is grows linearly (1, 2, 3,...) leading to a quadratic search schedule (indexes tried are 0, 1, @@ -8284,51 +8325,21 @@ enum SearchPolicy } /** - Represents a sorted random-access range. In addition to the regular - range primitives, supports fast operations using binary search. To - obtain a $(D SortedRange) from an unsorted range $(D r), use - $(XREF algorithm, sort) which sorts $(D r) in place and returns the - corresponding $(D SortedRange). To construct a $(D SortedRange) - from a range $(D r) that is known to be already sorted, use - $(LREF assumeSorted) described below. - - Example: - - ---- - auto a = [ 1, 2, 3, 42, 52, 64 ]; - auto r = assumeSorted(a); - assert(r.contains(3)); - assert(!r.contains(32)); - auto r1 = sort!"a > b"(a); - assert(r1.contains(3)); - assert(!r1.contains(32)); - assert(r1.release() == [ 64, 52, 42, 3, 2, 1 ]); - ---- - - $(D SortedRange) could accept ranges weaker than random-access, but it - is unable to provide interesting functionality for them. Therefore, - $(D SortedRange) is currently restricted to random-access ranges. - - No copy of the original range is ever made. If the underlying range is - changed concurrently with its corresponding $(D SortedRange) in ways - that break its sortedness, $(D SortedRange) will work erratically. - - Example: - - ---- - auto a = [ 1, 2, 3, 42, 52, 64 ]; - auto r = assumeSorted(a); - assert(r.contains(42)); - swap(a[3], a[5]); // illegal to break sortedness of original range - assert(!r.contains(42)); // passes although it shouldn't - ---- +Represents a sorted range. In addition to the regular range +primitives, supports additional operations that take advantage of the +ordering, such as merge and binary search. To obtain a $(D +SortedRange) from an unsorted range $(D r), use $(XREF algorithm, +sort) which sorts $(D r) in place and returns the corresponding $(D +SortedRange). To construct a $(D SortedRange) from a range $(D r) that +is known to be already sorted, use $(LREF assumeSorted) described +below. */ struct SortedRange(Range, alias pred = "a < b") -if (isRandomAccessRange!Range && hasLength!Range) +if (isInputRange!Range) { private import std.functional : binaryFun; - private alias binaryFun!pred predFun; + private alias predFun = binaryFun!pred; private bool geq(L, R)(L lhs, R rhs) { return !predFun(lhs, rhs); @@ -8342,8 +8353,19 @@ if (isRandomAccessRange!Range && hasLength!Range) // Undocummented because a clearer way to invoke is by calling // assumeSorted. this(Range input) + out + { + // moved out of the body as a workaround for Issue 12661 + dbgVerifySorted(); + } + body { this._input = input; + } + + // Assertion only. + private void dbgVerifySorted() + { if(!__ctfe) debug { @@ -8351,15 +8373,25 @@ if (isRandomAccessRange!Range && hasLength!Range) import std.conv : text; import std.random : MinstdRand, uniform; - // Check the sortedness of the input - if (this._input.length < 2) return; - immutable size_t msb = bsr(this._input.length) + 1; - assert(msb > 0 && msb <= this._input.length); - immutable step = this._input.length / msb; - static MinstdRand gen; - immutable start = uniform(0, step, gen); - auto st = stride(this._input, step); - assert(isSorted!pred(st), text(st)); + static if (isRandomAccessRange!Range) + { + // Check the sortedness of the input + if (this._input.length < 2) return; + immutable size_t msb = bsr(this._input.length) + 1; + assert(msb > 0 && msb <= this._input.length); + immutable step = this._input.length / msb; + static MinstdRand gen; + immutable start = uniform(0, step, gen); + auto st = stride(this._input, step); + static if (is(typeof(text(st)))) + { + assert(isSorted!pred(st), text(st)); + } + else + { + assert(isSorted!pred(st)); + } + } } } @@ -8370,6 +8402,7 @@ if (isRandomAccessRange!Range && hasLength!Range) } /// Ditto + static if (isForwardRange!Range) @property auto save() { // Avoid the constructor @@ -8379,7 +8412,7 @@ if (isRandomAccessRange!Range && hasLength!Range) } /// Ditto - @property auto front() + @property auto ref front() { return _input.front; } @@ -8391,22 +8424,26 @@ if (isRandomAccessRange!Range && hasLength!Range) } /// Ditto - @property auto back() + static if (isBidirectionalRange!Range) { - return _input.back; - } + @property auto ref back() + { + return _input.back; + } - /// Ditto - void popBack() - { - _input.popBack(); + /// Ditto + void popBack() + { + _input.popBack(); + } } /// Ditto - auto opIndex(size_t i) - { - return _input[i]; - } + static if (isRandomAccessRange!Range) + auto ref opIndex(size_t i) + { + return _input[i]; + } /// Ditto static if (hasSlicing!Range) @@ -8419,13 +8456,15 @@ if (isRandomAccessRange!Range && hasLength!Range) } /// Ditto - @property size_t length() //const + static if (hasLength!Range) { - return _input.length; + @property size_t length() //const + { + return _input.length; + } + alias opDollar = length; } - alias length opDollar; - /** Releases the controlled range and returns it. */ @@ -8438,7 +8477,7 @@ if (isRandomAccessRange!Range && hasLength!Range) // of the range and then 1 for the rest, returns the index at // which the first 1 appears. Used internally by the search routines. private size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v) - if (sp == SearchPolicy.binarySearch) + if (sp == SearchPolicy.binarySearch && isRandomAccessRange!Range) { size_t first = 0, count = _input.length; while (count > 0) @@ -8459,7 +8498,8 @@ if (isRandomAccessRange!Range && hasLength!Range) // Specialization for trot and gallop private size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v) - if (sp == SearchPolicy.trot || sp == SearchPolicy.gallop) + if ((sp == SearchPolicy.trot || sp == SearchPolicy.gallop) + && isRandomAccessRange!Range) { if (empty || test(front, v)) return 0; immutable count = length; @@ -8491,7 +8531,8 @@ if (isRandomAccessRange!Range && hasLength!Range) // Specialization for trotBackwards and gallopBackwards private size_t getTransitionIndex(SearchPolicy sp, alias test, V)(V v) - if (sp == SearchPolicy.trotBackwards || sp == SearchPolicy.gallopBackwards) + if ((sp == SearchPolicy.trotBackwards || sp == SearchPolicy.gallopBackwards) + && isRandomAccessRange!Range) { immutable count = length; if (empty || !test(back, v)) return count; @@ -8522,7 +8563,7 @@ if (isRandomAccessRange!Range && hasLength!Range) // lowerBound /** - This function uses binary search with policy $(D sp) to find the + This function uses a search with policy $(D sp) to find the largest left subrange on which $(D pred(x, value)) is $(D true) for all $(D x) (e.g., if $(D pred) is "less than", returns the portion of the range with elements strictly smaller than $(D value)). The search @@ -8538,32 +8579,52 @@ if (isRandomAccessRange!Range && hasLength!Range) ---- */ auto lowerBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value) - if (isTwoWayCompatible!(predFun, ElementType!Range, V)) + if (isTwoWayCompatible!(predFun, ElementType!Range, V) + && hasSlicing!Range) { return this[0 .. getTransitionIndex!(sp, geq)(value)]; } // upperBound /** - This function uses binary search with policy $(D sp) to find the - largest right subrange on which $(D pred(value, x)) is $(D true) - for all $(D x) (e.g., if $(D pred) is "less than", returns the - portion of the range with elements strictly greater than $(D - value)). The search schedule and its complexity are documented in - $(LREF SearchPolicy). See also STL's - $(WEB sgi.com/tech/stl/lower_bound.html,upper_bound). +This function searches with policy $(D sp) to find the largest right +subrange on which $(D pred(value, x)) is $(D true) for all $(D x) +(e.g., if $(D pred) is "less than", returns the portion of the range +with elements strictly greater than $(D value)). The search schedule +and its complexity are documented in $(LREF SearchPolicy). - Example: - ---- - auto a = assumeSorted([ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]); - auto p = a.upperBound(3); - assert(equal(p, [4, 4, 5, 6])); - ---- +For ranges that do not offer random access, $(D SearchPolicy.linear) +is the only policy allowed (and it must be specified explicitly lest it exposes +user code to unexpected inefficiencies). For random-access searches, all +policies are allowed, and $(D SearchPolicy.binarySearch) is the default. + +See_Also: STL's $(WEB sgi.com/tech/stl/lower_bound.html,upper_bound). + +Example: +---- +auto a = assumeSorted([ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]); +auto p = a.upperBound(3); +assert(equal(p, [4, 4, 5, 6])); +---- */ auto upperBound(SearchPolicy sp = SearchPolicy.binarySearch, V)(V value) if (isTwoWayCompatible!(predFun, ElementType!Range, V)) { - return this[getTransitionIndex!(sp, gt)(value) .. length]; + static assert(hasSlicing!Range || sp == SearchPolicy.linear, + "Specify SearchPolicy.linear explicitly for " + ~ typeof(this).stringof); + static if (sp == SearchPolicy.linear) + { + for (; !_input.empty && !predFun(value, _input.front); + _input.popFront()) + { + } + return this; + } + else + { + return this[getTransitionIndex!(sp, gt)(value) .. length]; + } } // equalRange @@ -8588,7 +8649,8 @@ if (isRandomAccessRange!Range && hasLength!Range) ---- */ auto equalRange(V)(V value) - if (isTwoWayCompatible!(predFun, ElementType!Range, V)) + if (isTwoWayCompatible!(predFun, ElementType!Range, V) + && isRandomAccessRange!Range) { size_t first = 0, count = _input.length; while (count > 0) @@ -8644,8 +8706,10 @@ assert(equal(r[2], [ 4, 4, 5, 6 ])); ---- */ auto trisect(V)(V value) - if (isTwoWayCompatible!(predFun, ElementType!Range, V)) + if (isTwoWayCompatible!(predFun, ElementType!Range, V) + && isRandomAccessRange!Range) { + import std.typecons : tuple; size_t first = 0, count = _input.length; while (count > 0) { @@ -8692,6 +8756,7 @@ sgi.com/tech/stl/binary_search.html, binary_search). */ bool contains(V)(V value) + if (isRandomAccessRange!Range) { size_t first = 0, count = _input.length; while (count > 0) @@ -8718,7 +8783,7 @@ sgi.com/tech/stl/binary_search.html, binary_search). } } -// Doc examples +/// unittest { auto a = [ 1, 2, 3, 42, 52, 64 ]; @@ -8731,6 +8796,24 @@ unittest assert(r1.release() == [ 64, 52, 42, 3, 2, 1 ]); } +/** +$(D SortedRange) could accept ranges weaker than random-access, but it +is unable to provide interesting functionality for them. Therefore, +$(D SortedRange) is currently restricted to random-access ranges. + +No copy of the original range is ever made. If the underlying range is +changed concurrently with its corresponding $(D SortedRange) in ways +that break its sortedness, $(D SortedRange) will work erratically. +*/ +unittest +{ + auto a = [ 1, 2, 3, 42, 52, 64 ]; + auto r = assumeSorted(a); + assert(r.contains(42)); + swap(a[3], a[5]); // illegal to break sortedness of original range + assert(!r.contains(42)); // passes although it shouldn't +} + unittest { auto a = [ 10, 20, 30, 30, 30, 40, 40, 50, 60 ]; @@ -8820,6 +8903,21 @@ unittest auto s = assumeSorted(arr); } +// Test on an input range +unittest +{ + import std.stdio, std.file, std.path, std.conv; + auto name = buildPath(tempDir(), "test.std.range." ~ text(__LINE__)); + auto f = File(name, "w"); + // write a sorted range of lines to the file + f.write("abc\ndef\nghi\njkl"); + f.close(); + f.open(name, "r"); + auto r = assumeSorted(f.byLine()); + auto r1 = r.upperBound!(SearchPolicy.linear)("def"); + assert(r1.front == "ghi", r1.front); +} + /** Assumes $(D r) is sorted by predicate $(D pred) and returns the corresponding $(D SortedRange!(pred, R)) having $(D r) as support. To @@ -8834,7 +8932,7 @@ almost-sorted range is likely to pass it). To check for sortedness at cost $(BIGOH n), use $(XREF algorithm,isSorted). */ auto assumeSorted(alias pred = "a < b", R)(R r) -if (isRandomAccessRange!(Unqual!R)) +if (isInputRange!(Unqual!R)) { return SortedRange!(Unqual!R, pred)(r); } @@ -9112,12 +9210,7 @@ assert(buffer2 == [11, 12, 13, 14, 15]); } else { - private static void _testSave(R)(R* range) - { - (*range).save; - } - - static if(isSafe!(_testSave!R)) + static if(isSafe!((R* r) => (*r).save)) { @property auto save() @trusted { @@ -9155,7 +9248,7 @@ assert(buffer2 == [11, 12, 13, 14, 15]); private static string _genSave() @safe pure nothrow { return `import std.conv;` ~ - `alias typeof((*_range).save) S;` ~ + `alias S = typeof((*_range).save);` ~ `static assert(isForwardRange!S, S.stringof ~ " is not a forward range.");` ~ `auto mem = new void[S.sizeof];` ~ `emplace!S(mem, cast(S)(*_range).save);` ~ @@ -9315,7 +9408,7 @@ assert(buffer2 == [11, 12, 13, 14, 15]); private static string _genOpSlice() @safe pure nothrow { return `import std.conv;` ~ - `alias typeof((*_range)[begin .. end]) S;` ~ + `alias S = typeof((*_range)[begin .. end]);` ~ `static assert(hasSlicing!S, S.stringof ~ " is not sliceable.");` ~ `auto mem = new void[S.sizeof];` ~ `emplace!S(mem, cast(S)(*_range)[begin .. end]);` ~ @@ -9732,3 +9825,18 @@ unittest // bug 9060 static void foo(R)(R r) { until!(x => x > 7)(r); } foo(r); } + +/********************************* + * An OutputRange that discards the data it receives. + */ +struct NullSink +{ + void put(E)(E){} +} + +unittest +{ + import std.algorithm; + [4, 5, 6].map!(x => x * 2).copy(NullSink()); +} + diff --git a/libphobos/src/std/regex.d b/libphobos/src/std/regex.d index 130fae541..8e29e48c0 100644 --- a/libphobos/src/std/regex.d +++ b/libphobos/src/std/regex.d @@ -14,13 +14,12 @@ void main() { // Print out all possible dd/mm/yy(yy) dates found in user input. - // g - global: find all matches. - auto r = regex(r"\b[0-9][0-9]?/[0-9][0-9]?/[0-9][0-9](?:[0-9][0-9])?\b", "g"); + auto r = regex(r"\b[0-9][0-9]?/[0-9][0-9]?/[0-9][0-9](?:[0-9][0-9])?\b"); foreach(line; stdin.byLine) { - // Match returns a range that can be iterated + // matchAll() returns a range that can be iterated // to get all subsequent matches. - foreach(c; match(line, r)) + foreach(c; matchAll(line, r)) writeln(c.hit); } } @@ -30,15 +29,15 @@ auto ctr = ctRegex!(`^.*/([^/]+)/?$`); // It works just like a normal regex: - auto m2 = match("foo/bar", ctr); // First match found here, if any - assert(m2); // Be sure to check if there is a match before examining contents! - assert(m2.captures[1] == "bar"); // Captures is a range of submatches: 0 = full match. + auto c2 = matchFirst("foo/bar", ctr); // First match found here, if any + assert(!c2.empty); // Be sure to check if there is a match before examining contents! + assert(c2[1] == "bar"); // Captures is a range of submatches: 0 = full match. ... - // The result of the match is directly testable with if/assert/while. + // The result of the $(D matchAll) is directly testable with if/assert/while. // e.g. test if a string consists of letters: - assert(match("Letter", `^\p{L}+$`)); + assert(matchFirst("Letter", `^\p{L}+$`)); --- @@ -253,14 +252,13 @@ Macros: module std.regex; -import std.internal.uni, std.internal.uni_tab;//Unicode property tables import std.array, std.algorithm, std.range, std.conv, std.exception, std.traits, std.typetuple, - std.utf, std.format, std.typecons, std.bitmanip, + std.uni, std.utf, std.format, std.typecons, std.bitmanip, std.functional, std.exception; import core.bitop, core.stdc.string, core.stdc.stdlib; -static import ascii = std.ascii; +static import std.ascii; import std.string : representation; debug(std_regex_parser) import std.stdio; //trace parser progress @@ -272,9 +270,6 @@ debug(std_regex_test) import std.stdio; //trace test suite progress private: -import std.uni : isAlpha, isWhite; - - // IR bit pattern: 0b1_xxxxx_yy // where yy indicates class of instruction, xxxxx for actual operation code // 00: atom, a normal instruction @@ -755,15 +750,16 @@ enum maxCharsetUsed = 6; enum maxCachedTries = 8; -alias Trie = CodepointTrie!8; +alias CodepointSetTrie!(13, 8) Trie; +alias codepointSetTrie!(13, 8) makeTrie; -Trie[const(CodepointSet)] trieCache; +Trie[CodepointSet] trieCache; //accessor with caching -@trusted Trie getTrie(in CodepointSet set) +@trusted Trie getTrie(CodepointSet set) {// @@@BUG@@@ 6357 almost all properties of AA are not @safe if(__ctfe || maxCachedTries == 0) - return Trie(set); + return makeTrie(set); else { auto p = set in trieCache; @@ -774,23 +770,23 @@ Trie[const(CodepointSet)] trieCache; // flush entries in trieCache trieCache = null; } - return (trieCache[set] = Trie(set)); + return (trieCache[set] = makeTrie(set)); } } //property for \w character class @property CodepointSet wordCharacter() { - return memoizeExpr!("CodepointSet.init.add(unicodeAlphabetic).add(unicodeMn).add(unicodeMc) - .add(unicodeMe).add(unicodeNd).add(unicodePc)")(); + return memoizeExpr!("unicode.Alphabetic | unicode.Mn | unicode.Mc + | unicode.Me | unicode.Nd | unicode.Pc")(); } @property Trie wordTrie() { - return memoizeExpr!("Trie(wordCharacter)")(); + return memoizeExpr!("makeTrie(wordCharacter)")(); } -auto memoizeExpr(string expr)() +@trusted auto memoizeExpr(string expr)() { if(__ctfe) return mixin(expr); @@ -805,66 +801,29 @@ auto memoizeExpr(string expr)() return slot; } -/+ - fetch codepoint set corresponding to a name (InBlock or binary property) -+/ -@trusted const(CodepointSet) getUnicodeSet(in char[] name, bool negated, bool casefold) +auto caseEnclose(CodepointSet set) { - alias ucmp = comparePropertyName; - CodepointSet s; - - //unicode property - //helper: direct access with a sanity check - if(ucmp(name, "L") == 0 || ucmp(name, "Letter") == 0) - { - s.add(unicodeLu).add(unicodeLl).add(unicodeLt) - .add(unicodeLo).add(unicodeLm); - } - else if(ucmp(name,"LC") == 0 || ucmp(name,"Cased Letter") == 0) - { - s.add(unicodeLl).add(unicodeLu).add(unicodeLt);//Title case - } - else if(ucmp(name, "M") == 0 || ucmp(name, "Mark") == 0) - { - s.add(unicodeMn).add(unicodeMc).add(unicodeMe); - } - else if(ucmp(name, "P") == 0 || ucmp(name, "Punctuation") == 0) - { - s.add(unicodePc).add(unicodePd).add(unicodePs).add(unicodePe) - .add(unicodePi).add(unicodePf).add(unicodePo); - } - else if(ucmp(name, "S") == 0 || ucmp(name, "Symbol") == 0) - { - s.add(unicodeSm).add(unicodeSc).add(unicodeSk).add(unicodeSo); - } - else if(ucmp(name, "Z") == 0 || ucmp(name, "Separator") == 0) + auto cased = set & unicode.LC; + foreach (dchar ch; cased.byCodepoint) { - s.add(unicodeZs).add(unicodeZl).add(unicodeZp); - } - else if(ucmp(name, "C") == 0 || ucmp(name, "Other") == 0) - { - s.add(unicodeCo).add(unicodeLo).add(unicodeNo) - .add(unicodeSo).add(unicodePo); - } - else if(ucmp(name, "any") == 0) - s.add(Interval(0,0x10FFFF)); - else if(ucmp(name, "ascii") == 0) - s.add(Interval(0,0x7f)); - else - { - auto range = assumeSorted!((x,y) => ucmp(x.name, y.name) < 0)(unicodeProperties); - //creating empty Codepointset is a workaround - auto eq = range.lowerBound(UnicodeProperty(cast(string)name,CodepointSet.init)).length; - enforce(eq != range.length && ucmp(name,range[eq].name) == 0, - "invalid property name"); - s = range[eq].set.dup; + foreach(c; simpleCaseFoldings(ch)) + set |= c; } + return set; +} +/+ + fetch codepoint set corresponding to a name (InBlock or binary property) ++/ +@trusted CodepointSet getUnicodeSet(in char[] name, bool negated, bool casefold) +{ + CodepointSet s = unicode(name); + //FIXME: caseEnclose for new uni as Set | CaseEnclose(SET && LC) if(casefold) - s = caseEnclose(s); + s = caseEnclose(s); if(negated) - s.negate(); - return cast(const CodepointSet)s; + s = s.inverted; + return s; } //basic stack, just in case it gets used anywhere else then Parser @@ -883,7 +842,7 @@ auto memoizeExpr(string expr)() auto val = data[$ - 1]; data = data[0 .. $ - 1]; if(!__ctfe) - data.assumeSafeAppend(); + cast(void)data.assumeSafeAppend(); return val; } @@ -920,7 +879,7 @@ struct Parser(R) uint nesting = 0; uint lookaroundNest = 0; uint counterDepth = 0; //current depth of nested counted repetitions - const(CodepointSet)[] charsets; // + CodepointSet[] charsets; // const(Trie)[] tries; // uint[] backrefed; //bitarray for groups @@ -953,6 +912,14 @@ struct Parser(R) backrefed[n / 32] |= 1 << (n & 31); } + bool isOpenGroup(uint n) + { + // walk the fixup stack and see if there are groups labeled 'n' + // fixup '0' is reserved for alternations + return fixupStack.data[1..$]. + canFind!(fix => ir[fix].code == IR.GroupStart && ir[fix].data == n)(); + } + @property dchar current(){ return _current; } bool _next() @@ -1002,7 +969,7 @@ struct Parser(R) uint parseDecimal() { uint r = 0; - while(ascii.isDigit(current)) + while(std.ascii.isDigit(current)) { if(r >= (uint.max/10)) error("Overflow in decimal number"); @@ -1090,7 +1057,7 @@ struct Parser(R) error("Expected alpha starting a named group"); name ~= current; while(next() && (isAlpha(current) || - current == '_' || ascii.isDigit(current))) + current == '_' || std.ascii.isDigit(current))) { name ~= current; } @@ -1102,7 +1069,7 @@ struct Parser(R) auto t = NamedGroup(name, nglob); auto d = assumeSorted!"a.name < b.name"(dict); auto ind = d.lowerBound(t).length; - insertInPlaceAlt(dict, ind, t); + insertInPlace(dict, ind, t); put(Bytecode(IR.GroupStart, nglob)); break; case '<': @@ -1191,7 +1158,7 @@ struct Parser(R) len = cast(uint)ir.length - fix - (ir[fix].length - 1); orStart = fix + ir[fix].length; } - insertInPlaceAlt(ir, orStart, Bytecode(IR.OrStart, 0), Bytecode(IR.Option, len)); + insertInPlace(ir, orStart, Bytecode(IR.OrStart, 0), Bytecode(IR.Option, len)); assert(ir[orStart].code == IR.OrStart); put(Bytecode(IR.GotoEndOr, 0)); fixupStack.push(orStart); //fixup for StartOR @@ -1258,14 +1225,14 @@ struct Parser(R) break; case '{': enforce(next(), "Unexpected end of regex pattern"); - enforce(ascii.isDigit(current), "First number required in repetition"); + enforce(std.ascii.isDigit(current), "First number required in repetition"); min = parseDecimal(); if(current == '}') max = min; else if(current == ',') { next(); - if(ascii.isDigit(current)) + if(std.ascii.isDigit(current)) max = parseDecimal(); else if(current == '}') max = infinite; @@ -1283,7 +1250,7 @@ struct Parser(R) default: if(replace) { - copyForwardAlt(ir[offset + 1 .. $],ir[offset .. $ - 1]); + copy(ir[offset + 1 .. $], ir[offset .. $ - 1]); ir.length -= 1; } return; @@ -1304,7 +1271,7 @@ struct Parser(R) if(replace) ir[offset] = op; else - insertInPlaceAlt(ir, offset, op); + insertInPlace(ir, offset, op); put(Bytecode(greedy ? IR.RepeatEnd : IR.RepeatQEnd, len)); put(Bytecode.init); //hotspot putRaw(1); @@ -1321,7 +1288,7 @@ struct Parser(R) if(replace) ir[offset] = op; else - insertInPlaceAlt(ir, offset, op); + insertInPlace(ir, offset, op); offset += 1;//so it still points to the repeated block put(Bytecode(greedy ? IR.RepeatEnd : IR.RepeatQEnd, len)); put(Bytecode.init); //hotspot @@ -1332,7 +1299,7 @@ struct Parser(R) } else if(replace) { - copyForwardAlt(ir[offset+1 .. $],ir[offset .. $-1]); + copy(ir[offset+1 .. $], ir[offset .. $-1]); ir.length -= 1; } put(Bytecode(greedy ? IR.InfiniteStart : IR.InfiniteQStart, len)); @@ -1348,7 +1315,7 @@ struct Parser(R) if(replace) ir[offset] = op; else - insertInPlaceAlt(ir, offset, op); + insertInPlace(ir, offset, op); //IR.InfinteX is always a hotspot put(Bytecode(greedy ? IR.InfiniteEnd : IR.InfiniteQEnd, len)); put(Bytecode.init); //merge index @@ -1386,13 +1353,13 @@ struct Parser(R) next(); break; default: + //FIXME: getCommonCasing in new std uni if(re_flags & RegexOption.casefold) { - dchar[5] data; - auto range = getCommonCasing(current, data); + auto range = simpleCaseFoldings(current); assert(range.length <= 5); if(range.length == 1) - put(Bytecode(IR.Char, range[0])); + put(Bytecode(IR.Char, range.front)); else foreach(v; range) put(Bytecode(IR.OrChar, v, cast(uint)range.length)); @@ -1453,13 +1420,12 @@ struct Parser(R) { if(re_flags & RegexOption.casefold) { - dchar[5] chars; - auto range = getCommonCasing(ch, chars); + auto range = simpleCaseFoldings(ch); foreach(v; range) - set.add(v); + set |= v; } else - set.add(ch); + set |= ch; } static Operator twinSymbolOperator(dchar symbol) @@ -1523,14 +1489,14 @@ struct Parser(R) state = State.CharDash; break; case '\\': - set.add(last); + set |= last; state = State.Escape; break; case '[': op = Operator.Union; goto case; case ']': - set.add(last); + set |= last; break L_CharTermLoop; default: addWithFlags(set, last, re_flags); @@ -1546,7 +1512,39 @@ struct Parser(R) next();//skip second twin char break L_CharTermLoop; } - goto case State.Char;// it's not a twin lets re-run normal logic + //~~~WORKAROUND~~~ + //It's a copy of State.Char, should be goto case but see @@@BUG12603 + switch(current) + { + case '|': + case '~': + case '&': + // then last is treated as normal char and added as implicit union + state = State.PotentialTwinSymbolOperator; + addWithFlags(set, last, re_flags); + last = current; + break; + case '-': // still need more info + state = State.CharDash; + break; + case '\\': + set |= last; + state = State.Escape; + break; + case '[': + op = Operator.Union; + goto case; + case ']': + set |= last; + break L_CharTermLoop; + default: + addWithFlags(set, last, re_flags); + state = State.Char; + last = current; + } + break; + //~~~END OF WORKAROUND~~~ + //goto case State.Char;// it's not a twin lets re-run normal logic case State.Escape: // xxx \ current xxx switch(current) @@ -1603,19 +1601,19 @@ struct Parser(R) state = State.Char; break; case 'd': - set.add(unicodeNd); + set.add(unicode.Nd); state = State.Start; break; case 'D': - set.add(unicodeNd.dup.negate()); + set.add(unicode.Nd.inverted); state = State.Start; break; case 's': - set.add(unicodeWhite_Space); + set.add(unicode.White_Space); state = State.Start; break; case 'S': - set.add(unicodeWhite_Space.dup.negate()); + set.add(unicode.White_Space.inverted); state = State.Start; break; case 'w': @@ -1623,7 +1621,7 @@ struct Parser(R) state = State.Start; break; case 'W': - set.add(wordCharacter.dup.negate()); + set.add(wordCharacter.inverted); state = State.Start; break; default: @@ -1658,7 +1656,7 @@ struct Parser(R) addWithFlags(set, ch, re_flags); } else - set.add(Interval(last, current)); + set.add(last, current + 1); state = State.Start; } break; @@ -1704,7 +1702,7 @@ struct Parser(R) error("invalid escape sequence"); } enforce(last <= end,"inverted range"); - set.add(Interval(last,end)); + set.add(last, end + 1); state = State.Start; break; } @@ -1727,7 +1725,7 @@ struct Parser(R) switch(op) { case Operator.Negate: - stack.top.negate(); + stack.top = stack.top.inverted; break; case Operator.Union: auto s = stack.pop();//2nd operand @@ -1742,7 +1740,7 @@ struct Parser(R) case Operator.SymDifference: auto s = stack.pop();//2nd operand enforce(!stack.empty, "no operand for '~~'"); - stack.top.symmetricSub(s); + stack.top ~= s; break; case Operator.Intersection: auto s = stack.pop();//2nd operand @@ -1821,27 +1819,38 @@ struct Parser(R) charsetToIr(vstack.top); } //try to generate optimal IR code for this CodepointSet - @trusted void charsetToIr(in CodepointSet set) + @trusted void charsetToIr(CodepointSet set) {//@@@BUG@@@ writeln is @system - uint chars = set.chars; + uint chars = cast(uint)set.length; if(chars < Bytecode.maxSequence) { switch(chars) { case 1: - put(Bytecode(IR.Char, set.ivals[0])); + put(Bytecode(IR.Char, set.byCodepoint.front)); break; case 0: error("empty CodepointSet not allowed"); break; default: - foreach(ch; set[]) + foreach(ch; set.byCodepoint) put(Bytecode(IR.OrChar, ch, chars)); } } else { - if(set.ivals.length > maxCharsetUsed) + import std.algorithm : countUntil; + auto ivals = set.byInterval; + auto n = charsets.countUntil(set); + if(n >= 0) + { + if(ivals.length*2 > maxCharsetUsed) + put(Bytecode(IR.Trie, cast(uint)n)); + else + put(Bytecode(IR.CodepointSet, cast(uint)n)); + return; + } + if(ivals.length*2 > maxCharsetUsed) { auto t = getTrie(set); put(Bytecode(IR.Trie, cast(uint)tries.length)); @@ -1872,21 +1881,21 @@ struct Parser(R) case 'd': next(); - charsetToIr(unicodeNd); + charsetToIr(unicode.Nd); break; case 'D': next(); - charsetToIr(unicodeNd.dup.negate()); + charsetToIr(unicode.Nd.inverted); break; case 'b': next(); put(Bytecode(IR.Wordboundary, 0)); break; case 'B': next(); put(Bytecode(IR.Notwordboundary, 0)); break; case 's': next(); - charsetToIr(unicodeWhite_Space); + charsetToIr(unicode.White_Space); break; case 'S': next(); - charsetToIr(unicodeWhite_Space.dup.negate()); + charsetToIr(unicode.White_Space.inverted); break; case 'w': next(); @@ -1894,7 +1903,7 @@ struct Parser(R) break; case 'W': next(); - charsetToIr(wordCharacter.dup.negate()); + charsetToIr(wordCharacter.inverted); break; case 'p': case 'P': auto CodepointSet = parseUnicodePropertySpec(current == 'P'); @@ -1921,20 +1930,18 @@ struct Parser(R) break; case '1': .. case '9': uint nref = cast(uint)current - '0'; - uint maxBackref; - foreach(v; groupStack.data) - maxBackref += v; - uint localLimit = maxBackref - groupStack.top; + uint maxBackref = sum(groupStack.data); enforce(nref < maxBackref, "Backref to unseen group"); //perl's disambiguation rule i.e. //get next digit only if there is such group number - while(nref < maxBackref && next() && ascii.isDigit(current)) + while(nref < maxBackref && next() && std.ascii.isDigit(current)) { nref = nref * 10 + current - '0'; } if(nref >= maxBackref) nref /= 10; - + enforce(!isOpenGroup(nref), "Backref to open group"); + uint localLimit = maxBackref - groupStack.top; if(nref >= localLimit) { put(Bytecode(IR.Backref, nref-localLimit)); @@ -1953,9 +1960,8 @@ struct Parser(R) //parse and return a CodepointSet for \p{...Property...} and \P{...Property..}, //\ - assumed to be processed, p - is current - const(CodepointSet) parseUnicodePropertySpec(bool negated) + CodepointSet parseUnicodePropertySpec(bool negated) { - alias ucmp = comparePropertyName; enum MAX_PROPERTY = 128; char[MAX_PROPERTY] result; uint k = 0; @@ -1964,7 +1970,7 @@ struct Parser(R) { while(k < MAX_PROPERTY && next() && current !='}' && current !=':') if(current != '-' && current != ' ' && current != '_') - result[k++] = cast(char)ascii.toLower(current); + result[k++] = cast(char)std.ascii.toLower(current); enforce(k != MAX_PROPERTY, "invalid property name"); enforce(current == '}', "} expected "); } @@ -2008,7 +2014,7 @@ struct Parser(R) public struct Regex(Char) { //temporary workaround for identifier lookup - const(CodepointSet)[] charsets; // + CodepointSet[] charsets; // Bytecode[] ir; //compiled bytecode of pattern /++ @@ -2092,7 +2098,7 @@ private: uint hotspotTableSize; //number of entries in merge table uint threadCount; uint flags; //global regex flags - const(Trie)[] tries; // + public const(Trie)[] tries; // uint[] backrefed; //bit array of backreferenced submatches Kickstart!Char kickstart; @@ -2282,7 +2288,8 @@ unittest @trusted uint lookupNamedGroup(String)(NamedGroup[] dict, String name) {//equal is @system? auto fnd = assumeSorted!"cmp(a,b) < 0"(map!"a.name"(dict)).lowerBound(name).length; - enforce(equal(dict[fnd].name, name), text("no submatch named ", name)); + enforce(fnd < dict.length && equal(dict[fnd].name, name), + text("no submatch named ", name)); return dict[fnd].group; } @@ -2355,13 +2362,13 @@ int quickTestFwd(RegEx)(uint pc, dchar front, const ref RegEx re) @trusted public struct SampleGenerator(Char) { import std.random; - const(Regex!Char) re; + Regex!Char re; Appender!(char[]) app; uint limit, seed; Xorshift gen; //generator for pattern r, with soft maximum of threshold elements //and a given random seed - this(in Regex!Char r, uint threshold, uint randomSeed) + this(ref Regex!Char r, uint threshold, uint randomSeed) { re = r; limit = threshold; @@ -2645,7 +2652,7 @@ private: auto t = worklist[$-1]; worklist.length -= 1; if(!__ctfe) - worklist.assumeSafeAppend(); + cast(void)worklist.assumeSafeAppend(); return t; } @@ -2656,7 +2663,7 @@ private: } public: - @trusted this(const ref Regex!Char re, uint[] memory) + @trusted this(ref Regex!Char re, uint[] memory) { assert(memory.length == 256); fChar = uint.max; @@ -2747,11 +2754,19 @@ public: } else { + static if(charSize == 1) static immutable codeBounds = [0x0, 0x7F, 0x80, 0x7FF, 0x800, 0xFFFF, 0x10000, 0x10FFFF]; else //== 2 static immutable codeBounds = [0x0, 0xFFFF, 0x10000, 0x10FFFF]; - auto srange = assumeSorted!"a <= b"(set.ivals); + uint[] arr = new uint[set.byInterval.length * 2]; + size_t ofs = 0; + foreach(ival; set.byInterval) + { + arr[ofs++] = ival.a; + arr[ofs++] = ival.b; + } + auto srange = assumeSorted!"a <= b"(arr); for(uint i = 0; i < codeBounds.length/2; i++) { auto start = srange.lowerBound(codeBounds[2*i]).length; @@ -2762,10 +2777,10 @@ public: } if(numS == 0 || t.idx + s[numS-1] > n_length) goto L_StopThread; - auto chars = set.chars; + auto chars = set.length; if(chars > charsetThreshold) goto L_StopThread; - foreach(ch; set[]) + foreach(ch; set.byCodepoint) { //avoid surrogate pairs if(0xD800 <= ch && ch <= 0xDFFF) @@ -3810,7 +3825,7 @@ template BacktrackingMatcher(bool CTregex) //helper function, saves engine state void pushState(uint pc, uint counter) { - if(stateSize + matches.length > stackAvail) + if(stateSize + trackers.length + matches.length > stackAvail) { newStack(); lastState = 0; @@ -3820,6 +3835,11 @@ template BacktrackingMatcher(bool CTregex) lastState += stateSize; memory[lastState .. lastState + 2 * matches.length] = (cast(size_t[])matches)[]; lastState += 2*matches.length; + if(trackers.length) + { + memory[lastState .. lastState + trackers.length] = trackers[]; + lastState += trackers.length; + } debug(std_regex_matcher) writefln("Saved(pc=%s) front: %s src: %s", pc, front, s[index..s.lastIndex]); @@ -3830,9 +3850,14 @@ template BacktrackingMatcher(bool CTregex) { if(!lastState) return prevStack(); + if (trackers.length) + { + lastState -= trackers.length; + trackers[] = memory[lastState .. lastState + trackers.length]; + } lastState -= 2*matches.length; auto pm = cast(size_t[])matches; - pm[] = memory[lastState .. lastState+2*matches.length]; + pm[] = memory[lastState .. lastState + 2 * matches.length]; lastState -= stateSize; State* state = cast(State*)&memory[lastState]; index = state.index; @@ -3887,7 +3912,7 @@ template BacktrackingMatcher(bool CTregex) //generate code for TypeTuple(S, S+1, S+2, ... E) @system string ctGenSeq(int S, int E) { - string s = "alias TypeTuple!("; + string s = "alias Sequence = TypeTuple!("; if(S < E) s ~= to!string(S); for(int i = S+1; i < E;i++) @@ -3895,7 +3920,7 @@ template BacktrackingMatcher(bool CTregex) s ~= ", "; s ~= to!string(i); } - return s ~") Sequence;"; + return s ~");"; } //alias to TypeTuple(S, S+1, S+2, ... E) @@ -3929,10 +3954,10 @@ struct CtContext total_matches = re.ngroup; } - CtContext lookaround() + CtContext lookaround(uint s, uint e) { CtContext ct; - ct.total_matches = total_matches; + ct.total_matches = e - s; ct.match = 1; return ct; } @@ -3947,6 +3972,12 @@ struct CtContext stackPop(counter);" : " counter = 0;"; + if(infNesting) + { + text ~= ctSub(` + stackPop(trackers[0..$$]); + `, curInfLoop + 1); + } if(match < total_matches) { text ~= ctSub(" @@ -3964,7 +3995,7 @@ struct CtContext string saveCode(uint pc, string count_expr="counter") { string text = ctSub(" - if(stackAvail < $$*(Group!(DataIndex)).sizeof/size_t.sizeof + $$) + if(stackAvail < $$*(Group!(DataIndex)).sizeof/size_t.sizeof + trackers.length + $$) { newStack(); lastState = 0; @@ -3975,6 +4006,12 @@ struct CtContext else text ~= ctSub(" stackPush(matches[$$..$]);", reserved); + if(infNesting) + { + text ~= ctSub(` + stackPush(trackers[0..$$]); + `, curInfLoop + 1); + } text ~= counter ? ctSub(" stackPush($$);", count_expr) : ""; text ~= ctSub(" @@ -4050,7 +4087,7 @@ struct CtContext string bwdCreate = "bwdMatcher(matcher, mem)"; uint start = IRL!(IR.LookbehindStart); uint end = IRL!(IR.LookbehindStart)+len+IRL!(IR.LookaheadEnd); - CtContext context = lookaround(); //split off new context + CtContext context = lookaround(ir[1].raw, ir[2].raw); //split off new context auto slice = ir[start .. end]; r.code ~= ctSub(` case $$: //fake lookaround "atom" @@ -4159,18 +4196,18 @@ struct CtContext { case IR.InfiniteStart, IR.InfiniteQStart: r ~= ctSub( ` - tracker_$$ = DataIndex.max; + trackers[$$] = DataIndex.max; goto case $$;`, curInfLoop, fixup); ir = ir[ir[0].length..$]; break; case IR.InfiniteEnd: testCode = ctQuickTest(ir[IRL!(IR.InfiniteEnd) .. $],addr + 1); r ~= ctSub( ` - if(tracker_$$ == index) + if(trackers[$$] == index) {//source not consumed goto case $$; } - tracker_$$ = index; + trackers[$$] = index; $$ { @@ -4186,25 +4223,25 @@ struct CtContext break; case IR.InfiniteQEnd: testCode = ctQuickTest(ir[IRL!(IR.InfiniteEnd) .. $],addr + 1); + auto altCode = testCode.length ? ctSub("else goto case $$;", fixup) : ""; r ~= ctSub( ` - if(tracker_$$ == index) + if(trackers[$$] == index) {//source not consumed goto case $$; } - tracker_$$ = index; + trackers[$$] = index; $$ { $$ goto case $$; } - else - goto case $$; + $$ case $$://restore state and go inside loop $$ - goto case $$;`, curInfLoop, addr+2, curInfLoop, - testCode, saveCode(addr+1), - addr+2, fixup, addr+1, restoreCode(), fixup); + goto case $$;`, curInfLoop, addr+2, + curInfLoop, testCode, saveCode(addr+1), + addr+2, altCode, addr+1, restoreCode(), fixup); ir = ir[ir[0].length..$]; break; case IR.RepeatStart, IR.RepeatQStart: @@ -4287,6 +4324,8 @@ struct CtContext { pc++; } + else if(ir[pc].code == IR.Backref) + break; else { auto code = ctAtomCode(ir[pc..$], -1); @@ -4504,9 +4543,6 @@ struct CtContext counter = 0; lastState = 0; auto start = s._index;`; - for(int i = 0; i < nInfLoops; i++) - r ~= ctSub(` - size_t tracker_$$;`, i); r ~= ` goto StartLoop; debug(std_regex_matcher) writeln("Try CT matching starting at ",s[index..s.lastIndex]); @@ -5207,14 +5243,13 @@ enum OneShot { Fwd, Bwd }; auto matcher = fwdMatcher(re.ir[t.pc .. end], subCounters.get(t.pc, 0)); else auto matcher = bwdMatcher(re.ir[t.pc .. end], subCounters.get(t.pc, 0)); - matcher.re.ngroup = re.ir[t.pc+2].raw - re.ir[t.pc+1].raw; + matcher.re.ngroup = me - ms; matcher.backrefed = backrefed.empty ? t.matches : backrefed; //backMatch - bool nomatch = (matcher.matchOneShot(t.matches, IRL!(IR.LookbehindStart)) - == MatchResult.Match) ^ positive; + auto mRes = matcher.matchOneShot(t.matches.ptr[ms .. me], IRL!(IR.LookbehindStart)); freelist = matcher.freelist; subCounters[t.pc] = matcher.genCounter; - if(nomatch) + if((mRes == MatchResult.Match) ^ positive) { recycle(t); t = worklist.fetch(); @@ -5238,13 +5273,12 @@ enum OneShot { Fwd, Bwd }; auto matcher = fwdMatcher(re.ir[t.pc .. end], subCounters.get(t.pc, 0)); matcher.re.ngroup = me - ms; matcher.backrefed = backrefed.empty ? t.matches : backrefed; - bool nomatch = (matcher.matchOneShot(t.matches, IRL!(IR.LookaheadStart)) - == MatchResult.Match) ^ positive; + auto mRes = matcher.matchOneShot(t.matches.ptr[ms .. me], IRL!(IR.LookaheadStart)); freelist = matcher.freelist; subCounters[t.pc] = matcher.genCounter; s.reset(index); next(); - if(nomatch) + if((mRes == MatchResult.Match) ^ positive) { recycle(t); t = worklist.fetch(); @@ -5259,9 +5293,7 @@ enum OneShot { Fwd, Bwd }; case IR.NeglookaheadEnd: case IR.LookbehindEnd: case IR.NeglookbehindEnd: - t.pc = re.ir[t.pc].indexOfPair(t.pc); - uint ms = re.ir[t.pc+1].raw, me = re.ir[t.pc+2].raw; - finish(t, matches.ptr[ms..me]); + finish(t, matches.ptr[0 .. re.ngroup]); recycle(t); //cut off low priority threads recycle(clist); @@ -5365,10 +5397,9 @@ enum OneShot { Fwd, Bwd }; { writefln("---------------single shot match ----------------- "); } - alias eval evalFn; + alias evalFn = eval; assert(clist == (ThreadList!DataIndex).init || startPc == RestartPc); // incorrect after a partial match assert(nlist == (ThreadList!DataIndex).init || startPc == RestartPc); - startPc = startPc; if(!atEnd)//if no char { debug(std_regex_matcher) @@ -5494,30 +5525,6 @@ enum OneShot { Fwd, Bwd }; to $(D match) or iteration over $(D RegexMatch) range. First element of range is the whole match. - - Example, showing basic operations on $(D Captures): - ---- - import std.regex; - import std.range; - - void main() - { - auto m = match("@abc#", regex(`(\w)(\w)(\w)`)); - auto c = m.captures; - assert(c.pre == "@"); // Part of input preceeding match - assert(c.post == "#"); // Immediately after match - assert(c.hit == c[0] && c.hit == "abc"); // The whole match - assert(c[2] =="b"); - assert(c.front == "abc"); - c.popFront(); - assert(c.front == "a"); - assert(c.back == "c"); - c.popBack(); - assert(c.back == "b"); - popFrontN(c, 2); - assert(c.empty); - } - ---- +/ @trusted public struct Captures(R, DIndex = size_t) if(isSomeString!R) @@ -5648,8 +5655,7 @@ public: import std.regex; import std.range; - auto m = match("a = 42;", regex(`(?P\w+)\s*=\s*(?P\d+);`)); - auto c = m.captures; + auto c = matchFirst("a = 42;", regex(`(?P\w+)\s*=\s*(?P\d+);`)); assert(c["var"] == "a"); assert(c["value"] == "42"); popFrontN(c, 2); @@ -5672,14 +5678,14 @@ public: @property ref captures(){ return this; } } -unittest//verify example +/// +unittest { - auto m = match("@abc#", regex(`(\w)(\w)(\w)`)); - auto c = m.captures; - assert(c.pre == "@");// part of input preceeding match - assert(c.post == "#"); // immediately after match - assert(c.hit == c[0] && c.hit == "abc");// the whole match - assert(c[2] =="b"); + auto c = matchFirst("@abc#", regex(`(\w)(\w)(\w)`)); + assert(c.pre == "@"); // Part of input preceding match + assert(c.post == "#"); // Immediately after match + assert(c.hit == c[0] && c.hit == "abc"); // The whole match + assert(c[2] == "b"); assert(c.front == "abc"); c.popFront(); assert(c.front == "a"); @@ -5771,7 +5777,7 @@ public: Functionality for processing subsequent matches of global regexes via range interface: --- import std.regex; - auto m = match("Hello, world!", regex(`\w+`, "g")); + auto m = matchAll("Hello, world!", regex(`\w+`)); assert(m.front.hit == "Hello"); m.popFront(); assert(m.front.hit == "world"); @@ -6472,7 +6478,7 @@ L_Replace_Loop: format = format[offset .. $]; break; case State.Dollar: - if(ascii.isDigit(format[0])) + if(std.ascii.isDigit(format[0])) { uint digit = parse!uint(format); enforce(ignoreBadSubs || digit < captures.length, text("invalid submatch number ", digit)); @@ -6481,7 +6487,7 @@ L_Replace_Loop: } else if(format[0] == '{') { - auto x = find!(a => !ascii.isAlpha(a))(format[1..$]); + auto x = find!(a => !std.ascii.isAlpha(a))(format[1..$]); enforce(!x.empty && x[0] == '}', "no matching '}' in replacement format"); auto name = format[1 .. $ - x.length]; format = x[1..$]; @@ -6871,16 +6877,16 @@ unittest TestVectors( `[-+*/\p{in-mathematical-operators}]{2}`, "a+\u2212", "y", "$&", "+\u2212"), TestVectors( `\p{Ll}+`, "XabcD", "y", "$&", "abc"), TestVectors( `\p{Lu}+`, "абвГДЕ", "y", "$&", "ГДЕ"), - TestVectors( `^\p{Currency Symbol}\p{Sc}` "$₤", "y", "$&", "$₤"), - TestVectors( `\p{Common}\p{Thai}` "!ฆ", "y", "$&", "!ฆ"), + TestVectors( `^\p{Currency Symbol}\p{Sc}`, "$₤", "y", "$&", "$₤"), + TestVectors( `\p{Common}\p{Thai}`, "!ฆ", "y", "$&", "!ฆ"), TestVectors( `[\d\s]*\D`, "12 \t3\U00001680\u0F20_2", "y", "$&", "12 \t3\U00001680\u0F20_"), TestVectors( `[c-wф]фф`, "ффф", "y", "$&", "ффф"), //case insensitive: - TestVectors( `^abcdEf$`, "AbCdEF" "y", "$&", "AbCdEF", "i"), + TestVectors( `^abcdEf$`, "AbCdEF", "y", "$&", "AbCdEF", "i"), TestVectors( `Русский язык`, "рУсскИй ЯзЫк", "y", "$&", "рУсскИй ЯзЫк", "i"), TestVectors( `ⒶⒷⓒ` , "ⓐⓑⒸ", "y", "$&", "ⓐⓑⒸ", "i"), TestVectors( "\U00010400{2}", "\U00010428\U00010400 ", "y", "$&", "\U00010428\U00010400", "i"), - TestVectors( `[adzУ-Я]{4}`, "DzюА" "y", "$&", "DzЮа", "i"), + TestVectors( `[adzУ-Я]{4}`, "DzюЯ", "y", "$&", "DzюЯ", "i"), TestVectors( `\p{L}\p{Lu}{10}`, "абвгдеЖЗИКЛ", "y", "$&", "абвгдеЖЗИКЛ", "i"), TestVectors( `(?:Dåb){3}`, "DåbDÅBdÅb", "y", "$&", "DåbDÅBdÅb", "i"), //escapes: @@ -7010,17 +7016,18 @@ unittest version(std_regex_ct1) { pragma(msg, "Testing 1st part of ctRegex"); - alias Tests = Sequence!(0, 90); + alias Tests = Sequence!(0, 155); } else version(std_regex_ct2) { pragma(msg, "Testing 2nd part of ctRegex"); - alias Tests = Sequence!(90, 165); + alias Tests = Sequence!(155, 174); } + //FIXME: #174-178 contains CTFE parser bug else version(std_regex_ct3) { pragma(msg, "Testing 3rd part of ctRegex"); - alias Tests = Sequence!(185, 220); + alias Tests = Sequence!(178, 220); } else version(std_regex_ct4) { @@ -7041,7 +7048,7 @@ unittest else { //BUG: tv[v] is fine but tvd is not known at compile time?! - enum r = ctRegex!(tv[v].pattern, tv[v].flags); + auto r = ctRegex!(tv[v].pattern, tv[v].flags); auto nr = regex(tvd.pattern, tvd.flags); assert(equal(r.ir, nr.ir), text("!C-T regex! failed to compile pattern #", a ,": ", tvd.pattern)); @@ -7068,6 +7075,13 @@ unittest run_tests!match(); //thompson VM } +unittest +{ + // test parser optimization of identical character classes + auto n = regex("[a-z]/[a-z]/[a-z]").charsets.length; + assert(n == 1, text(n)); +} + unittest { auto cr = ctRegex!("abc"); @@ -7116,6 +7130,7 @@ unittest assert(equal(mx.captures, [ "B", "B"])); enum cx2 = ctRegex!"(A|B)*"; assert(match("BAAA",cx2)); + enum cx3 = ctRegex!("a{3,4}","i"); auto mx3 = match("AaA",cx3); assert(mx3); @@ -7439,6 +7454,15 @@ unittest assert(match("aaaa", re).hit == "aaaa"); } +//bugzilla 10798 +unittest +{ + auto cr = ctRegex!("[abcd--c]*"); + auto m = "abc".match(cr); + assert(m); + assert(m.hit == "ab"); +} + // bugzilla 10913 unittest { @@ -7483,4 +7507,64 @@ unittest assert(regex(`(?P<я>\w+)`).namedCaptures.equal(["я"])); } +// bugzilla 12076 +unittest +{ + auto RE = ctRegex!(r"(?abc)`); + assert(collectException("abc".matchFirst(r)["b"])); +} + +// bugzilla 12691 +unittest +{ + assert(bmatch("e@", "^([a-z]|)*$").empty); + assert(bmatch("e@", ctRegex!`^([a-z]|)*$`).empty); +} + +//bugzilla 12713 +unittest +{ + assertThrown(regex("[[a-z]([a-z]|(([[a-z])))")); +} + +//bugzilla 12747 +unittest +{ + assertThrown(regex(`^x(\1)`)); + assertThrown(regex(`^(x(\1))`)); + assertThrown(regex(`^((x)(?=\1))`)); +} + }//version(unittest) diff --git a/libphobos/src/std/signals.d b/libphobos/src/std/signals.d index 052d2a091..e5d6cb17d 100644 --- a/libphobos/src/std/signals.d +++ b/libphobos/src/std/signals.d @@ -15,7 +15,7 @@ * $(LINK2 http://en.wikipedia.org/wiki/Observer_pattern, Observer pattern)$(BR) * $(LINK2 http://en.wikipedia.org/wiki/Signals_and_slots, Wikipedia)$(BR) * $(LINK2 http://boost.org/doc/html/$(SIGNALS).html, Boost Signals)$(BR) - * $(LINK2 http://doc.trolltech.com/4.1/signalsandslots.html, Qt)$(BR) + * $(LINK2 http://qt-project.org/doc/qt-5/signalsandslots.html, Qt)$(BR) * * There has been a great deal of discussion in the D newsgroups * over this, and several implementations: @@ -71,7 +71,7 @@ import core.exception : onOutOfMemoryError; extern (C) Object _d_toObject(void* p); // Used in place of Object.notifyRegister and Object.notifyUnRegister. -alias void delegate(Object) DisposeEvt; +alias DisposeEvt = void delegate(Object); extern (C) void rt_attachDisposeEvent( Object obj, DisposeEvt evt ); extern (C) void rt_detachDisposeEvent( Object obj, DisposeEvt evt ); //debug=signal; @@ -151,7 +151,7 @@ mixin template Signal(T1...) * Delegates to struct instances or nested functions must not be * used as slots. */ - alias void delegate(T1) slot_t; + alias slot_t = void delegate(T1); /*** * Call each of the connected slots, passing the argument(s) i to them. diff --git a/libphobos/src/std/socket.d b/libphobos/src/std/socket.d index 42ec45cfc..2a5984fb9 100644 --- a/libphobos/src/std/socket.d +++ b/libphobos/src/std/socket.d @@ -58,8 +58,8 @@ version(Windows) pragma (lib, "wsock32.lib"); private import std.c.windows.windows, std.c.windows.winsock, std.windows.syserror; - private alias std.c.windows.winsock.timeval _ctimeval; - private alias std.c.windows.winsock.linger _clinger; + private alias _ctimeval = std.c.windows.winsock.timeval; + private alias _clinger = std.c.windows.winsock.linger; enum socket_t : SOCKET { INVALID_SOCKET } private const int _SOCKET_ERROR = SOCKET_ERROR; @@ -103,8 +103,8 @@ else version(Posix) private import core.sys.posix.sys.time; //private import core.sys.posix.sys.select; private import core.sys.posix.sys.socket; - private alias core.sys.posix.sys.time.timeval _ctimeval; - private alias core.sys.posix.sys.socket.linger _clinger; + private alias _ctimeval = core.sys.posix.sys.time.timeval; + private alias _clinger = core.sys.posix.sys.socket.linger; private import core.stdc.errno; @@ -739,7 +739,7 @@ class InternetHost } version(Windows) - alias getHostNoSync getHost; + alias getHost = getHostNoSync; else { // posix systems use global state for return value, so we @@ -1730,10 +1730,15 @@ public: { const(ubyte)[16]* addr; static if (is(typeof(IN6ADDR_ANY))) - return addr = &IN6ADDR_ANY.s6_addr, *addr; - else - static if (is(typeof(in6addr_any))) - return addr = &in6addr_any.s6_addr, *addr; + { + addr = &IN6ADDR_ANY.s6_addr; + return *addr; + } + else static if (is(typeof(in6addr_any))) + { + addr = &in6addr_any.s6_addr; + return *addr; + } else static assert(0); } @@ -2003,8 +2008,8 @@ private mixin template FieldProxy(string target, string field) struct TimeVal { _ctimeval ctimeval; - alias typeof(ctimeval.tv_sec) tv_sec_t; - alias typeof(ctimeval.tv_usec) tv_usec_t; + alias tv_sec_t = typeof(ctimeval.tv_sec); + alias tv_usec_t = typeof(ctimeval.tv_usec); version (StdDdoc) // no DDoc for string mixins, can't forward individual fields { @@ -2023,109 +2028,166 @@ struct TimeVal /** * A collection of sockets for use with $(D Socket.select). * - * $(D SocketSet) allows specifying the capacity of the underlying - * $(D fd_set), however users should be aware that the exact meaning of this - * value varies depending on the current platform: - * $(UL $(LI On POSIX, $(D fd_set) is a bit array of file descriptors. The - * $(D SocketSet) capacity specifies the highest file descriptor which can be - * stored in the set.) - * $(LI on Windows, $(D fd_set) is an array of socket handles. Capacity - * indicates the actual number of sockets that can be stored in the set.)) + * $(D SocketSet) wraps the platform $(D fd_set) type. However, unlike + * $(D fd_set), $(D SocketSet) is not statically limited to $(D FD_SETSIZE) + * or any other limit, and grows as needed. */ class SocketSet { private: - version(Windows) + version (Windows) { - // the maximum number of sockets the allocated fd_set can hold - uint fdsetCapacity; + // On Windows, fd_set is an array of socket handles, + // following a word containing the fd_set instance size. + // We use one dynamic array for everything, and use its first + // element(s) for the count. + + alias fd_set_count_type = typeof(fd_set.init.fd_count); + alias fd_set_type = typeof(fd_set.init.fd_array[0]); + static assert(fd_set_type.sizeof == socket_t.sizeof); + + // Number of fd_set_type elements at the start of our array that are + // used for the socket count and alignment + + enum FD_SET_OFFSET = fd_set.fd_array.offsetof / fd_set_type.sizeof; + static assert(FD_SET_OFFSET); + static assert(fd_set.fd_count.offsetof % fd_set_type.sizeof == 0); + + fd_set_type[] set; + + final void resize(size_t size) + { + set.length = FD_SET_OFFSET + size; + } + + final ref fd_set_count_type count() @property inout + { + assert(set.length); + return *cast(fd_set_count_type*)set.ptr; + } - fd_set* set; - @property uint count() const { return set.fd_count; } + final size_t capacity() @property const + { + return set.length - FD_SET_OFFSET; + } + + final inout(socket_t)[] fds() inout @property + { + return cast(inout(socket_t)[])set[FD_SET_OFFSET..FD_SET_OFFSET+count]; + } } - else version(Posix) + else + version (Posix) { - int fdsetMax; + // On Posix, fd_set is a bit array. We assume that the fd_set + // type (declared in core.sys.posix.sys.select) is a structure + // containing a single field, a static array. - fd_set setData; - final @property fd_set* set() { return &setData; } - final @property const(fd_set)* set() const { return &setData; } - int maxfd; - uint count; - } + static assert(fd_set.tupleof.length==1); + // This is the type used in the fd_set array. + // Using the type of the correct size is important for big-endian + // architectures. -public: + alias fd_set_type = typeof(fd_set.init.tupleof[0][0]); - /** - * Set the capacity of this $(D SocketSet). The exact meaning of the - * $(D max) parameter varies from platform to platform. - * Throws: $(D SocketParameterException) if $(D max) exceeds this - * platform's maximum socket set size. - */ - this(uint max) - { - version(Windows) + // Number of file descriptors represented by one fd_set_type + + enum FD_NFDBITS = 8 * fd_set_type.sizeof; + + static fd_set_type mask(uint n) { - fdsetCapacity = max; - set = FD_CREATE(max); + return (cast(fd_set_type)1) << (n % FD_NFDBITS); } - else version(Posix) + + // Array size to fit that many sockets + + static size_t lengthFor(size_t size) { - // TODO (needs druntime changes) - enforce(max <= FD_SETSIZE, new SocketParameterException( - "Maximum socket set size exceeded for this platform")); - fdsetMax = max; + return (size + (FD_NFDBITS-1)) / FD_NFDBITS; } - reset(); + + fd_set_type[] set; + + final void resize(size_t size) + { + set.length = lengthFor(size); + } + + // Make sure we can fit that many sockets + + final void setMinCapacity(size_t size) + { + auto length = lengthFor(size); + if (set.length < length) + set.length = length; + } + + final size_t capacity() @property const + { + return set.length / FD_NFDBITS; + } + + int maxfd; } + else + static assert(false, "Unknown platform"); - /// Uses the default capacity for the system. - this() +public: + + /** + * Create a SocketSet with a specific initial capacity (defaults to + * $(D FD_SETSIZE), the system's default capacity). + */ + this(size_t size = FD_SETSIZE) { - this(FD_SETSIZE); + resize(size); + reset(); } /// Reset the $(D SocketSet) so that there are 0 $(D Socket)s in the collection. void reset() { - FD_ZERO(set); - - version(Posix) + version (Windows) + count = 0; + else { + set[] = 0; maxfd = -1; - count = 0; } } void add(socket_t s) { - // Make sure too many sockets don't get added. - version(Windows) - { - enforce(count < fdsetCapacity, new SocketParameterException( - "SocketSet capacity exceeded")); - } - else version(Posix) + version (Windows) { - enforce(s < fdsetMax, new SocketParameterException( - "Socket descriptor index exceeds SocketSet capacity")); + if (count == capacity) + { + set.length *= 2; + set.length = set.capacity; + } + fds[count++] = s; } - - FD_SET(s, set); - - version(Posix) + else { - ++count; - if(s > maxfd) + auto index = s / FD_NFDBITS; + auto length = set.length; + if (index >= length) + { + while (length < index) + length *= 2; + set.length = length; + set.length = set.capacity; + } + set[index] |= mask(s); + if (maxfd < s) maxfd = s; } } /// Add a $(D Socket) to the collection. - /// Throws: $(D SocketParameterException) if the capacity of this - /// $(D SocketSet) has been exceeded. + /// The socket must not already be in the collection. void add(Socket s) { add(s.sock); @@ -2133,22 +2195,27 @@ public: void remove(socket_t s) { - version(Posix) + version (Windows) { - enforce(s < fdsetMax, new SocketParameterException( - "Socket descriptor index exceeds SocketSet capacity")); + import std.algorithm : countUntil; + auto fds = fds; + auto p = fds.countUntil(s); + if (p >= 0) + fds[p] = fds[--count]; } - - FD_CLR(s, set); - version(Posix) + else { - --count; + auto index = s / FD_NFDBITS; + if (index >= set.length) + return; + set[index] &= ~mask(s); // note: adjusting maxfd would require scanning the set, not worth it } } /// Remove this $(D Socket) from the collection. + /// Does nothing if the socket is not in the collection already. void remove(Socket s) { remove(s.sock); @@ -2156,57 +2223,153 @@ public: int isSet(socket_t s) const { - version(Posix) + version (Windows) { - enforce(s < fdsetMax, new SocketParameterException( - "Socket descriptor index exceeds SocketSet capacity")); + import std.algorithm; + return fds.canFind(s) ? 1 : 0; + } + else + { + if (s > maxfd) + return 0; + auto index = s / FD_NFDBITS; + return (set[index] & mask(s)) ? 1 : 0; } - - return FD_ISSET(s, set); } - /// Returns nonzero if this $(D Socket) is in the collection. + /// Return nonzero if this $(D Socket) is in the collection. int isSet(Socket s) const { return isSet(s.sock); } - /// Return the capacity of this $(D SocketSet). The exact meaning of the - /// return value varies from platform to platform. + /// Return the current capacity of this $(D SocketSet). The exact + /// meaning of the return value varies from platform to platform. + /// Note that since D 2.065, this value does not indicate a + /// restriction, and $(D SocketSet) will grow its capacity as + /// needed automatically. @property uint max() const { - version(Windows) - { - return fdsetCapacity; - } - else version(Posix) - { - return fdsetMax; - } + return cast(uint)capacity; } fd_set* toFd_set() { - return set; + return cast(fd_set*)set.ptr; } int selectn() const { - version(Windows) + version (Windows) { return count; } - else version(Posix) + else version (Posix) { return maxfd + 1; } } } +unittest +{ + auto fds = cast(socket_t[]) + [cast(socket_t)1, 2, 0, 1024, 17, 42, 1234, 77, 77+32, 77+64]; + auto set = new SocketSet(); + foreach (fd; fds) assert(!set.isSet(fd)); + foreach (fd; fds) set.add(fd); + foreach (fd; fds) assert(set.isSet(fd)); + + // Make sure SocketSet reimplements fd_set correctly + auto fdset = set.toFd_set(); + foreach (fd; fds[0]..cast(socket_t)(fds[$-1]+1)) + assert(cast(bool)set.isSet(fd) == cast(bool)FD_ISSET(fd, fdset)); + + foreach (fd; fds) + { + assert(set.isSet(fd)); + set.remove(fd); + assert(!set.isSet(fd)); + } +} + +unittest +{ + softUnittest({ + enum PAIRS = 768; + version(Posix) + { + enum LIMIT = 2048; + static assert(LIMIT > PAIRS*2); + import core.sys.posix.sys.resource; + rlimit fileLimit; + getrlimit(RLIMIT_NOFILE, &fileLimit); + assert(fileLimit.rlim_max > LIMIT, "Open file hard limit too low"); + fileLimit.rlim_cur = LIMIT; + setrlimit(RLIMIT_NOFILE, &fileLimit); + } + + Socket[2][PAIRS] pairs; + foreach (ref pair; pairs) + pair = socketPair(); + scope(exit) + { + foreach (pair; pairs) + { + pair[0].close(); + pair[1].close(); + } + } + + import std.random; + auto rng = Xorshift(42); + pairs[].randomShuffle(rng); + + auto readSet = new SocketSet(); + auto writeSet = new SocketSet(); + auto errorSet = new SocketSet(); + + foreach (testPair; pairs) + { + void fillSets() + { + readSet.reset(); + writeSet.reset(); + errorSet.reset(); + foreach (ref pair; pairs) + foreach (s; pair[]) + { + readSet.add(s); + writeSet.add(s); + errorSet.add(s); + } + } + + fillSets(); + auto n = Socket.select(readSet, writeSet, errorSet); + assert(n == PAIRS*2); // All in writeSet + assert(writeSet.isSet(testPair[0])); + assert(writeSet.isSet(testPair[1])); + assert(!readSet.isSet(testPair[0])); + assert(!readSet.isSet(testPair[1])); + assert(!errorSet.isSet(testPair[0])); + assert(!errorSet.isSet(testPair[1])); + + ubyte[1] b; + testPair[0].send(b[]); + fillSets(); + n = Socket.select(readSet, null, null); + assert(n == 1); // testPair[1] + assert(readSet.isSet(testPair[1])); + assert(!readSet.isSet(testPair[0])); + testPair[1].receive(b[]); + } + }); +} /// The level at which a socket option is defined: enum SocketOptionLevel: int @@ -2231,8 +2394,8 @@ struct Linger version (StdDdoc) // no DDoc for string mixins, can't forward individual fields { - private alias typeof(_clinger.init.l_onoff ) l_onoff_t; - private alias typeof(_clinger.init.l_linger) l_linger_t; + private alias l_onoff_t = typeof(_clinger.init.l_onoff ); + private alias l_linger_t = typeof(_clinger.init.l_linger); l_onoff_t on; /// Nonzero for _on. l_linger_t time; /// Linger _time. } @@ -2946,8 +3109,7 @@ public: else version (Posix) { _ctimeval tv; - tv.tv_sec = to!(typeof(tv.tv_sec ))(value.total!"seconds"); - tv.tv_usec = to!(typeof(tv.tv_usec))(value.fracSec.usecs); + value.split!("seconds", "usecs")(tv.tv_sec, tv.tv_usec); setOption(level, option, (&tv)[0 .. 1]); } else static assert(false); @@ -3022,9 +3184,10 @@ public: //Winsock: possibly internally limited to 64 sockets per set static int select(SocketSet checkRead, SocketSet checkWrite, SocketSet checkError, Duration timeout) { + auto vals = timeout.split!("seconds", "usecs")(); TimeVal tv; - tv.seconds = to!(tv.tv_sec_t )(timeout.total!"seconds"); - tv.microseconds = to!(tv.tv_usec_t)(timeout.fracSec.usecs); + tv.seconds = cast(tv.tv_sec_t )vals.seconds; + tv.microseconds = cast(tv.tv_usec_t)vals.usecs; return select(checkRead, checkWrite, checkError, &tv); } @@ -3099,6 +3262,12 @@ public: { fe = null; } + + // Make sure the sets' capacity matches, to avoid select reading + // out of bounds just because one set was bigger than another + if (checkRead ) checkRead .setMinCapacity(n); + if (checkWrite) checkWrite.setMinCapacity(n); + if (checkError) checkError.setMinCapacity(n); } int result = .select(n, fr, fw, fe, &timeout.ctimeval); diff --git a/libphobos/src/std/stdio.d b/libphobos/src/std/stdio.d index 773c910a6..3ff619ab8 100644 --- a/libphobos/src/std/stdio.d +++ b/libphobos/src/std/stdio.d @@ -20,6 +20,7 @@ Authors: $(WEB digitalmars.com, Walter Bright), module std.stdio; public import core.stdc.stdio, std.string : KeepTerminator; +import core.vararg; static import std.c.stdio; import std.stdiobase; import core.stdc.errno, core.stdc.stddef, core.stdc.stdlib, core.memory, @@ -28,9 +29,6 @@ import std.range; import std.traits : Unqual, isSomeChar, isAggregateType, isSomeString, isIntegral, isBoolean, ParameterTypeTuple; -version (GNU) - import core.vararg; - version (DigitalMars) { version (Win32) @@ -49,7 +47,7 @@ version (Posix) { import core.sys.posix.fcntl; import core.sys.posix.stdio; - alias core.sys.posix.stdio.fileno fileno; + alias fileno = core.sys.posix.stdio.fileno; } version (linux) @@ -80,6 +78,8 @@ version(Windows) /+ Waiting for druntime pull 299 +/ extern (C) nothrow FILE* _wfopen(in wchar* filename, in wchar* mode); + + import core.sys.windows.windows : HANDLE; } version (DIGITAL_MARS_STDIO) @@ -91,6 +91,8 @@ version (DIGITAL_MARS_STDIO) * Use _iobuf* for the unshared version of FILE*, * usable when the FILE is locked. */ + nothrow: + @nogc: int _fputc_nlock(int, _iobuf*); int _fputwc_nlock(int, _iobuf*); int _fgetc_nlock(_iobuf*); @@ -100,19 +102,18 @@ version (DIGITAL_MARS_STDIO) int setmode(int, int); } - alias _fputc_nlock FPUTC; - alias _fputwc_nlock FPUTWC; - alias _fgetc_nlock FGETC; - alias _fgetwc_nlock FGETWC; + alias FPUTC = _fputc_nlock; + alias FPUTWC = _fputwc_nlock; + alias FGETC = _fgetc_nlock; + alias FGETWC = _fgetwc_nlock; - alias __fp_lock FLOCK; - alias __fp_unlock FUNLOCK; + alias FLOCK = __fp_lock; + alias FUNLOCK = __fp_unlock; - alias setmode _setmode; + alias _setmode = setmode; enum _O_BINARY = 0x8000; int _fileno(FILE* f) { return f._file; } - alias _fileno fileno; - alias _fdToHandle _get_osfhandle; + alias fileno = _fileno; } else version (MINGW_IO) { @@ -134,18 +135,18 @@ else version (MINGW_IO) int fgetc_unlocked(_iobuf* fp) { return fgetc(cast(shared) fp); } int fgetwc_unlocked(_iobuf* fp) { return fgetwc(cast(shared) fp); } - alias fputc_unlocked FPUTC; - alias fputwc_unlocked FPUTWC; - alias fgetc_unlocked FGETC; - alias fgetwc_unlocked FGETWC; + alias FPUTC = fputc_unlocked; + alias FPUTWC = fputwc_unlocked; + alias FGETC = fgetc_unlocked; + alias FGETWC = fgetwc_unlocked; - alias flockfile FLOCK; - alias funlockfile FUNLOCK; + alias FLOCK = flockfile; + alias FUNLOCK = funlockfile; - alias setmode _setmode; + alias _setmode = setmode; enum _O_BINARY = 0x8000; int _fileno(FILE* f) { return f._file; } - alias _fileno fileno; + alias fileno = _fileno; } else version (MICROSOFT_STDIO) { @@ -154,6 +155,8 @@ else version (MICROSOFT_STDIO) /* ** * Microsoft under-the-hood C I/O functions */ + nothrow: + @nogc: int _fputc_nolock(int, _iobuf*); int _fputwc_nolock(int, _iobuf*); int _fgetc_nolock(_iobuf*); @@ -162,17 +165,23 @@ else version (MICROSOFT_STDIO) void _unlock_file(FILE*); int _setmode(int, int); int _fileno(FILE*); + FILE* _fdopen(int, const (char)*); } - alias _fputc_nolock FPUTC; - alias _fputwc_nolock FPUTWC; - alias _fgetc_nolock FGETC; - alias _fgetwc_nolock FGETWC; + alias FPUTC = _fputc_nolock; + alias FPUTWC = _fputwc_nolock; + alias FGETC = _fgetc_nolock; + alias FGETWC = _fgetwc_nolock; - alias _lock_file FLOCK; - alias _unlock_file FUNLOCK; - - enum _O_BINARY = 0x8000; + alias FLOCK = _lock_file; + alias FUNLOCK = _unlock_file; + enum + { + _O_RDONLY = 0x0000, + _O_APPEND = 0x0004, + _O_TEXT = 0x4000, + _O_BINARY = 0x8000, + } } else version (GCC_IO) { @@ -182,6 +191,8 @@ else version (GCC_IO) */ extern (C) { + nothrow: + @nogc: int fputc_unlocked(int, _iobuf*); int fputwc_unlocked(wchar_t, _iobuf*); int fgetc_unlocked(_iobuf*); @@ -195,16 +206,19 @@ else version (GCC_IO) size_t size, size_t n, _iobuf *stream); } - alias fputc_unlocked FPUTC; - alias fputwc_unlocked FPUTWC; - alias fgetc_unlocked FGETC; - alias fgetwc_unlocked FGETWC; + alias FPUTC = fputc_unlocked; + alias FPUTWC = fputwc_unlocked; + alias FGETC = fgetc_unlocked; + alias FGETWC = fgetwc_unlocked; - alias flockfile FLOCK; - alias funlockfile FUNLOCK; + alias FLOCK = flockfile; + alias FUNLOCK = funlockfile; } else version (GENERIC_IO) { + nothrow: + @nogc: + extern (C) { void flockfile(FILE*); @@ -219,13 +233,13 @@ else version (GENERIC_IO) int fgetc_unlocked(_iobuf* fp) { return fgetc(cast(shared) fp); } int fgetwc_unlocked(_iobuf* fp) { return fgetwc(cast(shared) fp); } - alias fputc_unlocked FPUTC; - alias fputwc_unlocked FPUTWC; - alias fgetc_unlocked FGETC; - alias fgetwc_unlocked FGETWC; + alias FPUTC = fputc_unlocked; + alias FPUTWC = fputwc_unlocked; + alias FGETC = fgetc_unlocked; + alias FGETWC = fgetwc_unlocked; - alias flockfile FLOCK; - alias funlockfile FUNLOCK; + alias FLOCK = flockfile; + alias FUNLOCK = funlockfile; } else { @@ -355,7 +369,7 @@ struct File private Impl* _p; private string _name; - package this(FILE* handle, string name, uint refs = 1, bool isPopened = false) + package this(FILE* handle, string name, uint refs = 1, bool isPopened = false) @trusted { import std.exception : enforce; @@ -381,7 +395,7 @@ object refers to it anymore. Throws: $(D ErrnoException) if the file could not be opened. */ - this(string name, in char[] stdioOpenmode = "rb") + this(string name, in char[] stdioOpenmode = "rb") @safe { import std.conv : text; import std.exception : errnoEnforce; @@ -392,12 +406,12 @@ Throws: $(D ErrnoException) if the file could not be opened. name); } - ~this() + ~this() @safe { detach(); } - this(this) + this(this) @safe { if (!_p) return; assert(_p.refs); @@ -409,7 +423,7 @@ Assigns a file to another. The target of the assignment gets detached from whatever file it was attached to, and attaches itself to the new file. */ - void opAssign(File rhs) + void opAssign(File rhs) @safe { import std.algorithm : swap; @@ -424,7 +438,7 @@ cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) function. Throws: $(D ErrnoException) in case of error. */ - void open(string name, in char[] stdioOpenmode = "rb") + void open(string name, in char[] stdioOpenmode = "rb") @safe { detach(); this = File(name, stdioOpenmode); @@ -437,7 +451,7 @@ opengroup.org/onlinepubs/007908799/xsh/_popen.html, _popen). Throws: $(D ErrnoException) in case of error. */ - version(Posix) void popen(string command, in char[] stdioOpenmode = "r") + version(Posix) void popen(string command, in char[] stdioOpenmode = "r") @safe { import std.exception : errnoEnforce; @@ -447,8 +461,100 @@ Throws: $(D ErrnoException) in case of error. command, 1, true); } +/** +First calls $(D detach) (throwing on failure), and then attempts to +associate the given file descriptor with the $(D File). The mode must +be compatible with the mode of the file descriptor. + +Throws: $(D ErrnoException) in case of error. + */ + void fdopen(int fd, in char[] stdioOpenmode = "rb") @safe + { + fdopen(fd, stdioOpenmode, null); + } + + package void fdopen(int fd, in char[] stdioOpenmode, string name) @trusted + { + import std.string : toStringz; + import std.exception : errnoEnforce; + + detach(); + + version (DIGITAL_MARS_STDIO) + { + // This is a re-implementation of DMC's fdopen, but without the + // mucking with the file descriptor. POSIX standard requires the + // new fdopen'd file to retain the given file descriptor's + // position. + auto fp = core.stdc.stdio.fopen("NUL", toStringz(stdioOpenmode)); + errnoEnforce(fp, "Cannot open placeholder NUL stream"); + FLOCK(fp); + auto iob = cast(_iobuf*)fp; + .close(iob._file); + iob._file = fd; + iob._flag &= ~_IOTRAN; + FUNLOCK(fp); + } + else + { + version (Windows) // MSVCRT + auto fp = _fdopen(fd, toStringz(stdioOpenmode)); + else + auto fp = .fdopen(fd, toStringz(stdioOpenmode)); + errnoEnforce(fp); + } + this = File(fp, name); + } + + // Declare a dummy HANDLE to allow generating documentation + // for Windows-only methods. + version(StdDdoc) { version(Windows) {} else alias HANDLE = int; } + +/** +First calls $(D detach) (throwing on failure), and then attempts to +associate the given Windows $(D HANDLE) with the $(D File). The mode must +be compatible with the access attributes of the handle. Windows only. + +Throws: $(D ErrnoException) in case of error. +*/ + version(StdDdoc) + void windowsHandleOpen(HANDLE handle, in char[] stdioOpenmode); + + version(Windows) + void windowsHandleOpen(HANDLE handle, in char[] stdioOpenmode) + { + import std.exception : errnoEnforce; + import std.string : format; + + // Create file descriptors from the handles + version (DIGITAL_MARS_STDIO) + auto fd = _handleToFD(handle, FHND_DEVICE); + else // MSVCRT + { + int mode; + modeLoop: + foreach (c; stdioOpenmode) + switch (c) + { + case 'r': mode |= _O_RDONLY; break; + case '+': mode &=~_O_RDONLY; break; + case 'a': mode |= _O_APPEND; break; + case 'b': mode |= _O_BINARY; break; + case 't': mode |= _O_TEXT; break; + case ',': break modeLoop; + default: break; + } + + auto fd = _open_osfhandle(cast(intptr_t)handle, mode); + } + + errnoEnforce(fd >= 0, "Cannot open Windows HANDLE"); + fdopen(fd, stdioOpenmode, "HANDLE(%s)".format(handle)); + } + + /** Returns $(D true) if the file is opened. */ - @property bool isOpen() const pure nothrow + @property bool isOpen() const @safe pure nothrow { return _p !is null && _p.handle; } @@ -459,7 +565,7 @@ cplusplus.com/reference/clibrary/cstdio/feof.html, feof)). Throws: $(D Exception) if the file is not opened. */ - @property bool eof() const pure + @property bool eof() const @trusted pure { import std.exception : enforce; @@ -470,7 +576,7 @@ Throws: $(D Exception) if the file is not opened. /** Returns the name of the last opened file, if any. If a $(D File) was created with $(LREF tmpfile) and $(LREF wrapFile) it has no name.*/ - @property string name() const pure nothrow + @property string name() const @safe pure nothrow { return _name; } @@ -480,9 +586,21 @@ If the file is not opened, returns $(D false). Otherwise, returns $(WEB cplusplus.com/reference/clibrary/cstdio/ferror.html, ferror) for the file handle. */ - @property bool error() const pure nothrow + @property bool error() const @trusted pure nothrow + { + return !isOpen || .ferror(cast(FILE*) _p.handle); + } + + unittest { - return !_p.handle || .ferror(cast(FILE*) _p.handle); + // Issue 12349 + static import std.file; + auto deleteme = testFilename(); + auto f = File(deleteme, "w"); + scope(exit) std.file.remove(deleteme); + + f.close(); + assert(f.error); } /** @@ -490,7 +608,7 @@ Detaches from the underlying file. If the sole owner, calls $(D close). Throws: $(D ErrnoException) on failure if closing the file. */ - void detach() + void detach() @safe { if (!_p) return; if (_p.refs == 1) @@ -529,7 +647,7 @@ referring to the same handle will see a closed file henceforth. Throws: $(D ErrnoException) on error. */ - void close() + void close() @trusted { import std.exception : errnoEnforce; @@ -567,7 +685,7 @@ If the file is not opened, succeeds vacuously. Otherwise, returns $(WEB cplusplus.com/reference/clibrary/cstdio/_clearerr.html, _clearerr) for the file handle. */ - void clearerr() pure nothrow + void clearerr() @safe pure nothrow { _p is null || _p.handle is null || .clearerr(_p.handle); @@ -579,13 +697,26 @@ for the file handle. Throws: $(D Exception) if the file is not opened or if the call to $(D fflush) fails. */ - void flush() + void flush() @trusted { import std.exception : enforce, errnoEnforce; - errnoEnforce - (.fflush(enforce(_p.handle, "Calling fflush() on an unopened file")) - == 0); + enforce(isOpen, "Attempting to flush() in an unopened file"); + errnoEnforce(.fflush(_p.handle) == 0); + } + + unittest + { + // Issue 12349 + static import std.file; + import std.exception: assertThrown; + + auto deleteme = testFilename(); + auto f = File(deleteme, "w"); + scope(exit) std.file.remove(deleteme); + + f.close(); + assertThrown(f.flush()); } /** @@ -607,16 +738,18 @@ $(D rawRead) always reads in binary mode on Windows. import std.exception : enforce, errnoEnforce; enforce(buffer.length, "rawRead must take a non-empty buffer"); - version(Win32) + version(Windows) { immutable fd = ._fileno(_p.handle); immutable mode = ._setmode(fd, _O_BINARY); scope(exit) ._setmode(fd, mode); version(DIGITAL_MARS_STDIO) { + import core.atomic; + // @@@BUG@@@ 4243 immutable info = __fhnd_info[fd]; - __fhnd_info[fd] &= ~FHND_TEXT; + atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT); scope(exit) __fhnd_info[fd] = info; } } @@ -666,9 +799,11 @@ Throws: $(D ErrnoException) if the file is not opened or if the call to $(D fwri scope(exit) ._setmode(fd, mode); version(DIGITAL_MARS_STDIO) { + import core.atomic; + // @@@BUG@@@ 4243 immutable info = __fhnd_info[fd]; - __fhnd_info[fd] &= ~FHND_TEXT; + atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT); scope(exit) __fhnd_info[fd] = info; } scope(exit) flush(); // before restoring translation mode @@ -682,7 +817,6 @@ Throws: $(D ErrnoException) if the file is not opened or if the call to $(D fwri _name, "'")); } - version(Win64) {} else unittest { static import std.file; @@ -702,7 +836,7 @@ for the file handle. Throws: $(D Exception) if the file is not opened. $(D ErrnoException) if the call to $(D fseek) fails. */ - void seek(long offset, int origin = SEEK_SET) + void seek(long offset, int origin = SEEK_SET) @trusted { import std.exception : enforce, errnoEnforce; import std.conv : to, text; @@ -721,7 +855,6 @@ Throws: $(D Exception) if the file is not opened. } } - version(Win64) {} else unittest { static import std.file; @@ -758,7 +891,7 @@ managed file handle. Throws: $(D Exception) if the file is not opened. $(D ErrnoException) if the call to $(D ftell) fails. */ - @property ulong tell() const + @property ulong tell() const @trusted { import std.exception : enforce, errnoEnforce; @@ -796,7 +929,7 @@ for the file handle. Throws: $(D Exception) if the file is not opened. */ - void rewind() + void rewind() @safe { import std.exception : enforce; @@ -811,7 +944,7 @@ the file handle. Throws: $(D Exception) if the file is not opened. $(D ErrnoException) if the call to $(D setvbuf) fails. */ - void setvbuf(size_t size, int mode = _IOFBF) + void setvbuf(size_t size, int mode = _IOFBF) @trusted { import std.exception : enforce, errnoEnforce; @@ -827,7 +960,7 @@ _setvbuf) for the file handle. Throws: $(D Exception) if the file is not opened. $(D ErrnoException) if the call to $(D setvbuf) fails. */ - void setvbuf(void[] buf, int mode = _IOFBF) + void setvbuf(void[] buf, int mode = _IOFBF) @trusted { import std.exception : enforce, errnoEnforce; @@ -841,7 +974,7 @@ Throws: $(D Exception) if the file is not opened. version(Windows) { import core.sys.windows.windows; - import std.windows.syserror; + private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length, Flags flags) { @@ -854,12 +987,14 @@ Throws: $(D Exception) if the file is not opened. overlapped.Offset = liStart.LowPart; overlapped.OffsetHigh = liStart.HighPart; overlapped.hEvent = null; - return F(cast(HANDLE)_get_osfhandle(fileno), flags, 0, - liLength.LowPart, liLength.HighPart, &overlapped); + return F(windowsHandle, flags, 0, liLength.LowPart, + liLength.HighPart, &overlapped); } private static T wenforce(T)(T cond, string str) { + import std.windows.syserror; + if (cond) return cond; throw new Exception(str ~ ": " ~ sysErrorString(GetLastError())); } @@ -1070,7 +1205,7 @@ Throws: $(D Exception) if the file is not opened. auto w = lockingTextWriter(); foreach (arg; args) { - alias typeof(arg) A; + alias A = typeof(arg); static if (isAggregateType!A || is(A == enum)) { import std.format : formattedWrite; @@ -1363,19 +1498,26 @@ for every line. static import std.file; auto deleteme = testFilename(); - std.file.write(deleteme, "hello\nworld\n"); + std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n"); scope(exit) std.file.remove(deleteme); string s; auto f = File(deleteme); f.readf("%s\n", &s); assert(s == "hello", "["~s~"]"); + f.readf("%s\n", &s); + assert(s == "world", "["~s~"]"); + + // Issue 11698 + bool b1, b2; + f.readf("%s\n%s\n", &b1, &b2); + assert(b1 == true && b2 == false); } /** Returns a temporary file by calling $(WEB cplusplus.com/reference/clibrary/cstdio/_tmpfile.html, _tmpfile). Note that the created file has no $(LREF name).*/ - static File tmpfile() + static File tmpfile() @safe { import std.exception : errnoEnforce; @@ -1388,7 +1530,7 @@ for every line. Unsafe function that wraps an existing $(D FILE*). The resulting $(D File) never takes the initiative in closing the file. Note that the created file has no $(LREF name)*/ - /*private*/ static File wrapFile(FILE* f) + /*private*/ static File wrapFile(FILE* f) @safe { import std.exception : enforce; @@ -1399,7 +1541,7 @@ Note that the created file has no $(LREF name)*/ /** Returns the $(D FILE*) corresponding to this object. */ - FILE* getFP() pure + FILE* getFP() @safe pure { import std.exception : enforce; @@ -1416,7 +1558,7 @@ Returns the $(D FILE*) corresponding to this object. /** Returns the file number corresponding to this object. */ - /*version(Posix) */int fileno() const + /*version(Posix) */int fileno() const @trusted { import std.exception : enforce; @@ -1424,6 +1566,22 @@ Returns the file number corresponding to this object. return .fileno(cast(FILE*) _p.handle); } +/** +Returns the underlying operating system $(D HANDLE) (Windows only). +*/ + version(StdDdoc) + @property HANDLE windowsHandle(); + + version(Windows) + @property HANDLE windowsHandle() + { + version (DIGITAL_MARS_STDIO) + return _fdToHandle(fileno); + else + return cast(HANDLE)_get_osfhandle(fileno); + } + + // Note: This was documented until 2013/08 /* Range that reads one line at a time. Returned by $(LREF byLine). @@ -1478,7 +1636,6 @@ Allows to directly use range operations on lines of a file. Char[] line; Terminator terminator; KeepTerminator keepTerminator; - bool first_call = true; public: this(File f, KeepTerminator kt, Terminator terminator) @@ -1486,6 +1643,7 @@ Allows to directly use range operations on lines of a file. file = f; this.terminator = terminator; keepTerminator = kt; + popFront(); } // Range primitive implementations. @@ -1512,11 +1670,6 @@ Allows to directly use range operations on lines of a file. @property Char[] front() { - if (first_call) - { - popFront(); - first_call = false; - } return line; } @@ -1625,7 +1778,7 @@ the contents may well have changed). unittest { static import std.file; - import std.algorithm : take, equal; + import std.algorithm : equal; //printf("Entering test at line %d\n", __LINE__); scope(failure) printf("Failed test at line %d\n", __LINE__); @@ -1642,8 +1795,8 @@ the contents may well have changed). f.detach(); assert(!f.isOpen); - void testTerm(Terminator)(string txt, string[] witness, - KeepTerminator kt, Terminator term, bool popFirstLine) + void test(Terminator)(string txt, string[] witness, + KeepTerminator kt, Terminator term, bool popFirstLine = false) { import std.conv : text; @@ -1667,38 +1820,50 @@ the contents may well have changed). assert(line == witness[i++]); } assert(i == witness.length, text(i, " != ", witness.length)); + + // Issue 11830 + auto walkedLength = File(deleteme).byLine.walkLength; + assert(walkedLength == witness.length, text(walkedLength, " != ", witness.length)); + } + + KeepTerminator kt = KeepTerminator.no; + test("", null, kt, '\n'); + test("\n", [ "" ], kt, '\n'); + test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n'); + test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n', true); + test("asd\ndef\nasdf\n", [ "asd", "def", "asdf" ], kt, '\n'); + test("foo", [ "foo" ], kt, '\n', true); + test("bob\r\nmarge\r\nsteve\r\n", ["bob", "marge", "steve"], + kt, "\r\n"); + test("sue\r", ["sue"], kt, '\r'); + + kt = KeepTerminator.yes; + test("", null, kt, '\n'); + test("\n", [ "\n" ], kt, '\n'); + test("asd\ndef\nasdf", [ "asd\n", "def\n", "asdf" ], kt, '\n'); + test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n'); + test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n', true); + test("foo", [ "foo" ], kt, '\n'); + test("bob\r\nmarge\r\nsteve\r\n", ["bob\r\n", "marge\r\n", "steve\r\n"], + kt, "\r\n"); + test("sue\r", ["sue\r"], kt, '\r'); + } + + unittest + { + import std.algorithm : equal; + + version(Win64) + { + static import std.file; + + /* the C function tmpfile doesn't seem to work, even when called from C */ + auto deleteme = testFilename(); + auto file = File(deleteme, "w+"); + scope(success) std.file.remove(deleteme); } - /* Wrap with default args. - * Note: Having a default argument for terminator = '\n' would prevent - * instantiating Terminator=string (or "\n" would prevent Terminator=char) */ - void test(string txt, string[] witness, - KeepTerminator kt = KeepTerminator.no, - bool popFirstLine = false) - { - testTerm(txt, witness, kt, '\n', popFirstLine); - } - - test("", null); - test("\n", [ "" ]); - test("asd\ndef\nasdf", [ "asd", "def", "asdf" ]); - test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], KeepTerminator.no, true); - test("asd\ndef\nasdf\n", [ "asd", "def", "asdf" ]); - test("foo", [ "foo" ], KeepTerminator.no, true); - testTerm("bob\r\nmarge\r\nsteve\r\n", ["bob", "marge", "steve"], - KeepTerminator.no, "\r\n", false); - testTerm("sue\r", ["sue"], KeepTerminator.no, '\r', false); - - test("", null, KeepTerminator.yes); - test("\n", [ "\n" ], KeepTerminator.yes); - test("asd\ndef\nasdf", [ "asd\n", "def\n", "asdf" ], KeepTerminator.yes); - test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], KeepTerminator.yes); - test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], KeepTerminator.yes, true); - test("foo", [ "foo" ], KeepTerminator.yes, false); - testTerm("bob\r\nmarge\r\nsteve\r\n", ["bob\r\n", "marge\r\n", "steve\r\n"], - KeepTerminator.yes, "\r\n", false); - testTerm("sue\r", ["sue\r"], KeepTerminator.yes, '\r', false); - - auto file = File.tmpfile(); + else + auto file = File.tmpfile(); file.write("1\n2\n3\n"); // bug 9599 @@ -1939,7 +2104,7 @@ $(D Range) that locks the file and allows fast writing to it. _iobuf* handle; // the unshared version of fps int orientation; - this(ref File f) + this(ref File f) @trusted { import std.exception : enforce; @@ -1950,7 +2115,7 @@ $(D Range) that locks the file and allows fast writing to it. handle = cast(_iobuf*)fps; } - ~this() + ~this() @trusted { if(fps) { @@ -1960,7 +2125,7 @@ $(D Range) that locks the file and allows fast writing to it. } } - this(this) + this(this) @trusted { if(fps) { @@ -1969,28 +2134,33 @@ $(D Range) that locks the file and allows fast writing to it. } /// Range primitive implementations. - void put(A)(A writeme) if (is(ElementType!A : const(dchar))) + void put(A)(A writeme) + if (is(ElementType!A : const(dchar)) && + isInputRange!A && + !isInfinite!A) { import std.exception : errnoEnforce; - alias ElementEncodingType!A C; + alias C = ElementEncodingType!A; static assert(!is(C == void)); - if (writeme[0].sizeof == 1 && orientation <= 0) - { - //file.write(writeme); causes infinite recursion!!! - //file.rawWrite(writeme); - auto result = - .fwrite(writeme.ptr, C.sizeof, writeme.length, fps); - if (result != writeme.length) errnoEnforce(0); - } - else + static if (isSomeString!A && C.sizeof == 1) { - // put each character in turn - foreach (dchar c; writeme) + if (orientation <= 0) { - put(c); + //file.write(writeme); causes infinite recursion!!! + //file.rawWrite(writeme); + auto result = + .fwrite(writeme.ptr, C.sizeof, writeme.length, fps); + if (result != writeme.length) errnoEnforce(0); + return; } } + + // put each character in turn + foreach (dchar c; writeme) + { + put(c); + } } // @@@BUG@@@ 2340 @@ -2006,6 +2176,8 @@ $(D Range) that locks the file and allows fast writing to it. } else static if (c.sizeof == 2) { + import std.utf : toUTF8; + if (orientation <= 0) { if (c <= 0x7F) @@ -2027,6 +2199,8 @@ $(D Range) that locks the file and allows fast writing to it. } else // 32-bit characters { + import std.utf : toUTF8; + if (orientation <= 0) { if (c <= 0x7F) @@ -2085,7 +2259,7 @@ See $(LREF byChunk) for an example. } /// Get the size of the file, ulong.max if file is not searchable, but still throws if an actual error occurs. - @property ulong size() + @property ulong size() @safe { import std.exception : collectException; @@ -2110,6 +2284,27 @@ unittest assert(f.tell == 0); } +unittest +{ + static import std.file; + + auto deleteme = testFilename(); + scope(exit) std.file.remove(deleteme); + + { + File f = File(deleteme, "w"); + auto writer = f.lockingTextWriter(); + static assert(isOutputRange!(typeof(writer), dchar)); + writer.put("日本語"); + writer.put("日本語"w); + writer.put("日本語"d); + writer.put('日'); + writer.put(chain(only('本'), only('語'))); + writer.put(repeat('#', 12)); // BUG 11945 + } + assert(File(deleteme).readln() == "日本語日本語日本語日本語############"); +} + /// Used to specify the lock type for $(D File.lock) and $(D File.tryLock). enum LockType { @@ -2313,10 +2508,11 @@ unittest } /** - * $(RED Scheduled for deprecation in January 2013. - * Please use $(D isFileHandle) instead.) + * $(RED Deprecated. Please use $(D isFileHandle) instead. This alias will be + * removed in June 2015.) */ -alias isFileHandle isStreamingDevice; +deprecated("Please use isFileHandle instead.") +alias isStreamingDevice = isFileHandle; /*********************************** For each argument $(D arg) in $(D args), format the argument (as per @@ -2737,7 +2933,7 @@ unittest * (to $(D _wfopen) on Windows) * with appropriately-constructed C-style strings. */ -private FILE* fopen(in char[] name, in char[] mode = "r") +private FILE* fopen(in char[] name, in char[] mode = "r") @trusted { import std.string : toStringz; @@ -2770,7 +2966,7 @@ version (Posix) * Convenience function that forwards to $(D std.c.stdio.popen) * with appropriately-constructed C-style strings. */ - FILE* popen(in char[] name, in char[] mode = "r") + FILE* popen(in char[] name, in char[] mode = "r") @trusted { import std.string : toStringz; @@ -2863,18 +3059,18 @@ struct lines // if (fileName.length && fclose(f)) // StdioException("Could not close file `"~fileName~"'"); // } - alias ParameterTypeTuple!(dg) Parms; + alias Parms = ParameterTypeTuple!(dg); static if (isSomeString!(Parms[$ - 1])) { enum bool duplicate = is(Parms[$ - 1] == string) || is(Parms[$ - 1] == wstring) || is(Parms[$ - 1] == dstring); int result = 0; static if (is(Parms[$ - 1] : const(char)[])) - alias char C; + alias C = char; else static if (is(Parms[$ - 1] : const(wchar)[])) - alias wchar C; + alias C = wchar; else static if (is(Parms[$ - 1] : const(dchar)[])) - alias dchar C; + alias C = dchar; C[] line; static if (Parms.length == 2) Parms[0] i = 0; @@ -2909,7 +3105,7 @@ struct lines import std.exception : assumeUnique; import std.conv : to; - alias ParameterTypeTuple!(dg) Parms; + alias Parms = ParameterTypeTuple!(dg); enum duplicate = is(Parms[$ - 1] : immutable(ubyte)[]); int result = 1; int c = void; @@ -2926,7 +3122,7 @@ struct lines static if (duplicate) auto arg = assumeUnique(buffer); else - alias buffer arg; + alias arg = buffer; // unlock the file while calling the delegate FUNLOCK(f._p.handle); scope(exit) FLOCK(f._p.handle); @@ -2961,9 +3157,9 @@ unittest auto deleteme = testFilename(); scope(exit) { std.file.remove(deleteme); } - alias TypeTuple!(string, wstring, dstring, - char[], wchar[], dchar[]) - TestedWith; + alias TestedWith = + TypeTuple!(string, wstring, dstring, + char[], wchar[], dchar[]); foreach (T; TestedWith) { // test looping with an empty file std.file.write(deleteme, ""); @@ -3005,8 +3201,8 @@ unittest // test with ubyte[] inputs //@@@BUG 2612@@@ - //alias TypeTuple!(immutable(ubyte)[], ubyte[]) TestedWith2; - alias TypeTuple!(immutable(ubyte)[], ubyte[]) TestedWith2; + //alias TestedWith2 = TypeTuple!(immutable(ubyte)[], ubyte[]); + alias TestedWith2 = TypeTuple!(immutable(ubyte)[], ubyte[]); foreach (T; TestedWith2) { // test looping with an empty file std.file.write(deleteme, ""); @@ -3542,8 +3738,11 @@ private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator = '\n') version (GCC_IO) private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator = '\n') { + import std.utf : encode; + if (fwide(fps, 0) > 0) - { /* Stream is in wide characters. + { + /* Stream is in wide characters. * Read them and convert to chars. */ FLOCK(fps); @@ -3746,6 +3945,7 @@ version(linux) { import std.conv : to; import std.exception : enforce; + import std.string : toStringz; auto h = enforce( sock.gethostbyname(std.string.toStringz(host)), new StdioException("gethostbyname")); @@ -3771,12 +3971,13 @@ version(linux) enforce(sock.connect(s, cast(sock.sockaddr*) &addr, addr.sizeof) != -1, new StdioException("Connect failed")); - return File(enforce(fdopen(s, "w+".ptr)), - host ~ ":" ~ to!string(port)); + File f; + f.fdopen(s, "w+", host ~ ":" ~ to!string(port)); + return f; } } -version(unittest) string testFilename(string file = __FILE__, size_t line = __LINE__) +version(unittest) string testFilename(string file = __FILE__, size_t line = __LINE__) @safe pure { import std.conv : text; import std.path : baseName; diff --git a/libphobos/src/std/stream.d b/libphobos/src/std/stream.d index 49014be68..d8df6ed55 100644 --- a/libphobos/src/std/stream.d +++ b/libphobos/src/std/stream.d @@ -30,7 +30,6 @@ /* NOTE: This file has been patched from the original DMD distribution to work with the GDC compiler. */ - module std.stream; @@ -698,8 +697,13 @@ class Stream : InputStream, OutputStream { string fmt; int j = 0; int count = 0, i = 0; - char c = getc(); + char c; + bool firstCharacter = true; while ((j < arguments.length || i < fmt.length) && !eof) { + if(firstCharacter) { + c = getc(); + firstCharacter = false; + } if (fmt.length == 0 || i == fmt.length) { i = 0; if (arguments[j] is typeid(char[])) { @@ -877,9 +881,9 @@ class Stream : InputStream, OutputStream { c = getc(); count++; } - real n = 0; + real r = 0; while (isDigit(c) && width) { - n = n * 10 + (c - '0'); + r = r * 10 + (c - '0'); width--; c = getc(); count++; @@ -890,13 +894,13 @@ class Stream : InputStream, OutputStream { count++; double frac = 1; while (isDigit(c) && width) { - n = n * 10 + (c - '0'); + r = r * 10 + (c - '0'); frac *= 10; width--; c = getc(); count++; } - n /= frac; + r /= frac; } if (width && (c == 'e' || c == 'E')) { width--; @@ -923,24 +927,56 @@ class Stream : InputStream, OutputStream { } if (expneg) { while (exp--) - n /= 10; + r /= 10; } else { while (exp--) - n *= 10; + r *= 10; + } + } + } + if(width && (c == 'n' || c == 'N')) { + width--; + c = getc(); + count++; + if(width && (c == 'a' || c == 'A')) { + width--; + c = getc(); + count++; + if(width && (c == 'n' || c == 'N')) { + width--; + c = getc(); + count++; + r = real.nan; + } + } + } + if(width && (c == 'i' || c == 'I')) { + width--; + c = getc(); + count++; + if(width && (c == 'n' || c == 'N')) { + width--; + c = getc(); + count++; + if(width && (c == 'f' || c == 'F')) { + width--; + c = getc(); + count++; + r = real.infinity; } } } if (neg) - n = -n; + r = -r; if (arguments[j] is typeid(float*)) { float* p = va_arg!(float*)(args); - *p = n; + *p = r; } else if (arguments[j] is typeid(double*)) { double* p = va_arg!(double*)(args); - *p = n; + *p = r; } else if (arguments[j] is typeid(real*)) { real* p = va_arg!(real*)(args); - *p = n; + *p = r; } j++; i++; @@ -1408,6 +1444,45 @@ class Stream : InputStream, OutputStream { if (!seekable) throw new SeekException("Stream is not seekable"); } + + unittest { //unit tests for Issue 1668 + void tryFloatRoundtrip(float x, string fmt = "", string pad = "") { + auto s = new MemoryStream(); + s.writef(fmt, x, pad); + s.position = 0; + + float f; + assert(s.readf(&f)); + assert(x == f || (x != x && f != f)); //either equal or both NaN + } + + tryFloatRoundtrip(1.0); + tryFloatRoundtrip(1.0, "%f"); + tryFloatRoundtrip(1.0, "", " "); + tryFloatRoundtrip(1.0, "%f", " "); + + tryFloatRoundtrip(3.14); + tryFloatRoundtrip(3.14, "%f"); + tryFloatRoundtrip(3.14, "", " "); + tryFloatRoundtrip(3.14, "%f", " "); + + float nan = float.nan; + tryFloatRoundtrip(nan); + tryFloatRoundtrip(nan, "%f"); + tryFloatRoundtrip(nan, "", " "); + tryFloatRoundtrip(nan, "%f", " "); + + float inf = 1.0/0.0; + tryFloatRoundtrip(inf); + tryFloatRoundtrip(inf, "%f"); + tryFloatRoundtrip(inf, "", " "); + tryFloatRoundtrip(inf, "%f", " "); + + tryFloatRoundtrip(-inf); + tryFloatRoundtrip(-inf,"%f"); + tryFloatRoundtrip(-inf, "", " "); + tryFloatRoundtrip(-inf, "%f", " "); + } } /*** @@ -1734,7 +1809,7 @@ class BufferedStream : FilterStream { else return TreadLine!(char).readLine(inBuffer); } - alias Stream.readLine readLine; + alias readLine = Stream.readLine; override wchar[] readLineW(wchar[] inBuffer) { if (ungetAvailable()) @@ -1742,7 +1817,7 @@ class BufferedStream : FilterStream { else return TreadLine!(wchar).readLine(inBuffer); } - alias Stream.readLineW readLineW; + alias readLineW = Stream.readLineW; override void flush() out { @@ -1814,12 +1889,12 @@ class OpenException: StreamFileException { this(string msg) { super(msg); } } -// access modes; may be or'ed +/// Specifies the $(LREF File) access mode used when opening the file. enum FileMode { - In = 1, - Out = 2, - OutNew = 6, // includes FileMode.Out - Append = 10 // includes FileMode.Out + In = 1, /// Opens the file for reading. + Out = 2, /// Opens the file for writing. + OutNew = 6, /// Opens the file for writing, creates a new file if it doesn't exist. + Append = 10 /// Opens the file for writing, appending new data to the end of the file. } version (Windows) { @@ -1832,7 +1907,7 @@ version (Windows) { version (Posix) { private import core.sys.posix.fcntl; private import core.sys.posix.unistd; - alias int HANDLE; + alias HANDLE = int; } /// This subclass is for unbuffered file system streams. @@ -2031,7 +2106,7 @@ class File: Stream { throw new SeekException("unable to move file pointer"); ulong result = (cast(ulong)hi << 32) + low; } else version (Posix) { - auto result = lseek(hFile, cast(int)offset, rel); + auto result = lseek(hFile, cast(off_t)offset, rel); if (result == cast(typeof(result))-1) throw new SeekException("unable to move file pointer"); } diff --git a/libphobos/src/std/string.d b/libphobos/src/std/string.d index 977153191..246223360 100644 --- a/libphobos/src/std/string.d +++ b/libphobos/src/std/string.d @@ -136,6 +136,24 @@ unittest }); } +/++ + Returns a D-style array of $(D char) given a zero-terminated C-style string. + The returned array will retain the same type qualifiers as the input. + + $(RED Important Note:) The returned array is a slice of the original buffer. + The original data is not changed and not copied. ++/ + +inout(char)[] fromStringz(inout(char)* cString) @system pure { + return cString ? cString[0 .. strlen(cString)] : null; +} + +/// +@system pure unittest +{ + assert(fromStringz(null) == null); + assert(fromStringz("foo") == "foo"); +} /++ Returns a C-style zero-terminated string equivalent to $(D s). $(D s) @@ -148,7 +166,7 @@ unittest to it in your D code. Otherwise, it may go away during a garbage collection cycle and cause a nasty bug when the C code tries to use it. +/ -immutable(char)* toStringz(const(char)[] s) pure nothrow +immutable(char)* toStringz(const(char)[] s) @trusted pure nothrow in { // The assert below contradicts the unittests! @@ -191,7 +209,7 @@ body } /++ Ditto +/ -immutable(char)* toStringz(string s) pure nothrow +immutable(char)* toStringz(string s) @trusted pure nothrow { if (s.empty) return "".ptr; /* Peek past end of s[], if it's 0, no conversion necessary. @@ -210,7 +228,7 @@ immutable(char)* toStringz(string s) pure nothrow return toStringz(cast(const char[]) s); } -unittest +pure nothrow unittest { debug(string) printf("string.toStringz.unittest\n"); @@ -244,7 +262,7 @@ unittest enum CaseSensitive { no, yes } /++ - Returns the index of the first occurence of $(D c) in $(D s). If $(D c) + Returns the index of the first occurrence of $(D c) in $(D s). If $(D c) is not found, then $(D -1) is returned. $(D cs) indicates whether the comparisons are case sensitive. @@ -339,7 +357,7 @@ unittest } /++ - Returns the index of the first occurence of $(D c) in $(D s) with respect + Returns the index of the first occurrence of $(D c) in $(D s) with respect to the start index $(D startIdx). If $(D c) is not found, then $(D -1) is returned. If $(D c) is found the value of the returned index is at least $(D startIdx). $(D startIdx) represents a codeunit index in $(D s). If the @@ -404,7 +422,7 @@ unittest } /++ - Returns the index of the first occurence of $(D sub) in $(D s). If $(D sub) + Returns the index of the first occurrence of $(D sub) in $(D s). If $(D sub) is not found, then $(D -1) is returned. $(D cs) indicates whether the comparisons are case sensitive. @@ -480,7 +498,7 @@ unittest } /++ - Returns the index of the first occurence of $(D sub) in $(D s) with + Returns the index of the first occurrence of $(D sub) in $(D s) with respect to the start index $(D startIdx). If $(D sub) is not found, then $(D -1) is returned. If $(D sub) is found the value of the returned index is at least $(D startIdx). $(D startIdx) represents a codeunit index in @@ -563,7 +581,7 @@ unittest } /++ - Returns the index of the last occurence of $(D c) in $(D s). If $(D c) + Returns the index of the last occurrence of $(D c) in $(D s). If $(D c) is not found, then $(D -1) is returned. $(D cs) indicates whether the comparisons are case sensitive. @@ -668,7 +686,7 @@ unittest } /++ - Returns the index of the last occurence of $(D c) in $(D s). If $(D c) is + Returns the index of the last occurrence of $(D c) in $(D s). If $(D c) is not found, then $(D -1) is returned. The $(D startIdx) slices $(D s) in the following way $(D s[0 .. startIdx]). $(D startIdx) represents a codeunit index in $(D s). If the sequence ending at $(D startIdx) does not @@ -722,7 +740,7 @@ unittest } /++ - Returns the index of the last occurence of $(D sub) in $(D s). If $(D sub) + Returns the index of the last occurrence of $(D sub) in $(D s). If $(D sub) is not found, then $(D -1) is returned. $(D cs) indicates whether the comparisons are case sensitive. @@ -858,7 +876,7 @@ unittest } /++ - Returns the index of the last occurence of $(D sub) in $(D s). If $(D sub) + Returns the index of the last occurrence of $(D sub) in $(D s). If $(D sub) is not found, then $(D -1) is returned. The $(D startIdx) slices $(D s) in the following way $(D s[0 .. startIdx]). $(D startIdx) represents a codeunit index in $(D s). If the sequence ending at $(D startIdx) does not @@ -927,6 +945,401 @@ unittest } } +private ptrdiff_t indexOfAnyImpl(bool forward, Char, Char2)( + const(Char)[] haystack, const(Char2)[] needles, + CaseSensitive cs = CaseSensitive.yes) @safe pure + if (isSomeChar!Char && isSomeChar!Char2) +{ + if (cs == CaseSensitive.yes) + { + static if (forward) + { + size_t n = haystack.findAmong(needles).length; + return n ? haystack.length - n : -1; + } + else + { + size_t n = haystack.retro.findAmong(needles).source.length; + if (n) + { + return n - haystack.strideBack(n); + } + return -1; + } + + } + else + { + if (needles.length <= 16 || needles.walkLength(17)) + { + size_t si = 0; + dchar[16] scratch = void; + foreach ( dchar c; needles) + { + scratch[si++] = std.uni.toLower(c); + } + + static if (forward) + { + foreach (i, dchar c; haystack) + { + if (canFind(scratch[0 .. si], std.uni.toLower(c))) + { + return i; + } + } + } + else + { + foreach_reverse (i, dchar c; haystack) + { + if (canFind(scratch[0 .. si], std.uni.toLower(c))) + { + return i; + } + } + } + } + else + { + static bool f(dchar a, dchar b) + { + return std.uni.toLower(a) == b; + } + + static if (forward) + { + foreach (i, dchar c; haystack) + { + if (canFind!f(needles, std.uni.toLower(c))) + { + return i; + } + } + } + else + { + foreach_reverse (i, dchar c; haystack) + { + if (canFind!f(needles, std.uni.toLower(c))) + { + return i; + } + } + } + } + } + + return -1; +} + +/** + Returns the index of the first occurence of any of the elements in $(D + needles) in $(D haystack). If no element of $(D needles) is found, + then $(D -1) is returned. + + Params: + cs = Indicates whether the comparisons are case sensitive. +*/ +ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, + CaseSensitive cs = CaseSensitive.yes) @safe pure + if (isSomeChar!Char && isSomeChar!Char2) +{ + return indexOfAnyImpl!(true)(haystack, needles, cs); +} + +/// +unittest { + ptrdiff_t i = "helloWorld".indexOfAny("Wr"); + assert(i == 5); + i = "öällo world".indexOfAny("lo "); + assert(i == 4, to!string(i)); +} + +unittest +{ + debug(string) printf("string.indexOfAny.unittest\n"); + + assertCTFEable!( + { + foreach (S; TypeTuple!(string, wstring, dstring)) + { + foreach (T; TypeTuple!(string, wstring, dstring)) + { + assert(indexOfAny(cast(S)null, to!T("a")) == -1); + assert(indexOfAny(to!S("def"), to!T("rsa")) == -1); + assert(indexOfAny(to!S("abba"), to!T("a")) == 0); + assert(indexOfAny(to!S("def"), to!T("f")) == 2); + assert(indexOfAny(to!S("dfefffg"), to!T("fgh")) == 1); + assert(indexOfAny(to!S("dfeffgfff"), to!T("feg")) == 1); + + assert(indexOfAny(to!S("zfeffgfff"), to!T("ACDC"), + CaseSensitive.no) == -1); + assert(indexOfAny(to!S("def"), to!T("MI6"), + CaseSensitive.no) == -1); + assert(indexOfAny(to!S("abba"), to!T("DEA"), + CaseSensitive.no) == 0); + assert(indexOfAny(to!S("def"), to!T("FBI"), CaseSensitive.no) == 2); + assert(indexOfAny(to!S("dfefffg"), to!T("NSA"), CaseSensitive.no) + == -1); + assert(indexOfAny(to!S("dfeffgfff"), to!T("BND"), + CaseSensitive.no) == 0); + + assert(indexOfAny("\u0100", to!T("\u0100"), CaseSensitive.no) == 0); + } + } + }); +} + +/** + Returns the index of the first occurence of any of the elements in $(D + needles) in $(D haystack). If no element of $(D needles) is found, + then $(D -1) is returned. The $(D startIdx) slices $(D s) in the following + way $(D haystack[startIdx .. $]). $(D startIdx) represents a codeunit + index in $(D haystack). If the sequence ending at $(D startIdx) does not + represent a well formed codepoint, then a $(XREF utf,UTFException) may be + thrown. + + Params: + cs = Indicates whether the comparisons are case sensitive. + startIdx = slices haystack like this $(D haystack[startIdx .. $]). If + the startIdx is greater equal the length of haystack the functions + returns $(D -1). +*/ +ptrdiff_t indexOfAny(Char,Char2)(const(Char)[] haystack, const(Char2)[] needles, + const size_t startIdx, CaseSensitive cs = CaseSensitive.yes) @safe pure + if (isSomeChar!Char && isSomeChar!Char2) +{ + if (startIdx < haystack.length) + { + ptrdiff_t foundIdx = indexOfAny(haystack[startIdx .. $], needles, cs); + if (foundIdx != -1) + { + return foundIdx + cast(ptrdiff_t)startIdx; + } + } + + return -1; +} + +/// +unittest +{ + ptrdiff_t i = "helloWorld".indexOfAny("Wr", 4); + assert(i == 5); + + i = "Foo öällo world".indexOfAny("lh", 3); + assert(i == 8, to!string(i)); +} + +unittest +{ + debug(string) printf("string.indexOfAny(startIdx).unittest\n"); + + foreach(S; TypeTuple!(string, wstring, dstring)) + { + foreach(T; TypeTuple!(string, wstring, dstring)) + { + assert(indexOfAny(cast(S)null, to!T("a"), 1337) == -1); + assert(indexOfAny(to!S("def"), to!T("AaF"), 0) == -1); + assert(indexOfAny(to!S("abba"), to!T("NSa"), 2) == 3); + assert(indexOfAny(to!S("def"), to!T("fbi"), 1) == 2); + assert(indexOfAny(to!S("dfefffg"), to!T("foo"), 2) == 3); + assert(indexOfAny(to!S("dfeffgfff"), to!T("fsb"), 5) == 6); + + assert(indexOfAny(to!S("dfeffgfff"), to!T("NDS"), 1, + CaseSensitive.no) == -1); + assert(indexOfAny(to!S("def"), to!T("DRS"), 2, + CaseSensitive.no) == -1); + assert(indexOfAny(to!S("abba"), to!T("SI"), 3, + CaseSensitive.no) == -1); + assert(indexOfAny(to!S("deO"), to!T("ASIO"), 1, + CaseSensitive.no) == 2); + assert(indexOfAny(to!S("dfefffg"), to!T("fbh"), 2, + CaseSensitive.no) == 3); + assert(indexOfAny(to!S("dfeffgfff"), to!T("fEe"), 4, + CaseSensitive.no) == 4); + assert(indexOfAny(to!S("dfeffgffföä"), to!T("föä"), 9, + CaseSensitive.no) == 9); + + assert(indexOfAny("\u0100", to!T("\u0100"), 0, + CaseSensitive.no) == 0); + } + + foreach(cs; EnumMembers!CaseSensitive) + { + assert(indexOfAny("hello\U00010143\u0100\U00010143", + to!S("e\u0100"), 3, cs) == 9); + assert(indexOfAny("hello\U00010143\u0100\U00010143"w, + to!S("h\u0100"), 3, cs) == 7); + assert(indexOfAny("hello\U00010143\u0100\U00010143"d, + to!S("l\u0100"), 5, cs) == 6); + } + } +} + +/** + Returns the index of the last occurence of any of the elements in $(D + needles) in $(D haystack). If no element of $(D needles) is found, + then $(D -1) is returned. + + Params: + cs = Indicates whether the comparisons are case sensitive. +*/ +ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack, + const(Char2)[] needles, CaseSensitive cs = CaseSensitive.yes) @safe pure + if (isSomeChar!Char && isSomeChar!Char2) +{ + return indexOfAnyImpl!(false)(haystack, needles, cs); +} + +/// +unittest +{ + ptrdiff_t i = "helloWorld".lastIndexOfAny("Wlo"); + assert(i == 8); + + i = "Foo öäöllo world".lastIndexOfAny("öF"); + assert(i == 8); +} + +unittest +{ + debug(string) printf("string.lastIndexOfAny.unittest\n"); + + assertCTFEable!( + { + foreach (S; TypeTuple!(string, wstring, dstring)) + { + foreach (T; TypeTuple!(string, wstring, dstring)) + { + assert(lastIndexOfAny(cast(S)null, to!T("a")) == -1); + assert(lastIndexOfAny(to!S("def"), to!T("rsa")) == -1); + assert(lastIndexOfAny(to!S("abba"), to!T("a")) == 3); + assert(lastIndexOfAny(to!S("def"), to!T("f")) == 2); + assert(lastIndexOfAny(to!S("dfefffg"), to!T("fgh")) == 6); + + ptrdiff_t oeIdx = 9; + if (is(S == wstring) || is(S == dstring)) + { + oeIdx = 8; + } + + auto foundOeIdx = lastIndexOfAny(to!S("dfeffgföf"), to!T("feg")); + assert(foundOeIdx == oeIdx, to!string(foundOeIdx)); + + assert(lastIndexOfAny(to!S("zfeffgfff"), to!T("ACDC"), + CaseSensitive.no) == -1); + assert(lastIndexOfAny(to!S("def"), to!T("MI6"), + CaseSensitive.no) == -1); + assert(lastIndexOfAny(to!S("abba"), to!T("DEA"), + CaseSensitive.no) == 3); + assert(lastIndexOfAny(to!S("def"), to!T("FBI"), + CaseSensitive.no) == 2); + assert(lastIndexOfAny(to!S("dfefffg"), to!T("NSA"), + CaseSensitive.no) == -1); + + oeIdx = 2; + if (is(S == wstring) || is(S == dstring)) + { + oeIdx = 1; + } + assert(lastIndexOfAny(to!S("ödfeffgfff"), to!T("BND"), + CaseSensitive.no) == oeIdx); + + assert(lastIndexOfAny("\u0100", to!T("\u0100"), + CaseSensitive.no) == 0); + } + } + }); +} + +/** + Returns the index of the last occurence of any of the elements in $(D + needles) in $(D haystack). If no element of $(D needles) is found, + then $(D -1) is returned. The $(D stopIdx) slices $(D s) in the following + way $(D s[0 .. stopIdx]). $(D stopIdx) represents a codeunit index in + $(D s). If the sequence ending at $(D startIdx) does not represent a well + formed codepoint, then a $(XREF utf,UTFException) may be thrown. + + Params: + cs = Indicates whether the comparisons are case sensitive. + stopIdx = slices haystack like this $(D haystack[0 .. stopIdx]). If + the stopIdx is greater equal the length of haystack the functions + returns $(D -1). +*/ +ptrdiff_t lastIndexOfAny(Char,Char2)(const(Char)[] haystack, + const(Char2)[] needles, const size_t stopIdx, + CaseSensitive cs = CaseSensitive.yes) @safe pure + if (isSomeChar!Char && isSomeChar!Char2) +{ + if (stopIdx <= haystack.length) + { + return lastIndexOfAny(haystack[0u .. stopIdx], needles, cs); + } + + return -1; +} + +/// +unittest +{ + ptrdiff_t i = "helloWorld".lastIndexOfAny("Wlo", 4); + assert(i == 3); + + i = "Foo öäöllo world".lastIndexOfAny("öF", 3); + assert(i == 0); +} + +unittest +{ + debug(string) printf("string.lastIndexOfAny(index).unittest\n"); + + assertCTFEable!( + { + foreach (S; TypeTuple!(string, wstring, dstring)) + { + foreach (T; TypeTuple!(string, wstring, dstring)) + { + enum typeStr = S.stringof ~ " " ~ T.stringof; + + assert(lastIndexOfAny(cast(S)null, to!T("a"), 1337) == -1, + typeStr); + assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("c"), 7) == 6, + typeStr); + assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("cd"), 5) == 3, + typeStr); + assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("ef"), 6) == 5, + typeStr); + assert(lastIndexOfAny(to!S("abcdefCdef"), to!T("c"), 8) == 2, + typeStr); + assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("x"), 7) == -1, + typeStr); + assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("xy"), 4) == -1, + typeStr); + assert(lastIndexOfAny(to!S("öabcdefcdef"), to!T("ö"), 2) == 0, + typeStr); + + assert(lastIndexOfAny(cast(S)null, to!T("a"), 1337, + CaseSensitive.no) == -1, typeStr); + assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("C"), 7, + CaseSensitive.no) == 6, typeStr); + assert(lastIndexOfAny(to!S("ABCDEFCDEF"), to!T("cd"), 5, + CaseSensitive.no) == 3, typeStr); + assert(lastIndexOfAny(to!S("abcdefcdef"), to!T("EF"), 6, + CaseSensitive.no) == 5, typeStr); + assert(lastIndexOfAny(to!S("ABCDEFcDEF"), to!T("C"), 8, + CaseSensitive.no) == 6, typeStr); + assert(lastIndexOfAny(to!S("ABCDEFCDEF"), to!T("x"), 7, + CaseSensitive.no) == -1, typeStr); + assert(lastIndexOfAny(to!S("abCdefcdef"), to!T("XY"), 4, + CaseSensitive.no) == -1, typeStr); + assert(lastIndexOfAny(to!S("ÖABCDEFCDEF"), to!T("ö"), 2, + CaseSensitive.no) == 0, typeStr); + } + } + }); +} /** * Returns the representation of a string, which has the same type @@ -937,7 +1350,7 @@ auto representation(Char)(Char[] s) pure nothrow if (isSomeChar!Char) { // Get representation type - alias TypeTuple!(ubyte, ushort, uint)[Char.sizeof / 2] U; + alias U = TypeTuple!(ubyte, ushort, uint)[Char.sizeof / 2]; // const and immutable storage classes static if (is(Char == immutable)) @@ -1083,7 +1496,7 @@ unittest assert(s2 !is s1); s1 = to!S("\u0131 \u0130"); s2 = capitalize(s1); - assert(cmp(s2, "I \u0130") == 0); + assert(cmp(s2, "\u0049 \u0069") == 0); assert(s2 !is s1); s1 = to!S("\u017F \u0049"); @@ -1731,7 +2144,7 @@ S detab(S)(S s, size_t tabSize = 8) @trusted pure if (isSomeString!S) { assert(tabSize > 0); - alias Unqual!(typeof(s[0])) C; + alias C = Unqual!(typeof(s[0])); bool changes = false; C[] result; int column; @@ -1817,7 +2230,7 @@ S entab(S)(S s, size_t tabSize = 8) @trusted pure if (isSomeString!S) { bool changes = false; - alias Unqual!(typeof(s[0])) C; + alias C = Unqual!(typeof(s[0])); C[] result; int nspaces = 0; @@ -2511,7 +2924,7 @@ unittest } // Explicitly undocumented. It will be removed in July 2014. -deprecated("Please use std.string.format instead.") alias format xformat; +deprecated("Please use std.string.format instead.") alias xformat = format; deprecated unittest { @@ -2534,7 +2947,7 @@ deprecated unittest } // Explicitly undocumented. It will be removed in July 2014. -deprecated("Please use std.string.sformat instead.") alias sformat xsformat; +deprecated("Please use std.string.sformat instead.") alias xsformat = sformat; deprecated unittest { @@ -2941,7 +3354,7 @@ unittest $(D to) is taken to be the same as $(D from). If the modifier $(D 'd') is $(I not) present, and $(D to) is shorter than - $(D from), then $(D to) is extended by replicating the last charcter in + $(D from), then $(D to) is extended by replicating the last character in $(D to). Both $(D from) and $(D to) may contain ranges using the $(D '-') character @@ -3138,7 +3551,7 @@ unittest * * [in] bool bAllowSep * False by default, but when set to true it will accept the - * separator characters "," and "_" within the string, but these + * separator characters $(D ',') and $(D '__') within the string, but these * characters should be stripped from the string before using any * of the conversion functions like toInt(), toFloat(), and etc * else an error will occur. @@ -3160,7 +3573,7 @@ bool isNumeric(const(char)[] s, in bool bAllowSep = false) @safe pure ("nan", "nani", "nan+nani", "inf", "-inf")) return true; - immutable j = s[0].among!('-', '+') != 0; + immutable j = s[0].among!('-', '+')() != 0; bool bDecimalPoint, bExponent, bComplex, sawDigits; for (size_t i = j; i < iLen; i++) @@ -3189,14 +3602,14 @@ bool isNumeric(const(char)[] s, in bool bAllowSep = false) @safe pure } // Allow only one exponent per number - if (c.among!('e', 'E')) + if (c.among!('e', 'E')()) { // A 2nd exponent found, return not a number if (bExponent || i + 1 >= iLen) return false; // Look forward for the sign, and if // missing then this is not a number. - if (!s[i + 1].among!('-', '+')) + if (!s[i + 1].among!('-', '+')()) return false; bExponent = true; i++; @@ -3238,18 +3651,18 @@ bool isNumeric(const(char)[] s, in bool bAllowSep = false) @safe pure if (!sawDigits) return false; // Integer Whole Number - if (c.among!('u', 'l', 'U', 'L') && + if (c.among!('u', 'l', 'U', 'L')() && (!bDecimalPoint && !bExponent && !bComplex)) return true; // Check to see if the last character in the string // is the required 'i' character if (bComplex) - return c.among!('i', 'I') != 0; + return c.among!('i', 'I')() != 0; // Floating-Point Number - return c.among!('l', 'L', 'f', 'F', 'i', 'I') != 0; + return c.among!('l', 'L', 'f', 'F', 'i', 'I')() != 0; } // Check if separators are allowed to be in the numeric string - if (!bAllowSep || !c.among!('_', ',')) + if (!bAllowSep || !c.among!('_', ',')()) return false; } @@ -3736,29 +4149,7 @@ unittest * the input from being outdented. * * Works at compile-time. - * - * Example: - * --- - * writeln(q{ - * import std.stdio; - * void main() { - * writeln("Hello"); - * } - * }.outdent()); - * --- - * - * Output: - * --- - * - * import std.stdio; - * void main() { - * writeln("Hello"); - * } - * - * --- - * */ - S outdent(S)(S str) @safe pure if(isSomeString!S) { return str.splitLines(KeepTerminator.yes).outdent().join(); @@ -3774,17 +4165,17 @@ S[] outdent(S)(S[] lines) @safe pure if(isSomeString!S) static S leadingWhiteOf(S str) { - return str[ 0 .. $-find!(not!(std.uni.isWhite))(str).length ]; + return str[ 0 .. $ - stripLeft(str).length ]; } S shortestIndent; - foreach (i, line; lines) + foreach (ref line; lines) { - auto stripped = __ctfe? line.ctfe_strip() : line.strip(); + auto stripped = line.stripLeft(); if (stripped.empty) { - lines[i] = line[line.chomp().length..$]; + line = line[line.chomp().length .. $]; } else { @@ -3801,53 +4192,45 @@ S[] outdent(S)(S[] lines) @safe pure if(isSomeString!S) } } - foreach (i; 0..lines.length) + foreach (ref line; lines) { - auto stripped = __ctfe? lines[i].ctfe_strip() : lines[i].strip(); + auto stripped = line.stripLeft(); if (stripped.empty) { // Do nothing } - else if (lines[i].startsWith(shortestIndent)) + else if (line.startsWith(shortestIndent)) { - lines[i] = lines[i][shortestIndent.length..$]; + line = line[shortestIndent.length .. $]; } else { - if (__ctfe) - assert(false, "outdent: Inconsistent indentation"); - else - throw new StringException("outdent: Inconsistent indentation"); + throw new StringException("outdent: Inconsistent indentation"); } } return lines; } -// TODO: Remove this and use std.string.strip when retro() becomes ctfe-able. -private S ctfe_strip(S)(S str) if(isSomeString!(Unqual!S)) +/// +unittest { - return str.stripLeft().ctfe_stripRight(); + enum pretty = q{ + import std.stdio; + void main() { + writeln("Hello"); + } + }.outdent(); + + enum ugly = q{ +import std.stdio; +void main() { + writeln("Hello"); } +}; -// TODO: Remove this and use std.string.strip when retro() becomes ctfe-able. -private S ctfe_stripRight(S)(S str) if(isSomeString!(Unqual!S)) -{ - size_t endIndex = 0; - size_t prevIndex = str.length; - - foreach_reverse (i, dchar ch; str) - { - if (!std.uni.isWhite(ch)) - { - endIndex = prevIndex; - break; - } - prevIndex = i; - } - - return str[0..endIndex]; + assert(pretty == ugly); } unittest @@ -3880,11 +4263,6 @@ unittest assertCTFEable!( { - static assert(ctfe_strip(" \tHi \r\n") == "Hi"); - static assert(ctfe_strip(" \tHi©\u2028 \r\n") == "Hi©"); - static assert(ctfe_strip("Hi") == "Hi"); - static assert(ctfe_strip(" \t \r\n") == ""); - static assert(ctfe_strip("") == ""); foreach (S; TypeTuple!(string, wstring, dstring)) { @@ -3938,6 +4316,11 @@ unittest enum expected6 = "\r\n\r\n\u2028\u2029"; assert(testStr6.outdent() == expected6); static assert(testStr6.outdent() == expected6); + + enum testStr7 = " a \n b "; + enum expected7 = "a \nb "; + assert(testStr7.outdent() == expected7); + static assert(testStr7.outdent() == expected7); } }); } diff --git a/libphobos/src/std/traits.d b/libphobos/src/std/traits.d index 89ce81110..f64214880 100644 --- a/libphobos/src/std/traits.d +++ b/libphobos/src/std/traits.d @@ -46,6 +46,8 @@ * $(LREF InterfacesTuple) * $(LREF TransitiveBaseTypeTuple) * $(LREF MemberFunctionsTuple) + * $(LREF TemplateOf) + * $(LREF TemplateArgsOf) * $(LREF classInstanceAlignment) * )) * $(TR $(TD Type Conversion) $(TD @@ -145,13 +147,12 @@ * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) */ - -/* NOTE: This file has been patched from the original DMD distribution to - work with the GDC compiler. - */ module std.traits; import std.typetuple; +// FIXME @@@bug@@@ 12961 +import std.conv; + /////////////////////////////////////////////////////////////////////////////// // Functions /////////////////////////////////////////////////////////////////////////////// @@ -208,7 +209,8 @@ private 'c': FunctionAttribute.ref_, 'd': FunctionAttribute.property, 'e': FunctionAttribute.trusted, - 'f': FunctionAttribute.safe + 'f': FunctionAttribute.safe, + 'i': FunctionAttribute.nogc ]; uint atts = 0; @@ -227,26 +229,17 @@ private return Demangle!uint(atts, mstr); } - alias TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong) IntegralTypeList; - alias TypeTuple!(byte, short, int, long) SignedIntTypeList; - alias TypeTuple!(ubyte, ushort, uint, ulong) UnsignedIntTypeList; - alias TypeTuple!(float, double, real) FloatingPointTypeList; - alias TypeTuple!(ifloat, idouble, ireal) ImaginaryTypeList; - alias TypeTuple!(cfloat, cdouble, creal) ComplexTypeList; - alias TypeTuple!(IntegralTypeList, FloatingPointTypeList) NumericTypeList; - alias TypeTuple!(char, wchar, dchar) CharTypeList; + alias IntegralTypeList = TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong); + alias SignedIntTypeList = TypeTuple!(byte, short, int, long); + alias UnsignedIntTypeList = TypeTuple!(ubyte, ushort, uint, ulong); + alias FloatingPointTypeList = TypeTuple!(float, double, real); + alias ImaginaryTypeList = TypeTuple!(ifloat, idouble, ireal); + alias ComplexTypeList = TypeTuple!(cfloat, cdouble, creal); + alias NumericTypeList = TypeTuple!(IntegralTypeList, FloatingPointTypeList); + alias CharTypeList = TypeTuple!(char, wchar, dchar); } package { - /* Get an expression typed as T, like T.init */ - template defaultInit(T) - { - static if (!is(typeof({ T v = void; }))) // inout(U) - @property T defaultInit(T v = T.init); - else - @property T defaultInit(); - } - // Add specific qualifier to the given type T template MutableOf(T) { alias MutableOf = T ; } template InoutOf(T) { alias InoutOf = inout(T) ; } @@ -293,7 +286,7 @@ package version(unittest) { - alias TypeTuple!(MutableOf, ConstOf, SharedOf, SharedConstOf, ImmutableOf) TypeQualifierList; + alias TypeQualifierList = TypeTuple!(MutableOf, ConstOf, SharedOf, SharedConstOf, ImmutableOf); struct SubTypeOf(T) { @@ -302,6 +295,8 @@ version(unittest) } } +private alias parentOf(alias sym) = Identity!(__traits(parent, sym)); +private alias parentOf(alias sym : T!Args, alias T, Args...) = Identity!(__traits(parent, T)); /** * Get the full package name for the given symbol. @@ -315,8 +310,8 @@ template packageName(alias T) { import std.algorithm : startsWith; - static if (__traits(compiles, __traits(parent, T))) - enum parent = packageName!(__traits(parent, T)); + static if (__traits(compiles, parentOf!T)) + enum parent = packageName!(parentOf!T); else enum string parent = null; @@ -342,6 +337,9 @@ unittest static assert(packageName!core == "core"); static assert(packageName!(core.sync) == "core.sync"); static assert(packageName!Barrier == "core.sync"); + + struct X12287(T) { T i; } + static assert(packageName!(X12287!int.i) == "std"); } version (none) version(unittest) //Please uncomment me when changing packageName to test global imports @@ -376,7 +374,7 @@ template moduleName(alias T) enum moduleName = packagePrefix ~ T.stringof[7..$]; } else - alias moduleName!(__traits(parent, T)) moduleName; + alias moduleName = moduleName!(parentOf!T); // If you use enum, it will cause compiler ICE } unittest @@ -393,6 +391,9 @@ unittest static assert(!__traits(compiles, moduleName!(core.sync))); static assert(moduleName!(core.sync.barrier) == "core.sync.barrier"); static assert(moduleName!Barrier == "core.sync.barrier"); + + struct X12287(T) { T i; } + static assert(moduleName!(X12287!int.i) == "std.traits"); } version (none) version(unittest) //Please uncomment me when changing moduleName to test global imports @@ -419,26 +420,25 @@ template fullyQualifiedName(T...) { static if (is(T)) - enum fullyQualifiedName = fullyQualifiedNameImplForTypes!(T[0], false, false, false, false); + enum fullyQualifiedName = fqnType!(T[0], false, false, false, false); else - enum fullyQualifiedName = fullyQualifiedNameImplForSymbols!(T[0]); + enum fullyQualifiedName = fqnSym!(T[0]); } version(unittest) { - // Used for both fullyQualifiedNameImplForTypes and fullyQualifiedNameImplForSymbols unittests + // Used for both fqnType and fqnSym unittests private struct QualifiedNameTests { struct Inner { - const int opCmp(ref const Inner) { return 0; } } ref const(Inner[string]) func( ref Inner var1, lazy scope string var2 ); inout Inner inoutFunc(inout Inner); shared(const(Inner[string])[]) data; const Inner delegate(double, string) @safe nothrow deleg; - inout int delegate(inout int) inout inoutDeleg; + inout(int) delegate(inout int) inout inoutDeleg; Inner function(out double, string) funcPtr; extern(C) Inner function(double, string) cFuncPtr; @@ -453,6 +453,13 @@ version(unittest) const(Inner[const(Inner)]) qualAarray; shared(immutable(Inner) delegate(ref double, scope string) const shared @trusted nothrow) attrDeleg; + + struct Data(T) { int x; } + void tfunc(T...)(T args) {} + + template Inst(alias A) { int x; } + + class Test12309(T, int x, string s) {} } private enum QualifiedEnum @@ -461,39 +468,73 @@ version(unittest) } } -private template fullyQualifiedNameImplForSymbols(alias T) +private template fqnSym(alias T : X!A, alias X, A...) +{ + template fqnTuple(T...) + { + static if (T.length == 0) + enum fqnTuple = ""; + else static if (T.length == 1) + { + static if (isExpressionTuple!T) + enum fqnTuple = T[0].stringof; + else + enum fqnTuple = fullyQualifiedName!(T[0]); + } + else + enum fqnTuple = fqnTuple!(T[0]) ~ ", " ~ fqnTuple!(T[1 .. $]); + } + + enum fqnSym = + fqnSym!(__traits(parent, X)) ~ + '.' ~ __traits(identifier, X) ~ "!(" ~ fqnTuple!A ~ ")"; +} + +private template fqnSym(alias T) { static if (__traits(compiles, __traits(parent, T))) - enum parentPrefix = fullyQualifiedNameImplForSymbols!(__traits(parent, T)) ~ '.'; + enum parentPrefix = fqnSym!(__traits(parent, T)) ~ '.'; else enum parentPrefix = null; - enum fullyQualifiedNameImplForSymbols = parentPrefix ~ (s) + static string adjustIdent(string s) { import std.algorithm : skipOver, findSplit; - if(s.skipOver("package ") || s.skipOver("module ")) + if (s.skipOver("package ") || s.skipOver("module ")) return s; return s.findSplit("(")[0]; - }(__traits(identifier, T)); + } + enum fqnSym = parentPrefix ~ adjustIdent(__traits(identifier, T)); } unittest { + alias fqn = fullyQualifiedName; + // Make sure those 2 are the same - static assert(fullyQualifiedNameImplForSymbols!fullyQualifiedName - == fullyQualifiedName!fullyQualifiedName); + static assert(fqnSym!fqn == fqn!fqn); - // Main tests - alias fqn = fullyQualifiedName; static assert(fqn!fqn == "std.traits.fullyQualifiedName"); - static assert(fqn!(QualifiedNameTests.Inner) == "std.traits.QualifiedNameTests.Inner"); - static assert(fqn!(QualifiedNameTests.func) == "std.traits.QualifiedNameTests.func"); + + alias qnTests = QualifiedNameTests; + enum prefix = "std.traits.QualifiedNameTests."; + static assert(fqn!(qnTests.Inner) == prefix ~ "Inner"); + static assert(fqn!(qnTests.func) == prefix ~ "func"); + static assert(fqn!(qnTests.Data!int) == prefix ~ "Data!(int)"); + static assert(fqn!(qnTests.Data!int.x) == prefix ~ "Data!(int).x"); + static assert(fqn!(qnTests.tfunc!(int[])) == prefix ~ "tfunc!(int[])"); + static assert(fqn!(qnTests.Inst!(Object)) == prefix ~ "Inst!(object.Object)"); + static assert(fqn!(qnTests.Inst!(Object).x) == prefix ~ "Inst!(object.Object).x"); + + static assert(fqn!(qnTests.Test12309!(int, 10, "str")) + == prefix ~ "Test12309!(int, 10, \"str\")"); + import core.sync.barrier; - static assert(fullyQualifiedName!Barrier == "core.sync.barrier.Barrier"); + static assert(fqn!Barrier == "core.sync.barrier.Barrier"); } -private template fullyQualifiedNameImplForTypes(T, +private template fqnType(T, bool alreadyConst, bool alreadyImmutable, bool alreadyShared, bool alreadyInout) { import std.string; @@ -506,12 +547,12 @@ private template fullyQualifiedNameImplForTypes(T, _inout = 3 } - alias TypeTuple!(is(T == const), is(T == immutable), is(T == shared), is(T == inout)) qualifiers; - alias TypeTuple!(false, false, false, false) noQualifiers; + alias qualifiers = TypeTuple!(is(T == const), is(T == immutable), is(T == shared), is(T == inout)); + alias noQualifiers = TypeTuple!(false, false, false, false); string storageClassesString(uint psc)() @property { - alias ParameterStorageClass PSC; + alias PSC = ParameterStorageClass; return format("%s%s%s%s", psc & PSC.scope_ ? "scope " : "", @@ -525,8 +566,8 @@ private template fullyQualifiedNameImplForTypes(T, { import std.array, std.algorithm, std.range; - alias ParameterTypeTuple!(T) parameters; - alias ParameterStorageClassTuple!(T) parameterStC; + alias parameters = ParameterTypeTuple!(T); + alias parameterStC = ParameterStorageClassTuple!(T); enum variadic = variadicFunctionStyle!T; static if (variadic == Variadic.no) @@ -568,7 +609,7 @@ private template fullyQualifiedNameImplForTypes(T, string functionAttributeString(T)() @property { - alias FunctionAttribute FA; + alias FA = FunctionAttribute; enum attrs = functionAttributes!T; static if (attrs == FA.none) @@ -615,42 +656,42 @@ private template fullyQualifiedNameImplForTypes(T, static if (is(T == string)) { - enum fullyQualifiedNameImplForTypes = "string"; + enum fqnType = "string"; } else static if (is(T == wstring)) { - enum fullyQualifiedNameImplForTypes = "wstring"; + enum fqnType = "wstring"; } else static if (is(T == dstring)) { - enum fullyQualifiedNameImplForTypes = "dstring"; + enum fqnType = "dstring"; } else static if (isBasicType!T && !is(T == enum)) { - enum fullyQualifiedNameImplForTypes = chain!((Unqual!T).stringof); + enum fqnType = chain!((Unqual!T).stringof); } else static if (isAggregateType!T || is(T == enum)) { - enum fullyQualifiedNameImplForTypes = chain!(fullyQualifiedNameImplForSymbols!T); + enum fqnType = chain!(fqnSym!T); } else static if (isStaticArray!T) { import std.conv; - enum fullyQualifiedNameImplForTypes = chain!( - format("%s[%s]", fullyQualifiedNameImplForTypes!(typeof(T.init[0]), qualifiers), T.length) + enum fqnType = chain!( + format("%s[%s]", fqnType!(typeof(T.init[0]), qualifiers), T.length) ); } else static if (isArray!T) { - enum fullyQualifiedNameImplForTypes = chain!( - format("%s[]", fullyQualifiedNameImplForTypes!(typeof(T.init[0]), qualifiers)) + enum fqnType = chain!( + format("%s[]", fqnType!(typeof(T.init[0]), qualifiers)) ); } else static if (isAssociativeArray!T) { - enum fullyQualifiedNameImplForTypes = chain!( - format("%s[%s]", fullyQualifiedNameImplForTypes!(ValueType!T, qualifiers), fullyQualifiedNameImplForTypes!(KeyType!T, noQualifiers)) + enum fqnType = chain!( + format("%s[%s]", fqnType!(ValueType!T, qualifiers), fqnType!(KeyType!T, noQualifiers)) ); } else static if (isSomeFunction!T) @@ -664,8 +705,8 @@ private template fullyQualifiedNameImplForTypes(T, is(F == const) ? " const" : "" ); enum formatStr = "%s%s delegate(%s)%s%s"; - enum fullyQualifiedNameImplForTypes = chain!( - format(formatStr, linkageString!T, fullyQualifiedNameImplForTypes!(ReturnType!T, noQualifiers), + enum fqnType = chain!( + format(formatStr, linkageString!T, fqnType!(ReturnType!T, noQualifiers), parametersTypeString!(T), functionAttributeString!T, qualifierString) ); } @@ -676,18 +717,24 @@ private template fullyQualifiedNameImplForTypes(T, else enum formatStr = "%s%s(%s)%s"; - enum fullyQualifiedNameImplForTypes = chain!( - format(formatStr, linkageString!T, fullyQualifiedNameImplForTypes!(ReturnType!T, noQualifiers), + enum fqnType = chain!( + format(formatStr, linkageString!T, fqnType!(ReturnType!T, noQualifiers), parametersTypeString!(T), functionAttributeString!T) ); } } else static if (isPointer!T) { - enum fullyQualifiedNameImplForTypes = chain!( - format("%s*", fullyQualifiedNameImplForTypes!(PointerTarget!T, qualifiers)) + enum fqnType = chain!( + format("%s*", fqnType!(PointerTarget!T, qualifiers)) ); } + else static if (is(T : __vector(V[N]), V, size_t N)) + { + enum fqnType = chain!( + format("__vector(%s[%s])", fqnType!(V, qualifiers), N) + ); + } else // In case something is forgotten static assert(0, "Unrecognized type " ~ T.stringof ~ ", can't convert to fully qualified string"); @@ -696,13 +743,13 @@ private template fullyQualifiedNameImplForTypes(T, unittest { import std.string : format; + alias fqn = fullyQualifiedName; // Verify those 2 are the same for simple case alias Ambiguous = const(QualifiedNameTests.Inner); - static assert(fullyQualifiedName!Ambiguous == fullyQualifiedNameImplForTypes!(Ambiguous, false, false, false, false)); + static assert(fqn!Ambiguous == fqnType!(Ambiguous, false, false, false, false)); // Main tests - alias fullyQualifiedName fqn; enum inner_name = "std.traits.QualifiedNameTests.Inner"; with (QualifiedNameTests) { @@ -731,12 +778,12 @@ unittest static assert(fqn!(typeof(func)) == format("const(%s[string])(ref %s, scope lazy string) ref", inner_name, inner_name)); static assert(fqn!(typeof(inoutFunc)) == format("inout(%s(inout(%s)))", inner_name, inner_name)); static assert(fqn!(typeof(deleg)) == format("const(%s delegate(double, string) nothrow @safe)", inner_name)); - static assert(fqn!(typeof(inoutDeleg)) == "inout(int delegate(inout(int)) inout)"); + static assert(fqn!(typeof(inoutDeleg)) == "inout(int) delegate(inout(int)) inout"); static assert(fqn!(typeof(funcPtr)) == format("%s function(out double, string)", inner_name)); static assert(fqn!(typeof(cFuncPtr)) == format("extern(C) %s function(double, string)", inner_name)); // Delegate type with qualified function type - static assert(fqn!(typeof(attrDeleg)) == format("shared(immutable(%s) " + static assert(fqn!(typeof(attrDeleg)) == format("shared(immutable(%s) "~ "delegate(ref double, scope string) nothrow @trusted shared const)", inner_name)); // Variable argument function types @@ -744,6 +791,12 @@ unittest static assert(fqn!(typeof(dVarArg)) == "void(...)"); static assert(fqn!(typeof(dVarArg2)) == "void(int, ...)"); static assert(fqn!(typeof(typesafeVarArg)) == "void(int[] ...)"); + + // SIMD vector + static if (is(__vector(float[4]))) + { + static assert(fqn!(__vector(float[4])) == "__vector(float[4])"); + } } } @@ -765,7 +818,7 @@ template ReturnType(func...) if (func.length == 1 && isCallable!func) { static if (is(FunctionTypeOf!func R == return)) - alias R ReturnType; + alias ReturnType = R; else static assert(0, "argument has no return type"); } @@ -777,14 +830,14 @@ unittest int opCall (int i) { return 1;} } - alias ReturnType!G ShouldBeInt; + alias ShouldBeInt = ReturnType!G; static assert(is(ShouldBeInt == int)); G g; static assert(is(ReturnType!g == int)); G* p; - alias ReturnType!p pg; + alias pg = ReturnType!p; static assert(is(pg == int)); class C @@ -801,10 +854,10 @@ unittest { int prop() @property { return 0; } } - alias ReturnType!(Test.prop) R_Test_prop; + alias R_Test_prop = ReturnType!(Test.prop); static assert(is(R_Test_prop == int)); - alias ReturnType!((int a) { return a; }) R_dglit; + alias R_dglit = ReturnType!((int a) { return a; }); static assert(is(R_dglit == int)); } @@ -825,7 +878,7 @@ template ParameterTypeTuple(func...) if (func.length == 1 && isCallable!func) { static if (is(FunctionTypeOf!func P == function)) - alias P ParameterTypeTuple; + alias ParameterTypeTuple = P; else static assert(0, "argument has no parameters"); } @@ -846,10 +899,10 @@ unittest { int prop() @property { return 0; } } - alias ParameterTypeTuple!(Test.prop) P_Test_prop; + alias P_Test_prop = ParameterTypeTuple!(Test.prop); static assert(P_Test_prop.length == 0); - alias ParameterTypeTuple!((int a){}) P_dglit; + alias P_dglit = ParameterTypeTuple!((int a){}); static assert(P_dglit.length == 1); static assert(is(P_dglit[0] == int)); } @@ -887,12 +940,12 @@ function $(D func). Example: -------------------- -alias ParameterStorageClass STC; // shorten the enum name +alias STC = ParameterStorageClass; // shorten the enum name void func(ref int ctx, out real result, real param) { } -alias ParameterStorageClassTuple!func pstc; +alias pstc = ParameterStorageClassTuple!func; static assert(pstc.length == 3); // three parameters static assert(pstc[0] == STC.ref_); static assert(pstc[1] == STC.out_); @@ -916,13 +969,13 @@ enum ParameterStorageClass : uint template ParameterStorageClassTuple(func...) if (func.length == 1 && isCallable!func) { - alias Unqual!(FunctionTypeOf!func) Func; + alias Func = Unqual!(FunctionTypeOf!func); /* * TypeFuncion: * CallConvention FuncAttrs Arguments ArgClose Type */ - alias ParameterTypeTuple!Func Params; + alias Params = ParameterTypeTuple!Func; // chop off CallConvention and FuncAttrs enum margs = demangleFunctionAttributes(mangledName!Func[1 .. $]).rest; @@ -936,29 +989,30 @@ template ParameterStorageClassTuple(func...) enum skip = mangledName!(Params[i]).length; // for bypassing Type enum rest = demang.rest; - alias TypeTuple!( + alias demangleNextParameter = + TypeTuple!( demang.value + 0, // workaround: "not evaluatable at ..." demangleNextParameter!(rest[skip .. $], i + 1) - ) demangleNextParameter; + ); } else // went thru all the parameters { - alias TypeTuple!() demangleNextParameter; + alias demangleNextParameter = TypeTuple!(); } } - alias demangleNextParameter!margs ParameterStorageClassTuple; + alias ParameterStorageClassTuple = demangleNextParameter!margs; } unittest { - alias ParameterStorageClass STC; + alias STC = ParameterStorageClass; void noparam() {} static assert(ParameterStorageClassTuple!noparam.length == 0); void test(scope int, ref int, out int, lazy int, int) { } - alias ParameterStorageClassTuple!test test_pstc; + alias test_pstc = ParameterStorageClassTuple!test; static assert(test_pstc.length == 5); static assert(test_pstc[0] == STC.scope_); static assert(test_pstc[1] == STC.ref_); @@ -973,15 +1027,15 @@ unittest } Test testi; - alias ParameterStorageClassTuple!(Test.test_const) test_const_pstc; + alias test_const_pstc = ParameterStorageClassTuple!(Test.test_const); static assert(test_const_pstc.length == 1); static assert(test_const_pstc[0] == STC.none); - alias ParameterStorageClassTuple!(testi.test_sharedconst) test_sharedconst_pstc; + alias test_sharedconst_pstc = ParameterStorageClassTuple!(testi.test_sharedconst); static assert(test_sharedconst_pstc.length == 1); static assert(test_sharedconst_pstc[0] == STC.none); - alias ParameterStorageClassTuple!((ref int a) {}) dglit_pstc; + alias dglit_pstc = ParameterStorageClassTuple!((ref int a) {}); static assert(dglit_pstc.length == 1); static assert(dglit_pstc[0] == STC.ref_); @@ -1017,18 +1071,18 @@ template ParameterIdentifierTuple(func...) // Define dummy entities to avoid pointless errors template Get(size_t i) { enum Get = ""; } - alias TypeTuple!() PT; + alias PT = TypeTuple!(); } template Impl(size_t i = 0) { static if (i == PT.length) - alias TypeTuple!() Impl; + alias Impl = TypeTuple!(); else - alias TypeTuple!(Get!i, Impl!(i+1)) Impl; + alias Impl = TypeTuple!(Get!i, Impl!(i+1)); } - alias Impl!() ParameterIdentifierTuple; + alias ParameterIdentifierTuple = Impl!(); } /// @@ -1038,9 +1092,10 @@ unittest int foo(int num, string name); static assert([ParameterIdentifierTuple!foo] == ["num", "name"]); } + unittest { - alias ParameterIdentifierTuple PIT; + alias PIT = ParameterIdentifierTuple; void bar(int num, string name, int[] array){} static assert([PIT!bar] == ["num", "name", "array"]); @@ -1090,7 +1145,7 @@ template ParameterDefaultValueTuple(func...) static if (is(typeof(get()))) enum Get = get(); else - alias void Get; + alias Get = void; // If default arg doesn't exist, returns void instead. } } @@ -1107,18 +1162,18 @@ template ParameterDefaultValueTuple(func...) // Define dummy entities to avoid pointless errors template Get(size_t i) { enum Get = ""; } - alias TypeTuple!() PT; + alias PT = TypeTuple!(); } template Impl(size_t i = 0) { static if (i == PT.length) - alias TypeTuple!() Impl; + alias Impl = TypeTuple!(); else - alias TypeTuple!(Get!i, Impl!(i+1)) Impl; + alias Impl = TypeTuple!(Get!i, Impl!(i+1)); } - alias Impl!() ParameterDefaultValueTuple; + alias ParameterDefaultValueTuple = Impl!(); } /// @@ -1130,9 +1185,10 @@ unittest static assert( ParameterDefaultValueTuple!foo[1] == "hello"); static assert( ParameterDefaultValueTuple!foo[2] == [1,2,3]); } + unittest { - alias ParameterDefaultValueTuple PDVT; + alias PDVT = ParameterDefaultValueTuple; void bar(int n = 1, string s = "hello"){} static assert(PDVT!bar.length == 2); @@ -1170,9 +1226,9 @@ Returns the attributes attached to a function $(D func). Example: -------------------- -alias FunctionAttribute FA; // shorten the enum name +alias FA = FunctionAttribute; // shorten the enum name -real func(real x) pure nothrow @safe +real func(real x) @safe pure nothrow { return x; } @@ -1193,13 +1249,14 @@ enum FunctionAttribute : uint property = 0b00001000, /// ditto trusted = 0b00010000, /// ditto safe = 0b00100000, /// ditto + nogc = 0b01000000, /// ditto } /// ditto template functionAttributes(func...) if (func.length == 1 && isCallable!func) { - alias Unqual!(FunctionTypeOf!func) Func; + alias Func = Unqual!(FunctionTypeOf!func); enum uint functionAttributes = demangleFunctionAttributes(mangledName!Func[1 .. $]).value; @@ -1207,7 +1264,7 @@ template functionAttributes(func...) unittest { - alias FunctionAttribute FA; + alias FA = FunctionAttribute; interface Set { int pureF() pure; @@ -1242,16 +1299,16 @@ unittest static assert(functionAttributes!(Test2.pure_const) == FA.pure_); static assert(functionAttributes!(Test2.pure_sharedconst) == FA.pure_); - static assert(functionAttributes!((int a) {}) == (FA.safe | FA.pure_ | FA.nothrow_)); + static assert(functionAttributes!((int a) {}) == (FA.safe | FA.pure_ | FA.nothrow_ | FA.nogc)); auto safeDel = delegate() @safe {}; - static assert(functionAttributes!safeDel == (FA.safe | FA.pure_ | FA.nothrow_)); + static assert(functionAttributes!safeDel == (FA.safe | FA.pure_ | FA.nothrow_ | FA.nogc)); auto trustedDel = delegate() @trusted {}; - static assert(functionAttributes!trustedDel == (FA.trusted | FA.pure_ | FA.nothrow_)); + static assert(functionAttributes!trustedDel == (FA.trusted | FA.pure_ | FA.nothrow_ | FA.nogc)); auto systemDel = delegate() @system {}; - static assert(functionAttributes!systemDel == (FA.pure_ | FA.nothrow_)); + static assert(functionAttributes!systemDel == (FA.pure_ | FA.nothrow_ | FA.nogc)); } @@ -1433,10 +1490,10 @@ unittest /** -$(RED Scheduled for deprecation in January 2013. It's badly named and provides -redundant functionality. It was also badly broken prior to 2.060 (bug# 8362), so -any code which uses it probably needs to be changed anyway. Please use -$(D allSatisfy(isSafe, ...)) instead.) +$(RED Deprecated. It's badly named and provides redundant functionality. It was +also badly broken prior to 2.060 (bug# 8362), so any code which uses it +probably needs to be changed anyway. Please use $(D allSatisfy(isSafe, ...)) +instead. This will be removed in June 2015.) $(D true) all functions are $(D isSafe). @@ -1451,6 +1508,7 @@ static assert( areAllSafe!(add, sub)); static assert(!areAllSafe!(sub, mul)); -------------------- */ +deprecated("Please use allSatisfy(isSafe, ...) instead.") template areAllSafe(funcs...) if (funcs.length > 0) { @@ -1469,7 +1527,7 @@ template areAllSafe(funcs...) } //Verify Example -unittest +deprecated unittest { @safe int add(int a, int b) {return a+b;} @trusted int sub(int a, int b) {return a-b;} @@ -1480,7 +1538,7 @@ unittest static assert(!areAllSafe!(sub, mul)); } -unittest +deprecated unittest { interface Set { @@ -1510,7 +1568,7 @@ assert(b == "C"); // extern(C) template functionLinkage(func...) if (func.length == 1 && isCallable!func) { - alias Unqual!(FunctionTypeOf!func) Func; + alias Func = Unqual!(FunctionTypeOf!func); enum string functionLinkage = [ @@ -1566,7 +1624,7 @@ enum Variadic template variadicFunctionStyle(func...) if (func.length == 1 && isCallable!func) { - alias Unqual!(FunctionTypeOf!func) Func; + alias Func = Unqual!(FunctionTypeOf!func); // TypeFuncion --> CallConvention FuncAttrs Arguments ArgClose Type enum callconv = functionLinkage!Func; @@ -1625,27 +1683,29 @@ template FunctionTypeOf(func...) { static if (is(typeof(& func[0]) Fsym : Fsym*) && is(Fsym == function) || is(typeof(& func[0]) Fsym == delegate)) { - alias Fsym FunctionTypeOf; // HIT: (nested) function symbol + alias FunctionTypeOf = Fsym; // HIT: (nested) function symbol } else static if (is(typeof(& func[0].opCall) Fobj == delegate)) { - alias Fobj FunctionTypeOf; // HIT: callable object + alias FunctionTypeOf = Fobj; // HIT: callable object } else static if (is(typeof(& func[0].opCall) Ftyp : Ftyp*) && is(Ftyp == function)) { - alias Ftyp FunctionTypeOf; // HIT: callable type + alias FunctionTypeOf = Ftyp; // HIT: callable type } else static if (is(func[0] T) || is(typeof(func[0]) T)) { static if (is(T == function)) - alias T FunctionTypeOf; // HIT: function + alias FunctionTypeOf = T; // HIT: function else static if (is(T Fptr : Fptr*) && is(Fptr == function)) - alias Fptr FunctionTypeOf; // HIT: function pointer + alias FunctionTypeOf = Fptr; // HIT: function pointer else static if (is(T Fdlg == delegate)) - alias Fdlg FunctionTypeOf; // HIT: delegate - else static assert(0); + alias FunctionTypeOf = Fdlg; // HIT: delegate + else + static assert(0); } - else static assert(0); + else + static assert(0); } unittest @@ -1686,17 +1746,17 @@ unittest int test(); int test() @property; } - alias TypeTuple!(__traits(getVirtualFunctions, Overloads, "test")) ov; - alias FunctionTypeOf!(ov[0]) F_ov0; - alias FunctionTypeOf!(ov[1]) F_ov1; - alias FunctionTypeOf!(ov[2]) F_ov2; - alias FunctionTypeOf!(ov[3]) F_ov3; + alias ov = TypeTuple!(__traits(getVirtualFunctions, Overloads, "test")); + alias F_ov0 = FunctionTypeOf!(ov[0]); + alias F_ov1 = FunctionTypeOf!(ov[1]); + alias F_ov2 = FunctionTypeOf!(ov[2]); + alias F_ov3 = FunctionTypeOf!(ov[3]); static assert(is(F_ov0* == void function(string))); static assert(is(F_ov1* == real function(real))); static assert(is(F_ov2* == int function())); static assert(is(F_ov3* == int function() @property)); - alias FunctionTypeOf!((int a){ return a; }) F_dglit; + alias F_dglit = FunctionTypeOf!((int a){ return a; }); static assert(is(F_dglit* : int function(int))); } @@ -1714,11 +1774,7 @@ unittest * * Examples: * --- - * template ExternC(T) - * if (isFunctionPointer!T || isDelegate!T || is(T == function)) - * { - * alias SetFunctionAttributes!(T, "C", functionAttributes!T) ExternC; - * } + * alias ExternC(T) = SetFunctionAttributes!(T, "C", functionAttributes!T); * --- * * --- @@ -1778,6 +1834,8 @@ template SetFunctionAttributes(T, string linkage, uint attrs) result ~= " pure"; static if (attrs & FunctionAttribute.nothrow_) result ~= " nothrow"; + static if (attrs & FunctionAttribute.nogc) + result ~= " @nogc"; static if (attrs & FunctionAttribute.property) result ~= " @property"; static if (attrs & FunctionAttribute.trusted) @@ -1813,7 +1871,7 @@ unittest { import std.algorithm : reduce; - alias FunctionAttribute FA; + alias FA = FunctionAttribute; foreach (BaseT; TypeTuple!(typeof(&sc), typeof(&novar), typeof(&cstyle), typeof(&dstyle), typeof(&typesafe))) { @@ -1830,7 +1888,7 @@ unittest { foreach (newLinkage; TypeTuple!("D", "C", "Windows", "Pascal", "C++")) { - alias SetFunctionAttributes!(T, newLinkage, attrs) New; + alias New = SetFunctionAttributes!(T, newLinkage, attrs); static assert(functionLinkage!New == newLinkage, "Linkage test failed for: " ~ T.stringof ~ ", " ~ newLinkage ~ " (got " ~ New.stringof ~ ")"); @@ -1838,16 +1896,16 @@ unittest } // Add @safe. - alias SetFunctionAttributes!(T, functionLinkage!T, FA.safe) T1; + alias T1 = SetFunctionAttributes!(T, functionLinkage!T, FA.safe); static assert(functionAttributes!T1 == FA.safe); // Add all known attributes, excluding conflicting ones. enum allAttrs = reduce!"a | b"([EnumMembers!FA]) & ~FA.safe & ~FA.property; - alias SetFunctionAttributes!(T1, functionLinkage!T, allAttrs) T2; + alias T2 = SetFunctionAttributes!(T1, functionLinkage!T, allAttrs); static assert(functionAttributes!T2 == allAttrs); // Strip all attributes again. - alias SetFunctionAttributes!(T2, functionLinkage!T, FA.none) T3; + alias T3 = SetFunctionAttributes!(T2, functionLinkage!T, FA.none); static assert(is(T3 == T)); } } @@ -1868,6 +1926,17 @@ template isNested(T) enum isNested = __traits(isNested, T); } +/// +unittest +{ + static struct S { } + static assert(!isNested!S); + + int i; + struct NestedStruct { void f() { ++i; } } + static assert(isNested!NestedStruct); +} + /** Determines whether $(D T) or any of its representation types have a context pointer. @@ -1883,6 +1952,18 @@ template hasNested(T) enum hasNested = false; } +/// +unittest +{ + static struct S { } + + int i; + struct NS { void f() { ++i; } } + + static assert(!hasNested!(S[2])); + static assert(hasNested!(NS[2])); +} + unittest { static assert(!__traits(compiles, isNested!int)); @@ -1943,15 +2024,21 @@ unittest * If $(D T) isn't a struct, class, or union returns typetuple * with one element $(D T). */ - template FieldTypeTuple(T) { static if (is(T == struct) || is(T == union)) - alias typeof(T.tupleof[0 .. $ - isNested!T]) FieldTypeTuple; + alias FieldTypeTuple = typeof(T.tupleof[0 .. $ - isNested!T]); else static if (is(T == class)) - alias typeof(T.tupleof) FieldTypeTuple; + alias FieldTypeTuple = typeof(T.tupleof); else - alias TypeTuple!T FieldTypeTuple; + alias FieldTypeTuple = TypeTuple!T; +} + +/// +unittest +{ + struct S { int x; float y; } + static assert(is(FieldTypeTuple!S == TypeTuple!(int, float))); } unittest @@ -2022,11 +2109,11 @@ unittest // unittest // { -// alias FieldOffsetsTuple!int T1; +// alias T1 = FieldOffsetsTuple!int; // assert(T1.length == 1 && T1[0] == 0); // // // struct S2 { char a; int b; char c; double d; char e, f; } -// alias FieldOffsetsTuple!S2 T2; +// alias T2 = FieldOffsetsTuple!S2; // //pragma(msg, T2); // static assert(T2.length == 6 // && T2[0] == 0 && T2[1] == 4 && T2[2] == 8 && T2[3] == 16 @@ -2034,13 +2121,13 @@ unittest // // // class C { int a, b, c, d; } // struct S3 { char a; C b; char c; } -// alias FieldOffsetsTuple!S3 T3; +// alias T3 = FieldOffsetsTuple!S3; // //pragma(msg, T2); // static assert(T3.length == 3 // && T3[0] == 0 && T3[1] == 4 && T3[2] == 8); // // // struct S4 { char a; union { int b; char c; } int d; } -// alias FieldOffsetsTuple!S4 T4; +// alias T4 = FieldOffsetsTuple!S4; // //pragma(msg, FieldTypeTuple!S4); // static assert(T4.length == 4 // && T4[0] == 0 && T4[1] == 4 && T4[2] == 8); @@ -2066,20 +2153,19 @@ Example: ---- struct S1 { int a; float b; } struct S2 { char[] a; union { S1 b; S1 * c; } } -alias RepresentationTypeTuple!S2 R; +alias R = RepresentationTypeTuple!S2; assert(R.length == 4 && is(R[0] == char[]) && is(R[1] == int) && is(R[2] == float) && is(R[3] == S1*)); ---- */ - template RepresentationTypeTuple(T) { template Impl(T...) { static if (T.length == 0) { - alias TypeTuple!() Impl; + alias Impl = TypeTuple!(); } else { @@ -2087,43 +2173,35 @@ template RepresentationTypeTuple(T) static if (is(T[0] R: Rebindable!R)) { - alias Impl!(Impl!R, T[1 .. $]) Impl; + alias Impl = Impl!(Impl!R, T[1 .. $]); } else static if (is(T[0] == struct) || is(T[0] == union)) { // @@@BUG@@@ this should work //alias .RepresentationTypes!(T[0].tupleof) // RepresentationTypes; - alias Impl!(FieldTypeTuple!(T[0]), T[1 .. $]) Impl; - } - else static if (is(T[0] U == typedef)) - { - alias Impl!(FieldTypeTuple!U, T[1 .. $]) Impl; + alias Impl = Impl!(FieldTypeTuple!(T[0]), T[1 .. $]); } else { - alias TypeTuple!(T[0], Impl!(T[1 .. $])) Impl; + alias Impl = TypeTuple!(T[0], Impl!(T[1 .. $])); } } } static if (is(T == struct) || is(T == union) || is(T == class)) { - alias Impl!(FieldTypeTuple!T) RepresentationTypeTuple; - } - else static if (is(T U == typedef)) - { - alias RepresentationTypeTuple!U RepresentationTypeTuple; + alias RepresentationTypeTuple = Impl!(FieldTypeTuple!T); } else { - alias Impl!T RepresentationTypeTuple; + alias RepresentationTypeTuple = Impl!T; } } unittest { - alias RepresentationTypeTuple!int S1; + alias S1 = RepresentationTypeTuple!int; static assert(is(S1 == TypeTuple!int)); struct S2 { int a; } @@ -2135,20 +2213,20 @@ unittest struct S11 { int a; float b; } struct S21 { char[] a; union { S11 b; S11 * c; } } - alias RepresentationTypeTuple!S21 R; + alias R = RepresentationTypeTuple!S21; assert(R.length == 4 && is(R[0] == char[]) && is(R[1] == int) && is(R[2] == float) && is(R[3] == S11*)); class C { int a; float b; } - alias RepresentationTypeTuple!C R1; + alias R1 = RepresentationTypeTuple!C; static assert(R1.length == 2 && is(R1[0] == int) && is(R1[1] == float)); /* Issue 6642 */ import std.typecons : Rebindable; struct S5 { int a; Rebindable!(immutable Object) b; } - alias RepresentationTypeTuple!S5 R2; + alias R2 = RepresentationTypeTuple!S5; static assert(R2.length == 2 && is(R2[0] == int) && is(R2[1] == immutable(Object))); } @@ -2305,9 +2383,6 @@ unittest static assert(!hasRawAliasing!S12); static assert( hasRawAliasing!S13); - //typedef int* S8; - //static assert(hasRawAliasing!S8); - enum S9 { a } static assert(!hasRawAliasing!S9); @@ -2444,11 +2519,6 @@ unittest static assert(!hasRawUnsharedAliasing!(shared(const(void delegate() shared const)))); static assert(!hasRawUnsharedAliasing!(void function())); - //typedef int* S11; - //typedef shared int* S12; - //static assert( hasRawUnsharedAliasing!S11); - //static assert( hasRawUnsharedAliasing!S12); - enum S13 { a } static assert(!hasRawUnsharedAliasing!S13); @@ -2534,10 +2604,6 @@ private template hasObjects(T...) { enum hasObjects = false; } - else static if (is(T[0] U == typedef)) - { - enum hasObjects = hasObjects!(U, T[1 .. $]); - } else static if (is(T[0] == struct)) { enum hasObjects = hasObjects!( @@ -2561,10 +2627,6 @@ private template hasUnsharedObjects(T...) { enum hasUnsharedObjects = false; } - else static if (is(T[0] U == typedef)) - { - enum hasUnsharedObjects = hasUnsharedObjects!(U, T[1 .. $]); - } else static if (is(T[0] == struct)) { enum hasUnsharedObjects = hasUnsharedObjects!( @@ -2686,37 +2748,17 @@ least one of the following: $(OL $(LI a raw pointer $(D U*);) $(LI an array $(D U[]);) $(LI a reference to a class type $(D C).) $(LI an associative array.) $(LI a delegate.)) */ - template hasIndirections(T) { - template Impl(T...) - { - static if (!T.length) - { - enum Impl = false; - } - else static if(isFunctionPointer!(T[0])) - { - enum Impl = Impl!(T[1 .. $]); - } - else static if(isStaticArray!(T[0])) - { - static if (is(T[0] _ : void[N], size_t N)) - enum Impl = true; - else - enum Impl = Impl!(T[1 .. $]) || - Impl!(RepresentationTypeTuple!(typeof(T[0].init[0]))); - } - else - { - enum Impl = isPointer!(T[0]) || isDynamicArray!(T[0]) || - is (T[0] : const(Object)) || isAssociativeArray!(T[0]) || - isDelegate!(T[0]) || is(T[0] == interface) - || Impl!(T[1 .. $]); - } - } - - enum hasIndirections = Impl!(T, RepresentationTypeTuple!T); + static if (is(T == struct) || is(T == union)) + enum hasIndirections = anySatisfy!(.hasIndirections, FieldTypeTuple!T); + else static if (isStaticArray!T && is(T : E[N], E, size_t N)) + enum hasIndirections = is(E == void) ? true : hasIndirections!E; + else static if (isFunctionPointer!T) + enum hasIndirections = false; + else + enum hasIndirections = isPointer!T || isDelegate!T || isDynamicArray!T || + isAssociativeArray!T || is (T == class) || is(T == interface); } unittest @@ -2788,6 +2830,21 @@ unittest static assert( hasIndirections!S26); } +unittest //12000 +{ + static struct S(T) + { + static assert(hasIndirections!T); + } + + static class A(T) + { + S!A a; + } + + A!int dummy; +} + //Explicitly undocumented. They will be removed in December 2014. deprecated("Please use hasLocalAliasing instead.") alias hasLocalAliasing = hasUnsharedAliasing; deprecated("Please use hasRawLocalAliasing instead.") alias hasRawLocalAliasing = hasRawUnsharedAliasing; @@ -3012,13 +3069,15 @@ unittest True if $(D S) or any type directly embedded in the representation of $(D S) defines an elaborate assignment. Elaborate assignments are introduced by defining $(D opAssign(typeof(this))) or $(D opAssign(ref typeof(this))) - for a $(D struct) or when there is a compiler-generated $(D opAssign) - (in case $(D S) has an elaborate copy constructor or destructor). + for a $(D struct) or when there is a compiler-generated $(D opAssign). + + A type $(D S) gets compiler-generated $(D opAssign) in case it has + an elaborate copy constructor or elaborate destructor. Classes and unions never have elaborate assignments. Note: Structs with (possibly nested) postblit operator(s) will have a - hidden yet elaborate compiler generated assignement operator (unless + hidden yet elaborate compiler generated assignment operator (unless explicitly disabled). */ template hasElaborateAssign(S) @@ -3142,7 +3201,7 @@ unittest static assert( hasElaborateDestructor!S7); } -template Identity(alias A) { alias A Identity; } +alias Identity(alias A) = A; /** Yields $(D true) if and only if $(D T) is an aggregate that defines @@ -3154,7 +3213,7 @@ template hasMember(T, string name) { enum bool hasMember = staticIndexOf!(name, __traits(allMembers, T)) != -1 || - __traits(compiles, { mixin("alias Identity!(T."~name~") Sym;"); }); + __traits(compiles, { mixin("alias Sym = Identity!(T."~name~");"); }); } else { @@ -3290,7 +3349,7 @@ template EnumMembers(E) { mixin("template Symbolize(alias "~ ident ~")" ~"{" - ~"alias "~ ident ~" Symbolize;" + ~"alias Symbolize = "~ ident ~";" ~"}"); } } @@ -3299,19 +3358,20 @@ template EnumMembers(E) { static if (names.length > 0) { - alias TypeTuple!( + alias EnumSpecificMembers = + TypeTuple!( WithIdentifier!(names[0]) .Symbolize!(__traits(getMember, E, names[0])), EnumSpecificMembers!(names[1 .. $]) - ) EnumSpecificMembers; + ); } else { - alias TypeTuple!() EnumSpecificMembers; + alias EnumSpecificMembers = TypeTuple!(); } } - alias EnumSpecificMembers!(__traits(allMembers, E)) EnumMembers; + alias EnumMembers = EnumSpecificMembers!(__traits(allMembers, E)); } unittest @@ -3373,7 +3433,7 @@ unittest * * void main() * { - * alias BaseTypeTuple!B TL; + * alias TL = BaseTypeTuple!B; * writeln(typeid(TL)); // prints: (A,I) * } * --- @@ -3382,7 +3442,7 @@ unittest template BaseTypeTuple(A) { static if (is(A P == super)) - alias P BaseTypeTuple; + alias BaseTypeTuple = P; else static assert(0, "argument is not a class or interface"); } @@ -3406,7 +3466,7 @@ unittest class A { } class C : A, I1, I2 { } - alias BaseTypeTuple!C TL; + alias TL = BaseTypeTuple!C; assert(TL.length == 3); assert(is (TL[0] == A)); assert(is (TL[1] == I1)); @@ -3430,7 +3490,7 @@ unittest * * void main() * { - * alias BaseClassesTuple!C TL; + * alias TL = BaseClassesTuple!C; * writeln(typeid(TL)); // prints: (B,A,Object) * } * --- @@ -3441,17 +3501,17 @@ template BaseClassesTuple(T) { static if (is(T == Object)) { - alias TypeTuple!() BaseClassesTuple; + alias BaseClassesTuple = TypeTuple!(); } else static if (is(BaseTypeTuple!T[0] == Object)) { - alias TypeTuple!Object BaseClassesTuple; + alias BaseClassesTuple = TypeTuple!Object; } else { - alias TypeTuple!(BaseTypeTuple!T[0], - BaseClassesTuple!(BaseTypeTuple!T[0])) - BaseClassesTuple; + alias BaseClassesTuple = + TypeTuple!(BaseTypeTuple!T[0], + BaseClassesTuple!(BaseTypeTuple!T[0])); } } @@ -3491,7 +3551,7 @@ unittest * * void main() * { - * alias InterfacesTuple!C TL; + * alias TL = InterfacesTuple!C; * writeln(typeid(TL)); // prints: (I1, I2) * } * --- @@ -3503,21 +3563,21 @@ template InterfacesTuple(T) { static if (T.length) { - alias TypeTuple!(Flatten!H, Flatten!T) Flatten; + alias Flatten = TypeTuple!(Flatten!H, Flatten!T); } else { static if (is(H == interface)) - alias TypeTuple!(H, InterfacesTuple!H) Flatten; + alias Flatten = TypeTuple!(H, InterfacesTuple!H); else - alias InterfacesTuple!H Flatten; + alias Flatten = InterfacesTuple!H; } } static if (is(T S == super) && S.length) - alias NoDuplicates!(Flatten!S) InterfacesTuple; + alias InterfacesTuple = NoDuplicates!(Flatten!S); else - alias TypeTuple!() InterfacesTuple; + alias InterfacesTuple = TypeTuple!(); } unittest @@ -3529,7 +3589,7 @@ unittest class A : I1, I2 { } class B : A, I1 { } class C : B { } - alias InterfacesTuple!C TL; + alias TL = InterfacesTuple!C; static assert(is(TL[0] == I1) && is(TL[1] == I2)); } { @@ -3566,7 +3626,7 @@ unittest * * void main() * { - * alias TransitiveBaseTypeTuple!C TL; + * alias TL = TransitiveBaseTypeTuple!C; * writeln(typeid(TL)); // prints: (B,A,Object,I) * } * --- @@ -3575,10 +3635,10 @@ unittest template TransitiveBaseTypeTuple(T) { static if (is(T == Object)) - alias TypeTuple!() TransitiveBaseTypeTuple; + alias TransitiveBaseTypeTuple = TypeTuple!(); else - alias TypeTuple!(BaseClassesTuple!T, InterfacesTuple!T) - TransitiveBaseTypeTuple; + alias TransitiveBaseTypeTuple = + TypeTuple!(BaseClassesTuple!T, InterfacesTuple!T); } unittest @@ -3588,7 +3648,7 @@ unittest class B1 {} class B2 : B1, J1, J2 {} class B3 : B2, J1 {} - alias TransitiveBaseTypeTuple!B3 TL; + alias TL = TransitiveBaseTypeTuple!B3; assert(TL.length == 5); assert(is (TL[0] == B2)); assert(is (TL[1] == B1)); @@ -3727,15 +3787,15 @@ unittest { override C test() { return this; } } - alias MemberFunctionsTuple!(C, "test") test; + alias test =MemberFunctionsTuple!(C, "test"); static assert(test.length == 2); static assert(is(FunctionTypeOf!(test[0]) == FunctionTypeOf!(C.test))); static assert(is(FunctionTypeOf!(test[1]) == FunctionTypeOf!(K.test))); - alias MemberFunctionsTuple!(C, "noexist") noexist; + alias noexist = MemberFunctionsTuple!(C, "noexist"); static assert(noexist.length == 0); interface L { int prop() @property; } - alias MemberFunctionsTuple!(L, "prop") prop; + alias prop = MemberFunctionsTuple!(L, "prop"); static assert(prop.length == 1); interface Test_I @@ -3745,7 +3805,7 @@ unittest void foo(int, int); } interface Test : Test_I {} - alias MemberFunctionsTuple!(Test, "foo") Test_foo; + alias Test_foo = MemberFunctionsTuple!(Test, "foo"); static assert(Test_foo.length == 3); static assert(is(typeof(&Test_foo[0]) == void function())); static assert(is(typeof(&Test_foo[2]) == void function(int))); @@ -3753,6 +3813,94 @@ unittest } +/** +Returns an alias to the template that $(D T) is an instance of. + +Example: +-------------------- +struct Foo(T, U) {} +static assert(__traits(isSame, TemplateOf!(Foo!(int, real)), Foo)); +-------------------- + */ +template TemplateOf(alias T : Base!Args, alias Base, Args...) +{ + alias TemplateOf = Base; +} + +template TemplateOf(T : Base!Args, alias Base, Args...) +{ + alias TemplateOf = Base; +} + +unittest +{ + template Foo1(A) {} + template Foo2(A, B) {} + template Foo3(alias A) {} + template Foo4(string A) {} + struct Foo5(A) {} + struct Foo6(A, B) {} + struct Foo7(alias A) {} + template Foo8(A) { template Foo9(B) {} } + template Foo10() {} + + static assert(__traits(isSame, TemplateOf!(Foo1!(int)), Foo1)); + static assert(__traits(isSame, TemplateOf!(Foo2!(int, int)), Foo2)); + static assert(__traits(isSame, TemplateOf!(Foo3!(123)), Foo3)); + static assert(__traits(isSame, TemplateOf!(Foo4!("123")), Foo4)); + static assert(__traits(isSame, TemplateOf!(Foo5!(int)), Foo5)); + static assert(__traits(isSame, TemplateOf!(Foo6!(int, int)), Foo6)); + static assert(__traits(isSame, TemplateOf!(Foo7!(123)), Foo7)); + static assert(__traits(isSame, TemplateOf!(Foo8!(int).Foo9!(real)), Foo8!(int).Foo9)); + static assert(__traits(isSame, TemplateOf!(Foo10!()), Foo10)); +} + + +/** +Returns a $(D TypeTuple) of the template arguments used to instantiate $(D T). + +Example: +-------------------- +struct Foo(T, U) {} +static assert(is(TemplateArgsOf!(Foo!(int, real)) == TypeTuple!(int, real))); +-------------------- + */ +template TemplateArgsOf(alias T : Base!Args, alias Base, Args...) +{ + alias TemplateArgsOf = Args; +} + +template TemplateArgsOf(T : Base!Args, alias Base, Args...) +{ + alias TemplateArgsOf = Args; +} + +unittest +{ + template Foo1(A) {} + template Foo2(A, B) {} + template Foo3(alias A) {} + template Foo4(string A) {} + struct Foo5(A) {} + struct Foo6(A, B) {} + struct Foo7(alias A) {} + template Foo8(A) { template Foo9(B) {} } + template Foo10() {} + + enum x = 123; + enum y = "123"; + static assert(is(TemplateArgsOf!(Foo1!(int)) == TypeTuple!(int))); + static assert(is(TemplateArgsOf!(Foo2!(int, int)) == TypeTuple!(int, int))); + static assert(__traits(isSame, TemplateArgsOf!(Foo3!(x)), TypeTuple!(x))); + static assert(TemplateArgsOf!(Foo4!(y)) == TypeTuple!(y)); + static assert(is(TemplateArgsOf!(Foo5!(int)) == TypeTuple!(int))); + static assert(is(TemplateArgsOf!(Foo6!(int, int)) == TypeTuple!(int, int))); + static assert(__traits(isSame, TemplateArgsOf!(Foo7!(x)), TypeTuple!(x))); + static assert(is(TemplateArgsOf!(Foo8!(int).Foo9!(real)) == TypeTuple!(real))); + static assert(is(TemplateArgsOf!(Foo10!()) == TypeTuple!())); +} + + private template maxAlignment(U...) if (isTypeTuple!U) { static if (U.length == 0) @@ -3772,8 +3920,9 @@ Returns class instance alignment. */ template classInstanceAlignment(T) if(is(T == class)) { - alias maxAlignment!(void*, typeof(T.tupleof)) classInstanceAlignment; + alias classInstanceAlignment = maxAlignment!(void*, typeof(T.tupleof)); } + /// unittest { @@ -3800,26 +3949,27 @@ template CommonType(T...) { static if (!T.length) { - alias void CommonType; + alias CommonType = void; } else static if (T.length == 1) { static if(is(typeof(T[0]))) { - alias typeof(T[0]) CommonType; + alias CommonType = typeof(T[0]); } else { - alias T[0] CommonType; + alias CommonType = T[0]; } } else static if (is(typeof(true ? T[0].init : T[1].init) U)) { - alias CommonType!(U, T[2 .. $]) CommonType; + alias CommonType = CommonType!(U, T[2 .. $]); } else - alias void CommonType; + alias CommonType = void; } + /// unittest { @@ -3851,65 +4001,59 @@ unittest template ImplicitConversionTargets(T) { static if (is(T == bool)) - alias TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, - float, double, real, char, wchar, dchar) - ImplicitConversionTargets; + alias ImplicitConversionTargets = + TypeTuple!(byte, ubyte, short, ushort, int, uint, long, ulong, + float, double, real, char, wchar, dchar); else static if (is(T == byte)) - alias TypeTuple!(short, ushort, int, uint, long, ulong, - float, double, real, char, wchar, dchar) - ImplicitConversionTargets; + alias ImplicitConversionTargets = + TypeTuple!(short, ushort, int, uint, long, ulong, + float, double, real, char, wchar, dchar); else static if (is(T == ubyte)) - alias TypeTuple!(short, ushort, int, uint, long, ulong, - float, double, real, char, wchar, dchar) - ImplicitConversionTargets; + alias ImplicitConversionTargets = + TypeTuple!(short, ushort, int, uint, long, ulong, + float, double, real, char, wchar, dchar); else static if (is(T == short)) - alias TypeTuple!(ushort, int, uint, long, ulong, - float, double, real) - ImplicitConversionTargets; + alias ImplicitConversionTargets = + TypeTuple!(int, uint, long, ulong, float, double, real); else static if (is(T == ushort)) - alias TypeTuple!(int, uint, long, ulong, float, double, real) - ImplicitConversionTargets; + alias ImplicitConversionTargets = + TypeTuple!(int, uint, long, ulong, float, double, real); else static if (is(T == int)) - alias TypeTuple!(long, ulong, float, double, real) - ImplicitConversionTargets; + alias ImplicitConversionTargets = + TypeTuple!(long, ulong, float, double, real); else static if (is(T == uint)) - alias TypeTuple!(long, ulong, float, double, real) - ImplicitConversionTargets; + alias ImplicitConversionTargets = + TypeTuple!(long, ulong, float, double, real); else static if (is(T == long)) - alias TypeTuple!(float, double, real) - ImplicitConversionTargets; + alias ImplicitConversionTargets = TypeTuple!(float, double, real); else static if (is(T == ulong)) - alias TypeTuple!(float, double, real) - ImplicitConversionTargets; + alias ImplicitConversionTargets = TypeTuple!(float, double, real); else static if (is(T == float)) - alias TypeTuple!(double, real) - ImplicitConversionTargets; + alias ImplicitConversionTargets = TypeTuple!(double, real); else static if (is(T == double)) - alias TypeTuple!real - ImplicitConversionTargets; + alias ImplicitConversionTargets = TypeTuple!real; else static if (is(T == char)) - alias TypeTuple!(wchar, dchar, byte, ubyte, short, ushort, - int, uint, long, ulong, float, double, real) - ImplicitConversionTargets; + alias ImplicitConversionTargets = + TypeTuple!(wchar, dchar, byte, ubyte, short, ushort, + int, uint, long, ulong, float, double, real); else static if (is(T == wchar)) - alias TypeTuple!(wchar, dchar, short, ushort, int, uint, long, ulong, - float, double, real) - ImplicitConversionTargets; + alias ImplicitConversionTargets = + TypeTuple!(dchar, short, ushort, int, uint, long, ulong, + float, double, real); else static if (is(T == dchar)) - alias TypeTuple!(wchar, dchar, int, uint, long, ulong, - float, double, real) - ImplicitConversionTargets; + alias ImplicitConversionTargets = + TypeTuple!(int, uint, long, ulong, float, double, real); else static if (is(T : typeof(null))) - alias TypeTuple!(typeof(null)) ImplicitConversionTargets; + alias ImplicitConversionTargets = TypeTuple!(typeof(null)); else static if(is(T : Object)) - alias TransitiveBaseTypeTuple!(T) ImplicitConversionTargets; + alias ImplicitConversionTargets = TransitiveBaseTypeTuple!(T); else static if (isDynamicArray!T && !is(typeof(T.init[0]) == const)) alias ImplicitConversionTargets = TypeTuple!(const(Unqual!(typeof(T.init[0])))[]); else static if (is(T : void*)) - alias TypeTuple!(void*) ImplicitConversionTargets; + alias ImplicitConversionTargets = TypeTuple!(void*); else - alias TypeTuple!() ImplicitConversionTargets; + alias ImplicitConversionTargets = TypeTuple!(); } unittest @@ -3994,6 +4138,8 @@ unittest static assert( isAssignable!(int, shared int)); static assert( isAssignable!(shared int)); + static assert( isAssignable!(void[1], void[1])); + struct S { @disable this(); this(int n){} } static assert( isAssignable!(S, S)); @@ -4144,11 +4290,12 @@ classes of the arguments. */ private template isStorageClassImplicitlyConvertible(From, To) { + alias Pointify(T) = void*; + enum isStorageClassImplicitlyConvertible = isImplicitlyConvertible!( ModifyTypePreservingSTC!(Pointify, From), ModifyTypePreservingSTC!(Pointify, To) ); } -private template Pointify(T) { alias void* Pointify; } unittest { @@ -4193,8 +4340,8 @@ template isCovariantWith(F, G) enum isCovariantWith = true; else { - alias F Upr; - alias G Lwr; + alias Upr = F; + alias Lwr = G; /* * Check for calling convention: require exact match. @@ -4228,7 +4375,7 @@ template isCovariantWith(F, G) */ template checkAttributes() { - alias FunctionAttribute FA; + alias FA = FunctionAttribute; enum uprAtts = functionAttributes!Upr; enum lwrAtts = functionAttributes!Lwr; // @@ -4255,11 +4402,11 @@ template isCovariantWith(F, G) */ template checkParameters() { - alias ParameterStorageClass STC; - alias ParameterTypeTuple!Upr UprParams; - alias ParameterTypeTuple!Lwr LwrParams; - alias ParameterStorageClassTuple!Upr UprPSTCs; - alias ParameterStorageClassTuple!Lwr LwrPSTCs; + alias STC = ParameterStorageClass; + alias UprParams = ParameterTypeTuple!Upr; + alias LwrParams = ParameterTypeTuple!Lwr; + alias UprPSTCs = ParameterStorageClassTuple!Upr; + alias LwrPSTCs = ParameterStorageClassTuple!Lwr; // template checkNext(size_t i) { @@ -4294,12 +4441,10 @@ template isCovariantWith(F, G) } } -version (unittest) private template isCovariantWith(alias f, alias g) -{ - enum bool isCovariantWith = isCovariantWith!(typeof(f), typeof(g)); -} unittest { + enum bool isCovariantWith(alias f, alias g) = .isCovariantWith!(typeof(f), typeof(g)); + // covariant return type interface I {} interface J : I {} @@ -4310,9 +4455,9 @@ unittest static assert( isCovariantWith!(DerivA_2.test, BaseA.test)); static assert(!isCovariantWith!(BaseA.test, DerivA_1.test)); static assert(!isCovariantWith!(BaseA.test, DerivA_2.test)); - static assert(isCovariantWith!(BaseA.test, BaseA.test)); - static assert(isCovariantWith!(DerivA_1.test, DerivA_1.test)); - static assert(isCovariantWith!(DerivA_2.test, DerivA_2.test)); + static assert( isCovariantWith!(BaseA.test, BaseA.test)); + static assert( isCovariantWith!(DerivA_1.test, DerivA_1.test)); + static assert( isCovariantWith!(DerivA_2.test, DerivA_2.test)); // scope parameter interface BaseB { void test( int, int); } @@ -4721,11 +4866,8 @@ template ArrayTypeOf(T) static assert(0, T.stringof~" is not an array type"); } -unittest -{ -} - /* +Always returns the Dynamic Array version. */ template StringTypeOf(T) { @@ -4738,7 +4880,10 @@ template StringTypeOf(T) } else static if (is(T : const char[]) || is(T : const wchar[]) || is(T : const dchar[])) { - alias StringTypeOf = ArrayTypeOf!T; + static if (is(T : U[], U)) + alias StringTypeOf = U[]; + else + static assert(0); } else static assert(0, T.stringof~" is not a string type"); @@ -4755,8 +4900,8 @@ unittest { static assert(is(Q!T[] == StringTypeOf!( SubTypeOf!(Q!T[]) ))); - alias Q!T[] Str; - class C(Str) { Str val; alias val this; } + alias Str = Q!T[]; + class C(S) { S val; alias val this; } static assert(is(StringTypeOf!(C!Str) == Str)); } } @@ -4768,6 +4913,11 @@ unittest } } +unittest +{ + static assert(is(StringTypeOf!(char[4]) == char[])); +} + /* */ template AssocArrayTypeOf(T) @@ -4828,10 +4978,7 @@ template BuiltinTypeOf(T) /** * Detect whether $(D T) is a built-in boolean type. */ -template isBoolean(T) -{ - enum bool isBoolean = is(BooleanTypeOf!T) && !isAggregateType!T; -} +enum bool isBoolean(T) = is(BooleanTypeOf!T) && !isAggregateType!T; unittest { @@ -4845,10 +4992,7 @@ unittest * Detect whether $(D T) is a built-in integral type. Types $(D bool), * $(D char), $(D wchar), and $(D dchar) are not considered integral. */ -template isIntegral(T) -{ - enum bool isIntegral = is(IntegralTypeOf!T) && !isAggregateType!T; -} +enum bool isIntegral(T) = is(IntegralTypeOf!T) && !isAggregateType!T; unittest { @@ -4872,10 +5016,7 @@ unittest /** * Detect whether $(D T) is a built-in floating point type. */ -template isFloatingPoint(T) -{ - enum bool isFloatingPoint = is(FloatingPointTypeOf!T) && !isAggregateType!T; -} +enum bool isFloatingPoint(T) = is(FloatingPointTypeOf!T) && !isAggregateType!T; unittest { @@ -4902,10 +5043,7 @@ unittest Detect whether $(D T) is a built-in numeric type (integral or floating point). */ -template isNumeric(T) -{ - enum bool isNumeric = is(NumericTypeOf!T) && !isAggregateType!T; -} +enum bool isNumeric(T) = is(NumericTypeOf!T) && !isAggregateType!T; unittest { @@ -4920,12 +5058,9 @@ unittest } /** -Detect whether $(D T) is a scalar type. +Detect whether $(D T) is a scalar type (a built-in numeric, character or boolean type). */ -template isScalarType(T) -{ - enum bool isScalarType = isNumeric!T || isSomeChar!T || isBoolean!T; -} +enum bool isScalarType(T) = isNumeric!T || isSomeChar!T || isBoolean!T; unittest { @@ -4937,12 +5072,9 @@ unittest } /** -Detect whether $(D T) is a basic type. +Detect whether $(D T) is a basic type (scalar type or void). */ -template isBasicType(T) -{ - enum bool isBasicType = isScalarType!T || is(T == void); -} +enum bool isBasicType(T) = isScalarType!T || is(T == void); unittest { @@ -4956,10 +5088,7 @@ unittest /** Detect whether $(D T) is a built-in unsigned numeric type. */ -template isUnsigned(T) -{ - enum bool isUnsigned = is(UnsignedTypeOf!T) && !isAggregateType!T; -} +enum bool isUnsigned(T) = is(UnsignedTypeOf!T) && !isAggregateType!T; unittest { @@ -4976,10 +5105,7 @@ unittest /** Detect whether $(D T) is a built-in signed numeric type. */ -template isSigned(T) -{ - enum bool isSigned = is(SignedTypeOf!T) && !isAggregateType!T; -} +enum bool isSigned(T) = is(SignedTypeOf!T) && !isAggregateType!T; unittest { @@ -4996,10 +5122,7 @@ unittest /** Detect whether $(D T) is one of the built-in character types. */ -template isSomeChar(T) -{ - enum isSomeChar = is(CharTypeOf!T) && !isAggregateType!T; -} +enum bool isSomeChar(T) = is(CharTypeOf!T) && !isAggregateType!T; unittest { @@ -5024,15 +5147,18 @@ unittest /** Detect whether $(D T) is one of the built-in string types. + +The built-in string types are $(D Char[]), where $(D Char) is any of $(D char), +$(D wchar) or $(D dchar), with or without qualifiers. + +Static arrays of characters (like $(D char[80]) are not considered +built-in string types. */ -template isSomeString(T) -{ - enum isSomeString = is(StringTypeOf!T) && !isAggregateType!T; -} +enum bool isSomeString(T) = is(StringTypeOf!T) && !isAggregateType!T && !isStaticArray!T; unittest { - foreach (T; TypeTuple!(char[], dchar[], string, wstring, dstring, char[4])) + foreach (T; TypeTuple!(char[], dchar[], string, wstring, dstring)) { static assert( isSomeString!( T )); static assert(!isSomeString!(SubTypeOf!(T))); @@ -5042,19 +5168,17 @@ unittest static assert(!isSomeString!(int[])); static assert(!isSomeString!(byte[])); static assert(!isSomeString!(typeof(null))); + static assert(!isSomeString!(char[4])); enum ES : string { a = "aaa", b = "bbb" } static assert( isSomeString!ES); } -template isNarrowString(T) -{ - enum isNarrowString = (is(T : const char[]) || is(T : const wchar[])) && !isAggregateType!T; -} +enum bool isNarrowString(T) = (is(T : const char[]) || is(T : const wchar[])) && !isAggregateType!T && !isStaticArray!T; unittest { - foreach (T; TypeTuple!(char[], string, wstring, char[4])) + foreach (T; TypeTuple!(char[], string, wstring)) { foreach (Q; TypeTuple!(MutableOf, ConstOf, ImmutableOf)/*TypeQualifierList*/) { @@ -5063,7 +5187,7 @@ unittest } } - foreach (T; TypeTuple!(int, int[], byte[], dchar[], dstring)) + foreach (T; TypeTuple!(int, int[], byte[], dchar[], dstring, char[4])) { foreach (Q; TypeQualifierList) { @@ -5076,10 +5200,7 @@ unittest /** * Detect whether type $(D T) is a static array. */ -template isStaticArray(T) -{ - enum isStaticArray = is(StaticArrayTypeOf!T) && !isAggregateType!T; -} +enum bool isStaticArray(T) = is(StaticArrayTypeOf!T) && !isAggregateType!T; unittest { @@ -5110,10 +5231,7 @@ unittest /** * Detect whether type $(D T) is a dynamic array. */ -template isDynamicArray(T) -{ - enum isDynamicArray = is(DynamicArrayTypeOf!T) && !isAggregateType!T; -} +enum bool isDynamicArray(T) = is(DynamicArrayTypeOf!T) && !isAggregateType!T; unittest { @@ -5134,12 +5252,10 @@ unittest } /** - * Detect whether type $(D T) is an array. + * Detect whether type $(D T) is an array (static or dynamic; for associative + * arrays see $(LREF isAssociativeArray)). */ -template isArray(T) -{ - enum bool isArray = isStaticArray!T || isDynamicArray!T; -} +enum bool isArray(T) = isStaticArray!T || isDynamicArray!T; unittest { @@ -5160,10 +5276,7 @@ unittest /** * Detect whether $(D T) is an associative array type */ -template isAssociativeArray(T) -{ - enum bool isAssociativeArray = is(AssocArrayTypeOf!T) && !isAggregateType!T; -} +enum bool isAssociativeArray(T) = is(AssocArrayTypeOf!T) && !isAggregateType!T; unittest { @@ -5191,21 +5304,29 @@ unittest //static assert( isAssociativeArray!EAA); } -template isBuiltinType(T) +/** + * Detect whether type $(D T) is a builtin type. + */ +enum bool isBuiltinType(T) = is(BuiltinTypeOf!T) && !isAggregateType!T; + +/** + * Detect whether type $(D T) is a SIMD vector type. + */ +enum bool isSIMDVector(T) = is(T : __vector(V[N]), V, size_t N); + +unittest { - enum isBuiltinType = is(BuiltinTypeOf!T) && !isAggregateType!T; + alias __vector(float[4]) SimdVec; + static assert(isSIMDVector!(__vector(float[4]))); + static assert(isSIMDVector!SimdVec); + static assert(!isSIMDVector!uint); + static assert(!isSIMDVector!(float[4])); } /** * Detect whether type $(D T) is a pointer. */ -template isPointer(T) -{ - static if (is(T P == U*, U) && !isAggregateType!T) - enum isPointer = true; - else - enum isPointer = false; -} +enum bool isPointer(T) = is(T == U*, U) && !isAggregateType!T; unittest { @@ -5227,13 +5348,14 @@ unittest /** Returns the target type of a pointer. */ -template PointerTarget(T : T*) -{ - alias T PointerTarget; -} +alias PointerTarget(T : T*) = T; -/// $(RED Scheduled for deprecation. Please use $(LREF PointerTarget) instead.) -alias PointerTarget pointerTarget; +/** + $(RED Deprecated. Please use $(LREF PointerTarget) instead. This will be + removed in June 2015.) + */ +deprecated("Please use PointerTarget instead.") +alias pointerTarget = PointerTarget; unittest { @@ -5246,11 +5368,8 @@ unittest /** * Detect whether type $(D T) is an aggregate type. */ -template isAggregateType(T) -{ - enum isAggregateType = is(T == struct) || is(T == union) || - is(T == class) || is(T == interface); -} +enum bool isAggregateType(T) = is(T == struct) || is(T == union) || + is(T == class) || is(T == interface); /** * Returns $(D true) if T can be iterated over using a $(D foreach) loop with @@ -5259,10 +5378,7 @@ template isAggregateType(T) * that define $(D opApply) with a single loop variable, and builtin dynamic, * static and associative arrays. */ -template isIterable(T) -{ - enum isIterable = is(typeof({ foreach(elem; T.init) {} })); -} +enum bool isIterable(T) = is(typeof({ foreach(elem; T.init) {} })); unittest { @@ -5290,10 +5406,7 @@ unittest * Returns true if T is not const or immutable. Note that isMutable is true for * string, or immutable(char)[], because the 'head' is mutable. */ -template isMutable(T) -{ - enum isMutable = !is(T == const) && !is(T == immutable) && !is(T == inout); -} +enum bool isMutable(T) = !is(T == const) && !is(T == immutable) && !is(T == inout); unittest { @@ -5312,13 +5425,7 @@ unittest /** * Returns true if T is an instance of the template S. */ -template isInstanceOf(alias S, T) -{ - static if (is(T x == S!Args, Args...)) - enum bool isInstanceOf = true; - else - enum bool isInstanceOf = false; -} +enum bool isInstanceOf(alias S, T) = is(T == S!Args, Args...); unittest { @@ -5335,7 +5442,8 @@ unittest } /** - * Tells whether the tuple T is an expression tuple. + * Check whether the tuple T is an expression tuple. + * An expression tuple only contains expressions. See also $(LREF isTypeTuple). */ template isExpressionTuple(T ...) { @@ -5350,12 +5458,20 @@ template isExpressionTuple(T ...) enum bool isExpressionTuple = true; // default } +/// +unittest +{ + static assert(isExpressionTuple!(1, 2.0, "a")); + static assert(!isExpressionTuple!(int, double, string)); + static assert(!isExpressionTuple!(int, 2.0, "a")); +} + unittest { void foo(); static int bar() { return 42; } enum aa = [ 1: -1 ]; - alias int myint; + alias myint = int; static assert( isExpressionTuple!(42)); static assert( isExpressionTuple!aa); @@ -5371,7 +5487,8 @@ unittest /** -Detect whether tuple $(D T) is a type tuple. + * Check whether the tuple $(D T) is a type tuple. + * A type tuple only contains types. See also $(LREF isExpressionTuple). */ template isTypeTuple(T...) { @@ -5383,6 +5500,14 @@ template isTypeTuple(T...) enum bool isTypeTuple = true; // default } +/// +unittest +{ + static assert(isTypeTuple!(int, float, string)); + static assert(!isTypeTuple!(1, 2.0, "a")); + static assert(!isTypeTuple!(1, double, string)); +} + unittest { class C {} @@ -5687,11 +5812,11 @@ template Unqual(T) { version (none) // Error: recursive alias declaration @@@BUG1308@@@ { - static if (is(T U == const U)) alias Unqual!U Unqual; - else static if (is(T U == immutable U)) alias Unqual!U Unqual; - else static if (is(T U == inout U)) alias Unqual!U Unqual; - else static if (is(T U == shared U)) alias Unqual!U Unqual; - else alias T Unqual; + static if (is(T U == const U)) alias Unqual = Unqual!U; + else static if (is(T U == immutable U)) alias Unqual = Unqual!U; + else static if (is(T U == inout U)) alias Unqual = Unqual!U; + else static if (is(T U == shared U)) alias Unqual = Unqual!U; + else alias Unqual = T; } else // workaround { @@ -5719,7 +5844,7 @@ unittest static assert(is(Unqual!(shared inout const int) == int)); static assert(is(Unqual!( immutable int) == int)); - alias immutable(int[]) ImmIntArr; + alias ImmIntArr = immutable(int[]); static assert(is(Unqual!ImmIntArr == immutable(int)[])); } @@ -5760,7 +5885,7 @@ has both opApply and a range interface. */ template ForeachType(T) { - alias ReturnType!(typeof( + alias ForeachType = ReturnType!(typeof( (inout int x = 0) { foreach(elem; T.init) @@ -5768,7 +5893,7 @@ template ForeachType(T) return elem; } assert(0); - })) ForeachType; + })); } unittest @@ -5781,51 +5906,35 @@ unittest /** -Strips off all $(D typedef)s (including $(D enum) ones) from type $(D T). - -Example: --------------------- -enum E : int { a } -typedef E F; -typedef const F G; -static assert(is(OriginalType!G == const int)); --------------------- + * Strips off all $(D enum)s from type $(D T). */ template OriginalType(T) { template Impl(T) { - static if (is(T U == typedef)) alias OriginalType!U Impl; - else static if (is(T U == enum)) alias OriginalType!U Impl; - else alias T Impl; + static if (is(T U == enum)) alias Impl = OriginalType!U; + else alias Impl = T; } - alias ModifyTypePreservingSTC!(Impl, T) OriginalType; + alias OriginalType = ModifyTypePreservingSTC!(Impl, T); } +/// unittest { - //typedef real T; - //typedef T U; - //enum V : U { a } - //static assert(is(OriginalType!T == real)); - //static assert(is(OriginalType!U == real)); - //static assert(is(OriginalType!V == real)); enum E : real { a } enum F : E { a = E.a } - //typedef const F G; + alias G = const(F); static assert(is(OriginalType!E == real)); static assert(is(OriginalType!F == real)); - //static assert(is(OriginalType!G == const real)); + static assert(is(OriginalType!G == const real)); } /** * Get the Key type of an Associative Array. */ -template KeyType(V : V[K], K) -{ - alias K KeyType; -} +alias KeyType(V : V[K], K) = K; + /// unittest { @@ -5840,10 +5949,8 @@ unittest /** * Get the Value type of an Associative Array. */ -template ValueType(V : V[K], K) -{ - alias V ValueType; -} +alias ValueType(V : V[K], K) = V; + /// unittest { @@ -5863,9 +5970,11 @@ template Unsigned(T) { template Impl(T) { - static if (isUnsigned!T) + static if (is(T : __vector(V[N]), V, size_t N)) + alias Impl = __vector(Impl!V[N]); + else static if (isUnsigned!T) alias Impl = T; - else static if (isSigned!T) + else static if (isSigned!T && !isFloatingPoint!T) { static if (is(T == byte )) alias Impl = ubyte; static if (is(T == short)) alias Impl = ushort; @@ -5877,20 +5986,27 @@ template Unsigned(T) " does not have an Unsigned counterpart"); } - alias ModifyTypePreservingSTC!(Impl, OriginalType!T) Unsigned; + alias Unsigned = ModifyTypePreservingSTC!(Impl, OriginalType!T); } unittest { - alias Unsigned!int U1; - alias Unsigned!(const(int)) U2; - alias Unsigned!(immutable(int)) U3; + alias U1 = Unsigned!int; + alias U2 = Unsigned!(const(int)); + alias U3 = Unsigned!(immutable(int)); static assert(is(U1 == uint)); static assert(is(U2 == const(uint))); static assert(is(U3 == immutable(uint))); + static if (is(__vector(int[4])) && is(__vector(uint[4]))) + { + alias Unsigned!(__vector(int[4])) UV1; + alias Unsigned!(const(__vector(int[4]))) UV2; + static assert(is(UV1 == __vector(uint[4]))); + static assert(is(UV2 == const(__vector(uint[4])))); + } //struct S {} - //alias Unsigned!S U2; - //alias Unsigned!double U3; + //alias U2 = Unsigned!S; + //alias U3 = Unsigned!double; } /** @@ -5902,28 +6018,27 @@ template Largest(T...) if(T.length >= 1) { static if (T.length == 1) { - alias T[0] Largest; + alias Largest = T[0]; } else static if (T.length == 2) { static if(T[0].sizeof >= T[1].sizeof) { - alias T[0] Largest; + alias Largest = T[0]; } else { - alias T[1] Largest; + alias Largest = T[1]; } } else { - alias Largest!(Largest!(T[0 .. $/2]), Largest!(T[$/2 .. $])) Largest; + alias Largest = Largest!(Largest!(T[0 .. $/2]), Largest!(T[$/2 .. $])); } } unittest { - // NOTE: Backport from upstream specific to GDC. static assert(is(Largest!(uint, ubyte, ushort, real) == real)); static assert(is(Largest!(ulong, double) == ulong)); static assert(is(Largest!(double, ulong) == double)); @@ -5938,7 +6053,9 @@ template Signed(T) { template Impl(T) { - static if (isSigned!T) + static if (is(T : __vector(V[N]), V, size_t N)) + alias Impl = __vector(Impl!V[N]); + else static if (isSigned!T) alias Impl = T; else static if (isUnsigned!T) { @@ -5952,28 +6069,28 @@ template Signed(T) " does not have an Signed counterpart"); } - alias ModifyTypePreservingSTC!(Impl, OriginalType!T) Signed; + alias Signed = ModifyTypePreservingSTC!(Impl, OriginalType!T); } unittest { - alias Signed!uint S1; - alias Signed!(const(uint)) S2; - alias Signed!(immutable(uint)) S3; + alias S1 = Signed!uint; + alias S2 = Signed!(const(uint)); + alias S3 = Signed!(immutable(uint)); static assert(is(S1 == int)); static assert(is(S2 == const(int))); static assert(is(S3 == immutable(int))); + static assert(is(Signed!float == float)); + static if (is(__vector(int[4])) && is(__vector(uint[4]))) + { + alias Signed!(__vector(uint[4])) SV1; + alias Signed!(const(__vector(uint[4]))) SV2; + static assert(is(SV1 == __vector(int[4]))); + static assert(is(SV2 == const(__vector(int[4])))); + } } -// Remove import when unsigned is removed. -import std.conv; - -// Purposefully undocumented. Will be removed in June 2014. -deprecated("unsigned has been moved to std.conv. Please adjust your imports accordingly.") -alias unsigned = std.conv.unsigned; - - /** Returns the most negative value of the numeric type T. */ @@ -5988,6 +6105,7 @@ template mostNegative(T) enum mostNegative = T.min; } +/// unittest { static assert(mostNegative!float == -float.max); @@ -6089,11 +6207,6 @@ private string removeDummyEnvelope(string s) unittest { - //typedef int MyInt; - //MyInt test() { return 0; } - //static assert(mangledName!MyInt[$ - 7 .. $] == "T5MyInt"); // XXX depends on bug 4237 - //static assert(mangledName!test[$ - 7 .. $] == "T5MyInt"); - class C { int value() @property { return 0; } } static assert(mangledName!int == int.mangleof); static assert(mangledName!C == C.mangleof); @@ -6102,7 +6215,7 @@ unittest static assert(mangledName!removeDummyEnvelope == "_D3std6traits19removeDummyEnvelopeFAyaZAya"); int x; - static assert(mangledName!((int a) { return a+x; }) == "DFNbNfiZi"); // nothrow safe + static assert(mangledName!((int a) { return a+x; }) == "DFNbNiNfiZi"); // nothrow @safe @nnogc } unittest diff --git a/libphobos/src/std/typecons.d b/libphobos/src/std/typecons.d index a0ffb44f2..63372d431 100644 --- a/libphobos/src/std/typecons.d +++ b/libphobos/src/std/typecons.d @@ -14,11 +14,11 @@ Synopsis: ---- // value tuples -alias Tuple!(float, "x", float, "y", float, "z") Coord; +alias Coord = Tuple!(float, "x", float, "y", float, "z"); Coord c; c[1] = 1; // access by index c.z = 1; // access by given name -alias Tuple!(string, string) DicEntry; // names can be omitted +alias DicEntry = Tuple!(string, string); // names can be omitted // Rebindable references to const and immutable objects void bar() @@ -60,9 +60,10 @@ Example: struct Unique(T) { static if (is(T:Object)) - alias T RefT; + alias RefT = T; else - alias T * RefT; + alias RefT = T*; + public: /+ Doesn't work yet /** @@ -170,7 +171,7 @@ unittest ~this() { writefln(" Bar destructor"); } int val() const { return 4; } } - alias Unique!(Bar) UBar; + alias UBar = Unique!(Bar); UBar g(UBar u) { return u; @@ -194,7 +195,7 @@ unittest ~this() { writefln(" Bar destructor"); } int val() const { return 3; } } - alias Unique!(Foo) UFoo; + alias UFoo = Unique!(Foo); UFoo f(UFoo u) { @@ -246,7 +247,7 @@ members. The method above is still applicable to all fields. Example: ---- -alias Tuple!(int, "index", string, "value") Entry; +alias Entry = Tuple!(int, "index", string, "value"); Entry e; e.index = 4; e.value = "Hello"; @@ -277,19 +278,21 @@ template Tuple(Specs...) { static if (Specs.length == 0) { - alias TypeTuple!() parseSpecs; + alias parseSpecs = TypeTuple!(); } else static if (is(Specs[0])) { static if (is(typeof(Specs[1]) : string)) { - alias TypeTuple!(FieldSpec!(Specs[0 .. 2]), - parseSpecs!(Specs[2 .. $])) parseSpecs; + alias parseSpecs = + TypeTuple!(FieldSpec!(Specs[0 .. 2]), + parseSpecs!(Specs[2 .. $])); } else { - alias TypeTuple!(FieldSpec!(Specs[0]), - parseSpecs!(Specs[1 .. $])) parseSpecs; + alias parseSpecs = + TypeTuple!(FieldSpec!(Specs[0]), + parseSpecs!(Specs[1 .. $])); } } else @@ -301,19 +304,19 @@ template Tuple(Specs...) template FieldSpec(T, string s = "") { - alias T Type; - alias s name; + alias Type = T; + alias name = s; } - alias parseSpecs!Specs fieldSpecs; + alias fieldSpecs = parseSpecs!Specs; // Used with staticMap. - template extractType(alias spec) { alias spec.Type extractType; } - template extractName(alias spec) { alias spec.name extractName; } + alias extractType(alias spec) = spec.Type; + alias extractName(alias spec) = spec.name; // Generates named fields as follows: - // alias Identity!(field[0]) name_0; - // alias Identity!(field[1]) name_1; + // alias name_0 = Identity!(field[0]); + // alias name_1 = Identity!(field[1]); // : // NOTE: field[k] is an expression (which yields a symbol of a // variable) and can't be aliased directly. @@ -324,10 +327,10 @@ template Tuple(Specs...) { import std.string : format; - decl ~= format("alias Identity!(field[%s]) _%s;", i, i); + decl ~= format("alias _%s = Identity!(field[%s]);", i, i); if (name.length != 0) { - decl ~= format("alias _%s %s;", i, name); + decl ~= format("alias %s = _%s;", name, i); } } return decl; @@ -335,38 +338,51 @@ template Tuple(Specs...) // Returns Specs for a subtuple this[from .. to] preserving field // names if any. - template sliceSpecs(size_t from, size_t to) - { - alias staticMap!(expandSpec, - fieldSpecs[from .. to]) sliceSpecs; - } + alias sliceSpecs(size_t from, size_t to) = + staticMap!(expandSpec, fieldSpecs[from .. to]); template expandSpec(alias spec) { static if (spec.name.length == 0) { - alias TypeTuple!(spec.Type) expandSpec; + alias expandSpec = TypeTuple!(spec.Type); } else { - alias TypeTuple!(spec.Type, spec.name) expandSpec; + alias expandSpec = TypeTuple!(spec.Type, spec.name); } } - template areCompatibleTuples(Tup1, Tup2, string op) + enum areCompatibleTuples(Tup1, Tup2, string op) = isTuple!Tup2 && is(typeof( { - enum areCompatibleTuples = isTuple!Tup2 && is(typeof( + Tup1 tup1 = void; + Tup2 tup2 = void; + static assert(tup1.field.length == tup2.field.length); + foreach (i, _; Tup1.Types) { - Tup1 tup1 = void; - Tup2 tup2 = void; - static assert(tup1.field.length == tup2.field.length); - foreach (i, _; Tup1.Types) - { - auto lhs = typeof(tup1.field[i]).init; - auto rhs = typeof(tup2.field[i]).init; - auto result = mixin("lhs "~op~" rhs"); - } - })); + auto lhs = typeof(tup1.field[i]).init; + auto rhs = typeof(tup2.field[i]).init; + auto result = mixin("lhs "~op~" rhs"); + } + })); + + enum areBuildCompatibleTuples(Tup1, Tup2) = isTuple!Tup2 && is(typeof( + { + static assert(Tup1.Types.length == Tup2.Types.length); + foreach (i, _; Tup1.Types) + static assert(isBuildable!(Tup1.Types[i], Tup2.Types[i])); + })); + + /+ Returns $(D true) iff a $(D T) can be initialized from a $(D U). +/ + enum isBuildable(T, U) = is(typeof( + { + U u = U.init; + T t = u; + })); + /+ Helper for partial instanciation +/ + template isBuildableFrom(U) + { + enum isBuildableFrom(T) = isBuildable!(T, U); } struct Tuple @@ -374,7 +390,18 @@ template Tuple(Specs...) /** * The type of the tuple's components. */ - alias staticMap!(extractType, fieldSpecs) Types; + alias Types = staticMap!(extractType, fieldSpecs); + + /** + * The names of the tuple's components. Unnamed fields have empty names. + * + * Examples: + * ---- + * alias Fields = Tuple!(int, "id", string, float); + * static assert(Fields.fieldNames == TypeTuple!("id", "", "")); + * ---- + */ + alias fieldNames = staticMap!(extractName, fieldSpecs); /** * Use $(D t.expand) for a tuple $(D t) to expand it into its @@ -417,17 +444,18 @@ template Tuple(Specs...) alias field = expand; /** - * Constructor taking one value for each field. Each argument must be - * implicitly assignable to the respective element of the target. + * Constructor taking one value for each field. */ - this(Types values) + static if (Types.length > 0) { - field[] = values[]; + this(Types values) + { + field[] = values[]; + } } /** - * Constructor taking a compatible array. The array element type must - * be implicitly assignable to each element of the target. + * Constructor taking a compatible array. * * Examples: * ---- @@ -436,8 +464,7 @@ template Tuple(Specs...) * ---- */ this(U, size_t n)(U[n] values) - if (n == Types.length && - is(typeof({ foreach (i, _; Types) field[i] = values[i]; }))) + if (n == Types.length && allSatisfy!(isBuildableFrom!U, Types)) { foreach (i, _; Types) { @@ -446,12 +473,10 @@ template Tuple(Specs...) } /** - * Constructor taking a compatible tuple. Each element of the source - * must be implicitly assignable to the respective element of the - * target. + * Constructor taking a compatible tuple. */ this(U)(U another) - if (areCompatibleTuples!(typeof(this), U, "=")) + if (areBuildCompatibleTuples!(typeof(this), U)) { field[] = another.field[]; } @@ -549,6 +574,14 @@ template Tuple(Specs...) return *cast(typeof(return)*) &(field[from]); } + size_t toHash() const nothrow @trusted + { + size_t h = 0; + foreach (i, T; Types) + h += typeid(T).getHash(cast(const void*)&field[i]); + return h; + } + /** * Converts to string. */ @@ -584,19 +617,67 @@ template Tuple(Specs...) } } -private template isPrintable(T) -{ - enum isPrintable = is(typeof({ +private enum bool isPrintable(T) = + is(typeof({ import std.format : formattedWrite; Appender!string w; formattedWrite(w, "%s", T.init); })); + +private alias Identity(alias T) = T; + +/** + Return a copy of a Tuple with its fields in reverse order. + */ +ReverseTupleType!T reverse(T)(T t) + if (isTuple!T) +{ + import std.typetuple : Reverse; + // @@@BUG@@@ Cannot be an internal function due to forward reference issues. + + // @@@BUG@@@ 9929 Need 'this' when calling template with expanded tuple + // return tuple(Reverse!(t.expand)); + + typeof(return) result; + auto tup = t.expand; + result.expand = Reverse!tup; + return result; +} + +/// +unittest +{ + auto tup = tuple(1, "2"); + assert(tup.reverse == tuple("2", 1)); } -private template Identity(alias T) +/* Get a Tuple type with the reverse specification of Tuple T. */ +private template ReverseTupleType(T) + if (isTuple!T) { - alias T Identity; + static if (is(T : Tuple!A, A...)) + alias ReverseTupleType = Tuple!(ReverseTupleSpecs!A); +} + +/* Reverse the Specs of a Tuple. */ +private template ReverseTupleSpecs(T...) +{ + static if (T.length > 1) + { + static if (is(typeof(T[$-1]) : string)) + { + alias ReverseTupleSpecs = TypeTuple!(T[$-2], T[$-1], ReverseTupleSpecs!(T[0 .. $-2])); + } + else + { + alias ReverseTupleSpecs = TypeTuple!(T[$-1], ReverseTupleSpecs!(T[0 .. $-1])); + } + } + else + { + alias ReverseTupleSpecs = T; + } } unittest @@ -743,7 +824,7 @@ unittest { const int x = 1; auto t1 = tuple(x); - alias Tuple!(const(int)) T; + alias T = Tuple!(const(int)); auto t2 = T(1); } // 9431 @@ -751,6 +832,24 @@ unittest alias T = Tuple!(int[1][]); auto t = T([[10]]); } + // 7666 + { + auto tup = tuple(1, "2"); + assert(tup.reverse == tuple("2", 1)); + } + { + Tuple!(int, "x", string, "y") tup = tuple(1, "2"); + auto rev = tup.reverse; + assert(rev == tuple("2", 1)); + assert(rev.x == 1 && rev.y == "2"); + } + { + Tuple!(wchar, dchar, int, "x", string, "y", char, byte, float) tup; + tup = tuple('a', 'b', 3, "4", 'c', cast(byte)0x0D, 0.00); + auto rev = tup.reverse; + assert(rev == tuple(0.00, cast(byte)0x0D, 'c', "4", 3, 'b', 'a')); + assert(rev.x == 3 && rev.y == "4"); + } } unittest { @@ -870,6 +969,39 @@ unittest Tuple!(immutable(Foo)[]) a; } +unittest +{ + //Test non-assignable + static struct S + { + int* p; + } + alias IS = immutable S; + static assert(!isAssignable!IS); + + auto s = IS.init; + + alias TIS = Tuple!IS; + TIS a = tuple(s); + TIS b = a; + + alias TISIS = Tuple!(IS, IS); + TISIS d = tuple(s, s); + IS[2] ss; + TISIS e = TISIS(ss); +} + +// Bugzilla #9819 +unittest +{ + alias T = Tuple!(int, "x", double, "foo"); + static assert(T.fieldNames[0] == "x"); + static assert(T.fieldNames[1] == "foo"); + + alias Fields = Tuple!(int, "id", string, float); + static assert(Fields.fieldNames == TypeTuple!("id", "", "")); +} + /** Returns a $(D Tuple) object instantiated and initialized according to the arguments. @@ -957,15 +1089,15 @@ break the soundness of D's type system and does not incur any of the risks usually associated with $(D cast). */ -template Rebindable(T) if (is(T == class) || is(T == interface) || isArray!T) +template Rebindable(T) if (is(T == class) || is(T == interface) || isDynamicArray!T) { static if (!is(T X == const U, U) && !is(T X == immutable U, U)) { - alias T Rebindable; + alias Rebindable = T; } - else static if (isArray!T) + else static if (isDynamicArray!T) { - alias const(ElementType!T)[] Rebindable; + alias Rebindable = const(ElementEncodingType!T)[]; } else { @@ -976,29 +1108,29 @@ template Rebindable(T) if (is(T == class) || is(T == interface) || isArray!T) T original; U stripped; } - void opAssign(T another) pure nothrow + void opAssign(T another) @trusted pure nothrow { stripped = cast(U) another; } - void opAssign(Rebindable another) pure nothrow + void opAssign(Rebindable another) @trusted pure nothrow { stripped = another.stripped; } static if (is(T == const U)) { // safely assign immutable to const - void opAssign(Rebindable!(immutable U) another) pure nothrow + void opAssign(Rebindable!(immutable U) another) @trusted pure nothrow { stripped = another.stripped; } } - this(T initializer) pure nothrow + this(T initializer) @safe pure nothrow { opAssign(initializer); } - @property ref inout(T) get() inout pure nothrow + @property ref inout(T) get() @trusted inout pure nothrow { return original; } @@ -1013,7 +1145,7 @@ Convenience function for creating a $(D Rebindable) using automatic type inference. */ Rebindable!T rebindable(T)(T obj) -if (is(T == class) || is(T == interface) || isArray!T) +if (is(T == class) || is(T == interface) || isDynamicArray!T) { typeof(return) ret; ret = obj; @@ -1096,6 +1228,21 @@ unittest const arrConst = arr; assert(rebindable(arr) == arr); assert(rebindable(arrConst) == arr); + + // Issue 7654 + immutable(char[]) s7654; + Rebindable!(typeof(s7654)) r7654 = s7654; + + foreach (T; TypeTuple!(char, wchar, char, int)) + { + static assert(is(Rebindable!(immutable(T[])) == immutable(T)[])); + static assert(is(Rebindable!(const(T[])) == const(T)[])); + static assert(is(Rebindable!(T[]) == T[])); + } + + // Issue 12046 + static assert(!__traits(compiles, Rebindable!(int[1]))); + static assert(!__traits(compiles, Rebindable!(const int[1]))); } /** @@ -1157,38 +1304,6 @@ unittest static assert(passNormalY || passAbnormalY && double.alignof <= int.alignof); } -/*--* -First-class reference type -*/ -struct Ref(T) -{ - private T * _p; - this(ref T value) { _p = &value; } - ref T opDot() { return *_p; } - /*ref*/ T opImplicitCastTo() { return *_p; } - @property ref T value() { return *_p; } - - void opAssign(T value) - { - *_p = value; - } - void opAssign(T * value) - { - _p = value; - } -} - -unittest -{ - Ref!(int) x; - int y = 42; - x = &y; - assert(x.value == 42); - x = 5; - assert(x.value == 5); - assert(y == 5); -} - /** Defines a value paired with a distinctive "null" state that denotes the absence of a value. If default constructed, a $(D @@ -1223,7 +1338,7 @@ Constructor initializing $(D this) with $(D value). /** Returns $(D true) if and only if $(D this) is in the null state. */ - @property bool isNull() const pure nothrow @safe + @property bool isNull() const @safe pure nothrow { return _isNull; } @@ -1251,7 +1366,7 @@ succeeds, $(D this) becomes non-null. Gets the value. $(D this) must not be in the null state. This function is also called for the implicit conversion to $(D T). */ - @property ref inout(T) get() inout pure nothrow @safe + @property ref inout(T) get() inout @safe pure nothrow { enum message = "Called `get' on null Nullable!" ~ T.stringof ~ "."; assert(!isNull, message); @@ -1326,7 +1441,7 @@ unittest unittest { // Ensure Nullable can be used in pure/nothrow/@safe environment. - function() pure nothrow @safe + function() @safe pure nothrow { Nullable!int n; assert(n.isNull); @@ -1598,7 +1713,7 @@ unittest unittest { // Ensure Nullable can be used in pure/nothrow/@safe environment. - function() pure nothrow @safe + function() @safe pure nothrow { Nullable!(int, int.min) n; assert(n.isNull); @@ -1667,7 +1782,7 @@ struct NullableRef(T) /** Constructor binding $(D this) with $(D value). */ - this(T* value) pure nothrow @safe + this(T* value) @safe pure nothrow { _value = value; } @@ -1675,7 +1790,7 @@ Constructor binding $(D this) with $(D value). /** Binds the internal state to $(D value). */ - void bind(T* value) pure nothrow @safe + void bind(T* value) @safe pure nothrow { _value = value; } @@ -1683,7 +1798,7 @@ Binds the internal state to $(D value). /** Returns $(D true) if and only if $(D this) is in the null state. */ - @property bool isNull() const pure nothrow @safe + @property bool isNull() const @safe pure nothrow { return _value is null; } @@ -1691,7 +1806,7 @@ Returns $(D true) if and only if $(D this) is in the null state. /** Forces $(D this) to the null state. */ - void nullify() pure nothrow @safe + void nullify() @safe pure nothrow { _value = null; } @@ -1711,7 +1826,7 @@ Assigns $(D value) to the internally-held state. Gets the value. $(D this) must not be in the null state. This function is also called for the implicit conversion to $(D T). */ - @property ref inout(T) get() inout pure nothrow @safe + @property ref inout(T) get() inout @safe pure nothrow { enum message = "Called `get' on null NullableRef!" ~ T.stringof ~ "."; assert(!isNull, message); @@ -1762,7 +1877,7 @@ unittest unittest { // Ensure NullableRef can be used in pure/nothrow/@safe environment. - function() pure nothrow @safe + function() @safe pure nothrow { auto storage = new int; *storage = 19902; @@ -1862,11 +1977,7 @@ void main() See_Also: AutoImplement, generateEmptyFunction */ -template BlackHole(Base) -{ - alias AutoImplement!(Base, generateEmptyFunction, isAbstractFunction) - BlackHole; -} +alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction, isAbstractFunction); unittest { @@ -1896,6 +2007,18 @@ unittest assert(c.realValue.isNaN); // NaN c.doSomething(); } + + // Bugzilla 12058 + interface Foo + { + inout(Object) foo() inout; + } + BlackHole!Foo o; + + // Bugzilla 12464 + import std.stream; + import std.typecons; + BlackHole!OutputStream dout; } @@ -1923,18 +2046,10 @@ void main() } -------------------- -BUGS: - Nothrow functions cause program to abort in release mode because the trap is - implemented with $(D assert(0)) for nothrow functions. - See_Also: AutoImplement, generateAssertTrap */ -template WhiteHole(Base) -{ - alias AutoImplement!(Base, generateAssertTrap, isAbstractFunction) - WhiteHole; -} +alias WhiteHole(Base) = AutoImplement!(Base, generateAssertTrap, isAbstractFunction); // / ditto class NotImplementedError : Error @@ -1947,8 +2062,8 @@ class NotImplementedError : Error unittest { + import std.exception : assertThrown; // nothrow - debug // see the BUGS above { interface I_1 { @@ -1956,11 +2071,8 @@ unittest void bar() nothrow; } auto o = new WhiteHole!I_1; - uint trap; - try { o.foo(); } catch (Error e) { ++trap; } - assert(trap == 1); - try { o.bar(); } catch (Error e) { ++trap; } - assert(trap == 2); + assertThrown!NotImplementedError(o.foo()); + assertThrown!NotImplementedError(o.bar()); } // doc example { @@ -2036,10 +2148,7 @@ string generateLogger(C, alias fun)() @property -------------------- // Sees if fun returns something. -template hasValue(alias fun) -{ - enum bool hasValue = !is(ReturnType!(fun) == void); -} +enum bool hasValue(alias fun) = !is(ReturnType!(fun) == void); -------------------- @@ -2068,9 +2177,8 @@ $(UL */ class AutoImplement(Base, alias how, alias what = isAbstractFunction) : Base { - private alias AutoImplement_Helper!( - "autoImplement_helper_", "Base", Base, how, what ) - autoImplement_helper_; + private alias autoImplement_helper_ = + AutoImplement_Helper!("autoImplement_helper_", "Base", Base, how, what); mixin(autoImplement_helper_.code); } @@ -2092,15 +2200,15 @@ private static: { static if (lst.length > 0) { - alias staticFilter!(pred, lst[1 .. $]) tail; + alias tail = staticFilter!(pred, lst[1 .. $]); // static if (pred!(lst[0])) - alias TypeTuple!(lst[0], tail) staticFilter; + alias staticFilter = TypeTuple!(lst[0], tail); else - alias tail staticFilter; + alias staticFilter = tail; } else - alias TypeTuple!() staticFilter; + alias staticFilter = TypeTuple!(); } // Returns function overload sets in the class C, filtered with pred. @@ -2110,19 +2218,19 @@ private static: { static if (names.length > 0) { - alias staticFilter!(pred, MemberFunctionsTuple!(C, names[0])) methods; - alias Impl!(names[1 .. $]) next; + alias methods = staticFilter!(pred, MemberFunctionsTuple!(C, names[0])); + alias next = Impl!(names[1 .. $]); static if (methods.length > 0) - alias TypeTuple!(OverloadSet!(names[0], methods), next) Impl; + alias Impl = TypeTuple!(OverloadSet!(names[0], methods), next); else - alias next Impl; + alias Impl = next; } else - alias TypeTuple!() Impl; + alias Impl = TypeTuple!(); } - alias Impl!(__traits(allMembers, C)) enumerateOverloads; + alias enumerateOverloads = Impl!(__traits(allMembers, C)); } //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// @@ -2130,27 +2238,23 @@ private static: //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// // Add a non-final check to the cherrypickMethod. - template canonicalPicker(fun.../+[BUG 4217]+/) - { - enum bool canonicalPicker = !__traits(isFinalFunction, fun[0]) && - cherrypickMethod!(fun); - } + enum bool canonicalPicker(fun.../+[BUG 4217]+/) = + !__traits(isFinalFunction, fun[0]) && cherrypickMethod!(fun); /* * A tuple of overload sets, each item of which consists of functions to be * implemented by the generated code. */ - alias enumerateOverloads!(Base, canonicalPicker) targetOverloadSets; + alias targetOverloadSets = enumerateOverloads!(Base, canonicalPicker); /* * A tuple of the super class' constructors. Used for forwarding * constructor calls. */ static if (__traits(hasMember, Base, "__ctor")) - alias OverloadSet!("__ctor", __traits(getOverloads, Base, "__ctor")) - ctorOverloadSet; + alias ctorOverloadSet = OverloadSet!("__ctor", __traits(getOverloads, Base, "__ctor")); else - alias OverloadSet!("__ctor") ctorOverloadSet; // empty + alias ctorOverloadSet = OverloadSet!("__ctor"); // empty //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// @@ -2195,10 +2299,9 @@ private static: { mixin populate!(name, methods[0 .. $ - 1]); // - alias methods[$ - 1] target; + alias target = methods[$ - 1]; enum ith = methods.length - 1; - mixin( "alias FuncInfo!(target) " ~ - INTERNAL_FUNCINFO_ID!(name, ith) ~ ";" ); + mixin("alias " ~ INTERNAL_FUNCINFO_ID!(name, ith) ~ " = FuncInfo!target;"); } } @@ -2262,10 +2365,8 @@ private static: // Generated code //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::// - alias MemberFunctionGenerator!( ConstructorGeneratingPolicy!() ) - ConstructorGenerator; - alias MemberFunctionGenerator!( MethodGeneratingPolicy!() ) - MethodGenerator; + alias ConstructorGenerator = MemberFunctionGenerator!(ConstructorGeneratingPolicy!()); + alias MethodGenerator = MemberFunctionGenerator!(MethodGeneratingPolicy!()); public enum string code = ConstructorGenerator.generateCode!( ctorOverloadSet ) ~ "\n" ~ @@ -2280,9 +2381,9 @@ private static: } //debug = SHOW_GENERATED_CODE; -version(unittest) import core.vararg; unittest { + import core.vararg; // no function to implement { interface I_1 {} @@ -2404,7 +2505,7 @@ Used by MemberFunctionGenerator. package template OverloadSet(string nam, T...) { enum string name = nam; - alias T contents; + alias contents = T; } /* @@ -2412,13 +2513,13 @@ Used by MemberFunctionGenerator. */ package template FuncInfo(alias func, /+[BUG 4217 ?]+/ T = typeof(&func)) { - alias ReturnType!(T) RT; - alias ParameterTypeTuple!(T) PT; + alias RT = ReturnType!T; + alias PT = ParameterTypeTuple!T; } package template FuncInfo(Func) { - alias ReturnType!(Func) RT; - alias ParameterTypeTuple!(Func) PT; + alias RT = ReturnType!Func; + alias PT = ParameterTypeTuple!Func; } /* @@ -2466,24 +2567,21 @@ private static: // preferred identifier for i-th parameter variable static if (__traits(hasMember, Policy, "PARAMETER_VARIABLE_ID")) { - alias Policy.PARAMETER_VARIABLE_ID PARAMETER_VARIABLE_ID; + alias PARAMETER_VARIABLE_ID = Policy.PARAMETER_VARIABLE_ID; } else { - template PARAMETER_VARIABLE_ID(size_t i) - { - enum string PARAMETER_VARIABLE_ID = format("a%s", i); - // default: a0, a1, ... - } + enum string PARAMETER_VARIABLE_ID(size_t i) = format("a%s", i); + // default: a0, a1, ... } // Returns a tuple consisting of 0,1,2,...,n-1. For static foreach. template CountUp(size_t n) { static if (n > 0) - alias TypeTuple!(CountUp!(n - 1), n - 1) CountUp; + alias CountUp = TypeTuple!(CountUp!(n - 1), n - 1); else - alias TypeTuple!() CountUp; + alias CountUp = TypeTuple!(); } @@ -2503,7 +2601,7 @@ private static: foreach (i_; CountUp!(0 + overloads.length)) // workaround { enum i = 0 + i_; // workaround - alias overloads[i] oset; + alias oset = overloads[i]; code ~= generateCodeForOverloadSet!(oset); @@ -2512,9 +2610,10 @@ private static: // The generated function declarations may hide existing ones // in the base class (cf. HiddenFuncError), so we put an alias // declaration here to reveal possible hidden functions. - code ~= format("alias %s.%s %s;\n", + code ~= format("alias %s = %s.%s;\n", + oset.name, Policy.BASE_CLASS_ID, // [BUG 2540] super. - oset.name, oset.name ); + oset.name); } } return code; @@ -2549,10 +2648,13 @@ private static: string code; // the result + auto paramsRes = generateParameters!(myFuncInfo, func)(); + code ~= paramsRes.imports; + /*** Function Declarator ***/ { - alias FunctionTypeOf!(func) Func; - alias FunctionAttribute FA; + alias Func = FunctionTypeOf!(func); + alias FA = FunctionAttribute; enum atts = functionAttributes!(func); enum realName = isCtor ? "this" : name; @@ -2593,6 +2695,7 @@ private static: string postc = ""; if (is(Func == shared)) postc ~= " shared"; if (is(Func == const)) postc ~= " const"; + if (is(Func == inout)) postc ~= " inout"; if (is(Func == immutable)) postc ~= " immutable"; return postc; } @@ -2605,7 +2708,7 @@ private static: functionLinkage!(func), returnType, realName, - generateParameters!(myFuncInfo, func)(), + paramsRes.params, postAtts, storageClass ); } @@ -2617,10 +2720,10 @@ private static: /* Declare keywords: args, self and parent. */ string preamble; - preamble ~= "alias TypeTuple!(" ~ enumerateParameters!(nparams) ~ ") args;\n"; + preamble ~= "alias args = TypeTuple!(" ~ enumerateParameters!(nparams) ~ ");\n"; if (!isCtor) { - preamble ~= "alias " ~ name ~ " self;\n"; + preamble ~= "alias self = " ~ name ~ ";\n"; if (WITH_BASE_CLASS && !__traits(isAbstractFunction, func)) //preamble ~= "alias super." ~ name ~ " parent;\n"; // [BUG 2540] preamble ~= "auto parent = &super." ~ name ~ ";\n"; @@ -2641,16 +2744,19 @@ private static: } /* - * Returns D code which declares function parameters. + * Returns D code which declares function parameters, + * and optionally any imports (e.g. core.vararg) * "ref int a0, real a1, ..." */ - private string generateParameters(string myFuncInfo, func...)() + static struct GenParams { string imports, params; } + private GenParams generateParameters(string myFuncInfo, func...)() { - alias ParameterStorageClass STC; - alias ParameterStorageClassTuple!(func) stcs; + alias STC = ParameterStorageClass; + alias stcs = ParameterStorageClassTuple!(func); enum nparams = stcs.length; - string params = ""; // the result + string imports = ""; // any imports required + string params = ""; // parameters foreach (i, stc; stcs) { @@ -2670,12 +2776,14 @@ private static: } // Add some ellipsis part if needed. - final switch (variadicFunctionStyle!(func)) + auto style = variadicFunctionStyle!(func); + final switch (style) { case Variadic.no: break; case Variadic.c, Variadic.d: + imports ~= "import core.vararg;\n"; // (...) or (a, b, ...) params ~= (nparams == 0) ? "..." : ", ..."; break; @@ -2685,7 +2793,7 @@ private static: break; } - return params; + return typeof(return)(imports, params); } // Returns D code which enumerates n parameter variables using comma as the @@ -2726,21 +2834,11 @@ template generateEmptyFunction(C, func.../+[BUG 4217]+/) } /// ditto -template generateAssertTrap(C, func.../+[BUG 4217]+/) +template generateAssertTrap(C, func...) { - static if (functionAttributes!(func) & FunctionAttribute.nothrow_) //XXX - { - pragma(msg, "Warning: WhiteHole!(", C, ") used assert(0) instead " - "of Error for the auto-implemented nothrow function ", - C, ".", __traits(identifier, func)); - enum string generateAssertTrap = - `assert(0, "` ~ C.stringof ~ "." ~ __traits(identifier, func) - ~ ` is not implemented");`; - } - else - enum string generateAssertTrap = - `throw new NotImplementedError("` ~ C.stringof ~ "." - ~ __traits(identifier, func) ~ `");`; + enum string generateAssertTrap = + `throw new NotImplementedError("` ~ C.stringof ~ "." + ~ __traits(identifier, func) ~ `");`; } private @@ -2934,7 +3032,7 @@ if (Targets.length >= 1 && allSatisfy!(isMutable, Targets)) } static @property mod() { - alias TypeTuple!(TargetMembers[i].type)[0] type; + alias type = TypeTuple!(TargetMembers[i].type)[0]; string r; static if (is(type == immutable)) r ~= " immutable"; else @@ -3409,7 +3507,7 @@ unittest alias int F2() pure nothrow; static assert(is(DerivedFunctionType!(F1, F2) == F2)); alias int F3() @safe; - alias int F23() pure nothrow @safe; + alias int F23() @safe pure nothrow; static assert(is(DerivedFunctionType!(F2, F3) == F23)); // return type covariance @@ -3452,17 +3550,17 @@ unittest static assert(is(DerivedFunctionType!(F17, F18) == void)); } -private template staticIota(int beg, int end) +package template staticIota(int beg, int end) { static if (beg + 1 >= end) { static if (beg >= end) { - alias TypeTuple!() staticIota; + alias staticIota = TypeTuple!(); } else { - alias TypeTuple!(+beg) staticIota; + alias staticIota = TypeTuple!(+beg); } } else @@ -3482,7 +3580,7 @@ private template mixinAll(mixins...) } else { - alias mixins[0] it; + alias it = mixins[0]; mixin it; } } @@ -3495,10 +3593,7 @@ private template mixinAll(mixins...) private template Bind(alias Template, args1...) { - template Bind(args2...) - { - alias Bind = Template!(args1, args2); - } + alias Bind(args2...) = Template!(args1, args2); } @@ -3797,7 +3892,7 @@ unittest U u; } - alias RefCounted!S SRC; + alias SRC = RefCounted!S; } // 6436 @@ -3947,6 +4042,23 @@ mixin template Proxy(alias a) } } } + + static if (isArray!(typeof(a))) + { + auto opDollar() const { return a.length; } + } + else static if (is(typeof(a.opDollar!0))) + { + auto ref opDollar(size_t pos)() { return a.opDollar!pos(); } + } + else static if (is(typeof(a.opDollar) == function)) + { + auto ref opDollar() { return a.opDollar(); } + } + else static if (is(typeof(a.opDollar))) + { + alias opDollar = a.opDollar; + } } unittest { @@ -4154,15 +4266,53 @@ unittest } /** -Library typedef. - */ -template Typedef(T) -{ - alias .Typedef!(T, T.init) Typedef; -} +$(B Typedef) allows the creation of a unique type which is +based on an existing type. Unlike the $(D alias) feature, +$(B Typedef) ensures the two types are not considered as equals. -/// ditto -struct Typedef(T, T init, string cookie=null) +Example: +---- +alias MyInt = Typedef!int; +static void takeInt(int) { } +static void takeMyInt(MyInt) { } + +int i; +takeInt(i); // ok +takeMyInt(i); // fails + +MyInt myInt; +takeInt(myInt); // fails +takeMyInt(myInt); // ok +---- + +Params: + +init = Optional initial value for the new type. For example: + +---- +alias MyInt = Typedef!(int, 10); +MyInt myInt; +assert(myInt == 10); // default-initialized to 10 +---- + +cookie = Optional, used to create multiple unique types which are +based on the same origin type $(D T). For example: + +---- +alias TypeInt1 = Typedef!int; +alias TypeInt2 = Typedef!int; + +// The two Typedefs are the same type. +static assert(is(TypeInt1 == TypeInt2)); + +alias TypeFloat1 = Typedef!(float, float.init, "a"); +alias TypeFloat2 = Typedef!(float, float.init, "b"); + +// The two Typedefs are _not_ the same type. +static assert(!is(TypeFloat1 == TypeFloat2)); +---- + */ +struct Typedef(T, T init = T.init, string cookie=null) { private T Typedef_payload = init; @@ -4171,6 +4321,11 @@ struct Typedef(T, T init, string cookie=null) Typedef_payload = init; } + this(Typedef tdef) + { + this(tdef.Typedef_payload); + } + mixin Proxy!Typedef_payload; } @@ -4190,13 +4345,50 @@ unittest static assert(typeof(z).init == 1.0); - alias Typedef!(int, 0, "dollar") Dollar; - alias Typedef!(int, 0, "yen") Yen; + alias Dollar = Typedef!(int, 0, "dollar"); + alias Yen = Typedef!(int, 0, "yen"); static assert(!is(Dollar == Yen)); Typedef!(int[3]) sa; static assert(sa.length == 3); static assert(typeof(sa).length == 3); + + Typedef!(int[3]) dollar1; + assert(dollar1[0..$] is dollar1[0..3]); + + Typedef!(int[]) dollar2; + dollar2.length = 3; + assert(dollar2[0..$] is dollar2[0..3]); + + static struct Dollar1 + { + static struct DollarToken {} + enum opDollar = DollarToken.init; + auto opSlice(size_t, DollarToken) { return 1; } + auto opSlice(size_t, size_t) { return 2; } + } + + Typedef!Dollar1 drange1; + assert(drange1[0..$] == 1); + assert(drange1[0..1] == 2); + + static struct Dollar2 + { + size_t opDollar(size_t pos)() { return pos == 0 ? 1 : 100; } + size_t opIndex(size_t i, size_t j) { return i + j; } + } + + Typedef!Dollar2 drange2; + assert(drange2[$, $] == 101); + + static struct Dollar3 + { + size_t opDollar() { return 123; } + size_t opIndex(size_t i) { return i; } + } + + Typedef!Dollar3 drange3; + assert(drange3[$] == 123); } unittest @@ -4206,7 +4398,7 @@ unittest import std.bitmanip; static import core.stdc.config; - alias Typedef!(core.stdc.config.c_ulong) c_ulong; + alias c_ulong = Typedef!(core.stdc.config.c_ulong); static struct Foo { @@ -4217,6 +4409,14 @@ unittest } } +unittest // Issue 12596 +{ + import std.typecons; + alias TD = Typedef!int; + TD x = TD(1); + TD y = TD(x); + assert(x == y); +} /** Allocates a $(D class) object right inside the current scope, @@ -4232,8 +4432,8 @@ template scoped(T) { // _d_newclass now use default GC alignment (looks like (void*).sizeof * 2 for // small objects). We will just use the maximum of filed alignments. - alias classInstanceAlignment!T alignment; - alias _alignUp!alignment aligned; + alias alignment = classInstanceAlignment!T; + alias aligned = _alignUp!alignment; static struct Scoped { diff --git a/libphobos/src/std/typetuple.d b/libphobos/src/std/typetuple.d index 98ef0d30a..a6a5523db 100644 --- a/libphobos/src/std/typetuple.d +++ b/libphobos/src/std/typetuple.d @@ -39,7 +39,7 @@ module std.typetuple; */ template TypeTuple(TList...) { - alias TList TypeTuple; + alias TypeTuple = TList; } /// @@ -97,13 +97,13 @@ unittest private template genericIndexOf(args...) if (args.length >= 1) { - alias Alias!(args[0]) e; - alias args[1 .. $] tuple; + alias e = Alias!(args[0]); + alias tuple = args[1 .. $]; static if (tuple.length) { - alias Alias!(tuple[0]) head; - alias tuple[1 .. $] tail; + alias head = Alias!(tuple[0]); + alias tail = tuple[1 .. $]; static if (isSame!(e, head)) { @@ -144,7 +144,7 @@ unittest } /// Kept for backwards compatibility -alias staticIndexOf IndexOf; +alias IndexOf = staticIndexOf; /** * Returns a typetuple created from TList with the first occurrence, @@ -152,13 +152,13 @@ alias staticIndexOf IndexOf; */ template Erase(T, TList...) { - alias GenericErase!(T, TList).result Erase; + alias Erase = GenericErase!(T, TList).result; } /// Ditto template Erase(alias T, TList...) { - alias GenericErase!(T, TList).result Erase; + alias Erase = GenericErase!(T, TList).result; } /// @@ -173,22 +173,22 @@ unittest private template GenericErase(args...) if (args.length >= 1) { - alias Alias!(args[0]) e; - alias args[1 .. $] tuple; + alias e = Alias!(args[0]); + alias tuple = args[1 .. $] ; static if (tuple.length) { - alias Alias!(tuple[0]) head; - alias tuple[1 .. $] tail; + alias head = Alias!(tuple[0]); + alias tail = tuple[1 .. $]; static if (isSame!(e, head)) - alias tail result; + alias result = tail; else - alias TypeTuple!(head, GenericErase!(e, tail).result) result; + alias result = TypeTuple!(head, GenericErase!(e, tail).result); } else { - alias TypeTuple!() result; + alias result = TypeTuple!(); } } @@ -210,13 +210,13 @@ unittest */ template EraseAll(T, TList...) { - alias GenericEraseAll!(T, TList).result EraseAll; + alias EraseAll = GenericEraseAll!(T, TList).result; } /// Ditto template EraseAll(alias T, TList...) { - alias GenericEraseAll!(T, TList).result EraseAll; + alias EraseAll = GenericEraseAll!(T, TList).result; } /// @@ -232,23 +232,23 @@ unittest private template GenericEraseAll(args...) if (args.length >= 1) { - alias Alias!(args[0]) e; - alias args[1 .. $] tuple; + alias e = Alias!(args[0]); + alias tuple = args[1 .. $]; static if (tuple.length) { - alias Alias!(tuple[0]) head; - alias tuple[1 .. $] tail; - alias GenericEraseAll!(e, tail).result next; + alias head = Alias!(tuple[0]); + alias tail = tuple[1 .. $]; + alias next = GenericEraseAll!(e, tail).result; static if (isSame!(e, head)) - alias next result; + alias result = next; else - alias TypeTuple!(head, next) result; + alias result = TypeTuple!(head, next); } else { - alias TypeTuple!() result; + alias result = TypeTuple!(); } } @@ -271,9 +271,10 @@ unittest template NoDuplicates(TList...) { static if (TList.length == 0) - alias TList NoDuplicates; + alias NoDuplicates = TList; else - alias TypeTuple!(TList[0], NoDuplicates!(EraseAll!(TList[0], TList[1 .. $]))) NoDuplicates; + alias NoDuplicates = + TypeTuple!(TList[0], NoDuplicates!(EraseAll!(TList[0], TList[1 .. $]))); } /// @@ -300,25 +301,25 @@ unittest */ template Replace(T, U, TList...) { - alias GenericReplace!(T, U, TList).result Replace; + alias Replace = GenericReplace!(T, U, TList).result; } /// Ditto template Replace(alias T, U, TList...) { - alias GenericReplace!(T, U, TList).result Replace; + alias Replace = GenericReplace!(T, U, TList).result; } /// Ditto template Replace(T, alias U, TList...) { - alias GenericReplace!(T, U, TList).result Replace; + alias Replace = GenericReplace!(T, U, TList).result; } /// Ditto template Replace(alias T, alias U, TList...) { - alias GenericReplace!(T, U, TList).result Replace; + alias Replace = GenericReplace!(T, U, TList).result; } /// @@ -334,24 +335,24 @@ unittest private template GenericReplace(args...) if (args.length >= 2) { - alias Alias!(args[0]) from; - alias Alias!(args[1]) to; - alias args[2 .. $] tuple; + alias from = Alias!(args[0]); + alias to = Alias!(args[1]); + alias tuple = args[2 .. $]; static if (tuple.length) { - alias Alias!(tuple[0]) head; - alias tuple[1 .. $] tail; + alias head = Alias!(tuple[0]); + alias tail = tuple[1 .. $]; static if (isSame!(from, head)) - alias TypeTuple!(to, tail) result; + alias result = TypeTuple!(to, tail); else - alias TypeTuple!(head, - GenericReplace!(from, to, tail).result) result; + alias result = TypeTuple!(head, + GenericReplace!(from, to, tail).result); } else { - alias TypeTuple!() result; + alias result = TypeTuple!(); } } @@ -380,25 +381,25 @@ unittest */ template ReplaceAll(T, U, TList...) { - alias GenericReplaceAll!(T, U, TList).result ReplaceAll; + alias ReplaceAll = GenericReplaceAll!(T, U, TList).result; } /// Ditto template ReplaceAll(alias T, U, TList...) { - alias GenericReplaceAll!(T, U, TList).result ReplaceAll; + alias ReplaceAll = GenericReplaceAll!(T, U, TList).result; } /// Ditto template ReplaceAll(T, alias U, TList...) { - alias GenericReplaceAll!(T, U, TList).result ReplaceAll; + alias ReplaceAll = GenericReplaceAll!(T, U, TList).result; } /// Ditto template ReplaceAll(alias T, alias U, TList...) { - alias GenericReplaceAll!(T, U, TList).result ReplaceAll; + alias ReplaceAll = GenericReplaceAll!(T, U, TList).result; } /// @@ -414,24 +415,24 @@ unittest private template GenericReplaceAll(args...) if (args.length >= 2) { - alias Alias!(args[0]) from; - alias Alias!(args[1]) to; - alias args[2 .. $] tuple; + alias from = Alias!(args[0]); + alias to = Alias!(args[1]); + alias tuple = args[2 .. $]; static if (tuple.length) { - alias Alias!(tuple[0]) head; - alias tuple[1 .. $] tail; - alias GenericReplaceAll!(from, to, tail).result next; + alias head = Alias!(tuple[0]); + alias tail = tuple[1 .. $]; + alias next = GenericReplaceAll!(from, to, tail).result; static if (isSame!(from, head)) - alias TypeTuple!(to, next) result; + alias result = TypeTuple!(to, next); else - alias TypeTuple!(head, next) result; + alias result = TypeTuple!(head, next); } else { - alias TypeTuple!() result; + alias result = TypeTuple!(); } } @@ -488,11 +489,11 @@ unittest template MostDerived(T, TList...) { static if (TList.length == 0) - alias T MostDerived; + alias MostDerived = T; else static if (is(TList[0] : T)) - alias MostDerived!(TList[0], TList[1 .. $]) MostDerived; + alias MostDerived = MostDerived!(TList[0], TList[1 .. $]); else - alias MostDerived!(T, TList[1 .. $]) MostDerived; + alias MostDerived = MostDerived!(T, TList[1 .. $]); } /// @@ -514,12 +515,13 @@ unittest template DerivedToFront(TList...) { static if (TList.length == 0) - alias TList DerivedToFront; + alias DerivedToFront = TList; else - alias TypeTuple!(MostDerived!(TList[0], TList[1 .. $]), - DerivedToFront!(ReplaceAll!(MostDerived!(TList[0], TList[1 .. $]), - TList[0], - TList[1 .. $]))) DerivedToFront; + alias DerivedToFront = + TypeTuple!(MostDerived!(TList[0], TList[1 .. $]), + DerivedToFront!(ReplaceAll!(MostDerived!(TList[0], TList[1 .. $]), + TList[0], + TList[1 .. $]))); } /// @@ -569,14 +571,14 @@ unittest import std.traits : Unqual; // empty - alias staticMap!(Unqual) Empty; + alias Empty = staticMap!(Unqual); static assert(Empty.length == 0); // single - alias staticMap!(Unqual, const int) Single; + alias Single = staticMap!(Unqual, const int); static assert(is(Single == TypeTuple!int)); - alias staticMap!(Unqual, int, const int, immutable int) T; + alias T = staticMap!(Unqual, int, const int, immutable int); static assert(is(T == TypeTuple!(int, int, int))); } @@ -723,10 +725,7 @@ private version (unittest) */ template templateNot(alias pred) { - template templateNot(T...) - { - enum templateNot = !pred!T; - } + enum templateNot(T...) = !pred!T; } /// @@ -769,7 +768,7 @@ template templateAnd(Preds...) else { static if (Instantiate!(Preds[0], T)) - alias Instantiate!(.templateAnd!(Preds[1 .. $]), T) templateAnd; + alias templateAnd = Instantiate!(.templateAnd!(Preds[1 .. $]), T); else enum templateAnd = false; } @@ -829,7 +828,7 @@ template templateOr(Preds...) static if (Instantiate!(Preds[0], T)) enum templateOr = true; else - alias Instantiate!(.templateOr!(Preds[1 .. $]), T) templateOr; + alias templateOr = Instantiate!(.templateOr!(Preds[1 .. $]), T); } } } @@ -880,8 +879,8 @@ package: // symbols and literal values template Alias(alias a) { - static if (__traits(compiles, { alias a x; })) - alias a Alias; + static if (__traits(compiles, { alias x = a; })) + alias Alias = a; else static if (__traits(compiles, { enum x = a; })) enum Alias = a; else @@ -890,16 +889,16 @@ template Alias(alias a) // types and tuples template Alias(a...) { - alias a Alias; + alias Alias = a; } unittest { enum abc = 1; - static assert(__traits(compiles, { alias Alias!(123) a; })); - static assert(__traits(compiles, { alias Alias!(abc) a; })); - static assert(__traits(compiles, { alias Alias!(int) a; })); - static assert(__traits(compiles, { alias Alias!(1,abc,int) a; })); + static assert(__traits(compiles, { alias a = Alias!(123); })); + static assert(__traits(compiles, { alias a = Alias!(abc); })); + static assert(__traits(compiles, { alias a = Alias!(int); })); + static assert(__traits(compiles, { alias a = Alias!(1,abc,int); })); } @@ -993,7 +992,7 @@ unittest */ private template Pack(T...) { - alias T tuple; + alias tuple = T; // For convenience template equals(U...) @@ -1028,7 +1027,4 @@ unittest */ // TODO: Consider publicly exposing this, maybe even if only for better // understandability of error messages. -template Instantiate(alias Template, Params...) -{ - alias Template!Params Instantiate; -} +alias Instantiate(alias Template, Params...) = Template!Params; diff --git a/libphobos/src/std/uni.d b/libphobos/src/std/uni.d index caee1872c..e31547d1a 100644 --- a/libphobos/src/std/uni.d +++ b/libphobos/src/std/uni.d @@ -65,6 +65,17 @@ functions like $(LREF isAlpha) and $(LREF combiningClass), but for user-defined data sets. ) + $(LI + A useful technique for Unicode-aware parsers that perform + character classification of encoded $(CODEPOINTS) + is to avoid unnecassary decoding at all costs. + $(LREF utfMatcher) provides an improvement over the usual workflow + of decode-classify-process, combining the decoding and classification + steps. By extracting necessary bits directly from encoded + $(S_LINK Code unit, code units) matchers achieve + significant performance improvements. See $(LREF MatcherConcept) for + the common interface of UTF matchers. + ) $(LI Generally useful building blocks for customized normalization: $(LREF combiningClass) for querying combining class @@ -659,14 +670,14 @@ else import std.internal.unicode_tables; // generated file } -void copyBackwards(T)(T[] src, T[] dest) +void copyBackwards(T,U)(T[] src, U[] dest) { assert(src.length == dest.length); for(size_t i=src.length; i-- > 0; ) dest[i] = src[i]; } -void copyForward(T)(T[] src, T[] dest) +void copyForward(T,U)(T[] src, U[] dest) { assert(src.length == dest.length); for(size_t i=0; i= e) + { + foreach (i; s..e) + if(ptr[i]) + return false; + return true; + } + size_t pad_e = roundDown(e); + size_t i; + for(i=s; i= end) //rounded up >= then end of slice { //nothing to gain, use per element assignment @@ -1295,7 +1330,7 @@ pure nothrow: ptr[i] = val; return; } - size_t pad_end = end/factor*factor; // rounded down + size_t pad_end = roundDown(end); // rounded down size_t i; for(i=start; i ", b)); + } + } + body + { + InversionList set; + set.data = CowArray!(SP)(intervals); + return set; } /** @@ -1979,15 +2056,15 @@ public: in { assert(intervals.length % 2 == 0, "Odd number of interval bounds [a, b)!"); - for(uint i=0; i ", b)); } } body { - data = Uint24Array!(SP)(intervals); + data = CowArray!(SP)(intervals); sanitize(); //enforce invariant: sort intervals etc. } @@ -2025,6 +2102,19 @@ public: return sharSwitchLowerBound!"a<=b"(data[], val) & 1; } + // Linear scan for $(D ch). Useful only for small sets. + // TODO: + // used internally in std.regex + // should be properly exposed in a public API ? + package auto scanFor()(dchar ch) const + { + immutable len = data.length; + for(size_t i = 0; i < len; i++) + if(ch < data[i]) + return i & 1; + return 0; + } + /// Number of $(CODEPOINTS) in this set @property size_t length() { @@ -2228,18 +2318,68 @@ public: assert(unicode.ASCII.to!string == "[0..128$(RPAREN)"); --- */ - void toString(scope void delegate (const(char)[]) sink) + + private import std.format : FormatException, FormatSpec; + + /*************************************** + * Obtain a textual representation of this InversionList + * in form of open-right intervals. + * + * The formatting flag is applied individually to each value, for example: + * $(LI $(B %s) and $(B %d) format the intervals as a [low..high$(RPAREN) range of integrals) + * $(LI $(B %x) formats the intervals as a [low..high$(RPAREN) range of lowercase hex characters) + * $(LI $(B %X) formats the intervals as a [low..high$(RPAREN) range of uppercase hex characters) + */ + void toString(scope void delegate(const(char)[]) sink, + FormatSpec!char fmt) /* const */ { import std.format; auto range = byInterval; if(range.empty) return; - auto val = range.front; - formattedWrite(sink, "[%d..%d)", val.a, val.b); - range.popFront(); - foreach(i; range) - formattedWrite(sink, " [%d..%d)", i.a, i.b); + + while (1) + { + auto i = range.front; + range.popFront(); + + put(sink, "["); + formatValue(sink, i.a, fmt); + put(sink, ".."); + formatValue(sink, i.b, fmt); + put(sink, ")"); + if (range.empty) return; + put(sink, " "); + } } + + /// + unittest + { + import std.conv : to; + import std.string : format; + import std.uni : unicode; + + assert(unicode.Cyrillic.to!string == + "[1024..1157) [1159..1320) [7467..7468) [7544..7545) [11744..11776) [42560..42648) [42655..42656)"); + + // The specs '%s' and '%d' are equivalent to the to!string call above. + assert(format("%d", unicode.Cyrillic) == unicode.Cyrillic.to!string); + + assert(format("%#x", unicode.Cyrillic) == + "[0x400..0x485) [0x487..0x528) [0x1d2b..0x1d2c) [0x1d78..0x1d79) [0x2de0..0x2e00) [0xa640..0xa698) [0xa69f..0xa6a0)"); + + assert(format("%#X", unicode.Cyrillic) == + "[0X400..0X485) [0X487..0X528) [0X1D2B..0X1D2C) [0X1D78..0X1D79) [0X2DE0..0X2E00) [0XA640..0XA698) [0XA69F..0XA6A0)"); + } + + unittest + { + import std.string : format; + assertThrown!FormatException(format("%a", unicode.ASCII)); + } + + /** Add an interval [a, b$(RPAREN) to this set. @@ -2376,7 +2516,7 @@ public: --- import std.stdio; - // construct set directly from [a, b) intervals + // construct set directly from [a, b$RPAREN intervals auto set = CodepointSet(10, 12, 45, 65, 100, 200); writeln(set); writeln(set.toSourceCode("func")); @@ -2509,8 +2649,8 @@ public: } private: - alias typeof(this) This; - alias size_t Marker; + alias This = typeof(this); + alias Marker = size_t; // a random-access range of integral pairs static struct Intervals(Range) @@ -2606,7 +2746,8 @@ private: alias Ival = CodepointInterval; //intervals wrapper for a _range_ over packed array auto ivals = Intervals!(typeof(data[]))(data[]); - sort!("a.a < b.a", SwapStrategy.stable)(ivals); + //@@@BUG@@@ can't use "a.a < b.a" see issue 12265 + sort!((a,b) => a.a < b.a, SwapStrategy.stable)(ivals); // what follows is a variation on stable remove // differences: // - predicate is binary, and is tested against @@ -2658,22 +2799,23 @@ private: Marker addInterval(int a, int b, Marker hint=Marker.init) in { - assert(a <= b, text(a, " > ", b)); + assert(a <= b); } body { auto range = assumeSorted(data[]); size_t pos; - size_t a_idx = range.lowerBound(a).length; + size_t a_idx = hint + range[hint..$].lowerBound!(SearchPolicy.gallop)(a).length; if(a_idx == range.length) { // [---+++----++++----++++++] // [ a b] - data.append([a, b]); + data.append(a, b); return data.length-1; } - size_t b_idx = range[a_idx..range.length].lowerBound(b).length+a_idx; - uint[] to_insert; + size_t b_idx = range[a_idx..range.length].lowerBound!(SearchPolicy.gallop)(b).length+a_idx; + uint[3] buf = void; + uint to_insert; debug(std_uni) { writefln("a_idx=%d; b_idx=%d;", a_idx, b_idx); @@ -2684,14 +2826,17 @@ private: // [ s a b] if(a_idx & 1)// a in positive { - to_insert = [ b ]; + buf[0] = b; + to_insert = 1; } else// a in negative { - to_insert = [a, b]; + buf[0] = a; + buf[1] = b; + to_insert = 2; } - genericReplace(data, a_idx, b_idx, to_insert); - return a_idx+to_insert.length-1; + pos = genericReplace(data, a_idx, b_idx, buf[0..to_insert]); + return pos - 1; } uint top = data[b_idx]; @@ -2707,7 +2852,8 @@ private: { // [-------++++++++----++++++-] // [ s a b ] - to_insert = [top]; + buf[0] = top; + to_insert = 1; } else // b in negative { @@ -2716,10 +2862,13 @@ private: if(top == b) { assert(b_idx+1 < data.length); - pos = genericReplace(data, a_idx, b_idx+2, [data[b_idx+1]]); - return pos; + buf[0] = data[b_idx+1]; + pos = genericReplace(data, a_idx, b_idx+2, buf[0..1]); + return pos - 1; } - to_insert = [b, top ]; + buf[0] = b; + buf[1] = top; + to_insert = 2; } } else @@ -2728,7 +2877,9 @@ private: { // [----------+++++----++++++-] // [ a b ] - to_insert = [a, top]; + buf[0] = a; + buf[1] = top; + to_insert = 2; } else// b in negative { @@ -2737,19 +2888,24 @@ private: if(top == b) { assert(b_idx+1 < data.length); - pos = genericReplace(data, a_idx, b_idx+2, [a, data[b_idx+1] ]); - return pos; + buf[0] = a; + buf[1] = data[b_idx+1]; + pos = genericReplace(data, a_idx, b_idx+2, buf[0..2]); + return pos - 1; } - to_insert = [a, b, top]; + buf[0] = a; + buf[1] = b; + buf[2] = top; + to_insert = 3; } } - pos = genericReplace(data, a_idx, b_idx+1, to_insert); + pos = genericReplace(data, a_idx, b_idx+1, buf[0..to_insert]); debug(std_uni) { writefln("marker idx: %d; length=%d", pos, data[pos], data.length); - writeln("inserting ", to_insert); + writeln("inserting ", buf[0..to_insert]); } - return pos; + return pos - 1; } // @@ -2820,7 +2976,7 @@ private: return idx; } - Uint24Array!SP data; + CowArray!SP data; }; @system unittest @@ -2950,15 +3106,23 @@ void write24(ubyte* ptr, uint val, size_t idx) pure nothrow else return safeWrite24(ptr, val, idx); } - -// Packed array of 24-bit integers, COW semantics. -@trusted struct Uint24Array(SP=GcPolicy) +@trusted struct CowArray(SP=GcPolicy) { + static auto reuse(uint[] arr) + { + CowArray cow; + cow.data = arr; + SP.append(cow.data, 1); + assert(cow.refCount == 1); + assert(cow.length == arr.length); + return cow; + } + this(Range)(Range range) if(isInputRange!Range && hasLength!Range) { length = range.length; - copy(range, this[]); + copy(range, data[0..$-1]); } this(Range)(Range range) @@ -2966,7 +3130,7 @@ void write24(ubyte* ptr, uint val, size_t idx) pure nothrow { auto len = walkLength(range.save); length = len; - copy(range, this[]); + copy(range, data[0..$-1]); } this(this) @@ -2995,7 +3159,7 @@ void write24(ubyte* ptr, uint val, size_t idx) pure nothrow // report one less then actual size @property size_t length() const { - return data.length ? (data.length-4)/3 : 0; + return data.length ? data.length - 1 : 0; } //+ an extra slot for ref-count @@ -3007,10 +3171,10 @@ void write24(ubyte* ptr, uint val, size_t idx) pure nothrow freeThisReference(); return; } - immutable bytes = len*3+4; // including ref-count + immutable total = len + 1; // including ref-count if(empty) { - data = SP.alloc!ubyte(bytes); + data = SP.alloc!uint(total); refCount = 1; return; } @@ -3018,9 +3182,9 @@ void write24(ubyte* ptr, uint val, size_t idx) pure nothrow if(cur_cnt != 1) // have more references to this memory { refCount = cur_cnt - 1; - auto new_data = SP.alloc!ubyte(bytes); + auto new_data = SP.alloc!uint(total); // take shrinking into account - auto to_copy = min(bytes, data.length)-4; + auto to_copy = min(total, data.length) - 1; copy(data[0..to_copy], new_data[0..to_copy]); data = new_data; // before setting refCount! refCount = 1; @@ -3028,43 +3192,43 @@ void write24(ubyte* ptr, uint val, size_t idx) pure nothrow else // 'this' is the only reference { // use the realloc (hopefully in-place operation) - data = SP.realloc(data, bytes); + data = SP.realloc(data, total); refCount = 1; // setup a ref-count in the new end of the array } } alias opDollar = length; - // Read 24-bit packed integer - uint opIndex(size_t idx)const + uint opIndex()(size_t idx)const { - return read24(data.ptr, idx); + return data[idx]; } - // Write 24-bit packed integer void opIndexAssign(uint val, size_t idx) - in - { - assert(!empty && val <= 0xFF_FFFF); - } - body { auto cnt = refCount; if(cnt != 1) dupThisReference(cnt); - write24(data.ptr, val, idx); + data[idx] = val; } // auto opSlice(size_t from, size_t to) { - return sliceOverIndexed(from, to, &this); + if(!empty) + { + auto cnt = refCount; + if(cnt != 1) + dupThisReference(cnt); + } + return data[from .. to]; + } - /// + // auto opSlice(size_t from, size_t to) const { - return sliceOverIndexed(from, to, &this); + return data[from .. to]; } // length slices before the ref count @@ -3073,7 +3237,7 @@ void write24(ubyte* ptr, uint val, size_t idx) pure nothrow return opSlice(0, length); } - // length slices before the ref count + // ditto auto opSlice() const { return opSlice(0, length); @@ -3087,34 +3251,29 @@ void write24(ubyte* ptr, uint val, size_t idx) pure nothrow copy(range, this[nl-range.length..nl]); } - void append()(uint val) + void append()(uint[] val...) { - length = length + 1; - this[$-1] = val; + length = length + val.length; + data[$-val.length-1 .. $-1] = val[]; } - bool opEquals()(auto const ref Uint24Array rhs)const + bool opEquals()(auto const ref CowArray rhs)const { if(empty ^ rhs.empty) return false; // one is empty and the other isn't - return empty || data[0..$-4] == rhs.data[0..$-4]; + return empty || data[0..$-1] == rhs.data[0..$-1]; } private: // ref-count is right after the data @property uint refCount() const { - return read24(data.ptr, length); + return data[$-1]; } @property void refCount(uint cnt) - in { - assert(cnt <= 0xFF_FFFF); - } - body - { - write24(data.ptr, cnt, length); + data[$-1] = cnt; } void freeThisReference() @@ -3128,10 +3287,7 @@ private: } else SP.destroy(data); - version(bug10929) - assert(!data.ptr); - else - data = null; + assert(!data.ptr); } void dupThisReference(uint count) @@ -3144,14 +3300,14 @@ private: // dec shared ref-count refCount = count - 1; // copy to the new chunk of RAM - auto new_data = SP.alloc!ubyte(data.length); + auto new_data = SP.alloc!uint(data.length); // bit-blit old stuff except the counter - copy(data[0..$-4], new_data[0..$-4]); + copy(data[0..$-1], new_data[0..$-1]); data = new_data; // before setting refCount! refCount = 1; // so that this updates the right one } - ubyte[] data; + uint[] data; } @trusted unittest// Uint24 tests //@@@BUG@@ iota is system ?! @@ -3202,8 +3358,8 @@ private: foreach(Policy; TypeTuple!(GcPolicy, ReallocPolicy)) { - alias typeof(Uint24Array!Policy.init[]) Range; - alias Uint24Array!Policy U24A; + alias Range = typeof(CowArray!Policy.init[]); + alias U24A = CowArray!Policy; static assert(isForwardRange!Range); static assert(isBidirectionalRange!Range); static assert(isOutputRange!(Range, uint)); @@ -3238,7 +3394,7 @@ private: version(unittest) { - private alias TypeTuple!(InversionList!GcPolicy, InversionList!ReallocPolicy) AllSets; + private alias AllSets = TypeTuple!(InversionList!GcPolicy, InversionList!ReallocPolicy); } @trusted unittest// core set primitives test @@ -3322,7 +3478,7 @@ version(unittest) import std.conv, std.range, std.algorithm; //ensure constructor handles bad ordering and overlap auto c1 = CodepointSet('а', 'я'+1, 'А','Я'+1); - foreach(ch; chain(iota('a', 'я'+1)), iota('А','Я'+1)) + foreach(ch; chain(iota('а', 'я'+1), iota('А','Я'+1))) assert(ch in c1, to!string(ch)); //contiguos @@ -3533,7 +3689,7 @@ template mapTrieIndex(Prefix...) size_t mapTrieIndex(Key)(Key key) if(isValidPrefixForTrie!(Key, Prefix)) { - alias Prefix p; + alias p = Prefix; size_t idx; foreach(i, v; p[0..$-1]) { @@ -3652,7 +3808,7 @@ private: // page at once loop if(state[level].idx_zeros != size_t.max && val == T.init) { - alias typeof(table.slice!(level-1)[0]) NextIdx; + alias NextIdx = typeof(table.slice!(level-1)[0]); addValue!(level-1)(force!NextIdx(state[level].idx_zeros), numVals/pageSize); ptr = table.slice!level; //table structure might have changed @@ -3689,7 +3845,7 @@ private: // it also makes sure that previous levels point to the correct page in this level void spillToNextPageImpl(size_t level, Slice)(ref Slice ptr) { - alias typeof(table.slice!(level-1)[0]) NextIdx; + alias NextIdx = typeof(table.slice!(level-1)[0]); NextIdx next_lvl_index; enum pageSize = 1<value mapping"; public: @@ -3784,8 +3940,8 @@ public: v = ConstructState(size_t.max, size_t.max); table = typeof(table)(indices); // one page per level is a bootstrap minimum - foreach(i; Sequence!(0, Prefix.length)) - table.length!i = (1< 0) - alias TypeTuple!(sliceBits!(top - sizes[0], top) - , GetBitSlicing!(top - sizes[0], sizes[1..$])) GetBitSlicing; + alias GetBitSlicing = + TypeTuple!(sliceBits!(top - sizes[0], top), + GetBitSlicing!(top - sizes[0], sizes[1..$])); else - alias TypeTuple!() GetBitSlicing; + alias GetBitSlicing = TypeTuple!(); } template callableWith(T) @@ -4321,6 +4478,857 @@ public template buildTrie(Value, Key, Args...) } } +// helper in place of assumeSize to +//reduce mangled name & help DMD inline Trie functors +struct clamp(size_t bits) +{ + static size_t opCall(T)(T arg){ return arg; } + enum bitSize = bits; +} + +struct clampIdx(size_t idx, size_t bits) +{ + static size_t opCall(T)(T arg){ return arg[idx]; } + enum bitSize = bits; +} + +/** + Conceptual type that outlines the common properties of all UTF Matchers. + + Note: For illustration purposes only, every method + call results in assertion failure. + Use $(LREF utfMatcher) to obtain a concrete matcher + for UTF-8 or UTF-16 encodings. +*/ +public struct MatcherConcept +{ + /** + $(P Perform a semantic equivalent 2 operations: + decoding a $(CODEPOINT) at front of $(D inp) and testing if + it belongs to the set of $(CODEPOINTS) of this matcher. ) + + $(P The effect on $(D inp) depends on the kind of function called:) + + $(P Match. If the codepoint is found in the set then range $(D inp) + is advanced by its size in $(S_LINK Code unit, code units), + otherwise the range is not modifed.) + + $(P Skip. The range is always advanced by the size + of the tested $(CODEPOINT) regardless of the result of test.) + + $(P Test. The range is left unaffected regardless + of the result of test.) + */ + public bool match(Range)(ref Range inp) + if(isRandomAccessRange!Range && is(ElementType!Range : char)) + { + assert(false); + } + + ///ditto + public bool skip(Range)(ref Range inp) + if(isRandomAccessRange!Range && is(ElementType!Range : char)) + { + assert(false); + } + + ///ditto + public bool test(Range)(ref Range inp) + if(isRandomAccessRange!Range && is(ElementType!Range : char)) + { + assert(false); + } + /// + @safe unittest + { + string truth = "2² = 4"; + auto m = utfMatcher!char(unicode.Number); + assert(m.match(truth)); // '2' is a number all right + assert(truth == "² = 4"); // skips on match + assert(m.match(truth)); // so is the superscript '2' + assert(!m.match(truth)); // space is not a number + assert(truth == " = 4"); // unaffected on no match + assert(!m.skip(truth)); // same test ... + assert(truth == "= 4"); // but skips a codepoint regardless + assert(!m.test(truth)); // '=' is not a number + assert(truth == "= 4"); // test never affects argument + } + + /* + Advanced feature - provide direct access to a subset of matcher based a + set of known encoding lengths. Lengths are provided in + $(S_LINK Code unit, code units). The sub-matcher then may do less + operations per any $(D test)/$(D match). + + Use with care as the sub-matcher won't match + any $(CODEPOINTS) that have encoded length that doesn't belong + to the selected set of lengths. Also the sub-matcher object references + the parent matcher and must not be used past the liftetime + of the latter. + + Another caveat of using sub-matcher is that skip is not available + preciesly because sub-matcher doesn't detect all lengths. + */ + @property auto subMatcher(Lengths...)() + { + assert(0); + return this; + } + + /// + @safe unittest + { + auto m = utfMatcher!char(unicode.Number); + string square = "2²"; + // about sub-matchers + assert(!m.subMatcher!(2,3,4).test(square)); // ASCII no covered + assert(m.subMatcher!1.match(square)); // ASCII-only, works + assert(!m.subMatcher!1.test(square)); // unicode '²' + assert(m.subMatcher!(2,3,4).match(square)); // + assert(square == ""); + wstring wsquare = "2²"; + auto m16 = utfMatcher!wchar(unicode.Number); + // may keep ref, but the orignal (m16) must be kept alive + auto bmp = m16.subMatcher!1; + assert(bmp.match(wsquare)); // Okay, in basic multilingual plan + assert(bmp.match(wsquare)); // And '²' too + } +} + +/** + Test if $(D M) is an UTF Matcher for ranges of $(D Char). +*/ +public enum isUtfMatcher(M, C) = __traits(compiles, (){ + C[] s; + auto d = s.decoder; + M m; + assert(is(typeof(m.match(d)) == bool)); + assert(is(typeof(m.test(d)) == bool)); + static if(is(typeof(m.skip(d)))) + { + assert(is(typeof(m.skip(d)) == bool)); + assert(is(typeof(m.skip(s)) == bool)); + } + assert(is(typeof(m.match(s)) == bool)); + assert(is(typeof(m.test(s)) == bool)); +}); + +unittest +{ + alias CharMatcher = typeof(utfMatcher!char(CodepointSet.init)); + alias WcharMatcher = typeof(utfMatcher!wchar(CodepointSet.init)); + static assert(isUtfMatcher!(CharMatcher, char)); + static assert(isUtfMatcher!(CharMatcher, immutable(char))); + static assert(isUtfMatcher!(WcharMatcher, wchar)); + static assert(isUtfMatcher!(WcharMatcher, immutable(wchar))); +} + +enum Mode { + alwaysSkip, + neverSkip, + skipOnMatch +}; + +mixin template ForwardStrings() +{ + private bool fwdStr(string fn, C)(ref C[] str) const pure + { + alias type = typeof(units(str)); + return mixin(fn~"(*cast(type*)&str)"); + } +} + +template Utf8Matcher() +{ + enum validSize(int sz) = sz >= 1 && sz <=4; + + void badEncoding() pure @safe + { + import std.utf; + throw new UTFException("Invalid UTF-8 sequence"); + } + + //for 1-stage ASCII + alias AsciiSpec = TypeTuple!(bool, char, clamp!7); + //for 2-stage lookup of 2 byte UTF-8 sequences + alias Utf8Spec2 = TypeTuple!(bool, char[2], + clampIdx!(0, 5), clampIdx!(1, 6)); + //ditto for 3 byte + alias Utf8Spec3 = TypeTuple!(bool, char[3], + clampIdx!(0, 4), + clampIdx!(1, 6), + clampIdx!(2, 6) + ); + //ditto for 4 byte + alias Utf8Spec4 = TypeTuple!(bool, char[4], + clampIdx!(0, 3), clampIdx!(1, 6), + clampIdx!(2, 6), clampIdx!(3, 6) + ); + alias Tables = TypeTuple!( + typeof(TrieBuilder!(AsciiSpec)(false).build()), + typeof(TrieBuilder!(Utf8Spec2)(false).build()), + typeof(TrieBuilder!(Utf8Spec3)(false).build()), + typeof(TrieBuilder!(Utf8Spec4)(false).build()) + ); + alias Table(int size) = Tables[size-1]; + + enum leadMask(size_t size) = (cast(size_t)1<<(7 - size))-1; + enum encMask(size_t size) = ((1< 1) + { + import std.utf : encode; + char[4] buf; + std.utf.encode(buf, ch); + char[sz] ret; + buf[0] &= leadMask!sz; + foreach(n; 1..sz) + buf[n] = buf[n] & 0x3f; //keep 6 lower bits + ret[] = buf[0..sz]; + return ret; + } + + auto build(Set)(Set set) + { + auto ascii = set & unicode.ASCII; + auto utf8_2 = set & CodepointSet(0x80, 0x800); + auto utf8_3 = set & CodepointSet(0x800, 0x1_0000); + auto utf8_4 = set & CodepointSet(0x1_0000, lastDchar+1); + auto asciiT = ascii.byCodepoint.map!(x=>cast(char)x).buildTrie!(AsciiSpec); + auto utf8_2T = utf8_2.byCodepoint.map!(x=>encode!2(x)).buildTrie!(Utf8Spec2); + auto utf8_3T = utf8_3.byCodepoint.map!(x=>encode!3(x)).buildTrie!(Utf8Spec3); + auto utf8_4T = utf8_4.byCodepoint.map!(x=>encode!4(x)).buildTrie!(Utf8Spec4); + alias Ret = Impl!(1,2,3,4); + return Ret(asciiT, utf8_2T, utf8_3T, utf8_4T); + } + + // Bootstrap UTF-8 static matcher interface + // from 3 primitives: tab!(size), lookup and Sizes + mixin template DefMatcher() + { + import std.string : format; + enum hasASCII = staticIndexOf!(1, Sizes) >= 0; + alias UniSizes = Erase!(1, Sizes); + + //generate dispatch code sequence for unicode parts + static auto genDispatch() + { + string code; + foreach(size; UniSizes) + code ~= format(q{ + if ((ch & ~leadMask!%d) == encMask!(%d)) + return lookup!(%d, mode)(inp); + else + }, size, size, size); + static if (Sizes.length == 4) //covers all code unit cases + code ~= "{ badEncoding(); return false; }"; + else + code ~= "return false;"; //may be just fine but not covered + return code; + } + enum dispatch = genDispatch(); + + public bool match(Range)(ref Range inp) const pure @trusted + if(isRandomAccessRange!Range && is(ElementType!Range : char)) + { + enum mode = Mode.skipOnMatch; + assert(!inp.empty); + auto ch = inp[0]; + static if(hasASCII) + { + if (ch < 0x80) + { + bool r = tab!1[ch]; + if(r) + inp.popFront(); + return r; + } + else + mixin(dispatch); + } + else + mixin(dispatch); + } + + static if(Sizes.length == 4) // can skip iff can detect all encodings + { + public bool skip(Range)(ref Range inp) const pure @trusted + if(isRandomAccessRange!Range && is(ElementType!Range : char)) + { + enum mode = Mode.alwaysSkip; + assert(!inp.empty); + auto ch = inp[0]; + static if(hasASCII) + { + if (ch < 0x80) + { + inp.popFront(); + return tab!1[ch]; + } + else + mixin(dispatch); + } + else + mixin(dispatch); + } + } + + public bool test(Range)(ref Range inp) const pure @trusted + if(isRandomAccessRange!Range && is(ElementType!Range : char)) + { + enum mode = Mode.neverSkip; + assert(!inp.empty); + auto ch = inp[0]; + static if(hasASCII) + { + if (ch < 0x80) + return tab!1[ch]; + else + mixin(dispatch); + } + else + mixin(dispatch); + } + + bool match(C)(ref C[] str) const pure @trusted + if(isSomeChar!C) + { + return fwdStr!"match"(str); + } + + bool skip(C)(ref C[] str) const pure @trusted + if(isSomeChar!C) + { + return fwdStr!"skip"(str); + } + + bool test(C)(ref C[] str) const pure @trusted + if(isSomeChar!C) + { + return fwdStr!"test"(str); + } + + mixin ForwardStrings; + } + + struct Impl(Sizes...) + { + static assert(allSatisfy!(validSize, Sizes), + "Only lengths of 1, 2, 3 and 4 code unit are possible for UTF-8"); + private: + //pick tables for chosen sizes + alias OurTabs = staticMap!(Table, Sizes); + OurTabs tables; + mixin DefMatcher; + //static disptach helper UTF size ==> table + alias tab(int i) = tables[i - 1]; + + package @property auto subMatcher(SizesToPick...)() @trusted + { + return CherryPick!(Impl, SizesToPick)(&this); + } + + bool lookup(int size, Mode mode, Range)(ref Range inp) const pure @trusted + { + import std.typecons; + if(inp.length < size) + return badEncoding(), false; + char[size] needle = void; + needle[0] = leadMask!size & inp[0]; + foreach(i; staticIota!(1, size)) + { + needle[i] = truncate(inp[i]); + } + //overlong encoding checks + static if(size == 2) + { + //0x80-0x7FF + //got 6 bits in needle[1], must use at least 8 bits + //must use at least 2 bits in needle[1] + if(needle[0] < 2) badEncoding(); + } + else static if(size == 3) + { + //0x800-0xFFFF + //got 6 bits in needle[2], must use at least 12bits + //must use 6 bits in needle[1] or anything in needle[0] + if(needle[0] == 0 && needle[1] < 0x20) badEncoding(); + } + else static if(size == 4) + { + //0x800-0xFFFF + //got 2x6=12 bits in needle[2..3] must use at least 17bits + //must use 5 bits (or above) in needle[1] or anything in needle[0] + if(needle[0] == 0 && needle[1] < 0x10) badEncoding(); + } + static if(mode == Mode.alwaysSkip) + { + inp.popFrontN(size); + return tab!size[needle]; + } + else static if(mode == Mode.neverSkip) + { + return tab!size[needle]; + } + else + { + static assert(mode == Mode.skipOnMatch); + return tab!size[needle] && (inp.popFrontN(size), true); + } + } + } + + struct CherryPick(I, Sizes...) + { + static assert(allSatisfy!(validSize, Sizes), + "Only lengths of 1, 2, 3 and 4 code unit are possible for UTF-8"); + private: + I* m; + @property ref tab(int i)() const pure { return m.tables[i - 1]; } + bool lookup(int size, Mode mode, Range)(ref Range inp) const pure + { + return m.lookup!(size, mode)(inp); + } + mixin DefMatcher; + } +} + +template Utf16Matcher() +{ + enum validSize(int sz) = sz >= 1 && sz <=2; + + void badEncoding() pure + { + import std.utf; + throw new UTFException("Invalid UTF-16 sequence"); + } + + alias Seq = TypeTuple; + // 1-stage ASCII + alias AsciiSpec = Seq!(bool, wchar, clamp!7); + //2-stage BMP + alias BmpSpec = Seq!(bool, wchar, sliceBits!(7, 16), sliceBits!(0, 7)); + //4-stage - full Unicode + //assume that 0xD800 & 0xDC00 bits are cleared + //thus leaving 10 bit per wchar to worry about + alias UniSpec = Seq!(bool, wchar[2], + assumeSize!(x=>x[0]>>4, 6), assumeSize!(x=>x[0]&0xf, 4), + assumeSize!(x=>x[1]>>6, 4), assumeSize!(x=>x[1]&0x3f, 6), + ); + alias Ascii = typeof(TrieBuilder!(AsciiSpec)(false).build()); + alias Bmp = typeof(TrieBuilder!(BmpSpec)(false).build()); + alias Uni = typeof(TrieBuilder!(UniSpec)(false).build()); + + auto encode2(dchar ch) + { + ch -= 0x1_0000; + assert(ch <= 0xF_FFFF); + wchar[2] ret; + //do not put surrogate bits, they are sliced off + ret[0] = (ch>>10); + ret[1] = (ch & 0xFFF); + return ret; + } + + auto build(Set)(Set set) + { + auto ascii = set & unicode.ASCII; + auto bmp = (set & CodepointSet.fromIntervals(0x80, 0xFFFF+1)) + - CodepointSet.fromIntervals(0xD800, 0xDFFF+1); + auto other = set - (bmp | ascii); + auto asciiT = ascii.byCodepoint.map!(x=>cast(char)x).buildTrie!(AsciiSpec); + auto bmpT = bmp.byCodepoint.map!(x=>cast(wchar)x).buildTrie!(BmpSpec); + auto otherT = other.byCodepoint.map!(x=>encode2(x)).buildTrie!(UniSpec); + alias Ret = Impl!(1,2); + return Ret(asciiT, bmpT, otherT); + } + + //bootstrap full UTF-16 matcher interace from + //sizeFlags, lookupUni and ascii + mixin template DefMatcher() + { + public bool match(Range)(ref Range inp) const pure @trusted + if(isRandomAccessRange!Range && is(ElementType!Range : wchar)) + { + enum mode = Mode.skipOnMatch; + assert(!inp.empty); + auto ch = inp[0]; + static if(sizeFlags & 1) + return ch < 0x80 ? ascii[ch] && (inp.popFront(), true) + : lookupUni!mode(inp); + else + return lookupUni!mode(inp); + } + + static if(Sizes.length == 2) + { + public bool skip(Range)(ref Range inp) const pure @trusted + if(isRandomAccessRange!Range && is(ElementType!Range : wchar)) + { + enum mode = Mode.alwaysSkip; + assert(!inp.empty); + auto ch = inp[0]; + static if(sizeFlags & 1) + return ch < 0x80 ? (inp.popFront(), ascii[ch]) + : lookupUni!mode(inp); + else + return lookupUni!mode(inp); + } + } + + public bool test(Range)(ref Range inp) const pure @trusted + if(isRandomAccessRange!Range && is(ElementType!Range : wchar)) + { + enum mode = Mode.neverSkip; + assert(!inp.empty); + auto ch = inp[0]; + static if(sizeFlags & 1) + return ch < 0x80 ? ascii[ch] : lookupUni!mode(inp); + else + return lookupUni!mode(inp); + } + + bool match(C)(ref C[] str) const pure @trusted + if(isSomeChar!C) + { + return fwdStr!"match"(str); + } + + bool skip(C)(ref C[] str) const pure @trusted + if(isSomeChar!C) + { + return fwdStr!"skip"(str); + } + + bool test(C)(ref C[] str) const pure @trusted + if(isSomeChar!C) + { + return fwdStr!"test"(str); + } + + mixin ForwardStrings; //dispatch strings to range versions + } + + struct Impl(Sizes...) + if(Sizes.length >= 1 && Sizes.length <= 2) + { + private: + static assert(allSatisfy!(validSize, Sizes), + "Only lengths of 1 and 2 code units are possible in UTF-16"); + static if(Sizes.length > 1) + enum sizeFlags = Sizes[0] | Sizes[1]; + else + enum sizeFlags = Sizes[0]; + + static if(sizeFlags & 1) + { + Ascii ascii; + Bmp bmp; + } + static if(sizeFlags & 2) + { + Uni uni; + } + mixin DefMatcher; + + package @property auto subMatcher(SizesToPick...)() @trusted + { + return CherryPick!(Impl, SizesToPick)(&this); + } + + bool lookupUni(Mode mode, Range)(ref Range inp) const pure + { + wchar x = cast(wchar)(inp[0] - 0xD800); + //not a high surrogate + if(x > 0x3FF) + { + //low surrogate + if(x <= 0x7FF) badEncoding(); + static if(sizeFlags & 1) + { + auto ch = inp[0]; + static if(mode == Mode.alwaysSkip) + inp.popFront(); + static if(mode == Mode.skipOnMatch) + return bmp[ch] && (inp.popFront(), true); + else + return bmp[ch]; + } + else //skip is not available for sub-matchers, so just false + return false; + } + else + { + static if(sizeFlags & 2) + { + if(inp.length < 2) + badEncoding(); + wchar y = cast(wchar)(inp[1] - 0xDC00); + //not a low surrogate + if(y > 0x3FF) + badEncoding(); + wchar[2] needle = [inp[0] & 0x3ff, inp[1] & 0x3ff]; + static if(mode == Mode.alwaysSkip) + inp.popFrontN(2); + static if(mode == Mode.skipOnMatch) + return uni[needle] && (inp.popFrontN(2), true); + else + return uni[needle]; + } + else //ditto + return false; + } + } + } + + struct CherryPick(I, Sizes...) + if(Sizes.length >= 1 && Sizes.length <= 2) + { + private: + I* m; + enum sizeFlags = I.sizeFlags; + + static if(sizeFlags & 1) + { + @property ref ascii()() const pure{ return m.ascii; } + } + + bool lookupUni(Mode mode, Range)(ref Range inp) const pure + { + return m.lookupUni!mode(inp); + } + mixin DefMatcher; + static assert(allSatisfy!(validSize, Sizes), + "Only lengths of 1 and 2 code units are possible in UTF-16"); + } +} + +private auto utf8Matcher(Set)(Set set) @trusted +{ + return Utf8Matcher!().build(set); +} + +private auto utf16Matcher(Set)(Set set) @trusted +{ + return Utf16Matcher!().build(set); +} + +/** + Constructs a matcher object + to classify $(CODEPOINTS) from the $(D set) for encoding + that has $(D Char) as code unit. + + See $(LREF MatcherConcept) for API outline. +*/ +public auto utfMatcher(Char, Set)(Set set) @trusted + if(isCodepointSet!Set) +{ + static if(is(Char : char)) + return utf8Matcher(set); + else static if(is(Char : wchar)) + return utf16Matcher(set); + else static if(is(Char : dchar)) + static assert(false, "UTF-32 needs no decoding, + and thus not supported by utfMatcher"); + else + static assert(false, "Only character types 'char' and 'wchar' are allowed"); +} + + +//a range of code units, packed with index to speed up forward iteration +package auto decoder(C)(C[] s, size_t offset=0) @trusted + if(is(C : wchar) || is(C : char)) +{ + static struct Decoder + { + pure nothrow: + C[] str; + size_t idx; + @property C front(){ return str[idx]; } + @property C back(){ return str[$-1]; } + void popFront(){ idx++; } + void popBack(){ str = str[0..$-1]; } + void popFrontN(size_t n){ idx += n; } + @property bool empty(){ return idx == str.length; } + @property auto save(){ return this; } + auto opIndex(size_t i){ return str[idx+i]; } + @property size_t length(){ return str.length - idx; } + alias opDollar = length; + auto opSlice(size_t a, size_t b){ return Decoder(str[0..idx+b], idx+a); } + } + static assert(isRandomAccessRange!Decoder); + static assert(is(ElementType!Decoder : C)); + return Decoder(s, offset); +} + +/* + Expose UTF string $(D s) as a random-access + range of $(S_LINK Code unit, code units). +*/ +package auto units(C)(C[] s) + if(is(C : wchar) || is(C : char)) +{ + static struct Units + { + pure nothrow: + C[] str; + @property C front(){ return str[0]; } + @property C back(){ return str[$-1]; } + void popFront(){ str = str[1..$]; } + void popBack(){ str = str[0..$-1]; } + void popFrontN(size_t n){ str = str[n..$]; } + @property bool empty(){ return 0 == str.length; } + @property auto save(){ return this; } + auto opIndex(size_t i){ return str[i]; } + @property size_t length(){ return str.length; } + alias opDollar = length; + auto opSlice(size_t a, size_t b){ return Units(str[a..b]); } + } + static assert(isRandomAccessRange!Units); + static assert(is(ElementType!Units : C)); + return Units(s); +} + +@safe unittest +{ + import std.range; + string rs = "hi! ネемног砀 текста"; + auto codec = rs.decoder; + auto utf8 = utf8Matcher(unicode.Letter); + auto asc = utf8.subMatcher!(1); + auto uni = utf8.subMatcher!(2,3,4); + assert(asc.test(codec)); + assert(!uni.match(codec)); + assert(utf8.skip(codec)); + assert(codec.idx == 1); + + assert(!uni.match(codec)); + assert(asc.test(codec)); + assert(utf8.skip(codec)); + assert(codec.idx == 2); + assert(!asc.match(codec)); + + assert(!utf8.test(codec)); + assert(!utf8.skip(codec)); + + assert(!asc.test(codec)); + assert(!utf8.test(codec)); + assert(!utf8.skip(codec)); + assert(utf8.test(codec)); + foreach(i; 0..7) + { + assert(!asc.test(codec)); + assert(uni.test(codec)); + assert(utf8.skip(codec)); + } + assert(!utf8.test(codec)); + assert(!utf8.skip(codec)); + //the same with match where applicable + codec = rs.decoder; + assert(utf8.match(codec)); + assert(codec.idx == 1); + assert(utf8.match(codec)); + assert(codec.idx == 2); + assert(!utf8.match(codec)); + assert(codec.idx == 2); + assert(!utf8.skip(codec)); + assert(!utf8.skip(codec)); + + foreach(i; 0..7) + { + assert(!asc.test(codec)); + assert(utf8.test(codec)); + assert(utf8.match(codec)); + } + auto i = codec.idx; + assert(!utf8.match(codec)); + assert(codec.idx == i); +} + +@safe unittest +{ + static bool testAll(Matcher, Range)(ref Matcher m, ref Range r) + { + bool t = m.test(r); + auto save = r.idx; + assert(t == m.match(r)); + assert(r.idx == save || t); //ether no change or was match + r.idx = save; + static if(is(typeof(m.skip(r)))) + { + assert(t == m.skip(r)); + assert(r.idx != save); //always changed + r.idx = save; + } + return t; + } + auto utf16 = utfMatcher!wchar(unicode.L); + auto bmp = utf16.subMatcher!1; + auto nonBmp = utf16.subMatcher!1; + auto utf8 = utfMatcher!char(unicode.L); + auto ascii = utf8.subMatcher!1; + auto uni2 = utf8.subMatcher!2; + auto uni3 = utf8.subMatcher!3; + auto uni24 = utf8.subMatcher!(2,4); + foreach(ch; unicode.L.byCodepoint.stride(3)) + { + import std.utf : encode; + char[4] buf; + wchar[2] buf16; + auto len = std.utf.encode(buf, ch); + auto len16 = std.utf.encode(buf16, ch); + auto c8 = buf[0..len].decoder; + auto c16 = buf16[0..len16].decoder; + assert(testAll(utf16, c16)); + assert(testAll(bmp, c16) || len16 != 1); + assert(testAll(nonBmp, c16) || len16 != 2); + + assert(testAll(utf8, c8)); + + //submatchers return false on out of their domain + assert(testAll(ascii, c8) || len != 1); + assert(testAll(uni2, c8) || len != 2); + assert(testAll(uni3, c8) || len != 3); + assert(testAll(uni24, c8) || (len != 2 && len !=4)); + } +} + +// cover decode fail cases of Matcher +unittest +{ + import std.string : format; + auto utf16 = utfMatcher!wchar(unicode.L); + auto utf8 = utfMatcher!char(unicode.L); + //decode failure cases UTF-8 + alias fails8 = TypeTuple!("\xC1", "\x80\x00","\xC0\x00", "\xCF\x79", + "\xFF\x00\0x00\0x00\x00", "\xC0\0x80\0x80\x80", "\x80\0x00\0x00\x00", + "\xCF\x00\0x00\0x00\x00"); + foreach(msg; fails8){ + assert(collectException((){ + auto s = msg; + import std.utf; + size_t idx = 0; + //decode(s, idx); + utf8.test(s); + }()), format("%( %2x %)", cast(ubyte[])msg)); + } + //decode failure cases UTF-16 + alias fails16 = TypeTuple!([0xD811], [0xDC02]); + foreach(msg; fails16){ + assert(collectException((){ + auto s = msg.map!(x => cast(wchar)x); + utf16.test(s); + }())); + } +} + /++ Convenience function to construct optimal configurations for packed Trie from any $(D set) of $(CODEPOINTS). @@ -4475,14 +5483,17 @@ struct sliceBits(size_t from, size_t to) body { static assert(from < to); + static if(from == 0) + return x & ((1<> from) & ((1<<(to-from))-1); } } uint low_8(uint x) { return x&0xFF; } @safe pure nothrow uint midlow_8(uint x){ return (x&0xFF00)>>8; } -alias assumeSize!(low_8, 8) lo8; -alias assumeSize!(midlow_8, 8) mlo8; +alias lo8 = assumeSize!(low_8, 8); +alias mlo8 = assumeSize!(midlow_8, 8); static assert(bitSizeOf!lo8 == 8); static assert(bitSizeOf!(sliceBits!(4, 7)) == 3); @@ -4491,9 +5502,9 @@ static assert(bitSizeOf!(BitPacked!(uint, 2)) == 2); template Sequence(size_t start, size_t end) { static if(start < end) - alias TypeTuple!(start, Sequence!(start+1, end)) Sequence; + alias Sequence = TypeTuple!(start, Sequence!(start+1, end)); else - alias TypeTuple!() Sequence; + alias Sequence = TypeTuple!(); } //---- TRIE TESTS ---- @@ -4505,7 +5516,7 @@ unittest { import std.stdio; writeln("---TRIE FOOTPRINT STATS---"); - foreach(i; Sequence!(0, t.table.dim) ) + foreach(i; staticIota!(0, t.table.dim) ) { writefln("lvl%s = %s bytes; %s pages" , i, t.bytes!i, t.pages!i); @@ -4514,15 +5525,15 @@ unittest version(none) { writeln("INDEX (excluding value level):"); - foreach(i; Sequence!(0, t.table.dim-1) ) + foreach(i; staticIota!(0, t.table.dim-1) ) writeln(t.table.slice!(i)[0..t.table.length!i]); } writeln("---------------------------"); } } //@@@BUG link failure, lambdas not found by linker somehow (in case of trie2) - // alias assumeSize!(8, function (uint x) { return x&0xFF; }) lo8; - // alias assumeSize!(7, function (uint x) { return (x&0x7F00)>>8; }) next8; + // alias lo8 = assumeSize!(8, function (uint x) { return x&0xFF; }); + // alias next8 = assumeSize!(7, function (uint x) { return (x&0x7F00)>>8; }); alias CodepointSet Set; auto set = Set('A','Z','a','z'); auto trie = buildTrie!(bool, uint, 256, lo8)(set.byInterval);// simple bool array @@ -4620,7 +5631,7 @@ template idxTypes(Key, size_t fullBits, Prefix...) { static if(Prefix.length == 1) {// the last level is value level, so no index once reduced to 1-level - alias TypeTuple!() idxTypes; + alias idxTypes = TypeTuple!(); } else { @@ -4629,10 +5640,11 @@ template idxTypes(Key, size_t fullBits, Prefix...) // The bottom level is known to hold full bit width // thus it's size in pages is full_bit_width - size_of_last_prefix // Recourse on this notion - alias TypeTuple!( - idxTypes!(Key, fullBits - bitSizeOf!(Prefix[$-1]), Prefix[0..$-1]), - BitPacked!(typeof(Prefix[$-2](Key.init)), fullBits - bitSizeOf!(Prefix[$-1])) - ) idxTypes; + alias idxTypes = + TypeTuple!( + idxTypes!(Key, fullBits - bitSizeOf!(Prefix[$-1]), Prefix[0..$-1]), + BitPacked!(typeof(Prefix[$-2](Key.init)), fullBits - bitSizeOf!(Prefix[$-1])) + ); } } @@ -4732,13 +5744,14 @@ unittest } // Creates a range of $(D CodepointInterval) that lazily decodes compressed data. -@safe package auto decompressIntervals(const(ubyte)[] data) +@safe package auto decompressIntervals(const(ubyte)[] data) pure { return DecompressedIntervals(data); } @trusted struct DecompressedIntervals { +pure: const(ubyte)[] _stream; size_t _idx; CodepointInterval _front; @@ -4790,7 +5803,7 @@ else { // helper for looking up code point sets -@trusted ptrdiff_t findUnicodeSet(alias table, C)(in C[] name) +@trusted ptrdiff_t findUnicodeSet(alias table, C)(in C[] name) pure { auto range = assumeSorted!((a,b) => propertyNameLess(a,b)) (table.map!"a.name"()); @@ -4801,7 +5814,7 @@ else } // another one that loads it -@trusted bool loadUnicodeSet(alias table, Set, C)(in C[] name, ref Set dest) +@trusted bool loadUnicodeSet(alias table, Set, C)(in C[] name, ref Set dest) pure { auto idx = findUnicodeSet!table(name); if(idx >= 0) @@ -4813,13 +5826,13 @@ else } @trusted bool loadProperty(Set=CodepointSet, C) - (in C[] name, ref Set target) + (in C[] name, ref Set target) pure { - alias comparePropertyName ucmp; + alias ucmp = comparePropertyName; // conjure cumulative properties by hand if(ucmp(name, "L") == 0 || ucmp(name, "Letter") == 0) { - target |= asSet(uniProps.Lu); + target = asSet(uniProps.Lu); target |= asSet(uniProps.Ll); target |= asSet(uniProps.Lt); target |= asSet(uniProps.Lo); @@ -4827,25 +5840,25 @@ else } else if(ucmp(name,"LC") == 0 || ucmp(name,"Cased Letter")==0) { - target |= asSet(uniProps.Ll); + target = asSet(uniProps.Ll); target |= asSet(uniProps.Lu); target |= asSet(uniProps.Lt);// Title case } else if(ucmp(name, "M") == 0 || ucmp(name, "Mark") == 0) { - target |= asSet(uniProps.Mn); + target = asSet(uniProps.Mn); target |= asSet(uniProps.Mc); target |= asSet(uniProps.Me); } else if(ucmp(name, "N") == 0 || ucmp(name, "Number") == 0) { - target |= asSet(uniProps.Nd); + target = asSet(uniProps.Nd); target |= asSet(uniProps.Nl); target |= asSet(uniProps.No); } else if(ucmp(name, "P") == 0 || ucmp(name, "Punctuation") == 0) { - target |= asSet(uniProps.Pc); + target = asSet(uniProps.Pc); target |= asSet(uniProps.Pd); target |= asSet(uniProps.Ps); target |= asSet(uniProps.Pe); @@ -4855,27 +5868,27 @@ else } else if(ucmp(name, "S") == 0 || ucmp(name, "Symbol") == 0) { - target |= asSet(uniProps.Sm); + target = asSet(uniProps.Sm); target |= asSet(uniProps.Sc); target |= asSet(uniProps.Sk); target |= asSet(uniProps.So); } else if(ucmp(name, "Z") == 0 || ucmp(name, "Separator") == 0) { - target |= asSet(uniProps.Zs); + target = asSet(uniProps.Zs); target |= asSet(uniProps.Zl); target |= asSet(uniProps.Zp); } else if(ucmp(name, "C") == 0 || ucmp(name, "Other") == 0) { - target |= asSet(uniProps.Co); + target = asSet(uniProps.Co); target |= asSet(uniProps.Lo); target |= asSet(uniProps.No); target |= asSet(uniProps.So); target |= asSet(uniProps.Po); } else if(ucmp(name, "graphical") == 0){ - target |= asSet(uniProps.Alphabetic); + target = asSet(uniProps.Alphabetic); target |= asSet(uniProps.Mn); target |= asSet(uniProps.Mc); @@ -4901,9 +5914,9 @@ else target |= asSet(uniProps.So); } else if(ucmp(name, "any") == 0) - target = Set(0,0x110000); + target = Set.fromIntervals(0, 0x110000); else if(ucmp(name, "ascii") == 0) - target = Set(0,0x80); + target = Set.fromIntervals(0, 0x80); else return loadUnicodeSet!(uniProps.tab)(name, target); return true; @@ -4913,13 +5926,13 @@ else @safe bool isPrettyPropertyName(C)(in C[] name) { auto names = [ - "L", "Letters", + "L", "Letter", "LC", "Cased Letter", "M", "Mark", "N", "Number", "P", "Punctuation", "S", "Symbol", - "Z", "Separator" + "Z", "Separator", "Graphical", "any", "ascii" @@ -5007,7 +6020,7 @@ template SetSearcher(alias table, string kind) --- */ - static @property auto opDispatch(string name)() + static @property auto opDispatch(string name)() pure { static if(findAny(name)) return loadAny(name); @@ -5113,11 +6126,11 @@ private: || (ucmp(name[0..2],"In") == 0 && findSetName!(blocks.tab)(name[2..$])); } - static auto loadAny(Set=CodepointSet, C)(in C[] name) + static auto loadAny(Set=CodepointSet, C)(in C[] name) pure { Set set; bool loaded = loadProperty(name, set) || loadUnicodeSet!(scripts.tab)(name, set) - || (ucmp(name[0..2],"In") == 0 + || (name.length > 2 && ucmp(name[0..2],"In") == 0 && loadUnicodeSet!(blocks.tab)(name[2..$], set)); if(loaded) return set; @@ -5147,6 +6160,7 @@ unittest import std.exception; // R-T look up throws if no such set is found assert(collectException(unicode("InCyrilliac"))); + assert(collectException(unicode("X"))); assert(unicode.block.Greek_and_Coptic == unicode.InGreek_and_Coptic); @@ -5194,9 +6208,9 @@ template genericDecodeGrapheme(bool getValue) alias graphemeExtend = graphemeExtendTrie; alias spacingMark = mcTrie; static if(getValue) - alias Grapheme Value; + alias Value = Grapheme; else - alias void Value; + alias Value = void; Value genericDecodeGrapheme(Input)(ref Input range) { @@ -5393,10 +6407,10 @@ unittest See_Also: $(LREF byCodePoint) +/ -// TODO: Bidirectional access auto byGrapheme(Range)(Range range) if(isInputRange!Range && is(Unqual!(ElementType!Range) == dchar)) { + // TODO: Bidirectional access static struct Result { private Range _range; @@ -5489,10 +6503,10 @@ unittest $(P Acts as the identity function when given a range of code points.) +/ -// TODO: Propagate bidirectional access auto byCodePoint(Range)(Range range) if(isInputRange!Range && is(Unqual!(ElementType!Range) == Grapheme)) { + // TODO: Propagate bidirectional access static struct Result { private Range _range; @@ -5541,7 +6555,7 @@ Range byCodePoint(Range)(Range range) /// unittest { - import std.string : text; + import std.conv : text; string s = "noe\u0308l"; // noël @@ -6118,6 +7132,101 @@ unittest }); } +// This is package for the moment to be used as a support tool for std.regex +// It needs a better API +/* + Return a range of all $(CODEPOINTS) that casefold to + and from this $(D ch). +*/ +package auto simpleCaseFoldings(dchar ch) +{ + alias sTable = simpleCaseTable; + static struct Range + { + pure nothrow: + uint idx; //if == uint.max, then read c. + union + { + dchar c; // == 0 - empty range + uint len; + } + @property bool isSmall() const { return idx == uint.max; } + + this(dchar ch) + { + idx = uint.max; + c = ch; + } + + this(uint start, uint size) + { + idx = start; + len = size; + } + + @property dchar front() const + { + assert(!empty); + if(isSmall) + { + return c; + } + auto ch = sTable[idx].ch; + return ch; + } + + @property bool empty() const + { + if(isSmall) + { + return c == 0; + } + return len == 0; + } + + @property uint length() const + { + if(isSmall) + { + return c == 0 ? 0 : 1; + } + return len; + } + + void popFront() + { + if(isSmall) + c = 0; + else + { + idx++; + len--; + } + } + } + immutable idx = simpleCaseTrie[ch]; + if (idx == EMPTY_CASE_TRIE) + return Range(ch); + auto entry = sTable[idx]; + immutable start = idx - entry.n; + return Range(start, entry.size); +} + +unittest +{ + assertCTFEable!((){ + auto r = simpleCaseFoldings('Э').array; + assert(r.length == 2); + assert(r.canFind('э') && r.canFind('Э')); + auto sr = simpleCaseFoldings('~'); + assert(sr.equalS("~")); + //A with ring above - casefolds to the same bucket as Angstrom sign + sr = simpleCaseFoldings('Å'); + assert(sr.length == 3); + assert(sr.canFind('å') && sr.canFind('Å') && sr.canFind('\u212B')); + }); +} + /++ $(P Returns the $(S_LINK Combining class, combining class) of $(D ch).) @@ -6752,47 +7861,19 @@ else // trusted -> avoid bounds check @trusted pure nothrow -ushort toLowerIndex(dchar c) -{ - alias trie = toLowerIndexTrie; - return trie[c]; -} - -// trusted -> avoid bounds check -@trusted pure nothrow -dchar toLowerTab(size_t idx) -{ - return toLowerTable[idx]; -} - -// trusted -> avoid bounds check -@trusted pure nothrow -ushort toTitleIndex(dchar c) -{ - alias trie = toTitleIndexTrie; - return trie[c]; -} - -// trusted -> avoid bounds check -@trusted pure nothrow -dchar toTitleTab(size_t idx) { - return toTitleTable[idx]; -} + // hide template instances behind functions (Bugzilla 13232) + ushort toLowerIndex(dchar c) { return toLowerIndexTrie[c]; } + ushort toLowerSimpleIndex(dchar c) { return toLowerSimpleIndexTrie[c]; } + dchar toLowerTab(size_t idx) { return toLowerTable[idx]; } -// trusted -> avoid bounds check -@trusted pure nothrow -ushort toUpperIndex(dchar c) -{ - alias trie = toUpperIndexTrie; - return trie[c]; -} + ushort toTitleIndex(dchar c) { return toTitleIndexTrie[c]; } + ushort toTitleSimpleIndex(dchar c) { return toTitleSimpleIndexTrie[c]; } + dchar toTitleTab(size_t idx) { return toTitleTable[idx]; } -// trusted -> avoid bounds check -@trusted pure nothrow -dchar toUpperTab(size_t idx) -{ - return toUpperTable[idx]; + ushort toUpperIndex(dchar c) { return toUpperIndexTrie[c]; } + ushort toUpperSimpleIndex(dchar c) { return toUpperSimpleIndexTrie[c]; } + dchar toUpperTab(size_t idx) { return toUpperTable[idx]; } } public: @@ -6888,8 +7969,8 @@ dchar toLower(dchar c) return c + 32; return c; } - size_t idx = toLowerIndex(c); - if(idx < MAX_SIMPLE_LOWER) + size_t idx = toLowerSimpleIndex(c); + if(idx != ushort.max) { return toLowerTab(idx); } @@ -6910,8 +7991,8 @@ private dchar toTitlecase(dchar c) return c - 32; return c; } - size_t idx = toTitleIndex(c); - if(idx < MAX_SIMPLE_TITLE) + size_t idx = toTitleSimpleIndex(c); + if(idx != ushort.max) { return toTitleTab(idx); } @@ -6930,32 +8011,43 @@ private S toCase(alias indexFn, uint maxIdx, alias tableFn, S)(S s) @trusted pur ushort idx = indexFn(cOuter); if(idx == ushort.max) continue; - auto result = s[0 .. i].dup; + auto result = appender!S(s[0..i]); + result.reserve(s.length); foreach(dchar c; s[i .. $]) { idx = indexFn(c); if(idx == ushort.max) - result ~= c; + result.put(c); else if(idx < maxIdx) { c = tableFn(idx); - result ~= c; + result.put(c); } else { auto val = tableFn(idx); // unpack length + codepoint uint len = val>>24; - result ~= cast(dchar)(val & 0xFF_FFFF); + result.put(cast(dchar)(val & 0xFF_FFFF)); foreach(j; idx+1..idx+len) - result ~= tableFn(j); + result.put(tableFn(j)); } } - return cast(S) result; + return result.data; } return s; } +unittest //12428 +{ + auto s = "abcdefghij".replicate(300); + s = s[0..10]; + + toUpper(s); + + assert(s == "abcdefghij"); +} + // TODO: helper, I wish std.utf was more flexible (and stright) private size_t encodeTo(char[] buf, size_t idx, dchar c) @trusted pure { @@ -7334,6 +8426,15 @@ unittest // Test on wchar and dchar strings. assert(toLower("Some String"w) == "some string"w); assert(toLower("Some String"d) == "some string"d); + + // bugzilla 12455 + dchar c = 'İ'; // '\U0130' LATIN CAPITAL LETTER I WITH DOT ABOVE + assert(isUpper(c)); + assert(toLower(c) == 'i'); + // extend on 12455 reprot - check simple-case toUpper too + c = '\u1f87'; + assert(isLower(c)); + assert(toUpper(c) == '\u1F8F'); } @@ -7357,8 +8458,8 @@ dchar toUpper(dchar c) return c - 32; return c; } - size_t idx = toUpperIndex(c); - if(idx < MAX_SIMPLE_UPPER) + size_t idx = toUpperSimpleIndex(c); + if(idx != ushort.max) { return toUpperTab(idx); } @@ -7372,10 +8473,12 @@ dchar toUpper(dchar c) assert(std.ascii.toUpper(ch) == toUpper(ch)); assert(toUpper('я') == 'Я'); assert(toUpper('δ') == 'Δ'); + auto title = unicode.Titlecase_Letter; foreach(ch; unicode.lowerCase.byCodepoint) { dchar up = ch.toUpper(); - assert(up == ch || isUpper(up), format("%s -> %s", ch, up)); + assert(up == ch || isUpper(up) || title[up], + format("%x -> %x", ch, up)); } } @@ -7755,9 +8858,9 @@ private: // load static data from pre-generated tables into usable datastructures -@safe auto asSet(const (ubyte)[] compressed) +@safe auto asSet(const (ubyte)[] compressed) pure { - return CodepointSet(decompressIntervals(compressed)); + return CodepointSet.fromIntervals(decompressIntervals(compressed)); } @safe pure nothrow auto asTrie(T...)(in TrieEntry!T e) @@ -7874,9 +8977,11 @@ private: auto toUpperIndexTrie() { static immutable res = asTrie(toUpperIndexTrieEntries); return res; } auto toLowerIndexTrie() { static immutable res = asTrie(toLowerIndexTrieEntries); return res; } auto toTitleIndexTrie() { static immutable res = asTrie(toTitleIndexTrieEntries); return res; } - + //simple case conversion tables + auto toUpperSimpleIndexTrie() { static immutable res = asTrie(toUpperSimpleIndexTrieEntries); return res; } + auto toLowerSimpleIndexTrie() { static immutable res = asTrie(toLowerSimpleIndexTrieEntries); return res; } + auto toTitleSimpleIndexTrie() { static immutable res = asTrie(toTitleSimpleIndexTrieEntries); return res; } } }// version(!std_uni_bootstrap) - diff --git a/libphobos/src/std/uri.d b/libphobos/src/std/uri.d index 3f774068e..1aa5f2187 100644 --- a/libphobos/src/std/uri.d +++ b/libphobos/src/std/uri.d @@ -34,6 +34,7 @@ debug(uri) private import std.stdio; private import std.ascii; private import std.c.stdlib; private import std.utf; +private import std.traits : isSomeChar; import core.exception : OutOfMemoryError; import std.exception : assumeUnique; @@ -229,7 +230,7 @@ uint ascii2hex(dchar c) c - 'a' + 10; } -private dstring URI_Decode(string string, uint reservedSet) +private dstring URI_Decode(Char)(in Char[] uri, uint reservedSet) if (isSomeChar!Char) { uint j; uint k; @@ -240,8 +241,8 @@ private dstring URI_Decode(string string, uint reservedSet) dchar* R; uint Rlen; - auto len = string.length; - auto s = string.ptr; + auto len = uri.length; + auto s = uri.ptr; // Preallocate result buffer R guaranteed to be large enough for result auto Rsize = len; @@ -343,7 +344,7 @@ private dstring URI_Decode(string string, uint reservedSet) * Escape sequences that resolve to the '#' character are not replaced. */ -string decode(string encodedURI) +string decode(Char)(in Char[] encodedURI) if (isSomeChar!Char) { auto s = URI_Decode(encodedURI, URI_Reserved | URI_Hash); return std.utf.toUTF8(s); @@ -354,7 +355,7 @@ string decode(string encodedURI) * escape sequences are decoded. */ -string decodeComponent(string encodedURIComponent) +string decodeComponent(Char)(in Char[] encodedURIComponent) if (isSomeChar!Char) { auto s = URI_Decode(encodedURIComponent, 0); return std.utf.toUTF8(s); @@ -365,7 +366,7 @@ string decodeComponent(string encodedURIComponent) * not a valid URI character is escaped. The '#' character is not escaped. */ -string encode(string uri) +string encode(Char)(in Char[] uri) if (isSomeChar!Char) { auto s = std.utf.toUTF32(uri); return URI_Encode(s, URI_Reserved | URI_Hash | URI_Alpha | URI_Digit | URI_Mark); @@ -376,7 +377,7 @@ string encode(string uri) * Any character not a letter, digit, or one of -_.!~*'() is escaped. */ -string encodeComponent(string uriComponent) +string encodeComponent(Char)(in Char[] uriComponent) if (isSomeChar!Char) { auto s = std.utf.toUTF32(uriComponent); return URI_Encode(s, URI_Alpha | URI_Digit | URI_Mark); @@ -385,11 +386,11 @@ string encodeComponent(string uriComponent) /*************************** * Does string s[] start with a URL? * Returns: - * -1 it does not + * -1 it does not * len it does, and s[0..len] is the slice of s[] that is that URL */ -size_t uriLength(string s) +size_t uriLength(Char)(in Char[] s) if (isSomeChar!Char) { /* Must start with one of: * http:// @@ -458,7 +459,7 @@ unittest * References: * RFC2822 */ -size_t emailLength(string s) +size_t emailLength(Char)(in Char[] s) if (isSomeChar!Char) { size_t i; @@ -516,28 +517,45 @@ unittest { debug(uri) writeln("uri.encodeURI.unittest"); - string s = "http://www.digitalmars.com/~fred/fred's RX.html#foo"; - string t = "http://www.digitalmars.com/~fred/fred's%20RX.html#foo"; + string source = "http://www.digitalmars.com/~fred/fred's RX.html#foo"; + string target = "http://www.digitalmars.com/~fred/fred's%20RX.html#foo"; - auto r = encode(s); - debug(uri) writefln("r = '%s'", r); - assert(r == t); - r = decode(t); - debug(uri) writefln("r = '%s'", r); - assert(r == s); + auto result = encode(source); + debug(uri) writefln("result = '%s'", result); + assert(result == target); + result = decode(target); + debug(uri) writefln("result = '%s'", result); + assert(result == source); - r = encode( decode("%E3%81%82%E3%81%82") ); - assert(r == "%E3%81%82%E3%81%82"); + result = encode(decode("%E3%81%82%E3%81%82")); + assert(result == "%E3%81%82%E3%81%82"); - r = encodeComponent("c++"); - assert(r == "c%2B%2B"); + result = encodeComponent("c++"); + assert(result == "c%2B%2B"); auto str = new char[10_000_000]; str[] = 'A'; - r = encodeComponent(assumeUnique(str)); - foreach (char c; r) + result = encodeComponent(str); + foreach (char c; result) assert(c == 'A'); - r = decode("%41%42%43"); - debug(uri) writeln(r); + result = decode("%41%42%43"); + debug(uri) writeln(result); + + import std.typetuple : TypeTuple; + foreach (StringType; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring)) + { + import std.conv : to; + StringType decoded1 = source.to!StringType; + string encoded1 = encode(decoded1); + assert(decoded1 == source.to!StringType); // check that `decoded1` wasn't changed + assert(encoded1 == target); + assert(decoded1 == decode(encoded1).to!StringType); + + StringType encoded2 = target.to!StringType; + string decoded2 = decode(encoded2); + assert(encoded2 == target.to!StringType); // check that `encoded2` wasn't changed + assert(decoded2 == source); + assert(encoded2 == encode(decoded2).to!StringType); + } } diff --git a/libphobos/src/std/utf.d b/libphobos/src/std/utf.d index c3a47e947..bac6ace19 100644 --- a/libphobos/src/std/utf.d +++ b/libphobos/src/std/utf.d @@ -154,7 +154,7 @@ unittest valid UTF-8 sequence. Notes: - $(D stride) will only analize the first $(D str[index]) element. It + $(D stride) will only analyze the first $(D str[index]) element. It will not fully verify the validity of UTF-8 sequence, nor even verify the presence of the sequence: it will not actually guarantee that $(D index + stride(str, index) <= str.length). @@ -422,7 +422,7 @@ unittest valid UTF-16 sequence. Notes: - $(D stride) will only analize the first $(D str[index]) element. It + $(D stride) will only analyze the first $(D str[index]) element. It will not fully verify the validity of UTF-16 sequence, nor even verify the presence of the sequence: it will not actually guarantee that $(D index + stride(str, index) <= str.length). @@ -507,10 +507,10 @@ uint stride(S)(auto ref S str) foreach (S; TypeTuple!(wchar[], const wchar[], wstring)) { enum str = to!S("hello world"); - static assert(isSafe!({ stride(str, 0); })); - static assert(isSafe!({ stride(str); })); - static assert((functionAttributes!({ stride(str, 0); }) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!({ stride(str); }) & FunctionAttribute.pure_) != 0); + static assert(isSafe!(() => stride(str, 0))); + static assert(isSafe!(() => stride(str) )); + static assert((functionAttributes!(() => stride(str, 0)) & FunctionAttribute.pure_) != 0); + static assert((functionAttributes!(() => stride(str) ) & FunctionAttribute.pure_) != 0); } }); } @@ -534,7 +534,7 @@ uint stride(S)(auto ref S str) end of a valid UTF-16 sequence. Notes: - $(D stride) will only analize the element at $(D str[index - 1]) + $(D stride) will only analyze the element at $(D str[index - 1]) element. It will not fully verify the validity of UTF-16 sequence, nor even verify the presence of the sequence: it will not actually guarantee that $(D stride(str, index) <= index). @@ -623,10 +623,10 @@ unittest foreach (S; TypeTuple!(wchar[], const wchar[], wstring)) { enum str = to!S("hello world"); - static assert(isSafe!({ strideBack(str, 0); })); - static assert(isSafe!({ strideBack(str); })); - static assert((functionAttributes!({ strideBack(str, 0); }) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!({ strideBack(str); }) & FunctionAttribute.pure_) != 0); + static assert(isSafe!(() => strideBack(str, 0))); + static assert(isSafe!(() => strideBack(str) )); + static assert((functionAttributes!(() => strideBack(str, 0)) & FunctionAttribute.pure_) != 0); + static assert((functionAttributes!(() => strideBack(str) ) & FunctionAttribute.pure_) != 0); } }); } @@ -710,10 +710,10 @@ unittest foreach (S; TypeTuple!(dchar[], const dchar[], dstring)) { enum str = to!S("hello world"); - static assert(isSafe!({ stride(str, 0); })); - static assert(isSafe!({ stride(str); })); - static assert((functionAttributes!({ stride(str, 0); }) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!({ stride(str); }) & FunctionAttribute.pure_) != 0); + static assert(isSafe!(() => stride(str, 0))); + static assert(isSafe!(() => stride(str) )); + static assert((functionAttributes!(() => stride(str, 0)) & FunctionAttribute.pure_) != 0); + static assert((functionAttributes!(() => stride(str) ) & FunctionAttribute.pure_) != 0); } }); } @@ -807,10 +807,10 @@ unittest foreach (S; TypeTuple!(dchar[], const dchar[], dstring)) { enum str = to!S("hello world"); - static assert(isSafe!({ strideBack(str, 0); })); - static assert(isSafe!({ strideBack(str); })); - static assert((functionAttributes!({ strideBack(str, 0); }) & FunctionAttribute.pure_) != 0); - static assert((functionAttributes!({ strideBack(str); }) & FunctionAttribute.pure_) != 0); + static assert(isSafe!(() => strideBack(str, 0))); + static assert(isSafe!(() => strideBack(str) )); + static assert((functionAttributes!(() => strideBack(str, 0)) & FunctionAttribute.pure_) != 0); + static assert((functionAttributes!(() => strideBack(str) ) & FunctionAttribute.pure_) != 0); } }); } @@ -1134,8 +1134,8 @@ private dchar decodeImpl(bool canIndex, S)(auto ref S str, ref size_t index) else return new UTFException("Attempted to decode past the end of a string"); } - - assert(fst & 0x80); + if((fst & 0b1100_0000) != 0b1100_0000) + throw invalidUTF(); // starter must have at least 2 first bits set ubyte tmp = void; dchar d = fst; // upper control bits are masked out later fst <<= 1; @@ -1184,6 +1184,9 @@ private dchar decodeImpl(bool canIndex, S)(auto ref S str, ref size_t index) } index += i + 1; + static if (i == 3) + if (d > dchar.max) + throw invalidUTF(); return d; } } @@ -1525,7 +1528,16 @@ unittest }); } - +unittest +{ + char[4] val; + val[0] = 0b1111_0111; + val[1] = 0b1011_1111; + val[2] = 0b1011_1111; + val[3] = 0b1011_1111; + size_t i = 0; + assertThrown!UTFException((){ dchar ch = decode(val[], i); }()); +} /* =================== Encode ======================= */ /++ @@ -1850,12 +1862,11 @@ ubyte codeLength(C)(dchar c) @safe pure nothrow { static if (C.sizeof == 1) { - return - c <= 0x7F ? 1 - : c <= 0x7FF ? 2 - : c <= 0xFFFF ? 3 - : c <= 0x10FFFF ? 4 - : (assert(false), 6); + if (c <= 0x7F) return 1; + if (c <= 0x7FF) return 2; + if (c <= 0xFFFF) return 3; + if (c <= 0x10FFFF) return 4; + assert(false); } else static if (C.sizeof == 2) { @@ -2009,6 +2020,14 @@ void validate(S)(in S str) @safe pure } +unittest // bugzilla 12923 +{ + assertThrown((){ + char[3]a=[167, 133, 175]; + validate(a[]); + }()); +} + /* =================== Conversion to UTF8 ======================= */ pure @@ -2287,14 +2306,14 @@ dstring toUTF32(in dchar[] s) @safe +/ template toUTFz(P) { - P toUTFz(S)(S str) @system + P toUTFz(S)(S str) @safe pure { return toUTFzImpl!(P, S)(str); } } /// -unittest +@safe pure unittest { auto p1 = toUTFz!(char*)("hello world"); auto p2 = toUTFz!(const(char)*)("hello world"); @@ -2304,7 +2323,7 @@ unittest auto p6 = toUTFz!(immutable(dchar)*)("hello world"w); } -private P toUTFzImpl(P, S)(S str) @system +private P toUTFzImpl(P, S)(S str) @safe pure if (isSomeString!S && isPointer!P && isSomeChar!(typeof(*P.init)) && is(Unqual!(typeof(*P.init)) == Unqual!(ElementEncodingType!S)) && is(immutable(Unqual!(ElementEncodingType!S)) == ElementEncodingType!S)) @@ -2328,7 +2347,8 @@ private P toUTFzImpl(P, S)(S str) @system { if (!__ctfe) { - immutable p = str.ptr + str.length; + auto trustedPtrAdd(S s) @trusted { return s.ptr + s.length; } + immutable p = trustedPtrAdd(str); // Peek past end of str, if it's 0, no conversion necessary. // Note that the compiler will put a 0 past the end of static @@ -2347,7 +2367,7 @@ private P toUTFzImpl(P, S)(S str) @system } } -private P toUTFzImpl(P, S)(S str) @system +private P toUTFzImpl(P, S)(S str) @safe pure if (isSomeString!S && isPointer!P && isSomeChar!(typeof(*P.init)) && is(Unqual!(typeof(*P.init)) == Unqual!(ElementEncodingType!S)) && !is(immutable(Unqual!(ElementEncodingType!S)) == ElementEncodingType!S)) @@ -2363,7 +2383,8 @@ private P toUTFzImpl(P, S)(S str) @system { if (!__ctfe) { - auto p = str.ptr + str.length; + auto trustedPtrAdd(S s) @trusted { return s.ptr + s.length; } + auto p = trustedPtrAdd(str); if ((cast(size_t)p & 3) && *p == '\0') return str.ptr; @@ -2380,11 +2401,12 @@ private P toUTFzImpl(P, S)(S str) @system copy[0 .. $ - 1] = str[]; copy[$ - 1] = '\0'; - return cast(P)copy.ptr; + auto trustedCast(typeof(copy) c) @trusted { return cast(P)c.ptr; } + return trustedCast(copy); } } -private P toUTFzImpl(P, S)(S str) +private P toUTFzImpl(P, S)(S str) @safe pure if (isSomeString!S && isPointer!P && isSomeChar!(typeof(*P.init)) && !is(Unqual!(typeof(*P.init)) == Unqual!(ElementEncodingType!S))) //C1[], const(C1)[], or immutable(C1)[] -> C2*, const(C2)*, or immutable(C2)* @@ -2398,23 +2420,10 @@ private P toUTFzImpl(P, S)(S str) return cast(P)retval.data.ptr; } -unittest +@safe pure unittest { import std.algorithm; - static size_t zeroLen(C)(const(C)* ptr) - { - size_t len = 0; - - while (*ptr != '\0') - { - ++ptr; - ++len; - } - - return len; - } - assertCTFEable!( { foreach (S; TypeTuple!(string, wstring, dstring)) @@ -2426,24 +2435,34 @@ unittest temp[0 .. $ - 1] = s1[0 .. $]; temp[$ - 1] = '\n'; --temp.length; - auto s2 = assumeUnique(temp); + auto trustedAssumeUnique(T)(T t) @trusted { return assumeUnique(t); } + auto s2 = trustedAssumeUnique(temp); assert(s1 == s2); - foreach (P; TypeTuple!(C*, const(C)*, immutable(C)*)) + void trustedCStringAssert(P, S)(S s) @trusted { - auto p1 = toUTFz!P(s1); - assert(p1[0 .. s1.length] == s1); - assert(p1[s1.length] == '\0'); + auto p = toUTFz!P(s); + assert(p[0 .. s.length] == s); + assert(p[s.length] == '\0'); + } - auto p2 = toUTFz!P(s2); - assert(p2[0 .. s2.length] == s2); - assert(p2[s2.length] == '\0'); + foreach (P; TypeTuple!(C*, const(C)*, immutable(C)*)) + { + trustedCStringAssert!P(s1); + trustedCStringAssert!P(s2); } } }); - static void test(P, S)(S s, size_t line = __LINE__) + static void test(P, S)(S s, size_t line = __LINE__) @trusted { + static size_t zeroLen(C)(const(C)* ptr) @trusted + { + size_t len = 0; + while (*ptr != '\0') { ++ptr; ++len; } + return len; + } + auto p = toUTFz!P(s); immutable len = zeroLen(p); enforce(cmp(s, p[0 .. len]) == 0, @@ -2492,13 +2511,13 @@ unittest $(D toUTF16z) is suitable for calling the 'W' functions in the Win32 API that take an $(D LPWSTR) or $(D LPCWSTR) argument. +/ -const(wchar)* toUTF16z(C)(const(C)[] str) +const(wchar)* toUTF16z(C)(const(C)[] str) @safe pure if (isSomeChar!C) { return toUTFz!(const(wchar)*)(str); } -unittest +@safe pure unittest { //toUTFz is already thoroughly tested, so this will just verify that //toUTF16z compiles properly for the various string types. @@ -2509,7 +2528,7 @@ unittest /* ================================ tests ================================== */ -unittest +pure unittest { debug(utf) printf("utf.toUTF.unittest\n"); @@ -2660,3 +2679,814 @@ version(unittest) C[] _str; } } + + +/** + * Inserted in place of invalid UTF sequences. + * + * References: + * $(LINK http://en.wikipedia.org/wiki/Replacement_character#Replacement_character) + */ +enum dchar replacementDchar = '\uFFFD'; + +/******************************************** + * Iterate a range of char, wchar, or dchars by code unit. + * + * The purpose is to bypass the special case decoding that + * $(XREF array,front) does to character arrays. + * Params: + * r = input range of characters, or array of characters + * Returns: + * input range + */ + +auto byCodeUnit(R)(R r) if (isNarrowString!R) +{ + /* Turn an array into an InputRange. + */ + static struct ByCodeUnitImpl + { + @property bool empty() const { return r.length == 0; } + @property auto ref front() inout { return r[0]; } + void popFront() { r = r[1 .. $]; } + auto ref opIndex(size_t index) inout { return r[index]; } + + auto opSlice(size_t lower, size_t upper) + { + return r[lower..upper]; + } + + @property size_t length() const + { + return r.length; + } + alias opDollar = length; + + @property auto save() + { + return ByCodeUnitImpl(r.save); + } + + private: + R r; + } + + return ByCodeUnitImpl(r); +} + +/// Ditto +auto ref byCodeUnit(R)(R r) + if (!isNarrowString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) +{ + // byCodeUnit for ranges and dchar[] is a no-op + return r; +} + +unittest +{ + { + char[5] s; + int i; + foreach (c; "hello".byCodeUnit.byCodeUnit()) + { + s[i++] = c; + } + assert(s == "hello"); + } + { + wchar[5] s; + int i; + foreach (c; "hello"w.byCodeUnit.byCodeUnit()) + { + s[i++] = c; + } + assert(s == "hello"w); + } + { + dchar[5] s; + int i; + foreach (c; "hello"d.byCodeUnit.byCodeUnit()) + { + s[i++] = c; + } + assert(s == "hello"d); + } + { + auto r = "hello".byCodeUnit(); + assert(r.length == 5); + assert(r[3] == 'l'); + assert(r[2..4][1] == 'l'); + } + { + auto s = "hello".dup.byCodeUnit(); + s.front = 'H'; + assert(s.front == 'H'); + s[1] = 'E'; + assert(s[1] == 'E'); + } + { + auto r = "hello".byCodeUnit.byCodeUnit(); + assert(isForwardRange!(typeof(r))); + auto s = r.save; + r.popFront(); + assert(s.front == 'h'); + } +} + +/**************************** + * Iterate an input range of characters by char, wchar, or dchar. + * + * UTF sequences that cannot be converted to UTF-8 are replaced by U+FFFD + * per "5.22 Best Practice for U+FFFD Substitution" of the Unicode Standard 6.2. + * Hence, byChar, byWchar, and byDchar are not symmetric. + * This algorithm is lazy, and does not allocate memory. + * Purity, nothrow, and safety are inferred from the r parameter. + * Params: + * r = input range of characters, or array of characters + * Returns: + * input range + */ + +auto byChar(R)(R r) if (isNarrowString!R) +{ + /* This and the following two serve as adapters to convert arrays to ranges, + * so the following three + * won't get auto-decoded by std.array.front(). + */ + alias tchar = Unqual!(ElementEncodingType!R); + + static if (is(tchar == char)) + { + return r.byCodeUnit(); + } + else + { + return r.byCodeUnit.byChar(); + } +} + +/// Ditto +auto byWchar(R)(R r) if (isNarrowString!R) +{ + alias tchar = Unqual!(ElementEncodingType!R); + + static if (is(tchar == wchar)) + { + return r.byCodeUnit(); + } + else + { + return r.byCodeUnit.byWchar(); + } +} + +/// Ditto +auto byDchar(R)(R r) if (isNarrowString!R) +{ + alias tchar = Unqual!(ElementEncodingType!R); + + return r.byCodeUnit.byDchar(); +} + + +/// Ditto +auto ref byChar(R)(R r) + if (!isNarrowString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) +{ + alias tchar = Unqual!(ElementEncodingType!R); + + /* Defeat the auto-decoding of std.array.put() by handling arrays of chars + * explicitly. + */ + static if (is(tchar == char)) + { + return r; + } + else + { + static if (is(tchar == wchar)) + { + // Convert wchar => dchar => char + auto r2 = r.byDchar(); + } + else static if (is(tchar == dchar)) + { + alias r2 = r; + } + else + static assert(0); + + static struct byCharImpl + { + this(ref typeof(r2) r) + { + this.r = r; + } + + @property bool empty() + { + return !nLeft && r.empty; + } + + @property auto front() + { + static assert(replacementDchar > 0x7FF && replacementDchar <= 0xFFFF); + if (!nLeft) + { + dchar c = r.front; + + if (c <= 0x7F) + { + buf[0] = cast(char)c; + nLeft = 1; + } + else if (c <= 0x7FF) + { + buf[1] = cast(char)(0xC0 | (c >> 6)); + buf[0] = cast(char)(0x80 | (c & 0x3F)); + nLeft = 2; + } + else if (c <= 0xFFFF) + { + if (0xD800 <= c && c <= 0xDFFF) + c = replacementDchar; + + buf[2] = cast(char)(0xE0 | (c >> 12)); + buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); + buf[0] = cast(char)(0x80 | (c & 0x3F)); + nLeft = 3; + } + else if (c <= 0x10FFFF) + { + buf[3] = cast(char)(0xF0 | (c >> 18)); + buf[2] = cast(char)(0x80 | ((c >> 12) & 0x3F)); + buf[1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); + buf[0] = cast(char)(0x80 | (c & 0x3F)); + nLeft = 4; + } + else + { + buf[2] = cast(char)(0xE0 | (replacementDchar >> 12)); + buf[1] = cast(char)(0x80 | ((replacementDchar >> 6) & 0x3F)); + buf[0] = cast(char)(0x80 | (replacementDchar & 0x3F)); + nLeft = 3; + } + } + return buf[nLeft - 1]; + } + + void popFront() + { + if (!nLeft) + front; + --nLeft; + if (!nLeft) + r.popFront(); + } + + static if (isForwardRange!(typeof(r2))) + { + @property auto save() + { + auto ret = this; + ret.r = r.save; + return ret; + } + } + + private: + typeof(r2) r; + char[4] buf = void; + uint nLeft; + } + + return byCharImpl(r2); + } +} + +unittest +{ + { + char[5] s; + int i; + foreach (c; "hello".byChar.byChar()) + { + //writefln("[%d] '%c'", i, c); + s[i++] = c; + } + assert(s == "hello"); + } + { + char[5+2+3+4+3+3] s; + int i; + dchar[10] a; + a[0..8] = "hello\u07FF\uD7FF\U0010FFFF"d; + a[8] = 0xD800; // invalid + a[9] = cast(dchar)0x110000; // invalid + foreach (c; a[].byChar()) + { + //writefln("[%d] '%c'", i, c); + s[i++] = c; + } + assert(s == "hello\u07FF\uD7FF\U0010FFFF\uFFFD\uFFFD"); + } + { + auto r = "hello"w.byChar(); + r.popFront(); + r.popFront(); + assert(r.front == 'l'); + } + { + auto r = "hello"d.byChar(); + r.popFront(); + r.popFront(); + assert(r.front == 'l'); + } + { + auto r = "hello"d.byChar(); + assert(isForwardRange!(typeof(r))); + auto s = r.save; + r.popFront(); + assert(s.front == 'h'); + } +} + + +/// Ditto +auto ref byWchar(R)(R r) + if (!isNarrowString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) +{ + alias tchar = Unqual!(ElementEncodingType!R); + + static if (is(tchar == wchar)) + { + return r; + } + else + { + static if (is(tchar == char)) + { + auto r2 = r.byDchar(); + } + else static if (is(tchar == dchar)) + { + alias r2 = r; + } + else + static assert(0); + + static struct byWcharImpl + { + this(ref typeof(r2) r) + { + this.r = r; + } + + @property bool empty() + { + return !nLeft && r.empty; + } + + @property auto front() + { + static assert(replacementDchar > 0x7FF && replacementDchar <= 0xFFFF); + if (!nLeft) + { + dchar c = r.front; + + if (c <= 0xFFFF) + { + if (0xD800 <= c && c <= 0xDFFF) + c = replacementDchar; + + buf[0] = cast(wchar)c; + nLeft = 1; + } + else if (c <= 0x10FFFF) + { + buf[1] = cast(wchar)((((c - 0x10000) >> 10) & 0x3FF) + 0xD800); + buf[0] = cast(wchar)(((c - 0x10000) & 0x3FF) + 0xDC00); + nLeft = 2; + } + else + { + buf[0] = replacementDchar; + nLeft = 1; + } + } + return buf[nLeft - 1]; + } + + void popFront() + { + if (!nLeft) + front; + --nLeft; + if (!nLeft) + r.popFront(); + } + + static if (isForwardRange!(typeof(r2))) + { + @property auto save() + { + auto ret = this; + ret.r = r.save; + return ret; + } + } + + private: + typeof(r2) r; + wchar[2] buf = void; + uint nLeft; + } + + return byWcharImpl(r2); + } +} + +unittest +{ + { + wchar[11] s; + int i; + dchar[10] a; + a[0..8] = "hello\u07FF\uD7FF\U0010FFFF"d; + a[8] = 0xD800; // invalid + a[9] = cast(dchar)0x110000; // invalid + foreach (c; a[].byWchar()) + { + //writefln("[%d] '%c' x%x", i, c, c); + s[i++] = c; + } + foreach (j, wchar c; "hello\u07FF\uD7FF\U0010FFFF\uFFFD\uFFFD"w) + { + //writefln("[%d] '%c' x%x", j, c, c); + } + assert(s == "hello\u07FF\uD7FF\U0010FFFF\uFFFD\uFFFD"w); + } + + { + auto r = "hello".byWchar(); + r.popFront(); + r.popFront(); + assert(r.front == 'l'); + } + { + auto r = "hello"d.byWchar(); + r.popFront(); + r.popFront(); + assert(r.front == 'l'); + } + { + auto r = "hello"d.byWchar(); + assert(isForwardRange!(typeof(r))); + auto s = r.save; + r.popFront(); + assert(s.front == 'h'); + } +} + + +/// Ditto +auto ref byDchar(R)(R r) + if (!isNarrowString!R && isInputRange!R && isSomeChar!(ElementEncodingType!R)) +{ + alias tchar = Unqual!(ElementEncodingType!R); + + static if (is(tchar == char)) + { + static struct byDcharImpl + { + this(ref R r) + { + this.r = r; + } + + @property bool empty() + { + return !haveData && r.empty; + } + + @property dchar front() + { + if (haveData) + return frontChar; + dchar c = r.front; + if (c < 0x80) + { + } + else + { + uint fst = c; // upper control bits are masked out later + + /* Dchar bitmask for different numbers of UTF-8 code units. + */ + enum bitMask = [(1 << 7) - 1, (1 << 11) - 1, (1 << 16) - 1, (1 << 21) - 1]; + + foreach (i; TypeTuple!(1, 2, 3)) + { + + r.popFront(); + if (r.empty) + break; + + ubyte tmp = r.front; + + if ((tmp & 0xC0) != 0x80) + break; + + c = (c << 6) | (tmp & 0x3F); + + if (!(fst & (0x40 >> i))) // if no more bytes + { + c &= bitMask[i]; // mask out control bits + + // overlong, could have been encoded with i bytes + if ((c & ~bitMask[i - 1]) == 0) + break; + + // check for surrogates only needed for 3 bytes + static if (i == 2) + { + if (c >= 0xD800 && c < 0xE000) + break; + } + + // check for out of range only needed for 4 bytes + static if (i == 3) + { + if (c > 0x10FFFF) + break; + } + + frontChar = c; + haveData = true; + return c; + } + } + c = replacementDchar; + } + frontChar = c; + haveData = true; + return c; + } + + void popFront() + { + if (!haveData) + front; + r.popFront(); + haveData = false; + } + + static if (isForwardRange!R) + { + @property auto save() + { + auto ret = this; + ret.r = r.save; + return ret; + } + } + + private: + R r; + dchar frontChar; + bool haveData; + } + return byDcharImpl(r); + } + else static if (is(tchar == wchar)) + { + static struct byDcharImpl + { + this(ref R r) + { + this.r = r; + } + + @property bool empty() + { + return !haveData && r.empty; + } + + @property dchar front() + { + if (haveData) + return frontChar; + dchar c = r.front; + if (c < 0xD800) + { + } + else if (c <= 0xDBFF) + { + r.popFront(); + if (r.empty) + c = replacementDchar; + else + { + dchar c2 = r.front; + if (c2 < 0xDC00 || c2 > 0xDFFF) + c = replacementDchar; + else + c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00); + } + } + else if (c <= 0xDFFF) + c = replacementDchar; + frontChar = c; + haveData = true; + return c; + } + + void popFront() + { + if (!haveData) + front; + r.popFront(); + haveData = false; + } + + static if (isForwardRange!R) + { + @property auto save() + { + auto ret = this; + ret.r = r.save; + return ret; + } + } + + private: + R r; + dchar frontChar; + bool haveData; + } + return byDcharImpl(r); + } + else static if (is(tchar == dchar)) + { + return r; + } + else + static assert(0); +} + +import std.stdio; + +unittest +{ + { + dchar[9] s; + int i; + string a = "hello\u07FF\uD7FF\U00010000\U0010FFFF"; // 1,2,3,4 byte sequences + foreach (c; a.byDchar()) + { + //writefln("[%d] '%c' x%x", i, c, c); + s[i++] = c; + } + assert(s == "hello\u07FF\uD7FF\U00010000\U0010FFFF"d); + } + { + char[1] cs; + cs[0] = 0xC0; // truncated + auto r = cs[].byDchar(); + assert(!r.empty); + dchar c = r.front; + assert(c == replacementDchar); + } + { + char[2] cs; + cs[0] = 0xC0; + cs[1] = 0xC0; // invalid continuation + auto r = cs[].byDchar(); + assert(!r.empty); + assert(r.front == r.front); + dchar c = r.front; + assert(c == replacementDchar); + } + { + char[3] cs; + enum x = 0xDC00; // invalid surrogate value + cs[0] = 0xE0 | (x >> 12); + cs[1] = 0x80 | ((x >> 6) & 0x3F); + cs[2] = 0x80 | (x & 0x3F); + auto r = cs[].byDchar(); + assert(!r.empty); + dchar c = r.front; + assert(c == replacementDchar); + } + { + char[4] cs; + cs[0] = 0xF0; + cs[1] = 0x82; + cs[2] = 0x82; + cs[3] = 0xAC; // overlong + auto r = cs[].byDchar(); + assert(!r.empty); + assert(r.front == r.front); + dchar c = r.front; + assert(c == replacementDchar); + } + { + char[4] cs; + enum x = 0x110000; // out of range + cs[0] = cast(char)(0xF0 | (x >> 18)); + cs[1] = cast(char)(0x80 | ((x >> 12) & 0x3F)); + cs[2] = cast(char)(0x80 | ((x >> 6) & 0x3F)); + cs[3] = cast(char)(0x80 | (x & 0x3F)); + auto r = cs[].byDchar(); + assert(!r.empty); + dchar c = r.front; + assert(c == replacementDchar); + } + { + auto r = "hello".byDchar(); + r.popFront(); + r.popFront(); + assert(r.front == 'l'); + } + + { + dchar[8] s; + int i; + wstring a = "hello\u07FF\uD7FF\U0010FFFF"w; + foreach (c; a.byDchar()) + { + //writefln("[%d] '%c' x%x", i, c, c); + s[i++] = c; + } + assert(s == "hello\u07FF\uD7FF\U0010FFFF"d); + } + { + wchar[1] ws; + ws[0] = 0xD801; // truncated surrogate pair + auto r = ws[].byDchar(); + assert(!r.empty); + dchar c = r.front; + assert(c == replacementDchar); + } + { + wchar[1] ws; + ws[0] = 0xDC00; // unpaired 2nd surrogate + auto r = ws[].byDchar(); + assert(!r.empty); + dchar c = r.front; + assert(c == replacementDchar); + } + { + wchar[2] ws; + ws[0] = 0xD800; + ws[1] = 0xDD00; // correct surrogate pair + auto r = ws[].byDchar(); + assert(!r.empty); + assert(r.front == r.front); + dchar c = r.front; + assert(c == '\U00010100'); + } + { + wchar[2] ws; + ws[0] = 0xD800; + ws[1] = 0xDBFF; // second surrogate out of range + auto r = ws[].byDchar(); + assert(!r.empty); + assert(r.front == r.front); + dchar c = r.front; + assert(c == replacementDchar); + } + { + auto r = "hello"w.byDchar(); + r.popFront(); + r.popFront(); + assert(r.front == 'l'); + } + + { + dchar[5] s; + int i; + dstring a = "hello"d; + foreach (c; a.byDchar.byDchar()) + { + //writefln("[%d] '%c' x%x", i, c, c); + s[i++] = c; + } + assert(s == "hello"d); + } + { + auto r = "hello".byDchar(); + assert(isForwardRange!(typeof(r))); + auto s = r.save; + r.popFront(); + assert(s.front == 'h'); + } + { + auto r = "hello"w.byDchar(); + assert(isForwardRange!(typeof(r))); + auto s = r.save; + r.popFront(); + assert(s.front == 'h'); + } +} + diff --git a/libphobos/src/std/uuid.d b/libphobos/src/std/uuid.d index 9c1c2de7c..b0039a5a0 100644 --- a/libphobos/src/std/uuid.d +++ b/libphobos/src/std/uuid.d @@ -108,7 +108,7 @@ import std.conv, std.digest.md, std.digest.sha, std.random, std.range, std.strin public struct UUID { private: - @safe nothrow pure char toChar(size_t i) const + @safe pure nothrow char toChar(size_t i) const { if(i <= 9) return cast(char)('0' + i); @@ -116,7 +116,7 @@ public struct UUID return cast(char)('a' + (i-10)); } - @safe nothrow pure char[36] _toString() const + @safe pure nothrow char[36] _toString() const { char[36] result; @@ -625,7 +625,7 @@ public struct UUID /** * Swap the data of this UUID with the data of rhs. */ - @safe nothrow pure void swap(ref UUID rhs) + @safe pure nothrow void swap(ref UUID rhs) { auto bck = data; data = rhs.data; @@ -1114,6 +1114,14 @@ unittest * as long as these characters do not contain [0-9a-fA-F]) * ) * + * Note: + * Like most parsers, it consumes its argument. This means: + * ------------------------- + * string s = "8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46"; + * parseUUID(s); + * assert(s == ""); + * ------------------------- + * * Throws: * $(LREF UUIDParsingException) if the input is invalid * @@ -1316,8 +1324,8 @@ UUID parseUUID(Range)(ref Range uuidRange) if(isInputRange!Range } } } - alias TestRange!false TestInputRange; - alias TestRange!true TestForwardRange; + alias TestInputRange = TestRange!false; + alias TestForwardRange = TestRange!true; assert(isInputRange!TestInputRange); assert(is(ElementType!TestInputRange == dchar)); @@ -1475,7 +1483,7 @@ enum x500Namespace = UUID("6ba7b814-9dad-11d1-80b4-00c04fd430c8"); * writeln(found); * ------------------- */ -enum uuidRegex = r"[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}" +enum uuidRegex = "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}"~ "-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"; /// @@ -1484,11 +1492,11 @@ unittest import std.algorithm; import std.regex; - string test = "Lorem ipsum dolor sit amet, consetetur " - "6ba7b814-9dad-11d1-80b4-00c04fd430c8 sadipscing \n" - "elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore \r\n" - "magna aliquyam erat, sed diam voluptua. " - "8ab3060e-2cba-4f23-b74c-b52db3bdfb46 At vero eos et accusam et " + string test = "Lorem ipsum dolor sit amet, consetetur "~ + "6ba7b814-9dad-11d1-80b4-00c04fd430c8 sadipscing \n"~ + "elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore \r\n"~ + "magna aliquyam erat, sed diam voluptua. "~ + "8ab3060e-2cba-4f23-b74c-b52db3bdfb46 At vero eos et accusam et "~ "justo duo dolores et ea rebum."; auto r = regex(uuidRegex, "g"); @@ -1530,12 +1538,20 @@ public class UUIDParsingException : Exception private this(string input, size_t pos, Reason why = Reason.unknown, string msg = "", Throwable next = null, string file = __FILE__, size_t line = __LINE__) pure @trusted { - input = input; - position = pos; - reason = why; + this.input = input; + this.position = pos; + this.reason = why; string message = format("An error occured in the UUID parser: %s\n" ~ " * Input:\t'%s'\n * Position:\t%s", msg, replace(replace(input, "\r", "\\r"), "\n", "\\n"), pos); super(message, file, line, next); } } + +unittest +{ + auto ex = new UUIDParsingException("foo", 10, UUIDParsingException.Reason.tooMuch); + assert(ex.input == "foo"); + assert(ex.position == 10); + assert(ex.reason == UUIDParsingException.Reason.tooMuch); +} diff --git a/libphobos/src/std/variant.d b/libphobos/src/std/variant.d index 2970bc5c7..2f07aec9d 100644 --- a/libphobos/src/std/variant.d +++ b/libphobos/src/std/variant.d @@ -91,38 +91,42 @@ struct This; template AssociativeArray(T) { enum bool valid = false; - alias void Key; - alias void Value; + alias Key = void; + alias Value = void; } template AssociativeArray(T : V[K], K, V) { enum bool valid = true; - alias K Key; - alias V Value; + alias Key = K; + alias Value = V; } template This2Variant(V, T...) { - static if (T.length == 0) alias TypeTuple!() This2Variant; + static if (T.length == 0) + alias This2Variant = TypeTuple!(); else static if (is(AssociativeArray!(T[0]).Key == This)) { static if (is(AssociativeArray!(T[0]).Value == This)) - alias TypeTuple!(V[V], - This2Variant!(V, T[1 .. $])) This2Variant; + alias This2Variant = + TypeTuple!(V[V], + This2Variant!(V, T[1 .. $])); else - alias TypeTuple!(AssociativeArray!(T[0]).Value[V], - This2Variant!(V, T[1 .. $])) This2Variant; + alias This2Variant = + TypeTuple!(AssociativeArray!(T[0]).Value[V], + This2Variant!(V, T[1 .. $])); } else static if (is(AssociativeArray!(T[0]).Value == This)) - alias TypeTuple!(V[AssociativeArray!(T[0]).Key], - This2Variant!(V, T[1 .. $])) This2Variant; + alias This2Variant = + TypeTuple!(V[AssociativeArray!(T[0]).Key], + This2Variant!(V, T[1 .. $])); else static if (is(T[0] == This[])) - alias TypeTuple!(V[], This2Variant!(V, T[1 .. $])) This2Variant; + alias This2Variant = TypeTuple!(V[], This2Variant!(V, T[1 .. $])); else static if (is(T[0] == This*)) - alias TypeTuple!(V*, This2Variant!(V, T[1 .. $])) This2Variant; + alias This2Variant = TypeTuple!(V*, This2Variant!(V, T[1 .. $])); else - alias TypeTuple!(T[0], This2Variant!(V, T[1 .. $])) This2Variant; + alias This2Variant = TypeTuple!(T[0], This2Variant!(V, T[1 .. $])); } /** @@ -153,7 +157,7 @@ template This2Variant(V, T...) struct VariantN(size_t maxDataSize, AllowedTypesX...) { - alias This2Variant!(VariantN, AllowedTypesX) AllowedTypes; + alias AllowedTypes = This2Variant!(VariantN, AllowedTypesX); private: // Compute the largest practical size from maxDataSize @@ -163,7 +167,6 @@ private: ubyte[maxDataSize] data; } enum size = SizeChecker.sizeof - (int function()).sizeof; - static assert(size >= (void*).sizeof); /** Tells whether a type $(D_PARAM T) is statically allowed for * storage inside a $(D_PARAM VariantN) object by looking @@ -182,7 +185,7 @@ private: // Each internal operation is encoded with an identifier. See // the "handler" function below. - enum OpID { getTypeInfo, get, compare, testConversion, toString, + enum OpID { getTypeInfo, get, compare, equals, testConversion, toString, index, indexAssign, catAssign, copyOut, length, apply } @@ -212,6 +215,7 @@ private: // no need to copy the data (it's garbage) break; case OpID.compare: + case OpID.equals: auto rhs = cast(const VariantN *) parm; return rhs.peek!(A) ? 0 // all uninitialized are equal @@ -247,6 +251,42 @@ private: } return null; } + + static ptrdiff_t compare(A* rhsPA, A* zis, OpID selector) + { + static if (is(typeof(*rhsPA == *zis))) + { + // Work-around for bug 12164. + // Without the check for if selector is -1, this function always returns 0. + // TODO: Remove this once 12164 is fixed. + if (*rhsPA == *zis && selector != cast(OpID)-1) + { + return 0; + } + static if (is(typeof(*zis < *rhsPA))) + { + // Many types (such as any using the default Object opCmp) + // will throw on an invalid opCmp, so do it only + // if the caller requests it. + if (selector == OpID.compare) + return *zis < *rhsPA ? -1 : 1; + else + return ptrdiff_t.min; + } + else + { + // Not equal, and type does not support ordering + // comparisons. + return ptrdiff_t.min; + } + } + else + { + // Type does not support comparisons at all. + return ptrdiff_t.min; + } + } + auto zis = getPtr(pStore); // Input: TypeInfo object // Output: target points to a copy of *me, if me was not null @@ -254,25 +294,37 @@ private: // by the incoming TypeInfo static bool tryPutting(A* src, TypeInfo targetType, void* target) { - alias TypeTuple!(A, ImplicitConversionTargets!A) AllTypes; + alias UA = Unqual!A; + alias MutaTypes = TypeTuple!(UA, ImplicitConversionTargets!UA); + alias ConstTypes = staticMap!(ConstOf, MutaTypes); + alias SharedTypes = staticMap!(SharedOf, MutaTypes); + alias SharedConstTypes = staticMap!(SharedConstOf, MutaTypes); + alias ImmuTypes = staticMap!(ImmutableOf, MutaTypes); + + static if (is(A == immutable)) + alias AllTypes = TypeTuple!(ImmuTypes, ConstTypes, SharedConstTypes); + else static if (is(A == shared)) + { + static if (is(A == const)) + alias AllTypes = SharedConstTypes; + else + alias AllTypes = TypeTuple!(SharedTypes, SharedConstTypes); + } + else + { + static if (is(A == const)) + alias AllTypes = ConstTypes; + else + alias AllTypes = TypeTuple!(MutaTypes, ConstTypes); + } + foreach (T ; AllTypes) { - if (targetType != typeid(T) && - targetType != typeid(const(T))) + if (targetType != typeid(T)) { - static if (isImplicitlyConvertible!(T, immutable(T))) - { - if (targetType != typeid(immutable(T))) - { - continue; - } - } - else - { - continue; - } + continue; } - // found!!! + static if (is(typeof(*cast(T*) target = *src))) { auto zat = cast(T*) target; @@ -282,6 +334,18 @@ private: *zat = *src; } } + else static if (is(T == const(U), U) || + is(T == shared(U), U) || + is(T == shared const(U), U) || + is(T == immutable(U), U)) + { + auto zat = cast(U*) target; + if (src) + { + assert(target, "target must be non-null"); + *zat = *(cast(UA*) (src)); + } + } else { // type is not assignable @@ -315,6 +379,7 @@ private: case OpID.testConversion: return !tryPutting(null, *cast(TypeInfo*) parm, null); case OpID.compare: + case OpID.equals: auto rhsP = cast(VariantN *) parm; auto rhsType = rhsP.type; // Are we the same? @@ -322,28 +387,7 @@ private: { // cool! Same type! auto rhsPA = getPtr(&rhsP.store); - static if (is(typeof(A.init == A.init))) - { - if (*rhsPA == *zis) - { - return 0; - } - static if (is(typeof(A.init < A.init))) - { - return *zis < *rhsPA ? -1 : 1; - } - else - { - // Not equal, and type does not support ordering - // comparisons. - return ptrdiff_t.min; - } - } - else - { - // Type does not support comparisons at all. - return ptrdiff_t.min; - } + return compare(rhsPA, zis, selector); } else if (rhsType == typeid(void)) { // No support for ordering comparisons with @@ -358,7 +402,10 @@ private: // also fix up its fptr temp.fptr = rhsP.fptr; // now lhsWithRhsType is a full-blown VariantN of rhs's type - return temp.opCmp(*rhsP); + if (selector == OpID.compare) + return temp.opCmp(*rhsP); + else + return temp.opEquals(*rhsP) ? 0 : 1; } // Does rhs convert to zis? *cast(TypeInfo*) &temp.store = typeid(A); @@ -366,28 +413,7 @@ private: { // cool! Now temp has rhs in my type! auto rhsPA = getPtr(&temp.store); - static if (is(typeof(A.init == A.init))) - { - if (*rhsPA == *zis) - { - return 0; - } - static if (is(typeof(A.init < A.init))) - { - return *zis < *rhsPA ? -1 : 1; - } - else - { - // Not equal, and type does not support ordering - // comparisons. - return ptrdiff_t.min; - } - } - else - { - // Type does not support comparisons at all. - return ptrdiff_t.min; - } + return compare(rhsPA, zis, selector); } return ptrdiff_t.min; // dunno case OpID.toString: @@ -413,12 +439,7 @@ private: case OpID.index: // Added allowed!(...) prompted by a bug report by Chris // Nicholson-Sauls. - static if (isStaticArray!(A) && allowed!(typeof(A.init))) - { - enforce(0, "Not implemented"); - } - // Can't handle void arrays as there isn't any result to return. - static if (isDynamicArray!(A) && !is(Unqual!(typeof(A.init[0])) == void) && allowed!(typeof(A.init[0]))) + static if (isArray!(A) && !is(Unqual!(typeof(A.init[0])) == void) && allowed!(typeof(A.init[0]))) { // array type; input and output are the same VariantN auto result = cast(VariantN*) parm; @@ -466,7 +487,7 @@ private: { // array type; parm is the element to append auto arg = cast(VariantN*) parm; - alias typeof((*zis)[0]) E; + alias E = typeof((*zis)[0]); if (arg[0].convertsTo!(E)) { // append one element to the array @@ -566,7 +587,7 @@ public: else static if (is(T : const(VariantN))) { static assert(false, - "Assigning Variant objects from const Variant" + "Assigning Variant objects from const Variant"~ " objects is currently not supported."); } else @@ -651,20 +672,27 @@ public: * assert(a == 6); * ---- */ - @property inout T * peek(T)() inout + @property inout(T)* peek(T)() inout { static if (!is(T == void)) static assert(allowed!(T), "Cannot store a " ~ T.stringof ~ " in a " ~ VariantN.stringof); - return type == typeid(T) ? cast(T*) &store : null; + if (type != typeid(T)) + return null; + static if (T.sizeof <= size) + return cast(inout T*)&store; + else + return *cast(inout T**)&store; } /** * Returns the $(D_PARAM typeid) of the currently held value. */ - @property TypeInfo type() const + @property TypeInfo type() const nothrow @trusted { + scope(failure) assert(0); + TypeInfo result; fptr(OpID.getTypeInfo, null, &result); return result; @@ -697,11 +725,11 @@ public: // { // static if (isStaticArray!(T)) // { - // alias typeof(testing123(&T[0])) DecayStaticToDynamicArray; + // alias DecayStaticToDynamicArray = typeof(testing123(&T[0])); // } // else // { - // alias T DecayStaticToDynamicArray; + // alias DecayStaticToDynamicArray = T; // } // } @@ -738,7 +766,10 @@ public: union Buf { TypeInfo info; - Unqual!T result; + static if (is(T == shared)) + shared(Unqual!T) result; + else + Unqual!T result; } auto p = *cast(T**) &store; Buf buf = { typeid(T) }; @@ -761,7 +792,7 @@ public: @property T coerce(T)() { - static if (isNumeric!(T)) + static if (isNumeric!T || isBoolean!T) { if (convertsTo!real) { @@ -781,7 +812,6 @@ public: { return to!T(get!(immutable(char)[])); } - else { enforce(false, text("Type ", type, " does not convert to ", @@ -804,13 +834,6 @@ public: } } - // testing the string coerce - unittest - { - Variant a = "10"; - assert(a.coerce!int == 10); - } - /** * Formats the stored value as a string. */ @@ -830,10 +853,10 @@ public: bool opEquals(T)(auto ref T rhs) const { static if (is(Unqual!T == VariantN)) - alias rhs temp; + alias temp = rhs; else auto temp = VariantN(rhs); - return !fptr(OpID.compare, cast(ubyte[size]*) &store, + return !fptr(OpID.equals, cast(ubyte[size]*) &store, cast(void*) &temp); } @@ -852,7 +875,7 @@ public: int opCmp(T)(T rhs) { static if (is(T == VariantN)) - alias rhs temp; + alias temp = rhs; else auto temp = VariantN(rhs); auto result = fptr(OpID.compare, &store, &temp); @@ -869,7 +892,7 @@ public: * Computes the hash of the held value. */ - size_t toHash() + size_t toHash() const nothrow @safe { return type.getHash(&store); } @@ -1120,7 +1143,7 @@ public: */ int opApply(Delegate)(scope Delegate dg) if (is(Delegate == delegate)) { - alias ParameterTypeTuple!(Delegate)[0] A; + alias A = ParameterTypeTuple!(Delegate)[0]; if (type == typeid(A[])) { auto arr = get!(A[]); @@ -1163,6 +1186,19 @@ unittest assert(v("43") == 43); } +// opIndex with static arrays, issue 12771 +unittest +{ + int[4] elements = [0, 1, 2, 3]; + Variant v = elements; + assert(v == elements); + assert(v[2] == 2); + assert(v[3] == 3); + v[2] = 6; + assert(v[2] == 6); + assert(v != elements); +} + //Issue# 8195 unittest { @@ -1176,8 +1212,8 @@ unittest } static assert(S.sizeof >= Variant.sizeof); - alias TypeTuple!(string, int, S) Types; - alias VariantN!(maxSize!Types, Types) MyVariant; + alias Types = TypeTuple!(string, int, S); + alias MyVariant = VariantN!(maxSize!Types, Types); auto v = MyVariant(S.init); assert(v == S.init); @@ -1224,7 +1260,7 @@ unittest template Algebraic(T...) { - alias VariantN!(maxSize!(T), T) Algebraic; + alias Algebraic = VariantN!(maxSize!(T), T); } /** @@ -1237,7 +1273,7 @@ $(D_PARAM VariantN) directly with a different maximum size either for storing larger types, or for saving memory. */ -alias VariantN!(maxSize!(creal, char[], void delegate())) Variant; +alias Variant = VariantN!(maxSize!(creal, char[], void delegate())); /** * Returns an array of variants constructed from $(D_PARAM args). @@ -1316,18 +1352,18 @@ static class VariantException : Exception unittest { - alias This2Variant!(char, int, This[int]) W1; - alias TypeTuple!(int, char[int]) W2; + alias W1 = This2Variant!(char, int, This[int]); + alias W2 = TypeTuple!(int, char[int]); static assert(is(W1 == W2)); - alias Algebraic!(void, string) var_t; + alias var_t = Algebraic!(void, string); var_t foo = "quux"; } unittest { // @@@BUG@@@ - // alias Algebraic!(real, This[], This[int], This[This]) A; + // alias A = Algebraic!(real, This[], This[int], This[This]); // A v1, v2, v3; // v2 = 5.0L; // v3 = 42.0L; @@ -1395,6 +1431,26 @@ unittest // coerce tests a = Variant(42.22); assert(a.coerce!(int) == 42); a = cast(short) 5; assert(a.coerce!(double) == 5); + a = Variant("10"); assert(a.coerce!int == 10); + + a = Variant(1); + assert(a.coerce!bool); + a = Variant(0); + assert(!a.coerce!bool); + + a = Variant(1.0); + assert(a.coerce!bool); + a = Variant(0.0); + assert(!a.coerce!bool); + a = Variant(float.init); + assertThrown!ConvException(a.coerce!bool); + + a = Variant("true"); + assert(a.coerce!bool); + a = Variant("false"); + assert(!a.coerce!bool); + a = Variant(""); + assertThrown!ConvException(a.coerce!bool); // Object tests class B1 {} @@ -1402,8 +1458,7 @@ unittest a = new B2; assert(a.coerce!(B1) !is null); a = new B1; -// BUG: I can't get the following line to pass: -// assert(collectException(a.coerce!(B2) is null)); + assert(collectException(a.coerce!(B2) is null)); a = cast(Object) new B2; // lose static type info; should still work assert(a.coerce!(B2) !is null); @@ -1632,6 +1687,30 @@ unittest v = null; } +// Class and interface opEquals, issue 12157 +unittest +{ + class Foo { } + + class DerivedFoo : Foo { } + + Foo f1 = new Foo(); + Foo f2 = new DerivedFoo(); + + Variant v1 = f1, v2 = f2; + assert(v1 == f1); + assert(v1 != new Foo()); + assert(v1 != f2); + assert(v2 != v1); + assert(v2 == f2); + + // TODO: Remove once 12164 is fixed. + // Verify our assumption that there is no -1 OpID. + // Could also use std.algorithm.canFind at compile-time, but that may create bloat. + foreach(member; EnumMembers!(Variant.OpID)) + assert(member != cast(Variant.OpID)-1); +} + // Const parameters with opCall, issue 11361. unittest { @@ -1702,6 +1781,37 @@ unittest assertThrown!VariantException(Variant(A(3)) < Variant(A(4))); } +// Handling of empty types and arrays, e.g. issue 10958 +unittest +{ + class EmptyClass { } + struct EmptyStruct { } + alias EmptyArray = void[0]; + alias Alg = Algebraic!(EmptyClass, EmptyStruct, EmptyArray); + + Variant testEmpty(T)() + { + T inst; + Variant v = inst; + assert(v.get!T == inst); + assert(v.peek!T !is null); + assert(*v.peek!T == inst); + Alg alg = inst; + assert(alg.get!T == inst); + return v; + } + + testEmpty!EmptyClass(); + testEmpty!EmptyStruct(); + testEmpty!EmptyArray(); + + // EmptyClass/EmptyStruct sizeof is 1, so we have this to test just size 0. + EmptyArray arr = EmptyArray.init; + Algebraic!(EmptyArray) a = arr; + assert(a.length == 0); + assert(a.get!EmptyArray == arr); +} + // Handling of void function pointers / delegates, e.g. issue 11360 unittest { @@ -1714,6 +1824,37 @@ unittest assert(v2() == 3); } +// Using peek for large structs, issue 8580 +unittest +{ + struct TestStruct(bool pad) + { + int val1; + static if (pad) + ubyte[Variant.size] padding; + int val2; + } + + void testPeekWith(T)() + { + T inst; + inst.val1 = 3; + inst.val2 = 4; + Variant v = inst; + T* original = v.peek!T; + assert(original.val1 == 3); + assert(original.val2 == 4); + original.val1 = 6; + original.val2 = 8; + T modified = v.get!T; + assert(modified.val1 == 6); + assert(modified.val2 == 8); + } + + testPeekWith!(TestStruct!false)(); + testPeekWith!(TestStruct!true)(); +} + /** * Applies a delegate or function to the given Algebraic depending on the held type, * ensuring that all types are handled by the visiting functions. @@ -1722,7 +1863,7 @@ unittest * with $(D_PARM variant)'s current value. Visiting handlers are passed * in the template parameter list. * It is statically ensured that all types of - * $(D_PARAM variant) are handled accross all handlers. + * $(D_PARAM variant) are handled across all handlers. * $(D_PARAM visit) allows delegates and static functions to be passed * as parameters. * @@ -1753,7 +1894,7 @@ unittest * == -1); * ---------------------- * Returns: The return type of visit is deduced from the visiting functions and must be - * the same accross all overloads. + * the same across all overloads. * Throws: If no parameter-less, error function is specified: * $(D_PARAM VariantException) if $(D_PARAM variant) doesn't hold a value. */ @@ -1851,7 +1992,7 @@ unittest * ---------------------- * * Returns: The return type of tryVisit is deduced from the visiting functions and must be - * the same accross all overloads. + * the same across all overloads. * Throws: If no parameter-less, error function is specified: $(D_PARAM VariantException) if * $(D_PARAM variant) doesn't hold a value or * if $(D_PARAM variant) holds a value which isn't handled by the visiting @@ -1909,7 +2050,7 @@ unittest private auto visitImpl(bool Strict, VariantType, Handler...)(VariantType variant) if (isAlgebraic!VariantType && Handler.length > 0) { - alias VariantType.AllowedTypes AllowedTypes; + alias AllowedTypes = VariantType.AllowedTypes; /** @@ -1937,7 +2078,7 @@ private auto visitImpl(bool Strict, VariantType, Handler...)(VariantType variant // Handle normal function objects static if (isSomeFunction!dg) { - alias ParameterTypeTuple!dg Params; + alias Params = ParameterTypeTuple!dg; static if (Params.length == 0) { // Just check exception functions in the first @@ -2038,3 +2179,177 @@ unittest v2 = v1; // AssertError: target must be non-null assert(v1 == v2); } +unittest +{ + // http://d.puremagic.com/issues/show_bug.cgi?id=7069 + Variant v; + + int i = 10; + v = i; + foreach (qual; TypeTuple!(MutableOf, ConstOf)) + { + assert(v.get!(qual!int) == 10); + assert(v.get!(qual!float) == 10.0f); + } + foreach (qual; TypeTuple!(ImmutableOf, SharedOf, SharedConstOf)) + { + assertThrown!VariantException(v.get!(qual!int)); + } + + const(int) ci = 20; + v = ci; + foreach (qual; TypeTuple!(ConstOf)) + { + assert(v.get!(qual!int) == 20); + assert(v.get!(qual!float) == 20.0f); + } + foreach (qual; TypeTuple!(MutableOf, ImmutableOf, SharedOf, SharedConstOf)) + { + assertThrown!VariantException(v.get!(qual!int)); + assertThrown!VariantException(v.get!(qual!float)); + } + + immutable(int) ii = ci; + v = ii; + foreach (qual; TypeTuple!(ImmutableOf, ConstOf, SharedConstOf)) + { + assert(v.get!(qual!int) == 20); + assert(v.get!(qual!float) == 20.0f); + } + foreach (qual; TypeTuple!(MutableOf, SharedOf)) + { + assertThrown!VariantException(v.get!(qual!int)); + assertThrown!VariantException(v.get!(qual!float)); + } + + int[] ai = [1,2,3]; + v = ai; + foreach (qual; TypeTuple!(MutableOf, ConstOf)) + { + assert(v.get!(qual!(int[])) == [1,2,3]); + assert(v.get!(qual!(int)[]) == [1,2,3]); + } + foreach (qual; TypeTuple!(ImmutableOf, SharedOf, SharedConstOf)) + { + assertThrown!VariantException(v.get!(qual!(int[]))); + assertThrown!VariantException(v.get!(qual!(int)[])); + } + + const(int[]) cai = [4,5,6]; + v = cai; + foreach (qual; TypeTuple!(ConstOf)) + { + assert(v.get!(qual!(int[])) == [4,5,6]); + assert(v.get!(qual!(int)[]) == [4,5,6]); + } + foreach (qual; TypeTuple!(MutableOf, ImmutableOf, SharedOf, SharedConstOf)) + { + assertThrown!VariantException(v.get!(qual!(int[]))); + assertThrown!VariantException(v.get!(qual!(int)[])); + } + + immutable(int[]) iai = [7,8,9]; + v = iai; + //assert(v.get!(immutable(int[])) == [7,8,9]); // Bug ??? runtime error + assert(v.get!(immutable(int)[]) == [7,8,9]); + assert(v.get!(const(int[])) == [7,8,9]); + assert(v.get!(const(int)[]) == [7,8,9]); + //assert(v.get!(shared(const(int[]))) == cast(shared const)[7,8,9]); // Bug ??? runtime error + //assert(v.get!(shared(const(int))[]) == cast(shared const)[7,8,9]); // Bug ??? runtime error + foreach (qual; TypeTuple!(MutableOf)) + { + assertThrown!VariantException(v.get!(qual!(int[]))); + assertThrown!VariantException(v.get!(qual!(int)[])); + } + + class A {} + class B : A {} + B b = new B(); + v = b; + foreach (qual; TypeTuple!(MutableOf, ConstOf)) + { + assert(v.get!(qual!B) is b); + assert(v.get!(qual!A) is b); + assert(v.get!(qual!Object) is b); + } + foreach (qual; TypeTuple!(ImmutableOf, SharedOf, SharedConstOf)) + { + assertThrown!VariantException(v.get!(qual!B)); + assertThrown!VariantException(v.get!(qual!A)); + assertThrown!VariantException(v.get!(qual!Object)); + } + + const(B) cb = new B(); + v = cb; + foreach (qual; TypeTuple!(ConstOf)) + { + assert(v.get!(qual!B) is cb); + assert(v.get!(qual!A) is cb); + assert(v.get!(qual!Object) is cb); + } + foreach (qual; TypeTuple!(MutableOf, ImmutableOf, SharedOf, SharedConstOf)) + { + assertThrown!VariantException(v.get!(qual!B)); + assertThrown!VariantException(v.get!(qual!A)); + assertThrown!VariantException(v.get!(qual!Object)); + } + + immutable(B) ib = new immutable(B)(); + v = ib; + foreach (qual; TypeTuple!(ImmutableOf, ConstOf, SharedConstOf)) + { + assert(v.get!(qual!B) is ib); + assert(v.get!(qual!A) is ib); + assert(v.get!(qual!Object) is ib); + } + foreach (qual; TypeTuple!(MutableOf, SharedOf)) + { + assertThrown!VariantException(v.get!(qual!B)); + assertThrown!VariantException(v.get!(qual!A)); + assertThrown!VariantException(v.get!(qual!Object)); + } + + shared(B) sb = new shared B(); + v = sb; + foreach (qual; TypeTuple!(SharedOf, SharedConstOf)) + { + assert(v.get!(qual!B) is sb); + assert(v.get!(qual!A) is sb); + assert(v.get!(qual!Object) is sb); + } + foreach (qual; TypeTuple!(MutableOf, ImmutableOf, ConstOf)) + { + assertThrown!VariantException(v.get!(qual!B)); + assertThrown!VariantException(v.get!(qual!A)); + assertThrown!VariantException(v.get!(qual!Object)); + } + + shared(const(B)) scb = new shared const B(); + v = scb; + foreach (qual; TypeTuple!(SharedConstOf)) + { + assert(v.get!(qual!B) is scb); + assert(v.get!(qual!A) is scb); + assert(v.get!(qual!Object) is scb); + } + foreach (qual; TypeTuple!(MutableOf, ConstOf, ImmutableOf, SharedOf)) + { + assertThrown!VariantException(v.get!(qual!B)); + assertThrown!VariantException(v.get!(qual!A)); + assertThrown!VariantException(v.get!(qual!Object)); + } +} + +unittest +{ + static struct DummyScope + { + // https://d.puremagic.com/issues/show_bug.cgi?id=12540 + alias Alias12540 = Algebraic!Class12540; + + static class Class12540 + { + Alias12540 entity; + } + } +} diff --git a/libphobos/src/std/xml.d b/libphobos/src/std/xml.d index 489ee95a2..e7a26c197 100644 --- a/libphobos/src/std/xml.d +++ b/libphobos/src/std/xml.d @@ -883,7 +883,7 @@ class Element : Item /** * Returns the decoded interior of an element. * - * The element is assumed to containt text only. So, for + * The element is assumed to contain text only. So, for * example, given XML such as "<title>Good &amp; * Bad</title>", will return "Good & Bad". * @@ -1289,7 +1289,7 @@ class CData : Item private string content; /** - * Construct a chraracter data section + * Construct a character data section * * Params: * content = the body of the character data segment @@ -1696,9 +1696,9 @@ class DocumentParser : ElementParser */ class ElementParser { - alias void delegate(string) Handler; - alias void delegate(in Element element) ElementHandler; - alias void delegate(ElementParser parser) ParserHandler; + alias Handler = void delegate(string); + alias ElementHandler = void delegate(in Element element); + alias ParserHandler = void delegate(ElementParser parser); private { @@ -1831,7 +1831,7 @@ class ElementParser /** * Register an alternative handler which will be called whenever text * is encountered. This differs from onText in that onText will decode - * the text, wheras onTextRaw will not. This allows you to make design + * the text, whereas onTextRaw will not. This allows you to make design * choices, since onText will be more accurate, but slower, while * onTextRaw will be faster, but less accurate. Of course, you can * still call decode() within your handler, if you want, but you'd @@ -1856,7 +1856,7 @@ class ElementParser /** * Register a handler which will be called whenever a character data - * segement is encountered. + * segment is encountered. * * Examples: * -------------- @@ -1924,7 +1924,7 @@ class ElementParser * Examples: * -------------- * // Call this function whenever an XML instruction is encountered - * // (Note: XML instructions may only occur preceeding the root tag of a + * // (Note: XML instructions may only occur preceding the root tag of a * // document). * onPI = (string s) * { @@ -2350,7 +2350,7 @@ private int n = 0; if (s.startsWith("'yes'") || s.startsWith("\"yes\"")) n = 5; else if (s.startsWith("'no'" ) || s.startsWith("\"no\"" )) n = 4; - else fail("standalone attribute value must be 'yes', \"yes\"," + else fail("standalone attribute value must be 'yes', \"yes\","~ " 'no' or \"no\""); s = s[n..$]; } @@ -2624,7 +2624,7 @@ private * * Throws: CheckException if the document is not well formed * - * CheckException's toString() method will yield the complete heirarchy of + * CheckException's toString() method will yield the complete hierarchy of * parse failure (the XML equivalent of a stack trace), giving the line and * column number of every failure at every level. */ @@ -2686,7 +2686,7 @@ unittest } catch(CheckException e) { - int n = e.toString().indexOf("end tag name \"genres\" differs" + int n = e.toString().indexOf("end tag name \"genres\" differs"~ " from start tag name \"genre\""); assert(n != -1); } @@ -2781,7 +2781,7 @@ class TagException : XMLException */ class CheckException : XMLException { - CheckException err; /// Parent in heirarchy + CheckException err; /// Parent in hierarchy private string tail; /** * Name of production rule which failed to parse, @@ -2821,7 +2821,7 @@ class CheckException : XMLException } } -private alias CheckException Err; +private alias Err = CheckException; // Private helper functions diff --git a/libphobos/src/std/zip.d b/libphobos/src/std/zip.d index 11d8de4e1..a86738a57 100644 --- a/libphobos/src/std/zip.d +++ b/libphobos/src/std/zip.d @@ -572,6 +572,11 @@ final class ZipArchive de.comment = cast(string)(_data[i .. i + commentlen]); i += commentlen; + immutable uint dataOffset = de.offset + 30 + namelen + extralen; + if (dataOffset + de.compressedSize > endrecOffset) + throw new ZipException("Invalid directory entry offset or size."); + de._compressedData = _data[dataOffset .. dataOffset + de.compressedSize]; + _directory[de.name] = de; } @@ -689,10 +694,13 @@ unittest am1.name = "foo"; am1.expandedData = new ubyte[](1024); zip1.addMember(am1); - zip1.build(); + auto data1 = zip1.build(); zip2.addMember(zip1.directory["foo"]); zip2.build(); auto am2 = zip2.directory["foo"]; zip2.expand(am2); assert(am1.expandedData == am2.expandedData); + auto zip3 = new ZipArchive(data1); + zip3.build(); + assert(zip3.directory["foo"].compressedSize == am1.compressedSize); } diff --git a/libphobos/src/std/zlib.d b/libphobos/src/std/zlib.d index 81f74cf5d..e2f459516 100644 --- a/libphobos/src/std/zlib.d +++ b/libphobos/src/std/zlib.d @@ -580,7 +580,6 @@ unittest // by Dave bool CompressThenUncompress (ubyte[] src) { - try { ubyte[] dst = cast(ubyte[])std.zlib.compress(cast(void[])src); double ratio = (dst.length / cast(double)src.length); debug(zlib) writef("src.length: %1$d, dst: %2$d, Ratio = %3$f", src.length, dst.length, ratio); @@ -588,12 +587,8 @@ unittest // by Dave uncompressedBuf = cast(ubyte[])std.zlib.uncompress(cast(void[])dst); assert(src.length == uncompressedBuf.length); assert(src == uncompressedBuf); - } - catch { - debug(zlib) writefln(" ... Exception thrown when src.length = %1$d.", src.length); - return false; - } - return true; + + return true; } diff --git a/libphobos/src/unittest.d b/libphobos/src/unittest.d index 20cc8c7d9..00adcfe85 100644 --- a/libphobos/src/unittest.d +++ b/libphobos/src/unittest.d @@ -13,13 +13,12 @@ * (See accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) */ -version(Win64) {} -else -{ + public import std.base64; public import std.compiler; public import std.concurrency; public import std.conv; +public import std.container; public import std.cstream; public import std.datetime; public import std.demangle; @@ -28,7 +27,6 @@ public import std.format; public import std.getopt; public import std.math; public import std.mathspecial; -public import std.metastrings; public import std.mmfile; public import std.outbuffer; public import std.parallelism; @@ -62,19 +60,13 @@ public import std.digest.crc; public import std.digest.sha; public import std.digest.md; -} - int main(char[][] args) -{ - -version(Win64) {} -else { // Bring in unit test for module by referencing function in it - cmp("foo", "bar"); // string - filenameCharCmp('a', 'b'); // path - isNaN(1.0); // math + cast(void)cmp("foo", "bar"); // string + cast(void)filenameCharCmp('a', 'b'); // path + cast(void)isNaN(1.0); // math std.conv.to!double("1.0"); // std.conv OutBuffer b = new OutBuffer(); // outbuffer auto r = regex(""); // regex @@ -86,7 +78,7 @@ else Clock.currTime(); // datetime Exception e = new ReadException(""); // stream din.eof(); // cstream - isValidDchar(cast(dchar)0); // utf + cast(void)isValidDchar(cast(dchar)0); // utf std.uri.ascii2hex(0); // uri std.zlib.adler32(0,null); // D.zlib auto t = task!cmp("foo", "bar"); // parallelism @@ -109,12 +101,12 @@ else assert(x[1] == 3); assert(x[2] == 45); - std.math.sin(3.0); - std.mathspecial.gamma(6.2); + cast(void)std.math.sin(3.0); + cast(void)std.mathspecial.gamma(6.2); std.demangle.demangle("hello"); - std.uni.isAlpha('A'); + cast(void)std.uni.isAlpha('A'); std.file.exists("foo"); @@ -132,6 +124,5 @@ else auto crc = crc32Of("hello"); auto string = toHexString(crc); puts("Success!"); -} return 0; } From 574ccad16ebc690a75c2282121d1d62599ef6eea Mon Sep 17 00:00:00 2001 From: Johannes Pfau Date: Wed, 22 Oct 2014 19:05:23 +0200 Subject: [PATCH 2/6] There are no .di files in $(srcdir)/core --- libphobos/libdruntime/Makefile.am | 6 ------ libphobos/libdruntime/Makefile.in | 6 ------ 2 files changed, 12 deletions(-) diff --git a/libphobos/libdruntime/Makefile.am b/libphobos/libdruntime/Makefile.am index b54107fdd..027ab8386 100644 --- a/libphobos/libdruntime/Makefile.am +++ b/libphobos/libdruntime/Makefile.am @@ -242,12 +242,6 @@ install-data-local: libgdruntime.a for i in __entrypoint.di object.di; do \ $(INSTALL_HEADER) $(srcdir)/$$i $(DESTDIR)$(gdc_include_dir); \ done - for i in core; do \ - $(mkinstalldirs) $(DESTDIR)$(gdc_include_dir)/$$i; \ - for f in $(srcdir)/$$i/*.di; do \ - $(INSTALL_HEADER) $$f $(DESTDIR)$(gdc_include_dir)/$$i; \ - done; \ - done for i in core core/internal core/stdc core/sync \ core/sys/freebsd core/sys/freebsd/sys \ core/sys/linux core/sys/linux/sys \ diff --git a/libphobos/libdruntime/Makefile.in b/libphobos/libdruntime/Makefile.in index 2f9c0d1a7..d2cfb2b8c 100644 --- a/libphobos/libdruntime/Makefile.in +++ b/libphobos/libdruntime/Makefile.in @@ -657,12 +657,6 @@ install-data-local: libgdruntime.a for i in __entrypoint.di object.di; do \ $(INSTALL_HEADER) $(srcdir)/$$i $(DESTDIR)$(gdc_include_dir); \ done - for i in core; do \ - $(mkinstalldirs) $(DESTDIR)$(gdc_include_dir)/$$i; \ - for f in $(srcdir)/$$i/*.di; do \ - $(INSTALL_HEADER) $$f $(DESTDIR)$(gdc_include_dir)/$$i; \ - done; \ - done for i in core core/internal core/stdc core/sync \ core/sys/freebsd core/sys/freebsd/sys \ core/sys/linux core/sys/linux/sys \ From 75cb56ed0c4e0981b9284b0d2b1915f554b762a3 Mon Sep 17 00:00:00 2001 From: Johannes Pfau Date: Wed, 22 Oct 2014 19:05:46 +0200 Subject: [PATCH 3/6] Adjust test case for GDC --- gcc/testsuite/gdc.test/runnable/imports/template13478a.d | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gcc/testsuite/gdc.test/runnable/imports/template13478a.d b/gcc/testsuite/gdc.test/runnable/imports/template13478a.d index e10ceffe4..9fcecf3e3 100644 --- a/gcc/testsuite/gdc.test/runnable/imports/template13478a.d +++ b/gcc/testsuite/gdc.test/runnable/imports/template13478a.d @@ -1,8 +1,9 @@ module imports.template13478a; -bool foo(T)() { +import gcc.attribute; + +@attribute("noinline") bool foo(T)() { // Make sure this is not inlined so template13478.o actually // needs to reference it. - asm { nop; } return false; } From d8262203107046134f0d2f1c8cc9456d4e7f7cfd Mon Sep 17 00:00:00 2001 From: Johannes Pfau Date: Wed, 22 Oct 2014 19:06:42 +0200 Subject: [PATCH 4/6] Add -fbounds-check=safe --- gcc/d/d-lang.cc | 20 ++++++++++++++++++-- gcc/d/lang.opt | 6 ++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/gcc/d/d-lang.cc b/gcc/d/d-lang.cc index 2b5c57994..b0b99041f 100644 --- a/gcc/d/d-lang.cc +++ b/gcc/d/d-lang.cc @@ -177,6 +177,7 @@ d_init_options(unsigned int, cl_decoded_option *decoded_options) // extra D-specific options flag_emit_templates = TEnormal; + bounds_check_set_manually = false; } /* Initialize options structure OPTS. */ @@ -376,8 +377,21 @@ d_handle_option (size_t scode, const char *arg, int value, case OPT_fbounds_check: global.params.useArrayBounds = value ? 2 : 0; flag_bounds_check = value; + bounds_check_set_manually = true; break; + case OPT_fbounds_check_: + if (strcmp (arg, "safe") == 0) + { + global.params.useArrayBounds = 1; + flag_bounds_check = false; + bounds_check_set_manually = true; + } + else + { + error ("bad argument for -fbounds-check"); + } + case OPT_fdebug: global.params.debuglevel = value ? 1 : 0; break; @@ -516,9 +530,11 @@ d_handle_option (size_t scode, const char *arg, int value, global.params.useOut = !value; global.params.useAssert = !value; // release mode doesn't turn off bounds checking for safe functions. - if (global.params.useArrayBounds != 0) + if (!bounds_check_set_manually) + { global.params.useArrayBounds = !value ? 2 : 1; - flag_bounds_check = !value; + flag_bounds_check = !value; + } global.params.useSwitchError = !value; break; diff --git a/gcc/d/lang.opt b/gcc/d/lang.opt index 6975e7eb0..ca302e184 100644 --- a/gcc/d/lang.opt +++ b/gcc/d/lang.opt @@ -23,6 +23,8 @@ D Variable int flag_emit_templates +Variable +int bounds_check_set_manually debuglib= Driver Joined @@ -41,6 +43,10 @@ fbounds-check D Generate code to check bounds before indexing arrays +fbounds-check= +D Joined +-fbounds-check,-fno-bounds-check,-fbounds-check=safe Generate code to check bounds before indexing arrays + fbuiltin D Var(flag_no_builtin, 0) Recognize built-in functions From 5de862aba7cd84571cd63e36c0172447d34b1b14 Mon Sep 17 00:00:00 2001 From: Johannes Pfau Date: Wed, 22 Oct 2014 19:07:10 +0200 Subject: [PATCH 5/6] Rewrite -boundscheck=>-fbounds-check in test suite --- gcc/testsuite/gdc.test/d_do_test.exp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gcc/testsuite/gdc.test/d_do_test.exp b/gcc/testsuite/gdc.test/d_do_test.exp index fb3cfe7dd..2da8c191d 100644 --- a/gcc/testsuite/gdc.test/d_do_test.exp +++ b/gcc/testsuite/gdc.test/d_do_test.exp @@ -39,6 +39,16 @@ proc gdc-convert-args { args } { if [regexp -- {-inline} $args] { lappend out "-finline-functions" } + if [regexp -- {-boundscheck} $args] { + if [regexp -- {-boundscheck=safeonly} $args] { + lappend out "-fbounds-check=safe" + } elseif [regexp -- {-boundscheck=off} $args] { + lappend out "-fno-bounds-check" + } else { + lappend out "-fbounds-check" + } + + } if [regexp -- {-property} $args] { lappend out "-fproperty" } From 9c4abb077a59961e474fd5e5abfc5e1748a2e801 Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Sat, 17 Jan 2015 18:36:12 +0000 Subject: [PATCH 6/6] Update ChangeLog for 2.066.1 merge --- gcc/d/ChangeLog | 132 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/gcc/d/ChangeLog b/gcc/d/ChangeLog index bbabe39dc..594c59c75 100644 --- a/gcc/d/ChangeLog +++ b/gcc/d/ChangeLog @@ -1,3 +1,135 @@ +2015-01-18 Iain Buclaw + + * Make-lang.in: Update for D frontend changes. + * d-asmstmt.cc: Remove file. + * d-builtins.cc(build_dtype): No longer set struct handle. + (d_gcc_paint_type): Move to Target::paintAsType. + * d-codegen.cc(convert_expr): No longer call getImpl on associative + array conversions. Add case for converting void pointers to delegates. + (unhandled_arrayop_p): Remove. + (build_two_field_type): Use layout_type instead of building + TYPE_STUB_DECL and calling rest_of_decl_compilation. + (build_binop_assignment): New function. + (libcall_ids): Remove static variable. + (get_libcall): New function. + (maybe_set_intrinsic): Remove druntime library call handling. + (expand_intrinsic_vaarg): Dereference ref va_list parameters. + (build_closure): New function. + (WrappedExp::WrappedExp): Move to frontend sources. + (WrappedExp::toCBuffer): Ditto. + * d-codegen.h(LibCallFlag): New enum. + (LibCall): Use runtime.def macro to define members. + * d-ctype.cc(Type::toCParamtype): Remove function. + (TypeTypedef::toCParamtype): Ditto. + (TypeClass::toSymbol): Ditto. + (TypeFunction::retStyle): Move to retStyle. + (TypeSArray::toCParamtype): Ditto. + (Type::toSymbol): Ditto. + (Type::totym): Ditto. + (TypeFunction::totym): Ditto. + * d-decls.cc(Dsymbol::toSymbolX): Update for frontend changes. + (Dsymbol::toImport): Ditto. + (VarDeclaration::toSymbol): Ditto. + (FuncDeclaration::toSymbol): Ditto. + (InterfaceDeclaration::toSymbol): Use TREE_READONLY instead of + (EnumDeclaration::toDebug): Only call rest_of_type_compilation on + ENUMERAL_TYPE types. + TREE_CONSTANT to declare that the symbol cannot be modified. + (ClassDeclaration::toVtblSymbol): Ditto. + (AggregateDeclaration::toInitializer): Ditto. + (EnumDeclaration::toInitializer): Ditto. + (TypedefDeclaration::toInitializer): Remove function. + (TypedefDeclaration::toDebug): Ditto. + (Dsymbol::cvMember): Remove stub function. + (EnumDeclaration::cvMember): Ditto. + (FuncDeclaration::cvMember): Ditto. + (VarDeclaration::cvMember): Ditto. + (TypedefDeclaration::cvMember): Ditto. + * d-elem.cc(XorExp::toElem): Remove call to unhandled_arrayop_p. + (OrExp::toElem): Ditto. + (AndExp::toElem): Ditto. + (UshrExp::toElem): Ditto. + (ShrExp::toElem): Ditto. + (ShlExp::toElem): Ditto. + (ModExp::toElem): Ditto. + (DivExp::toElem): Ditto. + (MulExp::toElem): Ditto. + (MinExp::toElem): Ditto. + (AddExp::toElem): Ditto. + (XorAssignExp::toElem): Ditto. + (OrAssignExp::toElem): Ditto. + (AndAssignExp::toElem): Ditto. + (UshrAssignExp::toElem): Ditto. + (ShrAssignExp::toElem): Ditto. + (ShlAssignExp::toElem): Ditto. + (ModAssignExp::toElem): Ditto. + (DivAssignExp::toElem): Ditto. + (MulAssignExp::toElem): Ditto. + (PowAssignExp::toElem): Ditto. + (MinAssignExp::toElem): Ditto. + (AddAssignExp::toElem): Ditto. + (BinExp::toElemBin): Move to build_binop_assignment. + (AssignExp::toElem): Update for frontend changes. + (DelegatePtrExp::toElem): New function. + (DelegateFuncptrExp::toElem): New function. + (DelegateExp::toElem): Update for frontend changes. + (FuncExp::toElem): Ditto. + (NewExp::toElem): Ditto. + (StringExp::toElem): Don't set TREE_READONLY on string literals. + (AssocArrayLiteralExp::toElem): Remove codegen rewrite for new + associative array implementation. + * d-glue.cc(Global::isSpeculativeGagging): Remove function. + (Dsymbol::ungagSpeculative): Ditto. + (Ungag::~Ungag): Ditto. + (Loc::toChars): Update for new column diagnostic support. + (Loc::Loc): Ditto. + (Loc::equals): Ditto. + (error): Ditto. + (binary): Remove function. + (asmSemantic): New function. + (retStyle): New function. + (FuncDeclaration::isBuiltin): Rename to isBuiltin. + * d-intrinsics.def: Rename to intrinsics.def. + * d-irstate.cc(IRState::addExp): Remove old warning to catch statements + with no side effects. Now handled in frontend. + * d-lang.cc(d_init_options): Update for frontend changes. + (d_initialize_diagnostics): Remove function. + (d_add_builtin_version): Update for frontend changes. + (d_init): Ditto. + (d_handle_option): Ditto. + (d_post_options): Ditto. + (d_parse_file): Ditto. + * d-objfile.cc(Nspace::toObjFile): New function. + (StructDeclaration::toObjFile): Update for frontend changes. + (TypedefDeclaration::toObjFile): Remove function. + (TemplateInstance::toObjFile): Update for frontend changes. + (TemplateMixin::toObjFile): Ditto. + (unnest_function): New function. + (output_declaration_p): Update for frontend changes. + (FuncDeclaration::toObjFile): Ditto. + (FuncDeclaration::buildClosure): Move to buildClosure. + (get_linemap): Update for frontend changes. + (build_simple_function): Ditto. + (build_call_function): Ditto. + * d-target.cc(Target::va_listType): New function. + (Target::paintAsType): Ditto. + * d-todt.cc(dt_container2): Do not set TREE_READONLY on initialisers. + (dt_container): Ditto. + (ClassReferenceExp::toDt2): Update for C++ class support. + (ClassReferenceExp::toInstanceDt): Ditto. + (TypeTypedef::toDt): Remove function. + (TypeInfoTypedefDeclaration::toDt): Ditto. + (TypeInfoAssociativeArrayDeclaration::toDt): Update typeinfo size. + (TypeInfoAssociativeArrayDeclaration::toDt): Remove reference to impl + field in TypeInfo struct. + (TypeInfoStructDeclaration::toDt): Update for frontend changes. + * d-typinf.cc(Type::getTypeInfo): Update for frontend changes. + (TypeTypedef::getTypeInfoDeclaration): Remove function. + (createTypeInfoArray): Remove function. + * runtime.def: New file. + * toir.cc(IRVisitor::visit::DtorExpStatement): Remove function. + (IRVisitor::visit::ExtAsmStatement): Update for frontend changes. + 2015-01-17 Iain Buclaw * d-elem.cc(UshrAssignExp::toElem): Remove integer promotion on left