diff --git a/include/daScript/ast/compilation_errors.h b/include/daScript/ast/compilation_errors.h index f1da01ed65..3bcb899697 100644 --- a/include/daScript/ast/compilation_errors.h +++ b/include/daScript/ast/compilation_errors.h @@ -353,7 +353,7 @@ namespace das , exceeds_new_argument = 30509 // 1 site(s) , exceeds_structure = 30510 // 1 site(s) , exceeds_tuple_index = 30511 // 1 site(s) - , exceeds_type = 30512 // 2 site(s) + , exceeds_type = 30512 // 3 site(s) , exceeds_type_alias = 30513 // 1 site(s) , exceeds_typeinfo_sizeof = 30514 // 1 site(s) , exceeds_constant_range = 30515 // 1 site(s) diff --git a/include/daScript/simulate/REVIEW.md b/include/daScript/simulate/REVIEW.md index fb59b65ea4..6c056c670c 100644 --- a/include/daScript/simulate/REVIEW.md +++ b/include/daScript/simulate/REVIEW.md @@ -24,6 +24,13 @@ C++ half never opens the daslib checklist on its own. - **A diff that changes `KeyHash` (`runtime_table.h`) or `WrapsBuiltinValue` (`cast.h`) states in its own PR description which key types change hash value.** +- **A `cvt_*` inline in `aot.h` takes and returns `vec4f`, and none of them is overloaded.** + `vec4f` is the SIMD register; the `vec2`/`vec3`/`vec4` types are structs of scalars, so a + concrete return spills the lanes through `v_extract_*` and the next conversion reloads them. + Overloading is what forced that once: every flavor converts from `vec4f`, so an overload set + fed a `vec4f` result is ambiguous - the emitter writes `cvt_pass(cvt_uint3(..))` for + `x |> uint3 |> int3`. One name per conversion, `vec4f` throughout, keeps both. + - **A diff that makes the hot path cost more per evaluated expression in the build the repo ships is a defect.** The hot path is a `SimNode::eval*` method, any helper such a method calls on every evaluation, the dispatchers `Context::callOrFastcall` / diff --git a/include/daScript/simulate/aot.h b/include/daScript/simulate/aot.h index 23b5497835..2c53cea7d3 100644 --- a/include/daScript/simulate/aot.h +++ b/include/daScript/simulate/aot.h @@ -4517,12 +4517,7 @@ namespace das { __forceinline vec4f cvt_uint3 ( float3 f ) { return v_cast_vec4f(v_cvt_vec4i(f)); } __forceinline vec4f cvt_uint4 ( float4 f ) { return v_cast_vec4f(v_cvt_vec4i(f)); } - __forceinline vec4f cvt_pass ( int2 i ) { return i; } - __forceinline vec4f cvt_pass ( int3 i ) { return i; } - __forceinline vec4f cvt_pass ( int4 i ) { return i; } - __forceinline vec4f cvt_pass ( uint2 i ) { return i; } - __forceinline vec4f cvt_pass ( uint3 i ) { return i; } - __forceinline vec4f cvt_pass ( uint4 i ) { return i; } + __forceinline vec4f cvt_pass ( vec4f i ) { return i; } __forceinline int32_t class_rtti_size ( void * ptr ) { auto pti = (TypeInfo **)ptr; diff --git a/src/ast/ast_infer_type_function.cpp b/src/ast/ast_infer_type_function.cpp index f7a786fb53..d16170b847 100644 --- a/src/ast/ast_infer_type_function.cpp +++ b/src/ast/ast_infer_type_function.cpp @@ -557,6 +557,15 @@ namespace das { } padded.push_back(newArg); } + for (int ai = p; ai != k; ++ai) { + const auto & argType = winner->arguments[ai]->type; + if (!argType->isAutoOrAlias()) continue; + AliasMap aliases; + OptionsMap options; + if (!isMatchingArgument(winner, argType, padded[ai - p]->type, useGenerics, true, &aliases, &options)) { + return false; + } + } for (int ai = k - 1; ai >= p; --ai) { expr->arguments.insert(expr->arguments.begin() + p, padded[ai - p]); types.insert(types.begin() + p, padded[ai - p]->type); @@ -1908,7 +1917,6 @@ namespace das { if (!anyFailed) break; if (totalAliases == aliases.size()) { - DAS_ASSERTF(0, "we should not be here. function matched arguments!"); break; } } diff --git a/src/ast/ast_lint.cpp b/src/ast/ast_lint.cpp index 6adf6364d3..b66e786856 100644 --- a/src/ast/ast_lint.cpp +++ b/src/ast/ast_lint.cpp @@ -1109,6 +1109,10 @@ namespace das { program->error("[init] is disabled in the options or CodeOfPolicies", "", "", fn->at, CompilationError::cant_function); } + if ( !fn->result->isAutoOrAlias() && fn->result->getSizeOf64()>0x7fffffff ) { + program->error("function result of '" + fn->name + "' is too big", "", "", + fn->at, CompilationError::exceeds_type); + } } virtual FunctionPtr visit ( Function * fn ) override { // Derived class ctor: every CFG path must call super(...) exactly once, diff --git a/src/ast/ast_typedecl.cpp b/src/ast/ast_typedecl.cpp index b66d83d4e1..dce905eb21 100644 --- a/src/ast/ast_typedecl.cpp +++ b/src/ast/ast_typedecl.cpp @@ -3476,6 +3476,8 @@ namespace das uint64_t TypeDecl::getCountOf64() const { uint64_t size = 1; for ( const TypeDecl * t = this; t && t->baseType==Type::tFixedArray; t = t->firstType ) { + // dimAuto (-1) and dimConst (-2) are valid unresolved dims, not counts; uint64_t wraps them + if ( t->fixedDim<=0 ) return 0; size *= uint64_t(t->fixedDim); } return size; diff --git a/tests/language/failed_generic_default_type_mismatch.das b/tests/language/failed_generic_default_type_mismatch.das new file mode 100644 index 0000000000..6121c0a327 --- /dev/null +++ b/tests/language/failed_generic_default_type_mismatch.das @@ -0,0 +1,13 @@ +// A piped call pads a default in for the parameter it shifts across, and lookup admits the +// candidate on that default's existence alone. When the parameter type is concrete the +// candidate is still instanced, so the argument-matching loop gives up without progress and +// the instanced function names the real mistake - which is why that exit falls through. +options gen2 +expect 30161, 30341 + +def generic_with_mismatched_default(r : string <- 1, v) => 0 + +[export] +def main { + generic_with_mismatched_default(){} +} diff --git a/tests/language/failed_generic_unknown_arg_type.das b/tests/language/failed_generic_unknown_arg_type.das new file mode 100644 index 0000000000..13ea43315a --- /dev/null +++ b/tests/language/failed_generic_unknown_arg_type.das @@ -0,0 +1,12 @@ +// A generic whose first argument is an unresolved type expression with a default value. The +// call matches at lookup; the alias map then stops growing with that argument still +// unmatched, and the instancing after the loop used to read types the loop never produced. +options gen2 +expect 30341 + +def generic_with_unknown_arg_type(r : l() <- 1, v) => 0 + +[export] +def call_generic_with_unknown_arg_type { + generic_with_unknown_arg_type(){} +} diff --git a/tests/language/failed_pointer_math_unsized_pointee.das b/tests/language/failed_pointer_math_unsized_pointee.das new file mode 100644 index 0000000000..6f2d650730 --- /dev/null +++ b/tests/language/failed_pointer_math_unsized_pointee.das @@ -0,0 +1,9 @@ +// Pointer arithmetic whose pointee carries a dimension the parser already rejected. The +// dimension is negative, so the element count wrapped and the size read as 2^64-48. +options gen2 +expect 30109, 30239 + +[export] +def pointer_math_on_unsized_pointee { + var x = new class() - 0 +} diff --git a/tests/language/failed_result_type_by_ref_too_big.das b/tests/language/failed_result_type_by_ref_too_big.das new file mode 100644 index 0000000000..7a5305ad3b --- /dev/null +++ b/tests/language/failed_result_type_by_ref_too_big.das @@ -0,0 +1,13 @@ +// The oversized result on its own, with no other error to stop compilation first: this one +// reaches Program::simulate, where the debug-info walk sizes the result and asserts. +options gen2 +expect 30512 + +[export] +def result_too_big_by_ref(p : int[2000000000]?) : int[2000000000]& { + unsafe { return *p } +} + +[export] +def main { +} diff --git a/tests/language/failed_result_type_too_big.das b/tests/language/failed_result_type_too_big.das new file mode 100644 index 0000000000..13630bda24 --- /dev/null +++ b/tests/language/failed_result_type_too_big.das @@ -0,0 +1,13 @@ +// An oversized function result. The size gate covered locals, arguments, structures and +// aliases but not the result, so the type reached the debug-info walk and asserted there. +options gen2 +expect 30512 + +// the reported shape - the make-value on the path is diagnosed as well. the crash itself is +// in failed_result_type_by_ref_too_big.das, where no earlier error stops the walk +[export] +def result_too_big => <- tuple(uninitialized) + +[export] +def main { +} diff --git a/tests/language/vec_constructors.das b/tests/language/vec_constructors.das index 6068c9e8ef..85ed0f09e8 100644 --- a/tests/language/vec_constructors.das +++ b/tests/language/vec_constructors.das @@ -6,6 +6,12 @@ require dastest/testing_boost public // and type conversions between vector types. // VEC_SEP is a built-in constant (default ",") used in vector printing. +// globals, so the constant folder leaves the conversion chains below to codegen +var g_f2 = float2(1, 2) +var g_f3 = float3(1, 2, 3) +var g_f4 = float4(1, 2, 3, 4) +var g_i3 = int3(1, 2, 3) + def test_vec2(tt : T?; a : auto(vec2) -const -&; isRange = false) { let f2_0 = vec2() let f2_x = vec2(1.0, 2.0) @@ -117,4 +123,19 @@ def test_vec_constructors(t : T?) { t |> equal(uint3(float3(1, 2, 3)), uint3(1u, 2u, 3u)) t |> equal(uint4(float4(1, 2, 3, 4)), uint4(1u, 2u, 3u, 4u)) } + t |> run("chained type conversions") @(t : T?) { + // AOT writes these as cvt_pass(cvt_uint3(..)), which resolves only while each cvt_ + // answers in its vector type - a vec4f argument matches every cvt_pass overload + t |> equal(g_f2 |> uint2 |> int2, int2(1, 2)) + t |> equal(g_f3 |> uint3 |> int3, int3(1, 2, 3)) + t |> equal(g_f4 |> uint4 |> int4, int4(1, 2, 3, 4)) + t |> equal(g_f2 |> int2 |> uint2, uint2(1u, 2u)) + t |> equal(g_f3 |> int3 |> uint3, uint3(1u, 2u, 3u)) + t |> equal(g_f4 |> int4 |> uint4, uint4(1u, 2u, 3u, 4u)) + t |> equal(g_i3 |> float3 |> int3, int3(1, 2, 3)) + t |> equal(g_i3 |> uint3 |> float3, float3(1, 2, 3)) + // three links: cvt_pass(cvt_pass(cvt_uint3(..))), so a cvt_pass feeds a cvt_pass + t |> equal(g_f3 |> uint3 |> int3 |> uint3, uint3(1u, 2u, 3u)) + t |> equal(g_f2 |> int2 |> uint2 |> int2, int2(1, 2)) + } }