From: Jean Privat Date: Tue, 14 Apr 2015 09:33:40 +0000 (+0700) Subject: Merge: Fix type adaptation when there is loops X-Git-Tag: v0.7.4~26 X-Git-Url: http://nitlanguage.org?hp=f08b7bd0e7c96af4565deccfad1424fd18f81fc6 Merge: Fix type adaptation when there is loops Basically one of the oldest bug of Nit. The point of adaptive typing is that the static types of variables follows the static types of the assigned values, type tests and comparison to null. This is a great feature of the language but was buggy because loops where not taken in account (old TODO), until the present PR. The basic idea is just to track if a type adaptation occurs, if yes replay the whole typing phase. The immediate drawback is that now typing require 2 passes most of the time, and sometime more. This PR was a roller-coaster of emotions to develop since a lot a surprise issues were discovered. * bug in flow that give me hours of despair. * duplication of error messages; because multiple passes. * premature warning/error messages; because some error conditions on types are detected at the first pass but a second pass find it is fine in fact... but too late the error message was printed. * almost work at the first time (modulo the previous issues) * only two bugs found in the whole code, quite impressive, and one of them is not a real bug but an autocast issue. The issue about error duplication and premature errors is only workadounded (or fixme) in most places. But proper solutions will be required in future PR. close #647 and close #86, then reopen it again, then reclose it. Pull-Request: #1257 Reviewed-by: Alexis Laferrière Reviewed-by: Lucas Bajolet Reviewed-by: Alexandre Terrasa --- diff --git a/lib/pthreads/README.md b/lib/pthreads/README.md index e612dbe..4a117b4 100644 --- a/lib/pthreads/README.md +++ b/lib/pthreads/README.md @@ -11,6 +11,9 @@ This group also provides two optional modules with thread-safe collections: Theses services are implemented using the POSIX threads. +You can also use the `is threaded` annotation on methods, which makes them run on their own thread. +Methods with return value or self calls are not supported. + ## Known limitations: * Most services from the Nit library are not thread-safe. You must manage diff --git a/lib/pthreads/examples/threaded_example.nit b/lib/pthreads/examples/threaded_example.nit index 175c753..9a0af4b 100644 --- a/lib/pthreads/examples/threaded_example.nit +++ b/lib/pthreads/examples/threaded_example.nit @@ -19,12 +19,20 @@ module threaded_example import pthreads -# the "is threaded" annotation makes this fun run on an other thread +# the `is threaded` annotation makes this method run on an other thread fun foo is threaded do sys.nanosleep(1,0) print "threaded" end +# Parameterized `threaded` method, same as foo, but with parameters +fun bar(i : Int, s : String) is threaded do + sys.nanosleep(2, 0) + print i + print s +end + foo +bar(10, "parameterized and threaded") print "main" -sys.nanosleep(2,0) +sys.nanosleep(5,0) diff --git a/misc/README.md b/misc/README.md index 1661f43..9b4bf06 100644 --- a/misc/README.md +++ b/misc/README.md @@ -98,13 +98,15 @@ the environment variable `NIT_VIM_DIR`. ## Documentation in preview window -You can display the documentation for the entity under the cursor with `:call Nitdoc()`. -It will use the same metadata files as the omnifunc and the preview window. +The command `:Nitdoc` searches the documentation for the word under the cursor. +The results are displayed in the preview window in order of relevance. +You can search for any word by passing it as an argument, as in `:Nitdoc modulo`. +The Nitdoc command uses the same metadata files as the omnifunc. You may want to map the function to a shortcut by adding the following code to `~/.vimrc`. ~~~ " Map displaying Nitdoc to Ctrl-D -map :call Nitdoc() +map :Nitdoc ~~~ ## Search declarations and usages of the word under the cursor diff --git a/misc/vim/plugin/nit.vim b/misc/vim/plugin/nit.vim index f19224a..86d5e45 100644 --- a/misc/vim/plugin/nit.vim +++ b/misc/vim/plugin/nit.vim @@ -84,7 +84,7 @@ endfunction " Get path to the best metadata file named `name` " " Returns an empty string if not found. -fun NitMetadataFile(name) +fun s:NitMetadataFile(name) " Where are the generated metadata files? if empty($NIT_VIM_DIR) let metadata_dir = $HOME . '/.vim/nit' @@ -122,7 +122,7 @@ fun NitOmnifuncAddFromFile(base, matches, path) let synopsis_matches = [] let doc_matches = [] - let path = NitMetadataFile(a:path) + let path = s:NitMetadataFile(a:path) if empty(path) return endif @@ -134,19 +134,19 @@ fun NitOmnifuncAddFromFile(base, matches, path) " Add? if name == a:base " Exact match - call NitOmnifuncAddAMatch(a:matches, words, name) + call s:NitOmnifuncAddAMatch(a:matches, words, name) elseif name =~? '^'.a:base " Common-prefix match - call NitOmnifuncAddAMatch(prefix_matches, words, name) + call s:NitOmnifuncAddAMatch(prefix_matches, words, name) elseif name =~? a:base " Substring match - call NitOmnifuncAddAMatch(substring_matches, words, name) + call s:NitOmnifuncAddAMatch(substring_matches, words, name) elseif get(words, 2, '') =~? a:base " Match in the synopsis - call NitOmnifuncAddAMatch(synopsis_matches, words, name) + call s:NitOmnifuncAddAMatch(synopsis_matches, words, name) elseif get(words, 3, '') =~? a:base " Match in the longer doc - call NitOmnifuncAddAMatch(synopsis_matches, words, name) + call s:NitOmnifuncAddAMatch(doc_matches, words, name) endif endfor @@ -158,7 +158,7 @@ fun NitOmnifuncAddFromFile(base, matches, path) endfun " Internal function to search parse the information from a metadata line -fun NitOmnifuncAddAMatch(matches, words, name) +fun s:NitOmnifuncAddAMatch(matches, words, name) let pretty = get(a:words, 1, '') let synopsis = get(a:words, 2, '') let desc = get(a:words, 3, '') @@ -266,16 +266,24 @@ fun NitOmnifunc(findstart, base) endfun " Show doc for the entity under the cursor in the preview window -fun Nitdoc() - " Word under cursor - let word = expand("") +fun Nitdoc(...) + if a:0 == 0 || empty(a:1) + " Word under cursor + let word = expand("") + else + let word = join(a:000, ' ') + endif " All possible docs (there may be more than one entity with the same name) let docs = [] + let prefix_matches = [] + let substring_matches = [] + let synopsis_matches = [] + let doc_matches = [] " Search in all metadata files for file in ['modules', 'classes', 'properties'] - let path = NitMetadataFile(file.'.txt') + let path = s:NitMetadataFile(file.'.txt') if empty(path) continue endif @@ -283,17 +291,34 @@ fun Nitdoc() for line in readfile(path) let words = split(line, '#====#', 1) let name = get(words, 0, '') - if name =~ '^' . word . '\>' - " It fits our word, get long doc - let desc = get(words,3,'') - let desc = join(split(desc, '#nnnn#', 1), "\n") - call add(docs, desc) + if name =~ '^'.word.'\>' + " Exact match + call s:NitdocAdd(docs, words) + elseif name =~? '^'.word + " Common-prefix match + call s:NitdocAdd(prefix_matches, words) + elseif name =~? word + " Substring match + call s:NitdocAdd(substring_matches, words) + elseif get(words, 2, '') =~? word + " Match in the synopsis + call s:NitdocAdd(synopsis_matches, words) + elseif get(words, 3, '') =~? word + " Match in the longer doc + call s:NitdocAdd(doc_matches, words) endif endfor endfor + " Unite all results in prefered order + call extend(docs, sort(prefix_matches)) + call extend(docs, sort(substring_matches)) + call extend(docs, sort(synopsis_matches)) + call extend(docs, sort(doc_matches)) + " Found no doc, give up if empty(docs) || !(join(docs, '') =~ '\w') + echo 'Nitdoc found nothing for "' . word . '"' return endif @@ -310,6 +335,8 @@ fun Nitdoc() silent put = '' endif endfor + execute 0 + delete " the first empty line " Set options setlocal buftype=nofile @@ -322,6 +349,13 @@ fun Nitdoc() redraw! endfun +" Internal function to search parse the information from a metadata line +fun s:NitdocAdd(matches, words) + let desc = get(a:words, 3, '') + let desc = join(split(desc, '#nnnn#', 1), "\n") + call add(a:matches, desc) +endfun + " Call `git grep` on the word under the cursor " " Shows declarations first, then all matches, in the preview window. @@ -351,4 +385,7 @@ endfun " Activate the omnifunc on Nit files autocmd FileType nit set omnifunc=NitOmnifunc +" Define the user command Nitdoc for ease of use +command -nargs=* Nitdoc call Nitdoc("") + let s:script_dir = fnamemodify(resolve(expand(':p')), ':h') diff --git a/src/compiler/abstract_compiler.nit b/src/compiler/abstract_compiler.nit index f579a32..8c9e4b7 100644 --- a/src/compiler/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@ -3039,7 +3039,9 @@ redef class ANewExpr var recv = v.init_instance_or_extern(mtype) - var callsite = self.callsite.as(not null) + var callsite = self.callsite + if callsite == null then return recv + var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs) var res2 = v.compile_callsite(callsite, args) if res2 != null then diff --git a/src/frontend/parallelization_phase.nit b/src/frontend/parallelization_phase.nit index 7da80ff..8cd7965 100644 --- a/src/frontend/parallelization_phase.nit +++ b/src/frontend/parallelization_phase.nit @@ -39,10 +39,9 @@ private class ParallelizationPhase toolcontext.error(nmethdef.location, "Syntax error: only a method can be threaded.") return end - if nmethdef.n_signature.n_params.length != 0 then - toolcontext.error(nmethdef.location, "Syntax error: parametrized method not supported yet.") - return - end + + #TODO: check for self calls + if nmethdef.n_signature.n_type != null then toolcontext.error(nmethdef.location, "Syntax error: method with a return value not supported yet.") return @@ -66,11 +65,24 @@ private class ParallelizationPhase classname += nmethdef.n_methid.as(AIdMethid).n_id.text end - # Create a string corresponding to the threaded class - var s =""" + + var params = new Array[String] + # case if the method has parameters + if nmethdef.n_signature.n_params.not_empty then + for param in nmethdef.n_signature.n_params do + params.add(""" +var {{{param.n_id.text}}} : {{{param.n_type.n_id.text}}} + +""") + end + end + + # String corresponding to the generated class + var s=""" class {{{classname}}} super Thread + {{{params.join("\n")}}} redef fun main do end end @@ -95,11 +107,24 @@ end assert nullreturn isa AExpr mainfun.n_block.as(ABlockExpr).n_expr.add(nullreturn) + # Create new body for the annotated fun - var s_newbody =""" + var s_newbody : String + if nmethdef.n_signature.n_params.not_empty then + var init_params = new Array[String] + for param in nmethdef.n_signature.n_params do + init_params.add(param.n_id.text) + end + s_newbody =""" +var thread = new {{{classname}}}({{{init_params.join(",")}}}) +thread.start +""" + else + s_newbody = """ var thread = new {{{classname}}} thread.start """ + end var newbody = toolcontext.parse_something(s_newbody) nmethdef.n_block = newbody.as(ABlockExpr) diff --git a/src/interpreter/naive_interpreter.nit b/src/interpreter/naive_interpreter.nit index 4cab2e5..3168c76 100644 --- a/src/interpreter/naive_interpreter.nit +++ b/src/interpreter/naive_interpreter.nit @@ -1807,6 +1807,9 @@ redef class ANewExpr var mtype = v.unanchor_type(self.recvtype.as(not null)) var recv: Instance = new MutableInstance(mtype) v.init_instance(recv) + var callsite = self.callsite + if callsite == null then return recv + var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs) if args == null then return null var res2 = v.callsite(callsite, args) diff --git a/src/platform/ios.nit b/src/platform/ios.nit index f81b5d2..27c05c6 100644 --- a/src/platform/ios.nit +++ b/src/platform/ios.nit @@ -36,6 +36,12 @@ private class IOSPlatform redef fun toolchain(toolcontext, compiler) do return new IOSToolchain(toolcontext, compiler) end +private class IosProject + super AppProject + + redef fun namespace do return super.to_camel_case +end + private class IOSToolchain super MakefileToolchain @@ -43,7 +49,7 @@ private class IOSToolchain var ios_project_root: String is noinit # `app.nit` project for the current compilation target - var app_project = new AppProject(compiler.modelbuilder, compiler.mainmodule) is lazy + var app_project = new IosProject(compiler.modelbuilder, compiler.mainmodule) is lazy redef fun default_outname do return "{super}.app" diff --git a/src/platform/xcode_templates.nit b/src/platform/xcode_templates.nit index e4ebbdf..aa6cc5b 100644 --- a/src/platform/xcode_templates.nit +++ b/src/platform/xcode_templates.nit @@ -221,25 +221,6 @@ class PbxprojectTemplate path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; - AF9F83E91A5F0D21004B62C0 /* {{{name}}}Tests.xctest */ = { - isa = PBXFileReference; - explicitFileType = wrapper.cfbundle; - includeInIndex = 0; - path = {{{name}}}Tests.xctest; - sourceTree = BUILT_PRODUCTS_DIR; - }; - AF9F83EE1A5F0D21004B62C0 /* Info.plist */ = { - isa = PBXFileReference; - lastKnownFileType = text.plist.xml; - path = Info.plist; - sourceTree = ""; - }; - AF9F83EF1A5F0D21004B62C0 /* {{{name}}}Tests.m */ = { - isa = PBXFileReference; - lastKnownFileType = sourcecode.c.objc; - path = {{{name}}}Tests.m; - sourceTree = ""; - }; /* Changing generated files */ """ @@ -257,13 +238,6 @@ class PbxprojectTemplate ); runOnlyForDeploymentPostprocessing = 0; }; - AF9F83E61A5F0D21004B62C0 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -271,7 +245,6 @@ class PbxprojectTemplate isa = PBXGroup; children = ( AF9F83CE1A5F0D21004B62C0 /* {{{name}}} */, - AF9F83EC1A5F0D21004B62C0 /* {{{name}}}Tests */, AF9F83CD1A5F0D21004B62C0 /* Products */, ); sourceTree = ""; @@ -280,7 +253,6 @@ class PbxprojectTemplate isa = PBXGroup; children = ( AF9F83CC1A5F0D21004B62C0 /* {{{name}}}.app */, - AF9F83E91A5F0D21004B62C0 /* {{{name}}}Tests.xctest */, ); name = Products; sourceTree = ""; @@ -308,23 +280,6 @@ class PbxprojectTemplate name = "Supporting Files"; sourceTree = ""; }; - AF9F83EC1A5F0D21004B62C0 /* {{{name}}}Tests */ = { - isa = PBXGroup; - children = ( - AF9F83EF1A5F0D21004B62C0 /* {{{name}}}Tests.m */, - AF9F83ED1A5F0D21004B62C0 /* Supporting Files */, - ); - path = {{{name}}}Tests; - sourceTree = ""; - }; - AF9F83ED1A5F0D21004B62C0 /* Supporting Files */ = { - isa = PBXGroup; - children = ( - AF9F83EE1A5F0D21004B62C0 /* Info.plist */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -345,24 +300,6 @@ class PbxprojectTemplate productReference = AF9F83CC1A5F0D21004B62C0 /* {{{name}}}.app */; productType = "com.apple.product-type.application"; }; - AF9F83E81A5F0D21004B62C0 /* {{{name}}}Tests */ = { - isa = PBXNativeTarget; - buildConfigurationList = AF9F83F61A5F0D21004B62C0 /* Build configuration list for PBXNativeTarget "{{{name}}}Tests" */; - buildPhases = ( - AF9F83E51A5F0D21004B62C0 /* Sources */, - AF9F83E61A5F0D21004B62C0 /* Frameworks */, - AF9F83E71A5F0D21004B62C0 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - AF9F83EB1A5F0D21004B62C0 /* PBXTargetDependency */, - ); - name = {{{name}}}Tests; - productName = {{{name}}}Tests; - productReference = AF9F83E91A5F0D21004B62C0 /* {{{name}}}Tests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -374,10 +311,6 @@ class PbxprojectTemplate AF9F83CB1A5F0D21004B62C0 = { CreatedOnToolsVersion = 6.1.1; }; - AF9F83E81A5F0D21004B62C0 = { - CreatedOnToolsVersion = 6.1.1; - TestTargetID = AF9F83CB1A5F0D21004B62C0; - }; }; }; buildConfigurationList = AF9F83C71A5F0D21004B62C0 /* Build configuration list for PBXProject "{{{name}}}" */; @@ -394,7 +327,6 @@ class PbxprojectTemplate projectRoot = ""; targets = ( AF9F83CB1A5F0D21004B62C0 /* {{{name}}} */, - AF9F83E81A5F0D21004B62C0 /* {{{name}}}Tests */, ); }; /* End PBXProject section */ @@ -414,13 +346,6 @@ class PbxprojectTemplate ); runOnlyForDeploymentPostprocessing = 0; }; - AF9F83E71A5F0D21004B62C0 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -437,24 +362,8 @@ class PbxprojectTemplate ); runOnlyForDeploymentPostprocessing = 0; }; - AF9F83E51A5F0D21004B62C0 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - AF9F83F01A5F0D21004B62C0 /* {{{name}}}Tests.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - AF9F83EB1A5F0D21004B62C0 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = AF9F83CB1A5F0D21004B62C0 /* {{{name}}} */; - targetProxy = AF9F83EA1A5F0D21004B62C0 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ AF9F83DD1A5F0D21004B62C0 /* Main.storyboard */ = { isa = PBXVariantGroup; @@ -553,40 +462,6 @@ class PbxprojectTemplate }; name = Release; }; - AF9F83F71A5F0D21004B62C0 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - INFOPLIST_FILE = {{{name}}}Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/{{{name}}}.app/{{{name}}}"; - }; - name = Debug; - }; - AF9F83F81A5F0D21004B62C0 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - FRAMEWORK_SEARCH_PATHS = ( - "$(SDKROOT)/Developer/Library/Frameworks", - "$(inherited)", - ); - INFOPLIST_FILE = {{{name}}}Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/{{{name}}}.app/{{{name}}}"; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -607,14 +482,6 @@ class PbxprojectTemplate ); defaultConfigurationIsVisible = 0; }; - AF9F83F61A5F0D21004B62C0 /* Build configuration list for PBXNativeTarget "{{{name}}}Tests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - AF9F83F71A5F0D21004B62C0 /* Debug */, - AF9F83F81A5F0D21004B62C0 /* Release */, - ); - defaultConfigurationIsVisible = 0; - }; /* End XCConfigurationList section */ }; rootObject = AF9F83C41A5F0D21004B62C0 /* Project object */; diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 6727d02..a46f632 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -1875,6 +1875,7 @@ redef class ANewExpr end self.recvtype = recvtype + var kind = recvtype.mclass.kind var name: String var nid = self.n_id @@ -1883,11 +1884,24 @@ redef class ANewExpr else name = "new" end + if name == "intern" then + if kind != concrete_kind then + v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.") + return + end + if n_args.n_exprs.not_empty then + v.error(n_args, "Type Error: the intern constructor expects no arguments.") + return + end + # Our job is done + self.mtype = recvtype + return + end + var callsite = v.get_method(self, recvtype, name, false) if callsite == null then return if not callsite.mproperty.is_new then - var kind = recvtype.mclass.kind if kind != concrete_kind then v.error(self, "Type Error: Cannot instantiate {kind} {recvtype}.") return diff --git a/tests/base_new_intern.nit b/tests/base_new_intern.nit new file mode 100644 index 0000000..76819f0 --- /dev/null +++ b/tests/base_new_intern.nit @@ -0,0 +1,30 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import kernel + +class A + var i: Int + new do + var res = new A.intern + res.i = 1 + return res + end + init do + 0.output # not called + end +end + +var a = new A +a.i.output diff --git a/tests/sav/base_new_intern.res b/tests/sav/base_new_intern.res new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/tests/sav/base_new_intern.res @@ -0,0 +1 @@ +1 diff --git a/tests/sav/threaded_example.res b/tests/sav/threaded_example.res index 050d79d..e90aa48 100644 --- a/tests/sav/threaded_example.res +++ b/tests/sav/threaded_example.res @@ -1,2 +1,4 @@ main threaded +10 +parameterized and threaded