-
Notifications
You must be signed in to change notification settings - Fork 323
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Convert ShadowedPatternFields to Java
- Loading branch information
Showing
6 changed files
with
203 additions
and
167 deletions.
There are no files selected for viewing
187 changes: 187 additions & 0 deletions
187
engine/runtime-compiler/src/main/java/org/enso/compiler/pass/lint/ShadowedPatternFields.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
package org.enso.compiler.pass.lint; | ||
|
||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import org.enso.compiler.context.InlineContext; | ||
import org.enso.compiler.context.ModuleContext; | ||
import org.enso.compiler.core.CompilerError; | ||
import org.enso.compiler.core.IR; | ||
import org.enso.compiler.core.ir.Expression; | ||
import org.enso.compiler.core.ir.MetadataStorage; | ||
import org.enso.compiler.core.ir.Name; | ||
import org.enso.compiler.core.ir.Pattern; | ||
import org.enso.compiler.core.ir.expression.Case; | ||
import org.enso.compiler.core.ir.expression.Case.Branch; | ||
import org.enso.compiler.core.ir.expression.warnings.Shadowed.PatternBinding; | ||
import org.enso.compiler.pass.IRProcessingPass; | ||
import org.enso.compiler.pass.MiniIRPass; | ||
import org.enso.compiler.pass.MiniPassFactory; | ||
import org.enso.compiler.pass.analyse.AliasAnalysis$; | ||
import org.enso.compiler.pass.analyse.DataflowAnalysis$; | ||
import org.enso.compiler.pass.analyse.DemandAnalysis$; | ||
import org.enso.compiler.pass.analyse.TailCall; | ||
import org.enso.compiler.pass.desugar.GenerateMethodBodies$; | ||
import org.enso.compiler.pass.desugar.NestedPatternMatch$; | ||
import org.enso.compiler.pass.resolve.IgnoredBindings$; | ||
import scala.collection.immutable.List; | ||
import scala.collection.immutable.Seq; | ||
import scala.jdk.javaapi.CollectionConverters; | ||
|
||
/** | ||
* This pass detects and renames shadowed pattern fields. | ||
* | ||
* <p>This is necessary both in order to create a warning, but also to ensure that alias analysis | ||
* doesn't get confused. | ||
* | ||
* <p>This pass requires no configuration. | ||
* | ||
* <p>This pass requires the context to provide: | ||
* | ||
* <p>- Nothing | ||
*/ | ||
public final class ShadowedPatternFields implements MiniPassFactory { | ||
public static final ShadowedPatternFields INSTANCE = new ShadowedPatternFields(); | ||
|
||
private ShadowedPatternFields() {} | ||
|
||
@Override | ||
public List<IRProcessingPass> precursorPasses() { | ||
java.util.List<IRProcessingPass> list = java.util.List.of(GenerateMethodBodies$.MODULE$); | ||
return CollectionConverters.asScala(list).toList(); | ||
} | ||
|
||
@Override | ||
public List<IRProcessingPass> invalidatedPasses() { | ||
java.util.List<IRProcessingPass> list = | ||
java.util.List.of( | ||
AliasAnalysis$.MODULE$, | ||
DataflowAnalysis$.MODULE$, | ||
DemandAnalysis$.MODULE$, | ||
IgnoredBindings$.MODULE$, | ||
NestedPatternMatch$.MODULE$, | ||
TailCall.INSTANCE); | ||
return CollectionConverters.asScala(list).toList(); | ||
} | ||
|
||
@Override | ||
public MiniIRPass createForModuleCompilation(ModuleContext moduleContext) { | ||
return new Mini(); | ||
} | ||
|
||
@Override | ||
public MiniIRPass createForInlineCompilation(InlineContext inlineContext) { | ||
return new Mini(); | ||
} | ||
|
||
private static final class Mini extends MiniIRPass { | ||
@Override | ||
@SuppressWarnings("unchecked") | ||
public Expression transformExpression(Expression expr) { | ||
return switch (expr) { | ||
case Case.Branch branch -> lintCaseBranch(branch); | ||
case Case.Expr caseExpr -> { | ||
Seq<Branch> newBranches = caseExpr.branches().map(this::lintCaseBranch).toSeq(); | ||
yield caseExpr.copy( | ||
caseExpr.scrutinee(), | ||
newBranches, | ||
caseExpr.isNested(), | ||
caseExpr.location(), | ||
caseExpr.passData(), | ||
caseExpr.diagnostics(), | ||
caseExpr.id()); | ||
} | ||
default -> expr; | ||
}; | ||
} | ||
|
||
/** | ||
* Lints for shadowed pattern variables in a case branch. | ||
* | ||
* @param branch the case branch to lint | ||
* @return `branch`, with warnings for any shadowed pattern variables | ||
*/ | ||
private Case.Branch lintCaseBranch(Case.Branch branch) { | ||
var newPattern = lintPattern(branch.pattern()); | ||
return branch.copy( | ||
newPattern, | ||
branch.expression(), | ||
branch.terminalBranch(), | ||
branch.location(), | ||
branch.passData(), | ||
branch.diagnostics(), | ||
branch.id()); | ||
} | ||
|
||
/** | ||
* Lints a pattern for shadowed pattern variables. | ||
* | ||
* <p>A later pattern variable shadows an earlier pattern variable with the same name. | ||
* | ||
* @param pattern the pattern to lint | ||
* @return `pattern`, with a warning applied to any shadowed pattern variables | ||
*/ | ||
private Pattern lintPattern(Pattern pattern) { | ||
var seenNames = new HashSet<String>(); | ||
var lastSeen = new HashMap<String, IR>(); | ||
|
||
return go(pattern, seenNames, lastSeen); | ||
} | ||
|
||
private Pattern go(Pattern pattern, Set<String> seenNames, Map<String, IR> lastSeen) { | ||
return switch (pattern) { | ||
case Pattern.Name named -> { | ||
var name = named.name().name(); | ||
if (seenNames.contains(name)) { | ||
var warning = new PatternBinding(name, lastSeen.get(name), named.identifiedLocation()); | ||
lastSeen.put(name, named); | ||
var blank = new Name.Blank(named.identifiedLocation(), new MetadataStorage()); | ||
var patternCopy = named.copyWithName(blank); | ||
patternCopy.getDiagnostics().add(warning); | ||
yield patternCopy; | ||
} else if (!(named.name() instanceof Name.Blank)) { | ||
lastSeen.put(name, named); | ||
seenNames.add(name); | ||
yield named; | ||
} else { | ||
yield named; | ||
} | ||
} | ||
case Pattern.Constructor cons -> { | ||
var newFields = | ||
cons.fields().reverse().map(field -> go(field, seenNames, lastSeen)).reverse(); | ||
yield cons.copyWithFields(newFields); | ||
} | ||
case Pattern.Literal literal -> literal; | ||
case Pattern.Type typed -> { | ||
var name = typed.name().name(); | ||
if (seenNames.contains(name)) { | ||
var warning = new PatternBinding(name, lastSeen.get(name), typed.identifiedLocation()); | ||
lastSeen.put(name, typed); | ||
var blank = new Name.Blank(typed.identifiedLocation(), new MetadataStorage()); | ||
var typedCopy = | ||
typed.copy( | ||
blank, | ||
typed.tpe(), | ||
typed.location(), | ||
typed.passData(), | ||
typed.diagnostics(), | ||
typed.id()); | ||
typedCopy.getDiagnostics().add(warning); | ||
yield typedCopy; | ||
} else if (!(typed.name() instanceof Name.Blank)) { | ||
lastSeen.put(name, typed); | ||
seenNames.add(name); | ||
yield typed; | ||
} else { | ||
yield typed; | ||
} | ||
} | ||
case Pattern.Documentation doc -> throw new CompilerError( | ||
"Branch documentation should be desugared at an earlier stage."); | ||
default -> pattern; | ||
}; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
163 changes: 0 additions & 163 deletions
163
...e/runtime-compiler/src/main/scala/org/enso/compiler/pass/lint/ShadowedPatternFields.scala
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.