Skip to content

Commit

Permalink
resolve: update references collection logic
Browse files Browse the repository at this point in the history
  • Loading branch information
mio-19 committed Dec 27, 2024
1 parent e236970 commit 621d098
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 5 deletions.
4 changes: 3 additions & 1 deletion base/src/main/java/org/aya/resolve/visitor/ExprResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -260,11 +260,13 @@ private void addReference(@NotNull DefVar<?, ?> defVar) {
private void introduceDependencies(@NotNull GeneralizedVar var) {
if (allowedGeneralizes.containsKey(var)) return;

// Introduce dependencies first
var dependencies = collector.getDependencies(var);
for (var dep : dependencies) {
introduceDependencies(dep);
}

// Now introduce the variable itself
var owner = var.owner;
assert owner != null : "GeneralizedVar owner should not be null";
var param = owner.toExpr(false, var.toLocal());
Expand All @@ -275,11 +277,11 @@ private void introduceDependencies(@NotNull GeneralizedVar var) {
public @NotNull AnyVar resolve(@NotNull QualifiedID name) {
var result = ctx.get(name);
if (result instanceof GeneralizedVar gvar) {
// Ensure all dependencies are introduced
introduceDependencies(gvar);
var gened = allowedGeneralizes.getOrNull(gvar);
if (gened != null) return gened.ref();
}

return result;
}

Expand Down
49 changes: 48 additions & 1 deletion base/src/main/java/org/aya/resolve/visitor/StmtResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import kala.collection.SeqView;
import kala.collection.immutable.ImmutableSeq;
import kala.collection.mutable.MutableList;
import kala.value.MutableValue;
import org.aya.generic.stmt.TyckOrder;
import org.aya.generic.stmt.TyckUnit;
Expand Down Expand Up @@ -39,8 +40,54 @@ static void resolveStmt(@NotNull ResolvingStmt stmt, @NotNull ResolveInfo info)
case ResolvingStmt.GenStmt(var variables) -> {
var resolver = new ExprResolver(info.thisModule(), false);
resolver.enter(Where.Head);

// First pass: register all variables to detect cycles
for (var variable : variables.variables) {
resolver.collector().registerVariable(variable);
}

// Second pass: handle dependencies and references
var ownerRefs = MutableList.<TyckOrder>create();
for (var variable : variables.variables) {
var owner = variable.owner;
assert owner != null : "GeneralizedVar owner should not be null";

// Add to allowedGeneralizes
var param = owner.toExpr(false, variable.toLocal());
resolver.allowedGeneralizes().put(variable, param);

// Collect owner reference if it's a TyckUnit
if (owner instanceof TyckUnit unit) {
var ref = new TyckOrder.Head(unit);
if (!ownerRefs.contains(ref)) ownerRefs.append(ref);
}

// Handle dependencies
var deps = resolver.collector().getDependencies(variable);
for (var dep : deps) {
if (!resolver.allowedGeneralizes().containsKey(dep)) {
var depOwner = dep.owner;
assert depOwner != null : "GeneralizedVar owner should not be null";
var depParam = depOwner.toExpr(false, dep.toLocal());
resolver.allowedGeneralizes().put(dep, depParam);
}

// Add dependency owner reference if it's a TyckUnit
if (dep.owner instanceof TyckUnit depUnit) {
var ref = new TyckOrder.Head(depUnit);
if (!ownerRefs.contains(ref)) ownerRefs.append(ref);
}
}
}

// Add collected references to resolver
ownerRefs.forEach(resolver.reference()::append);

// Process the statement itself
variables.descentInPlace(resolver, (_, p) -> p);
addReferences(info, new TyckOrder.Head(variables), resolver);

// Do not add the GenStmt itself to TyckOrder as it's not a TyckUnit
// So we skip calling addReferences for the GenStmt
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,36 @@
import java.util.HashMap;
import java.util.Map;

/**
* Collects dependency information for generalized variables using DFS on their types.
*
* 1. A variable's type may reference other generalized variables; we record those as dependencies.
* 2. If we revisit a variable already on the DFS stack ("visiting" set), that indicates
* a cyclic dependency, and we report an error.
* 3. Once a variable is fully processed, it goes into the "visited" set; future registrations
* of the same variable skip repeated traversal.
*
* Pitfalls & Notes:
* - A single variable (e.g. “A”) should be registered once, to avoid duplication.
* - Attempting to re-scan or re-introduce “A” in another variable’s context can cause
* confusion or potential cycles. So we do all dependency scans here, at declaration time.
* - Any reference to a variable out of scope is handled as an error in the resolver
* if it’s not in the allowedGeneralizes map.
*/
public final class VariableDependencyCollector {
private final Map<GeneralizedVar, ImmutableSeq<GeneralizedVar>> dependencies = new HashMap<>();
private final Reporter reporter;
private final MutableSet<GeneralizedVar> visiting = MutableSet.create();
private final MutableSet<GeneralizedVar> visited = MutableSet.create();

public VariableDependencyCollector(Reporter reporter) {
this.reporter = reporter;
}

public void registerVariable(GeneralizedVar var) {
if (dependencies.containsKey(var)) return;
if (visited.contains(var)) return;

// If var is already being visited, we found a cycle.
// If var is already being visited in current DFS path, we found a cycle
if (!visiting.add(var)) {
reporter.report(new CyclicDependencyError(var.sourcePos(), var));
throw new Context.ResolvingInterruptedException();
Expand All @@ -40,7 +57,11 @@ public void registerVariable(GeneralizedVar var) {
visiting.remove(var);

// Recursively register dependencies
for (var dep : deps) registerVariable(dep);
for (var dep : deps) {
registerVariable(dep);
}

visited.add(var);
}

public ImmutableSeq<GeneralizedVar> getDependencies(GeneralizedVar var) {
Expand Down

0 comments on commit 621d098

Please sign in to comment.