Derived-type semantics: subprogram visibility, discrete subtypes, multidim arrays, and access types (RM 3.4)#44
Open
AdaDoom3 wants to merge 24 commits into
Open
Derived-type semantics: subprogram visibility, discrete subtypes, multidim arrays, and access types (RM 3.4)#44AdaDoom3 wants to merge 24 commits into
AdaDoom3 wants to merge 24 commits into
Conversation
Make derived subprograms and operators correctly visible, hidden, and further-derivable, matching GNAT's model (Alias / Ultimate_Alias for inherited bodies, visibility-based hiding on the homonym chain, and the RM 3.4 first-/second-kind derivability distinction). Core changes, all upstream of the expander: - RM 8.3 hiding of an implicit (derived) homograph by an explicit one, in either declaration order (Symbol_Add). - Per-overload visibility in the lookups that walk overload chains (Collect_Interpretations, Symbol_Find, Symbol_Find_By_Type); the last prefers a same-named-type match over a loose Type_Root match. - RM 3.4 derivability: Derive_Subprograms scans the parent type's declarative region and keeps exactly the operations a derived type inherits (visible-part explicit homograph overrides; private/body/block homograph is not derivable, so the inherited op is re-derived). - The body re-installs the spec's implicit derived ops; a forward spec never "completes" an inherited op. - Ultimate_Operation interleaves parent_operation and rename peels. - Visible-part renamings and subprogram instantiations are exported and derivable; a subprogram instance exposes its actual profile rather than the formal type's renamed view. - Unary user-operator resolution receives its context type; a USE view of a renaming/inherited op dispatches to the same body. c34014: 2/14 -> 14/14. No suite regressions; c83 24->26, c87 45->46. https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
…100%) Deriving from a constrained discrete subtype (e.g. `type S is new SUBPARENT`) must introduce an unconstrained base S'BASE holding every value of the parent base type, while S itself carries the constraint (RM 3.4). Inherited enumeration literals are values of that base and may fall outside S's range. They were typed with the constrained first subtype, so an assignment of an out-of-range literal hit the "source subtype fits target subtype" elision in the scalar range check (source == target trivially fits) and skipped the check, failing to raise CONSTRAINT_ERROR. NK_DERIVED_TYPE now builds a distinct unconstrained base for a derived discrete type whose parent is a constrained subtype (mirroring the array/record/access/ fixed base construction), and inherited enumeration literals are typed with that base. c34001 3/4 -> 4/4; c34016 0/1 -> 1/1; c34 51 -> 53. No suite regressions. https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
… (c34005 100%) Comparing a multidimensional constrained array against a value routed through the fat-pointer equality path (an aggregate, or an array type-conversion result) returned the wrong answer: Wrap_Constrained_As_Fat built the fat pointer from dimension 0's bounds only, but Generate_Array_Equality reads every dimension's bounds per RM 4.5.2, so the inner dimensions' lengths were undefined and equal arrays compared unequal. Wrap_Constrained_As_Fat now wraps a multidimensional array with every dimension's bounds via Emit_Fat_Pointer_For_Lvalue, matching the sibling fat-wrap path. One-dimensional arrays keep the existing path with its aggregate positional-count adjustment. c34005 13/14 -> 14/14; c34 53 -> 54. No suite regressions. https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
… (RM 4.1) Two codegen gaps around slicing with an implicit access dereference: - A parameterless function whose result is (an access to) an array, written F(...), was treated as a call passing the slice/index as an argument, so the range reached Generate_Expression as an unsupported node. Resolve_Apply now recognises F(...) as an index or slice of the implicitly called result (RM 4.1, 4.1.3), and the indexed-component emitter calls a function prefix rather than loading it as object storage. - A slice assignment X(L..H) := S where X is access-to-array did not dereference X, so the array-target path was skipped and the range fell through to the scalar path. The slice-assignment target now implicitly dereferences an access-to-array prefix (the access value is the fat pointer for an unconstrained/dynamic designated array); element assignment keeps the existing general lvalue path. Fixes the c34007d internal error. No suite regressions. https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
…s (RM 3.7.1) When a discriminated record is created with a discriminant constraint (e.g. an allocator NEW DESIGNATED(TRUE, 3), or a constrained subtype), the discriminant components were skipped entirely during default initialisation because their values come from the constraint, not their defaults. That also skipped binding the discriminant for dependent component bounds, so a component default such as S : STRING (1 .. L) := (1 .. L => 'A') referenced L as an unbound discriminant symbol — an undefined value at link time. Emit_Apply_Component_Defaults now binds each constrained discriminant to its field address in the record (where the constraint value already lives) before the dependent components are initialised, so L resolves through the record. Fixes the link failures in c34007s / c34007u. No suite regressions. https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
…ants (RM 3.3.2) X IN T where T is an access subtype constraining its designated record's discriminants (type T is new ACC(TRUE, 3)) always returned TRUE — only access-to-array subtypes checked the designated constraint. A non-null access value now belongs iff its designated record's discriminants equal the subtype's constraint; a null value belongs. Fixes c34007r and c34007u. https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
…ated array (RM 4.8) Allocating a multidimensional array through an access type with a qualified aggregate (NEW SUBDESIGNATED'((1,2,3),(4,5,6))) had three multi-dim gaps: - the heap byte size counted only the first dimension's length, under-allocating the data; - the result fat pointer carried only dimension 0's bounds, so a later check or comparison read garbage for the inner dimensions; - the object-declaration access constraint check looped over dimensions but read dimension 0's bounds each time (Emit_Fat_Pointer_Low/High instead of the per-dimension _Low_Dim/_High_Dim), so a value matching the first dimension was checked against every dimension's constraint and raised a spurious CONSTRAINT_ERROR. The qualified-aggregate allocator path now sizes the allocation by the product of all dimensions, builds a multi-dim heap bounds struct, and checks each dimension against the index subtype and the target access subtype; the access constraint check reads each dimension's actual bounds. Fixes c34007i; advances c34007g past elaboration. No suite regressions. https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
…or dynamic bounds too The previous multi-dim allocation fix sized the data only on the static-bounds path. A designated array with dynamic bounds (NEW SUBDESIGNATED'(...) where SUBDESIGNATED is DESIGNATED(IDENT_INT(4)..IDENT_INT(5), ...)) took the fat initializer branch, whose length is the first dimension's element count, so the allocation was short and every dereference read wrong data. The allocator now overrides the byte length for any multidimensional designated with the runtime product of all dimensions' lengths (evaluated from the initializer type's bounds, static or dynamic) times the element size, before the heap allocation and copy. Advances c34007g (initialization, conversions, and .ALL value now correct). No suite regressions. https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
…atic type (RM 5.2) Assigning to X.ALL where X designates an unconstrained or dynamically bounded array copied designated->size bytes — for an unconstrained array that static size is just one element, so only the first element was written. The whole-array .ALL assignment now derives the byte size from the access value's fat-pointer bounds (the product of every dimension's length times the element size). Advances c34007g (.ALL and indexed assignments now correct). https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
…straint (RM 4.1) X'RANGE used as an array index constraint (Y : DESIGNATED (X'RANGE, 1 .. 3)) returned no bound when X is an access-to-array, because the prefix type is an access, not an array. It now dereferences the access to the designated array, so X'RANGE and X'RANGE(N) denote the designated array's dimension bounds. Completes c34007g. https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
Generate_Composite_Address returned the access value for X.ALL without a null check, so dereferencing a null access in a context that takes its address — a record/array comparison, a component or element of X.ALL — read through the null pointer and crashed instead of raising CONSTRAINT_ERROR. The .ALL address path now emits the access check (which handles both thin and fat access values). Fixes c34007m and c34007s. https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
…d (RM 3.4) X.ALL := aggregate where X is a constrained access (type T is new ACC(TRUE,3)) typed the source aggregate with T's constrained designated, so a base-range value — e.g. (FALSE, 2, ...) reached through an inherited operation that yields an object outside the subtype's constraint — was checked against the static constraint and raised a spurious CONSTRAINT_ERROR. The .ALL assignment denotes the actual object, so the source is now typed against the unconstrained base designated and matched to the run-time object, not the constraint. Fixes c34007p. https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
…objects shadow outer subprograms (c34007 100%) Two fixes completing c34007 (derived access types): - A positional aggregate of a constrained array whose bounds are dynamic (NEW SUBDESIGNATED'(1,2,3) where SUBDESIGNATED is DESIGNATED(IDENT_INT(5).. IDENT_INT(7))) fell back to the index subtype's first (0), producing 0-based bounds that failed the constrained access target's RM 4.8 bound check. It now evaluates the dynamic constraint low and derives the high from the element count. - RM 8.3: an inner object whose type is access-to-array now hides an outer homograph subprogram of the same name, so A(I) inside a body with a local access variable A and an enclosing procedure A indexes the variable (through an implicit dereference) instead of referencing the procedure's frame. c34007: 10/11 -> 11/11. https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
Array assignment requires equal lengths per dimension (the value slides, but the lengths must match); a mismatch raises CONSTRAINT_ERROR. Two gaps: - a whole constrained array assigned from another constrained array of different length was memcpy'd with no length check; - a slice target assigned from a source slice did not compare the two slice lengths. Both now emit a per-dimension length check (static bounds fold to a constant comparison, dynamic bounds check at run time). Advances c52104 1/14 -> 3/14. https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
A slice target assigned from a non-slice source (a string literal, a whole constrained array, or an array value) now checks the source length against the slice length and raises CONSTRAINT_ERROR on a mismatch. Advances c52104 3/14 -> 5/14. https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
X(a..b)(c..d) := source crashed codegen (a range reached Generate_Lvalue as an expression). A slice keeps the original index values, so the outer range is already absolute; the target is collapsed to a single slice on the base array, which the slice-assignment path handles directly (including the length check). Advances c52104 5/14 -> 7/14 (all subtests now compile). https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
A slice of a dynamically bounded array (stored as a fat pointer) assigned from a string/array source did not compare lengths. The fat slice-assignment path now checks the source length against the slice length and raises CONSTRAINT_ERROR on a mismatch, like the constrained path. Advances c52104 7/14 -> 11/14. https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
…zer's (RM 3.6) An object A : T(2..6) := "QUINC" with dynamic constraint bounds stored the string initializer's bounds (1..5) instead of the subtype's (2..6), so a later slice A(2..6) raised a spurious 'slice bound outside array' CONSTRAINT_ERROR. A constrained array object now keeps its own subtype bounds (the initializer slides into them); an unconstrained object still takes the initializer's bounds. Advances c52104 11/14 -> 13/14 (fixes c52104l, c52104m). https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t
A scalar membership test X IN T is statically TRUE when X's nominal subtype is provably covered by T, since every value of that subtype belongs to T. Folding it removes the runtime range check, which was reading the operand even when it had not yet been assigned a value - making a test of an own-subtype membership depend on stack garbage. This mirrors GNAT's Compile_Time_Compare, which proves the bounds from the operand's nominal subtype rather than its runtime value.
An array type conversion now: - rebuilds the fat pointer at the target index type's bound width, so an operand whose index type is narrower (e.g. INT -100..100 in i8) is read correctly through an INTEGER-indexed view; - for a constrained target, imposes the target subtype's bounds and length- checks each dimension; - for an unconstrained target, checks that each non-null dimension's bounds belong to the target's index subtype; - checks that the operand and target component subtypes carry the same constraint (scalar range, real accuracy, nested array bounds, access / record discriminants), raising CONSTRAINT_ERROR when they differ. Fixes c46041a, c46042a, c46043a, c46043b, c46044a, c46044b.
A subprogram given as a forward spec in a declarative part now carries a module-global elaboration flag, reset false where the spec elaborates and set true where the body elaborates. Calls - both parametered and parameterless - check the flag and raise PROGRAM_ERROR when the body has not yet run, e.g. a function called in the initialization of an object declared before the body. The flag is global so a call reached from a generic instance or a default expression in another frame still sees it.
A generic unit that has a body now carries the same elaboration flag as a subprogram: reset false at the generic declaration, set true where its body elaborates, and checked at each instantiation. Instantiating before the body elaborates raises PROGRAM_ERROR. Bodyless generic packages have nothing to elaborate and are left unflagged, so a normal instantiation of one does not spuriously raise.
An object of an unconstrained discriminated type with defaults reports 'CONSTRAINED based on whether it can be re-discriminated. An IN formal parameter and a constant object never can, so 'CONSTRAINED is always TRUE for them regardless of the nominal subtype.
A heap object's discriminants are fixed when it is allocated, so a .ALL dereference always yields a constrained object. X.ALL'CONSTRAINED now returns TRUE regardless of whether the designated subtype is written with a constraint. Fixes c37404a.
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.
Completes five ACATS derived-type groups to 100%, implementing the Ada 83 semantics of RM 3.4 (derived types) and the visibility, derivability, allocation, and dereference rules they depend on. Every fix is upstream of the expander (semantics / type construction), grounded in the RM and GNAT's model.
Test results (full suite, this branch vs. base
f861211)Per-test diff over the full 1976-test Class C suite: 0 regressions (no test passing at base fails or skips now) and +30 net passes (33 newly passing, run-to-run tasking noise accounts for the small difference).
Groups driven to 100%
Collateral improvements with no regressions: c34 40→62, c41 36→38, c83 24→26, c87 45→47.
Key changes
RM 3.4 / 8.3 — derived subprogram visibility (c34014)
Subprograms_Are_Homographs,Subprogram_Is_Implicit): inSymbol_Add, an explicit subprogram hides an implicitly declared (derived) homograph in the same region, in either declaration order — mirroring GNAT's visibility-based hiding on the homonym chain.Symbol_Find,Symbol_Find_By_Type, andCollect_Interpretationscheck visibility per overload, not just at the bucket head, so a hidden homograph at the chain head no longer masks visible overloads.Symbol_Find_By_Typeprefers a same-named-type match over a looseType_Rootmatch.declared_in_visible_part,Subprogram_Is_Derivable,Has_Visible_Part_Explicit_Homograph):Derive_Subprogramsscans the parent type's declarative region (seeing implicit ops the export list omits) and keeps exactly the operations a derived type inherits — a visible-part explicit homograph overrides the inherited op; a private-part/body/block homograph is not derivable, so the inherited op is re-derived.Install_Derived_Operations): re-installs implicit derived ops from the spec scope into the body scope (walking buckets + overload chains, sincesymbols[]records only chain heads), preserving each op's parent for mangling.Ultimate_Operationinterleavesparent_operationand rename peels (GNATUltimate_Alias); a generic instance exposes its actual profile, not the formal's renamed view (Peel_Generic_Actual_View); unary user-operators receive their context type; a USE-view of a renaming/inherited op carries the links to dispatch to the same body; visible-part renamings and instantiations are exported (Decl_Is_Visible_Subprogram).Derived discrete subtypes (c34001, c34016)
T'BASE; inherited enumeration literals are values of that base, so an out-of-range literal assigned to the subtype is range-checked and raisesCONSTRAINT_ERROR.Derived multidimensional arrays (c34005)
Wrap_Constrained_As_Fatwraps a multidimensional array with every dimension's bounds (was dim 0 only), so array equality (RM 4.5.2) against an aggregate/conversion no longer reads garbage inner-dimension lengths.Derived access types (c34007)
.ALL :=sized from the access bounds;P'RANGEon access-to-array dereferences implicitly (RM 4.1)..ALLdereference taken as an address (RM 4.1); a.ALL-target aggregate is typed against the unconstrained base designated so base-range values reached through inherited operations are matched to the run-time object, not the static constraint (RM 3.4).https://claude.ai/code/session_01C2sPU3U9ejM9DgqkYwkj7t