From 34b968d7a9c4e29bb235f1b1df444ec880cffa45 Mon Sep 17 00:00:00 2001 From: Zhia Yang Date: Mon, 29 Oct 2018 08:24:21 +0800 Subject: [PATCH 1/9] fix a potential bug wrt. checkAsType in arithmetic.cpp --- source/typecheck/arithmetic.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/typecheck/arithmetic.cpp b/source/typecheck/arithmetic.cpp index 1497112e..28de59ac 100644 --- a/source/typecheck/arithmetic.cpp +++ b/source/typecheck/arithmetic.cpp @@ -250,13 +250,13 @@ TCResult ast::BinaryOp::typecheck(sst::TypecheckState* fs, fir::Type* inferred) } else { - this->right->checkAsType = true; + this->right->checkAsType = (this->op == Operator::TypeCast || this->op == Operator::TypeIs); if(this->op == Operator::TypeCast && l->type->isUnionType()) inferred = l->type; r = this->right->typecheck(fs, inferred).expr(); - if(this->op == Operator::TypeCast) + if(this->right->checkAsType) r = util::pool(r->loc, r->type); } From 7dbab542976fe2e721f6c6ec8f2da4fe74666592 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Fri, 2 Nov 2018 23:13:10 +0800 Subject: [PATCH 2/9] stuff --- .travis.yml | 4 +-- build/supertiny.flx | 63 ++++++++++++++++++++++++++------------------ issues.md | 36 +++++++++++++------------ output.txt | Bin 944 -> 0 bytes 4 files changed, 59 insertions(+), 44 deletions(-) delete mode 100644 output.txt diff --git a/.travis.yml b/.travis.yml index b9acd8a2..1452ce08 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,8 +29,8 @@ addons: script: - if [ "$TRAVIS_OS_NAME" == "osx" ]; - then PATH="/usr/local/opt/llvm@6/bin:$PATH" LLVM_CONFIG=llvm-config make tester; - else CXX=g++-$GCC_VERSION CC=gcc-$GCC_VERSION make ci; + then PATH="/usr/local/opt/llvm@6/bin:$PATH" LLVM_CONFIG=llvm-config make -j tester; + else CXX=g++-$GCC_VERSION CC=gcc-$GCC_VERSION make -j ci; fi notifications: diff --git a/build/supertiny.flx b/build/supertiny.flx index 5bb5adf2..ed2d02f4 100644 --- a/build/supertiny.flx +++ b/build/supertiny.flx @@ -5,7 +5,7 @@ export supertiny import "libc" as _ -// import "stdio" as _ +import "stdio" as _ // var glob: any // fn test_refcounting_problem_thing() @@ -29,8 +29,11 @@ import "libc" as _ let x = 30 if 1 == 1 < 4 > 3 > -5 == -7 + 2 < 10 > 3 { - printf("yay\n") + println("yay") } + + + overloadErrorTest() } @@ -73,32 +76,42 @@ import "libc" as _ -// fn overloadErrorTest() -// { -// fn printThings(mul: f64, flts: [f64: ...]) -// { -// printf("floats: "); -// for i in flts { printf("%.1f ", mul * i) } +fn overloadErrorTest() +{ + fn printThings(mul: f64, flts: [f64: ...]) + { + printf("floats: "); + for i in flts { printf("%.1f ", mul * i) } + + printf("\n") + } + + fn printThings(mul: str, flts: [f64: ...]) + { + printf("floats: "); + for i in flts { printf("%s: %.1f ", mul.ptr, i) } + + printf("\n") + } + + fn printThings() + { + printf("print things 3\n") + } + + do { + printThings(30, ...[as f64: 1, 2, 3, 4, 5 ]) + printThings(0.813, 1, 2, 4.41, 39, 491.3, 381.7) + } +} + + + + + -// printf("\n") -// } -// fn printThings(mul: str, flts: [f64: ...]) -// { -// printf("floats: "); -// for i in flts { printf("%s: %.1f ", mul.ptr, i) } -// printf("\n") -// } -// fn printThings() -// { -// printf("print things 3\n") -// } -// do { -// printThings(30, ...[ 1, 2, 3, 4, 5 ]) -// printThings(0.813, 1, 2, 4.41, 39, 491.3, 381.7) -// } -// } diff --git a/issues.md b/issues.md index 3965cf74..83539ef0 100644 --- a/issues.md +++ b/issues.md @@ -39,6 +39,9 @@ Note: this is just a personal log of outstanding issues, shorter rants/ramblings 18. Some way of handling both types and expressions in `sizeof`/`typeid`/`typeof`. Might work if we just check for identifiers, but then what about polymorphic types? Those won't even parse as expressions. + +19. Do we want to re-typecheck arguments for each function target?? this can help things infer better, but would also probably slow shit down a lot... + ----- @@ -47,23 +50,6 @@ Note: this is just a personal log of outstanding issues, shorter rants/ramblings 2. There are still some instances where we explicitly 'initialise' a class equivalent to `memset(0)` -- see *THINGS TO NOTE* below. -4. Some kind of construct to make a variable immutable beyond a certain point; thus you could have arbitrarily complex initialisation code, - then have the compiler enforce that your variable is immutable after that point, eg: - - ``` - var k = ... - if(some_cond1) k = get_value() - else k = get_other_value() - - k.mutating_func() - - make_immutable(k) - - k = ... // will not compile - ``` - - - ----- @@ -147,6 +133,22 @@ So, this thing serves as the shitty documentation for how the generic pipeline w `for [ first, middle, last, ... ] in list { ... }` +4. Some kind of construct to make a variable immutable beyond a certain point; thus you could have arbitrarily complex initialisation code, + then have the compiler enforce that your variable is immutable after that point, eg: + + ``` + var k = ... + if(some_cond1) k = get_value() + else k = get_other_value() + + k.mutating_func() + + make_immutable(k) + + k = ... // will not compile + ``` + + 5. Type inference for single-expr functions? It's a little weird to have two arrows like this: `fn foo(a: T) -> T => a * a` diff --git a/output.txt b/output.txt deleted file mode 100644 index a49fdd291e0af9a0560d3907bc65f7de9a83f3ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 944 zcmd6mO-sW-5Qg8g;D5-mqAfNu{ORg5n-uFm)BAV64a*eiXSJfc`iIJfsZo%Up#;frQFlpeAflz<^6)<(OE kVY>^RZO-*)^39?M7WOB$e}Kv2Ap7knIGyQq{S!?48-G)!FaQ7m From c00387513d2d737ac23977878bab84d5c118e392 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 5 Nov 2018 01:56:46 +0800 Subject: [PATCH 3/9] fix some bugs and stuff --- .travis.yml | 4 +-- source/backend/llvm/translator.cpp | 42 ++++++++++++++++++------------ source/fir/GlobalValue.cpp | 2 ++ source/fir/Module.cpp | 5 ++-- source/include/ir/module.h | 18 ++++++++----- 5 files changed, 44 insertions(+), 27 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1452ce08..b9acd8a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,8 +29,8 @@ addons: script: - if [ "$TRAVIS_OS_NAME" == "osx" ]; - then PATH="/usr/local/opt/llvm@6/bin:$PATH" LLVM_CONFIG=llvm-config make -j tester; - else CXX=g++-$GCC_VERSION CC=gcc-$GCC_VERSION make -j ci; + then PATH="/usr/local/opt/llvm@6/bin:$PATH" LLVM_CONFIG=llvm-config make tester; + else CXX=g++-$GCC_VERSION CC=gcc-$GCC_VERSION make ci; fi notifications: diff --git a/source/backend/llvm/translator.cpp b/source/backend/llvm/translator.cpp index 37c739ff..f0f68be8 100644 --- a/source/backend/llvm/translator.cpp +++ b/source/backend/llvm/translator.cpp @@ -380,7 +380,7 @@ namespace backend auto ret = cachedConstants[c]; if(ret) return ret; - if(fir::ConstantInt* ci = dcast(fir::ConstantInt, c)) + if(auto ci = dcast(fir::ConstantInt, c)) { llvm::Type* it = typeToLlvm(c->getType(), mod); if(ci->getType()->toPrimitiveType()->isSigned()) @@ -392,22 +392,26 @@ namespace backend return cachedConstants[c] = llvm::ConstantInt::get(it, ci->getUnsignedValue()); } } - else if(fir::ConstantBool* cc = dcast(fir::ConstantBool, c)) - { - llvm::Type* ct = typeToLlvm(c->getType(), mod); - return cachedConstants[c] = llvm::ConstantInt::get(ct, cc->getValue()); - } - else if(fir::ConstantFP* cf = dcast(fir::ConstantFP, c)) + else if(auto cf = dcast(fir::ConstantFP, c)) { llvm::Type* it = typeToLlvm(c->getType(), mod); return cachedConstants[c] = llvm::ConstantFP::get(it, cf->getValue()); } - else if(fir::ConstantBitcast* cbc = dcast(fir::ConstantBitcast, c)) + else if(auto cn = dcast(fir::ConstantNumber, c)) + { + error("cannot"); + } + else if(auto cc = dcast(fir::ConstantBool, c)) + { + llvm::Type* ct = typeToLlvm(c->getType(), mod); + return cachedConstants[c] = llvm::ConstantInt::get(ct, cc->getValue()); + } + else if(auto cbc = dcast(fir::ConstantBitcast, c)) { llvm::Type* t = typeToLlvm(cbc->getType(), mod); return cachedConstants[c] = llvm::ConstantExpr::getBitCast(constToLlvm(cbc->getValue(), valueMap, mod), t); } - else if(fir::ConstantArray* ca = dcast(fir::ConstantArray, c)) + else if(auto ca = dcast(fir::ConstantArray, c)) { // auto p = prof::Profile(PROFGROUP_LLVM, "const array"); @@ -431,7 +435,7 @@ namespace backend return cachedConstants[c] = llvm::ConstantArray::get(arrt, vals); } - else if(fir::ConstantTuple* ct = dcast(fir::ConstantTuple, c)) + else if(auto ct = dcast(fir::ConstantTuple, c)) { // auto p = prof::Profile(PROFGROUP_LLVM, "const tuple"); @@ -443,7 +447,7 @@ namespace backend return cachedConstants[c] = llvm::ConstantStruct::getAnon(LLVMBackend::getLLVMContext(), vals); } - else if(fir::ConstantEnumCase* cec = dcast(fir::ConstantEnumCase, c)) + else if(auto cec = dcast(fir::ConstantEnumCase, c)) { auto ty = typeToLlvm(cec->getType(), mod); iceAssert(ty->isStructTy()); @@ -451,7 +455,7 @@ namespace backend return cachedConstants[c] = llvm::ConstantStruct::get(llvm::cast(ty), constToLlvm(cec->getIndex(), valueMap, mod), constToLlvm(cec->getValue(), valueMap, mod)); } - else if(fir::ConstantString* cs = dcast(fir::ConstantString, c)) + else if(auto cs = dcast(fir::ConstantString, c)) { // auto p = prof::Profile(PROFGROUP_LLVM, "const string"); size_t origLen = cs->getValue().length(); @@ -465,8 +469,14 @@ namespace backend std::vector indices = { zconst, zconst }; llvm::Constant* gepd = llvm::ConstantExpr::getGetElementPtr(gv->getType()->getPointerElementType(), gv, indices); - auto eightconst = llvm::ConstantInt::get(llvm::Type::getInt64Ty(LLVMBackend::getLLVMContext()), 8); - gepd = llvm::ConstantExpr::getInBoundsGetElementPtr(gepd->getType()->getPointerElementType(), gepd, eightconst); + //! ACHTUNG ! + //????? WTF IS THIS??? + // seems like a relic from when we were doing the refcount-behind-string-data thing + // surprised we never hit a bug due to this, but i'm 99.99999% sure this is wrong. + /* + auto eightconst = llvm::ConstantInt::get(llvm::Type::getInt64Ty(LLVMBackend::getLLVMContext()), 8); + gepd = llvm::ConstantExpr::getInBoundsGetElementPtr(gepd->getType()->getPointerElementType(), gepd, eightconst); + */ auto len = llvm::ConstantInt::get(llvm::Type::getInt64Ty(LLVMBackend::getLLVMContext()), origLen); @@ -479,14 +489,14 @@ namespace backend cachedConstants[c] = ret; return ret; } - else if(fir::ConstantArraySlice* cas = dcast(fir::ConstantArraySlice, c)) + else if(auto cas = dcast(fir::ConstantArraySlice, c)) { std::vector mems = { constToLlvm(cas->getData(), valueMap, mod), constToLlvm(cas->getLength(), valueMap, mod) }; auto ret = llvm::ConstantStruct::get(llvm::cast(typeToLlvm(cas->getType(), mod)), mems); return cachedConstants[c] = ret; } - else if(fir::ConstantDynamicArray* cda = dcast(fir::ConstantDynamicArray, c)) + else if(auto cda = dcast(fir::ConstantDynamicArray, c)) { if(cda->getArray()) { diff --git a/source/fir/GlobalValue.cpp b/source/fir/GlobalValue.cpp index dd54cc62..61525542 100644 --- a/source/fir/GlobalValue.cpp +++ b/source/fir/GlobalValue.cpp @@ -29,6 +29,8 @@ namespace fir if(!immutable) this->kind = Kind::lvalue; else this->kind = Kind::clvalue; + + module->globals[this->ident] = this; } void GlobalVariable::setInitialValue(ConstantValue* constVal) diff --git a/source/fir/Module.cpp b/source/fir/Module.cpp index 582c0199..b8c3dc4c 100644 --- a/source/fir/Module.cpp +++ b/source/fir/Module.cpp @@ -16,12 +16,11 @@ namespace fir GlobalVariable* Module::createGlobalVariable(const Identifier& ident, Type* type, ConstantValue* initVal, bool isImmut, LinkageType linkage) { - GlobalVariable* gv = new GlobalVariable(ident, this, type, isImmut, linkage, initVal); if(this->globals.find(ident) != this->globals.end()) error("Already have a global with name '%s'", ident.str()); - this->globals[ident] = gv; - return gv; + // this adds itself to the module list. + return new GlobalVariable(ident, this, type, isImmut, linkage, initVal); } GlobalVariable* Module::createGlobalVariable(const Identifier& id, Type* type, bool isImmut, LinkageType linkage) diff --git a/source/include/ir/module.h b/source/include/ir/module.h index f5d1f160..ac21b7af 100644 --- a/source/include/ir/module.h +++ b/source/include/ir/module.h @@ -21,6 +21,9 @@ namespace fir { struct Module { + friend struct GlobalValue; + friend struct GlobalVariable; + Module(std::string nm); GlobalVariable* createGlobalVariable(const Identifier& id, Type* type, ConstantValue* initVal, bool isImmut, LinkageType linkage); @@ -59,13 +62,14 @@ namespace fir Function* getEntryFunction(); void setEntryFunction(Function* fn); - std::unordered_map, GlobalVariable*>> _getVtables() { return this->vtables; } - std::unordered_map _getIntrinsicFunctions() { return this->intrinsicFunctions; } - std::unordered_map _getGlobalStrings() { return this->globalStrings; } - std::unordered_map _getGlobals() { return this->globals; } - std::unordered_map _getFunctions() { return this->functions; } - std::unordered_map _getNamedTypes() { return this->namedTypes; } + const std::unordered_map, GlobalVariable*>>& _getVtables() { return this->vtables; } + const std::unordered_map& _getIntrinsicFunctions() { return this->intrinsicFunctions; } + const std::unordered_map& _getGlobalStrings() { return this->globalStrings; } + const std::unordered_map& _getGlobals() { return this->globals; } + const std::unordered_map& _getFunctions() { return this->functions; } + const std::unordered_map& _getNamedTypes() { return this->namedTypes; } + const std::unordered_map& _getAllGlobals() { return this->allGlobalValues; } private: @@ -77,6 +81,8 @@ namespace fir std::unordered_map functions; std::unordered_map namedTypes; + std::unordered_map allGlobalValues; + std::unordered_map intrinsicFunctions; Function* entryFunction = 0; From 3884fe125bab23df1d1156fc4fe6dcd8255d9f13 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 5 Nov 2018 03:18:01 +0800 Subject: [PATCH 4/9] switch to using '::' for static access --- build/supertiny.flx | 4 +- build/tester.flx | 110 ++++++++++---------- build/tests/arraytest.flx | 30 +++--- build/tests/fizzbuzz.flx | 10 +- build/tests/functions.flx | 12 +-- build/tests/intlimits.flx | 18 ++-- build/tests/linkedlist.flx | 2 +- build/tests/scopes.flx | 26 ++--- build/tests/slices.flx | 4 +- build/tests/unions.flx | 14 +-- build/tests/using.flx | 4 +- libs/stdio.flx | 12 +-- source/fir/GlobalValue.cpp | 2 +- source/fir/Module.cpp | 11 +- source/frontend/lexer.cpp | 6 ++ source/frontend/parser/expr.cpp | 10 +- source/frontend/parser/operators.cpp | 1 + source/include/ast.h | 1 + source/typecheck/dotop.cpp | 150 ++++++++++++++++++++++----- 19 files changed, 274 insertions(+), 153 deletions(-) diff --git a/build/supertiny.flx b/build/supertiny.flx index ed2d02f4..3da2dc8f 100644 --- a/build/supertiny.flx +++ b/build/supertiny.flx @@ -5,7 +5,7 @@ export supertiny import "libc" as _ -import "stdio" as _ +// import "stdio" as _ // var glob: any // fn test_refcounting_problem_thing() @@ -29,7 +29,7 @@ import "stdio" as _ let x = 30 if 1 == 1 < 4 > 3 > -5 == -7 + 2 < 10 > 3 { - println("yay") + printf("yay\n") } diff --git a/build/tester.flx b/build/tester.flx index 10fd8850..313d23f0 100644 --- a/build/tester.flx +++ b/build/tester.flx @@ -49,127 +49,127 @@ fn runTests() let endTitle = "============ TESTS COMPLETE ============\n" - libc.printf("%s%s", fibTitle, thinLine) + libc::printf("%s%s", fibTitle, thinLine) do { var n = 1 while n < 20 { - libc.printf("%d", test_fib.doRecursiveFib(n)) + libc::printf("%d", test_fib::doRecursiveFib(n)) n += 1 if n != 20 { - libc.printf(", ") + libc::printf(", ") } } - libc.printf("\n\n\n") + libc::printf("\n\n\n") } // fizzbuzz - libc.printf("%s%s", fizzbuzzTitle, thinLine) - test_fizz.doFizzBuzz(15) - libc.printf("\n\n\n") + libc::printf("%s%s", fizzbuzzTitle, thinLine) + test_fizz::doFizzBuzz(15) + libc::printf("\n\n\n") // int limits - libc.printf("%s%s", intLimitsTitle, thinLine) - test_limits.printIntegerLimits() - libc.printf("\n\n\n") + libc::printf("%s%s", intLimitsTitle, thinLine) + test_limits::printIntegerLimits() + libc::printf("\n\n\n") // scopes - libc.printf("%s%s", scopeTitle, thinLine) - test_scopes.doScopeTest("__llvm_jit__build/test") - libc.printf("\n\n\n") + libc::printf("%s%s", scopeTitle, thinLine) + test_scopes::doScopeTest("__llvm_jit__build/test") + libc::printf("\n\n\n") // operators and tuples (vectors type, mainly) - libc.printf("%s%s", operatorTitle, thinLine) + libc::printf("%s%s", operatorTitle, thinLine) // doOperatorTupleTest() - libc.printf("\n\n\n") + libc::printf("\n\n\n") // arrays - libc.printf("%s%s", arrayTitle, thinLine) - test_arrays.doArrayTest() - libc.printf("\n\n\n") + libc::printf("%s%s", arrayTitle, thinLine) + test_arrays::doArrayTest() + libc::printf("\n\n\n") // generics - libc.printf("%s%s", genericsTitle, thinLine) - test_generics.doGenericsTest() - libc.printf("\n\n\n") + libc::printf("%s%s", genericsTitle, thinLine) + test_generics::doGenericsTest() + libc::printf("\n\n\n") // classes - libc.printf("%s%s", classTitle, thinLine) - test_classes.doClassTest() - libc.printf("\n\n\n") + libc::printf("%s%s", classTitle, thinLine) + test_classes::doClassTest() + libc::printf("\n\n\n") // first-class-functions - libc.printf("%s%s", functionsTitle, thinLine) - test_functions.doFunctionTest() - libc.printf("\n\n\n") + libc::printf("%s%s", functionsTitle, thinLine) + test_functions::doFunctionTest() + libc::printf("\n\n\n") // defer semantics - libc.printf("%s%s", deferTitle, thinLine) - test_defer.doDeferTest() - libc.printf("\n\n\n") + libc::printf("%s%s", deferTitle, thinLine) + test_defer::doDeferTest() + libc::printf("\n\n\n") // any - libc.printf("%s%s", anyTitle, thinLine) - test_any.doAnyTest() - libc.printf("\n\n\n") + libc::printf("%s%s", anyTitle, thinLine) + test_any::doAnyTest() + libc::printf("\n\n\n") // slices - libc.printf("%s%s", slicesTitle, thinLine) - test_slices.doSlicesTest() - libc.printf("\n\n\n") + libc::printf("%s%s", slicesTitle, thinLine) + test_slices::doSlicesTest() + libc::printf("\n\n\n") // decomposition - libc.printf("%s%s", decomposeTitle, thinLine) - test_decomposition.doDecompositionTest() - libc.printf("\n\n\n") + libc::printf("%s%s", decomposeTitle, thinLine) + test_decomposition::doDecompositionTest() + libc::printf("\n\n\n") // for-loops - libc.printf("%s%s", forLoopTitle, thinLine) - test_forloops.doForLoopTest() - libc.printf("\n\n\n") + libc::printf("%s%s", forLoopTitle, thinLine) + test_forloops::doForLoopTest() + libc::printf("\n\n\n") // linked-list (generics) - libc.printf("%s%s", linkedListTitle, thinLine) - test_linkedlist.doLinkedListTest() - libc.printf("\n\n\n") + libc::printf("%s%s", linkedListTitle, thinLine) + test_linkedlist::doLinkedListTest() + libc::printf("\n\n\n") // unions (generics) - libc.printf("%s%s", unionsTitle, thinLine) - test_unions.doUnionsTest() - libc.printf("\n\n\n") + libc::printf("%s%s", unionsTitle, thinLine) + test_unions::doUnionsTest() + libc::printf("\n\n\n") // using - libc.printf("%s%s", usingTitle, thinLine) - test_using.doUsingTest() - libc.printf("\n\n\n") + libc::printf("%s%s", usingTitle, thinLine) + test_using::doUsingTest() + libc::printf("\n\n\n") // misc tests - libc.printf("%s%s", miscTitle, thinLine) + libc::printf("%s%s", miscTitle, thinLine) // miscellaneousTests() - libc.printf("\n\n\n") + libc::printf("\n\n\n") // fin. - libc.printf("%s\n\n\n\n\n", endTitle) + libc::printf("%s\n\n\n\n\n", endTitle) } @@ -185,7 +185,7 @@ fn runTests() runTests() - libc.printf("\n<< done >>\n") + libc::printf("\n<< done >>\n") return 0 } diff --git a/build/tests/arraytest.flx b/build/tests/arraytest.flx index 0157e1d4..225222d0 100644 --- a/build/tests/arraytest.flx +++ b/build/tests/arraytest.flx @@ -11,31 +11,31 @@ public fn doArrayTest() var arr: [[int: 2]: 2] = [ [ 1, 2 ], [ 5, 6 ] ] arr[1][1] = 400 - libc.printf("a[0][0]: %d, a[0][1]: %d, a[1][0]: %d, a[1][1]: %d\n", arr[0][0], arr[0][1], arr[1][0], arr[1][1]) + libc::printf("a[0][0]: %d, a[0][1]: %d, a[1][0]: %d, a[1][1]: %d\n", arr[0][0], arr[0][1], arr[1][0], arr[1][1]) var d: [f64: 4] = [ 1.0, 2.0, 4.0, 8.0 ] - libc.printf("d[0]: %f, d[1]: %f, d[2]: %f, d[3]: %f\n", d[0], d[1], d[2], d[3]); + libc::printf("d[0]: %f, d[1]: %f, d[2]: %f, d[3]: %f\n", d[0], d[1], d[2], d[3]); var arr1: &mut int = @raw alloc mut int [4] arr1[1] = 97 arr1[2] = 43 - libc.printf("arr[1] = %d\n", (arr1 + 1)[0]) - libc.printf("arr[2] = %d\n", (3 + arr1)[-1]) + libc::printf("arr[1] = %d\n", (arr1 + 1)[0]) + libc::printf("arr[2] = %d\n", (3 + arr1)[-1]) free arr1 - libc.printf("\n\n") + libc::printf("\n\n") // var s = alloc[4][4] string("array of array of strings test") // s[1][2] = "BOO YOU STRING" - // libc.printf("s[1][2] = %s, s[1][2].length = %ld\n", s[1][2], s[1][2].length) + // libc::printf("s[1][2] = %s, s[1][2].length = %ld\n", s[1][2], s[1][2].length) // free s } - libc.printf("\n") + libc::printf("\n") dynamicArrays() } @@ -73,9 +73,9 @@ fn setup(max: int) -> [int] fn dynamicArrays() { do { - libc.printf("PRE X\n") + libc::printf("PRE X\n") var x: [string] - libc.printf("POST X\n") + libc::printf("POST X\n") var y = alloc string [5] var i = 0 @@ -91,26 +91,26 @@ fn dynamicArrays() let k = x var z = k.clone() - libc.printf("z.length = %d\n", z.length) + libc::printf("z.length = %d\n", z.length) z[9] = string("LAST ELEMENT") - libc.printf("z <= x: %d\n", z <= x) + libc::printf("z <= x: %d\n", z <= x) while i < z.length { - libc.printf("z[%ld] = %s // %ld\n", i, z[i], z[i].refcount) + libc::printf("z[%ld] = %s // %ld\n", i, z[i], z[i].refcount) i += 1 } - libc.printf("z.back() = %s, length = %ld, cap = %ld\n", z[z.length - 1], z.length, z.capacity) + libc::printf("z.back() = %s, length = %ld, cap = %ld\n", z[z.length - 1], z.length, z.capacity) - libc.printf("x == k ? %d\n", x == k) + libc::printf("x == k ? %d\n", x == k) let fib = setup(5) let sum = foldl(fib, 1, f) - libc.printf("sum = %d\n", sum) + libc::printf("sum = %d\n", sum) } } diff --git a/build/tests/fizzbuzz.flx b/build/tests/fizzbuzz.flx index f59f5de6..40623c43 100644 --- a/build/tests/fizzbuzz.flx +++ b/build/tests/fizzbuzz.flx @@ -10,23 +10,23 @@ public fn doFizzBuzz(num: int) var i = 0 while i <= num { - libc.printf("%02d: ", i) + libc::printf("%02d: ", i) if i % 3 == 0 { - libc.printf("Fizz") + libc::printf("Fizz") } if i % 5 == 0 { - libc.printf("Buzz") + libc::printf("Buzz") } if i % 5 != 0 && i % 3 != 0 { - libc.printf("%d", i) + libc::printf("%d", i) } - libc.printf("\n") + libc::printf("\n") i += 1 } } diff --git a/build/tests/functions.flx b/build/tests/functions.flx index e7f9ac2a..13f370dc 100644 --- a/build/tests/functions.flx +++ b/build/tests/functions.flx @@ -102,25 +102,25 @@ public fn doFunctionTest() printf("\n\nscope test\n") do { - g = SomeClass.bar + g = SomeClass::bar g(10) - g = SomeNS.NestedClass.bar + g = SomeNS::NestedClass::bar g(20) - g = SomeNS.foo + g = SomeNS::foo g(30) } printf("\nmethod test\n") do { - var method = SomeClass.foo + var method = SomeClass::foo let sc = SomeClass() method(&sc, 40) - var method2 = SomeNS.NestedClass.foo - let nc = SomeNS.NestedClass() + var method2 = SomeNS::NestedClass::foo + let nc = SomeNS::NestedClass() method2(&nc, 50) } diff --git a/build/tests/intlimits.flx b/build/tests/intlimits.flx index db04dfa4..f9573874 100644 --- a/build/tests/intlimits.flx +++ b/build/tests/intlimits.flx @@ -9,15 +9,15 @@ import "limits" public fn printIntegerLimits() { - libc.printf("i8.min = %hd\t\t\t\ti8.max = %hhd\n", limits.int8.min as i16, limits. int8.max); - libc.printf("i16.min = %hd\t\t\ti16.max = %hd\n", limits.int16.min, limits.int16.max); - libc.printf("i32.min = %d\t\t\ti32.max = %d\n", limits.int32.min, limits.int32.max); - libc.printf("i64.min = %lld\t\ti64.max = %lld\n", limits.int64.min, limits.int64.max); + libc::printf("i8.min = %hd\t\t\t\ti8.max = %hhd\n", limits::int8::min as i16, limits::int8::max); + libc::printf("i16::min = %hd\t\t\ti16::max = %hd\n", limits::int16::min, limits::int16::max); + libc::printf("i32::min = %d\t\t\ti32::max = %d\n", limits::int32::min, limits::int32::max); + libc::printf("i64::min = %lld\t\ti64::max = %lld\n", limits::int64::min, limits::int64::max); - libc.printf("\n") + libc::printf("\n") - libc.printf("u8.min = %hhu\t\t\t\tu8.max = %hhu\n", limits.uint8.min,limits. uint8.max); - libc.printf("u16.min = %hu\t\t\t\tu16.max = %hu\n", limits.uint16.min, limits.uint16.max); - libc.printf("u32.min = %u\t\t\t\tu32.max = %u\n", limits.uint32.min, limits.uint32.max); - libc.printf("u64.min = %llu\t\t\t\tu64.max = %llu\n", limits.uint64.min, limits.uint64.max); + libc::printf("u8::min = %hhu\t\t\t\tu8::max = %hhu\n", limits::uint8::min,limits:: uint8::max); + libc::printf("u16::min = %hu\t\t\t\tu16::max = %hu\n", limits::uint16::min, limits::uint16::max); + libc::printf("u32::min = %u\t\t\t\tu32::max = %u\n", limits::uint32::min, limits::uint32::max); + libc::printf("u64::min = %llu\t\t\t\tu64::max = %llu\n", limits::uint64::min, limits::uint64::max); } diff --git a/build/tests/linkedlist.flx b/build/tests/linkedlist.flx index 7a3fd794..a27f84b1 100644 --- a/build/tests/linkedlist.flx +++ b/build/tests/linkedlist.flx @@ -50,7 +50,7 @@ public fn doLinkedListTest() { do { var list = LinkedList(data: 41) - LinkedList.hello() + LinkedList::hello() var list2 = LinkedList(data: "foo") list2.insert("hello") diff --git a/build/tests/scopes.flx b/build/tests/scopes.flx index f88df5bc..bb6ccbaf 100644 --- a/build/tests/scopes.flx +++ b/build/tests/scopes.flx @@ -10,7 +10,7 @@ import "math" @operator prefix 900 √ -operator prefix √ (x: f64) -> f64 { return math.sqrt(x) } +operator prefix √ (x: f64) -> f64 { return math::sqrt(x) } class Orr { @@ -114,7 +114,7 @@ public fn doScopeTest(argv: str) let m = √(41.5) - let mmx = Something.Inside.Another + let mmx = Something::Inside::Another let another = 4 let foo = Something() @@ -125,25 +125,25 @@ public fn doScopeTest(argv: str) printf("gg.0: %d, gg.1: %.2f\n", (4, 50).0, gg.1) printf("[%d]\n", foo.oor) - printf("p: %f, g: %d, m: %d, %.14f\n\n", p, another, mmx, math.π) + printf("p: %f, g: %d, m: %d, %.14f\n\n", p, another, mmx, math::π) - let x1: int = OutsideEnum.Three as int - let x2: int = Something.InsideEnum.Quadruple as int - let x3 = n1.n2.DeepClass.DeeperClass() - printf("x1: %d, x2: %d, x3: %d\n", x1, x2, n1.n2.DeepClass.DeeperClass.deepStatic()) + let x1: int = OutsideEnum::Three as int + let x2: int = Something::InsideEnum::Quadruple as int + let x3 = n1::n2::DeepClass::DeeperClass() + printf("x1: %d, x2: %d, x3: %d\n", x1, x2, n1::n2::DeepClass::DeeperClass::deepStatic()) - n1.n2.m.foo.oor = 968 + n1::n2::m.foo.oor = 968 - let t1 = n1.n2.m.foo.somefoo().bar - let t2 = n1.n2.nest().0.length + let t1 = n1::n2::m.foo.somefoo().bar + let t2 = n1::n2::nest().0.length - n1.n2.tup.0 = "HELLO, WORLD!" //string("HELLO, WORLD") + "!" - printf("tup: %s\n", n1.n2.tup.0) + n1::n2::tup.0 = "HELLO, WORLD!" //string("HELLO, WORLD") + "!" + printf("tup: %s\n", n1::n2::tup.0) printf("t1 = %d, t2 = %d\n", t1, t2) - printf("afoo: %d\n", Sheep.afoo.somefoo().bar) + printf("afoo: %d\n", Sheep::afoo.somefoo().bar) } diff --git a/build/tests/slices.flx b/build/tests/slices.flx index 6752b68e..3befc759 100644 --- a/build/tests/slices.flx +++ b/build/tests/slices.flx @@ -10,10 +10,10 @@ public fn doSlicesTest() do { var arr = [ 2, 3, 5, 7, 11, 13, 17 ] let slice = arr[:] - libc.printf("-- %d, %d, %d, %d, %d, %d, %d --\n", slice[0], slice[1], slice[2], slice[3], slice[4], slice[5], slice[6]) + libc::printf("-- %d, %d, %d, %d, %d, %d, %d --\n", slice[0], slice[1], slice[2], slice[3], slice[4], slice[5], slice[6]) let s = "Hello, world!" - libc.printf("original: %s\nslice: %.*s\n", s, s[3:10].length, s[3:10]) + libc::printf("original: %s\nslice: %.*s\n", s, s[3:10].length, s[3:10]) } } diff --git a/build/tests/unions.flx b/build/tests/unions.flx index 676aa7ec..a4895e4c 100644 --- a/build/tests/unions.flx +++ b/build/tests/unions.flx @@ -15,10 +15,10 @@ public fn doUnionsTest() { do { - let x = option.some("foobar") - let y = option.some(456) + let x = option::some("foobar") + let y = option::some(456) - printf("x = %s, y = %d\n", x as option.some, y as option.some) + printf("x = %s, y = %d\n", x as option::some, y as option::some) } do { @@ -29,11 +29,11 @@ public fn doUnionsTest() none } - let q = Bar.some("hello") - let v = Bar.other(30) - let m = Bar.none + let q = Bar::some("hello") + let v = Bar::other(30) + let m = Bar::none - printf("q = %s, v = %d\n", q as Bar.some, v as Bar.other) + printf("q = %s, v = %d\n", q as Bar::some, v as Bar::other) } } diff --git a/build/tests/using.flx b/build/tests/using.flx index bac0b9a8..a4fde4f3 100644 --- a/build/tests/using.flx +++ b/build/tests/using.flx @@ -19,7 +19,7 @@ public fn doUsingTest() printf("a = %d, b = %d, c = %d\n", Alpha.value, Bravo.value, Charlie.value) using Foo as f - printf("a = %d, b = %d, c = %d\n", 3 * f.Alpha.value, 3 * f.Bravo.value, 3 * f.Charlie.value) + printf("a = %d, b = %d, c = %d\n", 3 * f::Alpha.value, 3 * f::Bravo.value, 3 * f::Charlie.value) } do { @@ -34,7 +34,7 @@ public fn doUsingTest() } } - using xxx.Foo as _ + using xxx::Foo as _ printf("a = %d, b = %d, c = %d\n", Alpha.value, Bravo.value, Charlie.value) } diff --git a/libs/stdio.flx b/libs/stdio.flx index a469b2b2..44c3b311 100644 --- a/libs/stdio.flx +++ b/libs/stdio.flx @@ -14,14 +14,14 @@ fn char(c: str) -> i8 => c[0] fn error(msg: str) { - libc.printf("Invalid format specifier: %s\n", msg) - libc.abort() + libc::printf("Invalid format specifier: %s\n", msg) + libc::abort() } fn to_string_i64(n: i64) -> string { var ret = @raw alloc i8 [16] - let len = libc.sprintf(ret, "%lld", n) + let len = libc::sprintf(ret, "%lld", n) let s = string(ret, len) free ret @@ -32,7 +32,7 @@ fn to_string_i64(n: i64) -> string fn to_string_u64(n: u64) -> string { var ret = @raw alloc i8 [16] - let len = libc.sprintf(ret, "%llu", n) + let len = libc::sprintf(ret, "%llu", n) let s = string(ret, len) free ret @@ -44,7 +44,7 @@ fn to_string_u64(n: u64) -> string fn to_string_f64(n: f64) -> string { var ret = @raw alloc i8 [24] - let len = libc.sprintf(ret, "%f", n) + let len = libc::sprintf(ret, "%f", n) let s = string(ret, len) free ret @@ -118,7 +118,7 @@ public fn format(fmt: str, args: [any: ...]) -> string public fn println(fmt: str, args: [any: ...]) { - libc.printf("%s\n", format(fmt, ...args).ptr) + libc::printf("%s\n", format(fmt, ...args).ptr) } diff --git a/source/fir/GlobalValue.cpp b/source/fir/GlobalValue.cpp index 61525542..136e6d2d 100644 --- a/source/fir/GlobalValue.cpp +++ b/source/fir/GlobalValue.cpp @@ -30,7 +30,7 @@ namespace fir if(!immutable) this->kind = Kind::lvalue; else this->kind = Kind::clvalue; - module->globals[this->ident] = this; + // module->globals[this->ident] = this; } void GlobalVariable::setInitialValue(ConstantValue* constVal) diff --git a/source/fir/Module.cpp b/source/fir/Module.cpp index b8c3dc4c..48264e32 100644 --- a/source/fir/Module.cpp +++ b/source/fir/Module.cpp @@ -16,11 +16,18 @@ namespace fir GlobalVariable* Module::createGlobalVariable(const Identifier& ident, Type* type, ConstantValue* initVal, bool isImmut, LinkageType linkage) { + // if(this->globals.find(ident) != this->globals.end()) + // error("Already have a global with name '%s'", ident.str()); + + // // this adds itself to the module list. + // return new GlobalVariable(ident, this, type, isImmut, linkage, initVal); + + GlobalVariable* gv = new GlobalVariable(ident, this, type, isImmut, linkage, initVal); if(this->globals.find(ident) != this->globals.end()) error("Already have a global with name '%s'", ident.str()); - // this adds itself to the module list. - return new GlobalVariable(ident, this, type, isImmut, linkage, initVal); + this->globals[ident] = gv; + return gv; } GlobalVariable* Module::createGlobalVariable(const Identifier& id, Type* type, bool isImmut, LinkageType linkage) diff --git a/source/frontend/lexer.cpp b/source/frontend/lexer.cpp index 75397559..2eeb5a7d 100644 --- a/source/frontend/lexer.cpp +++ b/source/frontend/lexer.cpp @@ -304,6 +304,12 @@ namespace lexer tok.text = "^="; read = 2; } + else if(hasPrefix(stream, "::")) + { + tok.type = TokenType::DoubleColon; + tok.text = "::"; + read = 2; + } else if(hasPrefix(stream, "...")) { tok.type = TokenType::Ellipsis; diff --git a/source/frontend/parser/expr.cpp b/source/frontend/parser/expr.cpp index 8081cfde..dbe103ff 100644 --- a/source/frontend/parser/expr.cpp +++ b/source/frontend/parser/expr.cpp @@ -254,6 +254,7 @@ namespace parser return 2000; case TT::Period: + case TT::DoubleColon: return 1000; // unary ! @@ -412,12 +413,15 @@ namespace parser rhs = parseRhs(st, rhs, prec + 1); - if(op == ".") + if(op == "." || op == "::") { loc.col = lhs->loc.col; - loc.len = rhs->loc.col - lhs->loc.col + 1; //! is this correct??? or this? + rhs->loc.len; + loc.len = rhs->loc.col + rhs->loc.len - lhs->loc.col; - lhs = util::pool(loc, dynamic_cast(lhs), rhs); + auto dot = util::pool(loc, dynamic_cast(lhs), rhs); + dot->isStatic = (op == "::"); + + lhs = dot; } else if(op == "..." || op == "..<") { diff --git a/source/frontend/parser/operators.cpp b/source/frontend/parser/operators.cpp index 0cf87304..aac98ae7 100644 --- a/source/frontend/parser/operators.cpp +++ b/source/frontend/parser/operators.cpp @@ -144,6 +144,7 @@ namespace parser case TT::As: return Operator::TypeCast; case TT::Is: return Operator::TypeIs; + case TT::DoubleColon: return "::"; case TT::Period: return "."; case TT::At: return "@"; diff --git a/source/include/ast.h b/source/include/ast.h index 4b0158bd..c94b8ed5 100644 --- a/source/include/ast.h +++ b/source/include/ast.h @@ -677,6 +677,7 @@ namespace ast Expr* left = 0; Expr* right = 0; + bool isStatic = false; }; diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index ded153cb..644a7c9e 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -14,12 +14,85 @@ #include "mpool.h" + + +// make the nice message. +static ErrorMsg* wrongDotOpError(ErrorMsg* e, sst::StructDefn* str, const Location& l, const std::string& name, bool usedStatic) +{ + if(usedStatic) + { + // check static ones for a better error message. + sst::Defn* found = 0; + for(auto sm : str->methods) + if(sm->id.name == name) { found = sm; break; } + + if(!found) + { + for(auto sf : str->fields) + if(sf->id.name == name) { found = sf; break; } + } + + if(found) e->append(SimpleError::make(MsgType::Note, found->loc, "use '.' to refer to the instance member '%s'", name)); + + return e; + } + else + { + // check static ones for a better error message. + sst::Defn* found = 0; + for(auto sm : str->staticMethods) + if(sm->id.name == name) { found = sm; break; } + + if(!found) + { + for(auto sf : str->staticFields) + if(sf->id.name == name) { found = sf; break; } + } + + + if(found) e->append(SimpleError::make(MsgType::Note, found->loc, "use '::' to refer to the static member '%s'", name)); + + return e; + } +}; + + + + + + + + + + + + + + + + static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* dotop, fir::Type* infer) { auto lhs = dotop->left->typecheck(fs).expr(); // first we got to find the struct defn based on the type auto type = lhs->type; + if(!type) + { + if(dcast(sst::ScopeExpr, lhs) || (dcast(sst::VarRef, lhs) && dcast(sst::TreeDefn, dcast(sst::VarRef, lhs)->def))) + { + error(dotop, "invalid use of '.' for static scope access; use '::' instead"); + } + else + { + error(lhs, "failed to resolve type of lhs in expression dot-op!"); + } + } + else if(auto vr = dcast(sst::VarRef, lhs); vr && (dcast(sst::UnionDefn, vr->def) || dcast(sst::EnumDefn, vr->def))) + { + error(dotop, "use '::' to access enumeration cases and union variants"); + } + if(type->isTupleType()) { ast::LitNumber* ln = dcast(ast::LitNumber, dotop->right); @@ -287,6 +360,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d if(auto str = dcast(sst::StructDefn, defn)) { + // right. if(auto fc = dcast(ast::FunctionCall, dotop->right)) { @@ -331,7 +405,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d while(base) { auto cds = util::filter(util::map(str->fields, [](sst::VarDefn* fd) -> sst::Defn* { return fd; }), - [fc](const sst::Defn* d) -> bool { return d->id.name == fc->name; }); + [fc](const sst::Defn* d) -> bool { return d->id.name == fc->name && d->type->isFunctionType(); }); vcands.insert(vcands.end(), cds.begin(), cds.end()); @@ -345,11 +419,13 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d if(mcands.empty() && vcands.empty()) - error(fc, "no method or field named '%s' in struct/class '%s'", fc->name, str->id.name); - - sst::Defn* resolved = search(mcands, &arguments, true); + { + wrongDotOpError(SimpleError::make(fc->loc, "no method named '%s' in type '%s'", fc->name, str->id.name), + str, fc->loc, fc->name, false)->postAndQuit(); + } + sst::Defn* resolved = search(mcands, &arguments, true); sst::Expr* finalCall = 0; if(resolved) { @@ -415,8 +491,11 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d } // ok, we didn't find it. - if(auto cls = dcast(sst::ClassDefn, copy); cls && cls->baseClass) + if(auto cls = dcast(sst::ClassDefn, copy); cls) copy = cls->baseClass; + + else + copy = nullptr; } } @@ -431,10 +510,15 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d if(meths.empty()) { - error(dotop->right, "no such instance field or method named '%s' in struct '%s'", name, str->id.name); + wrongDotOpError(SimpleError::make(fc->loc, "no field named '%s' in type '%s'", fld->name, str->id.name), + str, fld->loc, fld->name, false)->postAndQuit(); } else { + // ok, we potentially have a method -- if we used '.', error out. + if(!dotop->isStatic) + error(dotop, "use '::' to refer to a method of a type"); + fir::Type* retty = 0; // ok, disambiguate if we need to @@ -486,7 +570,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d } else { - error(dotop->right, "unsupported right-side expression for dot-operator on struct/class '%s'", str->id.name); + error(dotop->right, "unsupported right-side expression for dot-operator on type '%s'", str->id.name); } } else @@ -497,10 +581,14 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d + + + + static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, sst::Expr* left, fir::Type* infer) { auto checkRhs = [](sst::TypecheckState* fs, ast::DotOperator* dot, const std::vector& olds, const std::vector& news, - fir::Type* rhs_infer) -> sst::Expr* { + fir::Type* rhs_infer, sst::StructDefn* possibleStructDefn = 0) -> sst::Expr* { if(auto id = dcast(ast::Ident, dot->right)) id->traverseUpwards = false; @@ -533,16 +621,16 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, else if(dcast(ast::Ident, dot->right) || dcast(ast::DotOperator, dot->right)) { fs->teleportToScope(news); - ret = dot->right->typecheck(fs, rhs_infer).expr(); - - //* special-case this thing. if we don't do this, then 'ret' is just a normal VarRef, - //* which during codegen will try to trigger the codegen for the UnionVariantDefn, - //* which returns 0 (because there's really nothing to define at code-gen time) - - //? note: ret->type can be null if we're in the middle of a namespace dot-op, - //? and 'ret' is a ScopeExpr. - if(ret->type && ret->type->isUnionVariantType()) - ret = sst::TypeExpr::make(ret->loc, ret->type); + auto res = dot->right->typecheck(fs, rhs_infer); + if(res.isError() && possibleStructDefn && dcast(ast::Ident, dot->right)) + { + wrongDotOpError(res.error(), possibleStructDefn, dot->right->loc, dcast(ast::Ident, dot->right)->name, true)->postAndQuit(); + } + else + { + // will post if res is an error, even if we didn't give the fancy error. + ret = res.expr(); + } } else { @@ -689,7 +777,7 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, } else { - // note: fallthrough to call to doExpressionDotOp() + error(dot, "invalid static access on variable (of type '%s'); use '.' to refer to instance members", def->type); } } else if(auto scp = dcast(sst::ScopeExpr, left)) @@ -713,6 +801,7 @@ static sst::Expr* doStaticDotOp(sst::TypecheckState* fs, ast::DotOperator* dot, } } + error("????!!!!"); return 0; } @@ -726,11 +815,8 @@ TCResult ast::DotOperator::typecheck(sst::TypecheckState* fs, fir::Type* infer) this->left->checkAsType = this->checkAsType; this->right->checkAsType = this->checkAsType; - auto ret = doStaticDotOp(fs, this, this->left->typecheck(fs).expr(), infer); - if(ret) return TCResult(ret); - - // catch-all, probably. - return TCResult(doExpressionDotOp(fs, this, infer)); + if(this->isStatic) return TCResult(doStaticDotOp(fs, this, this->left->typecheck(fs).expr(), infer)); + else return TCResult(doExpressionDotOp(fs, this, infer)); } @@ -738,6 +824,22 @@ TCResult ast::DotOperator::typecheck(sst::TypecheckState* fs, fir::Type* infer) + + + + + + + + + + + + + + + + From 1c0c71d114312e86bfcc9cdfb1dd530f37b3b838 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 5 Nov 2018 14:02:00 +0800 Subject: [PATCH 5/9] fix '::' in type specifiers --- meson.build | 2 +- source/codegen/{dotops.cpp => dotop.cpp} | 0 source/typecheck/type.cpp | 10 +++++----- 3 files changed, 6 insertions(+), 6 deletions(-) rename source/codegen/{dotops.cpp => dotop.cpp} (100%) diff --git a/meson.build b/meson.build index 086b8370..24e8d407 100644 --- a/meson.build +++ b/meson.build @@ -122,10 +122,10 @@ source_files = files([ 'source/codegen/slice.cpp', 'source/codegen/alloc.cpp', 'source/codegen/enums.cpp', + 'source/codegen/dotop.cpp', 'source/codegen/ranges.cpp', 'source/codegen/sizeof.cpp', 'source/codegen/assign.cpp', - 'source/codegen/dotops.cpp', 'source/codegen/unions.cpp', 'source/codegen/structs.cpp', 'source/codegen/classes.cpp', diff --git a/source/codegen/dotops.cpp b/source/codegen/dotop.cpp similarity index 100% rename from source/codegen/dotops.cpp rename to source/codegen/dotop.cpp diff --git a/source/typecheck/type.cpp b/source/typecheck/type.cpp index 799150a4..9e0d2b47 100644 --- a/source/typecheck/type.cpp +++ b/source/typecheck/type.cpp @@ -153,7 +153,7 @@ namespace sst }; - if(name.find(".") == std::string::npos) + if(name.find("::") == std::string::npos) { return returnTheThing(this->stree, name, false, allowFail); } @@ -164,19 +164,19 @@ namespace sst std::deque scopes; { std::string tmp; - for(auto c : name) + for(size_t i = 0; i < name.size(); i++) { - if(c == '.') + if(name[i] == ':' && (i + 1 < name.size()) && name[i+1] == ':') { if(tmp.empty()) - error(this->loc(), "expected identifier between consecutive periods ('.') in nested type specifier"); + error(this->loc(), "expected identifier between consecutive scopes ('::') in nested type specifier"); scopes.push_back(tmp); tmp.clear(); } else { - tmp += c; + tmp += name[i]; } } From 14d433083eac72f4432173bb2d7e1b14b4a7b34d Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 5 Nov 2018 14:12:32 +0800 Subject: [PATCH 6/9] use `Foo!<...>` to instantiate polys; should reduce ambiguity. --- build/tests/linkedlist.flx | 2 +- build/tests/unions.flx | 2 +- build/tests/using.flx | 6 +++--- source/fir/GlobalValue.cpp | 2 +- source/fir/Module.cpp | 11 +++++++++-- source/frontend/parser/expr.cpp | 19 +++++++++---------- 6 files changed, 24 insertions(+), 18 deletions(-) diff --git a/build/tests/linkedlist.flx b/build/tests/linkedlist.flx index 7a3fd794..0f9f5270 100644 --- a/build/tests/linkedlist.flx +++ b/build/tests/linkedlist.flx @@ -50,7 +50,7 @@ public fn doLinkedListTest() { do { var list = LinkedList(data: 41) - LinkedList.hello() + LinkedList!.hello() var list2 = LinkedList(data: "foo") list2.insert("hello") diff --git a/build/tests/unions.flx b/build/tests/unions.flx index 676aa7ec..b1b00a70 100644 --- a/build/tests/unions.flx +++ b/build/tests/unions.flx @@ -18,7 +18,7 @@ public fn doUnionsTest() let x = option.some("foobar") let y = option.some(456) - printf("x = %s, y = %d\n", x as option.some, y as option.some) + printf("x = %s, y = %d\n", x as option!.some, y as option!.some) } do { diff --git a/build/tests/using.flx b/build/tests/using.flx index bac0b9a8..6d60d326 100644 --- a/build/tests/using.flx +++ b/build/tests/using.flx @@ -34,7 +34,7 @@ public fn doUsingTest() } } - using xxx.Foo as _ + using xxx!.Foo as _ printf("a = %d, b = %d, c = %d\n", Alpha.value, Bravo.value, Charlie.value) } @@ -60,8 +60,8 @@ public fn doUsingTest() none } - using Option as _ - using Option as _ + using Option! as _ + using Option! as _ let x = some(30) let y = some("bye") diff --git a/source/fir/GlobalValue.cpp b/source/fir/GlobalValue.cpp index 61525542..136e6d2d 100644 --- a/source/fir/GlobalValue.cpp +++ b/source/fir/GlobalValue.cpp @@ -30,7 +30,7 @@ namespace fir if(!immutable) this->kind = Kind::lvalue; else this->kind = Kind::clvalue; - module->globals[this->ident] = this; + // module->globals[this->ident] = this; } void GlobalVariable::setInitialValue(ConstantValue* constVal) diff --git a/source/fir/Module.cpp b/source/fir/Module.cpp index b8c3dc4c..48264e32 100644 --- a/source/fir/Module.cpp +++ b/source/fir/Module.cpp @@ -16,11 +16,18 @@ namespace fir GlobalVariable* Module::createGlobalVariable(const Identifier& ident, Type* type, ConstantValue* initVal, bool isImmut, LinkageType linkage) { + // if(this->globals.find(ident) != this->globals.end()) + // error("Already have a global with name '%s'", ident.str()); + + // // this adds itself to the module list. + // return new GlobalVariable(ident, this, type, isImmut, linkage, initVal); + + GlobalVariable* gv = new GlobalVariable(ident, this, type, isImmut, linkage, initVal); if(this->globals.find(ident) != this->globals.end()) error("Already have a global with name '%s'", ident.str()); - // this adds itself to the module list. - return new GlobalVariable(ident, this, type, isImmut, linkage, initVal); + this->globals[ident] = gv; + return gv; } GlobalVariable* Module::createGlobalVariable(const Identifier& id, Type* type, bool isImmut, LinkageType linkage) diff --git a/source/frontend/parser/expr.cpp b/source/frontend/parser/expr.cpp index 8081cfde..656d937a 100644 --- a/source/frontend/parser/expr.cpp +++ b/source/frontend/parser/expr.cpp @@ -706,22 +706,21 @@ namespace parser auto ident = util::pool(st.ploc(), name); - //! ACHTUNG ! - //* here begins the shitshow of generic angle-bracket parsing. - - if(st.front() == TT::LAngle) + //* we've modified our generic thing to be Foo!<...>, so there shouldn't + //* be any ambiguity left. once we see '!' and '<' we know for sure. + //? as long as we don't make '!' a postfix operator... + //? for now, we'll leave in the try-and-restore mechanism. + if(st.front() == TT::Exclamation && st.lookahead(1) == TT::LAngle) { - // step 1: store the current position so we can rewind later. + bool fail = false; auto restore = st.getIndex(); - // step 2: try and parse a generic mapping. + st.pop(); + st.pop(); - bool fail = false; std::unordered_map mappings; { - st.pop(); - - //* foo<> is an error regardless of whether we're doing expression parsing or call parsing. + //* foo!<> is an error regardless of whether we're doing expression parsing or call parsing. if(st.front() == TT::RAngle) error(st.loc(), "at least one type argument is required between angle brackets <>"); From 372c06aa13c68c6ce5b1aadf4fdd4aa50c79281f Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 5 Nov 2018 14:13:57 +0800 Subject: [PATCH 7/9] fix a bug where we were doing stupid things with globals --- source/fir/GlobalValue.cpp | 2 +- source/fir/Module.cpp | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/source/fir/GlobalValue.cpp b/source/fir/GlobalValue.cpp index 61525542..136e6d2d 100644 --- a/source/fir/GlobalValue.cpp +++ b/source/fir/GlobalValue.cpp @@ -30,7 +30,7 @@ namespace fir if(!immutable) this->kind = Kind::lvalue; else this->kind = Kind::clvalue; - module->globals[this->ident] = this; + // module->globals[this->ident] = this; } void GlobalVariable::setInitialValue(ConstantValue* constVal) diff --git a/source/fir/Module.cpp b/source/fir/Module.cpp index b8c3dc4c..48264e32 100644 --- a/source/fir/Module.cpp +++ b/source/fir/Module.cpp @@ -16,11 +16,18 @@ namespace fir GlobalVariable* Module::createGlobalVariable(const Identifier& ident, Type* type, ConstantValue* initVal, bool isImmut, LinkageType linkage) { + // if(this->globals.find(ident) != this->globals.end()) + // error("Already have a global with name '%s'", ident.str()); + + // // this adds itself to the module list. + // return new GlobalVariable(ident, this, type, isImmut, linkage, initVal); + + GlobalVariable* gv = new GlobalVariable(ident, this, type, isImmut, linkage, initVal); if(this->globals.find(ident) != this->globals.end()) error("Already have a global with name '%s'", ident.str()); - // this adds itself to the module list. - return new GlobalVariable(ident, this, type, isImmut, linkage, initVal); + this->globals[ident] = gv; + return gv; } GlobalVariable* Module::createGlobalVariable(const Identifier& id, Type* type, bool isImmut, LinkageType linkage) From 3a38a5bbb3990576f083a41257cf2d153f184b26 Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 5 Nov 2018 17:36:46 +0800 Subject: [PATCH 8/9] allow implicit (aka positional, aka without names) polymorphic arguments basically (eg. Foo instead of Foo) --- build/tests/linkedlist.flx | 2 +- build/tests/unions.flx | 2 +- build/tests/using.flx | 4 +- source/frontend/parser/expr.cpp | 61 +------ source/frontend/parser/function.cpp | 17 +- source/frontend/parser/operators.cpp | 3 - source/frontend/parser/type.cpp | 190 ++++++++++++++------ source/frontend/pts.cpp | 20 +-- source/include/ast.h | 8 +- source/include/defs.h | 20 +++ source/include/parser_internal.h | 7 +- source/include/polymorph.h | 4 +- source/include/pts.h | 4 +- source/include/resolver.h | 10 +- source/include/typecheck.h | 1 - source/misc/identifier.cpp | 26 +++ source/typecheck/alloc.cpp | 2 +- source/typecheck/call.cpp | 3 +- source/typecheck/classes.cpp | 2 +- source/typecheck/dotop.cpp | 2 +- source/typecheck/polymorph/driver.cpp | 18 +- source/typecheck/polymorph/instantiator.cpp | 8 +- source/typecheck/polymorph/misc.cpp | 16 +- source/typecheck/resolver/driver.cpp | 19 +- source/typecheck/resolver/misc.cpp | 45 +++++ source/typecheck/resolver/resolver.cpp | 31 ++-- source/typecheck/type.cpp | 10 +- source/typecheck/variable.cpp | 5 +- 28 files changed, 349 insertions(+), 191 deletions(-) diff --git a/build/tests/linkedlist.flx b/build/tests/linkedlist.flx index 0f9f5270..7d51c86c 100644 --- a/build/tests/linkedlist.flx +++ b/build/tests/linkedlist.flx @@ -50,7 +50,7 @@ public fn doLinkedListTest() { do { var list = LinkedList(data: 41) - LinkedList!.hello() + LinkedList!.hello() var list2 = LinkedList(data: "foo") list2.insert("hello") diff --git a/build/tests/unions.flx b/build/tests/unions.flx index b1b00a70..6e75c835 100644 --- a/build/tests/unions.flx +++ b/build/tests/unions.flx @@ -18,7 +18,7 @@ public fn doUnionsTest() let x = option.some("foobar") let y = option.some(456) - printf("x = %s, y = %d\n", x as option!.some, y as option!.some) + printf("x = %s, y = %d\n", x as option!.some, y as option!.some) } do { diff --git a/build/tests/using.flx b/build/tests/using.flx index 6d60d326..2ee8401d 100644 --- a/build/tests/using.flx +++ b/build/tests/using.flx @@ -34,7 +34,7 @@ public fn doUsingTest() } } - using xxx!.Foo as _ + using xxx!.Foo as _ printf("a = %d, b = %d, c = %d\n", Alpha.value, Bravo.value, Charlie.value) } @@ -61,7 +61,7 @@ public fn doUsingTest() } using Option! as _ - using Option! as _ + using Option! as _ let x = some(30) let y = some("bye") diff --git a/source/frontend/parser/expr.cpp b/source/frontend/parser/expr.cpp index 656d937a..f9c44501 100644 --- a/source/frontend/parser/expr.cpp +++ b/source/frontend/parser/expr.cpp @@ -715,60 +715,15 @@ namespace parser bool fail = false; auto restore = st.getIndex(); - st.pop(); - st.pop(); - - std::unordered_map mappings; + auto pams = parsePAMs(st, &fail); + if(fail) { - //* foo!<> is an error regardless of whether we're doing expression parsing or call parsing. - if(st.front() == TT::RAngle) - error(st.loc(), "at least one type argument is required between angle brackets <>"); - - // step 2A: start parsing. - while(st.front() != TT::RAngle) - { - if(st.front() != TT::Identifier) - { - fail = true; - break; - } - - auto id = st.pop().str(); - if(st.pop() != TT::Colon) - { - fail = true; - break; - } - - //? I think beyond this point we pretty much can't fail since we have the colon. - //? so, we shouldn't need to handle the case where we fail to parse a type here. - if(mappings.find(id) != mappings.end()) - error(st.loc(), "type parameter '%s' already exists in the type parameter list for entity '%s'", id, name); - - auto ty = parseType(st); - mappings[id] = ty; - - if(st.front() == TT::Comma) - st.pop(); - - else if(st.front() != TT::RAngle) - expected(st.loc(), "',' or '>' in type parameter list to entity '" + name + "'", st.front().str()); - - // ok, if we get an rangle we break out of the loop here. - } - - if(fail) - { - st.rewindTo(restore); - } - else - { - iceAssert(st.front() == TT::RAngle); - st.pop(); - - ident->mappings = mappings; - ident->loc.len += (st.ploc().col - st.getTokenList()[restore].loc.col) + 1; - } + st.rewindTo(restore); + } + else + { + ident->mappings = pams; + ident->loc.len += (st.ploc().col - st.getTokenList()[restore].loc.col) + 1; } } diff --git a/source/frontend/parser/function.cpp b/source/frontend/parser/function.cpp index 3688305f..f8037e70 100644 --- a/source/frontend/parser/function.cpp +++ b/source/frontend/parser/function.cpp @@ -14,12 +14,12 @@ using namespace lexer; using TT = lexer::TokenType; namespace parser { - // declared in parser/operators.cpp (because we use it there) - std::tuple, std::unordered_map, pts::Type*, bool, Location> parseFunctionLookingDecl(State& st) + std::tuple, std::vector>, pts::Type*, bool, Location> + parseFunctionLookingDecl(State& st) { pts::Type* returnType = 0; std::vector args; - std::unordered_map generics; + std::vector> generics; // check for generic function if(st.front() == TT::LAngle) @@ -221,9 +221,10 @@ namespace parser - std::unordered_map parseGenericTypeList(State& st) + std::vector> parseGenericTypeList(State& st) { - std::unordered_map ret; + std::unordered_set seen; + std::vector> ret; while(st.front().type != TT::RAngle) { @@ -257,7 +258,11 @@ namespace parser error(st, "expected ',' or '>' to end type parameter list (2)"); } - ret[gt] = constrs; + if(seen.find(gt) != seen.end()) + error(st, "duplicate type parameter '%s'", gt); + + seen.insert(gt); + ret.push_back({ gt, constrs }); } else if(st.front().type == TT::Comma) { diff --git a/source/frontend/parser/operators.cpp b/source/frontend/parser/operators.cpp index 0cf87304..ca845d42 100644 --- a/source/frontend/parser/operators.cpp +++ b/source/frontend/parser/operators.cpp @@ -16,9 +16,6 @@ using namespace lexer; using TT = lexer::TokenType; namespace parser { - std::tuple, std::unordered_map, pts::Type*, bool, Location> - parseFunctionLookingDecl(State& st); - OperatorOverloadDefn* parseOperatorOverload(State& st) { using Kind = OperatorOverloadDefn::Kind; diff --git a/source/frontend/parser/type.cpp b/source/frontend/parser/type.cpp index 1ad4fe34..c5645276 100644 --- a/source/frontend/parser/type.cpp +++ b/source/frontend/parser/type.cpp @@ -477,9 +477,13 @@ namespace parser { s += ".", st.eat(); } + else if(st.front() == TT::DoubleColon) + { + s += "::", st.eat(); + } else if(st.front() == TT::Identifier) { - if(s.back() != '.') + if(s.back() != '.' && s.back() != ':') error(st, "unexpected identifer '%s' in type", st.front().str()); else @@ -493,57 +497,60 @@ namespace parser // check generic mapping - std::map gmaps; - if(st.front() == TT::LAngle) + PolyArgMapping_t pams; + if(st.front() == TT::Exclamation && st.lookahead(1) == TT::LAngle) { - // ok - st.pop(); - while(st.hasTokens()) - { - if(st.front() == TT::Identifier) - { - std::string ty = st.eat().str(); - if(st.eat() != TT::Colon) - expected(st, "':' to specify type mapping in parametric type instantiation", st.prev().str()); - - if(gmaps.find(ty) != gmaps.end()) - error(st, "duplicate mapping for parameter '%s' in type arguments to parametric type '%s'", ty, s); - - gmaps[ty] = parseType(st); - - if(st.front() == TT::Comma) - { - st.pop(); - continue; - } - else if(st.front() == TT::RAngle) - { - break; - } - else - { - expected(st, "either ',' or '>' to continue or terminate parametric type instantiation", st.front().str()); - } - } - else if(st.front() == TT::RAngle) - { - error(st, "need at least one type mapping in parametric type instantiation"); - } - else - { - // error(st, "Unexpected token '%s' in type mapping", st.front().str()); - break; - } - } - - if(st.front() != TT::RAngle) - expected(st, "'>' to end type mapping", st.front().str()); - - st.pop(); + //? note: if *failed is nullptr, then we will throw errors where we usually would return. + pams = parsePAMs(st, /* fail: */ nullptr); } - - - return pts::NamedType::create(s, gmaps); + // // ok + // st.pop(); + // while(st.hasTokens()) + // { + // if(st.front() == TT::Identifier) + // { + // std::string ty = st.eat().str(); + // if(st.eat() != TT::Colon) + // expected(st, "':' to specify type mapping in parametric type instantiation", st.prev().str()); + + // if(seen.find(ty) != seen.end()) + // error(st, "duplicate mapping for parameter '%s' in type arguments to parametric type '%s'", ty, s); + + // pams.add(ty, parseType(st)); + + // if(st.front() == TT::Comma) + // { + // st.pop(); + // continue; + // } + // else if(st.front() == TT::RAngle) + // { + // break; + // } + // else + // { + // expected(st, "either ',' or '>' to continue or terminate parametric type instantiation", st.front().str()); + // } + // } + // else if(st.front() == TT::RAngle) + // { + // error(st, "need at least one type mapping in parametric type instantiation"); + // } + // else + // { + // // error(st, "Unexpected token '%s' in type mapping", st.front().str()); + // break; + // } + // } + + // if(st.front() != TT::RAngle) + // expected(st, "'>' to end type mapping", st.front().str()); + + // st.pop(); + // } + + + return pts::NamedType::create(s, pams); } else if(auto isfn = (st.front() == TT::Func); st.front() == TT::LParen || st.front() == TT::Func) { @@ -601,4 +608,87 @@ namespace parser error(st, "unexpected token '%s' while parsing type", st.front().str()); } } + + + + PolyArgMapping_t parsePAMs(State& st, bool* failed) + { + iceAssert(st.front() == TT::Exclamation && st.lookahead(1) == TT::LAngle); + + st.pop(); + st.pop(); + + std::unordered_set seen; + PolyArgMapping_t mappings; + { + //* foo!<> is an error regardless of whether we're doing expression parsing or call parsing. + if(st.front() == TT::RAngle) + error(st.loc(), "at least one type argument is required between angle brackets <>"); + + // step 2A: start parsing. + size_t idx = 0; + while(st.front() != TT::RAngle) + { + if(st.front() != TT::Identifier) + { + if(failed) + { + *failed = true; + return mappings; + } + else + { + expected(st.loc(), "identifier in type argument list", st.front().str()); + } + } + + if(st.front() == TT::Identifier && st.lookahead(1) == TT::Colon) + { + auto id = st.pop().str(); + st.pop(); + + //? I think beyond this point we pretty much can't fail since we have the colon. + //? so, we shouldn't need to handle the case where we fail to parse a type here. + if(seen.find(id) != seen.end()) + error(st.loc(), "duplicate type argument '%s'", id); + + auto ty = parseType(st); + mappings.add(id, ty); + seen.insert(id); + } + else + { + if(!seen.empty()) + error(st.loc(), "cannot have positional type arguments after named arguments"); + + auto ty = parseType(st); + mappings.add(idx++, ty); + } + + if(st.front() == TT::Comma) + st.pop(); + + else if(st.front() != TT::RAngle) + expected(st.loc(), "',' or '>' in type argument list", st.front().str()); + } + + iceAssert(st.front() == TT::RAngle); + st.pop(); + + return mappings; + } + } } + + + + + + + + + + + + + diff --git a/source/frontend/pts.cpp b/source/frontend/pts.cpp index 326bc104..ca86c6ca 100644 --- a/source/frontend/pts.cpp +++ b/source/frontend/pts.cpp @@ -128,22 +128,22 @@ namespace pts return (it = new InferredType()); } - static std::map>, NamedType*> map; + // static std::map, NamedType*> map; NamedType* NamedType::create(const std::string& s) { return NamedType::create(s, { }); } - NamedType* NamedType::create(const std::string& s, const std::map& tm) + NamedType* NamedType::create(const std::string& s, const PolyArgMapping_t& tm) { - if(map.find({ s, tm }) != map.end()) - return map[{ s, tm }]; + // if(map.find({ s, tm }) != map.end()) + // return map[{ s, tm }]; auto ret = new NamedType(s); - for(const auto& p : tm) - ret->genericMapping[p.first] = p.second; + ret->genericMapping = tm; - return (map[{ s, tm }] = ret); + // return (map[{ s, tm }] = ret); + return ret; } @@ -156,10 +156,10 @@ namespace pts if(!this->genericMapping.empty()) { std::string m; - for(auto p : this->genericMapping) - m += p.first + ": " + p.second->str() + ", "; + for(auto p : this->genericMapping.maps) + m += strprintf("%s: %s, ", p.name.empty() ? std::to_string(p.index) : p.name, p.type->str()); - if(this->genericMapping.size() > 0) + if(this->genericMapping.maps.size() > 0) m.pop_back(), m.pop_back(); ret += "<" + m + ">"; diff --git a/source/include/ast.h b/source/include/ast.h index 4b0158bd..12ee03ad 100644 --- a/source/include/ast.h +++ b/source/include/ast.h @@ -83,7 +83,7 @@ namespace ast std::string name; - std::unordered_map generics; + std::vector> generics; std::vector>> genericVersions; // kind of a hack. @@ -479,7 +479,9 @@ namespace ast // for these cases: Foo(...) and Foo.staticAccess // where Foo is, respectively, a generic function and a generic type. - std::unordered_map mappings; + // std::unordered_map mappings; + + PolyArgMapping_t mappings; }; @@ -649,7 +651,7 @@ namespace ast std::string name; std::vector> args; - std::unordered_map mappings; + PolyArgMapping_t mappings; bool traverseUpwards = true; }; diff --git a/source/include/defs.h b/source/include/defs.h index 14e86b6c..f8f0013c 100644 --- a/source/include/defs.h +++ b/source/include/defs.h @@ -43,6 +43,7 @@ struct Identifier; enum class VisibilityLevel; namespace fir { struct Type; } +namespace pts { struct Type; } namespace tinyformat { void formatValue(std::ostream& out, const char* /*fmtBegin*/, const char* fmtEnd, int ntrunc, fir::Type* ty); @@ -555,6 +556,25 @@ struct TypeConstraints_t using TypeParamMap_t = std::unordered_map; +struct PolyArgMapping_t +{ + struct SingleArg + { + size_t index; + std::string name; + + pts::Type* type; + }; + + std::vector maps; + + bool empty() const { return maps.empty(); } + void add(const std::string& name, pts::Type* t); + void add(size_t idx, pts::Type* t); + + static inline PolyArgMapping_t none() { return PolyArgMapping_t(); } +}; + namespace util { diff --git a/source/include/parser_internal.h b/source/include/parser_internal.h index 3a44c070..355bd02d 100644 --- a/source/include/parser_internal.h +++ b/source/include/parser_internal.h @@ -292,7 +292,12 @@ namespace parser ast::Stmt* parseBreak(State& st); ast::Stmt* parseContinue(State& st); - std::unordered_map parseGenericTypeList(State& st); + std::vector> parseGenericTypeList(State& st); + + PolyArgMapping_t parsePAMs(State& st, bool* failed); + + std::tuple, std::vector>, + pts::Type*, bool, Location> parseFunctionLookingDecl(State& st); } diff --git a/source/include/polymorph.h b/source/include/polymorph.h index 95b0775a..9a07b222 100644 --- a/source/include/polymorph.h +++ b/source/include/polymorph.h @@ -30,7 +30,7 @@ namespace sst namespace poly { - using ProblemSpace_t = std::unordered_map; + using ProblemSpace_t = std::vector>; struct Solution_t { @@ -69,7 +69,7 @@ namespace sst std::vector> findPolymorphReferences(TypecheckState* fs, const std::string& name, - const std::vector& gdefs, const TypeParamMap_t& _gmaps, fir::Type* return_infer, + const std::vector& gdefs, const PolyArgMapping_t& _gmaps, fir::Type* return_infer, fir::Type* type_infer, bool isFnCall, std::vector* args); std::pair attemptToInstantiatePolymorph(TypecheckState* fs, ast::Parameterisable* thing, const std::string& name, diff --git a/source/include/pts.h b/source/include/pts.h index 83ae7586..84597a48 100644 --- a/source/include/pts.h +++ b/source/include/pts.h @@ -79,10 +79,10 @@ namespace pts virtual std::string str() override; std::string name; - std::unordered_map genericMapping; + PolyArgMapping_t genericMapping; static NamedType* create(const std::string& s); - static NamedType* create(const std::string& s, const std::map& genericMapping); + static NamedType* create(const std::string& s, const PolyArgMapping_t& genericMapping); private: explicit NamedType(const std::string& n) : name(n) { } diff --git a/source/include/resolver.h b/source/include/resolver.h index e4895b21..8070afa4 100644 --- a/source/include/resolver.h +++ b/source/include/resolver.h @@ -29,12 +29,12 @@ namespace sst TCResult resolveFunctionCall(TypecheckState* fs, const std::string& name, std::vector* arguments, - const TypeParamMap_t& gmaps, bool traverseUp, fir::Type* inferredRetType); + const PolyArgMapping_t& gmaps, bool traverseUp, fir::Type* inferredRetType); TCResult resolveFunctionCallFromCandidates(TypecheckState* fs, const std::vector& cs, std::vector* arguments, - const TypeParamMap_t& gmaps, bool allowImplicitSelf); + const PolyArgMapping_t& gmaps, bool allowImplicitSelf); - TCResult resolveConstructorCall(TypecheckState* fs, TypeDefn* defn, const std::vector& arguments, const TypeParamMap_t& gmaps); + TCResult resolveConstructorCall(TypecheckState* fs, TypeDefn* defn, const std::vector& arguments, const PolyArgMapping_t& gmaps); std::pair, ErrorMsg*> verifyStructConstructorArguments(const Location& callLoc, @@ -52,6 +52,8 @@ namespace sst std::vector canonicaliseCallArguments(const Location& target, const std::vector& params, const std::vector& args, ErrorMsg** err); + std::pair canonicalisePolyArguments(TypecheckState* fs, ast::Parameterisable* thing, const PolyArgMapping_t& pams); + std::vector typecheckCallArguments(TypecheckState* fs, const std::vector>& args); template @@ -71,7 +73,7 @@ namespace sst namespace internal { std::pair> resolveFunctionCallFromCandidates(TypecheckState* fs, const Location& callLoc, - const std::vector>>& cands, const TypeParamMap_t& gmaps, bool allowImplicitSelf, + const std::vector>>& cands, const PolyArgMapping_t& gmaps, bool allowImplicitSelf, fir::Type* return_infer); } } diff --git a/source/include/typecheck.h b/source/include/typecheck.h index 82e7911c..95ae0143 100644 --- a/source/include/typecheck.h +++ b/source/include/typecheck.h @@ -182,7 +182,6 @@ namespace sst // things that i might want to make non-methods someday fir::Type* convertParserTypeToFIR(pts::Type* pt, bool allowFailure = false); fir::Type* inferCorrectTypeForLiteral(fir::ConstantNumberType* lit); - TypeParamMap_t convertParserTypeArgsToFIR(const std::unordered_map& gmaps, bool allowFailure = false); fir::Type* checkIsBuiltinConstructorCall(const std::string& name, const std::vector& arguments); diff --git a/source/misc/identifier.cpp b/source/misc/identifier.cpp index 4bb3846b..1c80daea 100644 --- a/source/misc/identifier.cpp +++ b/source/misc/identifier.cpp @@ -49,6 +49,32 @@ sst::Defn* TCResult::defn() const } + + +void PolyArgMapping_t::add(const std::string& name, pts::Type* t) +{ + SingleArg arg; + arg.name = name; + arg.type = t; + arg.index = -1; + + this->maps.push_back(arg); +} + +void PolyArgMapping_t::add(size_t idx, pts::Type* t) +{ + SingleArg arg; + arg.name = ""; + arg.type = t; + arg.index = idx; + + this->maps.push_back(arg); +} + + + + + bool Identifier::operator == (const Identifier& other) const { return (other.name == this->name) && (other.str() == this->str()); diff --git a/source/typecheck/alloc.cpp b/source/typecheck/alloc.cpp index 8a81ebdb..c02c1513 100644 --- a/source/typecheck/alloc.cpp +++ b/source/typecheck/alloc.cpp @@ -49,7 +49,7 @@ TCResult ast::AllocOp::typecheck(sst::TypecheckState* fs, fir::Type* infer) iceAssert(cdf); auto arguments = sst::resolver::misc::typecheckCallArguments(fs, this->args); - auto constructor = sst::resolver::resolveConstructorCall(fs, cdf, arguments, { }); + auto constructor = sst::resolver::resolveConstructorCall(fs, cdf, arguments, PolyArgMapping_t::none()); ret->constructor = constructor.defn(); ret->arguments = arguments; diff --git a/source/typecheck/call.cpp b/source/typecheck/call.cpp index eb4267f0..85a3266a 100644 --- a/source/typecheck/call.cpp +++ b/source/typecheck/call.cpp @@ -30,8 +30,7 @@ sst::Expr* ast::FunctionCall::typecheckWithArguments(sst::TypecheckState* fs, co // resolve the function call here std::vector ts = _arguments; - auto gmaps = fs->convertParserTypeArgsToFIR(this->mappings); - auto res = sst::resolver::resolveFunctionCall(fs, this->name, &ts, gmaps, this->traverseUpwards, infer); + auto res = sst::resolver::resolveFunctionCall(fs, this->name, &ts, this->mappings, this->traverseUpwards, infer); auto target = res.defn(); iceAssert(target); diff --git a/source/typecheck/classes.cpp b/source/typecheck/classes.cpp index c52a19b4..527cf572 100644 --- a/source/typecheck/classes.cpp +++ b/source/typecheck/classes.cpp @@ -370,7 +370,7 @@ TCResult ast::InitFunctionDefn::typecheck(sst::TypecheckState* fs, fir::Type* in auto baseargs = sst::resolver::misc::typecheckCallArguments(fs, this->superArgs); - auto constr = sst::resolver::resolveConstructorCall(fs, call->classty, baseargs, { }); + auto constr = sst::resolver::resolveConstructorCall(fs, call->classty, baseargs, PolyArgMapping_t::none()); call->arguments = baseargs; call->target = dcast(sst::FunctionDefn, constr.defn()); diff --git a/source/typecheck/dotop.cpp b/source/typecheck/dotop.cpp index ded153cb..f01f1839 100644 --- a/source/typecheck/dotop.cpp +++ b/source/typecheck/dotop.cpp @@ -304,7 +304,7 @@ static sst::Expr* doExpressionDotOp(sst::TypecheckState* fs, ast::DotOperator* d //* 1. we right now cannot overload based on the mutating aspect of the method //* 2. mutable pointers can implicitly convert to immutable ones, but not vice versa. - return sst::resolver::resolveFunctionCallFromCandidates(fs, cands, ts, fs->convertParserTypeArgsToFIR(fc->mappings), false).defn(); + return sst::resolver::resolveFunctionCallFromCandidates(fs, cands, ts, fc->mappings, false).defn(); }; std::vector mcands; diff --git a/source/typecheck/polymorph/driver.cpp b/source/typecheck/polymorph/driver.cpp index 4f9e7264..81212437 100644 --- a/source/typecheck/polymorph/driver.cpp +++ b/source/typecheck/polymorph/driver.cpp @@ -20,7 +20,7 @@ namespace sst { namespace poly { std::vector> findPolymorphReferences(TypecheckState* fs, const std::string& name, - const std::vector& gdefs, const TypeParamMap_t& _gmaps, fir::Type* return_infer, fir::Type* type_infer, + const std::vector& gdefs, const PolyArgMapping_t& pams, fir::Type* return_infer, fir::Type* type_infer, bool isFnCall, std::vector* args) { iceAssert(gdefs.size() > 0); @@ -33,8 +33,16 @@ namespace poly for(const auto& gdef : gdefs) { - pots.push_back(attemptToInstantiatePolymorph(fs, gdef, name, _gmaps, return_infer, type_infer, isFnCall, args, - /* fillplaceholders: */ true)); + auto [ gmaps, err ] = resolver::misc::canonicalisePolyArguments(fs, gdef, pams); + if(err != nullptr) + { + pots.push_back({ TCResult(err), Solution_t() }); + } + else + { + pots.push_back(attemptToInstantiatePolymorph(fs, gdef, name, gmaps, return_infer, type_infer, isFnCall, args, + /* fillplaceholders: */ true)); + } } return pots; @@ -197,7 +205,7 @@ namespace poly static std::pair inferPolymorphicFunction(TypecheckState* fs, ast::Parameterisable* thing, const std::string& name, - const std::unordered_map& problems, const std::vector& input, + const ProblemSpace_t& problems, const std::vector& input, const TypeParamMap_t& partial, fir::Type* return_infer, fir::Type* type_infer, bool isFnCall, fir::Type* problem_infer, std::unordered_map* origParamOrder) { @@ -286,7 +294,7 @@ namespace poly std::pair inferTypesForPolymorph(TypecheckState* fs, ast::Parameterisable* thing, const std::string& name, - const std::unordered_map& problems, const std::vector& input, + const ProblemSpace_t& problems, const std::vector& input, const TypeParamMap_t& partial, fir::Type* return_infer, fir::Type* type_infer, bool isFnCall, fir::Type* problem_infer, std::unordered_map* origParamOrder) { diff --git a/source/typecheck/polymorph/instantiator.cpp b/source/typecheck/polymorph/instantiator.cpp index b41e183f..62f76d00 100644 --- a/source/typecheck/polymorph/instantiator.cpp +++ b/source/typecheck/polymorph/instantiator.cpp @@ -26,7 +26,7 @@ namespace poly return SimpleError::make(l, "type %s %s could not be inferred", util::plural("parameter", strs.size()), mstr); } - std::vector getMissingSolutions(const std::unordered_map& needed, const TypeParamMap_t& solution, + std::vector getMissingSolutions(const ProblemSpace_t& needed, const TypeParamMap_t& solution, bool allowPlaceholders) { std::vector missing; @@ -175,13 +175,15 @@ namespace poly t = t->getPointerElementType(), ptrs++; } - if(thing->generics.find(map.first) != thing->generics.end() && ptrs < thing->generics[map.first].pointerDegree) + if(auto it = std::find_if(thing->generics.begin(), thing->generics.end(), [&map](const auto& p) -> bool { + return map.first == p.first; + }); it != thing->generics.end() && ptrs < it->second.pointerDegree) { return TCResult( SimpleError::make(fs->loc(), "cannot map type '%s' to type parameter '%s' in instantiation of generic type '%s'", map.second, map.first, thing->name)->append( SimpleError::make(MsgType::Note, thing->loc, - "replacement type has pointer degree %d, which is less than the required %d", ptrs, thing->generics[map.first].pointerDegree) + "replacement type has pointer degree %d, which is less than the required %d", ptrs, it->second.pointerDegree) ) ); } diff --git a/source/typecheck/polymorph/misc.cpp b/source/typecheck/polymorph/misc.cpp index 598c2a27..37bf5118 100644 --- a/source/typecheck/polymorph/misc.cpp +++ b/source/typecheck/polymorph/misc.cpp @@ -55,8 +55,12 @@ namespace sst if(ty->isNamedType()) { fty = fs->convertParserTypeToFIR(ty, true); - if(!fty && (problems.find(ty->toNamedType()->name) != problems.end()) /* || fs->findGenericMapping(ty->toNamedType()->name, true) != 0 */) + if(!fty && (std::find_if(problems.begin(), problems.end(), [&ty](const auto& a) -> bool { + return ty->toNamedType()->name == a.first; + }) != problems.end())) + { fty = fir::PolyPlaceholderType::get(ty->toNamedType()->name, polysession); + } if(!fty) error("failed to find type '%s'", input->str()); } @@ -152,16 +156,6 @@ namespace sst if(allowFail) return 0; else error(this->loc(), "no mapping for type parameter '%s'", name); } - - - TypeParamMap_t TypecheckState::convertParserTypeArgsToFIR(const std::unordered_map& gmaps, bool allowFailure) - { - TypeParamMap_t ret; - for(const auto& [ name, type ] : gmaps) - ret[name] = this->convertParserTypeToFIR(type, allowFailure); - - return ret; - } } diff --git a/source/typecheck/resolver/driver.cpp b/source/typecheck/resolver/driver.cpp index 955d2960..77e4f0b6 100644 --- a/source/typecheck/resolver/driver.cpp +++ b/source/typecheck/resolver/driver.cpp @@ -18,7 +18,7 @@ namespace sst { namespace resolver { TCResult resolveFunctionCallFromCandidates(TypecheckState* fs, const std::vector& cands, std::vector* args, - const TypeParamMap_t& gmaps, bool allowImplicitSelf) + const PolyArgMapping_t& gmaps, bool allowImplicitSelf) { auto cds = util::map(cands, [&args](auto c) -> std::pair> { return { c, *args }; }); auto [ ret, new_args ] = resolver::internal::resolveFunctionCallFromCandidates(fs, fs->loc(), cds, gmaps, allowImplicitSelf, nullptr); @@ -27,7 +27,7 @@ namespace resolver return ret; } - TCResult resolveFunctionCall(TypecheckState* fs, const std::string& name, std::vector* arguments, const TypeParamMap_t& gmaps, + TCResult resolveFunctionCall(TypecheckState* fs, const std::string& name, std::vector* arguments, const PolyArgMapping_t& gmaps, bool travUp, fir::Type* return_infer) { StateTree* tree = fs->stree; @@ -111,22 +111,15 @@ namespace resolver { auto ts = args; // copy it. - if(auto fn = dcast(FunctionDecl, def)) + if(dcast(FunctionDecl, def) || dcast(TypeDefn, def)) { - cands.push_back({ fn, ts }); + cands.push_back({ def, ts }); } else if(dcast(VarDefn, def) && def->type->isFunctionType() /* && !didVar */) { cands.push_back({ def, ts }); didVar = true; } - else if(auto typedf = dcast(TypeDefn, def)) - { - // auto res = resolveConstructorCall(fs, typedf, ts, gmaps); - // if(!res.isError()) - - cands.push_back({ def, ts }); - } else { return TCResult( @@ -152,7 +145,7 @@ namespace resolver - TCResult resolveConstructorCall(TypecheckState* fs, TypeDefn* typedf, const std::vector& arguments, const TypeParamMap_t& gmaps) + TCResult resolveConstructorCall(TypecheckState* fs, TypeDefn* typedf, const std::vector& arguments, const PolyArgMapping_t& pams) { //! ACHTUNG: DO NOT REARRANGE ! //* NOTE: ClassDefn inherits from StructDefn * @@ -176,7 +169,7 @@ namespace resolver auto cand = resolveFunctionCallFromCandidates(fs, util::map(cls->initialisers, [](auto e) -> auto { return dcast(sst::Defn, e); - }), ©, gmaps, true); + }), ©, pams, true); // TODO: support re-eval of constructor args! // TODO: support re-eval of constructor args! diff --git a/source/typecheck/resolver/misc.cpp b/source/typecheck/resolver/misc.cpp index 7bb059cb..5c1df6c8 100644 --- a/source/typecheck/resolver/misc.cpp +++ b/source/typecheck/resolver/misc.cpp @@ -71,6 +71,51 @@ namespace sst + std::pair canonicalisePolyArguments(TypecheckState* fs, ast::Parameterisable* thing, const PolyArgMapping_t& pams) + { + if(thing->generics.empty()) + return { { }, SimpleError::make(fs->loc(), "cannot canonicalise poly arguments for non-generic (or nested) entity '%s'", thing->name) }; + + TypeParamMap_t ret; + + //? we only check if we provided more than necessary, because (1) we might have optional args in the future, and (2) we can omit args + //? to infer stuff. + if(thing->generics.size() < pams.maps.size()) + { + return { { }, SimpleError::make(fs->loc(), "mismatched number of type arguments to polymorph '%s'; expected %d, found %d instead", + thing->name, thing->generics.size(), pams.maps.size()) }; + } + + for(const auto& pam : pams.maps) + { + if(!pam.name.empty()) + { + // check if it exists. + auto it = std::find_if(thing->generics.begin(), thing->generics.end(), + [&pam](const std::pair& a) -> bool { + return a.first == pam.name; + }); + + if(it == thing->generics.end()) + return { { }, SimpleError::make(fs->loc(), "no type parameter named '%s' in polymorph '%s'", pam.name, thing->name) }; + + // ok, it works. + ret[pam.name] = fs->convertParserTypeToFIR(pam.type, /* allowFailure: */ false); + } + else + { + // ok, the index ought to exist. + iceAssert(pam.index < thing->generics.size()); + + // should be simple. + ret[thing->generics[pam.index].first] = fs->convertParserTypeToFIR(pam.type, /* allowFailure: */ false); + } + } + + return { ret, nullptr }; + } + + std::vector typecheckCallArguments(TypecheckState* fs, const std::vector>& args) { return util::map(args, [fs](const auto& a) -> FnCallArgument { diff --git a/source/typecheck/resolver/resolver.cpp b/source/typecheck/resolver/resolver.cpp index 6ee344b6..a3b931eb 100644 --- a/source/typecheck/resolver/resolver.cpp +++ b/source/typecheck/resolver/resolver.cpp @@ -54,7 +54,7 @@ namespace resolver namespace internal { std::pair> resolveFunctionCallFromCandidates(TypecheckState* fs, const Location& callLoc, - const std::vector>>& _cands, const TypeParamMap_t& gmaps, bool allowImplicitSelf, + const std::vector>>& _cands, const PolyArgMapping_t& pams, bool allowImplicitSelf, fir::Type* return_infer) { if(_cands.empty()) @@ -90,20 +90,29 @@ namespace resolver iceAssert(fd); iceAssert(fd->original); - // do an inference -- with the arguments that we have. - auto [ res, soln ] = poly::attemptToInstantiatePolymorph(fs, fd->original, fn->id.name, gmaps, /* return_infer: */ return_infer, - /* type_infer: */ nullptr, /* isFnCall: */ true, &replacementArgs, /* fillPlacholders: */ false, - /* problem_infer: */ fn->type); - - if(!res.isDefn()) + auto [ gmaps, err ] = resolver::misc::canonicalisePolyArguments(fs, fd->original, pams); + if(err != nullptr) { - fails[fn] = res.error(); + fails[fn] = err; dist = -1; } else { - curcandidate = res.defn(); - std::tie(dist, fails[fn]) = std::make_tuple(soln.distance, nullptr); + // do an inference -- with the arguments that we have. + auto [ res, soln ] = poly::attemptToInstantiatePolymorph(fs, fd->original, fn->id.name, gmaps, /* return_infer: */ return_infer, + /* type_infer: */ nullptr, /* isFnCall: */ true, &replacementArgs, /* fillPlacholders: */ false, + /* problem_infer: */ fn->type); + + if(!res.isDefn()) + { + fails[fn] = res.error(); + dist = -1; + } + else + { + curcandidate = res.defn(); + std::tie(dist, fails[fn]) = std::make_tuple(soln.distance, nullptr); + } } } } @@ -137,7 +146,7 @@ namespace resolver } else if(auto td = dcast(TypeDefn, curcandidate)) { - auto res = resolveConstructorCall(fs, td, replacementArgs, gmaps); + auto res = resolveConstructorCall(fs, td, replacementArgs, pams); if(!res.isDefn()) { fails[fn] = res.error(); diff --git a/source/typecheck/type.cpp b/source/typecheck/type.cpp index 799150a4..792f3def 100644 --- a/source/typecheck/type.cpp +++ b/source/typecheck/type.cpp @@ -7,6 +7,8 @@ #include "errors.h" #include "ir/type.h" #include "typecheck.h" + +#include "resolver.h" #include "polymorph.h" namespace sst @@ -112,7 +114,13 @@ namespace sst error(this->loc(), "parametric type '%s' cannot be referenced without type arguments", pt->toNamedType()->name); // right, now we instantiate it. - TypeParamMap_t mapping = this->convertParserTypeArgsToFIR(pt->toNamedType()->genericMapping, allowFail); + auto [ mapping, err ] = sst::resolver::misc::canonicalisePolyArguments(this, gdef,pt->toNamedType()->genericMapping); + + if(err != nullptr) + { + err->prepend(BareError::make("mismatched type arguments to instantiation of polymorphic type '%s':", gdef->name) + )->postAndQuit(); + } // types generally cannot be overloaded, so it doesn't make sense for it to be SFINAE-ed. // unwrapping it will post the error if any. diff --git a/source/typecheck/variable.cpp b/source/typecheck/variable.cpp index a0339867..c80e89c3 100644 --- a/source/typecheck/variable.cpp +++ b/source/typecheck/variable.cpp @@ -205,9 +205,8 @@ TCResult ast::Ident::typecheck(sst::TypecheckState* fs, fir::Type* infer) else if(auto gdefs = tree->getUnresolvedGenericDefnsWithName(this->name); gdefs.size() > 0) { std::vector fake; - auto gmaps = fs->convertParserTypeArgsToFIR(this->mappings); - auto pots = sst::poly::findPolymorphReferences(fs, this->name, gdefs, gmaps, /* return_infer: */ 0, /* type_infer: */ infer, - false, &fake); + auto pots = sst::poly::findPolymorphReferences(fs, this->name, gdefs, this->mappings, /* return_infer: */ 0, /* type_infer: */ infer, + /* isFnCall: */ false, &fake); if(pots.size() > 1) { From fb1eac5529a7ca3734ca35c0829e1996b1591edf Mon Sep 17 00:00:00 2001 From: zhiayang Date: Mon, 5 Nov 2018 17:43:57 +0800 Subject: [PATCH 9/9] fix the tests to use `!<...>` --- build/tests/linkedlist.flx | 2 +- build/tests/unions.flx | 2 +- build/tests/using.flx | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/tests/linkedlist.flx b/build/tests/linkedlist.flx index a27f84b1..23070626 100644 --- a/build/tests/linkedlist.flx +++ b/build/tests/linkedlist.flx @@ -50,7 +50,7 @@ public fn doLinkedListTest() { do { var list = LinkedList(data: 41) - LinkedList::hello() + LinkedList!::hello() var list2 = LinkedList(data: "foo") list2.insert("hello") diff --git a/build/tests/unions.flx b/build/tests/unions.flx index a4895e4c..cf1a29c1 100644 --- a/build/tests/unions.flx +++ b/build/tests/unions.flx @@ -18,7 +18,7 @@ public fn doUnionsTest() let x = option::some("foobar") let y = option::some(456) - printf("x = %s, y = %d\n", x as option::some, y as option::some) + printf("x = %s, y = %d\n", x as option!::some, y as option!::some) } do { diff --git a/build/tests/using.flx b/build/tests/using.flx index a4fde4f3..8a51d20d 100644 --- a/build/tests/using.flx +++ b/build/tests/using.flx @@ -34,7 +34,7 @@ public fn doUsingTest() } } - using xxx::Foo as _ + using xxx!::Foo as _ printf("a = %d, b = %d, c = %d\n", Alpha.value, Bravo.value, Charlie.value) } @@ -60,8 +60,8 @@ public fn doUsingTest() none } - using Option as _ - using Option as _ + using Option! as _ + using Option! as _ let x = some(30) let y = some("bye")