Lookup Rules

2 views
Skip to first unread message

John Skaller2

unread,
Aug 27, 2020, 8:42:35 PM8/27/20
to felix google
Felix currently uses a unified lookup space so each symbol in a symbol table can occur at most once,
except functions which can be overloaded with other functions of the same name.

So you cannot do this:

var f: int;
type f = “int”;

Its a duplicate definition. But there’s an issue, and I note most languages have separate
scopes for types and expressions: in this code:


+int

the + is a functor taking a the TYPE argument int. In

+1

the + is a function taking the expression argument 1.

For example:

fun prefix_plus : int -> int = “$1”;

is a function taking an argument of type int, whereas

typedef fun prefix_plus(T: TYPE):TYPE => carray[T];

is a type function taking an argument of kind TYPE, namely any type,
and returning another type.

The PROBLEM is we cannot have both in the same scope. Either prefix_plus
is a function from types to types or a function from values to values. But that
is what we want. The actual example is more nasty:

typedef fun prefix_plus(T: TYPE):TYPE => carray[T];
open class X { fun prefix_plus : int -> int = “$1”; }

Now if you try to write +1 it will find the type operator not the value operator,
because primary scopes, in this case the global scope, are searched before
the “shadow” scope created by using an open directive.

But I want both. I ran into a similar issue up a level trying to add
UNITSUM types:

UNITSUM + UNITSUM -> UNITSUM


For example

1 + 2 -> 3

considered as types. i had to use a different operator because + cannot
both add value and types.

I think now I will try to split the scopes so if you want a type non-type symbols
are ignored. There are three levels:

EXPRESSION
TYPE
KIND

and we might want:

ANY

so far. I will leave the existing unified symbol tables in place and just add
a filter, i.e. a flag to say what kind of symbol will be found.

The modification should be AGILE. Heh. I will start at the bottom level adding a flag,
and then change all calls to specify ANY, or something more appropriate. At worse,
using ANY, the behaviour should be the same. But first using a constant ANY,
only direct calls are impacted. Then we change it to a variable, and pass it to
callers if appropriate, changing their interfaces bottom up until we reach a routine
that has a specific need for one of the levels.



John Skaller
ska...@internode.on.net





John Skaller2

unread,
Aug 28, 2020, 3:08:43 AM8/28/20
to felix google
I found something weird. After my changes stuff stopped compiling like this:


SIMPLE NAME array NOT FOUND ERROR
In /Users/skaller/felix/src/packages/arrays.fdoc: line 640, cols 28 to 38
639:
640: open[T,N:COMPACTLINEAR] Eq[array[T,N]];
***********
641: open[T,N:COMPACTLINEAR] Tord[array[T,N]];

Routine: inner_lookup_name_in_env

So I reverted and it still failed. Finally it twigged what the problem was but it doesn’t
account for the fact it used to work!

Here’s the issue:

open class X[T] { typedef d[T]= …; .. instance[T] Str[T] { fun str … }};
open[T] Str[d[T]];

Basically the compiler cannot find d, even though X is open.

This is CORRECT. It’s not a bug. The problem is lookup in X depends
on the global scope in which Str[d[T]] is open, but to find d, X must be open.

Which one is open first?

So what Felix does is if it gets confused (and I think it should be forced not
a last resort), it reverst to a “bare bones environment” when looking for
the d inside [] brackets when doing an open. A bare bones environment
is one with all the scopes and defined symbols but NO opens.

So the correct resolution is to write

open[T] Str[X::d[T]];

or in the bugged real case:

open[T,N:COMPACTLINEAR] Eq[Farray::array[T,N]];

In other words add explicit qualification to specify exactly where
the symbol “array” is located.



John Skaller
ska...@internode.on.net





John Skaller2

unread,
Aug 28, 2020, 3:16:41 AM8/28/20
to felix google
So here’s the experiment I want to perform:

You cannot merge a function symbol in one table with a non-function
symbol in another. For example this will never work:

open class X { typedef fred = … }
open class Y { fun fred : … }

because overload sets are a single symbol, and Felix merges them when opening
multiple classes. But you cannot merge the non-function type fred with the
function set with one function fred in it.

But this should work:

open class X { typedef fred .. }
class Y {
fun fred ..
var x : fred = fred 1;
}

and also the converse ordering. The idea is that the first fred after var x is a type
and so should skip over the function fred during lookup. Reversing the freds,
it should also work. One type and one expression fred is used in the variable
definition and both should be visible.


John Skaller
ska...@internode.on.net





John Skaller2

unread,
Aug 28, 2020, 3:54:25 AM8/28/20
to felix google


> On 28 Aug 2020, at 17:08, John Skaller2 <ska...@internode.on.net> wrote:
>
> I found something weird. After my changes stuff stopped compiling like this:

I found the bug. A simple name change in Ocaml from tables to nametables
except in one place, causes the wrong tables to be searched.



John Skaller
ska...@internode.on.net





John Skaller2

unread,
Aug 30, 2020, 12:42:14 AM8/30/20
to felix google
Specifically this test case now WORKS:

// test for type/expr lookup split

begin
typedef hhhh = int;
begin
fun hhhh (x:int) => 2 * x;
var x : hhhh = hhhh 1;
println$ "x=" + x.str;
end
end
begin
fun hhhh (x:int) => 2 * x;
begin
typedef hhhh = int;
var x : hhhh = hhhh 1;
println$ "x=" + x.str;
end
end

We have a type and a function named hhhh. In the first case the type is in the
outer scope and the function in the inner. In the second case the locations are swapped.

The type is found correctly using the filter I added. However the function is found
correctly due to a secondary test:

FIRST CASE
[lookup_name_in_table_dirs] hhhh AnySym
Found entry set {64455}
[lookup_name_in_table_dirs] hhhh AnySym
Found entry set {64455}
[lookup_name_in_table_dirs] hhhh AnySym
Found entry set {64455}
[lookup_name_in_table_dirs] hhhh AnySym
Found entry set {64455}
[lookup_name_in_table_dirs] hhhh AnySym
Found entry set {64455}
[lookup_name_in_table_dirs] hhhh AnySym
Found entry set {64455}
[lookup_name_in_table_dirs] hhhh AnySym
Found entry set {64455}
[lookup_name_in_table_dirs] hhhh AnySym
Found entry set {64455}
[lookup_name_in_table_dirs] hhhh TypeSym
[lookup_name_in_table_dirs] hhhh TypeSym
[lookup_name_in_table_dirs] hhhh TypeSym
Found entry set 64452


SECOND CASE
[lookup_name_in_table_dirs] hhhh AnySym
Found entry set 64468
[lookup_name_in_table_dirs] hhhh AnySym
Found entry set 64468
[lookup_name_in_table_dirs] hhhh AnySym
Found entry set 64468
[lookup_name_in_table_dirs] hhhh AnySym
Found entry set 64468
[lookup_name_in_table_dirs] hhhh AnySym
Found entry set 64468
[lookup_name_in_table_dirs] hhhh AnySym
Found entry set 64468
[lookup_name_in_table_dirs] hhhh AnySym
Found entry set 64468
[lookup_name_in_table_dirs] hhhh AnySym
Found entry set 64468
[lookup_name_in_table_dirs] hhhh TypeSym
[lookup_name_in_table_dirs] hhhh TypeSym
Found entry set 64468
x=2
x=2

note sure how the second case succeeds. All the lookups are finding the type
because it is most local, but the function is still found somehow, probably using
a routine that doesn’t call the core lookup I modified.

The code splits function and other symbols anyhow.
So we must try now with a variable instead of a function.

// type variable split
begin
typedef hhhh = int;
begin
var hhhh = 2;
var x : hhhh = hhhh;
println$ "x=" + x.str;
end
end
begin
var hhhh = 2;
begin
typedef hhhh = int;
var x : hhhh = hhhh;
println$ "x=" + x.str;
end
end

The first case works but the second fails badly:

Flx_lookup:inner_bind_expression: unknown exception File "src/compiler/flx_bind/flx_btype_of_bsym.ml", line 32, characters 37-43: Assertion failed
Error at:
/Users/skaller/felix/tt.flx: line 34, cols 20 to 24
33: typedef hhhh = int;
34: var x : hhhh = hhhh;
*****
35: println$ "x=" + x.str;

This is an artefact of a hack!

let btype_of_bsym state bsym_table sr bt bid bsym =
match Flx_bsym.bbdcl bsym with
| BBDCL_label _ -> btyp_label ()
| BBDCL_invalid -> assert false
| BBDCL_nominal_type_alias _ -> assert false
| BBDCL_structural_type_alias _ -> assert false

A structural type alias is found and crashes the system.

This isn’t supposed to happen. The function is only supposed the called on
symbols that HAVE a type, not symbols that ARE a type.



John Skaller
ska...@internode.on.net





John Skaller2

unread,
Sep 1, 2020, 11:57:02 PM9/1/20
to felix google
I have been fixing some really bad diagnostics and rewriting details of the lookup code.
I managed to break and repair a lot of stuff. It seems to be working better now.

The test case now works as expected; that is, 3 subtests work but this one fails:

begin
var hhhh = 2;
begin
typedef hhhh = int;
var x : hhhh = hhhh;
println$ "x=" + x.str;
end
end

That’s because a type name can ALSO be a constructor function.

Now here’s something close but not exactly equal to the problem which originally
spurred on this work:

begin
typedef fun prefix_plus(T:TYPE) : TYPE => Carray::carray[T];
begin
fun prefix_plus [T]:&T -> carray[T] = "$t"; // unsafe
var x = 1,2,3,4,5; // felix array
var px : +int = +(&x.0); // convert address of array to C pointer
println$ *px;
end
end

begin
fun prefix_plus [T]:&T -> carray[T] = "$t"; // unsafe
begin
typedef fun prefix_plus(T:TYPE) : TYPE => Carray::carray[T];
var x = 1,2,3,4,5; // felix array
var px : +int = +(&x.0); // convert address of array to C pointer
println$ *px;
end
end

BOTH these cases pass now. However the library situation is not exactly
as shown but like this:

begin
typedef fun prefix_plus(T:TYPE) : TYPE => Carray::carray[T];
begin
open class X { fun prefix_plus [T]:&T -> carray[T] = "$t"; } // unsafe
var x = 1,2,3,4,5; // felix array
var px : +int = +(&x.0); // convert address of array to C pointer
println$ *px;
end
end


CLIENT ERROR
[flx_bind/flx_bind_type_index.ml:316: E103] [bind_type_index] Type prefix_plus<48344> must be a type [alias, abstract, union, struct, virtual type], got:
(C function binding) fun prefix_plus[T: TYPE]: &T -> carray[T] requires _rqs_X;
In /Users/skaller/felix/tt.flx: line 65, cols 20 to 63
64: begin
65: open class X { fun prefix_plus [T]:&T -> carray[T] = "$t"; } // unsafe
********************************************
66: var x = 1,2,3,4,5; // felix array

So the filter is somehow missing.


John Skaller
ska...@internode.on.net





John Skaller2

unread,
Sep 2, 2020, 8:38:23 AM9/2/20
to felix google
ok so here is the problem. When looking for a type, we can skip past a non-type.

However, when looking for an expression term in a function position like f in:

f x

f can be a nominal type. If we find a type, we look again for

_ctor_f

This is the function name generated by

ctor f ….

which is the same indeed as wrting

fun _ctor_f ….

(except Felix reserves names starting with underscore for itself).

Therefore this cannot work just by filtering:

begin
typedef fun prefix_plus(T:TYPE) : TYPE => Carray::carray[T];
open class X { fun prefix_plus [T]:&T -> carray[T] = "$t"; } // unsafe
var x = 1,2,3,4,5; // felix array
var px : +int = +(&x.0); // convert address of array to C pointer
println$ *px;
end

because the function prefix_plus is hidden by the typedef prefix_plus
(because primary scopes are searched before shadow scopes).

The only way to make this work is make the lookup go as follows:

1. Look for a non-type function f
2. If not found look for a type f
3. If found look for function _ctor_f

This is really nasty because the first lookup will skip through many levels possibly to global
scope looking for a non-type name f, and use that if found IN PREFERENCE TO A
LOCAL _ctor_f. This is also the case the other way around BUT skipping past functions and
variables to find a type name is comprehensible and cannot be hijacked. More precisely
any “hijack” must be a more local name introduced either by a shadowing definition
or an open, i.e. more locally. This is the case now, for example:

var x = ..
begin
var x = .. // shadows outer x

however with the three step lookup we might have this:

begin
struct f = …
var x = f y; // calls compiler generated constuctor

but now if we put

fun f ...
begin
typedef f = ..
var x = f y; // calls outer f NOT the constructor

and an OUTER definition has hidden an inner one. This is nonsense :-)
Sanity expects the more local definition to hide the outer one, even though
it happens to be a type.

A better solution is to apply the algorithm one table at a time during the lookup.
More precisely if looking for a function:

1. Look in the table for a function.
2. If not found look for a type
3. If found look for _ctor_ prefixed function of the same name
4. Finally if not found, look in the naxt outer table

This algorithm prevents hijacking. In other words a more outer definition
can never pre-empt an inner one that used to work.



John Skaller
ska...@internode.on.net





John Skaller2

unread,
Sep 6, 2020, 9:28:26 PM9/6/20
to felix google
I did a lot of messing about and in the end the experiment failed to be completely
satisfactory. I also did some good work fixing stupid OverloadResolution errors
which failed to identify the problem correctly, and, reworked a lot of the
compact linear type stuff. But it still isn’t working fully and some of the work
was based on shakey algebra so I’m going to revert it all away and start
again. I think the lookup rules can’t be easily fixed. The compact linear type stuff
needs a bit more research before fixing. The OverloadResolution stuff can be
fixed again.

Grr.




John Skaller
ska...@internode.on.net





John Skaller2

unread,
Sep 6, 2020, 9:58:26 PM9/6/20
to felix google
Argghh .. git is IMPOSSIBLE to use. Following tutorial on reverting, of course nothing
works. Someone should be shot for such a ridiculous system.




John Skaller
ska...@internode.on.net





John Skaller2

unread,
Sep 6, 2020, 10:20:21 PM9/6/20
to felix google


> On 7 Sep 2020, at 11:58, John Skaller2 <ska...@internode.on.net> wrote:
>
> Argghh .. git is IMPOSSIBLE to use. Following tutorial on reverting, of course nothing
> works. Someone should be shot for such a ridiculous system.

All i want to do is go back to a previous commit. Doing so is well nigh impossible.
None of the information around works it’s all completely wrong.

I am going to have to do this the hard way. Checkout an old version, copy it to
another directory, reload the current version, then clobber the current one.

Problem is moronic git puts rubbish in every directory so I have to
selectively copy files by hand, this is also because Unix has the worst
set of command line tools imaginable.

Grrr…..



John Skaller
ska...@internode.on.net





Chris Double

unread,
Sep 6, 2020, 10:37:38 PM9/6/20
to felix-l...@googlegroups.com
On Mon, Sep 7, 2020 at 2:20 PM John Skaller2 <ska...@internode.on.net> wrote:
>
> All i want to do is go back to a previous commit. Doing so is well nigh impossible.
> None of the information around works it’s all completely wrong.

If you want to go back to a previous commit, and lose all work from
that commit to the current commit:

git reset --hard <commit-id-to-go-back-to>

If you want to go back to a previous commit and have all work from
that commit to the current commit to exist in your current checkout,
ready to be changed and re-committed:

git reset <commit-id-to-go-back-to>

If you want to clean out temporary files, etc from your checkout:

git clean -xfd

--
htt[s://bluishcoder.co.nz

John Skaller2

unread,
Sep 7, 2020, 1:28:47 AM9/7/20
to felix google


> On 7 Sep 2020, at 12:37, Chris Double <chris....@double.co.nz> wrote:
>
> On Mon, Sep 7, 2020 at 2:20 PM John Skaller2 <ska...@internode.on.net> wrote:
>>
>> All i want to do is go back to a previous commit. Doing so is well nigh impossible.
>> None of the information around works it’s all completely wrong.
>
> If you want to go back to a previous commit, and lose all work from
> that commit to the current commit:
>
> git reset --hard <commit-id-to-go-back-to>

Problem is that only works for local repository and has the effect of detaching
it from the HEAD of the remote repository.

Shayne helped me get this sorted but it took has 20 minutes to figure out
how to do what should be a single trivial command.

i actually tried revert first and that didn’t work either. Every tutorial I found
got it wrong, and once wrong, undoing the commit AND the mess made
by getting it wrong was almost impossible.


John Skaller
ska...@internode.on.net





John Skaller2

unread,
Sep 7, 2020, 1:36:45 AM9/7/20
to felix google
OMG I am such an IDIOT!

I fixed the problem with + by changing all of TWO characters in the library!

var x = 1,2,3;
var px : +int = +(&x.1);
println$ *px;

This now works. The method was total triviality after a week struggling with it:

First:

t[sprefixed_pri] := "+" t[sprefixed_pri] =># "(tprefix 'tprefix_plus)”;

This is the TYPE grammar and now maps a prefix plus symbol
to the name ’tprefix_plus’ , formerly ‘prefix_plus’.

Now change the functor in the library:

typedef fun tprefix_plus(T:TYPE) : TYPE => Carray::carray[T];

and it works because now the two + have different names when used
in types or in expressions, resolving the conflict.

A WEEK of getting it wrong. Fixed by TWO character change.



John Skaller
ska...@internode.on.net





Keean Schupke

unread,
Sep 7, 2020, 2:41:11 AM9/7/20
to felix google
Hi John.


The easiest way is to checkout an old version and then commit that back to master.

You cannot 'remove' the commits as git treats the log as immutable (something that's necessary when multiple people use the system).

git checkout master
git reset --hard <old_commit_id>
git push -f origin master

Should do the trick.

Regarding command line tools, simple "cp -R" should do what you want (it ignores hidden '.' files). You shouldn't need to to this however, the git reset above should work. Do the 'push' before making any changes though.


Cheers,
Keean.

 

--
You received this message because you are subscribed to the Google Groups "Felix Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to felix-languag...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/felix-language/B7D9A00A-B29E-4063-80AA-4A57122A2621%40internode.on.net.

John Skaller2

unread,
Sep 7, 2020, 3:01:20 AM9/7/20
to felix google
So here’s the deal for compact linear types, now we have special product sum an exponentials for them.

The type of a value projection should be

domain -> codomain

end of story. The underlying projection function value will differ depending on the projection kind.
This is not new, projections of records and tuples already differ in the compiler term representing
them: records need field names (as do structs) whereas tuples used integer indices. In the back
end all these resolve to field names (integer 9 becomes field “mem9” or something in the
struct generated for a tuple).

Array value projections allow expressions for indices. These are used as generated to
index a C array in the backend representation. All this works even if type variables
appear.

For compact types the projection uses a divisor and remainder integer at run time
even if it can’t be resolved at compile time this is fine, C++ classes are used which can
do it all abstractly. Eg a projection is a class which accepts an compact linear type
as an argument and dividess and remainders it as required extract a component.
The work is easily delegated to C++ instead of bothering to generate C code
in the Felix compiler.

Note the type of the domain now dictates the kind of underlying function representation
required. If it is compact, a compact projection model is required for the function value.

Now the harder case, pointer projections. What’s new in Felix, relatively speaking,
is that these is a universal pointer type representation term. In the abstract it is:

mode, machine-base-type, component-type

For an ordinary pointer the machine and component types are the same.
For a compact type, the machine pointer is to the base, or whole value,
and the component is the actual type fetched by a deref.

The Ocaml implementation actually uses a list with zero or one element
for the component type, if empty, it implies the same type as the machine
base type. This saves an expensive type compariison.

So the type encoding indicates the type of the underlying pointer,
ordinary or compact. A compact one has a machine pointer to
the indicated type, and a divisor corresponding to the component.
In fact, we do not need this I think. It suffices to not compute these
values at all, just leave the projections stack up until the back end
can do the computations.






John Skaller
ska...@internode.on.net





John Skaller2

unread,
Sep 7, 2020, 3:02:50 AM9/7/20
to felix google

> Regarding command line tools, simple "cp -R" should do what you want (it ignores hidden '.' files). You shouldn't need to to this however, the git reset above should work. Do the 'push' before making any changes though.

Unfortunately that copies git hidden files too.

Anyhow its fixed. Thanks to those that helped!!!


John Skaller
ska...@internode.on.net





Keean Schupke

unread,
Sep 7, 2020, 3:47:22 AM9/7/20
to felix google
Yes, you are right, that's probably why I use 'rsync' to copy stuff :-)

Keean.



--
You received this message because you are subscribed to the Google Groups "Felix Language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to felix-languag...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages