Skip to content

Conversation

@jurgenvinju
Copy link
Member

@jurgenvinju jurgenvinju commented Oct 27, 2025

  • Fixes Tree of start[A] declared as A #2496 by redefining alub, aglb and asubtype such that start symbols are exclusively subtypes of themselves and Tree and node.
  • adds a syntaxRole field to AType::\start to make it more compatible with aadt and avoid case distinctions later in the compiler.
  • every time a start modifier is detected on a syntax rule for X, the collector:
    • declares three things:
      1. an implicit start type "start[X]" as AType::start(X, contextFreeSyntax())
      2. a start production rule with a top field prod(X, [label(X, "top")]. _),
      3. the top field within that production to start[X].top with type X.
    • To identify each of these three implicit definititions independently, the location of the start keyword is used in three ways. This avoids the multiplekey problem for the mapping between facts/types and their declaration locations.
      1. the loc of start as a whole, for the generated start type.
      2. the loc of the first s in start only, for the generated rule.
      3. the loc of the first t in start only: for the generated field.
  • removes all downstream special cases for the top field and start symbol handling, which are not needed anymore due to the above. everything is automatic now due to the right TModel.
  • some new tests test the subtype semantics between start[A] and A and the existence and type of the top field of a start[A] non-terminal (which is A)

@codecov
Copy link

codecov bot commented Oct 27, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 47%. Comparing base (5046bef) to head (37715c1).

Additional details and impacted files
@@           Coverage Diff           @@
##              main   #2499   +/-   ##
=======================================
  Coverage       47%     47%           
- Complexity    6705    6711    +6     
=======================================
  Files          791     791           
  Lines        65239   65239           
  Branches      9769    9769           
=======================================
+ Hits         30916   30933   +17     
+ Misses       31909   31896   -13     
+ Partials      2414    2410    -4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@toinehartman
Copy link
Member

@jurgenvinju Added the tests as requested in #2496 (and an additional one), but two of them still fail. I had a look, but did not see an obvious solution.

Copy link
Member Author

@jurgenvinju jurgenvinju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked for funny side effects. Still not working!

!isTerminalSym(sym),
tsym := s.getType(sym),
isNonTerminalAType(tsym),
stp := getSyntaxType(removeChainRule(tsym), s)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check later: what is remove chain rule?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PaulKlint I don't remember chain rule unwrapping as a feature for production fields. Do we have this in the interpreter?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I copied this from the behavior of the interpreter

@jurgenvinju
Copy link
Member Author

jurgenvinju commented Nov 17, 2025

This PR doesn't get through any type-checker test with a function or constructor call in it.

My internal notes, for later:

The changes made only pertain to the start symbol/atype so that is a surprise.

The intersection between start symbol and function calls happens probably when filtering the overloaded candidates. Each candidate's parameters are matched using acomparable which uses asubtype, which was changed in this PR.

All candidates are always filtered currently. So asubtype returns false for at least one of the function parameters. Possibly for all of them.

This points to a new bug introduced into asubtype. If not, then alub may also be a candidate. It was also changed in this PR and it can influence parameter types and then influence asubtype. Finally aglb but that seems unlikely since it is not used very often and even the simplest test fails.

If asubtype is broken then I should definitely check aglb and alub too. Also it would be interesting to see if these three functions can be randomly tested like their counterparts in Type...

@toinehartman
Copy link
Member

Everything succeeded at e6fbb9e. What motivated the changes made since then?

}
reportMissingNonTerminalCases(current, overloads, validOverloads, actuals, s);
next_cons:
for(ovl: <key,idRole, tp> <- overloads){
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm worried about reusing a tp variable here since it occurs more often in the code.

@jurgenvinju
Copy link
Member Author

jurgenvinju commented Nov 20, 2025

@toinehartman it didn't work for me at that commit. That's one reason I kept searching for better solutions.

While searching I also saw that start's top field implementation was scattered, and the top field test was exactly the failing test I was working on. So now the top field handling is all done in the collect for the Start modifier.

In the meantime my failing test was due to an old open typepal project. 🙈

@jurgenvinju jurgenvinju marked this pull request as draft November 26, 2025 09:34
bool isADTAType(aparameter(_,AType tvb)) = isADTAType(tvb);
bool isADTAType(aadt(_,_,_)) = true;
bool isADTAType(areified(_)) = true;
bool isADTAType(\start(AType s)) = isADTAType(s);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have my doubts here. What does it mean to be a ADTAType? Should it be obly aadt's? Then this should be false for start. If it should be all left hand side of syntax rules, then it should be true.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps @PaulKlint can answer this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Note: this line is changed, not removed; look at the full diff)

@jurgenvinju jurgenvinju self-assigned this Nov 26, 2025
@jurgenvinju jurgenvinju marked this pull request as ready for review November 26, 2025 10:58
c.define("<aStartSym>", nonterminalId(), current, st);
startProd = defType(aprod(prod(aStartSym, [nonterminalType[alabel="top"]])));
sPos = current@\loc.top(current@\loc.offset, 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks a bit tricky. Replace by original loc + modifier.

private AType computeFieldAssignableType(Statement current, AType receiverType, Tree field, str operator, AType rhs, loc scope, Solver s){
//println("computeFieldAssignableType: <current>");
fieldName = unescape("<field>");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So where is "top" declared these days?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fieldDef = defType(nonterminalType[alabel="top"]);
tPos = current@\loc.top(current@\loc.offset + 1, 1);
c.define("top", fieldId(), tPos, fieldDef);

@PaulKlint
Copy link
Member

Overall looks good.

All these "notes to self" are noise and complicate this review.

Minor concern: at the moment we do not support a start for lexical symbols. The original code allowed this while the new code always hard wires that it is context free.

I cannot assess whether this new code will break the code generator.

Copy link
Member

@toinehartman toinehartman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! I like how this removes all kinds of special casing, e.g. around the top field.
I have a couple of minor comments.

for(AType adtType <- allStarts){
syntaxDefinitions[\start(adtType)] = achoice(\start(adtType), { prod(\start(adtType), [definedLayout, adtType[alabel="top"], definedLayout]) });
// TODO JV: there are more places where layout is added to the start non-terminal...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this TODO indicate more work to in this PR before merging?

bool isADTAType(aparameter(_,AType tvb)) = isADTAType(tvb);
bool isADTAType(aadt(_,_,_)) = true;
bool isADTAType(areified(_)) = true;
bool isADTAType(\start(AType s)) = isADTAType(s);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps @PaulKlint can answer this?

bool isADTAType(aparameter(_,AType tvb)) = isADTAType(tvb);
bool isADTAType(aadt(_,_,_)) = true;
bool isADTAType(areified(_)) = true;
bool isADTAType(\start(AType s)) = isADTAType(s);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Note: this line is changed, not removed; look at the full diff)

private AType computeFieldAssignableType(Statement current, AType receiverType, Tree field, str operator, AType rhs, loc scope, Solver s){
//println("computeFieldAssignableType: <current>");
fieldName = unescape("<field>");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fieldDef = defType(nonterminalType[alabel="top"]);
tPos = current@\loc.top(current@\loc.offset + 1, 1);
c.define("top", fieldId(), tPos, fieldDef);

msg = error("Constructor `<ovl1.atype.alabel>` is overloaded, maybe<qualifyHint><argHint>",
msg = error("Constructor `<ovl1.atype.alabel>` is overloaded, maybe <qualifyHint><argHint>",
current@\loc);
iprintln(overloads);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be removed?

} else
continue;
}
// println("require: <i>, <formalTypesU[i]> -- <aiU>");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the compiler have a verbose/debug flag that we can hide this under?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tree of start[A] declared as A

3 participants