From ae851883259c3edeae846b2deaacf592016974e3 Mon Sep 17 00:00:00 2001 From: Ge Wang Date: Wed, 30 Oct 2024 01:27:58 -0700 Subject: [PATCH] extend dependency check to sporked function calls --- VERSIONS | 135 +++++++++++++----- src/core/chuck_emit.cpp | 10 +- src/core/chuck_type.cpp | 3 +- ...-spork.ck => 172-depend-spork.ck.disabled} | 0 src/test/06-Errors/error-depend-spork.ck | 22 +++ src/test/06-Errors/error-depend-spork.txt | 10 ++ 6 files changed, 139 insertions(+), 41 deletions(-) rename src/test/01-Basic/{172-depend-spork.ck => 172-depend-spork.ck.disabled} (100%) create mode 100644 src/test/06-Errors/error-depend-spork.ck create mode 100644 src/test/06-Errors/error-depend-spork.txt diff --git a/VERSIONS b/VERSIONS index 05ce42705..f2d102343 100644 --- a/VERSIONS +++ b/VERSIONS @@ -11,51 +11,112 @@ ChucK VERSIONS log *************************************************** https://chuck.stanford.edu/doc/language/import.html *************************************************** -- the @import system imports classes and operator overloads from another .ck file, or from a - chugin; it is designed to faciliate creating, using, and distributing multi-file programs - and libraries + +- the @import system imports classes and operator overloads from another + .ck file, or from a chugin; it is designed to faciliate creating, + using, and distributing multi-file programs and libraries - @import SYNTAX: @import "Foo.ck" // import Foo.ck @import "Bar.chug" // import Bar.chug chugin @import { "AAA.ck", "BBB.chug", "CCC" } // multiple imports on one line @import "/Users/ge/chuck/examples/import/foo-user.ck" // absolute path @import "../dir/Blarg.ck" // relative path - @import "Foo" // can use implicit filename; @import will search and match for - .ck, .chug, and .chug.wasm, in each directory in the chugin/import search paths -- @import picks up all (and only) *public* definitions (classes and operator overloads); - -- this implies that @import will only add new definitions to the type system, but will - NEVER automatically run any code; client code must first instantiate class or use an operator - to execute the contents of imported files - -- this makes it possible to write unit-test code for public classes in the same file in - which it is defined; any code outside of public class/operator definition will NOT be - type-checked or run (this code will only scanned for syntax) -- @imports are resolved at compile-time, before the rest of the host file is evaluated; this - makes it possible for a a host .ck file to @import one or more files and make use of their - contents immediately (when it comes to dependency management, this compile-time import - mechanism is more powerful than the run-time Machine.add()). -- @import can work recursively; can @import files that @import other files -- @import has built-in cycle-detection; if an @import cycle is detected, a compiler error - will be issued and that compilation will be failed + @import "Foo" // can use implicit filename; @import will search and + match for .ck, .chug, and .chug.wasm, in each directory in the + chugin/import search paths +- @import picks up all (and only) *public* definitions (classes and + operator overloads); + -- this implies that @import will only add new definitions to the type + system, but will NEVER automatically run any code; client code must + first instantiate class or use an operator to execute the contents + of imported files + -- this makes it possible to write unit-test code for public classes + in the same file in which it is defined; any code outside of public + class/operator definition will NOT be type-checked or run (this + code will only scanned for syntax) +- @imports are resolved at compile-time, before the rest of the host file + is evaluated; this makes it possible for a a host .ck file to @import + one or more files and make use of their contents immediately (when it + comes to dependency management, this compile-time import mechanism is + more powerful than the run-time Machine.add()). +- @import can work recursively; can @import files that @import other + files +- @import has built-in cycle-detection; if an @import cycle is detected, + a compiler error will be issued and that compilation will be failed ****************************************************** -- (updated behavior) the one-public-class-per-file restriction has been lifted; i.e., it is - now possible to define more than one public class in a single .ck file; this update is - intended to give programmers and library creators more flexibility in organizing their - public class definiition -- (updated behavior) .ck files found in chugin/import search paths (e.g., ~/.chuck/lib) NO - LONGER automatically loads; instead these now must be explicitly @imported to be used (FYI - just the file name will suffice, assuming there are no filename conflicts elsewhere in the - import paths) +- (updated behavior) the one-public-class-per-file restriction has been + lifted; i.e., it is now possible to define more than one public class + in a single .ck file; this update is intended to give programmers and + library creators more flexibility in organizing their public class + definiition +- (updated behavior) .ck files found in chugin/import search paths (e.g., + ~/.chuck/lib) NO LONGER automatically loads; instead these now must + be explicitly @imported to be used (FYI just the file name will + suffice, assuming there are no filename conflicts elsewhere in the + import paths) ****************************************************** -- (fixed) specific internal UGen connection error now handled without exiting -- (fixed, windows) single letter filenames without extensions (e.g., "A") now - are distinguished from windows drive letters (e.g., "A:\") -- (fixed) Type.of() now reports more specific array type (e.g., `string[]`) instead of - generic `@array` -- (fixed) modulo `%` by zero (int or float) now throws an exception; previously this would - either produce an incorrect result or cause a crash -- (updated) .sort() a string array will now sort alphabetically (instead of by Object refs) -- (updated, internal) dynamic array types are now cached and reused, preventing potential - memory build-up across compilations +- (fixed) specific internal UGen connection error now handled without + exiting +- (fixed, windows) single letter filenames without extensions (e.g., "A") + now are distinguished from windows drive letters (e.g., "A:\") +- (fixed) Type.of() now reports more specific array type (e.g., + `string[]`) instead ofgeneric `@array` +- (fixed) modulo `%` by zero (int or float) now throws an exception; + previously this would either produce an incorrect result or cause a + crash +- (updated) .sort() a string array will now sort alphabetically (instead + of by Object refs) +- (updated, internal) dynamic array types are now cached and reused, + preventing potential memory build-up across compilations +- (updated) variable dependency check now treats sporked functions same + as function calls; unfulfilled dependencies will result in a compiler + error; previously, correctness in this regard was left to the + programmer but could cause a crash. this update is more stringent than + is always necessary (due to spork being parallel and the timing + behavior, the dependency sometimes are resolved in actuality. + however, ChucK team deems potentially over-aggressive dependency + checking for sporked functions is preferrable to unexpected and + hard-to-debug crashes. +============ +For example: +============ +``` +// spork a function (that depends on foo) +spork ~ printArray(); + +// initialize foo +5 => int foo; + +// function that needs foo +fun void printArray() +{ + // need 'foo' initialized here + if( foo == 5 ) <<< "success" >>>; +} + +// give the sporked shred a chance to run +me.yield(); +``` +=============== +compiler error: +=============== +error: sporking 'printArray()' at this point potentially skips +initialization of a needed variable: + + [9] spork ~ printArray(); + ^ +error: ...(note: this skipped variable initialization is needed by 'fun +void printArray()') + + [12] 5 => int foo; + ^ +error: ...(note: this is where the variable is used within 'fun void +printArray()' or its subsidiaries) + + [18] if( foo == 5 ) <<< "success" >>>; + ^ +...(hint: try calling 'printArray()' after the variable initialization) +============== 1.5.3.2 (October 2024) diff --git a/src/core/chuck_emit.cpp b/src/core/chuck_emit.cpp index d8f41fe07..f0cf916e1 100644 --- a/src/core/chuck_emit.cpp +++ b/src/core/chuck_emit.cpp @@ -4277,7 +4277,11 @@ t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit, // only check dependency violations if we are at a context-top-level // or class-top-level scope, i.e., not in a function definition // also, once sporked, it will be up the programmer to ensure intention - if( !emit->env->func && !spork ) + // ------- + // 1.5.4.0 (ge) sporked function calls now subject to same dependency verification + // this may be more stringent (due to potential timing behavior) but + // should help prevent confusing crashes; removing check for spork + if( !emit->env->func /* && !spork */ ) { // dependency tracking: check if we invoke func before all its deps are initialized | 1.5.0.8 (ge) added // NOTE if func originates from another file, this should behave correctly and return NULL | 1.5.1.1 (ge) fixed @@ -4287,8 +4291,8 @@ t_CKBOOL emit_engine_emit_exp_func_call( Chuck_Emitter * emit, if( unfulfilled ) { EM_error2( where, - "calling '%s()' at this point skips initialization of a needed variable:", - func->base_name.c_str() ); + "%s '%s()' at this point %sskips initialization of a needed variable:", + spork ? "sporking" : "calling", func->base_name.c_str(), spork ? "potentially " : "" ); EM_error2( unfulfilled->where, "...(note: this skipped variable initialization is needed by '%s')", func->signature().c_str() ); diff --git a/src/core/chuck_type.cpp b/src/core/chuck_type.cpp index b397436be..bc25ec9e3 100644 --- a/src/core/chuck_type.cpp +++ b/src/core/chuck_type.cpp @@ -4696,7 +4696,8 @@ t_CKTYPE type_engine_check_exp_func_call( Chuck_Env * env, a_Exp exp_func, a_Exp // if sporking, then don't track dependencies... // up to the programmer to ensure correctness across spork and time - if( !env->sporking ) + // 1.5.4.0 (ge) treat spork as immediate function + // if( !env->sporking ) { // if in a function definition if( env->func ) diff --git a/src/test/01-Basic/172-depend-spork.ck b/src/test/01-Basic/172-depend-spork.ck.disabled similarity index 100% rename from src/test/01-Basic/172-depend-spork.ck rename to src/test/01-Basic/172-depend-spork.ck.disabled diff --git a/src/test/06-Errors/error-depend-spork.ck b/src/test/06-Errors/error-depend-spork.ck new file mode 100644 index 000000000..b1f2895b0 --- /dev/null +++ b/src/test/06-Errors/error-depend-spork.ck @@ -0,0 +1,22 @@ +// error checking: as of 1.5.4.0 dependency tracking +// for context-top-level variable where sporked functions +// are now verified same as non-sporked function calls +// +// previously, if sporking, then no dependency check and it's up to +// programmer to ensure behavior is as intended + +// spork a function (that depends on foo) +spork ~ printArray(); + +// initialize foo +5 => int foo; + +// function that needs array +fun void printArray() +{ + // need 'foo' initialized here + if( foo == 5 ) <<< "success" >>>; +} + +// give the sporked shred a chance to run +me.yield(); diff --git a/src/test/06-Errors/error-depend-spork.txt b/src/test/06-Errors/error-depend-spork.txt new file mode 100644 index 000000000..45da39d41 --- /dev/null +++ b/src/test/06-Errors/error-depend-spork.txt @@ -0,0 +1,10 @@ +error-depend-spork.ck:9:9: error: sporking 'printArray()' at this point potentially skips initialization of a needed variable: +[9] spork ~ printArray(); + ^ +error-depend-spork.ck:12:10: error: ...(note: this skipped variable initialization is needed by 'fun void printArray()') +[12] 5 => int foo; + ^ +error-depend-spork.ck:18:9: error: ...(note: this is where the variable is used within 'fun void printArray()' or its subsidiaries) +[18] if( foo == 5 ) <<< "success" >>>; + ^ +error-depend-spork.ck: ...(hint: try calling 'printArray()' after the variable initialization)