Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ast/tests: Repeat type resolution for arguments in lambda to fix #3315 and fix #3308 #3318

Merged
merged 7 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 59 additions & 38 deletions src/dev/flang/ast/Call.java
Original file line number Diff line number Diff line change
Expand Up @@ -650,11 +650,9 @@ private boolean loadCalledFeatureUnlessTargetVoid(Resolution res, AbstractFeatur
: res.state(thiz) .atLeast(State.RESOLVING_DECLARATIONS)));

var targetVoid = false;
var actualsResolved = true;
AbstractFeature targetFeature = null;
if (_calledFeature == null)
{
actualsResolved = false;
targetFeature = targetFeature(res, thiz);
if (CHECKS) check
(Errors.any() || targetFeature != null && targetFeature != Types.f_ERROR);
Expand Down Expand Up @@ -719,7 +717,6 @@ private boolean loadCalledFeatureUnlessTargetVoid(Resolution res, AbstractFeatur
if (_calledFeature == null)
{ // nothing found, try if we can build a chained bool: `a < b < c` => `(a < b) && (a < c)`
resolveTypesOfActuals(res,thiz);
actualsResolved = true;
findChainedBooleans(res, thiz);
}
// !isInheritanceCall: see issue #2153
Expand All @@ -731,10 +728,7 @@ private boolean loadCalledFeatureUnlessTargetVoid(Resolution res, AbstractFeatur
{
_actuals = new List<>();
}
if (_calledFeature != null && !actualsResolved)
{
resolveTypesOfActuals(res,thiz);
}
resolveTypesOfActuals(res,thiz);

if (POSTCONDITIONS) ensure
(Errors.any() || !calledFeatureKnown() || calledFeature() != Types.f_ERROR || targetVoid,
Expand Down Expand Up @@ -917,27 +911,51 @@ private List<FeatureAndOuter> hiddenCandidates(Resolution res, AbstractFeature t
}


/**
* Field used to detect and avoid repeated calls to resolveTypesOfActuals for
* the same outer feature. Moving the call into a lambda or a lazy value will
* change its outer feature and resolution of actuals will have to be
* repeated.
*/
protected AbstractFeature _actualsResolvedFor;


/**
* Resolve types of actual arguments for given outer features. This may be
* called repeatedly with different outer arguments as a result of this call
* being moved into a different feature (lambda, lazy, etc.).
*
* @param re the resolution instance
*
* @parem outer the outer feature we are resolving types against.
*/
private void resolveTypesOfActuals(Resolution res, AbstractFeature outer)
{
// NYI: check why _actuals.listIterator cannot be done inside
// whenResolvedTypes. If it could, the 'if calledFeature != null / Error
// would not be needed.
ListIterator<Expr> i = _actuals.listIterator(); // _actuals can change during resolveTypes, so create iterator early
outer.whenResolvedTypes
(() ->
{
while (i.hasNext())
if (_actualsResolvedFor != outer)
{
_actualsResolvedFor = outer;

// NYI: check why _actuals.listIterator cannot be done inside
// whenResolvedTypes. If it could, the 'if calledFeature != null / Error
// would not be needed.
ListIterator<Expr> i = _actuals.listIterator(); // _actuals can change during resolveTypes, so create iterator early
outer.whenResolvedTypes
(() ->
{
var a = i.next();
if (_calledFeature != null && _calledFeature != Types.f_ERROR)
while (_actualsResolvedFor == outer && // Abandon resolution of outer changed.
i.hasNext())
{
var a1 = res.resolveType(a, outer);
if (CHECKS) check
(a1 != null);
i.set(a1);
var a = i.next();
if (_calledFeature != null && _calledFeature != Types.f_ERROR)
{
var a1 = res.resolveType(a, outer);
if (CHECKS) check
(a1 != null);
i.set(a1);
}
}
}
});
});
}
}


Expand Down Expand Up @@ -979,6 +997,7 @@ private void findOperatorOnOuter(Resolution res, AbstractFeature thiz)
if (foa != null)
{
_calledFeature = foa._feature;
_resolvedFormalArgumentTypes = null;
_pendingError = null;
var newActuals = new List<>(_target);
newActuals.addAll(_actuals);
Expand Down Expand Up @@ -1340,17 +1359,20 @@ private void addToResolvedFormalArgumentTypes(Resolution res, int argnum, Abstra
*/
private void resolveFormalArgumentTypes(Resolution res)
{
var fargs = _calledFeature.valueArguments();
_resolvedFormalArgumentTypes = fargs.size() == 0 ? UnresolvedType.NO_TYPES
: new AbstractType[fargs.size()];
Arrays.fill(_resolvedFormalArgumentTypes, Types.t_UNDEFINED);
int count = 0;
for (var frml : fargs)
if (_resolvedFormalArgumentTypes == null)
{
int argnum = count; // effectively final copy of count
frml.whenResolvedTypes
(() -> resolveFormalArg(res, argnum, frml));
count++;
var fargs = _calledFeature.valueArguments();
_resolvedFormalArgumentTypes = fargs.size() == 0 ? UnresolvedType.NO_TYPES
: new AbstractType[fargs.size()];
Arrays.fill(_resolvedFormalArgumentTypes, Types.t_UNDEFINED);
int count = 0;
for (var frml : fargs)
{
int argnum = count; // effectively final copy of count
frml.whenResolvedTypes
(() -> resolveFormalArg(res, argnum, frml));
count++;
}
}
if (POSTCONDITIONS) ensure
(_resolvedFormalArgumentTypes != null);
Expand Down Expand Up @@ -2228,6 +2250,7 @@ public void tryResolveTypeCall(Resolution res, AbstractFeature thiz)
{
// we found a feature that fits a dot-type-call.
_calledFeature = f;
_resolvedFormalArgumentTypes = null;
_target = new DotType(_pos, _target.asParsedType()).resolveTypes(res, thiz);
}
}
Expand All @@ -2238,10 +2261,6 @@ public void tryResolveTypeCall(Resolution res, AbstractFeature thiz)
{
splitOffTypeArgs(res, f, thiz);
}
if (_calledFeature != null)
{
resolveTypesOfActuals(res,thiz);
}
}
}
}
Expand Down Expand Up @@ -2364,6 +2383,8 @@ else if (t != null)
}
}

resolveTypesOfActuals(res, outer);

if (POSTCONDITIONS) ensure
(_pendingError != null || Errors.any() || result.typeForInferencing() != Types.t_ERROR);

Expand Down Expand Up @@ -2632,7 +2653,7 @@ private Expr newIf(Expr cc, Expr block, Expr elseBlock)
*
* @return a new Expr to replace this call or this if it remains unchanged.
*/
Expr resolveSyntacticSugar(Resolution res, AbstractFeature outer)
Expr resolveSyntacticSugar1(Resolution res, AbstractFeature outer)
{
Expr result = this;
// must not be inheritance call since we do not want `: i32 2` turned into a numeric literal.
Expand Down
5 changes: 1 addition & 4 deletions src/dev/flang/ast/Destructure.java
Original file line number Diff line number Diff line change
Expand Up @@ -268,10 +268,7 @@ else if (t != Types.t_ERROR)
FuzionConstants.DESTRUCTURE_PREFIX + id++,
outer);
tmp.scheduleForResolution(res);
exprs.add(tmp.resolveTypes(res, outer));
Assign atmp = new Assign(res, pos(), tmp, _value, outer);
atmp.resolveTypes(res, outer);
exprs.add(atmp);
exprs.add(new Assign(res, pos(), tmp, _value, outer));
var names = _names.iterator();
var fields = _fields.iterator();
List<String> fieldNames = new List<>();
Expand Down
102 changes: 55 additions & 47 deletions src/dev/flang/ast/Feature.java
Original file line number Diff line number Diff line change
Expand Up @@ -1375,8 +1375,6 @@ static class ResolveTypes extends FeatureVisitor
@Override public Call action (Call c, AbstractFeature outer) { return c.resolveTypes (res, outer); }
@Override public Expr action (DotType d, AbstractFeature outer) { return d.resolveTypes (res, outer); }
@Override public Expr action (Destructure d, AbstractFeature outer) { return d.resolveTypes (res, outer); }
@Override public Expr action (Feature f, AbstractFeature outer) { /* use f.outer() since qualified feature name may result in different outer! */
return f.resolveTypes (res, f.outer() ); }
@Override public Function action (Function f, AbstractFeature outer) { f.resolveTypes (res, outer); return f; }
@Override public void action (Match m, AbstractFeature outer) { m.resolveTypes (res, outer); }
@Override public Expr action (This t, AbstractFeature outer) { return t.resolveTypes (res, outer); }
Expand Down Expand Up @@ -1490,10 +1488,7 @@ void resolveSyntacticSugar1(Resolution res)
{
typeFeature(res);
}
visit(new FeatureVisitor()
{
public Expr action(Call c, AbstractFeature outer) { return c.resolveSyntacticSugar(res, outer); }
});
visit(res._resolveSyntaxSugar1);

_state = State.RESOLVED_SUGAR1;
res.scheduleForTypeInference(this);
Expand Down Expand Up @@ -2015,62 +2010,75 @@ public Expr visit(FeatureVisitor v, AbstractFeature outer)


/**
* determine the static type of all expressions and declared features in this feature
* resolve syntactic sugar of feature declaration, i.e., add assignment for the
* initial value of fields.
*
* @param res this is called during type resolution, res gives the resolution
* instance.
* @param res the resolution instance.
*
* @param outer the root feature that contains this expression.
* @param outer the root feature that contains this feature declaration.
*/
public Expr resolveTypes(Resolution res, AbstractFeature outer)
public Expr resolveSyntacticSugar1(Resolution res, AbstractFeature outer)
{
if (PRECONDITIONS) require
(res != null,
outer.state() == State.RESOLVING_SUGAR1,
isUniverse() || outer != null || Errors.any());

Expr result = this;

if (CHECKS) check
(this.outer() == outer,
Errors.any() ||
(_impl._kind != Impl.Kind.FieldDef &&
_impl._kind != Impl.Kind.FieldActual)
|| _returnType == NoType.INSTANCE);
(Errors.any() ||
(_impl._kind != Impl.Kind.FieldDef &&
_impl._kind != Impl.Kind.FieldActual)
|| _returnType == NoType.INSTANCE);

if (_impl.hasInitialValue())
{
/* add assignment of initial value: */
result = new Block
(new List<>
(this,
new Assign(res, _pos, this, _impl.expr(), outer)
{
public AbstractAssign visit(FeatureVisitor v, AbstractFeature outer)
// outer() != outer may be the case for fields declared in types
//
// type.f := x
//
// or for qualified declarations
//
// String.new_field := 3
//
// which should have caused errors already.
if (CHECKS) check
(Errors.any() || this.outer() == outer);

if (this.outer() == outer)
{
/* add assignment of initial value: */
AbstractAssign ass = new Assign(res, _pos, this, _impl.expr(), outer)
{
/* During findFieldDefInScope, we check field uses in impl, but
* we have to avoid doing this again in this assignment since a declaration
*
* x := 3
* x := x + 1
*
* is converted into
*
* Feature x with impl kind FieldDef, initialvalue 3
* x := 3
* Feature x with impl kind FieldDef, initialvalue x + 1
* x := x + 1
*
* so the second assignment would find the second x, which is
* wrong.
*
* Alternatively, we could add this assignment in a later phase.
*/
return v.visitAssignFromFieldImpl()
? super.visit(v, outer)
: this;
}
}
));
public AbstractAssign visit(FeatureVisitor v, AbstractFeature outer)
{
/* During findFieldDefInScope, we check field uses in impl, but
* we have to avoid doing this again in this assignment since a declaration
*
* x := 3
* x := x + 1
*
* is converted into
*
* Feature x with impl kind FieldDef, initialvalue 3
* x := 3
* Feature x with impl kind FieldDef, initialvalue x + 1
* x := x + 1
*
* so the second assignment would find the second x, which is
* wrong.
*
* Alternatively, we could add this assignment in a later phase.
*/
return v.visitAssignFromFieldImpl()
? super.visit(v, outer)
: this;
}
};
ass = ass.visit(res._resolveSyntaxSugar1, outer);
result = new Block(new List<>(this, ass));
}
}
return result;
}
Expand Down
6 changes: 3 additions & 3 deletions src/dev/flang/ast/Function.java
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ public boolean isLambdaCall()
var inheritsName =
(t.feature() == Types.resolved.f_Unary && gs.size() == 2) ? Types.UNARY_NAME :
(t.feature() == Types.resolved.f_Lazy && gs.size() == 1) ? Types.LAZY_NAME
: Types.FUNCTION_NAME;
: Types.FUNCTION_NAME;

// inherits clause for wrapper feature: Function<R,A,B,C,...>
_inheritsCall = new Call(pos(), null, inheritsName);
Expand All @@ -321,10 +321,10 @@ public boolean isLambdaCall()
Contract.EMPTY_CONTRACT,
new Impl(pos(), new Block(expressions), Impl.Kind.Routine));
res._module.findDeclarations(_wrapper, outer);
res.resolveDeclarations(_wrapper);
res.resolveTypes(_feature);
if (inferResultType)
{
res.resolveDeclarations(_wrapper);
res.resolveTypes(_feature);
result = _feature.resultType();
_inheritsCall._generics = gs.setOrClone(0, result);
}
Expand Down
4 changes: 3 additions & 1 deletion src/dev/flang/ast/ParsedCall.java
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ protected void findChainedBooleans(Resolution res, AbstractFeature thiz)
new Block(new List<Expr>(as, t1)));
_actuals = new List<Expr>(result);
_calledFeature = Types.resolved.f_bool_AND;
_resolvedFormalArgumentTypes = null; // _calledFeature changed, so formal arg types must be resolved again
_pendingError = null;
_name = _calledFeature.featureName().baseName();
}
Expand Down Expand Up @@ -502,8 +503,9 @@ public Expr applyPartially(Resolution res, AbstractFeature outer, AbstractType t
{
_name = nn;
_calledFeature = null;
_pendingError = null;
}
_resolvedFormalArgumentTypes = null;
_pendingError = null;
var fn = new Function(pos(),
pns,
this)
Expand Down
12 changes: 12 additions & 0 deletions src/dev/flang/ast/Resolution.java
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,18 @@ public class Resolution extends ANY
FeatureVisitor resolveTypesFully = new Feature.ResolveTypes(this);


/**
* FeatureVisitor to call resolveSyntacticSugar1() on Calls and Features.
*
* This is used during state RESOLVING_SUGAR1
*/
FeatureVisitor _resolveSyntaxSugar1 = new FeatureVisitor()
{
public Expr action(Feature f, AbstractFeature outer) { return f.resolveSyntacticSugar1(Resolution.this, outer); }
public Expr action(Call c, AbstractFeature outer) { return c.resolveSyntacticSugar1(Resolution.this, outer); }
};


final FuzionOptions _options;


Expand Down
Loading