Repository navigation
v3.5.0
The release that finished the memory model and then found out what it had left
open. owned ^T went into the library, which is what ADR-0181 was built for,
and adopting it turned up four ways a borrow could outlive what it borrows that
the rule as written did not see — three of them found by a langspec audit whose
whole purpose is that no oracle here can contradict a reading, and every gate
was green over all three.
Two threads of control became something a program can steer. A task is a
value now, with a name and a type; wait joins one; and the select-statement
waits on several channels at once, takes the first arm that can proceed, and
gives up after a timeout. ThreadSanitizer is the oracle underneath that, and it
is a gate rather than a thing run by hand.
A pointer is not always eight bytes. i386 is admitted, which took seven
layout rules rather than the 3858 offsets that looked like the work, and it
brought clong and csize — the first required identifiers whose type
depends on the target. It also decided something this release states outright:
an i386 this compiler emits for has SSE2, because on an x87 an error condition
this language says it detects goes undetected.
And traits. A trait bounds a schema's type-valued discriminant and a
routine's type parameter, an implementation is written by whoever names the
type, and lib/dialect/passortx.pas is the first client that is not a test —
impl Sortable for Point; once, and Sort(ps) thereafter. Four records
preceded one working feature and each says why the previous was wrong; the
first client found the fifth thing in under an hour, which is now a rule in
doc/sop.md.
Added
-
Waiting for a task, and for whichever of several things comes first
(ADR-0312, ADR-0313, AP 6.4.17, AP 6.9.3.15).taskis a required
type-identifier — shadowable, likeint64, and not a word-symbol — and its
values are handles, so a task is released by the block that declared it,
moved bytake, and refused as an assignment source like every other handle.
wait(t)blocks until that one task has finished. The select-statement
waits on several channels at once and takes the first arm whose operation can
proceed, with the case-statement's own punctuation:select receive(x, got): send(out, got); receive(y, got): send(out, got); after 2000: writeln('gave up waiting') end
A
sendarm waits for room as areceivearm waits for a value, anafter
arm gives up after that many milliseconds, and anotherwisedeclines to wait
at all. -
A third target:
--target=i386-pc-linux-gnu(ADR-0325). Every layout rule
that wrote 8 now asksPtrSizeorWordAlign, so a pointer is four bytes
where the target says so. The corpus runs against a 32-bit runtime under the
target32gate; the one program that cannot is a deliberate 2 GB allocation. -
clongandcsize(ADR-0328), two required identifiers denoting whichever
integer type a Clongand a Csize_tfit on the target —int64on x86-64
and aarch64,integeron i386. Anexternaldeclaration naming a C routine
that takes alongis now writable once rather than per target. -
A protected parameter may be of an owned-pointer-type (ADR-0318,
AP 6.4.14.8), which is the read-only borrow: a name for what a variable owns,
read through, and through which nothing may be released. §6.4.1's exclusion of
a pointer-type does not reach an owned pointer. -
A discriminated schema may be written wherever a type-name is required
(ADR-0324, AP 6.7.3.1.1, AP 6.7.2.1) — a parameter-form and a result-type both
takeVec(integer)where they took only a name. -
An array determines a slice parameter's component type (ADR-0316), as a
slice-type already did, soarray of Taccepts an actual of an array-type
without the caller writing the slice. -
PasSortX, a sort over the element type itself (ADR-0344): the trait
SortablewithSort,SortWith,IsSortedandLowerBoundOfover an
array of T. The trait is declared in the module and every implementation
is written by the client —impl Sortable for Point;once, andSort(ps)
thereafter — which is the only shape separate translation admits and is
what makes a trait usable from a library at all.SortWithtakes the order
as a procedural parameter instead, for a caller whose order is not the
type's or whose element type implements nothing.PasSortis unchanged and
stays: it is conforming Extended Pascal, and itsSortIndexedstill answers
for parallel arrays. -
A type may implement a trait, and a trait may bound a type (ADR-0338 to
ADR-0341, AP 6.4.7.2, AP 6.7.9, AP 6.7.10).trait Sortable; function Rank(u: Self; v: Self): integer; end;names routines a program supplies, and
impl Sortable for Point;supplies them — each routine written as a name
alone, the trait having given the heading. A call selects by the type of its
first argument, after the ordinary lookup, so two implementations of one
trait live in one block and a program declaring its own routine of the name
keeps it. A subrange takes its host's implementation. The bound is written
where the client writes the type — on a schema's type-valued discriminant,
Map(K: Sortable; V: type; cap: integer), checked once at the type-denoter —
and also on a routine's type parameter,T: Sortable type, where a category
would stand. One bound, not two. Neithertraitnorimplis reserved. A
trait may be declared in a module's interface and exported; an
implementation belongs to one translation, so it may not stand in a
procedure or in a module heading. -
A task may be handed a handle (ADR-0303, AP 6.7.8.1): a formal parameter
of a task-declaration may be of a handle-type, and the actual is written
takeof a variable of that type — the one position outside an assignment
where AP 6.4.14.6 admits a move. The variable is emptied before the
activation commences and the task's block releases what it was given, so a
socket, a stream or a directory can now be given to a task. A channel is
still lent and a handle is moved; a handle still cannot be sent through
a channel. -
A channel is closed by the release the program wrote (ADR-0302,
AP 6.4.16.4):release(c),c := nilandc := take(d)each close the
channel wherever they stand, including inside a task. Before this a task's
release dropped that task's reference and left the channel open, so a
pipeline of stages each closing the one downstream of it deadlocked with no
diagnostic. The release performed by the end of a task's block is unchanged
and still only drops the reference, which is what a pool of workers needs. -
A release archive on every tag (ADR-0296). Pushing a
v*tag now
attachesafterschool-pascal-<tag>-x86_64-linux.tar.gzand an
aarch64-linuxone, each with a.sha256, to the GitHub release: the
cmake --installlayout,LICENSE,COPYING.RUNTIMEand a shortREADME,
withpascalclinked statically.clangis still needed at use time.
tools/release.shbuilds and checks the archive,release-archiveruns
both halves underctest, andtests/checks/install_layout.sh --prefix
checks an unpacked prefix.APASCAL_STATIC_PASCALCis the CMake option. -
examples/(ADR-0295): twelve complete programs of a page each,
written to be read by someone who knows Turbo Pascal, every one actest
case (example-<name>) with a golden, and swept by the same gates as the
rest of the corpus. Writing them found seven things — a task cannot close
the channel downstream of it and a program that tries deadlocks silently,
a map lookup writes two types the call already knows, four of twelve
programs collided with a library noun — recorded in the ADR and in
doc/roadmap.mdrather than fixed here. -
tests/checks/heap_balance.pymeasures a case that resolves its imports
by.importpath, and--writerefuses to write a catalogue when a case
did not run instead of striking it in silence. -
diagnostic-coverageandforeign-layoutread nothing when the checkout
is itself a.claude/worktreestree, their worktree filter having tested
the absolute path; both now test the path below the root.
Fixed
-
The committed seed no longer names the machine that generated it
(ADR-0347). Since ADR-0293 a trap carries its own position, so the source's
path is a string constant in the emitted module — andseed/refresh.sh
handed the compiler an absolute one, soseed/*.llheld the reseeding
machine's home directory andtests/checks/seed_current.shcould pass only
in the directory that had produced it. Both now translate from the repository
root with a relative path, and a newseed-portablegate asks the cheap half
of the question on every push rather than at a tag. -
Disposing a long owned chain or a deep owned tree costs one frame
(ADR-0322, ADR-0333, AP 6.4.14.3). The release routine walked the structure
recursively, so a list or a tree deeper than the stack ended the program in a
segmentation fault rather than freeing it. Every self-owned field is emptied
onto a work list threaded through the nodes themselves, so the depth costs no
stack and no allocation. -
A block could reach an owner through a procedural parameter (ADR-0326),
and through a formal bound before its own activation existed (ADR-0332). Both
were ways for a borrow to outlive what it borrowed that the rule as written
did not see; both are refused, and the second is one conjunct rather than a
second rule. -
disposeof a variable whose domain is a schema found no tuple
(ADR-0329) where the walk reached one below the outermost, so a discriminated
component was released against the wrong bounds. -
PasJsonreads a number by rounding once (ADR-0314). The reader
normalised and then computed, accumulating error before the value existed; it
now writes what it scanned as a Pascal real-literal and converts once, so a
decimal that reads back exactly does. -
PasJsonwrites the shortest number that reads back as the same value
(ADR-0309).0.75came out as7.500000000000E-01— valid JSON, and
§6.9.3.4.1's default real output arriving unchanged — and now comes out as
0.75. The writer renders at a precision and reads its own output back with
readstr, keeping the first spelling that returns the value it started
from; the point goes where ECMAScript'sNumber::toStringputs it, so10
is10and1e-7is1E-7. A whole number is unaffected:3is still3
and never3.0.examples/json_pretty.outandtests/dialect/lib_json.out
move with it. -
The language server reads
.importpath(ADR-0311). It honoured
.componentsand not ADR-0244's other sidecar, so opening any of the seven
examples/programs that name a place rather than a list of files put a
diagnostic on every imported name — 21 of them onword_freq.pas, all
false. The directories now reach the compiler as--import-path, and the
two sidecars combine. -
A source named with no directory finds its neighbours (ADR-0308).
ADR-0244's first search rule is the source's own directory, andSourceDir
answered the empty string for a name with no/in it, whichAddPath
drops on purpose — so a program and its modules in one directory compiled
underpascalc ./prog.pasand not underpascalc prog.pas. The answer is
./, andbare-source-nameis the new gate; it has to be a harness because
no test case can choose how it is named. -
A channel may carry a
string(n)or a text (ADR-0302).sendchose its
path withIsStructured, which a variable-string is not, so a program
sending one did not assemble at all; and copying the element's size out of a
string value read past what the expression produced, because a value is
shorter than the type it is going into. The value is now stored into a
temporary of the element type by the ordinary assignment, so it is padded
and — for a text — normalised where it crosses.
Changed
-
takeinside a generic means move where the type moves (ADR-0323). A
generic body may writetake(v)for a type parameter that turns out not to be
affine; there the variable is unchanged and not threatened, and the operation
denotes its value. Outside a generic it is refused as before, so one body
serves an element type that moves and one that copies. -
A borrow is refused where it is formed (ADR-0319, AP 6.4.14.9). A
variable-access reached through a dereference of an owned pointer may not be
passed to avarparameter of a call whose callee can name the entire
variable that owns it, and where such an access is awith-element, no call
in the body may activate a block that can name it. The earlier rule refused
the release and let the dangling name be formed. -
A schema domain is admitted only where what it produces holds nothing
affine (ADR-0320, AP 6.4.14.2).owned ^Vec(integer)is accepted where the
produced type contains no file, handle or owned pointer, and refused where it
does — which is what let the owned pointer into the library at all. -
An i386 this compiler emits for has SSE2 (ADR-0346,
doc/implementation-defined.md§2.2).tools/pascalccnames
-march=pentium4on everyclangit starts fori386-pc-linux-gnu, and for
no other target. clang's own default processor for that triple isi686,
whose x87 registers are eighty bits wide, and two things break on those
eighty bits: §6.7.6.3 definesround(x)as equivalent totrunc(x ± 0.5)
and the two disagree at-0.49999999999999994, and D.32'ssqrerror goes
undetected,sqr(-1e200)being an ordinary finite number in a register
with a fifteen-bit exponent. What is given up is a Pentium III and earlier.
A caller who wants the x87 back can name-march=i686in
AFTERSCHOOL_PASCAL_CFLAGS. -
A trait may not be named
numeric,ordinal,orderedorequatable
(ADR-0344, AP 6.7.9). Those four spellings name a type-parameter category
wherever a bound is written and nothing is looked up there, and a bound is
the only position a trait may stand in — so a trait of one of those names
could be declared and implemented and never applied. It is refused at the
declaration, where before the program was told at the call that its type was
not admitted by a category it had never written. -
PasContainerstates how to choose a map's key type (ADR-0310). Nothing
in the library changed and nothing new is exported: the map has been generic
over its key since ADR-0254 andMapKey(string(63)) is a ready-made key
type, not a bound — ADR-0295's sixth finding said otherwise and was wrong,
which a map keyed at 200 holding a 130-character key settles. What is real is
that a program keyed by text from outside must choose a capacity, so the
module header,lib/dialect/README.mdanddoc/tour.mdnow give the rule:
size the key to a bound the program already has, guard against
m^.slots[1].key.capacitywhere it has none, never clamp withsubstr.
examples/word_freq.pasis rewritten to the first shape and has no guard;
its output is unchanged. -
A generic activation may write a prefix of its type arguments
(AP 6.7.3.10.4, ADR-0304). ADR-0254 admitted two forms, every type argument
or none; an activation may now write the first k of them and leave the
rest to be inferred, so a routine whose type stands only in the result —
which §6.7.1 makes a type-name and not an actual, so nothing can determine
it — no longer makes its caller write the types the arguments already say.
VecGet(char, b, i)whereVecGet(JsonChars, char, b, i)was required.
The arity says how many were written; the tie-break separating this from an
activation short of an argument is asked at the first omitted type
parameter. Every existing call keeps its meaning. -
PasContainer.VecGetandMapKeyAttake their result type first
(ADR-0304).VecGet(Elem, Ptr, …)andMapKeyAt(K, Ptr, …), so that the
one type an activation must name is the prefix andVecGet(integer, v, 3)
is the call. This breaks a client that wrote the old order. Nothing else
inPasContainernames a type at a call:MapGet(m, k, 0, StrHash, StrEq)
has been inferable since ADR-0254 and this module's own header said
otherwise. -
Every file variable is bindable (AP 6.5.1, ADR-0299).
bind,unbind
andbindingaccept any variable-access that denotes a file, whether or not
its type-denoter saysbindable: avar f: textparameter — §6.7.6.8's own
examplebindfile— a dereference of^text, atextfield and atext
element. The word is still accepted wherever §6.4.1 admits it and is
redundant on a file; on a non-file it keeps its meaning andbindrefuses
it by design. ISO/IEC 10206:1991 programs keep their meaning; what moves is
that a refusal the standard requires is no longer made. The diagnostic
'f' is not bindable; only a variable whose type-denoter says 'bindable'
can be bound to something outside the program is gone with the rule, and
'bind' needs a file variable, found integer now ends : only a file
variable is bindable. -
PasFiletakes the file through a parameter. Three helpers take
var f: text; every exported name and answer is unchanged.