When the compiler error points the wrong way: a void-safety creation gotcha

19 views
Skip to first unread message

Finnian Reilly

unread,
Jun 24, 2026, 4:10:53 AM (11 days ago) Jun 24
to Eiffel Users

I have just completed my first port of a small codebase (Xpact-core), an Eiffel XML parser project) with EiffelStudio 25.12, compiling under complete void-safety for the first time. The whole process went smoothly until one last error that took far longer to resolve than it should have, and I think it is worth sharing as a cautionary tale for anyone attempting the same migration, especially newcomers to the language.

The compiler reported:

Error code: VGCC(6) Error: Creation instruction uses call to improper feature. Class: XT_STRING_INTERVALS Source class: ARRAYED_LIST [G] Feature: new_filled_list Creation of: Result Target type: [attached like Current] attached XT_STRING_INTERVALS Feature name: make_sized (n: INTEGER_32) do -> create Result.make (n) ensure

My first reaction was that this must be an ECF configuration problem. I have a precompile clause referencing base-safe.ecf, and I spent a long time checking precompile paths, comparing my ECF against a colleague's, and verifying that base and its precompile spec agreed on capability settings. None of that was the actual problem, though along the way I did find a genuine issue worth a separate report: the base-safe.ecf precompile spec on Linux declares no void_safety element at all in its own capability block, despite its name. That is misleading and worth ISE's attention, but it turned out to be a red herring with respect to this particular error. Removing the precompile clause entirely and compiling base from source did not make the error go away.

The actual cause had nothing to do with ECF configuration. My class looked like this:

class XT_STRING_INTERVALS inherit ARRAYED_LIST [INTEGER] rename make as make_sized, extend as extend_index export {NONE} all redefine make_sized, wipe_out end create make_sized feature -- Initialization make_sized (n: INTEGER) do Precursor (n) create additions_buffer.make_empty (0) end

I had renamed the inherited make to make_sized, which is a perfectly ordinary thing to do, and exported only make_sized for creation. What I had not anticipated is that ARRAYED_LIST itself still contains an old, obsolete routine, new_filled_list, deprecated since 2018, which internally does create Result.make (n) on a like Current return type. So long as a descendant exports a creation procedure named make, this resolves fine. The moment a descendant renames it away, as I had, the polymorphic creation call inside new_filled_list (reached indirectly through duplicate) becomes invalid, and under void-safe creation-validity checking, that is a hard compile error, not a warning.

The fix was to re-export make alongside make_sized:

create make, make_sized feature -- Initialization make (n, buffer_size: INTEGER) do make_sized (n) create additions_buffer.make_empty (buffer_size) end make_sized (n: INTEGER) do Precursor (n) create additions_buffer.make_empty (0) end

Nothing in the compiler's error message pointed anywhere near this. The error names new_filled_list, a routine I never call, marked obsolete since 2018, buried in ARRAYED_LIST. There is no hint that the actual fix is three inheritance levels away, in my own class's create clause, for a reason that has nothing to do with anything I had written directly. I have twenty five years with this language, and I needed AI assistance to walk back from the reported error to the actual cause. Without that, I would have spent considerably longer chasing ECF files and precompile settings, as I in fact did before getting to the real explanation.

I understand and agree with the underlying reasoning once it was explained to me. Void-safety has to reject a creation call that cannot be statically guaranteed valid on every possible descendant type, and renaming away the conventionally-named creation procedure is exactly the kind of thing that breaks that guarantee. The design is sound. But the diagnostic experience is not, and I think this is exactly the sort of thing that would put a newcomer off the language entirely. A beginner encountering this would have no realistic path from the reported error to the fix, since the error appears to implicate kernel code rather than their own class.

Two concrete things I think are worth raising with ISE:

  1. new_filled_list and its obsolete-era assumption that make is always available as a creation procedure is a latent trap for any descendant of ARRAYED_LIST that renames its creation procedure. At minimum this deserves a clearer diagnostic; arguably the routine should be retired now that void-safety enforces what its own deprecation notice was already advising against.
  2. Has anyone else doing void-safe migrations of older codebases hit a similarly disconnected error message, where the reported location bears no resemblance to the actual fix location? I would be curious whether this is a known class of issue or whether I was just unlucky with this particular inheritance pattern.

Eric Bezault

unread,
Jun 24, 2026, 5:34:52 AM (11 days ago) Jun 24
to eiffel...@googlegroups.com, Finnian Reilly
On 24/06/2026 10:10, Finnian Reilly wrote:
> My class looked like this:

~~~~
class
XT_STRING_INTERVALS

inherit
ARRAYED_LIST [INTEGER]
rename
make as make_sized,
extend as extend_index
export
{NONE} all
redefine
make_sized, wipe_out
end

create
make_sized

feature -- Initialization

make_sized (n: INTEGER)
do
Precursor (n)
create additions_buffer.make_empty (0)
end
~~~~

I think that the code above is OK.
Did you mean to show something like that:

~~~~
class
XT_STRING_INTERVALS

inherit
ARRAYED_LIST [INTEGER]
rename
make as make_sized,
extend as extend_index
export
{NONE} all
redefine
wipe_out
end

create
make

feature -- Initialization

make (n, buffer_size: INTEGER)
do
make_sized (n)
create additions_buffer.make_empty (buffer_size)
end
~~~~

Now about the error. It has nothing to do with void-safety.
It's about full class checking, meaning that inherited
features (written in ancestor classes) are checked again in
the context of the current class. This option is on by
default when using void-safety, so it's probably why you
are new to this kind of errors if you never explicitly set
it before. So the error:


~~~~
Error code: VGCC(6)
Error: Creation instruction uses call to improper feature.
Class: XT_STRING_INTERVALS
Source class: ARRAYED_LIST [G]
Feature: new_filled_list
Creation of: Result
Target type: [attached like Current] attached XT_STRING_INTERVALS
Feature name: make_sized (n: INTEGER_32)
do
-> create Result.make (n)
ensure
~~~~

tells us that when checking the feature `new_filled_list` inherited
from ARRAYED_LIST in the context of XT_STRING_INTERVALS, the creation
procedure used to create `Result`, which is known as `make_sized`
in XT_STRING_INTERVALS, is not a creation procedure in that class.

--
Eric Bezault <er...@gobosoft.com>
Eiffel expert - available for freelance work
https://www.gobosoft.com

Finnian Reilly

unread,
Jun 24, 2026, 8:28:30 AM (11 days ago) Jun 24
to Eiffel Users
Thanks for the explanation Eric. In Eiffel-Loop full class checking was getting in the way of what I wanted to do, so I got used to not having it. 
Reply all
Reply to author
Forward
0 new messages