fix(tests): quote the paths in runE2ETestPipeline's g++ command - #226
Open
MatthewReed303 wants to merge 7 commits into
Open
fix(tests): quote the paths in runE2ETestPipeline's g++ command#226MatthewReed303 wants to merge 7 commits into
MatthewReed303 wants to merge 7 commits into
Conversation
The argv array was joined into a shell string with nothing quoted, so a repo path containing a space word-split the -I flags and 115 tests failed. Quoted in the style the neighbouring call sites already use.
…h the runtime
An imported CODESYS library using ANY did not compile: generics existed
nowhere. They are now declarable on a VAR_INPUT of a FUNCTION, FUNCTION_BLOCK
or METHOD — CODESYS's seven names, since ANY_ELEMENTARY, ANY_MAGNITUDE and
ANY_DERIVED classify types without being declarable — and never as an array
element. iec_any.hpp carries the TYPE_CLASS enumeration and the IEC_ANY
descriptor; __SYSTEM.AnyType parses as a qualified type name, gated on that
namespace so a dot after any other type stays an error, and is declarable in
its own right so a block can keep what it was handed.
The analyzer enforces CODESYS's two rules: only a variable may be passed, a
literal or expression having no address; and its type must be one the declared
family accepts. That check earns its place — a concrete parameter is caught by
C++ refusing the assignment, but a REAL handed to an ANY_INT still produces
valid C++, a descriptor stamped TYPE_REAL the block was never written for.
Codegen builds the descriptor at every call shape: function block calls named
and positional, both AST shapes a method call arrives in, and FUNCTION calls.
typeclass comes from the declared type, because the payload cannot tell BYTE
from USINT; pvalue from raw_ptr(); diSize from IEC_SIZEOF. A STRING passed
this way queues a sync_length() flushed after the statement, since the callee
writes the characters but not the cached length. arr[i] and s.field reach a
generic pin too — they are VariableExpressions carrying subscripts, so they
passed the variable guard and then failed a lookup keyed on the variable's own
name.
ARRAY [*] OF T as a VAR_IN_OUT comes with it. The parameter is an ArrayView
carrying the runtime bounds, so it needs no copy back and cannot have one, and
ArrayView gains an unbound default because a function block stores its in-outs
as members before there is an array to point at.
STRING(23) parsed and was then widened to 254 downstream. An inline array
dropped its element's length; a VAR_EXTERNAL widened through toParamTypeRef,
which a parameter may do and a GlobalVar<…>* may not. TypeReference carries
elementMaxLength through the project model, and the three external sites share
one externalTypeRefCpp helper.
The debug dispatch was worse than oversized: its four string ops hard-cast
every string to IECStringVar<254>, so a STRING(23) read its length from past
the end of a 54-byte object and a write constructed 258 bytes into it. The
spare byte in Entry becomes cap, the ops take it, and IECStringView locates
each field from the capacity, with static_asserts pinning the layout at 1, 23
and 254. The table also walks the EXTENDS chain now, so an inherited member is
watchable, carrying the declaring type so memberCppName spells it right.
Three conformance fixes. REAL_TO_INT(2.5) answered 3, because
iec_convert_value used std::round, which rounds halves away from zero;
ties-to-even is written out with floor and fmod, which avr-libc carries.
INSERT('ABC','XY',2) answered 'AXYBC' — it inserted before the P-th character
rather than after it, while DELETE_STR beside it is right to keep pos - 1.
CONTINUE had no token and no parser rule.
Also: raw_ptr() and sync_length() on both string classes so a generic
parameter or a driver reaches the characters and not the forcing wrapper;
force() writes the raw value too, so a forced variable reads as forced through
a descriptor; IEC_SIZEOF reports capacity + 1 rather than the wrapper's size;
IECVar::value_field_offset() with static_asserts that the payload stays first,
since ADR(x) lowers to &(x); a native block can name a structure, enumeration
or function block its own library declares, which only worked across a real
dependency before; and a library's enum members are exported, so a bare
enumerator from one qualifies instead of failing to compile.
…ccess,
and generics that carry a composite
Forcing. IECStringVar, IECWStringVar, IEC_ENUM_Var and IEC_SUBRANGE_Var each
re-declared the force members but not the behaviour: set() was unguarded where
IECVar's is guarded, force() did not mirror into value_, and copy assignment
was memberwise. The debug layer already treated all four as IECVar by casting
to it, so the same variable behaved differently depending on whether ST or the
debugger touched it. All four now match IECVar exactly, and static_asserts pin
the member offsets the AVR read path depends on.
Both AVR branches of read_entry skipped Entry::cap, so every sized STRING read
as the 254 default and the string ops computed their forced-value offsets past
the end of the object. cap is now read at sizeof(void*) + 1.
test_iec_composite.cpp — 928 lines holding the only cover for array-element,
enum, subrange and struct-field forcing — was in no CMakeLists and would not
compile. Repaired and registered.
Long time types. LTIME, LTOD and LDT are registered with their literal
prefixes and the one-directional promotions from their short forms. The
literals lexed and then silently produced 0, because the DATE/TOD/DT
converters strip their own prefixes and were never taught the long forms.
LDATE stays out: it needs nanoseconds where DATE_t holds whole days, and its
debug-table mapping is removed rather than left wrong by 86400e9.
Partial access. .%X, .%B, .%W and .%D are lexed, parsed, type-resolved,
bounds-checked and lowered, for reads and writes alike. Index 0 is the least
significant part, a write is a read-modify-write masked to the part's width,
and all of it is 64-bit so a shift of 32 is defined. One parsePartialAccess is
shared by the six places that used to test /^\d+$/ on a field, so a bare var.3
and var.%X3 lower identically. A part of an integer is accepted and warns.
Unknown calls. compile("i := ZZQQ(1)") returned success: an unknown call was
emitted verbatim and failed at g++, or was rescued by C++ name lookup. Now
reported, resolving own methods through the EXTENDS chain and both SUPER^
forms. It immediately found TIME_TO_NS/US/M/H/D and the date-time arithmetic
family implemented in the runtime and never registered.
Generics. A generic pin now carries an array, a structure or an enumeration.
The descriptor gains DICOUNT and DISTRIDE so a block can walk what it was
handed, and its members are zeroed so an unwired pin reads as nothing rather
than as whatever was on the stack. IEC_SIZEOF reports the data a
variable-length array view addresses rather than the view's own size, which is
what makes SIZEOF(a) / count the element stride on any target. POINTER TO
lowers to IEC_Ptr in a structure element and a type alias, where it used to
append a star to the wrapped type and fail in C++ rather than here.
2560 tests passing; the 7 failures in integer-literal-exact-cpp.test.ts are
the pre-existing Clang-only ones. C++ runtime 156/156.
VAR_IN_OUT was barely checked: an argument could be a literal, a constant, or absent altogether, and the copy-back then emitted `7 = A.VAL`. A new analyzer pass covers every in-out of every call and resolves the callee through the symbol table, so it sees library blocks — the old check read the AST and never fired for them. A block passed as an in-out is a pointer at the caller's instance rather than a copy, so it can be driven and its outputs read but not written. Arguments given without a name fill the slots the named ones left, in declaration order; codegen dropped them before. A structure holding a block instance is emitted after the class it holds, and a call written as a dotted path drives the instance instead of looking for a method of that name. Together those let two programs share one instance through a global. IEC_ANY gains ELEMCLASS, so an array on a generic pin carries its element type instead of leaving the block to infer it from byte arithmetic.
The debugger locates a string's fields from a void* and a capacity, so the offsets are predicted arithmetically and checked against the real layout. The prediction assumed a 16-bit load needs an even address and rounded the length up to a multiple of two. AVR byte-aligns every type, so there the length sits immediately after the characters and the prediction pointed two bytes past it. Only an even capacity shows it: STRING(23) rounds to the same place either way, while the unqualified IEC_STRING is IECStringVar<254> and does not. The forced value had the same fault a second time, adding the padding a bool needs on a 2-aligned target and that AVR does not. Both now round to alignof(IECString<1>). The alignment does not vary with the capacity, so one instantiation answers for every one, and a wider member added later moves the offsets with it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
runE2ETestPipelinebuilt its g++ command byjoin(' ')-ing an argv array intoa shell string and quoted nothing, so any repo path containing a space
word-split the
-Iflags and backgrounded the command at an&.under such a path.
(
:93,:156, andoscat-gpp-compile.test.ts:185-193). No behaviour changewhere the path has no spaces.