diff options
Diffstat (limited to 'sources/scalac')
81 files changed, 23138 insertions, 0 deletions
diff --git a/sources/scalac/ApplicationError.java b/sources/scalac/ApplicationError.java new file mode 100644 index 0000000000..e689217eec --- /dev/null +++ b/sources/scalac/ApplicationError.java @@ -0,0 +1,87 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac; + +import java.io.PrintStream; +import java.io.PrintWriter; + +import scalac.util.Debug; + +public class ApplicationError extends Error { + + //######################################################################## + // Private state + + private final Throwable cause; // !!! remove and use API 1.4 + + //######################################################################## + // Private interface + + private static String combine(String message, Object object) { + String string = Debug.show(object); + return message == null ? string : message + ": " + string; + } + + //######################################################################## + // ApplicationError constructors + + public ApplicationError() { + this((String)null, (Throwable)null); + } + + public ApplicationError(String message) { + this(message, (Throwable)null); + } + + public ApplicationError(Object object) { + this(null, object, null); + } + + public ApplicationError(Throwable cause) { + this((String)null, cause); + } + + public ApplicationError(String message, Object object) { + this(message, object, null); + } + + public ApplicationError(String message, Throwable cause) { + super(message); + this.cause = cause; + } + + public ApplicationError(Object object, Throwable cause) { + this(null, object, cause); + } + + public ApplicationError(String message, Object object, Throwable cause) { + this(combine(message, object), cause); + } + + //######################################################################## + // Throwable interface + + public void printStackTrace(PrintStream printer) { + super.printStackTrace(printer); + if (cause != null) { + printer.print("Caused by: "); + cause.printStackTrace(printer); + } + } + + public void printStackTrace(PrintWriter printer) { + super.printStackTrace(printer); + if (cause != null) { + printer.print("Caused by: "); + cause.printStackTrace(printer); + } + } + + //######################################################################## +} diff --git a/sources/scalac/CompilerCommand.java b/sources/scalac/CompilerCommand.java new file mode 100644 index 0000000000..743187e81f --- /dev/null +++ b/sources/scalac/CompilerCommand.java @@ -0,0 +1,221 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scalac; + +import java.util.List; +import java.util.ArrayList; + +import scalac.util.ClassPath; +import scalac.util.Reporter; +import scalac.util.CommandParser; +import scalac.util.ArgumentParser; +import scalac.util.OptionParser; +import scalac.util.BooleanOptionParser; +import scalac.util.StringOptionParser; +import scalac.util.ChoiceOptionParser; +import scalac.util.HelpOptionParser; +import scalac.util.VersionOptionParser; +//import scalac.util.OptimizeOptionParser; +import scalac.util.PhaseSetOptionParser; +import scalac.util.PrintOptionParser; +import scalac.util.UnknownOptionParser; +import scalac.util.ScalaFileArgumentParser; +import scalac.util.UnknownArgumentParser; +import scalac.util.Strings; + +public class CompilerCommand extends CommandParser { + + public final PhaseRepository phases; + + public final BooleanOptionParser nowarn; + public final BooleanOptionParser verbose; + public final BooleanOptionParser debug; + public final BooleanOptionParser uniqid; + public final BooleanOptionParser types; + public final BooleanOptionParser prompt; + //public final OptimizeOptionParser optimize; + public final StringOptionParser classpath; + public final StringOptionParser sourcepath; + public final StringOptionParser bootclasspath; + public final StringOptionParser extdirs; + public final StringOptionParser outpath; + public final ChoiceOptionParser target; + public final BooleanOptionParser noimports; + public final StringOptionParser jaco; + public final PhaseSetOptionParser skip; + public final PhaseSetOptionParser check; + public final PrintOptionParser print; + public final ChoiceOptionParser printer; + public final StringOptionParser printfile; + public final PhaseSetOptionParser graph; + public final PhaseSetOptionParser stop; + public final PhaseSetOptionParser log; + public final VersionOptionParser version; + public final HelpOptionParser help; + public final UnknownOptionParser unknown_options; + public final ScalaFileArgumentParser files; + public final UnknownArgumentParser unknown_arguments; + + public CompilerCommand(String product, String version, + Reporter reporter, PhaseRepository phases) + { + this(product, version, "<source files>", reporter, phases); + } + + protected CompilerCommand(String product, String version, String syntax, + Reporter reporter, PhaseRepository phases) + { + super(product, version, syntax, reporter); + this.phases = phases; + + ArgumentParser[] parsers = new ArgumentParser[] { + + this.nowarn = new BooleanOptionParser(this, + "nowarn", "Generate no warnings", + false), + + this.verbose = new BooleanOptionParser(this, + "verbose", "Output messages about what the compiler is doing", + false), + + this.debug = new BooleanOptionParser(this, + "debug", "Output debugging messages", + false), + + this.uniqid = new BooleanOptionParser(this, + "uniqid", "Print identifiers with unique names (debugging option)", + false), + + this.types = new BooleanOptionParser(this, + "types", "Print tree types (debugging option)", + false), + + this.prompt = new BooleanOptionParser(this, + "prompt", "Display a prompt after each error (debugging option)", + false), + + //this.optimize = new OptimizeOptionParser(this, + // "optimize", "optimize bytecode (-optimize:help for option list)", + // null /* todo: uncomment: phases.OPTIMIZE */), + + this.classpath = new StringOptionParser(this, + "classpath", "Specify where to find user class files", + "path", ClassPath.CLASS_PATH), + + this.sourcepath = new StringOptionParser(this, + "sourcepath", "Specify where to find input source files", + "path", ClassPath.SOURCE_PATH), + + this.bootclasspath = new StringOptionParser(this, + "bootclasspath", "Override location of bootstrap class files", + "path", ClassPath.BOOT_PATH), + + this.extdirs = new StringOptionParser(this, + "extdirs", "Override location of installed extensions", + "dirs", ClassPath.EXTENSION_PATH), + + this.outpath = new StringOptionParser(this, + "d", "Specify where to place generated class files", + "directory", "."), + + this.target = new ChoiceOptionParser(this, + "target", "Specify which bakend to use", + "target", Global.TARGETS, Global.TARGET_JVM), + + this.noimports = new BooleanOptionParser(this, + "noimports", "Compile without any implicit imports", + false), + + this.jaco = new StringOptionParser(this, + "jaco", "Pass argument to jaco backend (debugging option)", + "argument", null), + + this.skip = new PhaseSetOptionParser(this, + "skip", "Skip <phases> (see below)", + phases.phases, PhaseDescriptor.SKIP), + + this.check = new PhaseSetOptionParser(this, + "check", "Check the tree after <phases> (see below)", + phases.phases, PhaseDescriptor.CHECK), + + this.print = new PrintOptionParser(this, + "print", "Print out program after <phases> (see below)", + phases.phases, PhaseDescriptor.PRINT), + + this.printer = new ChoiceOptionParser(this, + "printer", "Printer to use", + "printer", Global.PRINTERS, Global.PRINTER_TEXT), + + this.printfile = new StringOptionParser(this, + "print-file", "Specify file in which to print trees", + "file", "-"), + + this.graph = new PhaseSetOptionParser(this, + "graph", "Graph the program after <phases> (see below)", + phases.phases, PhaseDescriptor.GRAPH), + + this.stop = new PhaseSetOptionParser(this, + "stop", "Stop after first phase in <phases> (see below)", + phases.phases, PhaseDescriptor.STOP), + + this.log = new PhaseSetOptionParser(this, + "log", "Log operations in <phases> (see below)", + phases.phases, PhaseDescriptor.LOG), + + this.version = new VersionOptionParser(this, + "version", "Print product version and exit", + product() + " " + version() + " -- (c) 2002 LAMP/EPFL"), + + this.help = new HelpOptionParser(this, + "help", "Print a synopsis of standard options"), + + this.unknown_options = new UnknownOptionParser(this), + + this.files = new ScalaFileArgumentParser(this), + + this.unknown_arguments = new UnknownArgumentParser(this), + + }; + + for (int i = 0; i < parsers.length; i++) add(parsers[i]); + } + + public boolean parse(String[] args) { + boolean result = super.parse(args); + reporter().nowarn = nowarn.value; + reporter().verbose = verbose.value; + reporter().prompt = prompt.value; + return result; + } + + public String getHelpMessage() { + StringBuffer buffer = new StringBuffer(super.getHelpMessage()); + buffer.append(Strings.EOL); + buffer.append("and possible compilation phases include:"); + buffer.append(Strings.EOL); + List lines = new ArrayList(phases.phases.length); + for (int i = 0; i < phases.phases.length; i++) { + PhaseDescriptor phase = phases.phases[i]; + lines.add(" " + phase.name() + "\t " + phase.description()); + } + lines.add(" " + "all" + "\t " + "matches all phases"); + buffer.append(Strings.format(lines)); + return buffer.toString(); + } + + public ClassPath classpath() { + return new ClassPath(classpath.value, sourcepath.value, + bootclasspath.value, extdirs.value); + } + + public String outpath() { + return outpath.value + (outpath.value.endsWith("/") ? "" : "/"); + } + +} diff --git a/sources/scalac/Global.java b/sources/scalac/Global.java new file mode 100644 index 0000000000..3c3d73c0e6 --- /dev/null +++ b/sources/scalac/Global.java @@ -0,0 +1,354 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac; + +import java.io.*; +import java.util.*; +import scalac.util.*; +import scalac.ast.*; +import scalac.ast.parser.*; +import scalac.symtab.Definitions; +import scalac.ast.printer.*; + + +/** The global environment of a compiler run + * + * @author Matthias Zenger + * @version 1.0 + */ +public class Global { + + public static Global instance; + + /** global options + */ + public final boolean noimports; + //public final boolean optimize; + public final boolean debug; + public final boolean uniqid; + + public final boolean printtypes; + public final boolean printtokens; + + public final String outpath; + public final String target; + + public final String jacoDebug; + + /** the message reporter + */ + public final Reporter reporter; + + /** a stack for maintaining timestamps + */ + private final Stack startTimes = new Stack(); + + /** all compilation units + */ + public Unit[] units; + + /** the class path + */ + public final ClassPath classPath; + + /** the global tree factory + */ + public final TreeFactory make; + + /** the fresh name creator + */ + public final FreshNameCreator freshNameCreator; + + /** the tree generator + */ + public final TreeGen treeGen; + + /** the unique-id generator + */ + public final UniqueID uniqueID; + + /** the global tree printer + */ + public final TreePrinter printer; + public OutputStream printStream; + public final TreePrinter debugPrinter; + + /** the current phase + */ + public PhaseDescriptor currentPhase; + + /** the global definitions + */ + public Definitions definitions; + + /** the global primitives + */ + //public Primitives primitives; + + /** compilation phases. + */ + public final PhaseRepository PHASE; + public final PhaseDescriptor[] phases; + + public final int POST_ANALYZER_PHASE_ID = 3; + + /** compilation targets + */ + public static final String TARGET_INT; + public static final String TARGET_JAVA; + public static final String TARGET_JVM; + public static final String TARGET_MSIL; + + public static final String[] TARGETS = new String[] { + TARGET_INT = "int".intern(), + TARGET_JAVA = "java".intern(), + TARGET_JVM = "jvm".intern(), + TARGET_MSIL = "msil".intern(), + }; + + /** tree printers + */ + public static final String PRINTER_TEXT; + public static final String PRINTER_HTML; + + public static final String[] PRINTERS = new String[] { + PRINTER_TEXT = "text".intern(), + PRINTER_HTML = "html".intern(), + }; + + public Global(CompilerCommand args) { + this(args, false); + } + + public Global(CompilerCommand args, boolean interpret) { + if (Global.instance != null) { // jaco bug: can't use assert here + new Error("Duplicate creation of Global").printStackTrace(); + System.exit(1); + }; + Global.instance = this; + this.reporter = args.reporter(); + this.start(); // timestamp to compute the total time + this.noimports = args.noimports.value; + //this.optimize = args.optimize.optimize; + this.debug = args.debug.value; + this.uniqid = args.uniqid.value; + this.printtypes = args.types.value; + this.printtokens = args.print.tokens; + this.classPath = args.classpath(); + this.outpath = args.outpath(); + this.target = interpret ? TARGET_INT : args.target.value.intern(); + this.jacoDebug = args.jaco.value; + this.uniqueID = new UniqueID(); + String printFile = args.printfile.value; + try { + this.printStream = "-".equals(printFile) + ? System.out + : new FileOutputStream(printFile); + } catch (FileNotFoundException e) { + error("unable to open file " + printFile + ". Printing on console"); + this.printStream = System.out; + } + String printerName = args.printer.value.intern(); + if (printerName == PRINTER_TEXT) + this.printer = new TextTreePrinter(printStream); + else + this.printer = new HTMLTreePrinter(printStream); + this.debugPrinter = new TextTreePrinter(System.err); + this.freshNameCreator = new FreshNameCreator(); + this.make = new TreeCreator(); + this.currentPhase = PhaseDescriptor.INITIAL; + this.definitions = new Definitions(this); + //this.primitives = new Primitives(this); + this.treeGen = new TreeGen(this, make); + this.PHASE = args.phases; + List phases = new ArrayList(); + phases.add(PHASE.INITIAL); + phases.add(PHASE.PARSER); + phases.add(PHASE.ANALYZER); + phases.add(PHASE.REFCHECK); + phases.add(PHASE.UNCURRY); + /* + if (optimize) { + phases.add(PHASE.OPTIMIZE); + } + phases.add(PHASE.TRANSMATCH); + */ + //phases.add(PHASE.LAMBDALIFT); + /* + phases.add(PHASE.EXPLICITOUTER); + phases.add(PHASE.ADDACCESSORS); + phases.add(PHASE.ADDINTERFACES); + phases.add(PHASE.EXPANDMIXIN); + phases.add(PHASE.ERASURE); + if (target == TARGET_INT || target == TARGET_MSIL || target == TARGET_JVM) { + phases.add(PHASE.ADDCONSTRUCTORS); + } + if (target == TARGET_JAVA) phases.add(PHASE.GENJAVA); + if (target == TARGET_MSIL) phases.add(PHASE.GENMSIL); + if (target == TARGET_JVM) phases.add(PHASE.GENJVM); + */ + phases.add(PHASE.TERMINAL); + this.phases = new PhaseDescriptor[phases.size()]; + for (int i = 0; i < phases.size(); i++) { + PhaseDescriptor phase = (PhaseDescriptor)phases.get(i); + this.phases[i] = phase; + if (i > 0) this.phases[i - 1].flags |= phase.flags >>> 16; + phase.initialize(this, i); + assert phase.id == i; + } + assert PHASE.ANALYZER.id + 1 == POST_ANALYZER_PHASE_ID; + } + + /** Move to next phase + */ + public void nextPhase() { + currentPhase = phases[currentPhase.id + 1]; + } + + /** Move to previous phase + */ + public void prevPhase() { + currentPhase = phases[currentPhase.id - 1]; + } + + /** the top-level compilation process + */ + public void compile(String[] files, boolean console) { + reporter.resetCounters(); + // parse files + List units = new ArrayList(files.length); + for (int i = 0; i < files.length; i++) { + String file = files[i]; + try { + units.add(new Unit(this, new Sourcefile(file, console))); + } catch (FileNotFoundException e) { + error("file " + file + " not found"); + } catch (IOException e) { + error(e.toString()); + } + } + this.units = (Unit[])units.toArray(new Unit[units.size()]); + compile(); + } + + /** the top-level compilation process + */ + public void compile(String input, boolean console) { + reporter.resetCounters(); + Sourcefile source = new Sourcefile(input.getBytes(), console); + units = new Unit[]{new Unit(this, source)}; + compile(); + } + + /** compile all compilation units + */ + private void compile() { + printer.begin(); + + // apply successive phases and pray that it works + for (int i = 0; i < phases.length && reporter.errors() == 0; ++i) { + currentPhase = phases[i]; + if ((currentPhase.flags & PhaseDescriptor.SKIP) != 0) { + operation("skipping phase " + currentPhase.name()); + } else { + currentPhase.apply(this); + } + if ((currentPhase.flags & PhaseDescriptor.PRINT) != 0) + currentPhase.print(this); + if ((currentPhase.flags & PhaseDescriptor.GRAPH) != 0) + currentPhase.graph(this); + if ((currentPhase.flags & PhaseDescriptor.CHECK) != 0) + currentPhase.check(this); + if ((currentPhase.flags & PhaseDescriptor.STOP) != 0) { + operation("stopped after phase " + currentPhase.name()); + break; + } + } + + printer.end(); + } + + /** stop the compilation process immediately + */ + public Error fail() { + throw new ApplicationError(); + } + + /** stop the compilation process immediately + */ + public Error fail(String message) { + throw new ApplicationError(message); + } + + /** stop the compilation process immediately + */ + public Error fail(String message, Object object) { + throw new ApplicationError(message, object); + } + + /** stop the compilation process immediately + */ + public Error fail(Object value) { + throw new ApplicationError(value); + } + + /** issue a global error + */ + public void error(String message) { + reporter.error("error: " + message); + } + + /** issue a global warning + */ + public void warning(String message) { + reporter.warning("warning: " + message); + } + + /** issue a global note (if in verbose mode) + */ + public void note(String message) { + reporter.note("note: " + message); + } + + /** issue an operation note + */ + public void operation(String message) { + reporter.inform("[" + message + "]"); + } + + /** issue a debug message from currentPhase + */ + // the boolean return value is here to let one write "assert log( ... )" + public boolean log(String message) { + if (log()) { + reporter.report("[log " + currentPhase.name() + "] " + message); + } + return true; + } + + /** return true if logging is switched on for the current phase + */ + public boolean log() { + return (currentPhase.flags & PhaseDescriptor.LOG) != 0; + } + + /** start a new timer + */ + public void start() { + startTimes.push(new Long(System.currentTimeMillis())); + } + + /** issue timing information + */ + public void stop(String message) { + long start = ((Long)startTimes.pop()).longValue(); + reporter.inform("[" + message + " in " + + (System.currentTimeMillis() - start) + "ms]"); + } +} diff --git a/sources/scalac/Main.java b/sources/scalac/Main.java new file mode 100644 index 0000000000..2c0dc78140 --- /dev/null +++ b/sources/scalac/Main.java @@ -0,0 +1,38 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac; + +import scalac.util.Reporter; + +/** The main class for SoCoS, a compiler for the programming + * language Scala. + * + * @author Matthias Zenger + * @version 1.0 + */ +public class Main { + + public static final String PRODUCT = + System.getProperty("scala.product", "socos"); + public static final String VERSION = + System.getProperty("scala.version", "unknown version"); + + public static void main(String[] args) { + Reporter reporter = new Reporter(); + CompilerCommand command = new CompilerCommand( + PRODUCT, VERSION, reporter, new PhaseRepository()); + if (command.parse(args) && command.files.list.size() > 0) { + Global global = new Global(command); + global.compile(command.files.toArray(), false); + global.stop("total"); + global.reporter.printSummary(); + } + System.exit((reporter.errors() > 0) ? -1 : 0); + } +} diff --git a/sources/scalac/Phase.java b/sources/scalac/Phase.java new file mode 100644 index 0000000000..bbce46e013 --- /dev/null +++ b/sources/scalac/Phase.java @@ -0,0 +1,45 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac; + +/** Representation of a compiler phase. PhaseDescriptors create + * phase. Phases operate subsequently on all compilation units. + * + * @author Matthias Zenger + * @version 1.0 + */ +public abstract class Phase { + + /** the global environment + */ + public final Global global; + + /** the descriptor for this phase + */ + public final PhaseDescriptor descr; + + /** constructor + */ + public Phase(Global global, PhaseDescriptor descr) { + this.global = global; + this.descr = descr; + } + + /** apply this phase to all compilation units + */ + public void apply() { + for (int i = 0; i < global.units.length; i++) { + apply(global.units[i]); + } + } + + /** apply this phase to the given compilation unit + */ + public abstract void apply(Unit unit); +} diff --git a/sources/scalac/PhaseDescriptor.java b/sources/scalac/PhaseDescriptor.java new file mode 100644 index 0000000000..0316a80215 --- /dev/null +++ b/sources/scalac/PhaseDescriptor.java @@ -0,0 +1,169 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac; + +import java.util.*; +import scalac.ast.printer.*; +import scalac.ast.*; +import scalac.symtab.*; +import scalac.checkers.*; +import java.io.PrintWriter; + +/** + * Information about a compiler phase. + * + * @author Michel Schinz + */ + +public abstract class PhaseDescriptor { + + private static class InitialPhaseDescriptor extends PhaseDescriptor { + + public String name() { + return "initial"; + } + + public String description() { + return "initializing compiler"; + } + + public Phase createPhase(Global global) { + return null; + } + + /** apply phase to all compilation units + */ + public void apply(Global global) {} + } + + private static class TerminalPhaseDescriptor extends PhaseDescriptor { + + public String name() { + return "terminal"; + } + + public String description() { + return "compilation terminated "; + } + + public Phase createPhase(Global global) { + return null; + } + + /** apply phase to all compilation units + */ + public void apply(Global global) {} + } + + public static PhaseDescriptor INITIAL = new InitialPhaseDescriptor(); + public static PhaseDescriptor TERMINAL = new TerminalPhaseDescriptor(); + + public static final int SKIP = 0x0001; + public static final int CHECK = 0x0002; + public static final int PRINT = 0x0004; + public static final int GRAPH = 0x0008; + public static final int STOP = 0x0010; + public static final int LOG = 0x0020; + + public int flags; + public int id; + + /** return a short, one-word name for the phase. + */ + public abstract String name(); + + /** return a one-line description for the phase. + */ + public abstract String description(); + + /** a one-line task description of this phase + */ + public String taskDescription() { + return description(); + } + + /** initialize the phase + */ + public final void initialize(Global global) { + throw new Error(); + } + public void initialize(Global global, int id) { + this.id = id; + } + + /** create a phase + */ + public abstract Phase createPhase(Global global); + + /** Assume that `tp' is the info of symbol `sym' before this phase. + * Return the info of `sym' after the phase. + */ + public Type transformInfo(Symbol sym, Type tp) { + return tp; + } + + /** apply phase to all compilation units + */ + public void apply(Global global) { + global.start(); + createPhase(global).apply(); + global.stop(taskDescription()); + } + + /** check all compilation units + */ + public void check(Global global) { + for (int i = 0; i < global.units.length; i++) + check(global.units[i]); + } + + /** print all compilation units + */ + public void print(Global global) { + TreePrinter printer = global.printer; + + printer.beginSection(1, "Trees after phase " + name()); + for (int i = 0; i < global.units.length; i++) + printer.print(global.units[i]); + } + + /** graph all compilation units + */ + public void graph(Global global) { + for (int i = 0; i < global.units.length; i++) + graph(global.units[i]); + } + + /** return an array of checkers which can be applied after the phase + */ + public Checker[] postCheckers(Global global) { + return new Checker[0]; + } + + /** check the result of this phase for the given compilation unit + */ + public void check(Unit unit) { + Checker[] checkers = postCheckers(unit.global); + for (int i = 0; i < checkers.length; i++) + checkers[i].traverse(unit); + } + + /** graph the result of this phase for the given compilation unit + */ + public void graph(Unit unit) { + /* todo: uncomment + new scala.compiler.gdl.TreePrinter().printInFile( + unit, unit.source + "-" + name() + ".gdl"); + */ + } + + public String toString() { + return name(); + } +} diff --git a/sources/scalac/PhaseRepository.java b/sources/scalac/PhaseRepository.java new file mode 100644 index 0000000000..4b7e3d4343 --- /dev/null +++ b/sources/scalac/PhaseRepository.java @@ -0,0 +1,96 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scalac; + +import scalac.ast.parser.ParserPhase; +import scalac.typechecker.AnalyzerPhase; +import scalac.typechecker.RefCheckPhase; +import scalac.transformer.UnCurryPhase; +/* +import scalac.transformer.TransMatchPhase; +*/ +import scalac.transformer.LambdaLiftPhase; +/* +import scalac.transformer.ExplicitOuterClassesPhase; +import scalac.transformer.AddInterfacesPhase; +import scalac.transformer.AddAccessorsPhase; +import scalac.transformer.ExpandMixinsPhase; +import scalac.transformer.ErasurePhase; +import scalac.optimizer.OptimizePhase; +import scalac.backend.AddConstructorsPhase; +import scalac.backend.msil.GenMSILPhase; +import scalac.backend.jvm.GenJVMPhase; +import scalac.jaco.GenJavaPhase; +*/ + +public class PhaseRepository { + + //######################################################################## + // Private state + + public final PhaseDescriptor[] phases; + + //######################################################################## + // Reporter constructors + + public PhaseRepository() { + this.phases = new PhaseDescriptor[] { + INITIAL = PhaseDescriptor.INITIAL, + PARSER = new ParserPhase(), + ANALYZER = new AnalyzerPhase(), + REFCHECK = new RefCheckPhase(), + UNCURRY = new UnCurryPhase(), + /* + OPTIMIZE = new OptimizePhase(), + TRANSMATCH = new TransMatchPhase(), + */ + LAMBDALIFT = new LambdaLiftPhase(), + /* + EXPLICITOUTER = new ExplicitOuterClassesPhase(), + ADDACCESSORS = new AddAccessorsPhase(), + ADDINTERFACES = new AddInterfacesPhase(), + EXPANDMIXIN = new ExpandMixinsPhase(), + ERASURE = new ErasurePhase(), + ADDCONSTRUCTORS = new AddConstructorsPhase(), + GENMSIL = new GenMSILPhase(), + GENJAVA = new GenJavaPhase(), + GENJVM = new GenJVMPhase(), + */ + TERMINAL = PhaseDescriptor.TERMINAL, + }; + } + + //######################################################################## + // Reporter interface + + public final PhaseDescriptor INITIAL; + public final ParserPhase PARSER; + public final AnalyzerPhase ANALYZER; + public final RefCheckPhase REFCHECK; + public final UnCurryPhase UNCURRY; + /* + public final OptimizePhase OPTIMIZE; + public final TransMatchPhase TRANSMATCH; + */ + public final LambdaLiftPhase LAMBDALIFT; + /* + public final ExplicitOuterClassesPhase EXPLICITOUTER; + public final AddAccessorsPhase ADDACCESSORS; + public final AddInterfacesPhase ADDINTERFACES; + public final ExpandMixinsPhase EXPANDMIXIN; + public final ErasurePhase ERASURE; + public final AddConstructorsPhase ADDCONSTRUCTORS; + public final GenMSILPhase GENMSIL; + public final GenJavaPhase GENJAVA; + public final GenJVMPhase GENJVM; + */ + public final PhaseDescriptor TERMINAL; + + //######################################################################## +} diff --git a/sources/scalac/Unit.java b/sources/scalac/Unit.java new file mode 100644 index 0000000000..f9a3ec81c6 --- /dev/null +++ b/sources/scalac/Unit.java @@ -0,0 +1,123 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac; + +import scalac.util.*; +import scalac.symtab.NameMangler; +import scalac.ast.parser.Sourcefile; +import scalac.ast.Tree; +import scala.compiler.typechecker.*; +import java.io.*; +import java.util.*; + + +/** A representation for a compilation unit in scala + * + * @author Matthias Zenger + * @version 1.0 + */ +public class Unit { + + /** the global compilation environment + */ + public Global global; + + /** the associated source code file + */ + public Sourcefile source; + + /** the content of the compilation unit in tree form + */ + public Tree[] body; + + /** the generated symbol data; Symbol -> byte[] + public SymData symdata; + */ + + /** the name mangler + */ + public NameMangler mangler = new NameMangler(); + + /** number of errors issued for this compilation unit + */ + public int errors; + + /** number of warnings issued for this compilation unit + */ + public int warnings; + + /** number of notes issued for this compilation unit + */ + public int notes; + + public Unit(Global global, Sourcefile source) { + this.global = global; + this.source = source; + } + /* + public void print(String message) { + print(System.out, message); + } + + public void print(PrintStream out, String message) { + out.println("[[begin " + message + "]]"); + new Printer(out).printCompilationUnit(this); + out.println(); + out.println("[[end " + message + "]]"); + } + */ + /** issue an error in this compilation unit + */ + public void error(String message) { + error(Position.NOPOS, message); + } + + /** issue an error in this compilation unit at a specific location + */ + public void error(int pos, String message) { + boolean hidden = source.testAndSetLog(pos, message); + if (!hidden) errors++; + global.reporter.error(source.getMessage(pos, message), hidden); + } + + /** issue a warning in this compilation unit + */ + public void warning(String message) { + warning(Position.NOPOS, message); + } + + /** issue a warning in this compilation unit at a specific location + */ + public void warning(int pos, String message) { + if (global.reporter.nowarn) return; + message = "warning: " + message; + boolean hidden = source.testAndSetLog(pos, message); + if (!hidden) warnings++; + global.reporter.warning(source.getMessage(pos, message), hidden); + } + + /** issue a note in this compilation unit + */ + public void note(String message) { + note(Position.NOPOS, message); + } + + /** issue a note in this compilation unit at a specific location + */ + public void note(int pos, String message) { + global.reporter.note(source.getMessage(pos, message)); + notes++; + } + + /** return a string representation + */ + public String toString() { + return source.toString(); + } +} diff --git a/sources/scalac/ast/AbstractTreeCopyFactory.java b/sources/scalac/ast/AbstractTreeCopyFactory.java new file mode 100644 index 0000000000..358d468cd4 --- /dev/null +++ b/sources/scalac/ast/AbstractTreeCopyFactory.java @@ -0,0 +1,30 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +\* */ + +// $Id$ + +package scalac.ast; + +import scalac.ast.*; + +/** + * Abstract superclass for all TreeCopyFactories, which provides only + * the code to copy the attribution from the "old" to the "new" tree. + * + * @author Michel Schinz + * @version 1.0 + */ + +public abstract class AbstractTreeCopyFactory implements TreeCopyFactory { + public void attribute(Tree newTree, Tree oldTree) { + if (newTree != oldTree) { + newTree.type = oldTree.type; + if (newTree.hasSymbol()) + newTree.setSymbol(oldTree.symbol()); + } + } +} diff --git a/sources/scalac/ast/LazyTreeFactory.java b/sources/scalac/ast/LazyTreeFactory.java new file mode 100644 index 0000000000..43cef5652a --- /dev/null +++ b/sources/scalac/ast/LazyTreeFactory.java @@ -0,0 +1,430 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.ast; + +import scalac.util.Name; +import Tree.*; + +public class LazyTreeFactory extends AbstractTreeCopyFactory { + protected final TreeFactory make; + + public LazyTreeFactory(TreeFactory make) { + this.make = make; + } + + public Tree Bad(Tree tree) { + return tree; + } + + public Tree ClassDef(Tree tree, + int mods, + Name name, + TypeDef[] tparams, + ValDef[][] vparams, + Tree tpe, + Template impl) { + ClassDef t = (ClassDef)tree; + if ((t.mods == mods) && + (t.name == name) && + (t.tparams == tparams) && + (t.vparams == vparams) && + (t.tpe == tpe) && + (t.impl == impl)) + return t; + tree = make.ClassDef(t.pos, mods, name, tparams, vparams, tpe, impl); + attribute(tree, t); + return tree; + } + + public Tree PackageDef(Tree tree, + Tree packaged, + Template impl) { + PackageDef t = (PackageDef)tree; + if ((t.packaged == packaged) && + (t.impl == impl)) + return t; + tree = make.PackageDef(t.pos, packaged, impl); + attribute(tree, t); + return tree; + } + + public Tree ModuleDef(Tree tree, + int mods, + Name name, + Tree tpe, + Template impl) { + ModuleDef t = (ModuleDef)tree; + if ((t.mods == mods) && + (t.name == name) && + (t.tpe == tpe) && + (t.impl == impl)) + return t; + tree = make.ModuleDef(t.pos, mods, name, tpe, impl); + attribute(tree, t); + return tree; + } + + public Tree ValDef(Tree tree, + int mods, + Name name, + Tree tpe, + Tree rhs) { + ValDef t = (ValDef)tree; + if ((t.mods == mods) && + (t.name == name) && + (t.tpe == tpe) && + (t.rhs == rhs)) + return t; + tree = make.ValDef(t.pos, mods, name, tpe, rhs); + attribute(tree, t); + return tree; + } + + public Tree PatDef(Tree tree, + int mods, + Tree pat, + Tree rhs) { + PatDef t = (PatDef)tree; + if ((t.mods == mods) && + (t.pat == pat) && + (t.rhs == rhs)) + return t; + tree = make.PatDef(t.pos, mods, pat, rhs); + attribute(tree, t); + return tree; + } + + public Tree DefDef(Tree tree, + int mods, + Name name, + TypeDef[] tparams, + ValDef[][] vparams, + Tree tpe, + Tree rhs) { + DefDef t = (DefDef)tree; + if ((t.mods == mods) && + (t.name == name) && + (t.tparams == tparams) && + (t.vparams == vparams) && + (t.tpe == tpe) && + (t.rhs == rhs)) + return t; + tree = make.DefDef(t.pos, mods, name, tparams, vparams, tpe, rhs); + attribute(tree, t); + return tree; + } + + public Tree TypeDef(Tree tree, + int mods, + Name name, + TypeDef[] tparams, + Tree rhs) { + TypeDef t = (TypeDef)tree; + if ((t.mods == mods) && + (t.name == name) && + (t.tparams == tparams) && + (t.rhs == rhs)) + return t; + tree = make.TypeDef(t.pos, mods, name, tparams, rhs); + attribute(tree, t); + return tree; + } + + public Tree Import(Tree tree, + Tree expr, + Name[] selectors) { + Import t = (Import)tree; + if (t.expr == expr && t.selectors == selectors) + return t; + tree = make.Import(t.pos, expr, selectors); + attribute(tree, t); + return tree; + } + + public Tree CaseDef(Tree tree, + Tree pat, + Tree guard, + Tree body) { + CaseDef t = (CaseDef)tree; + if ((t.pat == pat) && + (t.guard == guard) && + (t.body == body)) + return t; + tree = make.CaseDef(t.pos, pat, guard, body); + attribute(tree, t); + return tree; + } + + public Template Template(Tree tree, + Tree[] parents, + Tree[] body) { + Template t = (Template)tree; + if ((t.parents == parents) && + (t.body == body)) + return t; + Template newTree = make.Template(t.pos, parents, body); + attribute(newTree, t); + return newTree; + } + + public Tree LabelDef(Tree tree, + Tree[] params, + Tree rhs) { + LabelDef t = (LabelDef)tree; + if ((t.params == params) && + (t.rhs == rhs)) + return t; + tree = make.LabelDef(t.pos,params,rhs); + attribute(tree,t); + return tree; + } + + public Tree Block(Tree tree, + Tree[] stats) { + Block t = (Block)tree; + if (t.stats == stats) + return t; + tree = make.Block(t.pos, stats); + attribute(tree, t); + return tree; + } + + public Tree Tuple(Tree tree, + Tree[] trees) { + Tuple t = (Tuple)tree; + if (t.trees == trees) + return t; + tree = make.Tuple(t.pos, trees); + attribute(tree, t); + return tree; + } + + public Tree Visitor(Tree tree, + CaseDef[] cases) { + Visitor t = (Visitor)tree; + if (t.cases == cases) + return t; + tree = make.Visitor(t.pos, cases); + attribute(tree, t); + return tree; + } + + public Tree Function(Tree tree, + ValDef[] vparams, + Tree body) { + Function t = (Function)tree; + if ((t.vparams == vparams) && + (t.body == body)) + return t; + tree = make.Function(t.pos, vparams, body); + attribute(tree, t); + return tree; + } + + public Tree Assign(Tree tree, + Tree lhs, + Tree rhs) { + Assign t = (Assign)tree; + if ((t.lhs == lhs) && + (t.rhs == rhs)) + return t; + tree = make.Assign(t.pos, lhs, rhs); + attribute(tree, t); + return tree; + } + + public Tree If(Tree tree, + Tree cond, + Tree thenp, + Tree elsep) { + If t = (If)tree; + if ((t.cond == cond) && + (t.thenp == thenp) && + (t.elsep == elsep)) + return t; + tree = make.If(t.pos, cond, thenp, elsep); + attribute(tree, t); + return tree; + } + + public Tree New(Tree tree, + Template templ) { + New t = (New)tree; + if (t.templ == templ) + return t; + tree = make.New(t.pos, templ); + attribute(tree, t); + return tree; + } + + public Tree Typed(Tree tree, + Tree expr, + Tree tpe) { + Typed t = (Typed)tree; + if ((t.expr == expr) && + (t.tpe == tpe)) + return t; + tree = make.Typed(t.pos, expr, tpe); + attribute(tree, t); + return tree; + } + + public Tree TypeApply(Tree tree, + Tree fun, + Tree[] args) { + TypeApply t = (TypeApply)tree; + if ((t.fun == fun) && + (t.args == args)) + return t; + tree = make.TypeApply(t.pos, fun, args); + attribute(tree, t); + return tree; + } + + public Tree Apply(Tree tree, + Tree fun, + Tree[] args) { + Apply t = (Apply)tree; + if ((t.fun == fun) && + (t.args == args)) + return t; + tree = make.Apply(t.pos, fun, args); + attribute(tree, t); + return tree; + } + + public Tree Super(Tree tree, + Tree tpe) { + Super t = (Super)tree; + if (t.tpe == tpe) + return t; + tree = make.Super(t.pos, tpe); + attribute(tree, t); + return tree; + } + + public Tree This(Tree tree, + Tree qualifier) { + This t = (This)tree; + if (t.qualifier == qualifier) + return t; + tree = make.This(t.pos, qualifier); + attribute(tree, t); + return tree; + } + + public Tree Select(Tree tree, + Tree qualifier, + Name selector) { + Select t = (Select)tree; + if ((t.qualifier == qualifier) && + (t.selector == selector)) + return t; + tree = make.Select(t.pos, qualifier, selector); + attribute(tree, t); + return tree; + } + + public Tree Ident(Tree tree, + Name name) { + Ident t = (Ident)tree; + if (t.name == name) + return t; + tree = make.Ident(t.pos, name); + attribute(tree, t); + return tree; + } + + public Tree Literal(Tree tree, + Object value) { + Literal t = (Literal)tree; + if (t.value == value) + return t; + tree = make.Literal(t.pos, value); + attribute(tree, t); + return tree; + } + + public Tree SingletonType(Tree tree, + Tree ref) { + SingletonType t = (SingletonType)tree; + if (t.ref == ref) + return t; + tree = make.SingletonType(t.pos, ref); + attribute(tree, t); + return tree; + } + + public Tree SelectFromType(Tree tree, + Tree qualifier, + Name selector) { + SelectFromType t = (SelectFromType)tree; + if (t.qualifier == qualifier && + t.selector == selector) + return t; + tree = make.SelectFromType(t.pos, qualifier, selector); + attribute(tree, t); + return tree; + } + + public Tree FunType(Tree tree, + Tree[] argtpes, + Tree restpe) { + FunType t = (FunType)tree; + if ((t.argtpes == argtpes) && + (t.restpe == restpe)) + return t; + tree = make.FunType(t.pos, argtpes, restpe); + attribute(tree, t); + return tree; + } + + public Tree CompoundType(Tree tree, + Tree[] parents, + Tree[] refinements) { + CompoundType t = (CompoundType)tree; + if ((t.parents == parents) && + (t.refinements == refinements)) + return t; + tree = make.CompoundType(t.pos, parents, refinements); + attribute(tree, t); + return tree; + } + + public Tree TupleType(Tree tree, + Tree[] types) { + TupleType t = (TupleType)tree; + if (t.types == types) + return t; + tree = make.TupleType(t.pos, types); + attribute(tree, t); + return tree; + } + + public Tree AppliedType(Tree tree, + Tree tpe, + Tree[] args) { + AppliedType t = (AppliedType)tree; + if ((t.tpe == tpe) && + (t.args == args)) + return t; + tree = make.AppliedType(t.pos, tpe, args); + attribute(tree, t); + return tree; + } + + public Tree CovariantType(Tree tree, + Tree tpe) { + CovariantType t = (CovariantType)tree; + if (t.tpe == tpe) return t; + tree = make.CovariantType(t.pos, tpe); + attribute(tree, t); + return tree; + } +} diff --git a/sources/scalac/ast/Transformer.java b/sources/scalac/ast/Transformer.java new file mode 100644 index 0000000000..1789801c7e --- /dev/null +++ b/sources/scalac/ast/Transformer.java @@ -0,0 +1,401 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.ast; + +import java.io.*; +import java.util.*; +import scalac.*; +import scalac.util.*; +import scalac.symtab.*; +import Tree.*; + + +/** A default transformer class. This class traverses the abstract + * syntax tree but does not do any transformations. + * + * @author Matthias Zenger + * @version 1.0 + */ +public class Transformer extends Phase { + /** the tree factory + */ + public final TreeFactory make; + + /** a factory for copying trees; the attribution is preserved + * or translated according to the TreeCopyFactory; trees are + * only copied if new tree introduces changes + */ + public final TreeCopyFactory copy; + + /** a tree generator + */ + public final TreeGen gen; + + /** various constructors + */ + public Transformer(Global global, PhaseDescriptor descr) { + this(global, descr, global.make, new LazyTreeFactory(global.make)); + } + + public Transformer(Global global, + PhaseDescriptor descr, + TreeFactory make) { + this(global, descr, make, new LazyTreeFactory(make)); + } + + public Transformer(Global global, + PhaseDescriptor descr, + TreeFactory make, + TreeCopyFactory copy) { + super(global, descr); + this.make = make; + this.copy = copy; + this.gen = global.treeGen; + } + + public void apply() { + super.apply(); + } + + public void apply(Unit unit) { + unit.body = transform(unit.body); + } + + public Tree[] transform(Tree[] ts) { + for (int i = 0; i < ts.length; i++) { + Tree t = transform(ts[i]); + if (t != ts[i]) { + Tree[] res = new Tree[ts.length]; + System.arraycopy(ts, 0, res, 0, i); + res[i] = t; + for (int j = i + 1; j < ts.length; j++) + res[j] = transform(ts[j]); + return res; + } + } + return ts; + } + + public Tree[][] transform(Tree[][] ts) { + for (int i = 0; i < ts.length; i++) { + Tree[] t = transform(ts[i]); + if (t != ts[i]) { + Tree[][] res = new Tree[ts.length][]; + System.arraycopy(ts, 0, res, 0, i); + res[i] = t; + for (int j = i + 1; j < ts.length; j++) + res[j] = transform(ts[j]); + return res; + } + } + return ts; + } + + public ValDef[][] transform(ValDef[][] ts) { + for (int i = 0; i < ts.length; i++) { + ValDef[] t = transform(ts[i]); + if (t != ts[i]) { + ValDef[][] res = new ValDef[ts.length][]; + System.arraycopy(ts, 0, res, 0, i); + res[i] = t; + for (int j = i + 1; j < ts.length; j++) + res[j] = transform(ts[j]); + return res; + } + } + return ts; + } + + public ValDef[] transform(ValDef[] ts) { + for (int i = 0; i < ts.length; i++) { + Tree t = transform(ts[i]); + if (t != ts[i]) { + ValDef[] res = new ValDef[ts.length]; + System.arraycopy(ts, 0, res, 0, i); + res[i] = (ValDef)t; + for (int j = i + 1; j < ts.length; j++) + res[j] = (ValDef)transform(ts[j]); + return res; + } + } + return ts; + } + + public TypeDef[] transform(TypeDef[] ts) { + for (int i = 0; i < ts.length; i++) { + Tree t = transform(ts[i]); + if (t != ts[i]) { + TypeDef[] res = new TypeDef[ts.length]; + System.arraycopy(ts, 0, res, 0, i); + res[i] = (TypeDef)t; + for (int j = i + 1; j < ts.length; j++) + res[j] = (TypeDef)transform(ts[j]); + return res; + } + } + return ts; + } + + public CaseDef[] transform(CaseDef[] ts) { + for (int i = 0; i < ts.length; i++) { + Tree t = transform(ts[i]); + if (t != ts[i]) { + CaseDef[] res = new CaseDef[ts.length]; + System.arraycopy(ts, 0, res, 0, i); + res[i] = (CaseDef)t; + for (int j = i + 1; j < ts.length; j++) + res[j] = (CaseDef)transform(ts[j]); + return res; + } + } + return ts; + } + + public Template transform(Template tree) { + return (Template)transform((Tree)tree); + } + + public Tree transform(Tree tree) { + if (tree == null) + return null; + switch (tree) { + case ClassDef(int mods, + Name name, + TypeDef[] tparams, + ValDef[][] vparams, + Tree tpe, + Template impl): + return copy.ClassDef(tree, + mods, + name, + transform(tparams), + transform(vparams), + transform(tpe), + transform(impl)); + case PackageDef(Tree packaged, Template impl): + return copy.PackageDef(tree, + transform(packaged), + transform(impl)); + case ModuleDef(int mods, + Name name, + Tree tpe, + Template impl): + return copy.ModuleDef(tree, + mods, + name, + transform(tpe), + transform(impl)); + case ValDef(int mods, + Name name, + Tree tpe, + Tree rhs): + return copy.ValDef(tree, + mods, + name, + transform(tpe), + transform(rhs)); + case PatDef(int mods, + Tree pat, + Tree rhs): + return copy.PatDef(tree, + mods, + transform(pat), + transform(rhs)); + case DefDef(int mods, + Name name, + TypeDef[] tparams, + ValDef[][] vparams, + Tree tpe, + Tree rhs): + return copy.DefDef(tree, + mods, + name, + transform(tparams), + transform(vparams), + transform(tpe), + transform(rhs)); + case TypeDef(int mods, + Name name, + TypeDef[] tparams, + Tree rhs): + return copy.TypeDef(tree, + mods, + name, + transform(tparams), + transform(rhs)); + case Import(Tree expr, Name[] selectors): + return copy.Import(tree, + transform(expr), + selectors); + case CaseDef(Tree pat, Tree guard, Tree body): + return copy.CaseDef(tree, + transform(pat), + transform(guard), + transform(body)); + case Template(Tree[] parents, Tree[] body): + return copy.Template(tree, + transform(parents), + transform(body)); + case LabelDef(Tree[] params,Tree rhs): + return copy.LabelDef(tree, + transform(params), + transform(rhs)); + case Block(Tree[] stats): + return copy.Block(tree, + transform(stats)); + case Tuple(Tree[] trees): + return copy.Tuple(tree, + transform(trees)); + case Visitor(CaseDef[] cases): + return copy.Visitor(tree, + transform(cases)); + case Function(ValDef[] vparams, Tree body): + return copy.Function(tree, + transform(vparams), + transform(body)); + case Assign(Tree lhs, Tree rhs): + return copy.Assign(tree, + transform(lhs), + transform(rhs)); + case If(Tree cond, Tree thenp, Tree elsep): + return copy.If(tree, + transform(cond), + transform(thenp), + transform(elsep)); + case New(Template templ): + return copy.New(tree, + transform(templ)); + case Typed(Tree expr, Tree tpe): + return copy.Typed(tree, + transform(expr), + transform(tpe)); + case TypeApply(Tree fun, Tree[] args): + return copy.TypeApply(tree, + transform(fun), + transform(args)); + case Apply(Tree fun, Tree[] args): + return copy.Apply(tree, + transform(fun), + transform(args)); + case Super(Tree tpe): + return copy.Super(tree, + transform(tpe)); + case This(Tree qualifier): + return copy.This(tree, + transform(qualifier)); + case Select(Tree qualifier, Name selector): + return copy.Select(tree, + transform(qualifier), + selector); + case Ident(Name name): + return copy.Ident(tree, name); + case Literal(Object value): + return copy.Literal(tree, value); + case SingletonType(Tree ref): + return copy.SingletonType(tree, + transform(ref)); + case SelectFromType(Tree qualifier, Name selector): + return copy.SelectFromType(tree, + transform(qualifier), + selector); + case FunType(Tree[] argtpes, Tree restpe): + return copy.FunType(tree, + transform(argtpes), + transform(restpe)); + case CompoundType(Tree[] parents, Tree[] refinements): + return copy.CompoundType(tree, + transform(parents), + transform(refinements)); + case TupleType(Tree[] types): + return copy.TupleType(tree, + transform(types)); + case AppliedType(Tree tpe, Tree[] args): + return copy.AppliedType(tree, + transform(tpe), + transform(args)); + case CovariantType(Tree tpe): + return copy.CovariantType(tree, + transform(tpe)); + default: + return tree; + } + } + /* a full pattern-matching statement: + + switch (tree) { + case PackageDef(Tree packaged, Template impl): + + case ClassDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, + Template impl): + + case ModuleDef(int mods, Name name, Tree tpe, Template impl): + + case ValDef(int mods, Name name, Tree tpe, Tree rhs): + + case PatDef(int mods, Tree pat, Tree rhs): + + case DefDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, + Tree tpe, Tree rhs): + + case TypeDef(int mods, Name name, TypeDef[] tparams, Tree rhs): + + case Import(Tree expr): + + case CaseDef(Tree pat, Tree guard, Tree body): + + case Template(Tree[] baseClasses, Tree[] body): + + case LabelDef(Tree[] params,Tree rhs): + + case Block(Tree[] stats): + + case Tuple(Tree[] trees): + + case Visitor(CaseDef[] cases): + + case Function(ValDef[] vparams, Tree body): + + case Assign(Tree lhs, Tree rhs): + + case If(Tree cond, Tree thenp, Tree elsep): + + case New(Template templ): + + case Typed(Tree expr, Tree tpe): + + case TypeApply(Tree fun, Tree[] args): + + case Apply(Tree fun, Tree[] args): + + case Super(Tree tpe): + + case Select(Tree qualifier, Name selector): + + case Ident(Name name): + + case Literal(int kind, Object value): + + case SingletonType(Tree ref): + + case SelectFromType(Tree qualifier, Name selector): + + case FunType(Tree argtpe, Tree restpe): + + case CompoundType(Tree[] baseTypes, Tree[] refinements): + + case TupleType(Tree[] types): + + case AppliedType(Tree tpe, Tree[] args): + + default: + return tree; + } + */ + +} diff --git a/sources/scalac/ast/Traverser.java b/sources/scalac/ast/Traverser.java new file mode 100644 index 0000000000..5c39d9e315 --- /dev/null +++ b/sources/scalac/ast/Traverser.java @@ -0,0 +1,215 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +\* */ + +// $Id$ + +package scalac.ast; + +import java.io.*; +import java.util.*; +import scalac.*; +import scalac.util.*; +import Tree.*; + + +/** Class to traverse a tree without modifying it. + * + * @author Michel Schinz + */ + +public class Traverser { + public Traverser() {} + + // this should be removed in the future + public Traverser(Global global) {} + + public void traverse(Unit unit) { + traverse(unit.body); + } + + public void traverse(Tree tree) { + switch (tree) { + case Bad(): + case Empty: + case Ident(_): + case Literal(_): + return; + + case ClassDef(int mods, + Name name, + TypeDef[] tparams, + ValDef[][] vparams, + Tree tpe, + Template impl): + traverse(tparams); + traverse(vparams); + traverse(tpe); + traverse(impl); + return; + + case PackageDef(Tree packaged, Template impl) : + traverse(packaged); + traverse(impl); + return; + + case ModuleDef(int mods, Name name, Tree tpe, Template impl): + traverse(tpe); + traverse(impl); + return; + + case ValDef(int mods, Name name, Tree tpe, Tree rhs): + traverse(tpe); + traverse(rhs); + return; + + case PatDef(int mods, Tree pat, Tree rhs): + traverse(pat); + traverse(rhs); + return; + + case DefDef(int mods, + Name name, + TypeDef[] tparams, + ValDef[][] vparams, + Tree tpe, + Tree rhs): + traverse(tparams); + traverse(vparams); + traverse(tpe); + traverse(rhs); + return; + + case TypeDef(int mods, Name name, TypeDef[] tparams, Tree rhs): + traverse(tparams); + traverse(rhs); + return; + + case Import(Tree expr, Name[] selectors): + traverse(expr); + return; + + case CaseDef(Tree pat, Tree guard, Tree body): + traverse(pat); + traverse(guard); + traverse(body); + return; + + case Template(Tree[] baseClasses, Tree[] body): + traverse(baseClasses); + traverse(body); + return; + + case LabelDef(Tree[] params,Tree rhs): + traverse(params); + traverse(rhs); + return; + + case Block(Tree[] stats): + traverse(stats); + return; + + case Tuple(Tree[] trees): + traverse(trees); + return; + + case Visitor(CaseDef[] cases): + traverse(cases); + return; + + case Function(ValDef[] vparams, Tree body): + traverse(vparams); + traverse(body); + return; + + case Assign(Tree lhs, Tree rhs): + traverse(lhs); + traverse(rhs); + return; + + case If(Tree cond, Tree thenp, Tree elsep): + traverse(cond); + traverse(thenp); + traverse(elsep); + return; + + case New(Template templ): + traverse(templ); + return; + + case Typed(Tree expr, Tree tpe): + traverse(expr); + traverse(tpe); + return; + + case TypeApply(Tree fun, Tree[] tparams): + traverse(fun); + traverse(tparams); + return; + + case Apply(Tree fun, Tree[] vparam): + traverse(fun); + traverse(vparam); + return; + + case Super(Tree tpe): + traverse(tpe); + return; + + case This(Tree qualifier): + traverse(qualifier); + return; + + case Select(Tree qualifier, Name selector): + traverse(qualifier); + return; + + case SingletonType(Tree ref): + traverse(ref); + return; + + case SelectFromType(Tree qualifier, Name selector): + traverse(qualifier); + return; + + case FunType(Tree[] argtpes, Tree restpe): + traverse(argtpes); + traverse(restpe); + return; + + case CompoundType(Tree[] baseTypes, Tree[] refinements): + traverse(baseTypes); + traverse(refinements); + return; + + case TupleType(Tree[] types): + traverse(types); + return; + + case AppliedType(Tree tpe, Tree[] args): + traverse(tpe); + traverse(args); + return; + + case CovariantType(Tree tpe): + traverse(tpe); + return; + + default: + throw new ApplicationError("unknown node " + tree); + } + } + + public void traverse(Tree[] array) { + for (int i = 0; i < array.length; ++i) + traverse(array[i]); + } + + public void traverse(Tree[][] array) { + for (int i = 0; i < array.length; ++i) + traverse(array[i]); + } +} diff --git a/sources/scalac/ast/Tree.java b/sources/scalac/ast/Tree.java new file mode 100644 index 0000000000..80304ea78e --- /dev/null +++ b/sources/scalac/ast/Tree.java @@ -0,0 +1,771 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.ast; + +import scalac.ast.printer.*; +import scalac.ApplicationError; +import scalac.util.Name; +import scalac.util.Position; +import scalac.symtab.Type; +import scalac.symtab.Symbol; + +public class Tree { + + public int pos = Position.NOPOS; + public Type type; + +/** empty tree array + */ + public static final Tree[] EMPTY_ARRAY = new Tree[0]; + +/** representation for parser errors + */ + public case Bad(); + +/** a tree node for the absence of a tree + */ + public case Empty; + static { Empty.type = Type.NoType; } + +/** class and data declaration + */ + public case ClassDef(int mods, + Name name, + TypeDef[] tparams, + ValDef[][] vparams, + Tree tpe, + Template impl) { + assert name.isTypeName(); + } + +/** package declaration + */ + public case PackageDef(Tree packaged, + Template impl) { + if (!packaged.isTerm()) + throw new ApplicationError("PackageDef expects term as rhs."); + } + +/** module declaration + */ + public case ModuleDef(int mods, + Name name, + Tree tpe, + Template impl) { + assert !name.isTypeName(); + } + +/** var or let declaration + */ + public case ValDef(int mods, + Name name, + Tree tpe, + Tree rhs) { + assert !name.isTypeName(); + if (!tpe.isType()) + throw new ApplicationError("ValDef expects type as tpe; found: " + tpe); + if (!rhs.isTerm()) + throw new ApplicationError("ValDef expects term as rhs."); + } + +/** val declaration + */ + public case PatDef(int mods, + Tree pat, + Tree rhs) { + if (!rhs.isTerm()) + throw new ApplicationError("PatDef expects term as rhs."); + } + +/** def declaration + */ + public case DefDef(int mods, + Name name, + TypeDef[] tparams, + ValDef[][] vparams, + Tree tpe, + Tree rhs) { + assert !name.isTypeName(); + if (!tpe.isType()) + throw new ApplicationError("DefDef expects type as tpe."); + if (!rhs.isTerm()) + throw new ApplicationError("DefDef expects term as rhs. Found: " + rhs.getClass()); + } + +/** type declaration + */ + public case TypeDef(int mods, + Name name, + TypeDef[] tparams, + Tree rhs) { + assert name.isTypeName(); + if (!rhs.isType()) + throw new ApplicationError("TypeDef expects type as rhs; found: " + rhs); + } + +/** import declaration + */ + public case Import(Tree expr, Name[] selectors) { + if (!expr.isTerm()) + throw new ApplicationError("Import expects term."); + } + +/** case declaration + */ + public case CaseDef(Tree pat, + Tree guard, + Tree body) { + if (!guard.isTerm()) + throw new ApplicationError("CaseDef expects term as guard."); + if (!body.isTerm()) + throw new ApplicationError("CaseDef expects term as body."); + } + +/** instantiation templates +*/ + public case Template(Tree[] parents, + Tree[] body) { + if (parents != null) { + for (int i = 0; i < parents.length; i++) { + if (!parents[i].isTerm()) + throw new ApplicationError("Template requires terms as baseClasses."); + } + } + } + +/** labelled expression - the symbols in the array (must be Idents!) are those the + label takes as argument +*/ + public case LabelDef(Tree[] params,Tree rhs) { + for (int i = 0;i < params.length; i++) { + if (!(params[i] instanceof Ident)) + throw new ApplicationError("LabelDef requires Idents"); + } + } + +/** block of expressions (semicolon separated expressions) + */ + public case Block(Tree[] stats); + +/** tuple of expressions (comma separated expressions) + */ + public case Tuple(Tree[] trees) { + if (trees != null) { + for (int i = 0; i < trees.length; i++) { + if (!trees[i].isTerm()) + throw new ApplicationError("Tuple requires terms"); + } + } + } + +/** visitor (a sequence of cases) + */ + public case Visitor(CaseDef[] cases); + +/** an anonymous function + */ + public case Function(ValDef[] vparams, + Tree body) { + if (!body.isTerm()) + throw new ApplicationError("Function body has to be a term."); + } + +/** assignment + */ + public case Assign(Tree lhs, + Tree rhs) { + if (!lhs.isTerm()) + throw new ApplicationError("lhs of Assign has to be a term."); + if (!rhs.isTerm()) + throw new ApplicationError("rhs of Assign has to be a term."); + } + +/** conditional expression + */ + public case If(Tree cond, + Tree thenp, + Tree elsep) { + assert cond.isTerm() && + thenp.isTerm() && + elsep.isTerm(); + } + +/** instantiation + */ + public case New(Template templ); + +/** type annotation + */ + public case Typed(Tree expr, + Tree tpe) { + if (!expr.isTerm()) + throw new ApplicationError("Typed expects term as first argument."); + if (!tpe.isType()) + throw new ApplicationError("Typed expects type as second argument."); + } + +/** type application + */ + public case TypeApply(Tree fun, + Tree[] args) { + if (!fun.isTerm()) { + new TextTreePrinter().print(fun).println().end();//debug + throw new ApplicationError("TypeApply expects term as function."); + } + for (int i = 0; i < args.length; i++) { + if (!args[i].isType()) + throw new ApplicationError("TypeApply expects types as arguments."); + } + } + +/** value application + */ + public case Apply(Tree fun, + Tree[] args) { + if (args != null) { + for (int i = 0; i < args.length; i++) { + if (!args[i].isTerm()) + throw new ApplicationError("Apply expects terms as arguments. Found: " + args[i].getClass()); + } + } + } + +/** super reference + */ + public case Super(Tree tpe) { + if (!tpe.isType()) { + throw new ApplicationError("Super expects type."); + } + } + +/** self reference + */ + public case This(Tree qualifier) { + if (!qualifier.isType()) + throw new ApplicationError("This expects type."); + } + +/** designator + */ + public case Select(Tree qualifier, + Name selector) { + if (!qualifier.isTerm()) + throw new ApplicationError("Select expects term."); + } + +/** identifier + */ + public case Ident(Name name) { + assert name != null; + } + +/** literal + */ + public case Literal(Object value); + +/** singleton type + */ + public case SingletonType(Tree ref) { + if (!ref.isTerm()) + throw new ApplicationError("SingletonType expects term."); + } + +/** type selection + */ + public case SelectFromType(Tree qualifier, + Name selector) { + if (!qualifier.isType()) + throw new ApplicationError("SelectFromType expects type."); + assert selector.isTypeName(); + } + +/** function type + */ + public case FunType(Tree[] argtpes, + Tree restpe) { + for (int i = 0; i < argtpes.length; i++) + if (!argtpes[i].isType()) + throw new ApplicationError("FunType requires types as args."); + if (!restpe.isType()) + throw new ApplicationError("FunType requires type as result."); + } + +/** object type (~ Template) + */ + public case CompoundType(Tree[] parents, + Tree[] refinements) { + if (parents != null) { + assert parents.length > 0; + for (int i = 0; i < parents.length; i++) { + if (!parents[i].isType()) + throw new ApplicationError("CompoundType requires types as parents."); + } + } + } + +/** tuple type (~ Tuple) + */ + public case TupleType(Tree[] types) { + if (types != null) { + for (int i = 0; i < types.length; i++) { + if (!types[i].isType()) + throw new ApplicationError("TupleType requires types."); + } + } + } + + /** applied type + */ + public case AppliedType(Tree tpe, Tree[] args) { + assert tpe.isType() : this; + for (int i = 0; i < args.length; i++) assert args[i].isType() : args[i]; + } + + /** a covariant type + */ + public case CovariantType(Tree tpe) { + assert tpe.isType(); + } + + /** Get the type of the node. */ + public Type type() { + assert type != null : this; + return type; + } + + /** Set the type of the node. */ + public Tree setType(Type type) { + assert !(type instanceof Type.LazyType) : symbol(); + this.type = type; + return this; + } + + /** + * Get types attached to array of nodes. + */ + public static Type[] typeOf(Tree[] trees) { + Type[] tps = new Type[trees.length]; + for (int i = 0; i < trees.length; i++) + tps[i] = trees[i].type(); + return tps; + } + + /** Has this tree a symbol field? */ + public boolean hasSymbol() { + return false; + } + + /** + * Get symbol attached to the node, if any. + */ + public Symbol symbol () { + return null; + } + + /** + * Set symbol attached to the node, if possible. + */ + public Tree setSymbol(Symbol sym) { + throw new ApplicationError ("no settable symbol for node", this); + } + + /** + * Get symbols attached to array of nodes. + */ + public static Symbol[] symbolOf(Tree[] trees) { + Symbol[] syms = new Symbol[trees.length]; + for (int i = 0; i < trees.length; i++) + syms[i] = trees[i].symbol(); + return syms; + } + + /** + * Tells if the tree defines a symbol. + **/ + public boolean definesSymbol() { + return false; + } + + /** Get string corresponding to this tree + * only implemented for prefix trees, maybe we should generalize this; + * the PatternMatch phase needs support for Apply, so this case got added + */ + public String toString() { + switch (this) { + case This(Tree qual): + return (qual == Tree.Empty) ? "this" : qual + ".this"; + case Select(Tree qual, Name name): + return qual + "." + name; + case Ident(Name name): + return name.toString(); + case Apply(Tree fn, Tree[] args): + String res = fn + "("; + if ((args == null) || (args.length == 0)) + return res + ")"; + res += args[0].toString(); + for (int i = 1; i < args.length; i++) + res += ", " + args[i]; + return res + ")"; + case Literal(Object value): + if (value instanceof String) + return "\"" + value + "\""; + else + return value.toString(); + case Import(Tree expr, _): + return "import " + expr; + case Empty: + return "<empty>"; + default: + return super.toString(); + } + } + + public static class ExtBad extends Bad { + private Symbol symbol; + + public ExtBad() { + super(); + } + + public boolean hasSymbol() { + return true; + } + + public Symbol symbol() { + return symbol; + } + + public Tree setSymbol(Symbol symbol) { + this.symbol = symbol; + return this; + } + } + + public static class ExtClassDef extends ClassDef { + private Symbol symbol; + + public ExtClassDef(int mods, Name name, TypeDef[] tparams, + ValDef[][] vparams, Tree tpe, Template impl) + { + super(mods, name, tparams, vparams, tpe, impl); + } + + public boolean hasSymbol() { + return true; + } + + public Symbol symbol() { + return symbol; + } + + public Tree setSymbol(Symbol symbol) { + this.symbol = symbol; + return this; + } + + public boolean definesSymbol() { + return true; + } + } + + public static class ExtModuleDef extends ModuleDef { + private Symbol symbol; + + public ExtModuleDef(int mods, Name name, Tree tpe, Template impl) + { + super(mods, name, tpe, impl); + } + + public boolean hasSymbol() { + return true; + } + + public Symbol symbol() { + return symbol; + } + + public Tree setSymbol(Symbol symbol) { + this.symbol = symbol; + return this; + } + + public boolean definesSymbol() { + return true; + } + } + + public static class ExtValDef extends ValDef { + + public static final ValDef[] EMPTY_ARRAY = new ValDef[0]; + public static final ValDef[][] EMPTY_ARRAY_ARRAY = new ValDef[0][0]; + + private Symbol symbol; + + public ExtValDef(int mods, Name name, Tree tpe, Tree rhs) + { + super(mods, name, tpe, rhs); + } + + public boolean hasSymbol() { + return true; + } + + public Symbol symbol() { + return symbol; + } + + public Tree setSymbol(Symbol symbol) { + this.symbol = symbol; + return this; + } + + public boolean definesSymbol() { + return true; + } + } + + public static class ExtDefDef extends DefDef { + private Symbol symbol; + + public ExtDefDef(int mods, Name name, TypeDef[] tparams, + ValDef[][] vparams, Tree tpe, Tree rhs) + { + super(mods, name, tparams, vparams, tpe, rhs); + } + + public boolean hasSymbol() { + return true; + } + + public Symbol symbol() { + return symbol; + } + + public Tree setSymbol(Symbol symbol) { + this.symbol = symbol; + return this; + } + + public boolean definesSymbol() { + return true; + } + } + + public static class ExtTypeDef extends TypeDef { + private Symbol symbol; + + public static final TypeDef[] EMPTY_ARRAY = new TypeDef[0]; + + public ExtTypeDef(int mods, Name name, TypeDef[] tparams, Tree rhs) + { + super(mods, name, tparams, rhs); + } + + public boolean hasSymbol() { + return true; + } + + public Symbol symbol() { + return symbol; + } + + public Tree setSymbol(Symbol symbol) { + this.symbol = symbol; + return this; + } + + public boolean definesSymbol() { + return true; + } + } + + public static class ExtImport extends Import { + private Symbol symbol; + + public ExtImport(Tree expr, Name[] selectors) { + super(expr, selectors); + } + + public boolean hasSymbol() { + return true; + } + + public Symbol symbol() { + return symbol; + } + + public Tree setSymbol(Symbol symbol) { + this.symbol = symbol; + return this; + } + } + + public static class ExtLabelDef extends LabelDef { + private Symbol symbol; + + public ExtLabelDef(Tree[] params,Tree rhs) { + super(params,rhs); + } + + public boolean hasSymbol() { + return true; + } + + public Symbol symbol() { + return symbol; + } + + public Tree setSymbol(Symbol symbol) { + this.symbol = symbol; + return this; + } + + public boolean definesSymbol() { + return true; + } + } + + public static class ExtSelect extends Select { + private Symbol symbol; + + public ExtSelect(Tree qualifier, Name selector) { + super(qualifier, selector); + } + + public boolean hasSymbol() { + return true; + } + + public Symbol symbol() { + return symbol; + } + + public Tree setSymbol(Symbol symbol) { + this.symbol = symbol; + return this; + } + } + + public static class ExtSelectFromType extends SelectFromType { + private Symbol symbol; + + public ExtSelectFromType(Tree qualifier, Name selector) { + super(qualifier, selector); + } + + public boolean hasSymbol() { + return true; + } + + public Symbol symbol() { + return symbol; + } + + public Tree setSymbol(Symbol symbol) { + this.symbol = symbol; + return this; + } + } + + public static class ExtIdent extends Ident { + private Symbol symbol; + + public ExtIdent(Name name) { + super(name); + } + + public boolean hasSymbol() { + return true; + } + + public Symbol symbol() { + return symbol; + } + + public Tree setSymbol(Symbol symbol) { + this.symbol = symbol; + return this; + } + } + + public static class ExtTemplate extends Template { + private Symbol symbol; + + public ExtTemplate(Tree[] parents, Tree[] body) { + super(parents, body); + } + + public boolean hasSymbol() { + return true; + } + + public Symbol symbol() { + return symbol; + } + + public Tree setSymbol(Symbol symbol) { + this.symbol = symbol; + return this; + } + + public boolean definesSymbol() { + return true; + } + } + + public boolean isTerm() { + switch(this) { + case Bad(): + case Empty: + case Tuple(_): + case If(_, _, _): + case Typed(_, _): + case Apply(_, _): + case TypeApply(_, _): + case Visitor(_): + case New(_): + case Literal(_): + case LabelDef(_,_): + case Block(_): + case Function(_, _): + case Assign(_, _): + case Super(_): + case This(_): + return true; + case Ident(Name name): + return !name.isTypeName(); + case Select(_, Name name): + return !name.isTypeName(); + default: + return false; + } + } + + public boolean isType() { + switch(this) { + case Bad(): + case Empty: + case SingletonType(_): + case SelectFromType(_, _): + case CompoundType(_, _): + case FunType(_, _): + case TupleType(_): + case AppliedType(_, _): + case CovariantType(_): + return true; + case Ident(Name name): + return name.isTypeName(); + case Select(_, Name name): + return name.isTypeName(); + default: + return false; + } + } +} + diff --git a/sources/scalac/ast/TreeCopyFactory.java b/sources/scalac/ast/TreeCopyFactory.java new file mode 100644 index 0000000000..5a9074c39f --- /dev/null +++ b/sources/scalac/ast/TreeCopyFactory.java @@ -0,0 +1,165 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.ast; + +import scalac.util.Name; +import Tree.*; + +/** + * Interface for a kind of factory which, for each node constructor, + * takes an original node from which some data will be copied or shared. + * + * @author Michel Schinz + * @version 1.1 + */ +public interface TreeCopyFactory { + + public void attribute(Tree newTree, Tree oldTree); + + public Tree Bad(Tree tree); + + public Tree ClassDef(Tree tree, + int mods, + Name name, + TypeDef[] tparams, + ValDef[][] vparams, + Tree tpe, + Template impl); + + public Tree PackageDef(Tree tree, + Tree packaged, + Template impl); + + public Tree ModuleDef(Tree tree, + int mods, + Name name, + Tree tpe, + Template impl); + + public Tree ValDef(Tree tree, + int mods, + Name name, + Tree tpe, + Tree rhs); + + public Tree PatDef(Tree tree, + int mods, + Tree pat, + Tree rhs); + + public Tree DefDef(Tree tree, + int mods, + Name name, + TypeDef[] tparams, + ValDef[][] vparams, + Tree tpe, + Tree rhs); + + public Tree TypeDef(Tree tree, + int mods, + Name name, + TypeDef[] tparams, + Tree rhs); + + public Tree Import(Tree tree, + Tree expr, + Name[] selectors); + + public Tree CaseDef(Tree tree, + Tree pat, + Tree guard, + Tree body); + + public Template Template(Tree tree, + Tree[] baseClasses, + Tree[] body); + + public Tree LabelDef(Tree tree, + Tree[] params, + Tree rhs); + + public Tree Block(Tree tree, + Tree[] stats); + + public Tree Tuple(Tree tree, + Tree[] trees); + + public Tree Visitor(Tree tree, + CaseDef[] cases); + + public Tree Function(Tree tree, + ValDef[] vparams, + Tree body); + + public Tree Assign(Tree tree, + Tree lhs, + Tree rhs); + + public Tree If(Tree tree, + Tree cond, + Tree thenp, + Tree elsep); + + public Tree New(Tree tree, + Template templ); + + public Tree Typed(Tree tree, + Tree expr, + Tree tpe); + + public Tree TypeApply(Tree tree, + Tree fun, + Tree[] args); + + public Tree Apply(Tree tree, + Tree fun, + Tree[] args); + + public Tree Super(Tree tree, + Tree tpe); + + public Tree This(Tree tree, + Tree qualifier); + + public Tree Select(Tree tree, + Tree qualifier, + Name selector); + + public Tree Ident(Tree tree, + Name name); + + public Tree Literal(Tree tree, + Object value); + + public Tree SingletonType(Tree tree, + Tree ref); + + public Tree SelectFromType(Tree tree, + Tree qualifier, + Name selector); + + public Tree FunType(Tree tree, + Tree[] argtpes, + Tree restpe); + + public Tree CompoundType(Tree tree, + Tree[] baseTypes, + Tree[] refinements); + + public Tree TupleType(Tree tree, + Tree[] types); + + public Tree AppliedType(Tree tree, + Tree tpe, + Tree[] args); + + public Tree CovariantType(Tree tree, + Tree tpe); + +} diff --git a/sources/scalac/ast/TreeCreator.java b/sources/scalac/ast/TreeCreator.java new file mode 100644 index 0000000000..6cb8afa1d8 --- /dev/null +++ b/sources/scalac/ast/TreeCreator.java @@ -0,0 +1,293 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.ast; + +import Tree.*; +import scalac.util.Name; + + +public class TreeCreator implements TreeFactory { + + public Tree Bad(int pos) { + Tree t = new ExtBad(); + t.pos = pos; + return t; + } + + public Tree ClassDef(int pos, + int mods, + Name name, + TypeDef[] tparams, + ValDef[][] vparams, + Tree tpe, + Template impl) { + Tree t = new ExtClassDef(mods, name, tparams, vparams, tpe, impl); + t.pos = pos; + return t; + } + + public Tree PackageDef(int pos, + Tree packaged, + Template impl) { + Tree t = new PackageDef(packaged, impl); + t.pos = pos; + return t; + } + + public Tree ModuleDef(int pos, + int mods, + Name name, + Tree tpe, + Template body) { + Tree t = new ExtModuleDef(mods, name, tpe, body); + t.pos = pos; + return t; + } + + public Tree ValDef(int pos, + int mods, + Name name, + Tree tpe, + Tree rhs) { + Tree t = new ExtValDef(mods, name, tpe, rhs); + t.pos = pos; + return t; + } + + public Tree PatDef(int pos, + int mods, + Tree pat, + Tree rhs) { + Tree t = new PatDef(mods, pat, rhs); + t.pos = pos; + return t; + } + + public Tree DefDef(int pos, + int mods, + Name name, + TypeDef[] tparams, + ValDef[][] vparams, + Tree tpe, + Tree rhs) { + Tree t = new ExtDefDef(mods, name, tparams, vparams, tpe, rhs); + t.pos = pos; + return t; + } + + + public Tree TypeDef(int pos, + int mods, + Name name, + TypeDef[] tparams, + Tree rhs) { + Tree t = new ExtTypeDef(mods, name, tparams, rhs); + t.pos = pos; + return t; + } + + public Tree Import(int pos, + Tree expr, + Name[] selectors) { + Tree t = new ExtImport(expr, selectors); + t.pos = pos; + return t; + } + + public CaseDef CaseDef(int pos, + Tree pat, + Tree guard, + Tree body) { + CaseDef t = new CaseDef(pat, guard, body); + t.pos = pos; + return t; + } + + public Template Template(int pos, + Tree[] baseClasses, + Tree[] body) { + Template t = new ExtTemplate(baseClasses, body); + t.pos = pos; + return t; + } + + public Tree LabelDef(int pos, + Tree[] params, + Tree body) { + Tree t = new ExtLabelDef(params,body); + t.pos = pos; + return t; + } + + public Tree Block(int pos, + Tree[] stats) { + Tree t = new Block(stats); + t.pos = pos; + return t; + } + + public Tree Tuple(int pos, + Tree[] trees) { + Tree t = new Tuple(trees); + t.pos = pos; + return t; + } + + public Tree Visitor(int pos, + CaseDef[] cases) { + Tree t = new Visitor(cases); + t.pos = pos; + return t; + } + + public Tree Function(int pos, + ValDef[] vparams, + Tree body) { + Tree t = new Function(vparams, body); + t.pos = pos; + return t; + } + + public Tree Assign(int pos, + Tree lhs, + Tree rhs) { + Tree t = new Assign(lhs, rhs); + t.pos = pos; + return t; + } + + public Tree If(int pos, + Tree cond, + Tree thenp, + Tree elsep) { + Tree t = new If(cond, thenp, elsep); + t.pos = pos; + return t; + } + + public Tree New(int pos, + Template templ) { + Tree t = new New(templ); + t.pos = pos; + return t; + } + + public Tree Typed(int pos, + Tree expr, + Tree tpe) { + Tree t = new Typed(expr, tpe); + t.pos = pos; + return t; + } + + public Tree TypeApply(int pos, + Tree fun, + Tree[] tparams) { + Tree t = new TypeApply(fun, tparams); + t.pos = pos; + return t; + } + + public Tree Apply(int pos, + Tree fun, + Tree[] vparam) { + Tree t = new Apply(fun, vparam); + t.pos = pos; + return t; + } + + public Tree Super(int pos, + Tree tpe) { + Tree t = new Super(tpe); + t.pos = pos; + return t; + } + + public Tree This(int pos, + Tree qualifier) { + Tree t = new This(qualifier); + t.pos = pos; + return t; + } + + public Tree Select(int pos, + Tree qualifier, + Name selector) { + Tree t = new ExtSelect(qualifier, selector); + t.pos = pos; + return t; + } + + public Tree Ident(int pos, + Name name) { + Tree t = new ExtIdent(name); + t.pos = pos; + return t; + } + + public Tree Literal(int pos, + Object value) { + Tree t = new Literal(value); + t.pos = pos; + return t; + } + + + public Tree SingletonType(int pos, Tree ref) { + Tree t = new SingletonType(ref); + t.pos = pos; + return t; + } + + public Tree SelectFromType(int pos, + Tree qualifier, + Name selector) { + Tree t = new ExtSelectFromType(qualifier, selector); + t.pos = pos; + return t; + } + + public Tree FunType(int pos, + Tree[] argtpes, + Tree restpe) { + Tree t = new FunType(argtpes, restpe); + t.pos = pos; + return t; + } + + public Tree CompoundType(int pos, + Tree[] mixins, + Tree[] fields) { + Tree t = new CompoundType(mixins, fields); + t.pos = pos; + return t; + } + + public Tree TupleType(int pos, + Tree[] types) { + Tree t = new TupleType(types); + t.pos = pos; + return t; + } + + public Tree AppliedType(int pos, + Tree tpe, + Tree[] args) { + Tree t = new AppliedType(tpe, args); + t.pos = pos; + return t; + } + + public Tree CovariantType(int pos, + Tree tpe) { + Tree t = new CovariantType(tpe); + t.pos = pos; + return t; + } +} diff --git a/sources/scalac/ast/TreeFactory.java b/sources/scalac/ast/TreeFactory.java new file mode 100644 index 0000000000..31ee67042e --- /dev/null +++ b/sources/scalac/ast/TreeFactory.java @@ -0,0 +1,155 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.ast; + +import scalac.util.Name; +import Tree.*; + +public interface TreeFactory { + + public Tree Bad(int pos); + + public Tree ClassDef(int pos, + int mods, + Name name, + TypeDef[] tparams, + ValDef[][] vparams, + Tree tpe, + Template impl); + + public Tree PackageDef(int pos, + Tree packaged, + Template impl); + + public Tree ModuleDef(int pos, + int mods, + Name name, + Tree tpe, + Template impl); + + public Tree ValDef(int pos, + int mods, + Name name, + Tree tpe, + Tree rhs); + + public Tree PatDef(int pos, + int mods, + Tree pat, + Tree rhs); + + public Tree DefDef(int pos, + int mods, + Name name, + TypeDef[] tparams, + ValDef[][] vparams, + Tree tpe, + Tree rhs); + + public Tree TypeDef(int pos, + int mods, + Name name, + TypeDef[] tparams, + Tree rhs); + + public Tree Import(int pos, + Tree expr, + Name[] selectors); + + public CaseDef CaseDef(int pos, + Tree pat, + Tree guard, + Tree body); + + public Template Template(int pos, + Tree[] baseClasses, + Tree[] body); + + public Tree LabelDef(int pos, + Tree[] params, + Tree rhs); + + public Tree Block(int pos, + Tree[] stats); + + public Tree Tuple(int pos, + Tree[] trees); + + public Tree Visitor(int pos, + CaseDef[] cases); + + public Tree Function(int pos, + ValDef[] vparams, + Tree body); + + public Tree Assign(int pos, + Tree lhs, + Tree rhs); + + public Tree If(int pos, + Tree cond, + Tree thenp, + Tree elsep); + + public Tree New(int pos, + Template templ); + + public Tree Typed(int pos, + Tree expr, + Tree tpe); + + public Tree TypeApply(int pos, + Tree fun, + Tree[] tparams); + + public Tree Apply(int pos, + Tree fun, + Tree[] vparam); + + public Tree Super(int pos, + Tree tpe); + + public Tree This(int pos, + Tree qualifier); + + public Tree Select(int pos, + Tree qualifier, + Name selector); + + public Tree Ident(int pos, + Name name); + + public Tree Literal(int pos, + Object value); + + public Tree SingletonType(int pos, + Tree ref); + + public Tree SelectFromType(int pos, + Tree qualifier, + Name selector); + + public Tree FunType(int pos, + Tree[] argtpes, + Tree restpe); + + public Tree CompoundType(int pos, + Tree[] baseTypes, + Tree[] refinements); + + public Tree TupleType(int pos, + Tree[] types); + + public Tree AppliedType(int pos, + Tree tpe, + Tree[] args); + + public Tree CovariantType(int pos, + Tree tpe); +} diff --git a/sources/scalac/ast/TreeGen.java b/sources/scalac/ast/TreeGen.java new file mode 100644 index 0000000000..26b02eb862 --- /dev/null +++ b/sources/scalac/ast/TreeGen.java @@ -0,0 +1,562 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.ast; + +import java.io.*; +import java.util.*; +import scalac.*; +import scalac.symtab.*; +import scalac.util.*; +import Tree.*; + +/** A helper class for building trees + * + * @author Martin Odersky, Christine Roeckl + * @version 1.0 + */ +public class TreeGen implements Kinds, Modifiers { + + /********************************************************************************/ + /********************************************************************************/ + /** VARIABLES **/ + + /** the global environment + */ + protected Global global; + + /** the global definitions + */ + protected Definitions definitions; + + /** the tree factory + */ + public TreeFactory make; + + /************************************************************************/ + /************************************************************************/ + /** CONSTRUCTORS **/ + + public TreeGen(Global global, TreeFactory make) { + this.global = global; + this.definitions = global.definitions; + this.make = make; + } + + public TreeGen(Global global) { + this(global, global.make); + } + + /*************************************************************************/ + /*************************************************************************/ + /** METHODS **/ + + /** Create a dummy symbol to be used for templates. + */ + public Symbol localDummy(int pos, Symbol owner) { + return new TermSymbol(pos, Name.EMPTY, owner, 0) + .setInfo(Type.NoType); + } + + public Tree mkRef(int pos, Type pre, Symbol sym) { + if (pre == Type.localThisType || pre.symbol().isRoot()) + return Ident(pos, sym); + else + return Select(pos, mkStableId(pos, pre), sym); + } + + /** Build and attribute stable identifier tree corresponding to given prefix. + */ + public Tree mkStableId(int pos, Type pre) { + switch (pre.expandModuleThis()) { + case ThisType(Symbol sym): + return make.This(pos, Ident(pos, sym)).setType(pre); + case SingleType(Type pre1, Symbol sym): + return mkRef(pos, pre1, sym); + default: + throw new ApplicationError(); + } + } + + /** Build and attribute tree corresponding to given type. + */ + public Tree mkType(int pos, Type type) { + Tree tree = mkTycon(pos, type); + switch (type) { + case TypeRef(Type pre, Symbol sym, Type[] args): + if (args.length != 0) + return make.AppliedType(pos, tree, mkType(pos, args)) + .setType(type); + } + return tree; + } + + /** Build and attribute tree corresponding to given type constructor. + */ + public Tree mkTycon(int pos, Type type) { + //System.out.println("making type " + type);//DEBUG + switch (type) { + + case NoType: + return Tree.Empty; + + case ErrorType: + case AnyType: + return make.Bad(pos).setSymbol(Symbol.ERROR).setType(type); + + case ThisType(_): + case SingleType(_, _): + return make.SingletonType(pos, mkStableId(pos, type)).setType(type); + + case TypeRef(Type pre, Symbol sym, Type[] args): + return mkRef(pos, pre, sym); + + case CompoundType(Type[] parents, Scope members): + if (parents.length == 1 && members.elems == Scope.Entry.NONE) + return mkType(pos, parents[0]); + else + return make.CompoundType( + pos, mkType(pos, parents), mkDefs(pos, members.elements())) + .setType(type); + + case CovarType(Type tp): + return make.CovariantType(pos, mkType(pos, tp)) + .setType(type); + + case UnboxedType(_): + case UnboxedArrayType(_): + return make.Ident(pos, Name.fromString(type.toString()).toTypeName()) + .setType(type); + + default: + throw new ApplicationError("illegal type", type); + } + } + + /** Build and attribute tree array corresponding to given type array. + */ + public Tree[] mkType(int pos, Type[] types) { + Tree[] res = new Tree[types.length]; + for (int i = 0; i < types.length; i++) { + res[i] = mkType(pos, types[i]); + } + return res; + } + + /** Build and attribute tree corresponding to symbol's declaration. + */ + public Tree mkDef(int pos, Symbol sym) { + switch (sym.kind) { + case ERROR: + return make.Bad(pos).setSymbol(Symbol.ERROR).setType(Type.ErrorType); + case TYPE: case ALIAS: + return TypeDef(pos, sym); + case VAL: + if (sym.isMethod()) return DefDef(pos, sym, Tree.Empty); + else return Param(pos, sym); + default: + throw new ApplicationError(); + } + } + + /** Build and attribute tree array corresponding to given symbol's declarations. + */ + public Tree[] mkDefs(int pos, Symbol[] syms) { + Tree[] res = new Tree[syms.length]; + for (int i = 0; i < syms.length; i++) { + res[i] = mkDef(pos, syms[i]); + } + return res; + } + + /** Build a tree to be used as a base class constructor for a template. + */ + public Tree mkParentConstr(int pos, Type parentType, Type root) { + switch (parentType) { + case TypeRef(Type pre, Symbol sym, Type[] args): + Tree ref = mkRef(pos, pre, sym.constructor()); + Tree constr = (args.length == 0) ? ref + : TypeApply(ref, mkType(sym.pos, args)); + switch (parentType) { + case MethodType(Symbol[] params, Type restpe): + assert params.length == 0 : parentType; + return Apply(constr, Tree.EMPTY_ARRAY); + default: + return constr; + } + default: + throw global.fail("invalid parent type", parentType); + } + } + + /** Build an array of trees to be used as base classes for a template. + */ + public Tree[] mkParentConstrs(int pos, Type[] parents, Type root) { + Tree[] constrs = new Tree[parents.length]; + for (int i = 0; i < parents.length; ++i) + constrs[i] = mkParentConstr(pos, parents[i], root); + return constrs; + } + + /** Build parameter sections corresponding to type. + */ + public ValDef[][] mkParams(int pos, Type type) { + switch (type) { + case PolyType(Symbol[] tparams, Type restype): + return mkParams(pos, restype); + case MethodType(Symbol[] vparams, Type restype): + ValDef[] params1 = mkParams(pos, vparams); + ValDef[][] paramss = mkParams(pos, restype); + if (paramss.length == 0) { + return new ValDef[][]{params1}; + } else { + ValDef[][] paramss1 = new ValDef[paramss.length + 1][]; + paramss1[0] = params1; + System.arraycopy(paramss, 0, paramss1, 1, paramss.length); + return paramss1; + } + default: + return new ValDef[][]{}; + } + } + + /** Build parameter section corresponding to given array of symbols . + */ + public ValDef[] mkParams(int pos, Symbol[] symbols) { + ValDef[] res = new ValDef[symbols.length]; + for (int i = 0; i < symbols.length; i++) { + res[i] = Param(pos, symbols[i]); + } + return res; + } + + /** Build type parameter section corresponding to given array of symbols . + */ + public TypeDef[] mkTypeParams(int pos, Symbol[] symbols) { + TypeDef[] res = new TypeDef[symbols.length]; + for (int i = 0; i < symbols.length; i++) { + res[i] = (TypeDef)TypeDef(pos, symbols[i]); + } + return res; + } + + /** Build type definition corresponding to given symbol . + */ + public TypeDef TypeDef(int pos, Symbol sym) { + Global.instance.nextPhase(); + Type symtype = sym.info(); + Global.instance.prevPhase(); + return (TypeDef) make.TypeDef( + pos, + sym.flags & SOURCEFLAGS, + sym.name, + mkTypeParams(pos, sym.typeParams()), + mkType(pos, symtype)) + .setSymbol(sym).setType(definitions.UNIT_TYPE); + } + + public Tree TypeDef(Symbol sym) { + return TypeDef(sym.pos, sym); + } + + /** Build parameter + */ + public ValDef Param(int pos, Symbol sym) { + return (ValDef)ValDef(pos, sym, Tree.Empty); + } + + public ValDef Param(Symbol sym) { + return Param(sym.pos, sym); + } + + /** Build and attribute block with given statements, starting + * at given position. The type is the type of the last + * statement in the block. + */ + public Tree Block(int pos, Tree[] stats) { + Type tp = (stats.length == 0) ? definitions.UNIT_TYPE + : stats[stats.length - 1].type; + return make.Block(pos, stats).setType(tp); + } + + /** Build and attribute non-empty block with given statements. + */ + public Tree Block(Tree[] stats) { + return Block(stats[0].pos, stats); + } + + public Tree Typed(Tree tree, Type tp) { + return make.Typed(tree.pos, tree, mkType(tree.pos, tp)).setType(tp); + } + + /** Build and attribute the assignment lhs = rhs + */ + public Tree Assign(int pos, Tree lhs, Tree rhs) { + return make.Assign(pos, lhs, rhs).setType(definitions.UNIT_TYPE); + } + + public Tree Assign(Tree lhs, Tree rhs) { + return Assign(lhs.pos, lhs, rhs); + } + + /** Build and attribute new B, given constructor expression B. + */ + public Tree New(Tree constr) { + Template templ = make.Template( + constr.pos, new Tree[]{constr}, Tree.EMPTY_ARRAY); + templ.setType(constr.type); + templ.setSymbol(localDummy(constr.pos, Symbol.NONE)); + return make.New(constr.pos, templ).setType(constr.type); } + + /** Build an allocation new P.C[TARGS](ARGS) + * given a (singleton) type P, class C, type arguments TARGS and arguments ARGS + */ + public Tree New(int pos, Type pre, Symbol clazz, + Type[] targs, Tree[] args) { + Tree constr = mkRef(pos, pre, clazz.constructor()); + if (targs.length != 0) constr = TypeApply(constr, mkType(pos, targs)); + Tree base = Apply(constr, args); + return New(base); + } + + /** Build a monomorphic allocation new P.C(ARGS) + * given a prefix P, class C and arguments ARGS + */ + public Tree New(int pos, Type pre, Symbol clazz, Tree[] args) { + return New(pos, pre, clazz, Type.EMPTY_ARRAY, args); + } + + /** Build and attribute application node with given function + * and argument trees. + */ + public Tree Apply(int pos, Tree fn, Tree[] args) { + switch (fn.type) { + case Type.MethodType(Symbol[] vparams, Type restpe): + return make.Apply(pos, fn, args).setType(restpe); + default: + throw new ApplicationError("method type required", fn.type); + } + } + + public Tree Apply(Tree fn, Tree[] args) { + return Apply(fn.pos, fn, args); + } + + /** Build and attribute type application node with given function + * and argument trees. + */ + public Tree TypeApply(int pos, Tree fn, Tree[] args) { + switch (fn.type) { + case Type.PolyType(Symbol[] tparams, Type restpe): + return make.TypeApply(pos, fn, args) + .setType(restpe.subst(tparams, Tree.typeOf(args))); + default: + throw new ApplicationError("poly type required", fn.type); + } + } + + public Tree TypeApply(Tree fn, Tree[] args) { + return TypeApply(fn.pos, fn, args); + } + + /** Build and applied type node with given function + * and argument trees. + */ + public Tree AppliedType(int pos, Tree fn, Tree[] args) { + return make.AppliedType(pos, fn, args) + .setType(Type.appliedType(fn.type, Tree.typeOf(args))); + } + + public Tree AppliedType(Tree fn, Tree[] args) { + return AppliedType(fn.pos, fn, args); + } + + /** Build and attribute select node of given symbol. + * It is assumed that the prefix is not empty. + */ + public Tree Select(int pos, Tree qual, Symbol sym) { + assert sym.kind != NONE; + Global.instance.nextPhase(); + Type symtype = qual.type.memberType(sym); + Global.instance.prevPhase(); + if (sym.kind == VAL && qual.type.isStable() && symtype.isObjectType()) + symtype = Type.singleType(qual.type, sym); + return make.Select(pos, qual, sym.name) + .setSymbol(sym).setType(symtype); + } + + public Tree Select(Tree qual, Symbol sym) { + return Select(qual.pos, qual, sym); + } + + public Tree Select(Tree qual, Name name) { + Symbol sym = qual.type.lookup(name); + assert (sym.kind != NONE && sym != Symbol.ERROR) : name + " from " + qual.type; + return Select(qual, sym); + } + + /** Build and attribute ident node with given symbol. + */ + public Tree Ident(int pos, Symbol sym) { + Global.instance.nextPhase(); + Type symtype = sym.type(); + Global.instance.prevPhase(); + if (sym.kind == VAL && symtype.isObjectType()) + symtype = Type.singleType(sym.owner().thisType(), sym); + return make.Ident(pos, sym.name) + .setSymbol(sym).setType(symtype); + } + + public Tree Ident(Symbol sym) { + return Ident(sym.pos, sym); + } + + /** Build and attribute this node with given symbol. + */ + public Tree This(int pos, Symbol sym) { + return make.This(pos, Ident(pos, sym)).setType(sym.thisType()); + } + + /** Build and attribute super node with given type. + */ + public Tree Super(int pos, Type type) { + return make.Super(pos, mkType(pos, type)).setType(type); + } + + /** Build and attribute value/variable/let definition node whose signature + * corresponds to given symbol and which has given rhs. + */ + public Tree ValDef(int pos, Symbol sym, Tree rhs) { + Global.instance.nextPhase(); + Type symtype = sym.type(); + Global.instance.prevPhase(); + return make.ValDef(pos, + sym.flags & SOURCEFLAGS, + sym.name, + mkType(pos, symtype), + rhs) + .setSymbol(sym).setType(definitions.UNIT_TYPE); + } + + public Tree ValDef(Symbol sym, Tree rhs) { + return ValDef(sym.pos, sym, rhs); + } + + /** Build and attribute value/variable/let definition node whose signature + * corresponds to given symbol and which has given body. + */ + public Tree DefDef(int pos, Symbol sym, Tree body) { + Global.instance.nextPhase(); + Type symtype = sym.type(); + Global.instance.prevPhase(); + return make.DefDef(pos, + sym.flags & SOURCEFLAGS, + sym.name, + mkTypeParams(pos, symtype.typeParams()), + mkParams(pos, symtype), + mkType(pos, symtype.resultType()), + body) + .setSymbol(sym).setType(definitions.UNIT_TYPE); + } + + public Tree DefDef(Symbol sym, Tree rhs) { + return DefDef(sym.pos, sym, rhs); + } + + /** Generate class definition from class symbol, parent constructors, and body. + */ + public Tree ClassDef(int pos, Symbol clazz, Tree[] constrs, Tree[] body) { + Global.instance.nextPhase(); + Type clazzinfo = clazz.info(); + Type constrtype = clazz.constructor().info(); + Global.instance.prevPhase(); + switch (clazzinfo) { + case CompoundType(Type[] parents, Scope members): + Template templ = make.Template(pos, constrs, body); + templ.setType(clazzinfo); + templ.setSymbol(localDummy(pos, clazz.owner())); + return make.ClassDef( + pos, + clazz.flags & SOURCEFLAGS, + clazz.name, + mkTypeParams(pos, constrtype.typeParams()), + mkParams(pos, constrtype), + Tree.Empty, + templ) + .setSymbol(clazz).setType(definitions.UNIT_TYPE); + default: + throw new ApplicationError(); + } + } + + public Tree ClassDef(Symbol clazz, Tree[] constrs, Tree[] body) { + return ClassDef(clazz.pos, clazz, constrs, body); + } + + /** Generate class definition from class symbol and body. + * All parents must by parameterless, or take unit parameters. + */ + public Tree ClassDef(int pos, Symbol clazz, Tree[] body) { + Global.instance.nextPhase(); + Type clazzinfo = clazz.info(); + Global.instance.prevPhase(); + return ClassDef(pos, + clazz, + mkParentConstrs(pos, clazzinfo.parents(), clazzinfo), + body); + } + + public Tree ClassDef(Symbol clazz, Tree[] body) { + return ClassDef(clazz.pos, clazz, body); + } + + /** Build the expansion of (() => expr) + * This is: + * { class $clazz() extends scala.Function0 { def apply() = expr } ; new $clazz() } + */ + public Tree mkUnitFunction(Tree expr, Type tp, Symbol owner) { + int pos = expr.pos; + Type f0t = definitions.functionType(Type.EMPTY_ARRAY, tp); + + ClassSymbol clazz = new ClassSymbol( + pos, Names.ANON_CLASS_NAME.toTypeName(), owner, 0); + clazz.setInfo(Type.compoundType(new Type[]{f0t}, new Scope(), clazz)); + clazz.constructor().setInfo( + Type.MethodType( + Symbol.EMPTY_ARRAY, + Type.TypeRef(owner.thisType(), clazz, Type.EMPTY_ARRAY))); + + Symbol applyMeth = new TermSymbol(pos, Names.apply, clazz, FINAL) + .setInfo(Type.MethodType(Symbol.EMPTY_ARRAY, tp)); + clazz.info().members().enter(applyMeth); + + Tree applyDef = DefDef(applyMeth, changeOwner(expr, owner, applyMeth)); + Tree classDef = ClassDef(clazz, new Tree[]{applyDef}); + Tree alloc = New(pos, Type.localThisType, clazz, Tree.EMPTY_ARRAY); + return Block(new Tree[]{classDef, alloc}); + } + + /** Change owner of all defined symbols from `prevOwner' to `newOwner' + */ + public Tree changeOwner(Tree tree, final Symbol prevOwner, final Symbol newOwner) { + Transformer lifter = new Transformer(global, global.currentPhase) { + public Tree transform(Tree tree) { + if (TreeInfo.isDefinition(tree)) { + Symbol sym = tree.symbol(); + if (sym != null && sym.owner() == prevOwner) { + sym.setOwner(newOwner); + if (sym.kind == Kinds.CLASS) + sym.constructor().setOwner(newOwner); + } + } + return super.transform(tree); + } + }; + return lifter.transform(tree); + } +} diff --git a/sources/scalac/ast/TreeInfo.java b/sources/scalac/ast/TreeInfo.java new file mode 100644 index 0000000000..81d9481f31 --- /dev/null +++ b/sources/scalac/ast/TreeInfo.java @@ -0,0 +1,136 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.ast; + +import scalac.ApplicationError; +import scalac.util.Name; +import scalac.symtab.Type; +import scalac.symtab.Symbol; +import scalac.symtab.Modifiers; + +public class TreeInfo { + + public static boolean isTerm(Tree tree) { + return tree.isTerm(); + } + + public static boolean isType(Tree tree) { + return tree.isType(); + } + + public static boolean isOwnerDefinition(Tree tree) { + switch (tree) { + case PackageDef(_, _): + case ClassDef(_, _, _, _, _, _): + case ModuleDef(_, _, _, _): + case DefDef(_, _, _, _, _, _): + case Import(_, _): + return true; + default: + return false; + } + } + + public static boolean isDefinition(Tree tree) { + switch (tree) { + case PackageDef(_, _): + case ClassDef(_, _, _, _, _, _): + case ModuleDef(_, _, _, _): + case DefDef(_, _, _, _, _, _): + case ValDef(_, _, _, _): + case TypeDef(_, _, _, _): + case Import(_, _): + return true; + default: + return false; + } + } + + public static boolean isDeclaration(Tree tree) { + switch (tree) { + case DefDef(_, _, _, _, _, Tree rhs): + return rhs == Tree.Empty; + case ValDef(_, _, _, Tree rhs): + return rhs == Tree.Empty; + case TypeDef(_, _, _, _): + return true; + default: + return false; + } + } + + /** Is tree a pure definition? + */ + public static boolean isPureDef(Tree tree) { + switch (tree) { + case ClassDef(_, _, _, _, _, _): + case ModuleDef(_, _, _, _): + case DefDef(_, _, _, _, _, _): + case TypeDef(_, _, _, _): + case Import(_, _): + return true; + case ValDef(int mods, _, _, Tree rhs): + return (mods & Modifiers.MUTABLE) == 0 && isPureExpr(rhs); + default: + return false; + } + } + + /** Is tree a stable & pure expression? + */ + public static boolean isPureExpr(Tree tree) { + switch (tree) { + case Empty: + case This(_): + case Super(_): + return true; + case Ident(_): + return tree.type.isStable(); + case Select(Tree qual, _): + return tree.type.isStable() && isPureExpr(qual); + case Typed(Tree expr, _): + return isPureExpr(expr); + case Literal(_): + return true; + default: + return false; + } + } + + /** Is tree a pure constructor? + * //todo: update + */ + public static boolean isPureConstr(Tree tree) { + switch (tree) { + case Ident(_): + return tree.symbol() != null && tree.symbol().isPrimaryConstructor(); + case Select(Tree qual, _): + return isPureExpr(qual) && + tree.symbol() != null && tree.symbol().isPrimaryConstructor(); + case TypeApply(Tree constr, _): + return isPureConstr(constr); + default: + return false; + } + } + + /** The method symbol of an application node, or Symbol.NONE, if none exists. + */ + public static Symbol methSymbol(Tree tree) { + switch (tree) { + case Apply(Tree fn, _): + return methSymbol(fn); + case TypeApply(Tree fn, _): + return methSymbol(fn); + default: + if (tree.hasSymbol()) return tree.symbol(); + else return Symbol.NONE; + } + } +} diff --git a/sources/scalac/ast/TreeList.java b/sources/scalac/ast/TreeList.java new file mode 100644 index 0000000000..d35627c686 --- /dev/null +++ b/sources/scalac/ast/TreeList.java @@ -0,0 +1,71 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id +\* */ + +package scalac.ast; + + +public final class TreeList { + Tree[] trees; + int len; + + public TreeList(Tree[] ts) { + trees = ts; + len = ts.length; + } + + public TreeList() { + this(new Tree[4]); + len = 0; + } + + public void append(Tree tree) { + if (len == trees.length) { + Tree[] ts = new Tree[len * 2]; + System.arraycopy(trees, 0, ts, 0, len); + trees = ts; + } + trees[len++] = tree; + } + + public void append(Tree[] ts) { + for (int j = 0; j < ts.length; j++) + append(ts[j]); + } + + public void append(TreeList tl) { + for (int j = 0; j < tl.len; j++) + append(tl.trees[j]); + } + + public int length() { + return len; + } + + public Tree get(int i) { + return trees[i]; + } + + public Tree first() { + return trees[0]; + } + + public Tree removeLast() { + return trees[--len]; + } + + public Tree[] toArray() { + Tree[] ts = new Tree[len]; + System.arraycopy(trees, 0, ts, 0, len); + return ts; + } + + public Tree[] copyTo(Tree[] ts) { + System.arraycopy(trees, 0, ts, 0, len); + return ts; + } +} diff --git a/sources/scalac/ast/parser/Parser.java b/sources/scalac/ast/parser/Parser.java new file mode 100644 index 0000000000..cd491e2af9 --- /dev/null +++ b/sources/scalac/ast/parser/Parser.java @@ -0,0 +1,1704 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.ast.parser; + +import java.util.*; +import scalac.*; +import scalac.util.*; +import scalac.symtab.Modifiers; +import scalac.ast.*; +import Tree.*; + +/** A recursive descent parser for the programming language Scala. + * + * @author Martin Odersky, Matthias Zenger + * @version 1.2 + */ +public class Parser implements Tokens { + + /** the lexical analyzer + */ + Scanner s; + + /** the tree factory + */ + TreeFactory make; + + public Parser(Unit unit) { + s = new Scanner(unit); + make = unit.global.make; + } + + /** this is the general parse method + */ + public Tree[] parse() { + Tree[] ts = compilationUnit(); + accept(EOF); + return ts; + } + +/////// ERROR HANDLING ////////////////////////////////////////////////////// + + private void skip() { + //System.out.println("<skipping> " + s.token2string(s.token));//DEBUG + int nparens = 0; + int nbraces = 0; + while (true) { + switch (s.token) { + case EOF: + return; + case SEMI: + if (nparens == 0 && nbraces == 0) + return; + break; + case RPAREN: + nparens--; + break; + case RBRACE: + if (nbraces == 0) + return; + nbraces--; + break; + case LPAREN: + nparens++; + break; + case LBRACE: + nbraces++; + break; + } + //System.out.println("skipped: " + s.token2string(s.token));//DEBUG + s.nextToken(); + } + } + + Tree syntaxError(String msg, boolean skip) { + return syntaxError(s.pos, msg, skip); + } + + Tree syntaxError(int pos, String msg, boolean skip) { + if (pos != s.errpos) { + s.unit.error(pos, msg); + s.errpos = pos; + } + if (skip) skip(); + return make.Bad(pos); + } + + int accept(int token) { + int pos = s.pos; + if (s.token != token) { + int errpos = ((s.pos >>> Position.LINESHIFT) > + (s.lastpos >>> Position.LINESHIFT)) ? + s.lastpos : s.pos; + syntaxError(errpos, s.token2string(token) + " expected but " + + s.token2string(s.token) + " found.", true); + } + if (s.token == token) s.nextToken(); + return pos; + } + +/////// TOKEN CLASSES ////////////////////////////////////////////////////// + + boolean isModifier() { + return (s.token == ABSTRACT) + || (s.token == FINAL) + || (s.token == PRIVATE) + || (s.token == PROTECTED) +// || (s.token == QUALIFIED) + || (s.token == OVERRIDE); + } + + boolean isLocalClassModifier() { + return (s.token == ABSTRACT) + || (s.token == FINAL); + } + + boolean isDefIntro() { + switch (s.token) { + case VAL: case VAR: case DEF: case CONSTR: case TYPE: + case MODULE: case CLASS: case CASECLASS: case TRAIT: + return true; + default: + return false; + } + } + + boolean isDclIntro() { + switch (s.token) { + case VAL: case VAR: case DEF: case CONSTR: case TYPE: + return true; + default: + return false; + } + } + + boolean isExprIntro() { + switch (s.token) { + case CHARLIT: case INTLIT: case LONGLIT: + case FLOATLIT: case DOUBLELIT: case STRINGLIT: case NULL: + case IDENTIFIER: case THIS: case SUPER: + case IF: case FOR: case NEW: case USCORE: + case LPAREN: case LBRACKET: case LBRACE: + return true; + default: + return false; + } + } + +/////// TREE CONSTRUCTION //////////////////////////////////////////////////// + + /** Name supply + */ + int fresh = 0; + + Name fresh() { + return Name.fromString("x$" + (fresh++)); + } + + /** Create tree representing binary operation expression or pattern. + */ + Tree makeBinop(boolean isExpr, int pos, Tree left, Name op, Tree right) { + if (isExpr) { + if (op.leftAssoc()) { + return make.Apply(pos, + make.Select(pos, left, NameTransformer.encode(op)), + new Tree[]{right}); + } else { + Name x = fresh(); + return make.Block(pos, + new Tree[]{ + make.ValDef(pos, 0, x, Tree.Empty, left), + make.Apply(pos, + make.Select(pos, right, NameTransformer.encode(op)), + new Tree[]{make.Ident(left.pos, x)})}); + } + } else { + return make.Apply(pos, + make.Ident(pos, NameTransformer.encode(op).toTypeName()), + new Tree[]{left, right}); + } + } + + /** Create tree representing type scala.Any + */ + Tree scalaAnyType(int pos) { + return make.Select(pos, make.Ident(pos, Names.scala), Names.Any.toTypeName()); + } + + /** Create tree representing constructor scala.Object + */ + Tree scalaObjectConstr(int pos) { + return make.Select(pos, make.Ident(pos, Names.scala), Names.Object.toConstrName()); + } + + /** Create tree for for-comprehension <for (enums) do body> or + * <for (enums) yield body> where mapName and flatmapName are chosen + * corresponding to whether this is a for-do or a for-yield. + */ + Tree makeFor(Tree[] enums, Name mapName, Name flatmapName, Tree body) { + switch (enums[0]) { + case PatDef(int mods, Tree pat, Tree rhs): + if (enums.length == 1) + return makeFor1(enums[0].pos, mapName, pat, rhs, body); + Tree[] newenums = new Tree[enums.length - 1]; + switch (enums[1]) { + case PatDef(int mods2, Tree pat2, Tree rhs2): + System.arraycopy(enums, 1, newenums, 0, newenums.length); + return makeFor1(enums[0].pos, flatmapName, pat, rhs, + makeFor(newenums, mapName, flatmapName, body)); + default: + System.arraycopy(enums, 2, newenums, 1, newenums.length - 1); + newenums[0] = make.PatDef( + enums[0].pos, mods, pat, + makeFor1(enums[0].pos, Names.filter, pat, rhs, enums[1])); + return makeFor(newenums, mapName, flatmapName, body); + } + default: + throw new ApplicationError(); + } + } + + //where + Tree makeFor1(int pos, Name name, Tree pat, Tree rhs, Tree body) { + Tree cont; + switch (pat) { + case Ident(Name name1): + cont = make.Function(pos, + new Tree.ValDef[]{ + (ValDef) + make.ValDef(pat.pos, Modifiers.PARAM, name1, Tree.Empty, Tree.Empty)}, + body); + break; + default: + cont = make.Visitor(pos, new Tree.CaseDef[]{ + (CaseDef)make.CaseDef(pos, pat, Tree.Empty, body)}); + } + return make.Apply(pos, make.Select(pos, rhs, name), new Tree[]{cont}); + } + + /** Convert tree to formal parameter list + */ + ValDef[] convertToParams(Tree t) { + switch (t) { + case Function(ValDef[] params, Tree.Empty): + return params; + case Ident(_): + case Typed(Ident(_), _): + return new ValDef[]{convertToParam(t)}; + case Block(Tree[] stats): + if (stats.length == 0) return Tree.ExtValDef.EMPTY_ARRAY; + } + syntaxError(t.pos, "malformed formal parameter list", false); + return Tree.ExtValDef.EMPTY_ARRAY; + } + + /** Convert list of trees to formal parameter list + */ + ValDef[] convertToParams(Tree[] ts) { + ValDef[] res = new ValDef[ts.length]; + for (int i = 0; i < res.length; i++) + res[i] = convertToParam(ts[i]); + return res; + } + + /** Convert tree to formal parameter + */ + ValDef convertToParam(Tree tree) { + switch (tree) { + case Ident(Name name): + return (ValDef)make.ValDef( + tree.pos, Modifiers.PARAM, name, Tree.Empty, Tree.Empty); + case Typed(Ident(Name name), Tree tpe): + return (ValDef)make.ValDef( + tree.pos, Modifiers.PARAM, name, tpe, Tree.Empty); + default: + Tree tpe = syntaxError(tree.pos, "not a legal formal parameter", false); + return (ValDef)make.ValDef( + tree.pos, Modifiers.PARAM, Names.ERROR, tpe, Tree.Empty); + } + } + + /** Convert (qual)ident to type identifier + */ + Tree convertToTypeId(Tree t) { + switch (t) { + case Ident(Name name): + return make.Ident(t.pos, name.toTypeName()); + case Select(Tree qual, Name name): + return make.Select(t.pos, qual, name.toTypeName()); + default: + return t; + } + } + + /** Convert (qual)ident to constructor identifier + */ + Tree convertToConstr(Tree t) { + switch (t) { + case Apply(Tree fn, Tree[] args): + return make.Apply(t.pos, convertToConstr(fn), args); + case TypeApply(Tree fn, Tree[] args): + return make.TypeApply(t.pos, convertToConstr(fn), args); + case Ident(Name name): + return make.Ident(t.pos, name.toConstrName()); + case Select(Tree qual, Name name): + return make.Select(t.pos, qual, name.toConstrName()); + default: + return syntaxError(t.pos, "class constructor expected", false); + } + } + +/////// OPERAND/OPERATOR STACK ///////////////////////////////////////////////// + + Tree[] operands = new Tree[8]; + int[] positions = new int[8]; + Name[] operators = new Name[8]; + int sp = 0; + + void push(Tree od, int pos, Name op) { + if (sp == operands.length) { + Tree[] operands1 = new Tree[sp * 2]; + System.arraycopy(operands, 0, operands1, 0, sp); + operands = operands1; + int[] positions1 = new int[sp * 2]; + System.arraycopy(positions, 0, positions1, 0, sp); + positions = positions1; + Name[] operators1 = new Name[sp * 2]; + System.arraycopy(operators, 0, operators1, 0, sp); + operators = operators1; + } + operands[sp] = od; + positions[sp] = pos; + operators[sp] = op; + sp++; + } + + Tree reduceStack(boolean isExpr, int base, Tree top, + int prec, boolean leftAssoc) { + if (sp != base && + operators[sp-1].precedence() == prec && + operators[sp-1].leftAssoc() != leftAssoc) { + syntaxError( + positions[sp-1], + "left- and right-associative operators with same precedence may not be mixed", + false); + } + while (sp != base && + (prec < operators[sp-1].precedence() || + (leftAssoc && prec == operators[sp-1].precedence()))) { + sp--; + top = makeBinop(isExpr, positions[sp], operands[sp], operators[sp], top); + } + return top; + } + +/////// IDENTIFIERS AND LITERALS //////////////////////////////////////////////////////////// + + static final Name MINUS = Name.fromString("-"); + static final Name PLUS = Name.fromString("+"); + static final Name BANG = Name.fromString("!"); + static final Name TILDE = Name.fromString("~"); + + Name ident() { + if (s.token == IDENTIFIER) { + Name name = NameTransformer.encode(s.name); + s.nextToken(); + return name; + } else { + accept(IDENTIFIER); + return Names.ERROR; + } + } + + /** StableRef ::= StableId + * | [Ident `.'] this + * SimpleType ::= StableRef [`.' type] + */ + Tree stableRef(boolean thisOK, boolean typeOK) { + Tree t; + if (s.token == THIS) { + t = make.This(s.skipToken(), Tree.Empty); + if (!thisOK || s.token == DOT) + t = selectors(accept(DOT), t, typeOK); + } else { + t = make.Ident(s.pos, ident()); + if (s.token == DOT) { + int pos = s.skipToken(); + if (s.token == THIS) { + s.nextToken(); + t = make.This(pos, convertToTypeId(t)); + if (!thisOK || s.token == DOT) + t = selectors(accept(DOT), t, typeOK); + } else { + t = selectors(pos, t, typeOK); + } + } + } + return t; + } + + Tree selectors(int pos, Tree t, boolean typeOK) { + if (typeOK && s.token == TYPE) { + s.nextToken(); + return make.SingletonType(pos, t); + } else { + t = make.Select(pos, t, ident()); + if (s.token == DOT) { + t = selectors(s.skipToken(), t, typeOK); + } + return t; + } + } + + /** StableId ::= [[Ident `.'] this `.'] {Id `.'} Id + */ + Tree stableId() { + return stableRef(false, false); + } + + /** QualId ::= Id {`.' Id} + */ + Tree qualId() { + Tree id = make.Ident(s.pos, ident()); + if (s.token == DOT) return selectors(s.skipToken(), id, false); + else return id; + } + + /** SimpleExpr ::= literal + * | null + */ + Tree literal() { + Tree t; + switch (s.token) { + case CHARLIT: + t = make.Literal(s.pos, new Character((char)s.intVal)); + break; + case INTLIT: + t = make.Literal(s.pos, new Integer((int)s.intVal)); + break; + case LONGLIT: + t = make.Literal(s.pos, new Long(s.intVal)); + break; + case FLOATLIT: + t = make.Literal(s.pos, new Float((float)s.floatVal)); + break; + case DOUBLELIT: + t = make.Literal(s.pos, new Double(s.floatVal)); + break; + case STRINGLIT: + t = make.Literal(s.pos, s.name.toString()); + break; + case NULL: + t = make.Ident(s.pos, Names.null_); + break; + default: + return syntaxError("illegal literal", true); + } + s.nextToken(); + return t; + } + +//////// TYPES /////////////////////////////////////////////////////////////// + + /** TypedOpt ::= [`:' Type] + */ + Tree typedOpt() { + if (s.token == COLON) { + s.nextToken(); + return type(); + } else { + return Tree.Empty; + } + } + + /** Types ::= Type {`,' Type} + */ + Tree[] types() { + TreeList ts = new TreeList(); + ts.append(type()); + while (s.token == COMMA) { + s.nextToken(); + ts.append(type()); + } + return ts.toArray(); + } + + /** Type ::= Type1 `=>' Type + * | `(' [Types] `)' `=>' Type + * | Type1 + */ + Tree type() { + Tree t; + if (s.token == LPAREN) { + s.nextToken(); + if (s.token == RPAREN) { + s.nextToken(); + int pos = accept(ARROW); + return make.FunType(pos, Tree.EMPTY_ARRAY, type()); + } else { + t = type(); + if (s.token == COMMA) { + s.nextToken(); + TreeList ts = new TreeList(); + ts.append(t); + ts.append(types()); + accept(RPAREN); + int pos = accept(ARROW); + return make.FunType(pos, ts.toArray(), type()); + } else { + accept(RPAREN); + } + } + } else { + t = type1(); + } + if (s.token == ARROW) + return make.FunType(s.skipToken(), new Tree[]{t}, type()); + else + return t; + } + + /** Type1 ::= SimpleType {with SimpleType} [with Refinement] + */ + Tree type1() { + int pos = s.pos; + Tree t = simpleType(); + if (s.token == WITH) { + TreeList ts = new TreeList(); + ts.append(t); + while (s.token == WITH) { + s.nextToken(); + if (s.token == LBRACE) + return make.CompoundType(pos, ts.toArray(), refinement()); + else + ts.append(simpleType()); + } + return make.CompoundType(pos, ts.toArray(), Tree.EMPTY_ARRAY); + } else { + return t; + } + } + + /** SimpleType ::= SimpleType TypeArgs + * | SimpleType `#' Id + * | StableId + * | StableRef `.' type + * | `[' Types `]' + * | `(' Type `)' + */ + Tree simpleType() { + int pos = s.pos; + Tree t; + if (s.token == LPAREN) { + s.nextToken(); + t = type(); + accept(RPAREN); + } else if (s.token == LBRACKET) { + s.nextToken(); + Tree[] ts = types(); + accept(RBRACKET); + t = make.TupleType(pos, ts); + } else { + t = convertToTypeId(stableRef(false, true)); + } + while (true) { + if (s.token == HASH) + t = make.SelectFromType(s.skipToken(), t, ident().toTypeName()); + else if (s.token == LBRACKET) + t = make.AppliedType(pos, t, varTypeArgs()); + else break; + } + return t; + } + + /** TypeArgs ::= `[' Types `]' + */ + Tree[] typeArgs() { + accept(LBRACKET); + Tree[] ts = types(); + accept(RBRACKET); + return ts; + } + + /** VarTypeArgs ::= `[' VarType {`,' VarType} `]' + */ + Tree[] varTypeArgs() { + int pos = accept(LBRACKET); + TreeList ts = new TreeList(); + ts.append(varType()); + while (s.token == COMMA) { + s.nextToken(); + ts.append(varType()); + } + accept(RBRACKET); + return ts.toArray(); + } + + /** VarType ::= [`+'] Type + */ + Tree varType() { + int pos = s.pos; + if (s.token == IDENTIFIER && s.name == PLUS) + return make.CovariantType(s.skipToken(), type()); + else + return type(); + } + +//////// EXPRESSIONS //////////////////////////////////////////////////////// + + /** EqualsExpr ::= `=' Expr + */ + Tree equalsExpr() { + accept(EQUALS); + return expr(); + } + + /** Exprs ::= Expr {`,' Expr} + */ + Tree[] exprs() { + TreeList ts = new TreeList(); + ts.append(expr()); + while (s.token == COMMA) { + s.nextToken(); + ts.append(expr()); + } + return ts.toArray(); + } + + /** Expr ::= [Bindings `=>'] Expr + * | if `(' Expr `)' Expr [[`;'] else Expr] + * | for `(' Enumerators `)' [do | yield] Expr + * | Designator `=' Expr + * | SimpleExpr ArgumentExprs `=' Expr + * | PostfixExpr [`:' Type1 | as Type1 | is Type1] + * Bindings ::= Id [`:' Type1] + * | `(' [Binding {`,' Binding}] `)' + */ + Tree expr() { + if (s.token == IF) { + int pos = s.skipToken(); + accept(LPAREN); + Tree cond = expr(); + accept(RPAREN); + Tree thenp = expr(); + Tree elsep = Tree.Empty; + if (s.token == ELSE) { + s.nextToken(); + elsep = expr(); + } else { + elsep = Tree.Empty; + } + return make.If(pos, cond, thenp, elsep) ; + } else if (s.token == FOR) { + s.nextToken(); + accept(LPAREN); + Tree[] enums = enumerators(); + accept(RPAREN); + if (s.token == DO) { + s.nextToken(); + return makeFor(enums, Names.foreach, Names.foreach, expr()); + } else if (s.token == YIELD) { + s.nextToken(); + return makeFor(enums, Names.map, Names.flatmap, expr()); + } else { + return syntaxError("`do' or `yield' expected", true); + } +// } else if (s.token == ARROW) { +// return make.Function(s.skipToken(), new ValDef[]{}, expr()); + } else { + Tree t = postfixExpr(); + if (s.token == EQUALS) { + switch (t) { + case Ident(_): + case Select(_, _): + case Apply(_, _): + t = make.Assign(s.skipToken(), t, expr()); + } + } else if (s.token == COLON) { + int pos = s.skipToken(); + Tree tp = type1(); + t = make.Typed(pos, t, tp); + } else if (s.token == AS || s.token == IS) { + Name op = (s.token == AS) ? Names.as : Names.is; + int pos = s.skipToken(); + t = make.TypeApply(pos, make.Select(pos, t, op), new Tree[]{type1()}); + } + if (s.token == ARROW) { + t = make.Function(s.skipToken(), convertToParams(t), expr()); + } + return t; + } + } + + /** PostfixExpr ::= InfixExpr [Id] + * InfixExpr ::= PrefixExpr + * | InfixExpr Id InfixExpr + */ + Tree postfixExpr() { + int base = sp; + Tree top = prefixExpr(); + while (s.token == IDENTIFIER) { + top = reduceStack( + true, base, top, s.name.precedence(), s.name.leftAssoc()); + push(top, s.pos, s.name); + ident(); + if (isExprIntro()) { + top = prefixExpr(); + } else { + sp--; + int pos = positions[sp]; + Name postOp = operators[sp]; + top = reduceStack(true, base, operands[sp], 0, true); + return make.Select(pos, top, postOp); + } + } + return reduceStack(true, base, top, 0, true); + } + + /** PrefixExpr ::= [op] SimpleExpr + */ + Tree prefixExpr() { + Tree t; + if (s.token == IDENTIFIER && + (s.name == MINUS || + s.name == PLUS || + s.name == TILDE || + s.name == BANG)) { + Name name = ident(); + t = make.Select(s.pos, simpleExpr(), name); + } else { + t = simpleExpr(); + } + return t; + } + + /* SimpleExpr ::= literal + * | null + * | StableRef + * | super `.' Id + * | SimpleExpr `.' Id + * | `(' [Expr] `)' + * | `[' [Exprs] `]' + * | BlockExpr + * | SimpleExpr `@' TypeArgs + * | SimpleExpr ArgumentExprs + * | new Template + * | `_' + */ + Tree simpleExpr() { + Tree t; + switch (s.token) { + case CHARLIT: + case INTLIT: + case LONGLIT: + case FLOATLIT: + case DOUBLELIT: + case STRINGLIT: + case NULL: + t = literal(); + break; + case IDENTIFIER: + case THIS: + t = stableRef(true, false); + break; + case SUPER: + int pos = s.skipToken(); + t = make.Select(accept(DOT), make.Super(pos, Tree.Empty), ident()); + break; + case LPAREN: + int pos = s.skipToken(); + if (s.token == RPAREN) { + s.nextToken(); + t = make.Block(pos, Tree.EMPTY_ARRAY); + } else { + t = expr(); + if (s.token == COMMA) { + int commapos = s.skipToken(); + TreeList ts = new TreeList(); + ts.append(t); + ts.append(exprs()); + accept(RPAREN); + if (s.token == ARROW) { + t = make.Function(pos, convertToParams(ts.toArray()), Tree.Empty); + } else { + t = syntaxError(commapos, "`)' expected", false); + } + } else { + accept(RPAREN); + } + } + break; + case LBRACKET: + int pos = s.skipToken(); + Tree[] ts; + if (s.token == RBRACKET) ts = Tree.EMPTY_ARRAY; + else ts = exprs(); + t = make.Tuple(pos, ts); + accept(RBRACKET); + break; + case LBRACE: + t = blockExpr(); + break; + case NEW: + t = make.New(s.skipToken(), template()); + break; + default: + return syntaxError("illegal start of expression", true); + } + while (true) { + switch (s.token) { + case DOT: + t = make.Select(s.skipToken(), t, ident()); + break; + case AT: + int pos = s.skipToken(); + t = make.TypeApply(pos, t, typeArgs()); + break; + case LPAREN: + case LBRACKET: + case LBRACE: + t = make.Apply(s.pos, t, argumentExprs()); + break; + default: + return t; + } + } + } + + /** ArgumentExprs ::= `(' [Exprs] `)' + * | `[' [Exprs] `]' + * | BlockExpr + */ + Tree[] argumentExprs() { + Tree[] ts = Tree.EMPTY_ARRAY; + if (s.token == LBRACE) { + ts = new Tree[]{blockExpr()}; + } else if (s.token == LBRACKET) { + int pos = s.skipToken(); + if (s.token != RBRACKET) + ts = exprs(); + accept(RBRACKET); + ts = new Tree[]{make.Tuple(pos, ts)}; + } else { + accept(LPAREN); + if (s.token != RPAREN) + ts = exprs(); + accept(RPAREN); + } + return ts; + } + + /** BlockExpr ::= `{' CaseClause {CaseClause} `}' + * | `{' Block `}' + */ + Tree blockExpr() { + Tree res; + int pos = accept(LBRACE); + if (s.token == CASE) { + TreeList stats = new TreeList(); + do { + stats.append(caseClause()); + } while (s.token == CASE); + res = make.Visitor( + pos, (CaseDef[]) stats.copyTo(new CaseDef[stats.length()])); + } else { + res = block(pos); + } + accept(RBRACE); + return res; + } + + /** BlockConstr ::= `{' Block `}' + */ + Tree blockConstr() { + int pos = accept(LBRACE); + Tree res = block(pos); + switch (res) { + case Block(Tree[] stats): + if (stats.length > 0) + stats[stats.length - 1] = convertToConstr(stats[stats.length - 1]); + else + syntaxError(res.pos, "class constructor expected", false); + } + accept(RBRACE); + return res; + } + + /** Block ::= BlockStatSeq + */ + Tree block(int pos) { + Tree[] stats = blockStatSeq(new TreeList()); + if (stats.length == 1) return stats[0]; + else return make.Block(pos, stats); + } + + /** CaseClause ::= case Pattern [if `(' Expr `)'] `=>' Block + */ + Tree caseClause() { + int pos = accept(CASE); + Tree pat = pattern(); + Tree guard = Tree.Empty; + if (s.token == IF) { + s.nextToken(); + accept(LPAREN); + guard = expr(); + accept(RPAREN); + } + accept(ARROW); + return make.CaseDef(pos, pat, guard, block(s.pos)); + } + + /** Enumerators ::= Generator {`;' Enumerator} + * Enumerator ::= Generator + * | Expr + */ + Tree[] enumerators() { + TreeList enums = new TreeList(); + enums.append(generator()); + while (s.token == SEMI) { + s.nextToken(); + if (s.token == VAL) enums.append(generator()); + else enums.append(expr()); + } + return enums.toArray(); + } + + /** Generator ::= val Pattern `<-' Expr + */ + Tree generator() { + int pos = accept(VAL); + Tree p = pattern(); + accept(LARROW); + return make.PatDef(pos, 0, p, expr()); + } + +//////// PATTERNS //////////////////////////////////////////////////////////// + + /** Patterns ::= Pattern {`,' Pattern} + */ + Tree[] patterns() { + TreeList ts = new TreeList(); + ts.append(pattern()); + while (s.token == COMMA) { + s.nextToken(); + ts.append(pattern()); + } + return ts.toArray(); + } + + /** Pattern ::= varid `:' Type1 + * | `_' `:' Type1 + * | SimplePattern {Id SimplePattern} + */ + Tree pattern() { + int base = sp; + Tree top = simplePattern(); + if (s.token == COLON) { + switch (top) { + case Ident(Name name): + if (name.isVariable()) + return make.Typed(s.skipToken(), top, type1()); + } + } + while (s.token == IDENTIFIER) { + top = reduceStack( + false, base, top, s.name.precedence(), s.name.leftAssoc()); + push(top, s.pos, s.name); + ident(); + top = simplePattern(); + } + return reduceStack(false, base, top, 0, true); + } + + /** SimplePattern ::= varid + * | `_' + * | literal + * | null + * | StableId {ArgumentPatterns} + * | `(' Pattern `)' + * | `[' [Patterns] `]' + */ + Tree simplePattern() { + switch (s.token) { + case IDENTIFIER: + case THIS: + Tree t = stableId(); + while (s.token == LPAREN || s.token == LBRACKET) { + t = make.Apply(s.pos, convertToConstr(t), argumentPatterns()); + } + return t; + case USCORE: + return make.Ident(s.skipToken(), Names.WILDCARD); + case CHARLIT: + case INTLIT: + case LONGLIT: + case FLOATLIT: + case DOUBLELIT: + case STRINGLIT: + case NULL: + return literal(); + case LPAREN: + s.nextToken(); + Tree t = pattern(); + accept(RPAREN); + return t; + case LBRACKET: + return tuplePattern(); + default: + return syntaxError("illegal start of pattern", true); + } + } + + /** SimplePattern ::= `[' [Patterns] ']' + */ + Tree tuplePattern() { + int pos = accept(LBRACKET); + Tree[] ts; + if (s.token == RBRACKET) ts = Tree.EMPTY_ARRAY; + else ts = patterns(); + accept(RBRACKET); + return make.Tuple(pos, ts); + } + + /** ArgumentPatterns ::= `(' [Patterns] `)' + * | `[' [Patterns] `]' + */ + Tree[] argumentPatterns() { + if (s.token == LBRACKET) { + return new Tree[]{tuplePattern()}; + } else { + Tree[] ts = Tree.EMPTY_ARRAY; + accept(LPAREN); + if (s.token != RPAREN) + ts = patterns(); + accept(RPAREN); + return ts; + } + } + +////////// MODIFIERS //////////////////////////////////////////////////////////// + + /** Modifiers ::= {Modifier} + * Modifier ::= final + * | private + * | protected + * | override + * | abstract + */ + int modifiers() { + int mods = 0; + while (true) { + int mod; + switch (s.token) { + case ABSTRACT: + mod = Modifiers.ABSTRACTCLASS; + break; + case FINAL: + mod = Modifiers.FINAL; + break; + case PRIVATE: + mod = Modifiers.PRIVATE; + break; + case PROTECTED: + mod = Modifiers.PROTECTED; + break; + case OVERRIDE: + mod = Modifiers.OVERRIDE; + break; + default: + return mods; + } + if ((mods & mod) != 0) + syntaxError(s.pos, "repeated modifier", false); + mods |= mod; + s.nextToken(); + } + } + + /** LocalClassModifiers ::= {LocalClassModifier} + * LocalClassModifier ::= final + * | private + */ + int localClassModifiers() { + int mods = 0; + while (true) { + int mod; + switch (s.token) { + case ABSTRACT: + mod = Modifiers.ABSTRACTCLASS; + break; + case FINAL: + mod = Modifiers.FINAL; + break; + default: + return mods; + } + if ((mods & mod) != 0) + syntaxError(s.pos, "repeated modifier", false); + mods |= mod; + s.nextToken(); + } + } + +//////// PARAMETERS ////////////////////////////////////////////////////////// + + /** ParamClauses ::= {ParamClause} + */ + ValDef[][] paramClauses() { + ArrayList ts = new ArrayList(); + while (s.token == LPAREN) + ts.add(paramClause()); + return (ValDef[][])ts.toArray(new ValDef[ts.size()][]); + } + + /** ParamClause ::= `(' [Param {`,' Param}] `)' + */ + ValDef[] paramClause() { + int pos = accept(LPAREN); + TreeList params = new TreeList(); + if (s.token != RPAREN) { + params.append(param()); + while (s.token == COMMA) { + s.nextToken(); + params.append(param()); + } + } + accept(RPAREN); + return (ValDef[])params.copyTo(new ValDef[params.length()]); + } + + /** Param ::= [def] Id `:' Type + */ + ValDef param() { + int pos = s.pos; + int mods = Modifiers.PARAM; + if (s.token == DEF) { + mods |= Modifiers.DEF; + s.nextToken(); + } + Name name = ident(); + accept(COLON); + return (ValDef)make.ValDef(pos, mods, name, type(), Tree.Empty); + } + + /** TypeParamClauseOpt ::= [`[' TypeSig {`,' TypeSig} `]'] + */ + TypeDef[] typeParamClauseOpt() { + TreeList params = new TreeList(); + if (s.token == LBRACKET) { + s.nextToken(); + params.append(typeParam()); + while (s.token == COMMA) { + s.nextToken(); + params.append(typeParam()); + } + accept(RBRACKET); + } + return (TypeDef[])params.copyTo(new TypeDef[params.length()]); + } + + /** TypeSig ::= Id [<: Type] + */ + Tree typeParam() { + int pos = s.pos; + Name name = ident(); + Tree tp; + if (s.token == SUBTYPE) { + s.nextToken(); + tp = type(); + } else { + tp = scalaAnyType(pos); + } + return make.TypeDef(pos, Modifiers.PARAM, name.toTypeName(), + Tree.ExtTypeDef.EMPTY_ARRAY, tp); + } + +//////// DEFS //////////////////////////////////////////////////////////////// + + /** Import ::= import ImportRef {`,' ImportRef} + */ + Tree[] importClause() { + accept(IMPORT); + TreeList ts = new TreeList(); + ts.append(importRef()); + while (s.token == COMMA) { + s.nextToken(); + ts.append(importRef()); + } + return ts.toArray(); + } + + /** ImportRef ::= StableId `.' (Id | `_' | ImportSelectors) + */ + Tree importRef() { + Tree t; + int startpos = s.pos; + int pos; + if (s.token == THIS) { + t = make.This(s.skipToken(), Tree.Empty); + t = make.Select(accept(DOT), t, ident()); + pos = accept(DOT); + } else { + t = make.Ident(s.pos, ident()); + pos = accept(DOT); + if (s.token == THIS) { + s.nextToken(); + t = make.This(pos, convertToTypeId(t)); + t = make.Select(accept(DOT), t, ident()); + pos = accept(DOT); + } + } + while (true) { + if (s.token == USCORE) { + s.nextToken(); + return make.Import(startpos, t, new Name[]{Names.WILDCARD}); + } else if (s.token == LBRACE) { + return make.Import(startpos, t, importSelectors()); + } else { + Name name = ident(); + if (s.token == DOT) { + t = make.Select(pos, t, name); + pos = accept(DOT); + } else { + return make.Import(startpos, t, new Name[]{name, name}); + } + } + } + } + + /** ImportSelectors ::= `{' {ImportSelector `,'} (ImportSelector | `_') `}' + */ + Name[] importSelectors() { + LinkedList/*<Name>*/ names = new LinkedList(); + accept(LBRACE); + boolean isLast = importSelector(names); + while (!isLast && s.token == COMMA) { + s.nextToken(); + isLast = importSelector(names); + } + accept(RBRACE); + return (Name[])names.toArray(new Name[]{}); + } + + /** ImportSelector ::= Id [`=>' [Id | `_']] + */ + boolean importSelector(LinkedList/*<Name>*/ names) { + if (s.token == USCORE) { + s.nextToken(); + names.add(Names.WILDCARD); + return true; + } else { + Name name = ident(); + names.add(name); + if (s.token == ARROW) { + s.nextToken(); + if (s.token == USCORE) { + s.nextToken(); + names.add(Names.WILDCARD); + } else { + names.add(ident()); + } + } else { + names.add(name); + } + return false; + } + } + + /** Def ::= val PatDef {`,' PatDef} + * | var VarDef {`,' VarDef} + * | def FunDef {`,' FunDef} + * | constr ConstrDef {`,' ConstrDef} + * | type TypeDef {`,' TypeDef} + * | TopDef + * Dcl ::= val ValSig {`,' ValSig} + * | var ValSig {`,' ValSig} + * | def FunSig {`,' FunSig} + * | constr ConstrSig {`,' ConstrSig} + * | type TypeSig {`,' TypeSig} + */ + Tree[] defOrDcl(int mods) { + TreeList ts = new TreeList(); + switch (s.token) { + case VAL: + do { + s.nextToken(); + ts.append(patDefOrSig(mods)); + } while (s.token == COMMA); + return ts.toArray(); + case VAR: + do { + s.nextToken(); + ts.append(varDefOrSig(mods)); + } while (s.token == COMMA); + return ts.toArray(); + case DEF: + do { + s.nextToken(); + ts.append(funDefOrSig(mods)); + } while (s.token == COMMA); + return ts.toArray(); + case CONSTR: + do { + s.nextToken(); + ts.append(constrDefOrSig(mods)); + } while (s.token == COMMA); + return ts.toArray(); + case TYPE: + do { + s.nextToken(); + ts.append(typeDefOrSig(mods)); + } while (s.token == COMMA); + return ts.toArray(); + default: + return topDef(mods); + } + } + + /** TopDef ::= ([case] class) ClassDef {`,' ClassDef} + * | trait TraitDef {`,' TraitDef} + * | module ModuleDef {`,' ModuleDef} + * LocalTopDef ::= class ClassDef {`,' ClassDef} + * | trait TraitDef {`,' TraitDef} + * | module ModuleDef {`,' ModuleDef} + */ + Tree[] topDef(int mods) { + TreeList ts = new TreeList(); + switch (s.token) { + case CLASS: + case CASECLASS: + case TRAIT: + if (s.token == CASECLASS) mods |= Modifiers.CASE; + else if (s.token == TRAIT) mods |= Modifiers.TRAIT | Modifiers.ABSTRACTCLASS; + do { + s.nextToken(); + ts.append(classDef(mods)); + } while (s.token == COMMA); + return ts.toArray(); + case MODULE: + do { + s.nextToken(); + ts.append(moduleDef(mods)); + } while (s.token == COMMA); + return ts.toArray(); + default: + return new Tree[]{syntaxError("illegal start of definition", true)}; + } + } + + /** PatDef ::= Pattern `=' Expr + * ValSig ::= Id `:' Type + */ + Tree patDefOrSig(int mods) { + int pos = s.pos; + Tree pat = pattern(); + Tree tp; + switch (pat) { + case Typed(Tree pat1, Tree tp1): + pat = pat1; + tp = tp1; + break; + default: + if (s.token == COLON) tp = typedOpt(); + else tp = Tree.Empty; + } + switch (pat) { + case Ident(Name name): + if (tp == Tree.Empty || s.token == EQUALS) + return make.ValDef(pos, mods, name, tp, equalsExpr()); + else + return make.ValDef(pos, mods | Modifiers.ABSTRACT, name, tp, Tree.Empty); + default: + return make.PatDef(pos, mods, pat, equalsExpr()); + } + } + + /** VarDef ::= Id [`:' Type] `=' Expr + * | Id `:' Type `=' `_' + * VarSig ::= Id `:' Type + */ + Tree varDefOrSig(int mods) { + int pos = s.pos; + Name name = ident(); + Tree type = typedOpt(); + if (type == Tree.Empty || s.token == EQUALS) { + accept(EQUALS); + Tree rhs; + if (type != Tree.Empty && s.token == USCORE) { + rhs = Tree.Empty; + s.nextToken(); + } else { + rhs = expr(); + } + return make.ValDef(pos, mods | Modifiers.MUTABLE, name, type, rhs); + } else { + return make.ValDef(pos, mods | Modifiers.MUTABLE | Modifiers.ABSTRACT, + name, type, Tree.Empty); + } + } + + /** FunDef ::= Id [TypeParamClause] {ParamClause} [`:' Type] + * (`=' Expr | BlockExpr) + * FunSig ::= Id [TypeParamClause] {ParamClause} `:' Type + */ + Tree funDefOrSig(int mods) { + int pos = s.pos; + Name name = ident(); + TypeDef[] tparams = typeParamClauseOpt(); + ValDef[][] vparams = paramClauses(); + Tree restype = typedOpt(); + if (s.token == LBRACE) + return make.DefDef(pos, mods, name, tparams, vparams, + restype, blockExpr()); + else if (s.token == EQUALS || restype == Tree.Empty) + return make.DefDef(pos, mods, name, tparams, vparams, + restype, equalsExpr()); + else + return make.DefDef(pos, mods | Modifiers.ABSTRACT, name, + tparams, vparams, restype, Tree.Empty); + } + + /* ConstrDef ::= Id [TypeParamClause] [ParamClause] [`:' Type] + * (`=' Constr | `=' BlockConstr | BlockConstr) + */ + Tree constrDefOrSig(int mods) { + int pos = s.pos; + Name name = ident().toConstrName(); + TypeDef[] tparams = typeParamClauseOpt(); + ValDef[][] vparams = new ValDef[][]{paramClause()}; + Tree restype = typedOpt(); + if (s.token == LBRACE) + return make.DefDef(pos, mods, name, tparams, vparams, + restype, blockConstr()); + else if (s.token == EQUALS || restype == Tree.Empty) { + accept(EQUALS); + return make.DefDef(pos, mods, name, tparams, vparams, + restype, (s.token == LBRACE) ? blockConstr() : constr()); + } else + return make.DefDef(pos, mods | Modifiers.ABSTRACT, name, + tparams, vparams, restype, Tree.Empty); + } + + /** TypeDef ::= Id [TypeParamClause] `=' Type + * TypeSig ::= Id [`<:' Type] + */ + Tree typeDefOrSig(int mods) { + int pos = s.pos; + Name name = ident().toTypeName(); + if (s.token == SUBTYPE) { + s.nextToken(); + return make.TypeDef(pos, mods | Modifiers.ABSTRACT, name, + Tree.ExtTypeDef.EMPTY_ARRAY, type()); + } else if (s.token == LBRACKET) { + TypeDef[] tparams = typeParamClauseOpt(); + accept(EQUALS); + return make.TypeDef(pos, mods, name, tparams, type()); + } else if (s.token == EQUALS) { + s.nextToken(); + return make.TypeDef(pos, mods, name, + Tree.ExtTypeDef.EMPTY_ARRAY, type()); + } else if (s.token == SEMI || s.token == COMMA) { + return make.TypeDef( + pos, mods | Modifiers.ABSTRACT, name, + Tree.ExtTypeDef.EMPTY_ARRAY, scalaAnyType(pos)); + } else { + return syntaxError("`=' or `<:' expected", true); + } + } + + /** ClassDef ::= Id [TypeParamClause] ParamClause [`:' Type] ClassTemplate + * TraitDef ::= Id [TypeParamClause] [`:' Type] ClassTemplate + */ + Tree classDef(int mods) { + int pos = s.pos; + Name name = ident(); + TypeDef[] tparams = typeParamClauseOpt(); + ValDef[][] params; + if ((mods & Modifiers.TRAIT) == 0) params = new ValDef[][]{paramClause()}; + else params = new ValDef[][]{}; + return make.ClassDef(pos, mods, name.toTypeName(), tparams, params, + typedOpt(), classTemplate()); + } + + /** ModuleDef ::= Id [`:' Type] ClassTemplate + */ + Tree moduleDef(int mods) { + return make.ModuleDef( + s.pos, mods, ident(), typedOpt(), classTemplate()); + } + + /** ClassTemplate ::= extends Template + * | TemplateBody + */ + Template classTemplate() { + int pos = s.pos; + if (s.token == EXTENDS) { + s.nextToken(); + return template(); + } else if (s.token == LBRACE) { + return (Template)make.Template( + pos, new Tree[]{scalaObjectConstr(pos)}, templateBody()); + } else { + syntaxError("`extends' or `{' expected", true); + return (Template)make.Template( + pos, new Tree[]{scalaObjectConstr(pos)}, Tree.EMPTY_ARRAY); + } + } + +////////// TEMPLATES //////////////////////////////////////////////////////////// + + + /** Template ::= Constr {`with' Constr} [TemplateBody] + */ + Template template() { + int pos = s.pos; + TreeList parents = new TreeList(); + parents.append(constr()); + while (s.token == WITH) { + s.nextToken(); + if (s.token == LBRACE) + return (Template)make.Template(pos, parents.toArray(), templateBody()); + else + parents.append(constr()); + } + Tree[] stats = (s.token == LBRACE) ? templateBody() : Tree.EMPTY_ARRAY; + return (Template)make.Template(pos, parents.toArray(), stats); + } + + /** Constr ::= StableId [TypeArgs] [`(' [Exprs] `)'] + */ + Tree constr() { + Tree t = convertToConstr(stableId()); + if (s.token == LBRACKET) + t = make.TypeApply(s.pos, t, typeArgs()); + if (s.token == LPAREN) + t = make.Apply(s.pos, t, argumentExprs()); + return t; + } + + /** TemplateBody ::= `{' [TemplateStat {`;' TemplateStat}] `}' + */ + Tree[] templateBody() { + accept(LBRACE); + Tree[] body = templateStatSeq(); + accept(RBRACE); + return body; + } + + /** Refinement ::= `{' [RefineStat {`;' RefineStat}] `}' + */ + Tree[] refinement() { + accept(LBRACE); + Tree[] body = refineStatSeq(); + accept(RBRACE); + return body; + } + +/////// STATSEQS ////////////////////////////////////////////////////////////// + + /** Packaging ::= package QualId `{' TopStatSeq `}' + */ + Tree packaging() { + int pos = accept(PACKAGE); + Tree pkg = qualId(); + accept(LBRACE); + Tree[] stats = topStatSeq(); + accept(RBRACE); + return + make.PackageDef(pos, pkg, make.Template(pos, Tree.EMPTY_ARRAY, stats)); + } + + /** TopStatSeq ::= [TopStat {`;' TopStat}] + * TopStat ::= Modifiers TopDef + * | Packaging + * | Import + * | + */ + Tree[] topStatSeq() { + TreeList stats = new TreeList(); + while (s.token != RBRACE && s.token != EOF) { + if (s.token == PACKAGE) { + stats.append(packaging()); + } else if (s.token == IMPORT) { + stats.append(importClause()); + } else if (s.token == CLASS || + s.token == CASECLASS || + s.token == TRAIT || + s.token == MODULE || + isModifier()) { + stats.append(topDef(modifiers())); + } else if (s.token != SEMI) { + syntaxError("illegal start of class or module definition", true); + } + if (s.token != RBRACE && s.token != EOF) accept(SEMI); + } + return stats.toArray(); + } + + /** TemplateStatSeq ::= TemplateStat {`;' TemplateStat} + * TemplateStat ::= Import + * | Modifiers Def + * | Modifiers Dcl + * | Expr + * % | val this `:' Type + * | + */ + Tree[] templateStatSeq() { + TreeList stats = new TreeList(); + while (s.token != RBRACE && s.token != EOF) { + if (s.token == IMPORT) { + stats.append(importClause()); + } else if (isExprIntro()) { + stats.append(expr()); + } else if (isDefIntro() || isModifier()) { + stats.append(defOrDcl(modifiers())); + } else if (s.token != SEMI) { + syntaxError("illegal start of definition", true); + } + if (s.token != RBRACE) accept(SEMI); + } + return stats.toArray(); + } + + /** RefineStatSeq ::= RefineStat {`;' RefineStat} + * RefineStat ::= Dcl + * | type TypeDef {`,' TypeDef} + * | + */ + Tree[] refineStatSeq() { + TreeList stats = new TreeList(); + while (s.token != RBRACE && s.token != EOF) { + if (isDclIntro()) { + stats.append(defOrDcl(0)); + } else if (s.token != SEMI) { + syntaxError("illegal start of declaration", true); + } + if (s.token != RBRACE) accept(SEMI); + } + return stats.toArray(); + } + + /** BlockStatSeq ::= { BlockStat `;' } [Expr] + * BlockStat ::= Import + * | Def + * | LocalClassModifiers LocalTopDef + * | Expr + * | + */ + Tree[] blockStatSeq(TreeList stats) { + while ((s.token != RBRACE) && (s.token != EOF) && (s.token != CASE)) { + if (s.token == IMPORT) { + stats.append(importClause()); + accept(SEMI); + } else if (isExprIntro()) { + stats.append(expr()); + if (s.token != RBRACE && s.token != CASE) accept(SEMI); + } else if (isDefIntro()) { + stats.append(defOrDcl(0)); + accept(SEMI); + } else if (isLocalClassModifier()) { + stats.append(topDef(localClassModifiers())); + accept(SEMI); + } else if (s.token == SEMI) { + s.nextToken(); + } else { + syntaxError("illegal start of statement", true); + } + } + return stats.toArray(); + } + + + /** CompilationUnit = [package QualId `;'] TopStatSeq + */ + Tree[] compilationUnit() { + if (s.token == PACKAGE) { + int pos = s.skipToken(); + Tree pkg = qualId(); + if (s.token == SEMI) { + s.nextToken(); + return new Tree[]{ + make.PackageDef( + pos, pkg, make.Template(pos, Tree.EMPTY_ARRAY, topStatSeq()))}; + } else { + TreeList stats = new TreeList(); + accept(LBRACE); + stats.append( + make.PackageDef( + pos, pkg, make.Template(pos, Tree.EMPTY_ARRAY, topStatSeq()))); + accept(RBRACE); + stats.append(topStatSeq()); + return stats.toArray(); + } + } else { + return topStatSeq(); + } + } +} + diff --git a/sources/scalac/ast/parser/ParserPhase.java b/sources/scalac/ast/parser/ParserPhase.java new file mode 100644 index 0000000000..204fb2b925 --- /dev/null +++ b/sources/scalac/ast/parser/ParserPhase.java @@ -0,0 +1,63 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** +** $Id$ +\* */ + +package scalac.ast.parser; + +import java.io.*; +import scalac.*; + +public class ParserPhase extends PhaseDescriptor { + + public String name() { + return "parse"; + } + + public String description () { + return "parse source files"; + } + + public String taskDescription() { + return "parsed"; + } + + public Phase createPhase(Global global) { + return new ParserWorker(global, this); + } +} + +public class ParserWorker extends Phase { + + /** constructor + */ + public ParserWorker(Global global, PhaseDescriptor descr) { + super(global, descr); + } + + /** apply this phase to all compilation units + */ + public void apply() { + super.apply(); + int count = 0; + for (int i = 0; i < global.units.length; i++) { + if (global.units[i].body != null) count++; + } + Unit[] units = new Unit[count]; + for (int i = 0, j = 0; i < global.units.length; i++) { + if (global.units[i].body != null) units[j++] = global.units[i]; + } + global.units = units; + } + + /** apply this phase to the given compilation unit + */ + public void apply(Unit unit) { + global.start(); + unit.body = new Parser(unit).parse(); + global.stop("parsed " + unit.source); + } +} diff --git a/sources/scalac/ast/parser/Scanner.java b/sources/scalac/ast/parser/Scanner.java new file mode 100644 index 0000000000..0e4af3a09d --- /dev/null +++ b/sources/scalac/ast/parser/Scanner.java @@ -0,0 +1,793 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.ast.parser; + +import scalac.*; +import scalac.util.Name; +import scalac.util.Position; + +/** A scanner for the programming language Scala. + * + * @author Matthias Zenger, Martin Odersky + * @version 1.0 + */ +public class Scanner extends TokenData { + + /** layout & character constants + */ + public int tabinc = 8; + public final static byte LF = 0xA; + protected final static byte FF = 0xC; + protected final static byte CR = 0xD; + protected final static byte SU = Sourcefile.SU; + + /** the names of all tokens + */ + public Name[] tokenName = new Name[128]; + public int numToken = 0; + + /** keyword array; maps from name indices to tokens + */ + protected byte[] key; + protected int maxKey = 0; + + /** we need one token lookahead + */ + protected TokenData next = new TokenData(); + protected TokenData prev = new TokenData(); + + /** the first character position after the previous token + */ + public int lastpos = 0; + + /** the last error position + */ + public int errpos = -1; + + /** the input buffer: + */ + protected byte[] buf; + protected int bp; + + /** the current character + */ + protected byte ch; + + /** the line and column position of the current character + */ + public int cline; + public int ccol; + + /** the current sourcefile + */ + public Sourcefile currentSource; + + /** a buffer for character and string literals + */ + protected byte[] lit = new byte[64]; + protected int litlen; + + /** the compilation unit + */ + public Unit unit; + + + /** Construct a scanner from a file input stream. + */ + public Scanner(Unit unit) { + this.unit = unit; + buf = (currentSource = unit.source).getBuffer(); + cline = 1; + bp = -1; + ccol = 0; + nextch(); + token = EMPTY; + init(); + nextToken(); + } + + private void nextch() { + ch = buf[++bp]; ccol++; + } + + /** read next token and return last position + */ + public int skipToken() { + int p = pos; + nextToken(); + return p; + } + + public void nextToken() { + if (token == RBRACE) { + int prevpos = pos; + fetchToken(); + switch (token) { + case ELSE: case EXTENDS: case WITH: + case YIELD: case DO: + case COMMA: case SEMI: case DOT: + case COLON: case EQUALS: case ARROW: + case LARROW: case SUBTYPE: case AT: + case HASH: case AS: case IS: + case RPAREN: case RBRACKET: case RBRACE: + break; + default: + if (token == EOF || + ((pos >>> Position.LINESHIFT) > + (prevpos >>> Position.LINESHIFT))) { + next.copyFrom(this); + this.token = SEMI; + this.pos = prevpos; + } + } + } else { + if (next.token == EMPTY) { + fetchToken(); + } else { + copyFrom(next); + next.token = EMPTY; + } + if (token == CASE) { + prev.copyFrom(this); + fetchToken(); + if (token == CLASS) { + token = CASECLASS; + } else { + next.copyFrom(this); + this.copyFrom(prev); + } + } else if (token == SEMI) { + prev.copyFrom(this); + fetchToken(); + if (token != ELSE) { + next.copyFrom(this); + this.copyFrom(prev); + } + } + } + //System.out.println("<" + token2string(token) + ">");//DEBUG + } + + /** read next token + */ + public void fetchToken() { + if (token == EOF) return; + lastpos = Position.encode(cline, ccol, currentSource.id); + int index = bp; + while(true) { + switch (ch) { + case ' ': + nextch(); + break; + case '\t': + ccol = ((ccol - 1) / tabinc * tabinc) + tabinc; + nextch(); + break; + case CR: + cline++; + ccol = 0; + nextch(); + if (ch == LF) { + ccol = 0; + nextch(); + } + break; + case LF: + case FF: + cline++; + ccol = 0; + nextch(); + break; + default: + pos = Position.encode(cline, ccol, currentSource.id); + index = bp; + switch (ch) { + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'L': case 'M': case 'N': case 'O': + case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': + case 'Z': case '$': + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': + case 'z': + nextch(); + getIdentRest(index); + return; + case '~': case '!': case '@': case '#': case '%': + case '^': case '*': case '+': case '-': case '<': + case '>': case '?': case ':': + case '=': case '&': case '|': + nextch(); + getOperatorRest(index); + return; + case '/': + nextch(); + if (!skipComment()) { + getOperatorRest(index); + return; + } + break; + case '_': + nextch(); + getIdentOrOperatorRest(index); + return; + case '0': + nextch(); + if (ch == 'x' || ch == 'X') { + nextch(); + getNumber(index + 2, 16); + } else + getNumber(index, 8); + return; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + getNumber(index, 10); + return; + case '\"': + nextch(); + litlen = 0; + while (ch != '\"' && ch != CR && ch != LF && ch != SU) + getlitch(); + if (ch == '\"') { + token = STRINGLIT; + name = Name.fromSource(lit, 0, litlen); + nextch(); + } + else + syntaxError("unclosed character literal"); + return; + case '\'': + nextch(); + litlen = 0; + getlitch(); + if (ch == '\'') { + nextch(); + token = CHARLIT; + byte[] ascii = new byte[litlen * 2]; + int alen = SourceRepresentation.source2ascii(lit, 0, litlen, ascii); + if (alen > 0) + intVal = SourceRepresentation.ascii2string(ascii, 0, alen).charAt(0); + else + intVal = 0; + } else + syntaxError("unclosed character literal"); + return; + case '.': + nextch(); + if (('0' <= ch) && (ch <= '9')) getFraction(index); + else token = DOT; + return; + case ';': + nextch(); token = SEMI; + return; + case ',': + nextch(); token = COMMA; + return; + case '(': + nextch(); token = LPAREN; + return; + case '{': + nextch(); token = LBRACE; + return; + case ')': + nextch(); token = RPAREN; + return; + case '}': + nextch(); token = RBRACE; + return; + case '[': + nextch(); token = LBRACKET; + return; + case ']': + nextch(); token = RBRACKET; + return; + case SU: + token = EOF; + currentSource.lines = cline; + return; + default: + nextch(); + syntaxError("illegal character"); + return; + } + } + } + } + + private boolean skipComment() { + if (ch == '/') { + do { + nextch(); + } while ((ch != CR) && (ch != LF) && (ch != SU)); + return true; + } else if (ch == '*') { + int openComments = 1; + while (openComments > 0) { + do { + do { + if (ch == CR) { + cline++; + ccol = 0; + nextch(); + if (ch == LF) { + ccol = 0; + nextch(); + } + } else if (ch == LF) { + cline++; + ccol = 0; + nextch(); + } + else if (ch == '\t') { + ccol = ((ccol - 1) / tabinc * tabinc) + tabinc; + nextch(); + } else if (ch == '/') { + nextch(); + if (ch == '*') { + nextch(); + openComments++; + } + } else { + nextch(); + } + } while ((ch != '*') && (ch != SU)); + while (ch == '*') { + nextch(); + } + } while (ch != '/' && ch != SU); + if (ch == '/') { + nextch(); + openComments--; + } else { + syntaxError("unclosed comment"); + return true; + } + } + return true; + } else { + return false; + } + } + + private void getIdentRest(int index) { + while (true) { + switch (ch) { + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'L': case 'M': case 'N': case 'O': + case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': + case 'Z': case '$': + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': + case 'z': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + nextch(); + break; + case '_': + nextch(); + getIdentOrOperatorRest(index); + return; + default: + treatIdent(index, bp); + return; + } + } + } + + private void getOperatorRest(int index) { + while (true) { + switch (ch) { + case '~': case '!': case '@': case '#': case '%': + case '^': case '*': case '+': case '-': case '<': + case '>': case '?': case ':': + case '=': case '&': case '|': + nextch(); + break; + case '/': + int lastbp = bp; + nextch(); + if (skipComment()) { + treatIdent(index, lastbp); + return; + } else { + break; + } + case '_': + nextch(); + getIdentOrOperatorRest(index); + return; + default: + treatIdent(index, bp); + return; + } + } + } + + private void getIdentOrOperatorRest(int index) { + switch (ch) { + case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': + case 'K': case 'L': case 'M': case 'N': case 'O': + case 'P': case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': case 'Y': + case 'Z': case '$': + case 'a': case 'b': case 'c': case 'd': case 'e': + case 'f': case 'g': case 'h': case 'i': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': case 'y': + case 'z': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + getIdentRest(index); + return; + case '~': case '!': case '@': case '#': case '%': + case '^': case '*': case '+': case '-': case '<': + case '>': case '?': case ':': + case '=': case '&': case '|': + case '/': + getOperatorRest(index); + return; + case '_': + nextch(); + getIdentOrOperatorRest(index); + return; + default: + treatIdent(index, bp); + return; + } + } + + void treatIdent(int start, int end) { + name = Name.fromAscii(buf, start, end - start); + if (name.index <= maxKey) + token = key[name.index]; + else + token = IDENTIFIER; + } + + /** generate an error at the given position + */ + void syntaxError(int pos, String msg) { + unit.error(pos, msg); + token = ERROR; + errpos = pos; + } + + /** generate an error at the current token position + */ + void syntaxError(String msg) { + syntaxError(pos, msg); + } + + /** append characteter to "lit" buffer + */ + protected void putch(byte c) { + if (litlen == lit.length) { + byte[] newlit = new byte[lit.length * 2]; + System.arraycopy(lit, 0, newlit, 0, lit.length); + lit = newlit; + } + lit[litlen++] = c; + } + + /** return true iff next 6 characters are a valid unicode sequence: + */ + protected boolean isUnicode() { + return + (bp + 6) < buf.length && + (buf[bp] == '\\') && + (buf[bp+1] == 'u') && + (SourceRepresentation.digit2int(buf[bp+2], 16) >= 0) && + (SourceRepresentation.digit2int(buf[bp+3], 16) >= 0) && + (SourceRepresentation.digit2int(buf[bp+4], 16) >= 0) && + (SourceRepresentation.digit2int(buf[bp+5], 16) >= 0); + } + + /** read next character in character or string literal: + */ + protected void getlitch() { + if (ch == '\\') { + if (isUnicode()) { + putch(ch); nextch(); + putch(ch); nextch(); + putch(ch); nextch(); + putch(ch); nextch(); + putch(ch); nextch(); + putch(ch); nextch(); + } else { + nextch(); + if ('0' <= ch && ch <= '7') { + byte leadch = ch; + int oct = SourceRepresentation.digit2int(ch, 8); + nextch(); + if ('0' <= ch && ch <= '7') { + oct = oct * 8 + SourceRepresentation.digit2int(ch, 8); + nextch(); + if (leadch <= '3' && '0' <= ch && ch <= '7') { + oct = oct * 8 + SourceRepresentation.digit2int(ch, 8); + nextch(); + } + } + putch((byte)oct); + } else if (ch != SU) { + switch (ch) { + case 'b': case 't': case 'n': + case 'f': case 'r': case '\"': + case '\'': case '\\': + putch((byte)'\\'); + putch(ch); + break; + default: + syntaxError(Position.encode(cline, ccol, currentSource.id) - 1, "invalid escape character"); + putch(ch); + } + nextch(); + } + } + } else if (ch != SU) { + putch(ch); + nextch(); + } + } + + /** read fractional part of floating point number; + * Then floatVal := buf[index..], converted to a floating point number. + */ + protected void getFraction(int index) { + while (SourceRepresentation.digit2int(ch, 10) >= 0) { + nextch(); + } + token = DOUBLELIT; + if ((ch == 'e') || (ch == 'E')) { + nextch(); + if ((ch == '+') || (ch == '-')) { + byte sign = ch; + nextch(); + if (('0' > ch) || (ch > '9')) { + ch = sign; + bp--; + ccol--; + } + } + while (SourceRepresentation.digit2int(ch, 10) >= 0) { + nextch(); + } + } + double limit = Double.MAX_VALUE; + if ((ch == 'd') || (ch == 'D')) { + nextch(); + } else if ((ch == 'f') || (ch == 'F')) { + token = FLOATLIT; + limit = Float.MAX_VALUE; + nextch(); + } + try { + floatVal = Double.valueOf(new String(buf, index, bp - index)).doubleValue(); + if (floatVal > limit) + syntaxError("floating point number too large"); + } catch (NumberFormatException e) { + syntaxError("malformed floating point number"); + } + } + + /** intVal := buf[index..index+len-1], converted to an integer number. + * base = the base of the number; one of 8, 10, 16. + * max = the maximal number before an overflow. + */ + protected void makeInt (int index, int len, int base, long max) { + intVal = 0; + int divider = (base == 10 ? 1 : 2); + for (int i = 0; i < len; i++) { + int d = SourceRepresentation.digit2int(buf[index + i], base); + if (d < 0) { + syntaxError("malformed integer number"); + return; + } + if (intVal < 0 || + max / (base / divider) < intVal || + max - (d / divider) < (intVal * (base / divider) - 0)) { + syntaxError("integer number too large"); + return; + } + intVal = intVal * base + d; + } + } + + /** read a number, + * and convert buf[index..], setting either intVal or floatVal. + * base = the base of the number; one of 8, 10, 16. + */ + protected void getNumber(int index, int base) { + while (SourceRepresentation.digit2int(ch, base == 8 ? 10 : base) >= 0) { + nextch(); + } + if (base <= 10 && ch == '.') { + nextch(); + if ((ch >= '0') && (ch <= '9')) + getFraction(index); + else { + ch = buf[--bp]; ccol--; + makeInt(index, bp - index, base, Integer.MAX_VALUE); + intVal = (int)intVal; + token = INTLIT; + } + } else if (base <= 10 && + (ch == 'e' || ch == 'E' || + ch == 'f' || ch == 'F' || + ch == 'd' || ch == 'D')) + getFraction(index); + else { + if (ch == 'l' || ch == 'L') { + makeInt(index, bp - index, base, Long.MAX_VALUE); + nextch(); + token = LONGLIT; + } else { + makeInt(index, bp - index, base, Integer.MAX_VALUE); + intVal = (int)intVal; + token = INTLIT; + } + } + } + + public int name2token(Name name) { + if (name.index <= maxKey) + return key[name.index]; + else + return IDENTIFIER; + } + + public String token2string(int token) { + switch (token) { + case IDENTIFIER: + return "identifier"; + case CHARLIT: + return "character literal"; + case INTLIT: + return "integer literal"; + case LONGLIT: + return "long literal"; + case FLOATLIT: + return "float literal"; + case DOUBLELIT: + return "double literal"; + case STRINGLIT: + return "string literal"; + case LPAREN: + return "'('"; + case RPAREN: + return "')'"; + case LBRACE: + return "'{'"; + case RBRACE: + return "'}'"; + case LBRACKET: + return "'['"; + case RBRACKET: + return "']'"; + case EOF: + return "eof"; + case ERROR: + return "something"; + case SEMI: + return "';'"; + case COMMA: + return "','"; + default: + try { + return "'" + tokenName[token].toString() + "'"; + } catch (ArrayIndexOutOfBoundsException e) { + return "'<" + token + ">'"; + } + } + } + + public String toString() { + switch (token) { + case IDENTIFIER: + return "id(" + name + ")"; + case CHARLIT: + return "char(" + intVal + ")"; + case INTLIT: + return "int(" + intVal + ")"; + case LONGLIT: + return "long(" + intVal + ")"; + case FLOATLIT: + return "float(" + floatVal + ")"; + case DOUBLELIT: + return "double(" + floatVal + ")"; + case STRINGLIT: + return "string(" + name + ")"; + case SEMI: + return ";"; + case COMMA: + return ","; + default: + return token2string(token); + } + } + + protected void enterKeyword(String s, int tokenId) { + while (tokenId > tokenName.length) { + Name[] newTokName = new Name[tokenName.length * 2]; + System.arraycopy(tokenName, 0, newTokName, 0, newTokName.length); + tokenName = newTokName; + } + Name n = Name.fromString(s); + tokenName[tokenId] = n; + if (n.index > maxKey) + maxKey = n.index; + if (tokenId >= numToken) + numToken = tokenId + 1; + } + + protected void init() { + initKeywords(); + key = new byte[maxKey+1]; + for (int i = 0; i <= maxKey; i++) + key[i] = IDENTIFIER; + for (byte j = 0; j < numToken; j++) + if (tokenName[j] != null) + key[tokenName[j].index] = j; + } + + protected void initKeywords() { + enterKeyword("if", IF); + enterKeyword("for", FOR); + enterKeyword("else", ELSE); + enterKeyword("this", THIS); + enterKeyword("null", NULL); + enterKeyword("new", NEW); + enterKeyword("with", WITH); + enterKeyword("super", SUPER); + enterKeyword("case", CASE); + enterKeyword("val", VAL); + enterKeyword("abstract", ABSTRACT); + enterKeyword("final", FINAL); + enterKeyword("private", PRIVATE); + enterKeyword("protected", PROTECTED); + enterKeyword("qualified", QUALIFIED); + enterKeyword("override", OVERRIDE); + enterKeyword("var", VAR); + enterKeyword("def", DEF); + enterKeyword("type", TYPE); + enterKeyword("extends", EXTENDS); + enterKeyword("let", LET); + enterKeyword("module", MODULE); + enterKeyword("class",CLASS); + enterKeyword("constr",CONSTR); + enterKeyword("import", IMPORT); + enterKeyword("package", PACKAGE); + enterKeyword(".", DOT); + enterKeyword("_", USCORE); + enterKeyword(":", COLON); + enterKeyword("=", EQUALS); + enterKeyword("=>", ARROW); + enterKeyword("<-", LARROW); + enterKeyword("<:", SUBTYPE); + enterKeyword("yield", YIELD); + enterKeyword("do", DO); + enterKeyword("@", AT); + enterKeyword("#", HASH); + enterKeyword("trait", TRAIT); + enterKeyword("as", AS); + enterKeyword("is", IS); + } +} + + diff --git a/sources/scalac/ast/parser/SourceRepresentation.java b/sources/scalac/ast/parser/SourceRepresentation.java new file mode 100644 index 0000000000..2a20e13fab --- /dev/null +++ b/sources/scalac/ast/parser/SourceRepresentation.java @@ -0,0 +1,205 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id +\* */ + +package scalac.ast.parser; + +public final class SourceRepresentation { + + public static int digit2int(byte ch, int base) { + if ('0' <= ch && ch <= '9' && ch < '0' + base) + return ch - '0'; + else if ('A' <= ch && ch < 'A' + base - 10) + return ch - 'A' + 10; + else if ('a' <= ch && ch < 'a' + base - 10) + return ch - 'a' + 10; + else + return -1; + } + + public static byte int2digit(int x) { + if (x <= 9) + return (byte)(x + '0'); + else + return (byte)(x - 10 + 'A'); + } + +/* the next 4 functions convert between three fundamental name + * representations: + * - string each character 16 bit, + * - source characters outside 0..127 are represented by + * unicode escapes, \ u X X X X + * - ascii characters outside 0..127 are represented by two or three + * byte sequences with high bit set (as in class file format). + */ + +/** convert source bytes in source[offset..offset+len-1] to ascii. + */ + public static int source2ascii(byte source[], int offset, int len, byte ascii[]) { + int j = 0; + int i = 0; + while (i < len) { + if (source[offset + i] == '\\' && i + 1 < len) { + i++; + switch (source[offset + i]) { + case 'n': + ascii[j++] = (byte)'\n'; i++; continue; + case 't': + ascii[j++] = (byte)'\t'; i++; continue; + case 'b': + ascii[j++] = (byte)'\b'; i++; continue; + case 'r': + ascii[j++] = (byte)'\r'; i++; continue; + case 'f': + ascii[j++] = (byte)'\f'; i++; continue; + case 'u': + if (i + 4 < len) { + int code = 0; + int k = 1; + int d = 0; + while (k <= 4 && d >= 0) { + d = digit2int(source[offset + i + k], 16); + code = code * 16 + d; + k++; + } + if (d >= 0) { + if (code <= 0x7F) + ascii[j++] = (byte)code; + else + if (code <= 0x3FF) { + ascii[j++] = (byte)(0xC0 | (code >> 6)); + ascii[j++] = (byte)(0x80 | (code & 0x3F)); + } else { + ascii[j++] = (byte)(0xE0 | (code >> 12)); + ascii[j++] = (byte)(0x80 | + ((code >> 6) & 0x3F)); + ascii[j++] = (byte)(0x80 | (code & 0x3F)); + } + i = i + 5; + continue; + } + } + } + } + byte b = source[offset + i++]; + if (b >= 0) + ascii[j++] = b; + else { + ascii[j++] = (byte)(0xC0 | ((b >> 6) & 0x3)); + ascii[j++] = (byte)(0x80 | (b & 0x3F)); + } + } + return j; + } + +/** convert ascii bytes in ascii[offset..offset+len-1] to a string + */ + public static String ascii2string(byte ascii[], int offset, int len) { + char cs[] = new char[len]; + int i = offset; + int j = 0; + len += offset; + while (i < len) { + int b = ascii[i++] & 0xFF; + if (b >= 0xE0) { + b = ((b & 0x0F) << 12) | (ascii[i++] & 0x3F) << 6; + b = b | (ascii[i++] & 0x3F); + } + else + if (b >= 0xC0) + b = ((b & 0x1F) << 6) | (ascii[i++] & 0x3F); + cs[j++] = (char)b; + } + return new String(cs, 0, j); + } + +/** convert string to array of source bytes + */ + public static byte[] string2source(String s) { + byte[] source = new byte[s.length() * 6]; + int j = 0; + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + switch (ch) { + case '\n': + source[j++] = (byte)'\\'; + source[j++] = (byte)'n'; + break; + case '\t': + source[j++] = (byte)'\\'; + source[j++] = (byte)'t'; + break; + case '\b': + source[j++] = (byte)'\\'; + source[j++] = (byte)'b'; + break; + case '\r': + source[j++] = (byte)'\\'; + source[j++] = (byte)'r'; + break; + case '\f': + source[j++] = (byte)'\\'; + source[j++] = (byte)'f'; + break; + case '\"': + source[j++] = (byte)'\\'; + source[j++] = (byte)'\"'; + break; + case '\'': + source[j++] = (byte)'\\'; + source[j++] = (byte)'\''; + break; + case '\\': + source[j++] = (byte)'\\'; + source[j++] = (byte)'\\'; + break; + default: + if ((' ' <= ch) && (ch <= 127)) + source[j++] = (byte)ch; + else { + source[j++] = (byte)'\\'; + source[j++] = (byte)'u'; + source[j++] = int2digit((ch >> 12) & 0xF); + source[j++] = int2digit((ch >> 8) & 0xF); + source[j++] = int2digit((ch >> 4) & 0xF); + source[j++] = int2digit(ch & 0xF); + } + } + } + byte[] res = new byte[j]; + System.arraycopy(source, 0, res, 0, j); + return res; + } + +/** convert string to array of ascii bytes + */ + public static byte[] string2ascii(String s) { + byte[] source = string2source(s); + byte[] ascii = new byte[source.length * 2]; + int alen = source2ascii(source, 0, source.length, ascii); + byte[] res = new byte[alen]; + System.arraycopy(ascii, 0, res, 0, alen); + return res; + } + +/** escape all characters outside 32..127 in string s + */ + public static String escape(String s) { + try { + return new String(string2source(s), "8859_1"); + } catch (java.io.UnsupportedEncodingException e) { + throw new InternalError(e.getMessage()); + } + } + +/** escape character c, if outside 32..127. + */ + public static String escape(char c) { + char[] s = {c}; + return escape(new String(s)); + } +} diff --git a/sources/scalac/ast/parser/Sourcefile.java b/sources/scalac/ast/parser/Sourcefile.java new file mode 100644 index 0000000000..91277f9ac8 --- /dev/null +++ b/sources/scalac/ast/parser/Sourcefile.java @@ -0,0 +1,304 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.ast.parser; + +import java.io.*; +import java.util.Hashtable; +import scalac.util.AbstractFile; +import scalac.util.Name; +import scalac.util.Position; + + +/** This class represents a single scala source file. It provides + * functionality to read the file and to output error messages on + * a given line and column. Error messages are logged to + * decouple the time where an error message is issued from the + * time where the error messages are displayed. + * + * @author Matthias Zenger + * @version 1.0 + */ +public class Sourcefile { + + /** the id management + */ + public static int numIds = 1; + public static String[] files = new String[]{"console", null, null, null}; + public static Sourcefile[] sources = new Sourcefile[]{null, null, null, null}; + public int id; + + /** the filename + */ + public final boolean console; + protected String filename; + public String shortname; + public String pathname; + + /** the encoding of the file + */ + protected String encoding; + + /** a log of all errors generated so far; used to avoid printing an + * error message more than once + */ + protected Hashtable recorded = new Hashtable(); + + /** the buffer containing the file that is currently translated + */ + protected byte[] buf = null; + + /** the last error position + */ + protected int lastLine = 0; + protected int lastPos = 0; + protected int lineEnd = 0; + protected int newPos = 0; + + /** constants used for source parsing + */ + final static byte LF = 0xA; + final static byte FF = 0xC; + final static byte CR = 0xD; + final static byte SU = 0x1A; + + /** set col to NO_COLUMN, if the printLine method should not mark + * the column + */ + final static int NO_COLUMN = -1; + + /** number of lines and bytes (not used internally) + */ + public int lines; // set externally + public int bytes; + + /** prompt after error? + */ + public boolean prompt; + + /** constructors + */ + public Sourcefile(String filename, boolean console) throws IOException, FileNotFoundException { + this.console = console; + if (filename == null) { + this.filename = "(sourcefile not available)"; + this.shortname = "?"; + this.pathname = "?"; + buf = new byte[]{SU}; + } else { + File f = new File(filename); + this.filename = filename; + this.shortname = f.getName(); + this.pathname = f.getAbsoluteFile().getParentFile(). + getCanonicalPath(); + fillBuffer(new FileInputStream(f)); + } + if (numIds == files.length) { + String[] newfiles = new String[numIds * 2]; + System.arraycopy(files, 0, newfiles, 0, numIds); + files = newfiles; + Sourcefile[] newsources = new Sourcefile[numIds * 2]; + System.arraycopy(sources, 0, newsources, 0, numIds); + sources = newsources; + } + sources[numIds] = this; + files[id = numIds++] = shortname; + } + + public Sourcefile(AbstractFile abs) throws IOException, FileNotFoundException { + this.console = false; + if (filename == null) { + this.filename = "(sourcefile not available)"; + this.shortname = "?"; + this.pathname = "?"; + buf = new byte[]{SU}; + } else { + this.filename = abs.getPath(); + this.shortname = abs.getName(); + this.pathname = abs.getPath(); + fillBuffer(abs.getInputStream()); + } + if (numIds == files.length) { + String[] newfiles = new String[numIds * 2]; + System.arraycopy(files, 0, newfiles, 0, numIds); + files = newfiles; + Sourcefile[] newsources = new Sourcefile[numIds * 2]; + System.arraycopy(sources, 0, newsources, 0, numIds); + sources = newsources; + } + sources[numIds] = this; + files[id = numIds++] = shortname; + } + + public Sourcefile(byte[] input, boolean console) { + this.console = console; + if (input == null) { + this.filename = "(sourcefile not available)"; + this.shortname = "?"; + this.pathname = "?"; + buf = new byte[]{SU}; + } else { + this.filename = "console"; + this.shortname = "console"; + this.pathname = "console"; + buf = new byte[input.length + 2]; + System.arraycopy(input, 0, buf, 0, input.length); + buf[input.length] = Scanner.LF; + buf[input.length + 1] = SU; + } + sources[0] = this; + id = 0; + } + + /** fill the buffer using the InputStream + */ + private void fillBuffer(InputStream in) throws IOException { + try { + buf = new byte[(bytes = in.available()) + 1]; + if (in.read(buf) != (buf.length - 1)) + throw new IOException(); + in.close(); + buf[buf.length - 1] = SU; + } catch (IOException e) { + throw new IOException("cannot read '" + filename + "'"); + } + } + + /** return filename as a string + */ + public String toString() { + return filename; + } + + /** return filename as a name + */ + public Name getName() { + return Name.fromString(filename); + } + + /** return the shortname without the suffix + */ + public String getShortnameWithoutSuffix() { + int idx = shortname.lastIndexOf('.'); + if (idx < 0) + return shortname; + else + return shortname.substring(0, idx); + } + + /** return the source buffer of this file + */ + public byte[] getBuffer() { + return buf; + } + + /** number of logged entries + */ + public int logged() { + return recorded.size(); + } + + /** is there already an entry at position 'pos' + */ + public boolean isLogged(int pos) { + return (recorded.get(new Integer(pos)) != null); + } + + /** enter entry into log table + */ + public void log(int pos, String message) { + recorded.put(new Integer(pos), message); + } + + /** set encoding of the file + */ + public void setEncoding(String encoding) { + this.encoding = encoding; + } + + /** return true if there is an entry for this position, + * otherwise return false and enter message into log + */ + public boolean testAndSetLog(int pos, String message) { + if (!isLogged(pos)) { + log(pos, message); + return false; + } + return true; + } + + /** get error message with line from sourcefile + */ + public String getMessage(int pos, String message) { + if (pos == Position.NOPOS) + return filename + ": " + message; + else { + int fileId = Position.file(pos); + String filename = files[fileId]; + int line = Position.line(pos); + int col = Position.column(pos); + String main = filename + ":" + line + ": " + message; + if ((fileId > 0) && + (fileId < numIds) && + (sources[fileId] != null)) + return main + '\n' + sources[fileId].getLine(line, col); + else + return main; + //else + // System.out.println("(source file not available anymore)"); + } + } + + /** get source line + */ + public String getLine(int line, int col) { + int pos = 0; + if (lastLine > line) + lastLine = 0; + else + pos = newPos; + while ((pos < buf.length) && (lastLine < line)) + { + lastPos = pos; + while ((pos < buf.length) && (buf[pos] != CR) && + (buf[pos] != LF) && (buf[pos] != FF)) + pos++; + lineEnd = pos; + if (pos < buf.length) + pos++; + if ((pos < buf.length) && (buf[pos-1] == CR) && (buf[pos] == LF)) + pos++; + lastLine++; + } + newPos = pos; + try + { + String errline = (encoding != null) ? + new String(buf, lastPos, lineEnd - lastPos, encoding) : + new String(buf, lastPos, lineEnd - lastPos); + if (col != NO_COLUMN) + { + byte[] ptr = new byte[col]; + for (int i = col - 2; i >= 0; i--) + ptr[i] = (byte)' '; + ptr[col - 1] = (byte)'^'; + return errline + '\n' + new String(ptr); + } else + return errline; + } catch (UnsupportedEncodingException e) { + throw new InternalError(e.getMessage()); + } + } + + /** release all sourcefile objects + */ + public static void flushSources() { + for (int i = 0; i < sources.length; i++) + sources[i] = null; + } +} diff --git a/sources/scalac/ast/parser/TokenData.java b/sources/scalac/ast/parser/TokenData.java new file mode 100644 index 0000000000..d6bcac6c1c --- /dev/null +++ b/sources/scalac/ast/parser/TokenData.java @@ -0,0 +1,44 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.ast.parser; + +import scalac.util.Name; + +/** A class for representing a token's data. + * + * @author Matthias Zenger + * @version 1.0 + */ +public class TokenData implements Tokens { + + /** the next token + */ + public int token = EMPTY; + + /** the token's position. pos = line << Position.LINESHIFT + col + */ + public int pos = 0; + + /** the name of an identifier or token + */ + public Name name; + + /** the value of a number + */ + public long intVal; + public double floatVal; + + public void copyFrom(TokenData td) { + this.token = td.token; + this.pos = td.pos; + this.name = td.name; + this.intVal = td.intVal; + this.floatVal = td.floatVal; + } +} diff --git a/sources/scalac/ast/parser/Tokens.java b/sources/scalac/ast/parser/Tokens.java new file mode 100644 index 0000000000..6c3bef6a55 --- /dev/null +++ b/sources/scalac/ast/parser/Tokens.java @@ -0,0 +1,84 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.ast.parser; + +public interface Tokens { + byte EMPTY = -3, + UNDEF = -2, + ERROR = -1, + EOF = 0, + + /* literals */ + CHARLIT = 1, + INTLIT = 2, + LONGLIT = 3, + FLOATLIT = 4, + DOUBLELIT = 5, + STRINGLIT = 6, + + /* identifier */ + IDENTIFIER = 10, + + /* keywords */ + IF = 20, + FOR = 21, + ELSE = 22, + THIS = 23, + NULL = 24, + NEW = 25, + WITH = 26, + SUPER = 27, + CASE = 28, + CASECLASS = 29, + VAL = 30, + ABSTRACT = 31, + FINAL = 32, + PRIVATE = 33, + PROTECTED = 34, + QUALIFIED = 35, + OVERRIDE = 36, + VAR = 37, + DEF = 38, + TYPE = 39, + EXTENDS = 40, + LET = 41, + MODULE = 43, + CLASS = 44, + CONSTR = 45, + IMPORT = 46, + PACKAGE = 47, + AS = 48, + IS = 49, + YIELD = 50, + DO = 51, + TRAIT = 52, + + /* special symbols */ + COMMA = 61, + SEMI = 62, + DOT = 63, + USCORE = 64, + COLON = 65, + EQUALS = 66, + LARROW = 57, + ARROW = 68, + SUBTYPE = 69, + AT = 70, + HASH = 71, + + /* parenthesis */ + LPAREN = 90, + RPAREN = 91, + LBRACKET = 92, + RBRACKET = 93, + LBRACE = 94, + RBRACE = 95; +} + + diff --git a/sources/scalac/ast/printer/HTMLTreePrinter.java b/sources/scalac/ast/printer/HTMLTreePrinter.java new file mode 100644 index 0000000000..8dbd34f2a0 --- /dev/null +++ b/sources/scalac/ast/printer/HTMLTreePrinter.java @@ -0,0 +1,173 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +\* */ + +// $Id$ + +package scalac.ast.printer; + +import scalac.Unit; +import scalac.symtab.Symbol; + +import java.io.OutputStream; +import java.io.PrintWriter; +import java.lang.Math; +import java.util.HashMap; + +/** + * HTML pretty printer for Scala abstract syntax trees. + * + * @author Michel Schinz + * @version 1.0 + */ + +public class HTMLTreePrinter extends TextTreePrinter { + protected int outSectionLevel = 1; + protected boolean started = false; + + public HTMLTreePrinter(OutputStream stream) { + super(stream); + } + + public void begin() { + assert !started; + + super.begin(); + out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"); + out.println("<html>"); + out.println("<head>"); + out.println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\">"); + out.println("<link rel=\"stylesheet\" href=\"scala.css\" type=\"text/css\">"); + out.println("<title>Scala tree</title>"); + out.println("</head>"); + out.println("<body>"); + + started = true; + } + + public void end() { + assert started; + + out.println("</body>"); + out.println("</html>"); + super.end(); + + started = false; + } + + public void beginSection(int level, String title) { + outSectionLevel = Math.min(level, 4); + beginSection1(outSectionLevel, title); + } + + protected void beginSection1(int level, String title) { + if (level == 1) + out.println("<hr/>"); + String tag = "h" + level; + startTag(tag); + print(Text.Simple(title)); + endTag(tag); + } + + protected void startTag(String tag) { + out.print('<'); out.print(tag); out.print('>'); + } + + protected void startTag(String tag, String attr1, String val1) { + out.print('<'); + out.print(tag); + out.print(' '); + out.print(attr1); + out.print("=\""); + out.print(val1); + out.print("\">"); + } + + protected void endTag(String tag) { + out.print("</"); out.print(tag); out.print(">"); + } + + protected void startSpan(String cls) { + startTag("span", "class", cls); + } + + protected void endSpan() { + endTag("span"); + } + + protected void printString(String str) { + StringBuffer buf = null; + int strLen = str.length(); + + for (int i = 0; i < strLen; ++i) { + String entity; + char c = str.charAt(i); + switch (c) { + case '<': entity = "lt"; break; + case '>': entity = "gt"; break; + case '&': entity = "amp"; break; + default: entity = null; break; + } + if (entity != null) { + out.print('&'); + out.print(entity); + out.print(';'); + } else + out.print(c); + } + } + + protected static HashMap/*<Symbol,Integer>*/ symAnchors = new HashMap(); + protected String symbolAnchor(Symbol sym, SymbolUsage usage) { + Integer anchorId = (Integer)symAnchors.get(sym); + if (anchorId == null) { + anchorId = new Integer(symAnchors.size()); + symAnchors.put(sym, anchorId); + } + if (usage == SymbolUsage.Definition) + return anchorId.toString(); + else + return "#" + anchorId.toString(); + } + + protected void print(Text text) { + switch (text) { + case Keyword(String name): + startSpan("kw"); + printString(name); + endSpan(); + break; + case Literal(String str): + startSpan("lit"); + printString(str); + endSpan(); + break; + case Identifier(Symbol symbol, String name, SymbolUsage usage): + boolean defined = (usage == SymbolUsage.Definition); + if (defined) startSpan("idDef"); + if (symbol != null) { + String attr = (defined ? "name" : "href"); + startTag("a", attr, symbolAnchor(symbol, usage)); + } + printString(name); + if (symbol != null) + endTag("a"); + if (defined) endSpan(); + break; + default: + super.print(text); + } + } + + protected void printUnitHeader(Unit unit) { + beginSection1(outSectionLevel + 1, unit.source.toString()); + startTag("pre"); + } + + protected void printUnitFooter(Unit unit) { + endTag("pre"); + } +} diff --git a/sources/scalac/ast/printer/TextTreePrinter.java b/sources/scalac/ast/printer/TextTreePrinter.java new file mode 100644 index 0000000000..c5138d025f --- /dev/null +++ b/sources/scalac/ast/printer/TextTreePrinter.java @@ -0,0 +1,683 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +\* */ + +// $Id$ + +package scalac.ast.printer; + +import scalac.ast.*; +import scalac.symtab.*; +import scalac.util.Debug; +import scalac.Global; +import scalac.Unit; +import scalac.util.Name; + +import java.io.*; +import java.util.*; + +/** + * Text pretty printer for Scala abstract syntax trees. + * + * @author Michel Schinz, Matthias Zenger + * @version 1.0 + */ +public class TextTreePrinter implements TreePrinter { + protected PrintWriter out; + + protected int indent = 0; + protected final int INDENT_STEP = 2; + protected final String INDENT_STRING = + " "; + protected final int MAX_INDENT = INDENT_STRING.length(); + + public TextTreePrinter(OutputStream stream) { + out = new PrintWriter(stream); + } + + public TextTreePrinter() { + this(System.out); + } + + public void begin() { } + + public void end() { + out.flush(); + } + + public void flush() { + out.flush(); + } + + public TreePrinter print(String str) { + out.print(str); + return this; + } + + public TreePrinter println() { + out.println(); + return this; + } + + public void beginSection(int level, String title) { + out.println("[[" + title + "]]"); + out.flush(); + } + + protected void indent() { + indent += Math.min(MAX_INDENT, INDENT_STEP); + } + + protected void undent() { + indent -= Math.max(0, INDENT_STEP); + } + + protected void printString(String str) { + out.print(str); + } + + protected void printNewLine() { + out.println(); + if (indent > 0) + out.write(INDENT_STRING, 0, indent); + } + + public static class SymbolUsage { + public case Definition; + public case Use; + } + + public static class Text { + public case None; + public case Space; + public case Newline; + public case Simple(String str); + public case Literal(String str); + public case Keyword(String name); + public case Identifier(Symbol symbol, String name, SymbolUsage usage); + public case Sequence(Text[] elements); + } + + protected void print(Text text) { + switch (text) { + case None : break; + case Space : printString(" "); break; + case Newline : printNewLine(); break; + case Simple(String str) : printString(str); break; + case Literal(String str) : printString(str); break; + case Keyword(String name) : printString(name); break; + case Identifier(Symbol sym, String name, _) : + printString(name); + if (sym != null && Global.instance.uniqid) + printString("#" + Global.instance.uniqueID.id(sym)); + break; + case Sequence(Text[] elements) : print(elements); break; + } + } + + protected void print(Text[] texts) { + for (int i = 0; i < texts.length; ++i) + print(texts[i]); + } + + protected static final Text KW_ABSTRACT = Text.Keyword("abstract"); + protected static final Text KW_CASE = Text.Keyword("case"); + protected static final Text KW_CLASS = Text.Keyword("class"); + protected static final Text KW_CONSTR = Text.Keyword("constr"); + protected static final Text KW_DEF = Text.Keyword("def"); + protected static final Text KW_DO = Text.Keyword("do"); + protected static final Text KW_ELSE = Text.Keyword("else"); + protected static final Text KW_EXTENDS = Text.Keyword("extends"); + protected static final Text KW_FINAL = Text.Keyword("final"); + protected static final Text KW_FOR = Text.Keyword("for"); + protected static final Text KW_IF = Text.Keyword("if"); + protected static final Text KW_IMPORT = Text.Keyword("import"); + protected static final Text KW_INTERFACE = Text.Keyword("interface"); + protected static final Text KW_LET = Text.Keyword("let"); + protected static final Text KW_MODULE = Text.Keyword("module"); + protected static final Text KW_NEW = Text.Keyword("new"); + protected static final Text KW_NULL = Text.Keyword("null"); + protected static final Text KW_OUTER = Text.Keyword("outer"); + protected static final Text KW_OVERRIDE = Text.Keyword("override"); + protected static final Text KW_PACKAGE = Text.Keyword("package"); + protected static final Text KW_PRIVATE = Text.Keyword("private"); + protected static final Text KW_PROTECTED = Text.Keyword("protected"); + protected static final Text KW_QUALIFIED = Text.Keyword("qualified"); + protected static final Text KW_STATIC = Text.Keyword("static"); + protected static final Text KW_SUPER = Text.Keyword("super"); + protected static final Text KW_THIS = Text.Keyword("this"); + protected static final Text KW_TYPE = Text.Keyword("type"); + protected static final Text KW_VAL = Text.Keyword("val"); + protected static final Text KW_VAR = Text.Keyword("var"); + protected static final Text KW_WITH = Text.Keyword("with"); + protected static final Text KW_YIELD = Text.Keyword("yield"); + + protected static final Text TXT_ERROR = Text.Simple("<error>"); + protected static final Text TXT_UNKNOWN = Text.Simple("<unknown>"); + protected static final Text TXT_NULL = Text.Simple("<null>"); + protected static final Text TXT_MODULE_COMMENT + = Text.Simple("/*module*/ "); + protected static final Text TXT_EMPTY = Text.Simple("<empty>"); + + protected static final Text TXT_QUOTE = Text.Simple("\""); + protected static final Text TXT_PLUS = Text.Simple("+"); + protected static final Text TXT_COLON = Text.Simple(":"); + protected static final Text TXT_SEMICOLON = Text.Simple(";"); + protected static final Text TXT_DOT = Text.Simple("."); + protected static final Text TXT_COMMA = Text.Simple(","); + protected static final Text TXT_EQUAL = Text.Simple("="); + protected static final Text TXT_SUBTYPE = Text.Simple("<:"); + protected static final Text TXT_AT = Text.Simple("@"); + protected static final Text TXT_HASH = Text.Simple("#"); + protected static final Text TXT_RIGHT_ARROW = Text.Simple("=>"); + protected static final Text TXT_LEFT_PAREN = Text.Simple("("); + protected static final Text TXT_RIGHT_PAREN = Text.Simple(")"); + protected static final Text TXT_LEFT_BRACE = Text.Simple("{"); + protected static final Text TXT_RIGHT_BRACE = Text.Simple("}"); + protected static final Text TXT_LEFT_BRACKET = Text.Simple("["); + protected static final Text TXT_RIGHT_BRACKET = Text.Simple("]"); + + protected static final Text TXT_WITH_BLOCK_BEGIN = + Text.Sequence(new Text[] { + Text.Space, KW_WITH, Text.Space, TXT_LEFT_BRACE, Text.Newline + }); + protected static final Text TXT_WITH_SP = + Text.Sequence(new Text[]{ Text.Space, KW_WITH, Text.Space }); + protected static final Text TXT_BLOCK_BEGIN = + Text.Sequence(new Text[]{ TXT_LEFT_BRACE, Text.Newline }); + protected static final Text TXT_BLOCK_END = + Text.Sequence(new Text[]{ Text.Newline, TXT_RIGHT_BRACE }); + protected static final Text TXT_BLOCK_SEP = + Text.Sequence(new Text[]{ TXT_SEMICOLON, Text.Newline }); + protected static final Text TXT_COMMA_SP = + Text.Sequence(new Text[]{ TXT_COMMA, Text.Space }); + protected static final Text TXT_ELSE_NL = + Text.Sequence(new Text[]{ KW_ELSE, Text.Newline }); + + public void print(Unit unit) { + printUnitHeader(unit); + if (unit.body != null) { + for (int i = 0; i < unit.body.length; ++i) { + print(unit.body[i]); + print(TXT_BLOCK_SEP); + } + } else + print(TXT_NULL); + printUnitFooter(unit); + + out.flush(); + } + + protected void printUnitHeader(Unit unit) { + print(Text.Simple("// Scala source: " + unit.source + "\n")); + } + + protected void printUnitFooter(Unit unit) { + print(Text.Newline); + } + + public TreePrinter print(Tree tree) { + switch (tree) { + case Bad(): + print(TXT_ERROR); + break; + + case Empty: + print(TXT_EMPTY); + break; + + case ClassDef(int mods, // : + Name name, + Tree.TypeDef[] tparams, + Tree.ValDef[][] vparams, + Tree tpe, + Tree.Template impl): + printModifiers(mods); + print((mods & Modifiers.INTERFACE) != 0 + ? KW_INTERFACE + : KW_CLASS); + print(Text.Space); + printSymbolDefinition(tree.symbol(), name); + printParams(tparams); + printParams(vparams); + printOpt(TXT_COLON, tpe, false); + printTemplate(KW_EXTENDS, impl, true); + break; + + case PackageDef(Tree packaged, Tree.Template impl): + print(KW_PACKAGE); + print(Text.Space); + print(packaged); + print(Text.Space); + printTemplate(KW_WITH, impl, true); + break; + + case ModuleDef(int mods, // : + Name name, + Tree tpe, + Tree.Template impl): + printModifiers(mods); + print(KW_MODULE); + print(Text.Space); + printSymbolDefinition(tree.symbol(), name); + printOpt(TXT_COLON, tpe, false); + printTemplate(KW_EXTENDS, impl, true); + break; + + case ValDef(int mods, Name name, Tree tpe, Tree rhs): + printModifiers(mods); + if ((mods & Modifiers.MUTABLE) != 0) print(KW_VAR); + else { + if ((mods & Modifiers.MODUL) != 0) print(TXT_MODULE_COMMENT); + print(KW_VAL); + } + print(Text.Space); + printSymbolDefinition(tree.symbol(), name); + printOpt(TXT_COLON, tpe, false); + printOpt(TXT_EQUAL, rhs, true); + break; + + case PatDef(int mods, Tree pat, Tree rhs): + printModifiers(mods); + print(KW_VAL); + print(Text.Space); + print(pat); + printOpt(TXT_EQUAL, rhs, true); + break; + + case DefDef(int mods, + Name name, + Tree.TypeDef[] tparams, + Tree.ValDef[][] vparams, + Tree tpe, + Tree rhs): + printModifiers(mods); + if (name.isConstrName()) print(KW_CONSTR); else print(KW_DEF); + print(Text.Space); + printSymbolDefinition(tree.symbol(), name); + printParams(tparams); + printParams(vparams); + printOpt(TXT_COLON, tpe, false); + printOpt(TXT_EQUAL, rhs, true); + break; + + case TypeDef(int mods, + Name name, + Tree.TypeDef[] tparams, + Tree rhs): + printModifiers(mods); + print(KW_TYPE); + print(Text.Space); + printSymbolDefinition(tree.symbol(), name); + printParams(tparams); + if ((mods & Modifiers.ABSTRACT) != 0) printOpt(TXT_SUBTYPE, rhs, true); + else printOpt(TXT_EQUAL, rhs, true); + break; + + case Import(Tree expr, Name[] selectors): + print(KW_IMPORT); + print(Text.Space); + print(expr); + print(TXT_LEFT_BRACE); + for (int i = 0; i < selectors.length; i = i + 2) { + if (i > 0) print(TXT_COMMA_SP); + print(selectors[i].toString()); + if (i + 1 < selectors.length && selectors[i] != selectors[i+1]) { + print(TXT_RIGHT_ARROW); + print(selectors[i+1].toString()); + } + } + print(TXT_RIGHT_BRACE); + break; + + case CaseDef(Tree pat, Tree guard, Tree body): + print(KW_CASE); + print(Text.Space); + print(pat); + printOpt(KW_IF, guard, true); + print(Text.Space); + print(TXT_RIGHT_ARROW); + print(Text.Space); + print(body); + break; + + case LabelDef(Tree[] params, Tree rhs): + assert tree.symbol() != null; + printSymbolDefinition(tree.symbol(), null); + printArray(params, TXT_LEFT_PAREN, TXT_RIGHT_PAREN, TXT_COMMA_SP); + print(rhs); + break; + + case Block(Tree[] stats): + printArray(stats, TXT_BLOCK_BEGIN, TXT_BLOCK_END, TXT_BLOCK_SEP); + break; + + case Tuple(Tree[] trees): + printArray(trees, TXT_LEFT_BRACKET, TXT_RIGHT_BRACKET, TXT_COMMA_SP); + break; + + case Visitor(Tree.CaseDef[] cases): + printArray(cases, TXT_BLOCK_BEGIN, TXT_BLOCK_END, Text.Newline); + break; + + case Function(Tree.ValDef[] vparams, Tree body): + print(TXT_LEFT_BRACE); + printParams(vparams); + print(Text.Space); + print(TXT_RIGHT_ARROW); + print(Text.Space); + print(body); + print(TXT_RIGHT_BRACE); + break; + + case Assign(Tree lhs, Tree rhs): + print(lhs); + print(Text.Space); + print(TXT_EQUAL); + print(Text.Space); + print(rhs); + break; + + case If(Tree cond, Tree thenp, Tree elsep): + print(KW_IF); + print(Text.Space); + print(TXT_LEFT_PAREN); + print(cond); + print(TXT_RIGHT_PAREN); + indent(); print(Text.Newline); + print(thenp); + undent(); print(Text.Newline); + indent(); printOpt(TXT_ELSE_NL, elsep, false); undent(); + printType(tree); + break; + + case New(Tree.Template templ): + printTemplate(KW_NEW, templ, false); + printType(tree); + break; + + case Typed(Tree expr, Tree tpe): + print(TXT_LEFT_PAREN); + print(expr); + print(TXT_RIGHT_PAREN); + print(Text.Space); + print(TXT_COLON); + print(Text.Space); + print(tpe); + printType(tree); + break; + + case TypeApply(Tree fun, Tree[] targs): + print(fun); + print(TXT_AT); + printArray(targs, TXT_LEFT_BRACKET, TXT_RIGHT_BRACKET, TXT_COMMA_SP); + printType(tree); + break; + + case Apply(Tree fun, Tree[] vargs): + print(fun); + printArray(vargs, TXT_LEFT_PAREN, TXT_RIGHT_PAREN, TXT_COMMA_SP); + printType(tree); + break; + + case Super(Tree tpe): + if (tpe != Tree.Empty) + print(TXT_LEFT_PAREN); + + print(KW_SUPER); + + if (tpe != Tree.Empty) { + print(Text.Space); + print(TXT_COLON); + print(Text.Space); + print(tpe); + print(TXT_RIGHT_PAREN); + } + printType(tree); + break; + + case This(Tree qualifier): + if (qualifier != Tree.Empty) { + print(qualifier); + print(TXT_DOT); + } + print(KW_THIS); + printType(tree); + break; + + case Select(Tree qualifier, Name name): + print(qualifier); + print(TXT_DOT); + printSymbolUse(tree.symbol(), name); + printType(tree); + break; + + case Ident(Name name): + printSymbolUse(tree.symbol(), name); + printType(tree); + break; + + case Literal(Object obj): + String str; + if (obj instanceof String) + str = "\"" + obj + "\""; + else + str = String.valueOf(obj); + print(Text.Literal(str)); + printType(tree); + break; + + case SingletonType(Tree ref): + print(ref); + print(TXT_DOT); print(KW_TYPE); + break; + + case SelectFromType(Tree qualifier, Name selector): + print(qualifier); + print(Text.Space); print(TXT_HASH); print(Text.Space); + printSymbolUse(tree.symbol(), selector); + break; + + case FunType(Tree[] argtpes, Tree restpe): + printArray(argtpes, TXT_LEFT_PAREN, TXT_RIGHT_PAREN, TXT_COMMA_SP); + print(TXT_RIGHT_ARROW); + print(restpe); + break; + + case CompoundType(Tree[] baseTypes, Tree[] refinements): + printArray(baseTypes, Text.None, Text.None, TXT_WITH_SP); + printArray(refinements, TXT_WITH_BLOCK_BEGIN, TXT_BLOCK_END, Text.Newline); + break; + + case TupleType(Tree[] types): + printArray(types, TXT_LEFT_BRACKET, TXT_RIGHT_BRACKET, TXT_COMMA_SP); + break; + + case AppliedType(Tree tpe, Tree[] args): + print(tpe); + indent(); + print(TXT_LEFT_BRACKET); + for (int i = 0; i < args.length; ++i) { + if (i > 0) print(TXT_COMMA_SP); + print(args[i]); + } + undent(); + print(TXT_RIGHT_BRACKET); + break; + + case CovariantType(Tree tpe): + print(TXT_PLUS); + print(tpe); + break; + + case Template(Tree[] parents, Tree[] body): + Debug.abort("unexpected case", tree); + break; + + default: + print(TXT_UNKNOWN); + break; + } + return this; + } + + // Printing helpers + + protected void printArray(Tree[] trees, Text open, Text close, Text sep) { + indent(); + print(open); + for (int i = 0; i < trees.length; ++i) { + if (i > 0) print(sep); + print(trees[i]); + } + undent(); + print(close); + } + + protected void printOpt(Text prefix, Tree tree, boolean spaceBefore) { + if (tree != Tree.Empty) { + if (spaceBefore) + print(Text.Space); + print(prefix); + print(Text.Space); + print(tree); + } + } + + // Printing of symbols + + protected String symbolString(Symbol symbol, Name name) { + if (symbol != null) + return symbol.name.toString(); + else + return name.toString(); + } + + protected void printSymbolDefinition(Symbol symbol, Name name) { + print(Text.Identifier(symbol, + symbolString(symbol, name), + SymbolUsage.Definition)); + } + + protected void printSymbolUse(Symbol symbol, Name name) { + print(Text.Identifier(symbol, + symbolString(symbol, name), + SymbolUsage.Use)); + } + + // Printing of trees + + protected void printType(Tree tree) { + if (Global.instance.printtypes) { + print(TXT_LEFT_BRACE); + if (tree.type != null) + print(Text.Simple(tree.type.toString())); + else + print(TXT_NULL); + print(TXT_RIGHT_BRACE); + } + } + + protected void printModifiers(int flags) { + if ((flags & Modifiers.ABSTRACT) != 0) { + print(KW_ABSTRACT); + print(Text.Space); + } + if ((flags & Modifiers.FINAL) != 0) { + print(KW_FINAL); + print(Text.Space); + } + if ((flags & Modifiers.PRIVATE) != 0) { + print(KW_PRIVATE); + print(Text.Space); + } + if ((flags & Modifiers.PROTECTED) != 0) { + print(KW_PROTECTED); + print(Text.Space); + } + if ((flags & Modifiers.QUALIFIED) != 0) { + print(KW_QUALIFIED); + print(Text.Space); + } + if ((flags & Modifiers.OVERRIDE) != 0) { + print(KW_OVERRIDE); + print(Text.Space); + } + if ((flags & Modifiers.CASE) != 0) { + print(KW_CASE); + print(Text.Space); + } + if ((flags & Modifiers.DEF) != 0) { + print(KW_DEF); + print(Text.Space); + } + if ((flags & Modifiers.STATIC) != 0) { + print(KW_STATIC); + print(Text.Space); + } + } + + protected void printTemplate(Text prefix, + Tree.Template templ, + boolean spaceBefore) { + if (! (templ.parents.length == 0 + || (templ.parents.length == 1 + && templ.parents[0] == Tree.Empty))) { + if (spaceBefore) + print(Text.Space); + print(prefix); + print(Text.Space); + printArray(templ.parents, Text.None, Text.None, TXT_WITH_SP); + } + + if (templ.body.length > 0) + printArray(templ.body, TXT_WITH_BLOCK_BEGIN, TXT_BLOCK_END, TXT_BLOCK_SEP); + } + + protected void printParams(Tree.TypeDef[] tparams) { + if (tparams.length > 0) { + print(TXT_LEFT_BRACKET); + for (int i = 0; i < tparams.length; i++) { + if (i > 0) print(TXT_COMMA_SP); + printParam(tparams[i]); + } + print(TXT_RIGHT_BRACKET); + } + } + + protected void printParams(Tree.ValDef[][] vparamss) { + for (int i = 0; i < vparamss.length; ++i) + printParams(vparamss[i]); + } + + protected void printParams(Tree.ValDef[] vparams) { + print(TXT_LEFT_PAREN); + for (int i = 0; i < vparams.length; ++i) { + if (i > 0) print(TXT_COMMA_SP); + printParam(vparams[i]); + } + print(TXT_RIGHT_PAREN); + } + + protected void printParam(Tree tree) { + switch (tree) { + case TypeDef(int mods, Name name, _, Tree bound): + printModifiers(mods); + printSymbolDefinition(tree.symbol(), name); + printOpt(KW_EXTENDS, bound, true); + break; + + case ValDef(int mods, Name name, Tree tpe, Tree.Empty): + printModifiers(mods); + printSymbolDefinition(tree.symbol(), name); + printOpt(TXT_COLON, tpe, false); + break; + + default: + Debug.abort("bad parameter: " + tree); + } + } +} diff --git a/sources/scalac/ast/printer/TreePrinter.java b/sources/scalac/ast/printer/TreePrinter.java new file mode 100644 index 0000000000..059b335c72 --- /dev/null +++ b/sources/scalac/ast/printer/TreePrinter.java @@ -0,0 +1,34 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scalac.ast.printer; + +import java.io.OutputStream; + +import scalac.Unit; +import scalac.ast.Tree; + +/** + * Interface for all abstract tree printers. + * + * @author Michel Schinz + * @version 1.0 + */ +public interface TreePrinter { + public void begin(); + public void end(); + public void flush(); + + public void beginSection(int level, String title); + + public void print(Unit unit); + + public TreePrinter print(Tree tree); + public TreePrinter print(String str); + public TreePrinter println(); +} diff --git a/sources/scalac/checkers/CheckOwners.java b/sources/scalac/checkers/CheckOwners.java new file mode 100644 index 0000000000..f23bcb4d7e --- /dev/null +++ b/sources/scalac/checkers/CheckOwners.java @@ -0,0 +1,168 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scalac.checkers; + +import scalac.util.*; +import scalac.ast.*; +import scalac.symtab.*; +import scalac.Global; +import scalac.util.Debug; +import Tree.*; + +/** + * Check that the owner of symbols is set correctly. + * + * @author Michel Schinz + * @version 1.0 + */ + +public class CheckOwners extends Checker { + protected Symbol currentOwner; + + public CheckOwners(Global global) { + super(global); + currentOwner = global.definitions.ROOT_CLASS; + } + + protected void traverse(Tree tree, Symbol owner) { + Symbol prevOwner = currentOwner; + currentOwner = owner; + traverse(tree); + currentOwner = prevOwner; + } + + protected void traverse(Tree[] array, Symbol owner) { + Symbol prevOwner = currentOwner; + currentOwner = owner; + traverse(array); + currentOwner = prevOwner; + } + + protected void traverse(Tree[][] array, Symbol owner) { + Symbol prevOwner = currentOwner; + currentOwner = owner; + traverse(array); + currentOwner = prevOwner; + } + + protected void traverse(Template templ, Symbol owner) { + Symbol prevOwner = currentOwner; + if (owner.kind == Kinds.CLASS) + currentOwner = owner.constructor(); + traverse(templ.parents); + currentOwner = owner; + + Symbol templSymbol = templ.symbol(); + Tree[] body = templ.body; + for (int i = 0; i < body.length; ++i) { + switch (body[i]) { + case PackageDef(_,_): + case ClassDef(_,_,_,_,_,_): + case ModuleDef(_,_,_,_): + case DefDef(_,_,_,_,_,_): + case ValDef(_,_,_,_): + case TypeDef(_,_,_,_): + traverse(body[i], owner); + break; + default: + traverse(body[i], templSymbol); + } + } + + currentOwner = prevOwner; + } + + protected void checkOwner(Tree tree, Symbol sym) { + Symbol owner = sym.owner(); + verify(tree, + owner == currentOwner, + "owner", + "incorrect owner for " + Debug.toString(sym) + ":\n" + + " found: " + Debug.toString(owner) + "\n" + + " required: " + Debug.toString(currentOwner)); + } + + public void traverse(Tree tree) { + switch(tree) { + case PackageDef(Tree packaged, Template impl): + check(tree); + traverse(packaged); + traverse(impl, packaged.symbol()); + break; + + case ClassDef(int mods, + Name name, + TypeDef[] tparams, + ValDef[][] vparams, + Tree tpe, + Template impl): { + check(tree); + traverse(tparams, tree.symbol()); + traverse(vparams, tree.symbol()); + traverse(tpe); + traverse(impl, tree.symbol()); + } break; + + case ModuleDef(int mods, Name name, Tree tpe, Template impl): { + check(tree); + traverse(tpe); + traverse(impl, tree.symbol()); + } break; + + case DefDef(int mods, + Name name, + TypeDef[] tparams, + ValDef[][] vparams, + Tree tpe, + Tree rhs): { + check(tree); + traverse(tparams, tree.symbol()); + traverse(vparams, tree.symbol()); + traverse(tpe, tree.symbol()); + traverse(rhs, tree.symbol()); + } break; + + case ValDef(int mods, Name name, Tree tpe, Tree rhs): { + check(tree); + traverse(tpe); + traverse(rhs, tree.symbol()); + } break; + + case TypeDef(int mods, Name name, TypeDef[] tparams, Tree rhs): { + check(tree); + traverse(tparams, tree.symbol()); + traverse(rhs, tree.symbol()); + } break; + + default: + super.traverse(tree); + } + } + + public void check(Tree tree) { + switch (tree) { + case PackageDef(_,_): + case ClassDef(_,_,_,_,_,_): + case ModuleDef(_,_,_,_): + case DefDef(_,_,_,_,_,_): + case ValDef(_,_,_,_): + case TypeDef(_,_,_,_): { + Symbol sym = tree.symbol(); + if (sym != null && sym != Symbol.NONE) { + checkOwner(tree, sym); + if (sym.kind == Kinds.CLASS) + checkOwner(tree, sym.constructor()); + } + } break; + + default: + ; // nothing to do + } + } +} diff --git a/sources/scalac/checkers/CheckSymbols.java b/sources/scalac/checkers/CheckSymbols.java new file mode 100644 index 0000000000..ab1a76ae2d --- /dev/null +++ b/sources/scalac/checkers/CheckSymbols.java @@ -0,0 +1,36 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scalac.checkers; + +import scalac.ast.Tree; +import scalac.symtab.Symbol; +import scalac.Global; + +/** + * Verify that all tree nodes for which hasSymbol() is true have a + * non-null symbol. + * + * @author Michel Schinz + * @version 1.0 + */ + +public class CheckSymbols extends Checker { + public CheckSymbols(Global global) { super(global); } + + public void check(Tree tree) { + verify(tree, + implies(tree.hasSymbol(), tree.symbol() != null), + "symbol not null", + "hasSymbol => symbol not null"); + verify(tree, + implies(tree.hasSymbol(), tree.symbol() != Symbol.NONE), + "symbol not NONE", + "hasSymbol => symbol not NONE"); + } +} diff --git a/sources/scalac/checkers/CheckTypes.java b/sources/scalac/checkers/CheckTypes.java new file mode 100644 index 0000000000..cfe2bd656d --- /dev/null +++ b/sources/scalac/checkers/CheckTypes.java @@ -0,0 +1,30 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scalac.checkers; + +import scalac.ast.Tree; +import scalac.util.Name; +import scalac.symtab.Type; +import scalac.Global; +import scalac.util.Debug; + +/** + * Check that all tree nodes have a type. + * + * @author Michel Schinz + * @version 1.0 + */ + +public class CheckTypes extends Checker { + public CheckTypes(Global global) { super(global); } + + public void check(Tree tree) { + verify(tree, tree.type != null, "non-null type", "type of tree is not null"); + } +} diff --git a/sources/scalac/checkers/Checker.java b/sources/scalac/checkers/Checker.java new file mode 100644 index 0000000000..ae52a5b1f6 --- /dev/null +++ b/sources/scalac/checkers/Checker.java @@ -0,0 +1,41 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scalac.checkers; + +import scalac.ast.*; +import scalac.Global; + +public abstract class Checker extends Traverser { + protected final Global global; + + public Checker(Global global) { + this.global = global; + } + + public boolean implies(boolean b1, boolean b2) { + return (!b1) | b2; + } + + public void verify(Tree tree, boolean b, String name, String message) { + if (! b) { + System.err.println("ERROR: Condition '" + name + "' violated (after " + + global.currentPhase + ")!"); + System.err.println(message); + global.debugPrinter.print(tree); + System.err.println(); + } + } + + abstract public void check(Tree tree); + + public void traverse(Tree tree) { + check(tree); + super.traverse(tree); + } +} diff --git a/sources/scalac/symtab/Definitions.java b/sources/scalac/symtab/Definitions.java new file mode 100644 index 0000000000..6d67edac42 --- /dev/null +++ b/sources/scalac/symtab/Definitions.java @@ -0,0 +1,421 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab; + +import scalac.*; +import scalac.util.*; +import scalac.symtab.classfile.*; +import Type.*; + +public class Definitions { + + /** the root module + */ + public final Symbol ROOT; + public final Symbol ROOT_CLASS; + public final Type ROOT_TYPE; + + /** the scala module + */ + public final Symbol SCALA; + public final Symbol SCALA_CLASS; + public final Type SCALA_TYPE; + + /** the java module + */ + public final Symbol JAVA; + public final Symbol JAVA_CLASS; + public final Type JAVA_TYPE; + + /** the java.lang module + */ + public final Symbol JAVALANG; + public final Symbol JAVALANG_CLASS; + public final Type JAVALANG_TYPE; + + /** the scala.runtime module + */ + public final Symbol SCALARUNTIME; + public final Symbol SCALARUNTIME_CLASS; + public final Type SCALARUNTIME_TYPE; + + /** the null value + */ + public final Symbol NULL; + + /** the scala.Any class + */ + public final Symbol ANY_CLASS; + public final Type ANY_TYPE; + + /** some methods of the scala.Any class + */ + public final Symbol MATCH; + public final Symbol IS; + public final Symbol AS; + public final Symbol EQEQ; + public final Symbol BANGEQ; + public final Symbol TOSTRING; + public final Symbol HASHCODE; + + /** a method of class java.lang.Throwable + */ + public final Symbol THROW; + + /** the scala.AnyVal class + */ + public final Symbol ANYVAL_CLASS; + public final Type ANYVAL_TYPE; + + /** the scala.AnyRef class + */ + public final Symbol ANYREF_CLASS; + public final Type ANYREF_TYPE; + + /** the java.lang.Object class + */ + public final Symbol JAVA_OBJECT_CLASS; + public final Type JAVA_OBJECT_TYPE; + + /** the scala.Object class + */ + public final Symbol OBJECT_CLASS; + public final Type OBJECT_TYPE; + + /** the primitive types + */ + public final Symbol BYTE_CLASS; + public final Type BYTE_TYPE; + public final Symbol SHORT_CLASS; + public final Type SHORT_TYPE; + public final Symbol CHAR_CLASS; + public final Type CHAR_TYPE; + public final Symbol INT_CLASS; + public final Type INT_TYPE; + public final Symbol LONG_CLASS; + public final Type LONG_TYPE; + public final Symbol FLOAT_CLASS; + public final Type FLOAT_TYPE; + public final Symbol DOUBLE_CLASS; + public final Type DOUBLE_TYPE; + public final Symbol BOOLEAN_CLASS; + public final Type BOOLEAN_TYPE; + public final Symbol UNIT_CLASS; + public final Type UNIT_TYPE; + + /** the array class + */ + public final Symbol ARRAY_CLASS; + + /** types from java.lang + */ + public final Symbol JAVA_STRING_CLASS; + public final Type JAVA_STRING_TYPE; + public final Symbol JAVA_THROWABLE_CLASS; + public final Type JAVA_THROWABLE_TYPE; + + /** types from scala + */ + public final Symbol STRING_CLASS; + public final Type STRING_TYPE; + + /** string concatenation pseudo-methods of classes scala.Any and + * java.lang.String + */ + //public final Symbol ANY_PLUS_STRING; + public final Symbol STRING_PLUS_ANY; + + /** members of class Boolean + */ + private Symbol TRUE; + private Symbol FALSE; + private Symbol BARBAR; + private Symbol AMPAMP; + + public Symbol TRUE () { loadBooleanMembers(); return TRUE ; } + public Symbol FALSE () { loadBooleanMembers(); return FALSE ; } + public Symbol BARBAR() { loadBooleanMembers(); return BARBAR; } + public Symbol AMPAMP() { loadBooleanMembers(); return AMPAMP; } + + public Definitions(Global global) { + // a hack to make definitions accessible earlier to other + // components + global.definitions = this; + PackageParser pparser = new PackageParser(global); + + // this is the root value; all top-level functions, + // modules etc. are a member of this value + ROOT = TermSymbol.newJavaPackageModule( + Names.EMPTY, Symbol.NONE, pparser); + ROOT_CLASS = ROOT.moduleClass(); + // this is a prefix for all types inside of the anonymous package + ROOT_TYPE = ROOT_CLASS.thisType(); + + // the scala module + SCALA = getModule(Names.scala); + // the scala class + SCALA_CLASS = SCALA.moduleClass(); + // the scala package as a prefix + SCALA_TYPE = Type.singleType(ROOT_TYPE, SCALA); + + // the java module + JAVA = getModule(Names.java); + // the java class + JAVA_CLASS = JAVA.moduleClass(); + // the java package as a prefix + JAVA_TYPE = Type.singleType(ROOT_TYPE, JAVA); + + // the java.lang module + JAVALANG = getModule(Names.java_lang); + // the java.lang class + JAVALANG_CLASS = JAVALANG.moduleClass(); + // the java.lang package as a prefix + JAVALANG_TYPE = Type.singleType(JAVA_TYPE, JAVALANG); + + // the scala.runtime module + SCALARUNTIME = getModule(Names.scala_runtime); + // the scala.runtime class + SCALARUNTIME_CLASS = SCALARUNTIME.moduleClass(); + // the scala.runtime package as a prefix + SCALARUNTIME_TYPE = Type.singleType(SCALA_TYPE, SCALARUNTIME); + + // the scala.ANY classs + ANY_CLASS = new ClassSymbol( + Position.NOPOS, Names.Any.toTypeName(), SCALA_CLASS, Modifiers.JAVA); + SCALA_CLASS.members().enter(ANY_CLASS); + ANY_TYPE = monoType(ANY_CLASS); + ANY_CLASS.setInfo(Type.compoundType(Type.EMPTY_ARRAY, new Scope(), ANY_CLASS)); + ANY_CLASS.constructor().setInfo( + Type.PolyType(Symbol.EMPTY_ARRAY, ANY_TYPE)); + + // the java.lang.OBJECT class + JAVA_OBJECT_CLASS = getClass(Names.java_lang_Object); + JAVA_OBJECT_TYPE = monoType(JAVA_OBJECT_CLASS); + JAVA_OBJECT_CLASS.setInfo(pparser.classCompletion); + + // the scala.ANYREF class + ANYREF_CLASS = new TypeSymbol( + Kinds.ALIAS, Position.NOPOS, Names.AnyRef.toTypeName(), + SCALA_CLASS, Modifiers.JAVA) + .setInfo(JAVA_OBJECT_TYPE); + ANYREF_CLASS.constructor().setInfo( + Type.PolyType(Symbol.EMPTY_ARRAY, Type.NoType)); + + ANYREF_TYPE = monoType(ANYREF_CLASS); + SCALA.members().enter(ANYREF_CLASS); + + // the scala.OBJECT class + OBJECT_CLASS = getClass(Names.scala_Object); + OBJECT_TYPE = monoType(OBJECT_CLASS); + + // the scala.ANYVAL class + ANYVAL_CLASS = getClass(Names.scala_AnyVal); + ANYVAL_TYPE = monoType(ANYVAL_CLASS); + + // the primitive types + DOUBLE_CLASS = getClass(Names.scala_Double); + DOUBLE_TYPE = monoType(DOUBLE_CLASS); + FLOAT_CLASS = getClass(Names.scala_Float); + FLOAT_TYPE = monoType(FLOAT_CLASS); + LONG_CLASS = getClass(Names.scala_Long); + LONG_TYPE = monoType(LONG_CLASS); + INT_CLASS = getClass(Names.scala_Int); + INT_TYPE = monoType(INT_CLASS); + CHAR_CLASS = getClass(Names.scala_Char); + CHAR_TYPE = monoType(CHAR_CLASS); + SHORT_CLASS = getClass(Names.scala_Short); + SHORT_TYPE = monoType(SHORT_CLASS); + BYTE_CLASS = getClass(Names.scala_Byte); + BYTE_TYPE = monoType(BYTE_CLASS); + BOOLEAN_CLASS = getClass(Names.scala_Boolean); + BOOLEAN_TYPE = monoType(BOOLEAN_CLASS); + UNIT_CLASS = getClass(Names.scala_Unit); + UNIT_TYPE = monoType(UNIT_CLASS); + + // the array class + ARRAY_CLASS = getClass(Names.scala_Array); + + // add members to java.lang.Throwable + JAVA_THROWABLE_CLASS = getClass(Names.java_lang_Throwable); + JAVA_THROWABLE_TYPE = monoType(JAVA_THROWABLE_CLASS); + THROW = new TermSymbol( + Position.NOPOS, Names.throw_, JAVA_THROWABLE_CLASS, Modifiers.FINAL); + Symbol tvar = newTypeParameter(THROW, ANY_TYPE); + THROW.setInfo(Type.PolyType(new Symbol[]{tvar}, tvar.type())); + JAVA_THROWABLE_CLASS.members().enter(THROW); + + // add the java.lang.String class to the scala package + JAVA_STRING_CLASS = getClass(Names.java_lang_String); + JAVA_STRING_TYPE = monoType(JAVA_STRING_CLASS); + STRING_CLASS = new TypeSymbol( + Kinds.ALIAS, Position.NOPOS, Names.String.toTypeName(), SCALA_CLASS, 0) + .setInfo(JAVA_STRING_TYPE); + STRING_CLASS.constructor().setInfo( + Type.PolyType(Symbol.EMPTY_ARRAY, Type.NoType)); + STRING_TYPE = monoType(STRING_CLASS); + SCALA.members().enter(STRING_CLASS); + + /* + ANY_PLUS_STRING = new TermSymbol( + Position.NOPOS, Names.PLUS, ANY_CLASS, Modifiers.FINAL); + ANY_PLUS_STRING.setInfo( + Type.MethodType( + new Symbol[]{newParameter(ANY_PLUS_STRING, STRING_TYPE)}, + STRING_TYPE)); + ANY_CLASS.members().enter(ANY_PLUS_STRING); + */ + + STRING_PLUS_ANY = new TermSymbol( + Position.NOPOS, Names.PLUS, STRING_CLASS, Modifiers.FINAL); + STRING_PLUS_ANY.setInfo( + Type.MethodType( + new Symbol[]{newParameter(STRING_PLUS_ANY, ANY_TYPE)}, + STRING_TYPE)); + STRING_CLASS.members().enter(STRING_PLUS_ANY); + + // add members to class scala.Any + MATCH = new TermSymbol( + Position.NOPOS, Names.match, ANY_CLASS, Modifiers.FINAL); + MATCH.setInfo( + Type.MethodType( + new Symbol[]{newParameter(MATCH, OBJECT_TYPE)}, + OBJECT_TYPE)); + ANY_CLASS.members().enter(MATCH); + + AS = new TermSymbol( + Position.NOPOS, Names.as, ANY_CLASS, Modifiers.FINAL); + tvar = newTypeParameter(AS, ANY_TYPE); + AS.setInfo(Type.PolyType(new Symbol[]{tvar}, tvar.type())); + ANY_CLASS.members().enter(AS); + + IS = new TermSymbol( + Position.NOPOS, Names.is, ANY_CLASS, Modifiers.FINAL); + IS.setInfo(Type.PolyType(new Symbol[]{newTypeParameter(IS, ANY_TYPE)}, + BOOLEAN_TYPE)); + ANY_CLASS.members().enter(IS); + + EQEQ = new TermSymbol( + Position.NOPOS, Names.EQEQ, ANY_CLASS, 0); + EQEQ.setInfo(Type.MethodType(new Symbol[]{newParameter(EQEQ, ANY_TYPE)}, + BOOLEAN_TYPE)); + ANY_CLASS.members().enter(EQEQ); + + BANGEQ = new TermSymbol( + Position.NOPOS, Names.BANGEQ, ANY_CLASS, 0); + BANGEQ.setInfo(Type.MethodType(new Symbol[]{newParameter(BANGEQ, ANY_TYPE)}, + BOOLEAN_TYPE)); + ANY_CLASS.members().enter(BANGEQ); + + TOSTRING = new TermSymbol( + Position.NOPOS, Names.toString, ANY_CLASS, 0); + TOSTRING.setInfo(Type.MethodType(Symbol.EMPTY_ARRAY, STRING_TYPE)); + ANY_CLASS.members().enter(TOSTRING); + + HASHCODE = new TermSymbol( + Position.NOPOS, Names.hashCode, ANY_CLASS, 0); + HASHCODE.setInfo(Type.MethodType(Symbol.EMPTY_ARRAY, INT_TYPE)); + ANY_CLASS.members().enter(HASHCODE); + + // add a null value to the root scope + NULL = new TermSymbol( + Position.NOPOS, Names.null_, ROOT_CLASS, 0); + tvar = newTypeParameter(NULL, ANYREF_TYPE); + NULL.setInfo(Type.PolyType(new Symbol[]{tvar}, tvar.type())); + ROOT.members().enter(NULL); + } + + private Symbol newParameter(Symbol owner, Type tp) { + return new TermSymbol(Position.NOPOS, Name.fromString("v"), owner, Modifiers.PARAM) + .setInfo(tp); + } + + private Symbol newTypeParameter(Symbol owner, Type bound) { + return new TypeSymbol( + Kinds.TYPE, Position.NOPOS, Name.fromString("T").toTypeName(), owner, Modifiers.PARAM) + .setInfo(bound); + } + + public Symbol getModule(Name fullname) { + Scope scope = ROOT_CLASS.members(); + int i = 0; + int j = fullname.pos((byte)'.', i); + while (j < fullname.length()) { + scope = scope.lookup(fullname.subName(i, j)).members(); + i = j + 1; + j = fullname.pos((byte)'.', i); + } + Symbol sym = scope.lookup(fullname.subName(i, fullname.length())); + if (!sym.isModule()) { + switch (sym.type()) { + case OverloadedType(Symbol[] alts, Type[] alttypes): + for (int k = 0; k < alts.length; k++) + if ((sym = alts[k]).isModule()) break; + } + } + assert sym.isModule() : "no module '" + fullname + "'"; + return sym; + } + + public Symbol getClass(Name fullname) { + Scope scope = ROOT_CLASS.members(); + int i = 0; + int j = fullname.pos((byte)'.', i); + while (j < fullname.length()) { + scope = scope.lookup(fullname.subName(i, j)).members(); + i = j + 1; + j = fullname.pos((byte)'.', i); + } + Symbol sym = scope.lookup(fullname.subName(i, fullname.length()).toTypeName()); + assert sym.kind != Kinds.NONE : "no class '" + fullname + "'"; + return sym; + } + + public Type getType(Name fullname) { + return getClass(fullname).type(); + } + + public Type monoType(Symbol c) { + return Type.TypeRef(c.owner().thisType(), c, Type.EMPTY_ARRAY); + } + + public Type getJavaType(Name fullname) { + return monoType(getClass(fullname)); + } + + private void loadBooleanMembers() { + if (TRUE != null) return; + Symbol booleanStatics = getModule(Names.scala_Boolean); + TRUE = booleanStatics.members().lookup(Names.True); + FALSE = booleanStatics.members().lookup(Names.False); + BARBAR = BOOLEAN_TYPE.lookup(Names.BARBAR); + AMPAMP = BOOLEAN_TYPE.lookup(Names.AMPAMP); + } + + public Type arrayType(Type elemtpe) { + return Type.appliedType(monoType(ARRAY_CLASS), new Type[]{elemtpe}); + } + + public Type functionType(Type[] argtps, Type restp) { + Type[] argtps1 = new Type[argtps.length + 1]; + System.arraycopy(argtps, 0, argtps1, 0, argtps.length); + argtps1[argtps.length] = Type.covarType(restp); + return Type.appliedType( + getType(Name.fromString("scala.Function" + argtps.length)), + argtps1); + } + + public Type tupleType(Type[] args) { + assert args.length > 0; + Type[] args1 = new Type[args.length]; + for (int i = 0; i < args.length; i++) + args1[i] = Type.covarType(args[i]); + return Type.appliedType( + getType(Name.fromString("scala.Tuple" + args.length)), args1); + } +} diff --git a/sources/scalac/symtab/Kinds.java b/sources/scalac/symtab/Kinds.java new file mode 100644 index 0000000000..8902ff4699 --- /dev/null +++ b/sources/scalac/symtab/Kinds.java @@ -0,0 +1,28 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab; + + +public interface Kinds { + + /** kind of error symbol + */ + int ERROR = 0; + + /** kind of non-existent symbol + */ + int NONE = 1; + + /** definition kinds + */ + int ALIAS = 2; + int CLASS = 3; + int TYPE = 4; + int VAL = 5; +} diff --git a/sources/scalac/symtab/Modifiers.java b/sources/scalac/symtab/Modifiers.java new file mode 100644 index 0000000000..3fc119419e --- /dev/null +++ b/sources/scalac/symtab/Modifiers.java @@ -0,0 +1,135 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab; + +public interface Modifiers { + + // modifiers + int ABSTRACT = 0x00000001; + int FINAL = 0x00000002; + int PRIVATE = 0x00000004; + int PROTECTED = 0x00000008; + + int QUALIFIED = 0x00000010; + int OVERRIDE = 0x00000020; + int CASE = 0x00000040; + int ABSTRACTCLASS = 0x00000080; // abstract class + + int DEF = 0x00000100; // a def parameter + int SYNTHETIC = 0x00000200; + int DEPRECATED = 0x00000400; + int JAVA = 0x00000800; // symbol was defined by a Java class + + int MODUL = 0x00001000; // symbol is module or class implementing a module + int MUTABLE = 0x00002000; // symbol is a mutable variable. + int PARAM = 0x00004000; // symbol is a (type) parameter to a method + + int INITIALIZED = 0x00010000; // symbol's definition is complete + int LOCKED = 0x00020000; // temporary flag to catch cyclic dependencies + int ACCESSED = 0x00040000; // symbol was accessed at least once + int SELECTOR = 0x00080000; // symbol was used as selector in Select + + int PACKAGE = 0x00100000; // symbol is a java packages. + int LABEL = 0x00200000; // symbol is a label symbol + int STATIC = 0x00400000; // "static" inner classes (i.e. after class norm.) + int STABLE = 0x00800000; // functions that are assumed to be stable + // (typically, access methods for valdefs) + + int CAPTURED = 0x01000000; // variables is accessed from nested function. + + int ACCESSOR = 0x04000000; // function is an access function for a + // value or variable + int BRIDGE = 0x0800000; // function is a bridge method. + + int INTERFACE = 0x10000000; // symbol is a Java interface + int TRAIT = 0x20000000; // symbol is a Trait + + int SNDTIME = 0x40000000; //debug + + // masks + int SOURCEFLAGS = 0x00000077 | PARAM | TRAIT; // these modifiers can be set in source programs. + int ACCESSFLAGS = PRIVATE | PROTECTED; + + public static class Helper { + + public static boolean isAbstract(int flags) { + return (flags & (ABSTRACT | ABSTRACTCLASS)) != 0; + } + + public static boolean isFinal(int flags) { + return (flags & FINAL) != 0; + } + + public static boolean isPrivate(int flags) { + return (flags & PRIVATE) != 0; + } + + public static boolean isProtected(int flags) { + return (flags & PROTECTED) != 0; + } + + public static boolean isQualified(int flags) { + return (flags & QUALIFIED) != 0; + } + + public static boolean isOverride(int flags) { + return (flags & OVERRIDE) != 0; + } + + public static boolean isCase(int flags) { + return (flags & CASE) != 0; + } + + public static boolean isInterface(int flags) { + return (flags & INTERFACE) != 0; + } + + public static boolean isDef(int flags) { + return (flags & DEF) != 0; + } + + public static boolean isModClass(int flags) { + return (flags & MODUL) != 0; + } + + public static boolean isStatic(int flags) { + return (flags & STATIC) != 0; + } + + public static boolean isJava(int flags) { + return (flags & JAVA) != 0; + } + + public static boolean isNoVal(int flags) { + return (flags & PACKAGE) != 0; + } + + public static String toString(int flags) { + StringBuffer buffer = new StringBuffer(); + toString(buffer, flags); + return buffer.toString(); + } + + public static void toString(StringBuffer buffer, int flags) { + //buffer.append(flags).append(": ");//debug + int marker = buffer.length(); + if (isPrivate(flags)) buffer.append("private "); + if (isProtected(flags)) buffer.append("protected "); + if (isAbstract(flags)) buffer.append("abstract "); + if (isFinal(flags)) buffer.append("final "); + if (isQualified(flags)) buffer.append("qualified "); + if (isInterface(flags)) buffer.append("interface "); + if (isCase(flags)) buffer.append("case "); + if (isDef(flags)) buffer.append("def "); + if (isOverride(flags)) buffer.append("override "); + int length = buffer.length(); + buffer.setLength(length - (length == marker ? 0 : 1)); + } + } +} diff --git a/sources/scalac/symtab/NameMangler.java b/sources/scalac/symtab/NameMangler.java new file mode 100644 index 0000000000..30de8193d0 --- /dev/null +++ b/sources/scalac/symtab/NameMangler.java @@ -0,0 +1,33 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** +** $Id$ +\* */ + +package scalac.symtab; + +import scalac.util.Name; +import java.util.HashMap; + +public class NameMangler { + + private HashMap/*<Symbol,HashMap<Symbol,int[]>>*/ mangleMap = new HashMap(); + + public void setMangledName(Symbol innerclazz) { + Symbol topclazz = innerclazz.enclToplevelClass(); + HashMap map = (HashMap) mangleMap.get(topclazz); + if (map == null) { + map = new HashMap(); + mangleMap.put(topclazz, map); + } + int[] ctr = (int[]) map.get(innerclazz); + if (ctr == null) { + ctr = new int[1]; + map.put(innerclazz, ctr); + } + innerclazz.setMangledName( + Name.fromString(topclazz.name + "$" + (ctr[0]++) + innerclazz.name)); + } +} diff --git a/sources/scalac/symtab/Scope.java b/sources/scalac/symtab/Scope.java new file mode 100644 index 0000000000..3bd22f51ed --- /dev/null +++ b/sources/scalac/symtab/Scope.java @@ -0,0 +1,306 @@ + /* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab; + +import scalac.util.*; +import scalac.ApplicationError; + +public class Scope { + + public static abstract class SymbolIterator { + public abstract boolean hasNext(); + public abstract Symbol next(); + } + + /** A symbol iterator that returns all alternatives of an overloaded symbol + * instead of the overloaded symbol itself. + */ + public static class UnloadIterator extends SymbolIterator { + private SymbolIterator iterator; + private Symbol[] alternatives; + private int index; + + public UnloadIterator(SymbolIterator iterator) { + this.iterator = iterator; + this.alternatives = null; + this.index = -1; + } + + public boolean hasNext() { + return index >= 0 || iterator.hasNext(); + } + public Symbol next() { + if (index >= 0) { + Symbol symbol = alternatives[index++]; + if (index == alternatives.length) { + alternatives = null; + index = -1; + } + return symbol; + } else { + Symbol symbol = iterator.next(); + switch (symbol.type()) { + case OverloadedType(Symbol[] alts, _): + alternatives = alts; + index = 0; + return next(); + default: + return symbol; + } + } + } + } + + public static class Entry { + + /** the absent entry + */ + public static final Entry NONE = new Entry(); + + /** the symbol of the entry (this is the symbol containing the name) + */ + public Symbol sym; + + /** the next entry in the hash bucket + */ + Entry tail; + + /** the next entry in this scope + */ + public Entry next; + + /** The owner of the entry; + */ + public Scope owner; + + Entry(Symbol sym, Scope owner) { + if (sym == null) throw new ApplicationError(); + this.sym = sym; + this.owner = owner; + this.next = owner.elems; + owner.elems = this; + } + + private Entry() { + this.sym = Symbol.NONE; + } + + public Entry setSymbol(Symbol sym) { + this.sym = sym; + owner.elemsCache = null; + return this; + } + + public int hashCode() { + return sym.name.index; + } + + public String toString() { + return sym.toString(); + } + } + + /** all elements of this scope + */ + public Entry elems; + + /** the hash table + */ + private Entry[] hashtable; + + /** a cache for all elements, to be used by symbol iterator. + */ + private Symbol[] elemsCache = null; + + /** size and mask of hash tables + * todo: make hashtables grow? + */ + private final int HASHSIZE = 0x80; + private final int HASHMASK = 0x7f; + + /** the threshold number of entries from which a hashtable is constructed. + */ + private final int MIN_HASH = 6; + + /** construct a new name space + */ + public Scope() { + this.elems = Entry.NONE; + } + + public Scope(Entry elems) { + this.elems = elems; + if (size() >= MIN_HASH) createHash(); + } + + public Scope(Scope base) { + this.elems = base.elems; + if (base.hashtable != null) { + this.hashtable = new Entry[HASHSIZE]; + for (int i = 0; i < HASHSIZE; i++) + hashtable[i] = base.hashtable[i]; + } + } + + public Scope(Symbol[] members) { + this(); + for (int i = 0; i < members.length; i++) + enter(members[i]); + } + + /** the number of entries in this scope + */ + int size() { + int s = 0; + for (Entry e = elems; e != Entry.NONE; e = e.next) s++; + return s; + } + + private Scope enter(Entry e) { + elems = e; + elemsCache = null; + if (hashtable != null) { + int i = e.sym.name.index & HASHMASK; + elems.tail = hashtable[i]; + hashtable[i] = elems; + } else if (size() >= MIN_HASH) { + createHash(); + } + return this; + } + + /** enter a symbol + */ + public Scope enter(Symbol sym) { + return enter(new Entry(sym, this)); + } + + public Scope enterOrOverload(Symbol sym) { + Entry e = lookupEntry(sym.name); + if (e.owner == this && (sym.flags & Modifiers.PRIVATE) == 0) { + e.setSymbol(e.sym.overloadWith(sym)); + return this; + } else { + return enter(sym); + } + } + + private void createHash() { + hashtable = new Entry[HASHSIZE]; + for (int i = 0; i < HASHSIZE; i++) + hashtable[i] = Entry.NONE; + enterInHash(elems); + } + + private void enterInHash(Entry e) { + if (e != Entry.NONE) { + enterInHash(e.next); + int i = e.sym.name.index & HASHMASK; + e.tail = hashtable[i]; + hashtable[i] = e; + } + } + + /** remove entry + */ + public void unlink(Entry e) { + Entry e1 = hashtable[e.sym.name.index & HASHMASK]; + if (e1 == e) { + hashtable[e.sym.name.index & HASHMASK] = e.tail; + } else { + while (e1.tail != e) e1 = e1.tail; + } + if (elems == e) { + elems = e.next; + } else { + e1 = elems; + while (e1.next != e) e1 = e1.next; + e1.next = e.next; + } + elemsCache = null; + } + + /** lookup a symbol + */ + public Symbol lookup(Name name) { + return lookupEntry(name).sym; + } + + /** lookup a symbol entry. + */ + public Entry lookupEntry(Name name) { + Entry e; + if (hashtable != null) { + e = hashtable[name.index & HASHMASK]; + while (e != Entry.NONE && e.sym.name != name) e = e.tail; + } else { + e = elems; + while (e != Entry.NONE && e.sym.name != name) e = e.next; + } + return e; + } + + /** return all symbols as an array, + * in the order they were entered in this scope. + */ + public Symbol[] elements() { + if (elemsCache == null) { + int s = size(); + elemsCache = new Symbol[s]; + for (Entry e = elems; e != Entry.NONE; e = e.next) + elemsCache[--s] = e.sym; + } + return elemsCache; + } + + /** return all symbols as an iterator, + * in the order they were entered in this scope. + */ + public SymbolIterator iterator() { return new MySymbols(); } + + class MySymbols extends SymbolIterator { + + private int index; + MySymbols() { + elements(); + index = 0; + } + + public boolean hasNext() { + return index < elemsCache.length; + } + + public Symbol next() { + return elemsCache[index++]; + } + } + + public String toString() { + StringBuffer str = new StringBuffer("{"); + SymbolIterator it = iterator(); + while (it.hasNext()) { + str.append("\n " + it.next().defString()); + } + str.append("}"); + return str.toString(); + } + + public String simpleToString() { + StringBuffer str = new StringBuffer("{"); + SymbolIterator it = iterator(); + while (it.hasNext()) { + str.append("\n " + it.next().name); + } + str.append("}"); + return str.toString(); + } + + public static Scope EMPTY = new Scope(); +} + diff --git a/sources/scalac/symtab/SourceCompleter.java b/sources/scalac/symtab/SourceCompleter.java new file mode 100644 index 0000000000..1656f2029f --- /dev/null +++ b/sources/scalac/symtab/SourceCompleter.java @@ -0,0 +1,53 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab; + +import scalac.*; +import scalac.ast.parser.*; +import scalac.typechecker.Analyzer; +import java.io.*; + + +public class SourceCompleter extends Type.LazyType { + + /** the global compilation environment + */ + protected Global global; + protected String filename; + private boolean completed = false; + + public SourceCompleter(Global global, String filename) { + this.global = global; + this.filename = filename; + } + + /** complete class symbol c by loading the class + */ + public void complete(Symbol c) { + if (completed) { + c.setInfo(Type.ErrorType); + } else if (filename != null) { + try { + String fname = filename; + long msec = System.currentTimeMillis(); + Unit unit = new Unit(global, new Sourcefile(filename, false)); + filename = null; + global.PHASE.PARSER.createPhase(global).apply(unit); + ((Analyzer)global.PHASE.ANALYZER.createPhase(global)).lateEnter(unit, c); + global.operation("added " + fname + " in " + + (System.currentTimeMillis() - msec) + "ms"); + } catch (IOException e) { + e.printStackTrace(); + global.error("i/o error while loading " + c); + c.setInfo(Type.ErrorType); + } + completed = true; + } + } +} diff --git a/sources/scalac/symtab/SymSet.java b/sources/scalac/symtab/SymSet.java new file mode 100644 index 0000000000..f72b9764f5 --- /dev/null +++ b/sources/scalac/symtab/SymSet.java @@ -0,0 +1,89 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scalac.symtab; + +/** Sets of symbols, implemented as binary trees. + */ +public class SymSet { + private SymSet l, r; + private Symbol sym; + + public SymSet() {} + + private SymSet(Symbol sym, SymSet l, SymSet r) { + this.sym = sym; this.l = l; this.r = r; + } + + /** Union of this set and `{sym}'. + */ + public SymSet incl(Symbol sym) { + if (this == EMPTY) { + return new SymSet(sym, EMPTY, EMPTY); + } else if (sym == this.sym) { + return this; + } else if (sym.isLess(this.sym)) { + return new SymSet(this.sym, l.incl(sym), r); + } else { + assert this.sym.isLess(sym); + return new SymSet(this.sym, l, r.incl(sym)); + } + } + + /** Is `sym' an element of this set? + */ + public boolean contains(Symbol sym) { + if (this == EMPTY) { + return false; + } else if (sym == this.sym) { + return true; + } else if (sym.isLess(this.sym)) { + return l.contains(sym); + } else { + assert this.sym.isLess(sym); + return r.contains(sym); + } + } + + /** The number of elements in ths set. + */ + public int size() { + if (this == EMPTY) { + return 0; + } else { + return 1 + l.size() + r.size(); + } + } + + /** Copy elements of this set into `ss', starting at index `from'. + * Return index one past last element copied. + */ + public int copyToArray(Symbol[] ss, int from) { + if (this == EMPTY) { + return from; + } else { + from = l.copyToArray(ss, from); + ss[from] = sym; + return r.copyToArray(ss, from + 1); + } + } + + /** Return all elements of this set as an array. + */ + public Symbol[] toArray() { + int s = size(); + if (s == 0) return Symbol.EMPTY_ARRAY; + Symbol[] ss = new Symbol[s]; + copyToArray(ss, 0); + return ss; + } + + /** The empty set. + */ + public final static SymSet EMPTY = new SymSet(); +} diff --git a/sources/scalac/symtab/Symbol.java b/sources/scalac/symtab/Symbol.java new file mode 100644 index 0000000000..f13192848b --- /dev/null +++ b/sources/scalac/symtab/Symbol.java @@ -0,0 +1,1291 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** +** $Id$ +\* */ + +//todo check significance of JAVA flag. + +package scalac.symtab; + +import scalac.ApplicationError; +import scalac.Global; +import scalac.PhaseDescriptor; +import scalac.util.ArrayApply; +import scalac.util.Name; +import scalac.util.Names; +import scalac.util.NameTransformer; +import scalac.util.Position; +import scalac.util.Debug; +import scalac.symtab.classfile.*; + + +public abstract class Symbol implements Modifiers, Kinds { + + /** An empty symbol array */ + public static final Symbol[] EMPTY_ARRAY = new Symbol[0]; + + /** An empty array of symbol arrays */ + public static final Symbol[][] EMPTY_ARRAY_ARRAY = new Symbol[0][]; + + /** The error symbol */ + public static final ErrorSymbol ERROR = new ErrorSymbol(); + + /** The absent symbol */ + public static final NoSymbol NONE = new NoSymbol(); + +// Fields ------------------------------------------------------------- + + /** The kind of the symbol */ + public int kind; + + /** The position of the symbol */ + public int pos; + + /** The name of the symbol */ + public Name name; + + /** The modifiers of the symbol */ + public int flags; + + /** The owner of the symbol */ + private Symbol owner; + + /** The infos of the symbol */ + private TypeIntervalList infos = TypeIntervalList.EMPTY; + +// Constructors ----------------------------------------------------------- + + /** Generic symbol constructor */ + public Symbol(int kind, int pos, Name name, Symbol owner, int flags) { + assert (!isTerm() || !name.isTypeName()) && (!isType() || name.isTypeName()); + + this.kind = kind; + this.pos = pos; + this.name = name; + this.owner = owner; + this.flags = flags & ~(INITIALIZED | LOCKED); // safety first + } + + /** Return a fresh symbol with the same fields as this one. + */ + public abstract Symbol cloneSymbol(); + + /** copy all fields to `sym' + */ + public void copyTo(Symbol sym) { + sym.kind = kind; + sym.pos = pos; + sym.name = name; + sym.flags = flags; + sym.owner = owner; + sym.infos = infos; + } + +// Setters --------------------------------------------------------------- + + /** Set the mangled name of this Symbol */ + public Symbol setMangledName(Name name) { + throw new ApplicationError("illegal operation on " + getClass()); + } + + /** Set owner */ + public Symbol setOwner(Symbol owner) { + this.owner = owner; + return this; + } + + /** Set information, except if symbol is both initialized and locked. + */ + public Symbol setInfo(Type info) { + return setInfo(info, currentPhaseId()); + } + + public Symbol setInfo(Type info, int limit) { + if ((flags & (INITIALIZED | LOCKED)) != (INITIALIZED | LOCKED)) { + if (infos == TypeIntervalList.EMPTY) + infos = new TypeIntervalList(TypeIntervalList.EMPTY); + infos.limit = limit; + infos.info = info; + } + return this; + } + + /** Set type -- this is an alias for setInfo(Type info) */ + public Symbol setType(Type info) { return setInfo(info); } + + public Symbol updateInfo(Type info) { + // Global.instance.currentPhase.setInfo(this, info); + if (infos.limit <= Global.instance.currentPhase.id) { + infos = new TypeIntervalList(infos); + infos.limit = Global.instance.currentPhase.id + 1; + } else { + assert infos.limit == Global.instance.currentPhase.id + 1 : this; + } + infos.info = info; + return this; + } + +// Symbol classification ---------------------------------------------------- + + /** Does this symbol denote a type? */ + public final boolean isType() { + return kind == TYPE || kind == CLASS || kind == ALIAS; + } + + /** Does this symbol denote a term? */ + public final boolean isTerm() { + return kind == VAL; + } + + /** Does this symbol denote a value? */ + public final boolean isValue() { + return kind == VAL && !(isModule() && isJava()) && !isPackage(); + } + + /** Does this symbol denote a variable? */ + public final boolean isVariable() { + return kind == VAL && (flags & MUTABLE) != 0; + } + + /** Does this symbol denote a method? + */ + public final boolean isInitializedMethod() { + if (infos.limit < 0) return false; + switch (rawInfo()) { + case MethodType(_, _): + case PolyType(_, _): return true; + default: return false; + } + } + + public final boolean isMethod() { + initialize(); + return isInitializedMethod(); + } + + /* Does this symbol denote an anonymous class? */ + public final boolean isAnonymousClass() { + return kind == CLASS && + (name == Names.EMPTY.toTypeName() || + name == Names.ANON_CLASS_NAME.toTypeName()); + } + + /** Does this symbol denote the root class or root module? + */ + public final boolean isRoot() { + return this.moduleClass() == Global.instance.definitions.ROOT_CLASS; + } + + /** Does this symbol denote something loaded from a Java class? */ + public final boolean isJava() { + return (flags & JAVA) != 0; + } + + /** Does this symbol denote a Java package? */ + public final boolean isPackage() { + return (flags & PACKAGE) != 0; + } + + /** Does this symbol denote a module? */ + public final boolean isModule() { + return kind == VAL && (flags & MODUL) != 0; + } + + /** Does this symbol denote a module? */ + public final boolean isModuleClass() { + return kind == CLASS && (flags & MODUL) != 0; + } + + /** Does this symbol denote a module? */ + public final boolean isClass() { + return kind == CLASS; + } + + /** Does this symbol denote a case class? + */ + public final boolean isCaseClass() { + return kind == CLASS && (flags & CASE) != 0; + } + + /** Does this symbol denote a uniform (i.e. parameterless) class? */ + public final boolean isTrait() { + return kind == CLASS && (flags & TRAIT) != 0; + } + + /** Does this class symbol denote a compound type symbol? + */ + public final boolean isCompoundSym() { + return name == Names.COMPOUND_NAME.toTypeName(); + } + + /** Does this symbol denote an interface? */ + public final boolean isInterface() { + return (flags & INTERFACE) != 0; + } + + /** Does this symbol denote a static member? */ + public final boolean isStatic() { + return (flags & STATIC) != 0; + } + + /** Is this symbol locally defined? I.e. not a member of a class or module */ + public final boolean isLocal() { + return owner.kind == VAL && !owner.isPrimaryConstructor(); + } + + /** Is this symbol a parameter? Includes type parameters of methods. + */ + public final boolean isParameter() { + return (flags & PARAM) != 0; + } + + /** Is this symbol a def parameter? + */ + public final boolean isDefParameter() { + return (flags & (PARAM | DEF)) == (PARAM | DEF); + } + + /** Is this class locally defined? + * A class is local, if + * - it is anonymous, or + * - its owner is a value + * - it is defined within a local class + */ + public final boolean isLocalClass() { + return kind == CLASS && + !isPackage() && + (name == Names.EMPTY.toTypeName() || + owner.isValue() || + owner.isLocalClass()); + } + + /** Is this symbol a constructor? */ + public final boolean isConstructor() { + return name.isConstrName(); + } + + /** Is this symbol the primary constructor of a type? */ + public final boolean isPrimaryConstructor() { + return isConstructor() && this == constructorClass().constructor(); + } + + public boolean isGenerated() { + return name.pos((byte)'$') < name.length(); + } + + /** Symbol was preloaded from package + */ + public boolean isPreloaded() { + return owner.isPackage() && pos == Position.NOPOS; + } + +// Symbol names ---------------------------------------------------------------- + + /** Get the fully qualified name of this Symbol + * (this is always a normal name, never a type name) + */ + public Name fullName() { + return name.toTermName(); + } + + /** Get the mangled name of this Symbol + * (this is always a normal name, never a type name) + */ + public Name mangledName() { + return name.toTermName(); + } + + /** Get the fully qualified mangled name of this Symbol */ + public Name mangledFullName() { + return fullName().replace((byte)'.', (byte)'$'); + } + +// Acess to related symbols ----------------------------------------------------- + + /** Get type parameters */ + public Symbol[] typeParams() { + return EMPTY_ARRAY; + } + + /** Get primary constructor of class */ + public Symbol constructor() { + return NONE; + } + + /** Get module associated with class */ + public Symbol module() { + return NONE; + } + + /** Get owner */ + public Symbol owner() { + return owner; + } + + /** Get owner, but if owner is primary constructor of a class, + * get class symbol instead. This is useful for type parameters + * and value parameters in classes which have the primary constructor + * as owner. + */ + public Symbol classOwner() { + Symbol owner = owner(); + Symbol clazz = owner.constructorClass(); + if (clazz.constructor() == owner) return clazz; + else return owner; + } + + /** The next enclosing class */ + public Symbol enclClass() { + return owner().enclClass(); + } + + /** The top-level class enclosing `sym' + */ + Symbol enclToplevelClass() { + Symbol sym = this; + while (sym.kind == VAL || + (sym.kind == CLASS && !sym.owner().isPackage())) { + sym = sym.owner(); + } + return sym; + } + + /* If this is a constructor, return the class it constructs. + * Otherwise return the symbol itself. + */ + public Symbol constructorClass() { + return this; + } + + /* If this is a module, return its class. + * Otherwise return the symbol itself. + */ + public Symbol moduleClass() { + return this; + } + + /** The symbol accessed by this accessor function. + */ + public Symbol accessed() { + assert (flags & ACCESSOR) != 0; + Name name1 = name; + if (name1.endsWith(Names._EQ)) + name1 = name1.subName(0, name1.length() - Names._EQ.length()); + return owner.info().lookup(Name.fromString(name1 + "$")); + } + + /** The members of this class or module symbol + */ + public Scope members() { + return info().members(); + } + +// Symbol types -------------------------------------------------------------- + + /** Was symbol's type updated during phase `id'? + */ + public boolean isUpdated(int id) { + return infos.limit >= id; + } + + /** the current phase id, or the id after analysis, whichever is larger. + */ + int currentPhaseId() { + int id = Global.instance.currentPhase.id; + if (id > Global.instance.POST_ANALYZER_PHASE_ID) + id = Global.instance.POST_ANALYZER_PHASE_ID; + return id; + } + + /** Is this symbol initialized? */ + public final boolean isInitialized() { + return (flags & INITIALIZED) != 0; + } + + /** Initialize the symbol */ + public final Symbol initialize() { + info(); + return this; + } + + /** Get info; This is: + * for a term symbol, its type + * for a type variable, its bound + * for a type alias, its right-hand side + * for a class symbol, the compound type consisting of + * its baseclasses and members. + */ + public Type info() { + if ((flags & INITIALIZED) == 0) { + int id = currentPhaseId(); + Type info = rawInfoAt(id); + assert info != null : this; + + if ((flags & LOCKED) != 0) { + setInfo(Type.ErrorType); + flags |= INITIALIZED; + throw new CyclicReference(this, info); + } + flags |= LOCKED; + //System.out.println("completing " + this);//DEBUG + info.complete(this); + flags = flags & ~LOCKED; + if (info instanceof SourceCompleter && (flags & SNDTIME) == 0) { + flags |= SNDTIME; + return info(); + } else { + assert !(rawInfoAt(id) instanceof Type.LazyType) : this; + flags |= INITIALIZED; + } + //System.out.println("done: " + this);//DEBUG + } + return rawInfoAt(Global.instance.currentPhase.id); + } + + /** Get info at phase #id + */ + public Type infoAt(int id) { + info(); + return rawInfoAt(id); + } + + /** get info at phase #id, without forcing lazy types. + */ + private Type rawInfoAt(int id) { + int nextid = infos.limit; + assert infos != TypeIntervalList.EMPTY : this; + if (nextid < id) { + do { + Type newInfo = + Global.instance.phases[nextid].transformInfo(this, infos.info); + if (newInfo != infos.info) { + infos = new TypeIntervalList(infos); + infos.info = newInfo; + } + nextid++; + infos.limit = nextid; + } while (nextid < id); + return infos.info; + } else { + TypeIntervalList infos1 = infos; + while (infos1.prev.limit >= id) { + infos1 = infos1.prev; + } + return infos1.info; + } + } + + public Type rawInfo() { + return rawInfoAt(Global.instance.currentPhase.id); + } + + /** The type of a symbol is: + * for a type symbol, the type corresponding to the symbol itself + * for a term symbol, its usual type + */ + public Type type() { + return info(); + } + + /** The type at phase #id + */ + public Type typeAt(int id) { + return infoAt(id); + } + + /** The types of these symbols as an array. + */ + static public Type[] type(Symbol[] syms) { + Type[] tps = new Type[syms.length]; + for (int i = 0; i < syms.length; i++) + tps[i] = syms[i].type(); + return tps; + } + + /** Get this.type corresponding to this symbol + */ + public Type thisType() { + return Type.localThisType; + } + + public Type typeOfThis() { + return Type.localThisType; + } + + /** A total ordering between symbols that refines the class + * inheritance graph (i.e. subclass.isLess(superclass) always holds). + */ + public boolean isLess(Symbol that) { + if (this == that) return false; + int diff; + if (this.isType()) { + if (that.isType()) { + diff = this.closure().length - that.closure().length; + if (diff > 0) return true; + if (diff < 0) return false; + } else { + return true; + } + } else if (that.isType()) { + return false; + } + + diff = that.mangledName().index - this.mangledName().index; + if (diff > 0) return true; + if (diff < 0) return false; + + diff = that.mangledFullName().index - this.mangledFullName().index; + if (diff > 0) return true; + if (diff < 0) return false; + + throw new ApplicationError( + "Giving up: can't order two incarnations of class " + + this.mangledFullName()); + } + + /** Return the symbol's type itself followed by all its direct and indirect + * base types, sorted by isLess(). Overridden for class symbols. + */ + public Type[] closure() { + return info().closure(); + } + + /** Return position of `c' in the closure of this type; -1 if not there. + */ + public int closurePos(Symbol c) { + if (this == c) return 0; + if (c.isCompoundSym()) return -1; + Type[] closure = closure(); + int lo = 0; + int hi = closure.length - 1; + while (lo <= hi) { + int mid = (lo + hi) / 2; + Symbol clsym = closure[mid].symbol(); + if (c == clsym) return mid; + else if (c.isLess(clsym)) hi = mid - 1; + else if (clsym.isLess(c)) lo = mid + 1; + else throw new ApplicationError(); + } + return -1; + } + + Type baseType(Symbol sym) { + int i = closurePos(sym); + if (i >= 0) return closure()[i]; + else return Type.NoType; + } + + /** Is this class a subclass of `c'? I.e. does it have a type instance + * of `c' as indirect base class? + */ + public boolean isSubClass(Symbol c) { + return this == c || c.kind == Kinds.ERROR || closurePos(c) >= 0; + } + +// ToString ------------------------------------------------------------------- + + /** String representation of symbol's simple name. + * Translates expansions of operators back to operator symbol. E.g. + * $eq => =. + */ + public String nameString() { + return NameTransformer.decode(name).toString(); + } + + /** String representation of symbol's full name. + * Translates expansions of operators back to operator symbol. E.g. + * $eq => =. + */ + public String fullNameString() { + return NameTransformer.decode(fullName()).toString(); + } + + public String idString() { + if (Global.instance.uniqid && + (kind == TYPE || Global.instance.debug)) + return "#" + Global.instance.uniqueID.id(this); + else return ""; + } + + /** String representation, including symbol's kind + * e.g., "class Foo", "function Bar". + */ + public String toString() { + if (isRoot()) return "<root package>"; + if (isAnonymousClass()) return "<template>"; + String kstr = kindString(); + String str; + if (kstr.length() == 0) str = fullNameString(); + else str = kstr + " " + fullNameString(); + return str + idString(); + } + + /** String representation of location. + */ + public String locationString() { + if (owner.kind == CLASS && !owner.isAnonymousClass()) + return " in " + owner; + else + return ""; + } + + /** String representation of definition. + */ + public String defString() { + String inner; + if (kind == CLASS) inner = " extends "; + else if (kind == TYPE) inner = " <: "; + else if (kind == ALIAS) inner = " = "; + else inner = " : "; + return + (isParameter() ? "" : defKeyword() + " ") + + nameString() + idString() + inner + + (rawInfoAt(Global.instance.POST_ANALYZER_PHASE_ID) + instanceof Type.LazyType ? "?" : info()); + } + + public static String[] defString(Symbol[] defs) { + String[] strs = new String[defs.length]; + for (int i = 0; i < defs.length; i++) + strs[i] = defs[i].defString(); + return strs; + } + + /** String representation of kind */ + public String kindString() { + switch (kind) { + case CLASS: + if ((flags & TRAIT) != 0) + return "trait"; + else if ((flags & MODUL) != 0 && Global.instance.debug) + return "module class"; + else + return "class"; + case TYPE: + case ALIAS: + return "type"; + case VAL: + if (isVariable()) return "variable"; + else if (isModule()) return "module"; + else if (isConstructor()) return "constructor"; + else if (isInitializedMethod()) return "method"; + else return "value"; + default: return ""; + } + } + + /** Definition keyword of kind + */ + public String defKeyword() { + switch (kind) { + case CLASS: if ((flags & TRAIT) != 0) return "trait"; else return "class"; + case TYPE: + case ALIAS: return "type"; + case VAL: + if (isVariable()) return "var"; + else if (isModule()) return "module"; + else if (isInitializedMethod()) return "def"; + else return "val"; + default: return ""; + } + } + +// Overloading and Overriding ------------------------------------------- + + /** Add another overloaded alternative to this symbol. + */ + public Symbol overloadWith(Symbol that) { + assert isTerm() : this; + assert this.name == that.name : this + " " + that; + assert this.owner == that.owner : this + " " + that; + assert (this.flags & that.flags & JAVA) != 0 || + (this.flags & (SOURCEFLAGS | JAVA) & ~ACCESSFLAGS) == + (that.flags & (SOURCEFLAGS | JAVA) & ~ACCESSFLAGS) : this + " " + that; + TermSymbol overloaded = new TermSymbol( + pos, name, owner, + ((this.flags | that.flags) & (SOURCEFLAGS | JAVA) & ~ACCESSFLAGS) | + (this.flags & that.flags & ACCESSFLAGS)); + overloaded.setInfo(new LazyOverloadedType(this, that)); + return overloaded; + } + + /** A lazy type which, when forced computed the overloaded type + * of symbols `sym1' and `sym2'. It also checks that this type is well-formed. + */ + private static class LazyOverloadedType extends Type.LazyType { + Symbol sym1; + Symbol sym2; + LazyOverloadedType(Symbol sym1, Symbol sym2) { + this.sym1 = sym1; + this.sym2 = sym2; + } + private Symbol[] alts(Symbol sym) { + if (sym == null) return Symbol.EMPTY_ARRAY; + switch (sym.type()) { + case OverloadedType(Symbol[] alts, _): return alts; + default: return new Symbol[]{sym}; + } + } + private Type[] alttypes(Symbol sym) { + if (sym == null) return Type.EMPTY_ARRAY; + switch (sym.type()) { + case OverloadedType(_, Type[] alttypes): return alttypes; + default: return new Type[]{sym.type()}; + } + } + public void complete(Symbol overloaded) { + if (sym1 != null) sym1.initialize(); + if (sym2 != null) sym2.initialize(); + + Symbol[] alts1 = alts(sym1); + Symbol[] alts2 = alts(sym2); + Symbol[] alts3 = new Symbol[alts1.length + alts2.length]; + System.arraycopy(alts1, 0, alts3, 0, alts1.length); + System.arraycopy(alts2, 0, alts3, alts1.length, alts2.length); + + Type[] alttypes1 = alttypes(sym1); + Type[] alttypes2 = alttypes(sym2); + Type[] alttypes3 = new Type[alttypes1.length + alttypes2.length]; + System.arraycopy(alttypes1, 0, alttypes3, 0, alttypes1.length); + System.arraycopy(alttypes2, 0, alttypes3, alttypes1.length, alttypes2.length); + overloaded.setInfo(Type.OverloadedType(alts3, alttypes3)); + } + } + + /** All the alternatives of this symbol if it's overloaded, the + * symbol alone otherwise. + */ + public Symbol[] alternatives() { + switch (type()) { + case OverloadedType(Symbol[] alts, _): return alts; + default: return new Symbol[]{this}; + } + } + + /** The symbol which is overridden by this symbol in base class `base' + * `base' must be a superclass of this.owner(). + */ + public Symbol overriddenSymbol(Type base) { + Symbol sym1 = base.lookupNonPrivate(name); + if (sym1.kind == Kinds.NONE || (sym1.flags & STATIC) != 0) { + return Symbol.NONE; + } else { + //System.out.println(this + ":" + this.type() + locationString() + " overrides? " + sym1 + sym1.type() + sym1.locationString()); //DEBUG + + Type symtype = owner.thisType().memberType(this); + //todo: try whether we can do: this.type(); instead + Type sym1type = owner.thisType().memberType(sym1); + switch (sym1type) { + case OverloadedType(Symbol[] alts, Type[] alttypes): + for (int i = 0; i < alts.length; i++) { + if (symtype.isSameAs(alttypes[i])) return alts[i]; + } + return Symbol.NONE; + default: + if (symtype.isSameAs(sym1type)) return sym1; + else { + System.out.println(this + locationString() + " does not override " + sym1 + sym1.locationString() + ", since " + symtype + " # " + sym1type);//DEBUG + return Symbol.NONE; + } + } + } + } +} + +/** A class for term symbols + */ +public class TermSymbol extends Symbol { + + private Symbol clazz; + + /** Constructor */ + public TermSymbol(int pos, Name name, Symbol owner, int flags) { + super(VAL, pos, name, owner, flags); + } + + public static TermSymbol newConstructor(Symbol clazz, int flags) { + TermSymbol sym = new TermSymbol( + clazz.pos, clazz.name.toConstrName(), clazz.owner(), + flags | FINAL); + sym.clazz = clazz; + return sym; + } + + public static TermSymbol newJavaConstructor(Symbol clazz) { + return newConstructor(clazz, clazz.flags & (ACCESSFLAGS | QUALIFIED | JAVA)); + } + + public static TermSymbol newModule(int pos, Name name, Symbol owner, int flags) { + TermSymbol sym = new TermSymbol(pos, name, owner, flags | MODUL | FINAL); + Symbol clazz = new ClassSymbol( + pos, name.toTypeName(), owner, flags | MODUL | FINAL, sym); + Type clazztype = Type.TypeRef(owner.thisType(), clazz, Type.EMPTY_ARRAY); + clazz.constructor().setInfo(Type.MethodType(Symbol.EMPTY_ARRAY, clazztype)); + sym.clazz = clazz; + sym.setInfo(clazztype); + return sym; + } + + /** Constructor for companion modules to classes, which need to be completed. + */ + public static TermSymbol newCompanionModule(Symbol clazz, int flags, Type.LazyType parser) { + TermSymbol sym = newModule(Position.NOPOS, clazz.name.toTermName(), clazz.owner(), + FINAL | flags); + sym.clazz.setInfo(parser); + return sym; + } + + /** Java package module constructor + */ + public static TermSymbol newJavaPackageModule(Name name, Symbol owner, Type.LazyType parser) { + TermSymbol sym = newModule(Position.NOPOS, name, owner, JAVA | PACKAGE); + sym.clazz.flags |= SYNTHETIC; + sym.clazz.setInfo(parser); + return sym; + } + + /** Get this.type corresponding to this class or module + */ + public Type thisType() { + if ((flags & MODUL) != 0) return moduleClass().thisType(); + else return Type.localThisType; + } + /** Get the fully qualified name of this Symbol */ + public Name fullName() { + if ((flags & MODUL) != 0) return moduleClass().fullName(); + else return super.fullName(); + } + + /** Return a fresh symbol with the same fields as this one. + */ + public Symbol cloneSymbol() { + assert !isPrimaryConstructor() : Debug.show(this); + TermSymbol other; + if (isModule()) { + other = newModule(pos, name, owner(), flags); + } else { + other = new TermSymbol(pos, name, owner(), flags); + other.clazz = clazz; + } + other.setInfo(info()); + return other; + } + + public Symbol constructorClass() { + return isConstructor() ? clazz : this; + } + + public Symbol moduleClass() { + return (flags & MODUL) != 0 ? clazz : this; + } +} + +/** A class for (abstract and alias) type symbols. It has ClassSymbol as a subclass. + */ +public class TypeSymbol extends Symbol { + + /** A cache for closures + */ + private ClosureIntervalList closures = ClosureIntervalList.EMPTY; + + /** The symbol's type template */ + private Type template; + + /** The primary constructor of this type */ + public final Symbol constructor; + + /** Constructor */ + public TypeSymbol(int kind, int pos, Name name, Symbol owner, int flags) { + super(kind, pos, name, owner, flags); + this.constructor = TermSymbol.newConstructor(this, flags); + if (kind == TYPE) { // provide a constructor type + this.constructor().setInfo( + Type.PolyType(Symbol.EMPTY_ARRAY, Type.NoType)); + } + } + + /** Return a fresh symbol with the same fields as this one. + */ + public Symbol cloneSymbol() { + if (Global.instance.debug) System.out.println("cloning " + this + this.locationString()); + TypeSymbol other = new TypeSymbol(kind, pos, name, owner(), flags); + other.setInfo(info()); + other.constructor.setInfo(constructor.info()); + return other; + } + + /** Get self type */ + public Type type() { + if (template == null || template.typeArgs().length != typeParams().length) { + template = Type.TypeRef( + owner().thisType(), this, type(typeParams())); + } + return template; + } + + //todo: needed? + public Type typeAt(int id) { + return type(); + } + + /** Get type parameters */ + public Symbol[] typeParams() { + return constructor.info().typeParams(); + } + + /** Get primary constructor */ + public Symbol constructor() { + return constructor; + } + + public Type[] closure() { + if (kind == ALIAS) return info().symbol().closure(); + int id = Global.instance.currentPhase.id; + if (closures.limit < id) { + if (id == 0 || changes(closureAt(id - 1))) { + closures = new ClosureIntervalList(closures); + closures.limit = id; + computeClosure(); + } else { + closures.limit = id; + } + return closures.closure; + } else { + ClosureIntervalList closures1 = closures; + while (closures1.prev.limit >= id) { + closures1 = closures1.prev; + } + return closures1.closure; + } + } + + //todo: needed? + private Type[] closureAt(int id) { + PhaseDescriptor savedPhase = Global.instance.currentPhase; + Global.instance.currentPhase = Global.instance.phases[id]; + Type[] c = closure(); + Global.instance.currentPhase = savedPhase; + return c; + } + + private boolean changes(Type[] closure) { + for (int i = 0; i < closure.length; i++) { + Symbol c = closure[i].symbol(); + if (c.infoAt(Global.instance.currentPhase.id - 1) != c.info()) + return true; + } + return false; + } + + private static Type[] BAD_CLOSURE = new Type[0]; + + /** Return the type itself followed by all direct and indirect + * base types of this type, sorted by isLess(). + */ + private void computeClosure() { + assert closures.closure != BAD_CLOSURE : this; + closures.closure = BAD_CLOSURE; // to catch cycles. + SymSet closureClassSet = inclClosureBases(SymSet.EMPTY, this); + Symbol[] closureClasses = new Symbol[closureClassSet.size() + 1]; + closureClasses[0] = this; + closureClassSet.copyToArray(closureClasses, 1); + //System.out.println(ArrayApply.toString(closureClasses));//DEBUG + closures.closure = Symbol.type(closureClasses); + //System.out.println(ArrayApply.toString(closures.closure));//DEBUG + adjustType(type()); + //System.out.println("closure(" + this + ") at " + Global.instance.currentPhase.name() + " = " + ArrayApply.toString(closures.closure));//DEBUG + } + //where + + private SymSet inclClosureBases(SymSet set, Symbol c) { + Type[] parents = c.type().parents(); + for (int i = 0; i < parents.length; i++) { + set = inclClosure(set, parents[i].symbol()); + } + return set; + } + + private SymSet inclClosure(SymSet set, Symbol c) { + Symbol c1 = c; + while (c1.kind == ALIAS) c1 = c1.info().symbol(); + return inclClosureBases(set.incl(c1), c1); + } + + void adjustType(Type tp) { + Type tp1 = tp.unalias(); + int pos = closurePos(tp1.symbol()); + assert pos >= 0 : this + " " + tp1 + " " + tp1.symbol(); + closures.closure[pos] = tp1; + Type[] parents = tp1.parents(); + for (int i = 0; i < parents.length; i++) { + adjustType(parents[i]); + } + } +} + +/** A class for class symbols. It has JavaClassSymbol as a subclass. + */ +public class ClassSymbol extends TypeSymbol { + + /** The mangled class name */ + private Name mangled; + + /** The module belonging to the class. This means: + * For Java classes, its statics parts. + * For module classes, the corresponding module. + * For other classes, null. + */ + private Symbol module = NONE; + + /** Principal Constructor for module classes and classes with static members. + */ + public ClassSymbol(int pos, Name name, Symbol owner, int flags) { + super(CLASS, pos, name, owner, flags); + this.mangled = name; + } + + /** Constructor for module classes and classes with static members. + */ + public ClassSymbol(int pos, Name name, Symbol owner, int flags, Symbol module) { + this(pos, name, owner, flags); + this.module = module; + } + + /** Constructor for classes to load as source files + */ + public ClassSymbol(Name name, Symbol owner, SourceCompleter parser) { + this(Position.NOPOS, name, owner, 0); + this.module = TermSymbol.newCompanionModule(this, 0, parser); + this.mangled = name; + this.setInfo(parser); + } + + /** Constructor for classes to load as class files. + */ + public ClassSymbol(Name name, Symbol owner, ClassParser parser) { + super(CLASS, Position.NOPOS, name, owner, JAVA); + this.module = TermSymbol.newCompanionModule(this, JAVA, parser.staticsParser(this)); + this.mangled = name; + this.setInfo(parser); + } + + /** Return a fresh symbol with the same fields as this one. + */ + public Symbol cloneSymbol() { + ClassSymbol other = new ClassSymbol(pos, name, owner(), flags); + other.setInfo(info()); + other.constructor.setInfo(constructor.info()); + other.mangled = mangled; + other.module = module; + return other; + } + + /** Get module */ + public Symbol module() { + return module; + } + + /** Set the mangled name of this Symbol */ + public Symbol setMangledName(Name name) { + this.mangled = name; + return this; + } + + /** Get the fully qualified name of this Symbol */ + public Name fullName() { + if (owner().kind == CLASS && owner().name.length() != 0) + return Name.fromString(owner().fullName() + "." + name); + else + return name.toTermName(); + } + + /** Get the mangled name of this Symbol */ + public Name mangledName() { + return mangled; + } + + /** Get the fully qualified mangled name of this Symbol */ + public Name mangledFullName() { + if (mangled == name) { + return fullName().replace((byte)'.', (byte)'$'); + } else { + return Name.fromString( + enclToplevelClass().mangledFullName() + "$" + mangled); + } + } + + private Type thistp = Type.ThisType(this); + + public Type thisType() { + return thistp; + } + + /** Return the next enclosing class */ + public Symbol enclClass() { + return this; + } + + public Symbol caseFieldAccessor(int index) { + assert (flags & CASE) != 0 : this; + Scope.SymbolIterator it = info().members().iterator(); + Symbol sym = null; + for (int i = 0; i <= index; i++) { + do { + sym = it.next(); + } while (sym.kind != VAL || (sym.flags & CASE) == 0); + } + //System.out.println(this + ", case field[" + index + "] = " + sym);//DEBUG + assert sym != null : this; + return sym; + } +} + +/** A class for error symbols. + */ +public final class ErrorSymbol extends Symbol { + + /** Constructor */ + public ErrorSymbol() { + super(Kinds.ERROR, Position.NOPOS, Name.fromString("<error>"), null, + INITIALIZED); + this.setOwner(this); + this.setInfo(Type.ErrorType); + } + + public Symbol cloneSymbol() { + return this; + } + + /** Set the mangled name of this Symbol */ + public Symbol mangled(Name name) { + return this; + } + + /** Set owner */ + public Symbol setOwner(Symbol owner) { + if (owner != this) + throw new ApplicationError("illegal operation on " + getClass()); + return super.setOwner(owner); + } + + /** Set type */ + public Symbol setInfo(Type info) { + if (info != Type.ErrorType) + throw new ApplicationError("illegal operation on " + getClass()); + return super.setInfo(info); + } + + /** Get primary constructor */ + public Symbol constructor() { + return TermSymbol.newConstructor(this, 0).setInfo(Type.ErrorType); + } + + /** Return the next enclosing class */ + public Symbol enclClass() { + return this; + } +} + +/** The class of Symbol.NONE + */ +public final class NoSymbol extends Symbol { + + /** Constructor */ + public NoSymbol() { + super(Kinds.NONE, Position.NOPOS, Name.fromString("<none>"), null, INITIALIZED); + this.setInfo(Type.NoType); + this.setOwner(this); + } + + /** Return a fresh symbol with the same fields as this one. + */ + public Symbol cloneSymbol() { + return this; + } + + /** Set the mangled name of this Symbol */ + public Symbol mangled(Name name) { + throw new ApplicationError("illegal operation on " + getClass()); + } + + /** Set owner */ + public Symbol setOwner(Symbol owner) { + if (owner != this) + throw new ApplicationError("illegal operation on " + getClass()); + return super.setOwner(owner); + } + + /** Set type */ + public Symbol setInfo(Type info) { + if (info != Type.NoType) + throw new ApplicationError("illegal operation on " + getClass()); + return super.setInfo(info); + } + + /** Return the next enclosing class */ + public Symbol enclClass() { + return this; + } + + public Symbol owner() { + throw new ApplicationError(); + } +} + +/** A class for symbols generated in label definitions. + */ +public class LabelSymbol extends TermSymbol { + + /** give as argument the symbol of the function that triggered + the creation of this label */ + public LabelSymbol(Symbol f) { + super(f.pos, f.name, f, LABEL); + } +} + +/** An exception for signalling cyclic references. + */ +public class CyclicReference extends Type.Error { + public Symbol sym; + public Type info; + public CyclicReference(Symbol sym, Type info) { + super("illegal cyclic reference involving " + sym); + this.sym = sym; + this.info = info; + } +} + +/** A class for types indexed by phase numbers. + */ +class TypeIntervalList { + int limit; + Type info; + TypeIntervalList prev; + TypeIntervalList(TypeIntervalList prev) { + this.prev = prev; + } + static TypeIntervalList EMPTY = new TypeIntervalList(null); + + static { + EMPTY.limit = -1; + } +} + +/** A class for closures indexed by phase numbers. + */ +class ClosureIntervalList { + int limit; + Type[] closure; + ClosureIntervalList prev; + ClosureIntervalList(ClosureIntervalList prev) { + this.prev = prev; + } + static ClosureIntervalList EMPTY = new ClosureIntervalList(null); + static { + EMPTY.limit = -1; + } +} + diff --git a/sources/scalac/symtab/Type.java b/sources/scalac/symtab/Type.java new file mode 100644 index 0000000000..3ec37799f4 --- /dev/null +++ b/sources/scalac/symtab/Type.java @@ -0,0 +1,2353 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** +** $Id$ +\* */ +//todo: implement rule single + +package scalac.symtab; + +import scalac.ApplicationError; +import scalac.util.*; +import scalac.Global; + +public class Type implements Modifiers, Kinds, TypeTags { + + public static boolean debugSwitch = false; + private static int indent = 0; + + //todo: convert C with {} to C. + + public case ErrorType; // not used after analysis + public case AnyType; // not used after analysis + public case NoType; + + public case ThisType(Symbol sym); + + public case TypeRef(Type pre, Symbol sym, Type[] args); + + public case SingleType(Type pre, Symbol sym) { + assert this instanceof ExtSingleType; + } + + public case CompoundType(Type[] parts, Scope members) { + assert this instanceof ExtCompoundType; + } + public case MethodType(Symbol[] vparams, Type result); + public case PolyType(Symbol[] tparams, Type result); + public case OverloadedType(Symbol[] alts, Type[] alttypes); + + /** Type such as +T. Only used as type arguments. + */ + public case CovarType(Type tp) { + assert !(tp instanceof CovarType); + } + + /** Hidden case to implement delayed evaluation of types. + * No need to pattern match on this type; it will never come up. + */ + public case LazyType(); + + /** Hidden case to implement local type inference. + * Later phases do not need to match on this type. + */ + public case TypeVar(Type origin, Constraint constr); + + /** Hidden cases to implement type erasure. + * Earlier phases do not need to match on these types. + */ + public case UnboxedType(int tag); + public case UnboxedArrayType(Type elemtp); + + /** Force evaluation of a lazy type. No cycle + * check is needed; since this is done in Symbol. + * @see Symbol.info(). + */ + public void complete(Symbol p) {} + +// Creators --------------------------------------------------------------------- + + /** An owner-less ThisType + */ + public static Type localThisType = ThisType(Symbol.NONE); + + /** An empty Type array */ + public static final Type[] EMPTY_ARRAY = new Type[0]; + + /** A non-existing Type array; used to express type erasure */ + public static final Type[] NO_ARRAY = new Type[0]; + + public static SingleType singleType(Type pre, Symbol sym) { + return new ExtSingleType(pre, sym); + } + + public static TypeRef appliedType(Type tycon, Type[] args) { + switch (tycon) { + case TypeRef(Type pre, Symbol sym, Type[] args1): + if (args == args1) return (TypeRef)tycon; + else return TypeRef(pre, sym, args); + default: + throw new ApplicationError(); + } + } + + public static CovarType covarType(Type tp) { + if (tp instanceof CovarType) return (CovarType)tp; + else return CovarType(tp); + } + + public static CompoundType compoundType(Type[] parts, Scope members, + Symbol clazz) { + ExtCompoundType res = new ExtCompoundType(parts, members); + res.tsym = clazz; + return res; + } + + public static CompoundType compoundType(Type[] parts, Scope members) { + ExtCompoundType res = new ExtCompoundType(parts, members); + res.tsym = new ClassSymbol( + Position.NOPOS, Names.COMPOUND_NAME.toTypeName(), Symbol.NONE, + SYNTHETIC | ABSTRACTCLASS); + res.tsym.setInfo(res); + res.tsym.constructor().setInfo( + Type.PolyType(Symbol.EMPTY_ARRAY, Type.NoType)); + return res; + } + + static class ExtSingleType extends SingleType { + Type tp = null; + int definedId = -1; + ExtSingleType(Type pre, Symbol sym) { + super(pre, sym); + } + public Type widen() { + if (definedId != Global.instance.currentPhase.id) { + definedId = Global.instance.currentPhase.id; + tp = pre.memberType(sym).widen(); + } + return tp; + } + } + + static class ExtCompoundType extends CompoundType { + Symbol tsym; + ExtCompoundType(Type[] parts, Scope members) { + super(parts, members); + } + public Symbol symbol() { + return tsym; + } + void validate() {//debug + for (Scope.Entry e = members.elems; e != Scope.Entry.NONE; e = e.next) + assert e.sym.owner() == tsym; + } + } + + void validate() {//debug + } + +// Access methods --------------------------------------------------------------- + + /** If this is a thistype, named type, applied type, singleton type, or compound type, + * its symbol, otherwise Symbol.NONE. + */ + public Symbol symbol() { + switch (this) { + case ErrorType: + return Symbol.ERROR; + case ThisType(Symbol sym): + return sym; + case TypeRef(_, Symbol sym, _): + return sym; + case SingleType(_, Symbol sym): + return sym; + case TypeVar(Type origin, _): + return origin.symbol(); + case CompoundType(_, _): + // overridden in ExtCompoundType + throw new ApplicationError(); + default: + return Symbol.NONE; + } + } + + public static Symbol[] symbol(Type[] tps) { + Symbol[] syms = new Symbol[tps.length]; + for (int i = 0; i < syms.length; i++) + syms[i] = tps[i].symbol(); + return syms; + } + + /** If this type is a thistype or singleton type, its underlying object type, + * otherwise the type itself. + */ + public Type widen() { + switch (this) { + case ThisType(Symbol sym): + return sym.type(); + case SingleType(Type pre, Symbol sym): + // overridden in ExtSingleType + throw new ApplicationError(); + case TypeVar(Type origin, Constraint constr): + if (constr.inst != NoType) return constr.inst.widen(); + else return this; + default: + return this; + } + } + + public static Type[] widen(Type[] tps) { + if (tps.length == 0) return Type.EMPTY_ARRAY; + Type[] tps1 = new Type[tps.length]; + for (int i = 0; i < tps1.length; i++) { + tps1[i] = tps[i].widen(); + assert !(tps1[i] instanceof SingleType) : tps[i];//debug + } + return tps1; + } + + /** The thistype or singleton type corresponding to values of this type. + */ + public Type narrow() { + switch (this) { + case TypeRef(Type pre, Symbol sym, Type[] args): + if (sym.kind == ALIAS) return pre.memberInfo(sym).narrow(); + else if (sym.kind == CLASS) return sym.thisType(); + else return ThisType(sym); + case CompoundType(_, _): + return symbol().thisType(); + default: + return this; + } + } + + /** The this is a this-type, named-type, applied type or single-type, its prefix, + * otherwise NoType. + */ + public Type prefix() { + switch (this) { + case ThisType(Symbol sym): return sym.owner().thisType(); + case TypeRef(Type pre, _, _): return pre; + case SingleType(Type pre, _): return pre; + case TypeVar(Type origin, Constraint constr): + if (constr.inst != NoType) return constr.inst.prefix(); + else return NoType; + default: return NoType; + } + } + + /** Get all type arguments of this type. + */ + public Type[] typeArgs() { + switch (unalias()) { + case TypeRef(_, _, Type[] args): + return args; + default: + return Type.EMPTY_ARRAY; + } + } + + /** Remove all aliases + */ + public Type unalias() { + Type result = unalias(0);//debug + //if (this != result) System.out.println(this + " ==> " + result);//DEBUG + return result; + } + + private Type unalias(int n) { + if (n == 20) throw new Type.Error("recursive type alias: " + this); + switch (this) { + case TypeRef(Type pre, Symbol sym, Type[] args): + if (sym.kind == ALIAS) { + return pre.memberInfo(sym).subst(sym.typeParams(), args).unalias(n + 1); + } + break; + case TypeVar(Type origin, Constraint constr): + if (constr.inst != NoType) return constr.inst.unalias(n + 1); + else return this; + } + return this; + } + + /** The (prefix-adapted) parents of this type. + */ + public Type[] parents() { + switch (unalias()) { + case ThisType(_): + case SingleType(_, _): + return widen().parents(); + case TypeRef(Type pre, Symbol sym, Type[] args): + if (sym.kind == ALIAS) + return unalias().parents(); + else if (sym.kind == CLASS) { + assert sym.typeParams().length == args.length : sym + " " + ArrayApply.toString(args);//debug + return subst(asSeenFrom(sym.info().parents(), pre, sym.owner()), + sym.typeParams(), args); + } else + return new Type[]{sym.info().asSeenFrom(pre, sym.owner())}; + case CompoundType(Type[] parts, _): + return parts; + default: + return Type.EMPTY_ARRAY; + } + } + + /** Get type parameters of polymorphic method + * or EMPTY_ARRAY if not applicable. + */ + public Symbol[] typeParams() { + switch (this) { + case PolyType(Symbol[] tparams, _): + return tparams; + case TypeRef(_, Symbol sym, _): + if (sym.kind == CLASS || sym.kind == ALIAS) return sym.typeParams(); + break; + } + return Symbol.EMPTY_ARRAY; + } + + /** If this type is a (possibly polymorphic) method type, its result type + * after applying all method argument sections, + * otherwise the type itself. + */ + public Type resultType() { + switch (this) { + case PolyType(_, Type tpe): + return tpe.resultType(); + case MethodType(_, Type tpe): + return tpe.resultType(); + default: + return this; + } + } + + /** The number of value parameter sections of this type. + */ + public int paramSectionCount() { + switch (this) { + case PolyType(_, Type restpe): + return restpe.paramSectionCount(); + case MethodType(_, Type restpe): + return restpe.paramSectionCount() + 1; + default: return 0; + } + } + + /** The arity of the first parameter section of this type. + */ + public Symbol[] firstParams() { + switch (this) { + case PolyType(_, Type restpe): + return restpe.firstParams(); + case MethodType(Symbol[] params, _): + return params; + default: return Symbol.EMPTY_ARRAY; + } + } + + /** If this type is overloaded, its alternative types, + * otherwise an array consisting of this type itself. + */ + public Type[] alternatives() { + switch (this) { + case OverloadedType(_, Type[] alttypes): + return alttypes; + default: + return new Type[]{this}; + } + } + + /** If type is a this type of a module class, transform to singletype of + * module. + */ + public Type expandModuleThis() { + switch (this) { + case ThisType(Symbol sym): + if ((sym.flags & MODUL) != 0 && sym.module() != Symbol.NONE) { + return singleType( + sym.owner().thisType().expandModuleThis(), sym.module()); + } + } + return this; + } + + /** If this is a covariant type, its underlying type, otherwise the type itself. + */ + public Type dropVariance() { + switch (this) { + case CovarType(Type tp): return tp; + default: return this; + } + } + + public static Map dropVarianceMap = new Map() { + public Type apply(Type t) { return t.dropVariance(); } + }; + +// Tests -------------------------------------------------------------------- + + /** Is this type a this type or singleton type? + */ + public boolean isStable() { + switch (unalias()) { + case ThisType(_): + case SingleType(_, _): + case ErrorType: + return true; + default: + return false; + } + } + + /** Is this type a reference to an object type? + */ + public boolean isObjectType() { + switch (unalias()) { + case ThisType(_): + case SingleType(_, _): + case CompoundType(_, _): + return true; + case TypeRef(Type pre, Symbol sym, _): + return sym.kind != ALIAS || unalias().isObjectType(); + default: + return false; + } + } + + /** Is this type a covariant type? + */ + public boolean isCovarType() { + switch (this) { + case CovarType(_): + return true; + default: + return false; + } + } + + /** Is this type of the form scala.Tuple_N[+T_0, ..., +T_N-1]? + */ + public boolean isTupleType() { + switch (this) { + case TypeRef(Type pre, Symbol sym, Type[] args): + if (sym.owner() == Global.instance.definitions.SCALA_CLASS && + sym.name.startsWith(Names.Tuple)) { + for (int i = 0; i < args.length; i++) + if (!args[i].isCovarType()) return false; + return true; + } + } + return false; + } + + /** Is this type of the form scala.FunctionN[T_1, ..., T_n, +T]? + */ + public boolean isFunctionType() { + switch (this) { + case TypeRef(Type pre, Symbol sym, Type[] args): + if (sym.fullName().startsWith(Names.Function)) + for (int i = 0; i < args.length - 1; i++) + if (args[i].isCovarType()) return false; + return args.length > 0 && args[args.length - 1].isCovarType(); + default: + return false; + } + } + +// Members and Lookup ------------------------------------------------------- + + /** Get the scope containing the local members of this type. + * Symbols in this scope are not prefix-adapted! + */ + public Scope members() { + switch (this) { + case ErrorType: + return new Scope(); + case TypeRef(_, Symbol sym, _): + return sym.info().members(); + case SingleType(_, Symbol sym): + return widen().members(); + case CompoundType(Type[] basetypes, Scope members): + return members; + default: + return Scope.EMPTY; + } + } + + /** Lookup symbol with given name among all local and inherited members + * of this type; return Symbol.NONE if not found. + */ + public Symbol lookup(Name name) { + switch (this) { + case ErrorType: + return Symbol.ERROR; + case ThisType(_): + case SingleType(_, _): + return widen().lookup(name); + case TypeRef(_, Symbol sym, _): + return sym.info().lookup(name); + case CompoundType(Type[] parts, Scope members): + Symbol sym = members.lookup(name); + if (sym.kind != NONE) return sym; + else return lookupNonPrivate(name); + default: + return Symbol.NONE; + } + } + + /** Lookup non-private symbol with given name among all local and + * inherited members of this type; return Symbol.NONE if not found. + */ + public Symbol lookupNonPrivate(Name name) { + return lookupNonPrivate(name, 0); + } + + /** Same as before, but with additional parameter `start'. + * If start == 0, lookup in all basetypes of a compound type. + * If start == 1, lookup only in mixin classes. + */ + private Symbol lookupNonPrivate(Name name, int start) { + switch (this) { + case ErrorType: + return Symbol.ERROR; + case ThisType(_): + case SingleType(_, _): + return widen().lookupNonPrivate(name); + case TypeRef(_, Symbol sym, _): + return sym.info().lookupNonPrivate(name, start); + case CompoundType(Type[] parts, Scope members): + Symbol sym = members.lookup(name); + if (sym.kind != NONE && (sym.flags & PRIVATE) == 0) + return sym; + + // search base types in reverse; non-abstract members + // take precedence over abstract ones. + int i = parts.length; + sym = Symbol.NONE; + while (i > start && (sym.kind == NONE || (sym.flags & ABSTRACT) != 0)) { + i--; + Symbol sym1 = parts[i].lookupNonPrivate(name, i == 0 ? 0 : 1); + if (sym1.kind != NONE && + (sym1.flags & PRIVATE) == 0 && + (sym.kind == NONE || (sym1.flags & ABSTRACT) == 0)) + sym = sym1; + } + return sym; + default: + return Symbol.NONE; + } + } + +// Maps -------------------------------------------------------------------------- + + /** The type of type-to-type functions. + */ + public abstract static class Map { + + boolean covariantOK = true; + + public abstract Type apply(Type t); + + /** Apply map to all top-level components of this type. + */ + public Type map(Type tp) { + switch (tp) { + case ErrorType: + case AnyType: + case NoType: + case UnboxedType(_): + case TypeVar(_, _): + case ThisType(_): + return tp; + case TypeRef(Type pre, Symbol sym, Type[] args): + Type pre1 = map(pre); + Type[] args1 = map(args); + if (pre1 == pre && args1 == args) return tp; + else return TypeRef(pre1, sym, args1); + case SingleType(Type pre, Symbol sym): + Type pre1 = map(pre); + if (pre1 == pre) return tp; + else return singleType(pre1, sym); + case CompoundType(Type[] parts, Scope members): + Type[] parts1 = map(parts); + Scope members1 = map(members); + if (parts1 == parts && members1 == members) { + return tp; + } else { + Scope members2 = new Scope(); + Type tp1 = (tp.symbol().isCompoundSym()) ? compoundType(parts1, members2) + : compoundType(parts1, members2, tp.symbol()); + Symbol[] syms1 = members1.elements(); + Symbol[] syms2 = new Symbol[syms1.length]; + for (int i = 0; i < syms2.length; i++) { + syms2[i] = syms1[i].cloneSymbol().setOwner(tp1.symbol()); + } + for (int i = 0; i < syms2.length; i++) { + syms2[i].setInfo(syms1[i].info().subst(syms1, syms2)); + } + for (int i = 0; i < syms2.length; i++) { + members2.enter(syms2[i]); + } + return tp1; + } + + case MethodType(Symbol[] vparams, Type result): + covariantOK = false; + Symbol[] vparams1 = map(vparams); + covariantOK = true; + Type result1 = apply(result); + if (vparams1 == vparams && result1 == result) return tp; + else return MethodType(vparams1, result1); + case PolyType(Symbol[] tparams, Type result): + covariantOK = false; + Symbol[] tparams1 = map(tparams); + covariantOK = true; + Type result1 = apply(result); + if (tparams1 != tparams) result1 = result1.subst(tparams, tparams1); + if (tparams1 == tparams && result1 == result) return tp; + else return PolyType(tparams1, result1); + case OverloadedType(Symbol[] alts, Type[] alttypes): + Type[] alttypes1 = map(alttypes); + if (alttypes1 == alttypes) return tp; + else return OverloadedType(alts, alttypes1); + case CovarType(Type t): + Type t1 = apply(t); + if (t1 == t) return tp; + else return covarType(t1); + case UnboxedArrayType(Type elemtp): + Type elemtp1 = apply(elemtp); + if (elemtp1 == elemtp) return tp; + else return UnboxedArrayType(elemtp1); + default: + throw new ApplicationError(tp); + } + } + + public Symbol map(Symbol sym) { + Type tp = sym.info(); + Type tp1 = apply(tp); + if (tp == tp1) return sym; + else return sym.cloneSymbol().setInfo(tp1); + } + + public Type[] map(Type[] tps) { + Type[] tps1 = tps; + for (int i = 0; i < tps.length; i++) { + Type tp = tps[i]; + Type tp1 = apply(tp); + if (tp1 != tp && tps1 == tps) { + tps1 = new Type[tps.length]; + System.arraycopy(tps, 0, tps1, 0, i); + } + tps1[i] = tp1; + } + return tps1; + } + + /** Apply map to all elements of this array of symbols, + * preserving recursive references to symbols in the array. + */ + public Symbol[] map(Symbol[] syms) { + Symbol[] syms1 = syms; + for (int i = 0; i < syms.length; i++) { + Symbol sym = syms[i]; + Symbol sym1 = map(sym); + if (sym != sym1 && syms1 == syms) { + syms1 = new Symbol[syms.length]; + System.arraycopy(syms, 0, syms1, 0, i); + } + syms1[i] = sym1; + } + if (syms1 != syms) { + for (int i = 0; i < syms1.length; i++) { + if (syms1[i] == syms[i]) + syms1[i] = syms[i].cloneSymbol(); + } + for (int i = 0; i < syms1.length; i++) { + syms1[i].setInfo(syms1[i].info().subst(syms, syms1)); + } + } + return syms1; + } + + /** Apply map to all elements of this array of this scope. + */ + public Scope map(Scope s) { + Symbol[] members = s.elements(); + Symbol[] members1 = map(members); + if (members == members1) return s; + else return new Scope(members1); + } + } + +// baseType and asSeenFrom -------------------------------------------------------- + + /** Return the base type of this type whose symbol is `clazz', or NoType, if + * such a type does not exist. + */ + public Type baseType(Symbol clazz) { + //System.out.println(this + ".baseType(" + clazz + ")");//DEBUG + switch (this) { + case ErrorType: + return ErrorType; + + case ThisType(_): + case SingleType(_, _): + return widen().baseType(clazz); + + case TypeRef(Type pre, Symbol sym, Type[] args): + if (sym == clazz) + return this; + else if (sym.kind == TYPE) + return pre.memberInfo(sym).baseType(clazz); + else if (sym.kind == ALIAS) + return pre.memberInfo(sym).baseType(clazz) + .subst(sym.typeParams(), args); + else if (clazz.isCompoundSym()) + return NoType; + else + return sym.baseType(clazz) + .asSeenFrom(pre, clazz.owner()) + .subst(sym.typeParams(), args); + + case CompoundType(Type[] parts, _): + for (int i = parts.length - 1; i >= 0; i--) { + Type result = parts[i].baseType(clazz); + if (result != NoType) return result; + } + break; + + case UnboxedArrayType(_): + if (clazz == Global.instance.definitions.ANY_CLASS) + return clazz.type(); + } + return NoType; + } + + /** Return overriding instance of `sym' in this type, + * or `sym' itself if none exists. + */ + public Symbol rebind(Symbol sym) { + Symbol sym1 = lookupNonPrivate(sym.name); + if (sym1.kind != NONE) { + //System.out.println("rebinding " + sym + " to " + sym1);//DEBUG + return sym1; + } + else return sym; + } + + /** A map to implement `asSeenFrom'. + */ + static class AsSeenFromMap extends Map { + + private Type pre; + private Symbol clazz; + private Type illegalType = NoType; + private boolean typeArg = false; + + AsSeenFromMap(Type pre, Symbol clazz) { + this.pre = pre; this.clazz = clazz; + } + + public Type apply(Type t) { + switch (t) { + case ThisType(Symbol sym): + return t.toPrefix(pre, clazz); + + case TypeRef(Type prefix, Symbol sym, Type[] args): + if (sym.kind == ALIAS) { + return apply(t.unalias()); + } else if (sym.owner().isPrimaryConstructor()) { + assert sym.kind == TYPE; + Type t1 = t.toInstance(pre, clazz); + //System.out.println(t + ".toInstance(" + pre + "," + clazz + ") = " + t1);//DEBUG + switch (t1) { + case CovarType(Type tp): + if (!covariantOK) { + if (illegalType == NoType) illegalType = t1; + } else if (!typeArg) { + t1 = tp; + } + } + return t1; + } else { + Type prefix1 = prefix.toPrefix(pre, clazz); + Symbol sym1 = (prefix1 == prefix || (sym.flags & MODUL) != 0) + ? sym : prefix1.rebind(sym); + boolean prevTypeArg = typeArg; + typeArg = true; + Type[] args1 = map(args); + typeArg = prevTypeArg; + if (prefix1 == prefix && args1 == args) return t; + else return TypeRef(prefix1, sym1, args1); + } + + case SingleType(Type prefix, Symbol sym): + try { + Type prefix1 = prefix.toPrefix(pre, clazz); + if (prefix1 == prefix) return t; + else return singleType(prefix1, prefix1.rebind(sym)); + } catch (Type.Error ex) { + return apply(t.widen()); + } + break; + + default: + return map(t); + } + } + + void checkLegal(Type tp) { + if (illegalType != NoType) { + throw new ApplicationError( + "malformed type: " + tp + "; " + + illegalType + " does not occur in covariant position"); + } + } + } + //where + Type toInstance(Type pre, Symbol clazz) { + if (pre == NoType || clazz.kind != CLASS) + return this; + Symbol sym = symbol(); + Symbol ownclass = sym.owner().constructorClass(); + if (ownclass.isSubClass(clazz) && + pre.symbol().isSubClass(ownclass)) { + switch (pre.baseType(ownclass)) { + case TypeRef(_, Symbol basesym, Type[] baseargs): + Symbol[] baseparams = basesym.typeParams(); + for (int i = 0; i < baseparams.length; i++) + if (sym == baseparams[i]) return baseargs[i];//??? + break; + case ErrorType: + return ErrorType; + } + throw new ApplicationError( + this + " cannot be instantiated from " + pre); + } else { + return toInstance( + pre.baseType(clazz).prefix(), clazz.owner()); + } + } + + Type toPrefix(Type pre, Symbol clazz) { + if (pre == NoType || clazz.kind != CLASS) + return this; + if (symbol().isSubClass(clazz) && + pre.symbol().isSubClass(symbol())) { + if (!pre.isStable()) { + throw new Type.Error ( + "malformed type: " + pre + "." + symbol().nameString()); + } + return pre; + } else { + return toPrefix( + pre.baseType(clazz).prefix(), clazz.owner()); + } + } + + /** This type Types as seen from prefix `pre' and class `clazz'. This means: + * Replace all this thistypes of `clazz' or one of its superclasses by `pre'. + * Proceed analogously for thistypes referring to outer classes. + */ + public Type asSeenFrom(Type pre, Symbol clazz) { + //System.out.println("computing asseenfrom of " + this + " with " + pre + "," + clazz);//DEBUG + AsSeenFromMap f = new AsSeenFromMap(pre, clazz); + Type t = f.apply(this); + f.checkLegal(t); + return t; + } + + /** Types `these' as seen from prefix `pre' and class `clazz'. + */ + public static Type[] asSeenFrom(Type[] these, Type pre, Symbol clazz) { + AsSeenFromMap f = new AsSeenFromMap(pre, clazz); + Type[] these1 = f.map(these); + for (int i = 0; i < these1.length; i++) + f.checkLegal(these1[i]); + return these1; + } + + /** The info of `sym', seen as a member of this type. + */ + public Type memberInfo(Symbol sym) { + return memberTransform(sym, sym.info()); + } + + /** The type of `sym', seen as a member of this type. + */ + public Type memberType(Symbol sym) { + return memberTransform(sym, sym.type()); + } + + private Type memberTransform(Symbol sym, Type tp) { + Type tp1 = tp.asSeenFrom(narrow(), sym.owner()); + Type tp2 = tp1.asSeenFrom(this, widen().symbol()); + //todo: sym.owner()? + //if (Global.instance.debug) System.out.println(this + "/" + widen() + ".memberType(" + sym + ":" + tp + ") = " + tp1 + "/" + tp2);//DEBUG + return tp2; + } + +// Substitutions --------------------------------------------------------------- + + /** A common map superclass for symbol/symbol and type/symbol substitutions. + */ + static abstract class SubstMap extends Map { + private Symbol[] from; + + SubstMap(Symbol[] from) { + this.from = from; + } + + /** Produce replacement type + * @param i The index in `from' of the symbol to be replaced. + * @param fromtp The type referring to this symbol. + */ + abstract Type replacement(int i, Type fromtp); + + /** Produce new substitution where some symbols are excluded. + * @param newfrom The new array of from symbols (without excluded syms) + * @param excluded The array of excluded sysmbols + */ + abstract SubstMap exclude(Symbol[] newfrom, Symbol[] excluded); + + public Type apply(Type t) { + switch (t) { + case TypeRef(ThisType(_), Symbol sym, Type[] args): + for (int i = 0; i < from.length; i++) { + if (sym == from[i]) return replacement(i, t); + } + break; + case SingleType(ThisType(_), Symbol sym): + for (int i = 0; i < from.length; i++) { + if (sym == from[i]) return replacement(i, t); + } + break; + case PolyType(Symbol[] tparams, Type result): + Symbol[] from1 = excludeSyms(from, tparams, from); + if (from1 != from) { + SubstMap f = exclude(from1, tparams); + Symbol[] tparams1 = f.map(tparams); + Type result1 = f.apply(result); + if (tparams1 != tparams) + result1 = result1.subst(tparams, tparams1); + if (tparams1 == tparams && result1 == result) return t; + else return PolyType(tparams1, result1); + } + } + return map(t); + } + //where + private boolean contains1(Symbol[] syms, Symbol sym) { + int i = 0; + while (i < syms.length && syms[i] != sym) i++; + return i < syms.length; + } + + private int nCommon(Symbol[] from, Symbol[] tparams) { + int cnt = 0; + for (int i = 0; i < from.length; i++) { + if (contains1(tparams, from[i])) cnt++; + } + return cnt; + } + + private Symbol[] excludeSyms(Symbol[] from, Symbol[] tparams, Symbol[] syms) { + int n = nCommon(from, tparams); + if (n == 0) { + return syms; + } else { + Symbol[] syms1 = new Symbol[syms.length - n]; + int j = 0; + for (int i = 0; i < from.length; i++) { + if (!contains1(tparams, from[i])) syms1[j++] = syms[i]; + } + return syms1; + } + } + + private Type[] excludeTypes(Symbol[] from, Symbol[] tparams, Type[] types) { + int n = nCommon(from, tparams); + if (n == 0) { + return types; + } else { + Type[] types1 = new Type[types.length - n]; + int j = 0; + for (int i = 0; i < from.length; i++) { + if (!contains1(tparams, from[i])) types1[j++] = types[i]; + } + return types1; + } + } + } + + /** A map for symbol/symbol substitutions + */ + static class SubstSymMap extends SubstMap { + Symbol[] to; + SubstSymMap(Symbol[] from, Symbol[] to) { + super(from); + this.to = to; + } + Type replacement(int i, Type fromtp) { + switch (fromtp) { + case TypeRef(Type pre, Symbol sym, Type[] args): + return TypeRef(pre, to[i], args); + case SingleType(Type pre, Symbol sym): + return singleType(pre, to[i]); + default: + throw new ApplicationError(); + } + } + SubstMap exclude(Symbol[] newfrom, Symbol[] excluded) { + return new SubstSymMap(newfrom, excludeSyms(from, excluded, to)); + } + } + + /** A map for type/symbol substitutions + */ + static class SubstTypeMap extends SubstMap { + Type[] to; + SubstTypeMap(Symbol[] from, Type[] to) { + super(from); + this.to = to; + } + Type replacement(int i, Type fromtp) { + return to[i]; + } + SubstMap exclude(Symbol[] newfrom, Symbol[] excluded) { + return new SubstTypeMap(newfrom, excludeTypes(from, excluded, to)); + } + } + + /** Substitute symbols `to' for occurrences of symbols `from' in this type. + */ + public Type subst(Symbol[] from, Symbol[] to) { + assert from.length == to.length + : this + ": " + from.length + " != " + to.length; + if (from.length != 0 && from != to) + return new SubstSymMap(from, to).apply(this); + else return this; + } + + /** Substitute symbols `to' for occurrences of symbols `from' in these types. + */ + public static Type[] subst(Type[] these, Symbol[] from, Symbol[] to) { + assert from.length == to.length; + if (these.length != 0 && from.length != 0 && from != to) + return new SubstSymMap(from, to).map(these); + else return these; + } + + /** Substitute types `to' for occurrences of symbols `from' in this type. + */ + public Type subst(Symbol[] from, Type[] to) { + assert from.length == to.length + : this + ": " + from.length + " != " + to.length; + if (from.length != 0) + return new SubstTypeMap(from, to).apply(this); + else return this; + } + + /** Substitute types `to' for occurrences of symbols `from' in these types. + */ + public static Type[] subst(Type[] these, Symbol[] from, Type[] to) { + assert from.length == to.length; + if (these.length != 0 && from.length != 0) + return new SubstTypeMap(from, to).map(these); + else return these; + } + + /** A map for substitutions of thistypes. + */ + static class SubstThisMap extends Map { + Symbol from; + Type to; + SubstThisMap(Symbol from, Type to) { + this.from = from; + this.to = to; + } + public Type apply(Type t) { + switch (t) { + case ThisType(Symbol sym): + if (sym == from) return to; + else return t; + default: + return map(t); + } + } + } + + public Type substThis(Symbol from, Type to) { + return new SubstThisMap(from, to).apply(this); + } + + public static Type[] substThis(Type[] these, Symbol from, Type to) { + return new SubstThisMap(from, to).map(these); + } + + static class ContainsMap extends Map { + boolean result = false; + Symbol sym; + ContainsMap(Symbol sym) { + this.sym = sym; + } + public Type apply(Type t) { + switch (t) { + case TypeRef(Type pre, Symbol sym1, Type[] args): + if (sym == sym1) result = true; + else { map(pre); map(args); } + break; + case SingleType(Type pre, Symbol sym1): + map(pre); + if (sym == sym1) result = true; + break; + default: + map(t); + } + return t; + } + } + + /** Does this type contain symbol `sym'? + */ + public boolean contains(Symbol sym) { + ContainsMap f = new ContainsMap(sym); + f.apply(this); + return f.result; + } + + /** Does this type contain any of the symbols `syms'? + */ + public boolean containsSome(Symbol[] syms) { + for (int i = 0; i < syms.length; i++) + if (contains(syms[i])) return false; + return true; + } + +// Comparisons ------------------------------------------------------------------ + + /** Is this type a subtype of that type? + */ + public boolean isSubType(Type that) { + if (debugSwitch) { + for (int i = 0; i < indent; i++) System.out.print(" "); + System.out.println(this + " < " + that + "?"); + indent++; + } + boolean result = isSubType0(that); + if (debugSwitch) { + indent--; + for (int i = 0; i < indent; i++) System.out.print(" "); + System.out.println(result); + } + return result; + } + + public boolean isSubType0(Type that) { + if (this == that) return true; + + switch (this) { + case ErrorType: + case AnyType: + return true; + } + + switch (that) { + case ErrorType: + case AnyType: + return true; + + case NoType: + return false; + + case ThisType(Symbol sym1): + switch (this) { + case ThisType(Symbol sym): + return sym == sym1; + case SingleType(Type pre, Symbol sym): + return sym.isModule() + && sym.moduleClass() == sym1 + && pre.isSameAs(sym1.owner().thisType()); + } + break; + + case SingleType(Type pre1, Symbol sym1): + switch (this) { + case SingleType(Type pre, Symbol sym): + return sym == sym1 && pre.isSameAs(pre1); + case ThisType(Symbol sym): + return sym1.isModule() + && sym == sym1.moduleClass() + && sym.owner().thisType().isSameAs(pre1); + } + break; + + case TypeRef(Type pre1, Symbol sym1, Type[] args1): + switch (this) { + case TypeRef(Type pre, Symbol sym, Type[] args): + if (sym == sym1 && pre.isSameAs(pre1) && isSubArgs(args, args1)) + return true; + break; + } + if (sym1.kind == CLASS) { + Type base = this.baseType(sym1); + if (this != base && base.isSubType(that)) + return true; + } + break; + + case CompoundType(Type[] parts1, Scope members1): + int i = 0; + while (i < parts1.length && isSubType(parts1[i])) i++; + if (i == parts1.length && specializes(members1)) + return true; + break; + + case MethodType(Symbol[] ps1, Type res1): + switch (this) { + case MethodType(Symbol[] ps, Type res): + if (ps.length != ps1.length) return false; + for (int i = 0; i < ps.length; i++) { + Symbol p1 = ps1[i]; + Symbol p = ps[i]; + if (!p1.type().isSubType(p.type()) || + (p1.flags & Modifiers.DEF) != (p.flags & Modifiers.DEF)) + return false; + } + return res.isSubType(res1); + } + break; + + case PolyType(Symbol[] ps1, Type res1): + switch (this) { + case PolyType(Symbol[] ps, Type res): + if (ps.length != ps1.length) return false; + for (int i = 0; i < ps.length; i++) + if (!ps1[i].info().subst(ps1, ps).isSubType(ps[i].info())) + return false; + return res.isSubType(res1.subst(ps1, ps)); + } + break; + + case OverloadedType(Symbol[] alts1, Type[] alttypes1): + if (isSubSet(alttypes1, alternatives())) + return true; + break; + + case UnboxedType(int tag1): + switch (this) { + case UnboxedType(int tag): + return tag <= tag1 && tag1 <= DOUBLE && tag1 != CHAR; + } + break; + + case UnboxedArrayType(Type elemtp1): + switch (this) { + case UnboxedArrayType(Type elemtp): + return !(elemtp1 instanceof UnboxedType) && elemtp.isSubType(elemtp1); + } + break; + + case TypeVar(Type origin, Constraint constr): + //todo: should we test for equality with origin? + if (constr.inst != NoType) { + return this.isSubType(constr.inst); + } else if (!this.isCovarType()) { + constr.lobounds = new List(this, constr.lobounds); + return true; + } + break; + + default: + throw new ApplicationError(this + " <: " + that); + } + + switch (this) { + case NoType: + return false; + case ThisType(_): + case SingleType(_, _): + if (this.widen().isSubType(that)) return true; + break; + case TypeVar(Type origin, Constraint constr): + if (constr.inst != NoType) { + return constr.inst.isSubType(that); + } else { + constr.hibounds = new List(that.dropVariance(), constr.hibounds); + return true; + } + + case TypeRef(_, Symbol sym, _): + if (sym.kind == ALIAS) return this.unalias().isSubType(that); + break; + + case OverloadedType(Symbol[] alts, Type[] alttypes): + for (int i = 0; i < alttypes.length; i++) { + if (alttypes[i].isSameAs0(that)) return true; + } + break; + } + + switch (that) { + case TypeRef(_, Symbol sym1, _): + if (sym1.kind == ALIAS) return this.isSubType(that.unalias()); + break; + } + + return false; + } + + /** Are types `these' subtypes of corresponding types `those'? + */ + public static boolean isSubType(Type[] these, Type[] those) { + if (these.length != those.length) return false; + for (int i = 0; i < these.length; i++) { + if (!these[i].isSubType(those[i])) return false; + } + return true; + } + + /** Are types `these' arguments types conforming to corresponding types `those'? + */ + static boolean isSubArgs(Type[] these, Type[] those) { + if (these.length != those.length) return false; + for (int i = 0; i < these.length; i++) { + switch (those[i]) { + case CovarType(Type tp1): + switch (these[i]) { + case CovarType(Type tp): + if (!tp.isSubType(tp1)) return false; + break; + default: + if (!these[i].isSubType(tp1)) return false; + } + break; + default: + if (these[i].isCovarType() || !these[i].isSameAs(those[i])) + return false; + } + } + return true; + } + + public static boolean isSubSet(Type[] alts, Type[] alts1) { + for (int i = 0; i < alts.length; i++) { + int j = 0; + while (j < alts1.length && !alts1[j].isSameAs(alts[i])) j++; + if (j == alts1.length) return false; + } + return true; + } + + /** Does this type implement all symbols in scope `s' with same or stronger types? + */ + public boolean specializes(Scope s) { + for (Scope.Entry e = s.elems; e != Scope.Entry.NONE; e = e.next) + if (!specializes(e.sym)) return false; + return true; + } + + /** Does this type implement symbol `sym1' with same or stronger type? + */ + public boolean specializes(Symbol sym1) { + if (debugSwitch) { + for (int i = 0; i < indent; i++) System.out.print(" "); + System.out.println(this + " specializes " + sym1 + "?"); + indent++; + } + boolean result = specializes0(sym1); + if (debugSwitch) { + indent--; + for (int i = 0; i < indent; i++) System.out.print(" "); + System.out.println(result); + } + return result; + } + + public boolean specializes0(Symbol sym1) { + Symbol sym = lookup(sym1.name); + Type self = narrow(); + return + sym == sym1 || + ((sym.kind == sym1.kind || sym1.kind == TYPE) && + self.memberInfo(sym).isSubType( + sym1.info().substThis(sym.owner(), self))) || + (sym.kind == TYPE && sym1.kind == ALIAS && + sym1.info().unalias().isSameAs(sym.type())); + } + + /** Is this type the same as that type? + */ + public boolean isSameAs(Type that) { + if (debugSwitch) { + for (int i = 0; i < indent; i++) System.out.print(" "); + System.out.println(this + " = " + that + "?"); + indent++; + } + boolean result = isSameAs0(that); + if (debugSwitch) { + indent--; + for (int i = 0; i < indent; i++) System.out.print(" "); + System.out.println(result); + } + return result; + } + + public boolean isSameAs0(Type that) { + if (this == that) return true; + + switch (this) { + case ErrorType: + case AnyType: + return true; + + case ThisType(Symbol sym): + switch (that) { + case ThisType(Symbol sym1): + return sym == sym1; + case SingleType(Type pre1, Symbol sym1): + return sym1.isModule() + && sym == sym1.moduleClass() + && sym.owner().thisType().isSameAs(pre1); + } + break; + + case SingleType(Type pre, Symbol sym): + switch (that) { + case SingleType(Type pre1, Symbol sym1): + return sym == sym1 && pre.isSameAs(pre1); + case ThisType(Symbol sym1): + return sym.isModule() + && sym.moduleClass() == sym1 + && pre.isSameAs(sym1.owner().thisType()); + } + break; + + case TypeRef(Type pre, Symbol sym, Type[] args): + switch (that) { + case TypeRef(Type pre1, Symbol sym1, Type[] args1): + if (sym == sym1 && pre.isSameAs(pre1) && isSameArgs(args, args1)) + return true; + } + break; + + case CompoundType(Type[] parts, Scope members): + switch (that) { + case CompoundType(Type[] parts1, Scope members1): + if (parts.length != parts1.length) return false; + for (int i = 0; i < parts.length; i++) + if (!parts[i].isSameAs(parts1[i])) return false; + return isSameAs(members, members1); + } + break; + + case MethodType(Symbol[] ps, Type res): + switch (that) { + case MethodType(Symbol[] ps1, Type res1): + if (ps.length != ps1.length) return false; + for (int i = 0; i < ps.length; i++) { + Symbol p1 = ps1[i]; + Symbol p = ps[i]; + if (!p1.type().isSameAs(p.type()) || + (p1.flags & Modifiers.DEF) != (p.flags & Modifiers.DEF)) + return false; + } + return res.isSameAs(res1); + } + break; + + case PolyType(Symbol[] ps, Type res): + switch (that) { + case PolyType(Symbol[] ps1, Type res1): + if (ps.length != ps1.length) return false; + for (int i = 0; i < ps.length; i++) + if (!ps1[i].info().subst(ps1, ps).isSameAs(ps[i].info())) + return false; + return res.isSameAs(res1.subst(ps1, ps)); + } + break; + + case OverloadedType(Symbol[] alts, Type[] alttypes): + switch (that) { + case OverloadedType(Symbol[] alts1, Type[] alttypes1): + return isSubSet(alttypes1, alttypes) + && isSubSet(alttypes, alttypes1); + } + break; + + case UnboxedType(int kind): + switch (that) { + case UnboxedType(int kind1): + return kind == kind1; + } + break; + + case UnboxedArrayType(Type elemtp): + switch (that) { + case UnboxedArrayType(Type elemtp1): + return elemtp.isSameAs(elemtp1); + } + break; + } + + switch (that) { + case ErrorType: + case AnyType: + return true; + case NoType: + return false; + case TypeVar(Type origin, Constraint constr): + if (constr.inst != NoType) + return constr.inst.isSameAs(this); + else if (!this.isCovarType()) + return constr.instantiate(this.any2typevar()); + } + + switch (this) { + case NoType: + return false; + case TypeVar(Type origin, Constraint constr): + if (constr.inst != NoType) + return constr.inst.isSameAs(that); + else if (!that.isCovarType()) + return constr.instantiate(that.any2typevar()); + break; + case TypeRef(_, Symbol sym, _): + if (sym.kind == ALIAS) return this.unalias().isSameAs(that); + } + + switch (that) { + case TypeRef(_, Symbol sym, _): + if (sym.kind == ALIAS) return this.isSameAs(that.unalias()); + } + + return false; + } + + /** Is this type argument that same as `that'? + */ + public boolean isSameArg(Type that) { + switch (this) { + case CovarType(Type tp): + switch (that) { + case CovarType(Type tp1): + return tp.isSameAs(tp1); + } + } + return this.isSameAs(that); + } + + /** Are types `these' the same as corresponding types `those'? + */ + public static boolean isSameAs(Type[] these, Type[] those) { + if (these.length != those.length) return false; + for (int i = 0; i < these.length; i++) { + if (!these[i].isSameAs(those[i])) return false; + } + return true; + } + + /** Are type arguments `these' the same as corresponding types `those'? + */ + public static boolean isSameArgs(Type[] these, Type[] those) { + if (these.length != those.length) return false; + for (int i = 0; i < these.length; i++) { + if (!these[i].isSameArg(those[i])) return false; + } + return true; + } + + /** Do scopes `s1' and `s2' define he same symbols with the same kinds and infos? + */ + public boolean isSameAs(Scope s1, Scope s2) { + return isSubScope(s1, s2) && isSubScope(s2, s1); + } + + /** Does scope `s1' define all symbols of scope `s2' with the same kinds and infos? + */ + private boolean isSubScope(Scope s1, Scope s2) { + for (Scope.Entry e = s2.elems; e != Scope.Entry.NONE; e = e.next) { + Symbol sym2 = e.sym; + Symbol sym1 = s1.lookup(sym2.name); + if (sym1.kind != sym2.kind || + !sym1.info().isSameAs( + sym2.info().substThis(sym2.owner(), sym1.owner().thisType()))) + return false; + } + return true; + } + + boolean isSameAsAll(Type[] tps) { + int i = 1; + while (i < tps.length && isSameAs(tps[i])) i++; + return i == tps.length; + } + + /** Map every occurrence of AnyType to a fresh type variable. + */ + public static Map any2typevarMap = new Map() { + public Type apply(Type t) { return t.any2typevar(); } + }; + + public Type any2typevar() { + switch (this) { + case AnyType: + return TypeVar(this, new Constraint()); + default: + return any2typevarMap.map(this); + } + } + +// Closures and Least Upper Bounds --------------------------------------------------- + + /** The closure of this type, i.e. the widened type itself followed by all + * its direct and indirect (pre-) base types, sorted by Symbol.isLess(). + * Note that (pre-) base types do _not_ carry type parameters; these + * are added by baseType(). + */ + public Type[] closure() { + switch (this.widen().unalias()) { + case TypeRef(Type pre, Symbol sym, Type[] args): + return subst( + asSeenFrom(sym.closure(), pre, sym.owner()), + sym.typeParams(), args); + + case CompoundType(Type[] parts, Scope members): + Type[][] closures = new Type[parts.length][]; + for (int i = 0; i < parts.length; i++) + closures[i] = parts[i].closure(); + return union(closures); + + default: + return new Type[]{this}; + } + } + + /** return union of array of closures + */ + static private Type[] union(Type[][] closures) { + if (closures.length == 1) return closures[0]; // fast special case + int[] index = new int[closures.length]; + int totalsize = 0; + for (int i = 0; i < index.length; i++) { + index[i] = 0; + totalsize = totalsize + closures[i].length; + } + Type[] res = new Type[totalsize]; + int j = 0; + + while (true) { + // find minimal element + Type min = null; + for (int i = 0; i < index.length; i++) { + if (index[i] < closures[i].length) { + Type cltype = closures[i][index[i]]; + if (min == null || + cltype.symbol().isLess(min.symbol()) || + cltype.symbol() == min.symbol() && cltype.isSubType(min)) { + min = cltype; + } + } + } + if (min == null) break; + + res[j] = min; + j = j + 1; + + // bump all indices that start with minimal element + for (int i = 0; i < index.length; i++) { + if (index[i] < closures[i].length && + closures[i][index[i]].symbol() == min.symbol()) + index[i] = index[i] + 1; + } + } + Type[] result = new Type[j]; + System.arraycopy(res, 0, result, 0, j); + return result; + } + + /** return intersection of array of closures + */ + static private Type[] intersection(Type[][] closures) { + if (closures.length == 1) return closures[0]; // fast special case + int[] index = new int[closures.length]; + Type[] mintypes = new Type[closures.length]; + int minsize = Integer.MAX_VALUE; + for (int i = 0; i < index.length; i++) { + index[i] = 0; + if (closures[i].length < minsize) minsize = closures[i].length; + } + Type[] res = new Type[minsize]; + int j = 0; + + L: + while (true) { + // find minimal element + Symbol min = null; + for (int i = 0; i < index.length; i++) { + if (index[i] == closures[i].length) break L; + Symbol clsym = closures[i][index[i]].symbol(); + if (min == null || clsym.isLess(min)) min = clsym; + } + + boolean agree = true; + // bump all indices that start with minimal element + for (int i = 0; i < index.length; i++) { + Type cltype = closures[i][index[i]]; + if (cltype.symbol() == min) { + mintypes[i] = cltype; + index[i] = index[i] + 1; + } else { + agree = false; + } + } + if (agree) { + Type mintype; + mintype = commonType(mintypes); + if (mintype == NoType) + mintype = arglub(mintypes); + if (mintype.symbol().kind == CLASS) { + res[j] = mintype; + j = j + 1; + } + } + } + Type[] result = new Type[j]; + System.arraycopy(res, 0, result, 0, j); + return result; + } + + //todo: catch lubs not within bounds. + static Type arglub(Type[] types) { + Type pre = types[0].prefix(); + Symbol sym = types[0].symbol(); + Type[] args = new Type[sym.typeParams().length]; + Type[][] argss = new Type[args.length][types.length]; + for (int i = 0; i < types.length; i++) { + switch (types[i]) { + case TypeRef(Type pre1, Symbol sym1, Type[] args1): + assert sym == sym1; + assert args1.length == args.length; + if (!pre.isSameAs(pre1)) return NoType; + for (int j = 0; j < args1.length; j++) + argss[j][i] = args1[j]; + case ErrorType: + return ErrorType; + default: + assert false : types[i]; + } + } + for (int j = 0; j < args.length; j++) { + args[j] = commonType(argss[j]); + if (args[j] == NoType) + args[j] = CovarType(lub(argss[j])); + } + return TypeRef(pre, sym, args); + } + + /** The frontier of a closure C is the minimal set of types such that + * the union of the closures of these types equals C. + */ + static private Type[] frontier(Type[] closure) { + Type[] front = new Type[closure.length]; + int j = 0; + for (int i = 0; i < closure.length; i++) { + int k = 0; + Type tp = closure[i]; + while (k < j && !front[k].symbol().isSubClass(tp.symbol())) + k++; + if (k == j) { + front[j] = tp; + j++; + } + } + Type[] result = new Type[j]; + System.arraycopy(front, 0, result, 0, j); + return result; + } + + /** Return the least upper bound of non-empty array of types `tps'. + * todo: do we need to consider refinements as well? + */ + public static Type lub(Type[] tps) { + //System.out.println("lub" + ArrayApply.toString(tps));//DEBUG + Type lubType = commonType(tps); + if (lubType != NoType) return lubType; + Type[][] closures = new Type[tps.length][]; + for (int i = 0; i < tps.length; i++) { + if (!tps[i].isObjectType()) return Type.NoType;//todo: change + closures[i] = tps[i].closure(); + } + Type[] allBaseTypes = intersection(closures); + Type[] leastBaseTypes = frontier(allBaseTypes); + if (leastBaseTypes.length == 0) return Type.NoType; + Scope members = new Scope(); + lubType = compoundType(leastBaseTypes, members); + Type lubThisType = lubType.narrow(); + //System.out.println("lubtype = " + lubType);//DEBUG + + Symbol[] rsyms = new Symbol[tps.length]; + Type[] rtps = new Type[tps.length]; + for (int i = 0; i < allBaseTypes.length; i++) { + for (Scope.Entry e = allBaseTypes[i].members().elems; + e != Scope.Entry.NONE; + e = e.next) { + Name name = e.sym.name; + if ((e.sym.flags & PRIVATE) == 0 && lubType.lookup(name) == e.sym) { + Type symType = lubThisType.memberInfo(e.sym); + int j = 0; + while (j < tps.length) { + rsyms[j] = tps[j].lookupNonPrivate(name); + if (rsyms[j] == e.sym) break; + rtps[j] = tps[j].memberType(rsyms[j]) + .substThis(tps[j].symbol(), lubThisType); + if (rtps[j].isSameAs(symType)) break; + j++; + } + if (j == tps.length) { + Symbol lubSym = lub(rsyms, rtps, lubType.symbol()); + if (lubSym.kind != NONE && !lubSym.info().isSameAs(symType)) + members.enter(lubSym); + } + } + } + } + //System.out.print("lub "); System.out.print(ArrayApply.toString(tps)); System.out.println(" = " + lubType);//DEBUG + if (leastBaseTypes.length == 1 && members.elems == Scope.Entry.NONE) + return leastBaseTypes[0]; + else return lubType; + } + + private static Type commonType(Type[] tps) { + Type tp = tps[0]; + if (tp.isSameAsAll(tps)) return tp; + tp = tp.widen(); + if (tp.isSameAsAll(widen(tps))) return tp; + return NoType; + } + + private static Symbol lub(Symbol[] syms, Type[] tps, Symbol owner) { + //System.out.println("lub" + ArrayApply.toString(syms));//DEBUG + int lubKind = syms[0].kind; + for (int i = 1; i < syms.length; i++) { + Symbol sym = syms[i]; + if (sym.kind == ERROR) return Symbol.NONE; + if (sym.isType() && sym.kind != lubKind) lubKind = TYPE; + } + if (lubKind == syms[0].kind && tps[0].isSameAsAll(tps)) { + return syms[0].cloneSymbol(); + } + Type lubType = lub(tps); + if (lubType == Type.NoType) return Symbol.NONE; + Symbol lubSym; + switch (lubKind) { + case VAL: + lubSym = new TermSymbol(syms[0].pos, syms[0].name, owner, 0); + break; + case TYPE: case ALIAS: case CLASS: + lubSym = new TypeSymbol(TYPE, syms[0].pos, syms[0].name, owner, 0); + break; + default: + throw new ApplicationError(); + } + lubSym.setInfo(lubType); + return lubSym; + } + +// Erasure -------------------------------------------------------------------------- + + public static Map erasureMap = new Map() { + public Type apply(Type t) { return t.erasure(); } + }; + + private static final Type[] unboxedType = + new Type[LastUnboxedTag + 1 - FirstUnboxedTag]; + private static final Type[] unboxedArrayType = + new Type[LastUnboxedTag + 1 - FirstUnboxedTag]; + private static final Name[] unboxedName = + new Name[LastUnboxedTag + 1 - FirstUnboxedTag]; + private static final Name[] boxedName = + new Name[LastUnboxedTag + 1 - FirstUnboxedTag]; + private static final Name[] boxedFullName = + new Name[LastUnboxedTag + 1 - FirstUnboxedTag]; + + private static void mkStdClassType(int kind, String unboxedstr, String boxedstr) { + unboxedType[kind - FirstUnboxedTag] = UnboxedType(kind); + unboxedArrayType[kind - FirstUnboxedTag] = UnboxedArrayType(unboxedType(kind)); + unboxedName[kind - FirstUnboxedTag] = Name.fromString(unboxedstr); + boxedName[kind - FirstUnboxedTag] = Name.fromString(boxedstr); + boxedFullName[kind - FirstUnboxedTag] = Name.fromString("scala." + boxedstr); + } + + static { + mkStdClassType(BYTE, "byte", "Byte"); + mkStdClassType(SHORT, "short", "Short"); + mkStdClassType(CHAR, "char", "Char"); + mkStdClassType(INT, "int", "Int"); + mkStdClassType(LONG, "long", "Long"); + mkStdClassType(FLOAT, "float", "Float"); + mkStdClassType(DOUBLE, "double", "Double"); + mkStdClassType(BOOLEAN, "boolean", "Boolean"); + mkStdClassType(UNIT, "void", "Unit"); + } + + /** Return unboxed type of given kind. + */ + public static Type unboxedType(int kind) { + return unboxedType[kind - FirstUnboxedTag]; + } + + /** Return unboxed array type of given element kind. + */ + public static Type unboxedArrayType(int kind) { + return unboxedArrayType[kind - FirstUnboxedTag]; + } + + /** Return the name of unboxed type of given kind. + */ + public static Name unboxedName(int kind) { + return unboxedName[kind - FirstUnboxedTag]; + } + + /** Return the name of boxed type of given kind. + */ + public static Name boxedName(int kind) { + return boxedName[kind - FirstUnboxedTag]; + } + + /** Return the full name of boxed type of given kind. + */ + public static Name boxedFullName(int kind) { + return boxedFullName[kind - FirstUnboxedTag]; + } + + /** If type is boxed, return its unboxed equivalent; otherwise return the type + * itself. + */ + public Type unbox() { + switch (this) { + case TypeRef(Type pre, Symbol sym, Type[] args): + if ((sym.flags & MODUL) == 0) { + Name fullname = sym.fullName(); + if (fullname == Names.scala_Array && args.length == 1 + /*&& args[0].unalias().symbol().kind != TYPE Q: why needed?*/) { + return UnboxedArrayType(args[0].erasure()); + } else { + for (int i = 0; i < boxedFullName.length; i++) { + if (boxedFullName[i] == fullname) return unboxedType[i]; + } + } + } + } + return this; + } + + /** Return the erasure of this type. + */ + public Type erasure() { + switch (this) { + case ThisType(_): + case SingleType(_, _): + return widen().erasure(); + case TypeRef(Type pre, Symbol sym, Type[] args): + switch (sym.kind) { + case ALIAS: case TYPE: + return pre.memberInfo(sym).erasure(); + + case CLASS: + if (sym.fullName() == Names.java_lang_Object || + sym.fullName() == Names.scala_AnyRef || + sym.fullName() == Names.scala_AnyVal) + return Global.instance.definitions.ANY_TYPE; + else { + Type this1 = unbox(); + if (this1 != this) return this1; + else if (args.length == 0) return this; + else return TypeRef(pre, sym, Type.EMPTY_ARRAY); + } + + default: throw new ApplicationError(); + } + case CompoundType(Type[] parents, _): + if (parents.length > 0) return parents[0].erasure(); + else return this; + case CovarType(Type tp): + return tp.erasure(); // note: needed because of UnboxedArrayType + case MethodType(Symbol[] params, Type tp): + Symbol[] params1 = erasureMap.map(params); + Type tp1 = tp.erasure(); + switch (tp1) { + case MethodType(Symbol[] params2, Type tp2): + Symbol[] newparams = new Symbol[params1.length + params2.length]; + System.arraycopy(params1, 0, newparams, 0, params1.length); + System.arraycopy(params2, 0, newparams, params1.length, params2.length); + return MethodType(newparams, tp2); + default: + if (params1 == params && tp1 == tp) return this; + else return MethodType(params1, tp1); + } + case PolyType(_, Type result): + return result.erasure(); + default: + return erasureMap.map(this); + } + } + +// Object Interface ----------------------------------------------------------------- + + public String toString() { + switch (this) { + case ErrorType: + return "<error>"; + case AnyType: + return "<any type>"; + case NoType: + return "<notype>"; + case ThisType(Symbol sym): + if (sym.isRoot()) return "<root>.this.type"; + else if (this == localThisType) return "<local>.this.type"; + else { + Type this1 = (Global.instance.debug) ? this : expandModuleThis(); + if (this1 == this) return sym.nameString() + ".this.type"; + else return this1.toString(); + } + case TypeRef(Type pre, Symbol sym, Type[] args): + if (sym.isRoot()) return "<root>"; + if (!Global.instance.debug) { + if (isTupleType()) + return ArrayApply.toString( + dropVarianceMap.map(args), "[", ",", "]"); + if (isFunctionType()) { + Type[] params = new Type[args.length - 1]; + System.arraycopy(args, 0, params, 0, params.length); + return ArrayApply.toString(params, "(", ",", ") => ") + + args[params.length].dropVariance(); + } + } + Type pre1 = (Global.instance.debug) ? pre : pre.expandModuleThis(); + String result = pre1.prefixString() + sym.nameString() + sym.idString(); + if (args.length != 0) + result = result + ArrayApply.toString(args, "[", ",", "]"); + return result; + case SingleType(Type pre, Symbol sym): + if ((sym.flags & SYNTHETIC) != 0 && !Global.instance.debug) + return widen().toString(); + if (sym.isRoot()) return "<root>.type"; + Type pre1 = (Global.instance.debug) ? pre : pre.expandModuleThis(); + return pre1.prefixString() + sym.nameString() + sym.idString() + ".type"; + case CompoundType(Type[] parts, Scope members): + validate();//debug + StringBuffer buf = new StringBuffer(); + if (parts.length > 0) { + buf.append(parts[0].toString()); + int i = 1; + while (i < parts.length) { + buf.append(" with "); + buf.append(parts[i].toString()); + i++; + } + } + boolean first = true; + for (Scope.SymbolIterator it = members.iterator(); it.hasNext(); ) { + Symbol sym = it.next(); + buf.append(first ? "{" : ", "); + first = false; + buf.append(sym.defString()); + } + if (!first) buf.append("}"); + return buf.toString(); + case MethodType(Symbol[] vparams, Type result): + return ArrayApply.toString(Symbol.type(vparams), "(", ",", ")") + result; + case PolyType(Symbol[] tparams, Type result): + return ArrayApply.toString(Symbol.defString(tparams), "[", ",", "]") + + result; + case CovarType(Type tp): + return "+" + tp; + case OverloadedType(Symbol[] alts, Type[] alttypes): + return "<overloaded> " + ArrayApply.toString(alttypes, "", " <and> ", "");//debug + case TypeVar(Type origin, Constraint constr): + if (constr.inst != NoType) return constr.inst.toString(); + else return origin + "?"; + case UnboxedType(int kind): + return unboxedName(kind).toString(); + case UnboxedArrayType(Type elemtp): + return elemtp.toString() + "[]"; + case LazyType(): + return "<lazy type " + getClass() + ">"; + default: + return "<unknown type " + getClass() + ">"; + } + } + + public String toLongString() { + String str = toString(); + if (str.endsWith(".type")) return str + " (with underlying type " + widen() + ")"; + else return str; + } + + private String prefixString() { + if ((this == localThisType || symbol().isRoot()) && !Global.instance.debug) { + return ""; + } else { + String spre = toString(); + if (spre.length() == 0) + return ""; + else if (spre.endsWith(".type")) + return spre.substring(0, spre.length() - 4); + else + return spre + "@"; + } + } + + public int hashCode() { + switch (this) { + case ErrorType: + return ERROR; + case NoType: + return NOtpe; + case ThisType(Symbol sym): + return THIStpe + ^ (sym.hashCode() * 41); + case TypeRef(Type pre, Symbol sym, Type[] args): + return NAMEDtpe + ^ (pre.hashCode() * 41) + ^ (sym.hashCode() * (41*41)) + ^ (hashCode(args) * (41*41*41)); + case SingleType(Type pre, Symbol sym): + return SINGLEtpe + ^ (pre.hashCode() * 41) + ^ (sym.hashCode() * (41*41)); + case CompoundType(Type[] parts, Scope members): + return COMPOUNDtpe + ^ (hashCode(parts) * 41) + ^ (members.hashCode() * (41 * 41)); + case MethodType(Symbol[] vparams, Type result): + return METHODtpe + ^ (hashCode(Symbol.type(vparams)) * 41) + ^ (result.hashCode() * (41 * 41)); + case PolyType(Symbol[] tparams, Type result): + return POLYtpe + ^ (hashCode(tparams) * 41) + ^ (result.hashCode() * (41 * 41)); + case CovarType(Type tp): + return COVARtpe + ^ (tp.hashCode() * (41)); + case OverloadedType(Symbol[] alts, Type[] alttypes): + return OVERLOADEDtpe + ^ (hashCode(alts) * 41) + ^ (hashCode(alttypes) * (41 * 41)); + case UnboxedType(int kind): + return UNBOXEDtpe + ^ (kind * 41); + case UnboxedArrayType(Type elemtp): + return UNBOXEDARRAYtpe + ^ (elemtp.hashCode() * 41); + default: + throw new ApplicationError(); + } + } + + public static int hashCode(Object[] elems) { + int h = 0; + for (int i = 0; i < elems.length; i++) + h = h * 41 + elems[i].hashCode(); + return h; + } + + public static int hashCode(Scope.Entry elems) { + int h = 0; + for (Scope.Entry e = elems; e != Scope.Entry.NONE; e = e.next) + h = h * 41 + + e.sym.kind + + e.sym.name.hashCode() + + e.sym.info().hashCode(); + return h; + } + + // todo: change in relation to needs. + + public boolean equals(Object other) { + if (this == other) { + return true; + } else if (other instanceof Type) { + Type that = (Type) other; + switch (this) { + case ErrorType: + return that == ErrorType; + case NoType: + return that == NoType; + case ThisType(Symbol sym): + switch (that) { + case ThisType(Symbol sym1): + return sym == sym1; + default: return false; + } + case TypeRef(Type pre, Symbol sym, Type[] args): + switch (that) { + case TypeRef(Type pre1, Symbol sym1, Type[] args1): + return pre.equals(pre1) && sym == sym1 && equals(args, args1); + default: return false; + } + case SingleType(Type pre, Symbol sym): + switch (that) { + case SingleType(Type pre1, Symbol sym1): + return pre.equals(pre1) && sym == sym1; + default: return false; + } + case CompoundType(Type[] parts, Scope members): + switch (that) { + case CompoundType(Type[] parts1, Scope members1): + return parts.equals(parts1) && members.equals(members1); + default: return false; + } + case MethodType(Symbol[] vparams, Type result): + switch (that) { + case MethodType(Symbol[] vparams1, Type result1): + return equals(Symbol.type(vparams), Symbol.type(vparams1)) && + result.equals(result1); + default: return false; + } + case PolyType(Symbol[] tparams, Type result): + switch (that) { + case PolyType(Symbol[] tparams1, Type result1): + return equals(tparams, tparams1) && result.equals(result1); + default: return false; + } + case CovarType(Type tp): + switch (that) { + case CovarType(Type tp1): + return tp.equals(tp1); + default: return false; + } + case OverloadedType(Symbol[] alts, Type[] alttypes): + switch (that) { + case OverloadedType(Symbol[] alts1, Type[] alttypes1): + return equals(alts, alts1) && equals(alttypes, alttypes1); + default: return false; + } + case UnboxedType(int kind): + switch (that) { + case UnboxedType(int kind1): + return kind == kind1; + default: return false; + } + case UnboxedArrayType(Type elemtp): + switch (that) { + case UnboxedArrayType(Type elemtp1): + return elemtp.equals(elemtp1); + default: return false; + } + default: + } + } + return false; + } + + public static boolean equals(Object[] elems1, Object[] elems2) { + if (elems1.length != elems2.length) return false; + for (int i = 0; i < elems1.length; i++) { + if (!elems1[i].equals(elems2[i])) return false; + } + return true; + } + +// Type.List class ----------------------------------------------------------------- + + /** A class for lists of types. + */ + public static class List { + public Type head; + public List tail; + public List(Type head, List tail) { + this.head = head; this.tail = tail; + } + public int length() { + return (this == EMPTY) ? 0 : 1 + tail.length(); + } + public Type[] toArray() { + Type[] ts = new Type[length()]; + copyToArray(ts, 0, 1); + return ts; + } + public void copyToArray(Type[] ts, int start, int delta) { + if (this != EMPTY) { + ts[start] = head; + tail.copyToArray(ts, start+delta, delta); + } + } + public Type[] toArrayReverse() { + Type[] ts = new Type[length()]; + copyToArray(ts, ts.length - 1, -1); + return ts; + } + + public static List EMPTY = new List(null, null); + + public static List append(List l, Type tp) { + return (l == EMPTY) ? new List(tp, EMPTY) + : new List(l.head, append(l.tail, tp)); + } + } + +// Type.Constraint class ------------------------------------------------------- + + /** A class for keeping sub/supertype constraints and instantiations + * of type variables. + */ + public static class Constraint { + public List lobounds = List.EMPTY; + public List hibounds = List.EMPTY; + public Type inst = NoType; + + public boolean instantiate(Type tp) { + for (List l = lobounds; l != List.EMPTY; l = l.tail) { + if (!l.head.isSubType(tp)) return false; + } + for (List l = hibounds; l != List.EMPTY; l = l.tail) { + if (!tp.isSubType(l.head)) return false; + } + inst = tp; + return true; + } + } + +// Type.Error class -------------------------------------------------------------- + + /** A class for throwing type errors + */ + public static class Error extends java.lang.Error { + public String msg; + public Error(String msg) { + super(msg); + this.msg = msg; + } + } + + /** A class for throwing type errors + */ + public static class VarianceError extends Error { + public VarianceError(String msg) { + super(msg); + } + } +} + +/* A standard pattern match: + + case ErrorType: + case AnyType: + case NoType: + case ThisType(Symbol sym): + case TypeRef(Type pre, Symbol sym, Type[] args): + case SingleType(Type pre, Symbol sym): + case CompoundType(Type[] parts, Scope members): + case MethodType(Symbol[] vparams, Type result): + case PolyType(Symbol[] tparams, Type result): + case OverloadedType(Symbol[] alts, Type[] alttypes): + case CovarType(Type tp): +*/ + diff --git a/sources/scalac/symtab/TypeTags.java b/sources/scalac/symtab/TypeTags.java new file mode 100644 index 0000000000..a3bd103432 --- /dev/null +++ b/sources/scalac/symtab/TypeTags.java @@ -0,0 +1,47 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab; + +public interface TypeTags { + + /** unboxed type tags + */ + int BYTE = 10; + int CHAR = 11; + int SHORT = 12; + int INT = 13; + int LONG = 14; + int FLOAT = 15; + int DOUBLE = 16; + int BOOLEAN = 17; + int UNIT = 18; + int STRING = 19; + + int FirstUnboxedTag = BYTE; + int LastUnboxedTag = UNIT; + + /** other type tags (used for hashcodes and Pickling) + */ + int ERRORtpe = 20; + int NOtpe = 21; + int THIStpe = 22; + int NAMEDtpe = 23; + int SINGLEtpe = 24; + int COMPOUNDtpe = 25; + int METHODtpe = 26; + int POLYtpe = 27; + int CONSTRUCTORtpe = 28; + int COVARtpe = 29; + int OVERLOADEDtpe = 30; + int UNBOXEDtpe = 31; + int UNBOXEDARRAYtpe = 32; + + int firstTypeTag = ERRORtpe; + int lastTypeTag = UNBOXEDARRAYtpe; +} diff --git a/sources/scalac/symtab/classfile/AttributeParser.java b/sources/scalac/symtab/classfile/AttributeParser.java new file mode 100644 index 0000000000..0ed09b44d0 --- /dev/null +++ b/sources/scalac/symtab/classfile/AttributeParser.java @@ -0,0 +1,439 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.*; +import scalac.symtab.*; +import scalac.util.*; +import java.util.*; + +public class AttributeParser implements ClassfileConstants { + + /** the classfile input buffer + */ + protected AbstractFileReader in; + + /** the constant pool + */ + protected ConstantPool pool; + + protected ClassfileParser parser; + + /** constructor + */ + public AttributeParser(AbstractFileReader in, ConstantPool pool, ClassfileParser parser) { + this.in = in; + this.pool = pool; + this.parser = parser; + } + + public int nameToId(Name name) { + if (name == SOURCEFILE_N) + return SOURCEFILE_ATTR; + if (name == SYNTHETIC_N) + return SYNTHETIC_ATTR; + if (name == DEPRECATED_N) + return DEPRECATED_ATTR; + if (name == CODE_N) + return CODE_ATTR; + if (name == EXCEPTIONS_N) + return EXCEPTIONS_ATTR; + if (name == CONSTANT_VALUE_N) + return CONSTANT_VALUE_ATTR; + if (name == LINE_NUM_TABLE_N) + return LINE_NUM_TABLE_ATTR; + if (name == LOCAL_VAR_TABLE_N) + return LOCAL_VAR_TABLE_ATTR; + if (name == INNERCLASSES_N) + return INNERCLASSES_ATTR; + if (name == META_N) + return META_ATTR; + if (name == SCALA_N) + return SCALA_ATTR; + return BAD_ATTR; + } + + public Symbol readAttributes(Symbol def, Type type, int attrs) { + char nattr = in.nextChar(); + for (int i = 0; i < nattr; i++) { + Name attrName = (Name)pool.readPool(in.nextChar()); + int attr = nameToId(attrName); + int attrLen = in.nextInt(); + if ((attrs & attr) == 0) { + //System.out.println("# skipping " + attrName + " of " + def); + in.skip(attrLen); + } else { + //System.out.println("# reading " + attrName + " of " + def); + readAttribute(def, type, attr, attrLen); + } + } + return def; + } + + public void readAttribute(Symbol sym, Type type, int attr, int attrLen) { + switch (attr) { + // class attributes + case SCALA_ATTR: + in.skip(attrLen); + /* not yet + Name sourcefile = (Name)pool.readPool(in.nextChar()); + new UnPickle( + (JavaClassSymbol) sym, in.nextBytes(attrLen - 2), sourcefile); + */ + return; + + case SOURCEFILE_ATTR: + // ((ClassDef)def).sourcefile = (Name)reader.readPool(in.nextChar()); + in.skip(attrLen); + return; + + case INNERCLASSES_ATTR: + /* int n = in.nextChar(); + for (int i = 0; i < n; i++) { + Symbol inner = (Symbol)pool.readPool(in.nextChar()); + Symbol outer = (Symbol)pool.readPool(in.nextChar()); + Name name = (Name)pool.readPool(in.nextChar()); + int flags = in.nextChar(); + if (name != null) { + inner.owner(outer); + inner.mangled(name); + inner.flags = flags; + } + } */ + in.skip(attrLen); + return; + + // method attributes + case CODE_ATTR: + in.skip(attrLen); + return; + + case EXCEPTIONS_ATTR: + //int nexceptions = in.nextChar(); + //Type[] thrown = new Type[nexceptions]; + //for (int j = 0; j < nexceptions; j++) + // thrown[j] = make.classType(reader.readClassName(in.nextChar())); + //((MethodType)def.type).thrown = thrown; + in.skip(attrLen); + return; + + case LINE_NUM_TABLE_ATTR: + in.skip(attrLen); + return; + + case LOCAL_VAR_TABLE_ATTR: + in.skip(attrLen); + return; + + // general attributes + case SYNTHETIC_ATTR: + sym.flags |= Modifiers.SYNTHETIC; + return; + + case DEPRECATED_ATTR: + sym.flags |= Modifiers.DEPRECATED; + return; + + case CONSTANT_VALUE_ATTR: + // Type ctype = (Type)reader.readPool(in.nextChar()); + // def.type = types.coerce(ctype, def.type); + in.skip(attrLen); + return; + + case META_ATTR: + //System.out.println("parsing meta data for " + sym); + String meta = pool.readPool(in.nextChar()).toString().trim(); + sym.setInfo(new MetaParser(meta, tvars, sym, type).parse(), parser.phaseId); + + return; + } + throw new RuntimeException("unknown classfile attribute"); + } + + Scope tvars = new Scope(); + + class MetaParser { + + Symbol owner; + StringTokenizer scanner; + Type defaultType; + String token; + Scope tvars; + Scope locals; + + MetaParser(String meta, Scope tvars, Symbol owner, Type defaultType) { + //System.out.println("meta = " + meta); + this.scanner = new StringTokenizer(meta, "()[], \t<;", true); + this.defaultType = defaultType; + this.owner = owner; + this.tvars = tvars; + } + + private Symbol getTVar(String name) { + return getTVar(name, parser.c.constructor()); + } + + private Symbol getTVar(String name, Symbol owner) { + if (name.startsWith("?")) { + if (locals != null) { + Symbol s = locals.lookup(Name.fromString(name).toTypeName()); + if (s != Symbol.NONE) + return s; + } + Symbol s = tvars.lookup(Name.fromString(name).toTypeName()); + if (s == Symbol.NONE) { + s = new TypeSymbol(Kinds.TYPE, + Position.NOPOS, + Name.fromString(token).toTypeName(), + owner, + Modifiers.PARAM); + s.setInfo(parser.defs.ANY_TYPE, parser.phaseId); + tvars.enter(s); + } + return s; + } else + return Symbol.NONE; + } + + private String nextToken() { + do { + token = scanner.nextToken().trim(); + } while (token.length() == 0); + return token; + } + + protected Type parse() { + if (scanner.hasMoreTokens()) { + nextToken(); + if (!scanner.hasMoreTokens()) + return defaultType; + if ("class".equals(token)) + return parseMetaClass(); + if ("method".equals(token)) + return parseMetaMethod(); + if ("field".equals(token)) + return parseMetaField(); + if ("constr".equals(token)) + return parseConstrField(); + } + return defaultType; + } + + protected Type parseMetaClass() { + nextToken(); + //System.out.println("parse meta class " + token);//DEBUG + if ("[".equals(token)) { + try { + Vector syms = new Vector(); + do { + nextToken(); + assert token.startsWith("?"); + Symbol s = getTVar(token); + if (s == Symbol.NONE) + return defaultType; + nextToken(); + //System.out.println("new var " + s + ", " + token);//DEBUG + if (token.equals("<")) { + nextToken(); + s.setInfo(parseType(), parser.phaseId); + } + syms.add(s); + } while (token.equals(",")); + assert "]".equals(token); + nextToken(); + Symbol[] smbls = (Symbol[])syms.toArray(new Symbol[syms.size()]); + //System.out.println("*** " + syms);//DEBUG + Type constrtype = Type.appliedType( + parser.ctype, Symbol.type(smbls)); + + if ((parser.c.flags & Modifiers.INTERFACE) != 0) { + parser.c.constructor().setInfo( + Type.PolyType(smbls, constrtype), parser.phaseId); + //System.out.println("info = " + parser.c.constructor().info());//DEBUG + } + Symbol[] constrs; + switch (parser.c.constructor().rawInfo()) { + case OverloadedType(Symbol[] alts, _): + constrs = alts; + break; + default: + constrs = new Symbol[]{parser.c.constructor()}; + } + for (int i = 0; i < constrs.length; i++) { + //System.out.println("info = " + e.sym.info()); + switch (constrs[i].rawInfo()) { + case MethodType(Symbol[] vparams, _): + constrs[i].setInfo( + Type.PolyType(smbls, + Type.MethodType( + vparams, constrtype)), parser.phaseId); + break; + case PolyType(_, _): + constrs[i].setInfo( + Type.PolyType(smbls, constrtype), parser.phaseId); + break; + } + //System.out.println("*** constructor " + e.sym + ": " + e.sym.info());//DEBUG + } + } catch (NoSuchElementException e) { + } + } + Type res = defaultType; + if ("extends".equals(token)) { + Vector basetpes = new Vector(); + do { + nextToken(); + basetpes.add(parseType()); + } while (token.equals("with")); + switch (defaultType) { + case CompoundType(_, Scope scope): + res = Type.compoundType( + (Type[])basetpes.toArray(new Type[basetpes.size()]), + scope, + defaultType.symbol()); + } + } + assert ";".equals(token); + return res; + } + + protected Type parseType() { + String name = token; + Symbol s = getTVar(name); + nextToken(); + if (s != Symbol.NONE) + return s.type(); + Symbol clazz = parser.defs.getClass(Name.fromString(name)); + if (token.equals("[")) { + Vector types = new Vector(); + do { + nextToken(); + types.add(parseType()); + } while (token.equals(",")); + assert "]".equals(token); + nextToken(); + Type[] args = new Type[types.size()]; + types.toArray(args); + return Type.TypeRef(clazz.owner().thisType(), clazz, args); + } else { + return parser.defs.monoType(clazz); + } + } + + protected Type parseMetaMethod() { + locals = new Scope(); + try { + nextToken(); + Symbol[] smbls = null; + //System.out.println("parse meta method " + token); + if ("[".equals(token)) { + Vector syms = new Vector(); + do { + nextToken(); + if ("]".equals(token)) + break; + assert token.startsWith("?"); + Symbol s = getTVar(token, owner); + if (s == Symbol.NONE) + return defaultType; + nextToken(); + //System.out.println("new var " + s + ", " + token); + if (token.equals("<")) { + nextToken(); + s.setInfo(parseType(), parser.phaseId); + } + syms.add(s); + } while (token.equals(",")); + assert "]".equals(token); + nextToken(); + smbls = (Symbol[])syms.toArray(new Symbol[syms.size()]); + } + if ("(".equals(token)) { + int i = 0; + Vector params = new Vector(); + do { + nextToken(); + if (")".equals(token)) + break; + params.add(new TermSymbol( + Position.NOPOS, + Name.fromString("x" + (i++)), + owner, + Modifiers.PARAM).setInfo(parseType(), parser.phaseId)); + //System.out.println(" + " + token); + } while (token.equals(",")); + assert ")".equals(token); + nextToken(); + //System.out.println("+++ method " + token); + Type restpe = parseType(); + assert ";".equals(token); + if (smbls == null) + return Type.MethodType( + (Symbol[])params.toArray(new Symbol[params.size()]), + restpe); + else + return Type.PolyType( + smbls, + Type.MethodType( + (Symbol[])params.toArray(new Symbol[params.size()]), + restpe)); + } else { + Type res = parseType(); + assert ";".equals(token); + if (smbls == null) + return Type.PolyType(Symbol.EMPTY_ARRAY, res); + else + return Type.PolyType(smbls, res); + } + } catch (NoSuchElementException e) { + return defaultType; + } finally { + locals = null; + } + } + + protected Type parseMetaField() { + nextToken(); + return parseType(); + } + + protected Type parseConstrField() { + try { + nextToken(); + //System.out.println("+++ constr " + token); + if ("(".equals(token)) { + int i = 0; + Vector params = new Vector(); + do { + nextToken(); + if (")".equals(token)) + break; + params.add(new TermSymbol( + Position.NOPOS, + Name.fromString("x" + (i++)), + owner, + Modifiers.PARAM).setInfo(parseType(), parser.phaseId)); + //System.out.println(" + " + token); + } while (token.equals(",")); + assert ")".equals(token); + nextToken(); + assert ";".equals(token); + return Type.MethodType( + (Symbol[])params.toArray(new Symbol[params.size()]), + parser.ctype); + } else { + assert ";".equals(token); + return Type.PolyType(Symbol.EMPTY_ARRAY, parser.ctype); + } + } catch (NoSuchElementException e) { + return defaultType; + } + } + } +} diff --git a/sources/scalac/symtab/classfile/ClassParser.java b/sources/scalac/symtab/classfile/ClassParser.java new file mode 100644 index 0000000000..9948ba33cf --- /dev/null +++ b/sources/scalac/symtab/classfile/ClassParser.java @@ -0,0 +1,111 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.*; +import scalac.symtab.*; +import scalac.util.*; +import java.io.*; + + +public class ClassParser extends Type.LazyType { + + /** the global compilation environment + */ + protected Global global; + + public ClassParser(Global global) { + this.global = global; + } + + /** complete class symbol c by loading the class + */ + public void complete(Symbol c) { + //System.out.println("loading " + c);//DEBUG + try { + long msec = System.currentTimeMillis(); + String filename = externalizeFileName(c.fullName()) + ".class"; + AbstractFile f = global.classPath.openFile(filename); + if (f == null) + global.error("could not read class " + c); + else { + new ClassfileParser(global, new AbstractFileReader(f), c).parse(); + global.operation("loaded " + f.getPath() + " in " + + (System.currentTimeMillis() - msec) + "ms"); + //for (Definition e = c.locals().elems; e != null; e = e.sibling) + // if (e.def.kind == TYP) + // e.def.complete(); + } + } catch (IOException e) { + e.printStackTrace(); + global.error("i/o error while loading " + c); + c.setInfo(Type.ErrorType); + } + } + + /** return external representation of file name s, + * converting '.' to File.separatorChar + */ + public String externalizeFileName(Name n) { + if ((n == null) || (n.length() == 0)) + return "."; + byte[] ascii = n.toAscii(); + String s = SourceRepresentation.ascii2string( + ascii, 0, ascii.length); + return s.replace('.', File.separatorChar); + } + + public Type.LazyType staticsParser(Symbol clazz) { + return new StaticsParser(clazz); + } + + public Type.LazyType aliasParser(Symbol alias) { + return new AliasParser(alias); + } + + class StaticsParser extends Type.LazyType { + Symbol clazz; + + StaticsParser(Symbol clazz) { + this.clazz = clazz; + } + + public void complete(Symbol statics) { + ClassParser.this.complete(clazz); + } + } + + class AliasParser extends Type.LazyType { + Symbol alias; + + AliasParser(Symbol alias) { + this.alias = alias; + } + + public void complete(Symbol c) { + try { + long msec = System.currentTimeMillis(); + String filename = externalizeFileName(alias.fullName()) + ".class"; + AbstractFile f = global.classPath.openFile(filename); + if (f == null) + global.error("could not read class " + c); + else { + new ClassfileParser(global, new AbstractFileReader(f), c).parse(); + global.operation("loaded " + f.getPath() + " in " + + (System.currentTimeMillis() - msec) + "ms"); + } + } catch (IOException e) { + e.printStackTrace(); + global.error("i/o error while loading " + c); + c.setInfo(Type.ErrorType); + } + } + } +} + diff --git a/sources/scalac/symtab/classfile/ClassfileConstants.java b/sources/scalac/symtab/classfile/ClassfileConstants.java new file mode 100644 index 0000000000..16b9f5af44 --- /dev/null +++ b/sources/scalac/symtab/classfile/ClassfileConstants.java @@ -0,0 +1,57 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.util.Name; + +public interface ClassfileConstants { + + int JAVA_MAGIC = 0xCAFEBABE; + int JAVA_MAJOR_VERSION = 45; + int JAVA_MINOR_VERSION = 3; + + int CONSTANT_UTF8 = 1; + int CONSTANT_UNICODE = 2; + int CONSTANT_INTEGER = 3; + int CONSTANT_FLOAT = 4; + int CONSTANT_LONG = 5; + int CONSTANT_DOUBLE = 6; + int CONSTANT_CLASS = 7; + int CONSTANT_STRING = 8; + int CONSTANT_FIELDREF = 9; + int CONSTANT_METHODREF = 10; + int CONSTANT_INTFMETHODREF = 11; + int CONSTANT_NAMEANDTYPE = 12; + + int BAD_ATTR = 0x00000; + int SOURCEFILE_ATTR = 0x00001; + int SYNTHETIC_ATTR = 0x00002; + int DEPRECATED_ATTR = 0x00004; + int CODE_ATTR = 0x00008; + int EXCEPTIONS_ATTR = 0x00010; + int CONSTANT_VALUE_ATTR = 0x00020; + int LINE_NUM_TABLE_ATTR = 0x00040; + int LOCAL_VAR_TABLE_ATTR = 0x00080; + int INNERCLASSES_ATTR = 0x08000; + int META_ATTR = 0x10000; + int SCALA_ATTR = 0x20000; + + Name SOURCEFILE_N = Name.fromString("SourceFile"); + Name SYNTHETIC_N = Name.fromString("Synthetic"); + Name DEPRECATED_N = Name.fromString("Deprecated"); + Name CODE_N = Name.fromString("Code"); + Name EXCEPTIONS_N = Name.fromString("Exceptions"); + Name CONSTANT_VALUE_N = Name.fromString("ConstantValue"); + Name LINE_NUM_TABLE_N = Name.fromString("LineNumberTable"); + Name LOCAL_VAR_TABLE_N = Name.fromString("LocalVariableTable"); + Name INNERCLASSES_N = Name.fromString("InnerClasses"); + Name META_N = Name.fromString("JacoMeta"); + Name SCALA_N = Name.fromString("ScalaSignature"); + Name CONSTR_N = Name.fromString("<init>"); +} diff --git a/sources/scalac/symtab/classfile/ClassfileParser.java b/sources/scalac/symtab/classfile/ClassfileParser.java new file mode 100644 index 0000000000..956c83968f --- /dev/null +++ b/sources/scalac/symtab/classfile/ClassfileParser.java @@ -0,0 +1,239 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.*; +import scalac.util.*; +import scalac.symtab.*; +import java.io.*; +import java.util.*; + +//todo: don't keep statics module in scope. + +public class ClassfileParser implements ClassfileConstants { + + static final int CLASS_ATTR = SOURCEFILE_ATTR + | INNERCLASSES_ATTR + | SYNTHETIC_ATTR + | DEPRECATED_ATTR + | META_ATTR + | SCALA_ATTR; + static final int METH_ATTR = CODE_ATTR + | EXCEPTIONS_ATTR + | SYNTHETIC_ATTR + | DEPRECATED_ATTR + | META_ATTR; + static final int FIELD_ATTR = CONSTANT_VALUE_ATTR + | SYNTHETIC_ATTR + | DEPRECATED_ATTR + | META_ATTR; + + protected Global global; + protected AbstractFileReader in; + protected Symbol c; + protected Type ctype; + protected Scope locals; + protected Scope statics; + protected Scope constr; + protected JavaTypeFactory make; + protected Signatures sigs; + protected ConstantPool pool; + protected AttributeParser attrib; + protected Definitions defs; + protected int phaseId; + + + public ClassfileParser(Global global, AbstractFileReader in, Symbol c) { + this.global = global; + this.in = in; + this.c = c; + this.ctype = Type.TypeRef(c.owner().thisType(), c, Type.EMPTY_ARRAY); + this.make = new JavaTypeCreator(global); + this.sigs = new Signatures(global, make); + this.pool = new ConstantPool(in, sigs); + this.attrib = new AttributeParser(in, pool, this); + this.defs = global.definitions; + this.phaseId = global.POST_ANALYZER_PHASE_ID; + } + + + /** parse the classfile and throw IO exception if there is an + * error in the classfile structure + */ + public void parse() throws IOException { + try { + if (in.nextInt() != JAVA_MAGIC) + throw new IOException("illegal start of class file"); + int minorVersion = in.nextChar(); + int majorVersion = in.nextChar(); + if ((majorVersion < JAVA_MAJOR_VERSION) || + ((majorVersion == JAVA_MAJOR_VERSION) && + (minorVersion < JAVA_MINOR_VERSION))) + throw new IOException("class file has wrong version " + + majorVersion + "." + minorVersion + ", should be " + + JAVA_MAJOR_VERSION + "." + JAVA_MINOR_VERSION); + pool.indexPool(); + int flags = in.nextChar(); + Name name = readClassName(in.nextChar()); + if (c.fullName() != name) + throw new IOException("class file '" + c.fullName() + + "' contains wrong class " + name); + // todo: correct flag transition + c.flags = transFlags(flags); + if ((c.flags & Modifiers.ABSTRACT) != 0) + c.flags = c.flags & ~Modifiers.ABSTRACT | Modifiers.ABSTRACTCLASS; + Type supertpe = readClassType(in.nextChar()); + Type[] basetpes = new Type[in.nextChar() + 1]; + this.locals = new Scope(); + this.statics = new Scope(); + this.constr = new Scope(); + // set type of class + Type classType = Type.compoundType(basetpes, locals, c); + c.setInfo(classType, phaseId); + // set type of statics + Symbol staticsClass = c.module().moduleClass(); + Type staticsInfo = Type.compoundType(Type.EMPTY_ARRAY, statics, staticsClass); + staticsClass.setInfo(staticsInfo, phaseId); + c.module().setInfo(Type.TypeRef(staticsClass.owner().thisType(), + staticsClass, Type.EMPTY_ARRAY)); + basetpes[0] = supertpe; + for (int i = 1; i < basetpes.length; i++) + basetpes[i] = readClassType(in.nextChar()); + int fieldCount = in.nextChar(); + for (int i = 0; i < fieldCount; i++) + parseField(); + int methodCount = in.nextChar(); + for (int i = 0; i < methodCount; i++) + parseMethod(); + // set constructor type to the declared type + Symbol[] constrs = constr.elements(); + if (constrs.length != 0) { + assert constrs.length == 1; + c.constructor().setInfo(constrs[0].info(), phaseId); + } else { + Type constrtype = ((c.flags & Modifiers.INTERFACE) != 0) + ? Type.PolyType(Symbol.EMPTY_ARRAY, ctype) + : Type.MethodType(new Symbol[]{Symbol.NONE}, ctype); + c.constructor().setInfo(constrtype, phaseId); + } + attrib.readAttributes(c, classType, CLASS_ATTR); + //System.out.println("dynamic class: " + c); + //System.out.println("statics class: " + staticsClass); + //System.out.println("module: " + c.module()); + //System.out.println("modules class: " + c.module().type().symbol()); + } catch (RuntimeException e) { + e.printStackTrace(); + throw new IOException("bad class file (" + e.getMessage() + ")"); + } + } + + /** convert Java modifiers into Scala flags + */ + public int transFlags(int flags) { + int res = 0; + if (((flags & 0x0007) == 0) || + ((flags & 0x0002) != 0)) + res |= Modifiers.PRIVATE; + else if ((flags & 0x0004) != 0) + res |= Modifiers.PROTECTED; + if ((flags & 0x0400) != 0) + res |= Modifiers.ABSTRACT; + if ((flags & 0x0010) != 0) + res |= Modifiers.FINAL; + if ((flags & 0x0200) != 0) + res |= Modifiers.INTERFACE | Modifiers.ABSTRACTCLASS; + return res | Modifiers.JAVA; + } + + /** read a class name + */ + protected Name readClassName(int i) { + return (Name)pool.readPool(i); + } + + /** read a class name and return the corresponding class type + */ + protected Type readClassType(int i) { + if (i == 0) + return defs.ANY_TYPE; + Type res = defs.getJavaType((Name)pool.readPool(i)); + if (res == Type.ErrorType) + global.error("unknown class reference " + pool.readPool(i)); + if (res.symbol() == defs.JAVA_OBJECT_TYPE.symbol()) + return defs.ANYREF_TYPE; + else + return res; + } + + /** read a signature and return it as a type + */ + protected Type readType(int i) { + Name sig = pool.readExternal(i); + return sigs.sigToType(Name.names, sig.index, sig.length()); + } + + /** read a field + */ + protected void parseField() { + int flags = in.nextChar(); + Name name = (Name)pool.readPool(in.nextChar()); + Type type = readType(in.nextChar()); + int mods = transFlags(flags); + if ((flags & 0x0010) == 0) + mods |= Modifiers.MUTABLE; + Symbol owner = c; + if ((flags & 0x0008) != 0) + owner = c.module().moduleClass(); + Symbol s = new TermSymbol(Position.NOPOS, name, owner, mods); + s.setInfo(type, phaseId); + attrib.readAttributes(s, type, FIELD_ATTR); + ((flags & 0x0008) != 0 ? statics : locals).enterOrOverload(s); + } + + /** read a method + */ + protected void parseMethod() { + int flags = in.nextChar(); + Name name = (Name)pool.readPool(in.nextChar()); + Type type = readType(in.nextChar()); + if (CONSTR_N.equals(name)) { + Symbol s = TermSymbol.newConstructor(c, transFlags(flags)); + // kick out protected, package visible or + // private constructors + if (((flags & 0x0004) != 0) || + ((flags & 0x0002) != 0) || + ((flags & 0x0007) == 0)) { + attrib.readAttributes(s, type, METH_ATTR); + return; + } + switch (type) { + case MethodType(Symbol[] vparams, _): + if (c == defs.OBJECT_CLASS) + type = Type.PolyType(Symbol.EMPTY_ARRAY, ctype); + else + type = Type.MethodType(vparams, ctype); + break; + default: + throw new ApplicationError(); + } + s.setInfo(type, phaseId); + attrib.readAttributes(s, type, METH_ATTR); + //System.out.println("-- enter " + s); + constr.enterOrOverload(s); + } else { + Symbol s = new TermSymbol( + Position.NOPOS, name, + ((flags & 0x0008) != 0) ? c.module().moduleClass() : c, + transFlags(flags)); + s.setInfo(type, phaseId); + attrib.readAttributes(s, type, METH_ATTR); + ((flags & 0x0008) != 0 ? statics : locals).enterOrOverload(s); + } + } +} diff --git a/sources/scalac/symtab/classfile/ConstantPool.java b/sources/scalac/symtab/classfile/ConstantPool.java new file mode 100644 index 0000000000..4260b9e6ad --- /dev/null +++ b/sources/scalac/symtab/classfile/ConstantPool.java @@ -0,0 +1,183 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.*; +import scalac.symtab.*; +import scalac.util.*; + +public class ConstantPool implements ClassfileConstants { + + AbstractFileReader in; + Signatures sigparser; + + /** the objects of the constant pool + */ + protected Object[] poolObj; + + /** for every constant pool entry, an index into in.buf where the + * defining section of the entry is found + */ + protected int[] poolIdx; + + /** constructor + */ + protected ConstantPool(AbstractFileReader in, Signatures sigparser) { + this.in = in; + this.sigparser = sigparser; + } + + /** index all constant pool entries, writing their start + * addresses into poolIdx + */ + public void indexPool() { + poolIdx = new int[in.nextChar()]; + poolObj = new Object[poolIdx.length]; + int i = 1; + while (i < poolIdx.length) { + poolIdx[i++] = in.bp; + byte tag = in.nextByte(); + switch (tag) { + case CONSTANT_UTF8: + case CONSTANT_UNICODE: { + int len = in.nextChar(); + in.skip(len); + break; + } + case CONSTANT_CLASS: + case CONSTANT_STRING: + in.skip(2); + break; + case CONSTANT_FIELDREF: + case CONSTANT_METHODREF: + case CONSTANT_INTFMETHODREF: + case CONSTANT_NAMEANDTYPE: + case CONSTANT_INTEGER: + case CONSTANT_FLOAT: + in.skip(4); + break; + case CONSTANT_LONG: + case CONSTANT_DOUBLE: + in.skip(8); + i++; + break; + default: + throw new RuntimeException("bad constant pool tag: " + tag + + " at " + (in.bp - 1)); + } + } + } + + /** if name is an array type or class signature, return the + * corresponding type; otherwise return a class definition with given name + */ + protected Object classOrType(Name name) { + if ((name.sub(0) == '[') || (name.sub(name.length() - 1) == ';')) { + byte[] ascii = name.toAscii(); + return sigparser.sigToType(ascii, 0, ascii.length); + } else + return name; + } + + /** read constant pool entry at start address i, use poolObj as a cache. + */ + public Object readPool(int i) { + if (poolObj[i] != null) + return poolObj[i]; + int index = poolIdx[i]; + if (index == 0) + return null; + switch (in.byteAt(index)) { + case CONSTANT_UTF8: + poolObj[i] = Name.fromAscii(in.buf, index + 3, in.getChar(index + 1)); + break; + + case CONSTANT_UNICODE: + throw new RuntimeException("can't read unicode strings in classfiles"); + + case CONSTANT_CLASS: + poolObj[i] = classOrType(readExternal(in.getChar(index + 1))); + break; + + case CONSTANT_FIELDREF: { + //Symbol owner = (Symbol)readPool(in.getChar(index + 1)); + //NameAndType nt = (NameAndType)readPool(in.getChar(index + 3)); + //poolObj[i] = new TermSymbol(Kinds.VAR, Position.NOPOS, nt.name, owner, 0) + // .type(sigparser.sigToType(Name.names, nt.sig.index, nt.sig.length())); + throw new RuntimeException("can't read constant_fieldrefs in classfiles"); + } + + case CONSTANT_METHODREF: + case CONSTANT_INTFMETHODREF: { + //Symbol owner = (Symbol)readPool(in.getChar(index + 1)); + //NameAndType nt = (NameAndType)readPool(in.getChar(index + 3)); + //poolObj[i] = new TermSymbol(Kinds.FUN, Position.NOPOS, nt.name, owner, 0) + // .type(sigparser.sigToType(Name.names, nt.sig.index, nt.sig.length())); + throw new RuntimeException("can't read constant_methodrefs in classfiles"); + } + + case CONSTANT_NAMEANDTYPE: + poolObj[i] = new NameAndType((Name)readPool(in.getChar(index + 1)), + readExternal(in.getChar(index + 3))); + break; + + case CONSTANT_STRING: + case CONSTANT_INTEGER: + case CONSTANT_FLOAT: + case CONSTANT_LONG: + case CONSTANT_DOUBLE: + throw new RuntimeException("can't read constants in classfiles"); + + default: + throw new RuntimeException("bad constant pool tag: " + in.byteAt(index)); + } + return poolObj[i]; + } + + /** return internal representation of buf[offset..offset+len-1], + * converting '/' to '.' + */ + public byte[] internalize(byte[] buf, int offset, int len) { + byte[] translated = new byte[len]; + for (int j = 0; j < len; j++) { + byte b = buf[offset + j]; + if (b == '/') + translated[j] = '.'; + else + translated[j] = b; + } + return translated; + } + + /** read a constant pool string and convert to internal representation. + */ + public Name readExternal(int i) { + if (poolObj[i] == null) { + int index = poolIdx[i]; + if (in.byteAt(index) == CONSTANT_UTF8) { + int len = in.getChar(index + 1); + byte[] translated = internalize(in.buf, index + 3, len); + poolObj[i] = Name.fromAscii(translated, 0, len); + } + } + return (Name)poolObj[i]; + } + + /** the name and type signature of a method or field + */ + public static final class NameAndType { + public Name name; + public Name sig; + + public NameAndType(Name name, Name sig) { + this.name = name; + this.sig = sig; + } + } +} diff --git a/sources/scalac/symtab/classfile/JavaTypeCreator.java b/sources/scalac/symtab/classfile/JavaTypeCreator.java new file mode 100644 index 0000000000..5bdc9402ba --- /dev/null +++ b/sources/scalac/symtab/classfile/JavaTypeCreator.java @@ -0,0 +1,82 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.Global; +import scalac.util.*; +import scalac.symtab.*; +import Type.*; + + +public class JavaTypeCreator implements JavaTypeFactory { + + protected Global global; + + public JavaTypeCreator(Global global) { + this.global = global; + } + + public Type byteType() { + return global.definitions.BYTE_TYPE; + } + + public Type shortType() { + return global.definitions.SHORT_TYPE; + } + + public Type charType() { + return global.definitions.CHAR_TYPE; + } + + public Type intType() { + return global.definitions.INT_TYPE; + } + + public Type longType() { + return global.definitions.LONG_TYPE; + } + + public Type floatType() { + return global.definitions.FLOAT_TYPE; + } + + public Type doubleType() { + return global.definitions.DOUBLE_TYPE; + } + + public Type booleanType() { + return global.definitions.BOOLEAN_TYPE; + } + + public Type voidType() { + return global.definitions.UNIT_TYPE; + } + + public Type classType(Name classname) { + return global.definitions.getJavaType(classname); + } + + public Type arrayType(Type elemtpe) { + return global.definitions.arrayType(elemtpe); + } + + public Type methodType(Type[] argtpes, Type restpe, Type[] thrown) { + Symbol[] args = new Symbol[argtpes.length]; + for (int i = 0; i < args.length; i++) { + args[i] = new TermSymbol( + Position.NOPOS, Name.fromString("x" + i), Symbol.NONE, Modifiers.PARAM); + args[i].setInfo(argtpes[i]); + } + return new MethodType(args, restpe); + } + + public Type packageType(Name packagename) { + return null; + } +} diff --git a/sources/scalac/symtab/classfile/JavaTypeFactory.java b/sources/scalac/symtab/classfile/JavaTypeFactory.java new file mode 100644 index 0000000000..8205055bc9 --- /dev/null +++ b/sources/scalac/symtab/classfile/JavaTypeFactory.java @@ -0,0 +1,28 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.symtab.Type; +import scalac.util.Name; + +public interface JavaTypeFactory { + Type byteType(); + Type shortType(); + Type charType(); + Type intType(); + Type longType(); + Type floatType(); + Type doubleType(); + Type booleanType(); + Type voidType(); + Type classType(Name classname); + Type arrayType(Type elemtpe); + Type methodType(Type[] argtpes, Type restpe, Type[] thrown); + Type packageType(Name packagename); +} diff --git a/sources/scalac/symtab/classfile/PackageParser.java b/sources/scalac/symtab/classfile/PackageParser.java new file mode 100644 index 0000000000..50fcb46b00 --- /dev/null +++ b/sources/scalac/symtab/classfile/PackageParser.java @@ -0,0 +1,129 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.*; +import scalac.symtab.*; +import scalac.util.*; +import java.io.*; + +public class PackageParser extends Type.LazyType { + + /** the global compilation environment + */ + protected Global global; + + /** the class parser + */ + public ClassParser classCompletion; + + public PackageParser(Global global) { + this.global = global; + this.classCompletion = new ClassParser(global); + } + + /** complete package type symbol p by loading all package members + */ + public void complete(Symbol p) { + long msec = System.currentTimeMillis(); + Scope members = new Scope(); + String dirname = null; + Name name = p.fullName(); + if (name.length() == 0) { + // includeMembers(AbstractFile.open(null, "."), p, members, false); + } else { + dirname = externalizeFileName(name); + assert !dirname.startsWith("com") : p;//debug + if (!dirname.endsWith("/")) + dirname += "/"; + } + String[] base = global.classPath.components(); + for (int i = 0; i < base.length; i++) { + includeMembers( + AbstractFile.open(base[i], dirname), p, members, dirname != null); + } + p.setInfo(Type.compoundType(Type.EMPTY_ARRAY, members, p)); + if (dirname == null) + dirname = "anonymous package"; + global.operation("scanned " + dirname + " in " + + (System.currentTimeMillis() - msec) + "ms"); + } + + /** read directory of a classpath directory and include members + * in package/module scope + */ + protected void includeMembers(AbstractFile dir, Symbol p, Scope locals, + boolean inclClasses) { + if (dir == null) + return; + String[] filenames = null; + try { + if ((filenames = dir.list()) == null) + return; + for (int j = 0; j < filenames.length; j++) { + String fname = filenames[j]; + if (inclClasses && fname.endsWith(".class")) { + Name n = Name.fromString(fname.substring(0, fname.length() - 6)) + .toTypeName(); + ClassSymbol clazz = new ClassSymbol(n, p, classCompletion); + clazz.constructor().setInfo( + classCompletion.staticsParser(clazz)); + // enter class + locals.enter(clazz); + locals.enter(clazz.constructor()); + // enter module, except for scala.Object class + // todo: why not there also?. + if (!(n == Names.Object.toTypeName() && + p.fullName().toTermName() == Names.scala)) { + Scope.Entry e = locals.lookupEntry(clazz.module().name); + if (e != Scope.Entry.NONE) { + // we already have a package of the same name; delete it + locals.unlink(e); + } + locals.enter(clazz.module()); + } + } else if (fname.endsWith("/") && !fname.equals("META-INF/")) { + Name n = Name.fromString(fname.substring(0, fname.length() - 1)); + if (locals.lookup(n) == Symbol.NONE) { + TermSymbol module = TermSymbol.newJavaPackageModule(n, p, this); + locals.enter(module); + } + } else if (fname.endsWith(".scala")) { + Name n = Name.fromString(fname.substring(0, fname.length() - 6)) + .toTypeName(); + if (locals.lookup(n) == Symbol.NONE) { + SourceCompleter completer = new SourceCompleter(global, + dir.getPath() + File.separatorChar + fname); + ClassSymbol clazz = new ClassSymbol(n, p, completer); + clazz.constructor().setInfo(completer); + clazz.module().setInfo(completer); + // enter class + locals.enter(clazz); + locals.enter(clazz.constructor()); + locals.enter(clazz.module()); + } + } + } + } catch (IOException e) { + } + } + + /** return external representation of file name s, + * converting '.' to File.separatorChar + */ + + public String externalizeFileName(Name n) { + if ((n == null) || (n.length() == 0)) + return "."; + byte[] ascii = n.toAscii(); + String s = SourceRepresentation.ascii2string( + ascii, 0, ascii.length); + return s.replace('.', File.separatorChar); + } +} diff --git a/sources/scalac/symtab/classfile/Signatures.java b/sources/scalac/symtab/classfile/Signatures.java new file mode 100644 index 0000000000..9676882aed --- /dev/null +++ b/sources/scalac/symtab/classfile/Signatures.java @@ -0,0 +1,122 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.symtab.classfile; + +import scalac.*; +import scalac.symtab.*; +import scalac.util.*; +import java.util.*; +import Type.*; + +public class Signatures { + + /** signature constants + */ + Name BYTE_SIG = Name.fromString("B"); + Name SHORT_SIG = Name.fromString("S"); + Name CHAR_SIG = Name.fromString("C"); + Name INT_SIG = Name.fromString("I"); + Name LONG_SIG = Name.fromString("J"); + Name FLOAT_SIG = Name.fromString("F"); + Name DOUBLE_SIG = Name.fromString("D"); + Name BOOLEAN_SIG = Name.fromString("Z"); + Name VOID_SIG = Name.fromString("V"); + Name CLASS_SIG = Name.fromString("L"); + Name ARRAY_SIG = Name.fromString("["); + Name ARGBEGIN_SIG = Name.fromString("("); + Name ARGEND_SIG = Name.fromString(")"); + + Global global; + JavaTypeFactory make; + + + public Signatures(Global global, JavaTypeFactory make) { + this.make = make; + this.global = global; + } + + /** the type represented by signature[offset..]. + */ + protected byte[] signature; + protected int sigp; + protected int limit; + + public Type sigToType(byte[] sig, int offset, int len) { + signature = sig; + sigp = offset; + limit = offset + len; + return sigToType(); + } + + protected Type sigToType() { + switch (signature[sigp]) { + case 'B': + sigp++; + return make.byteType(); + case 'C': + sigp++; + return make.charType(); + case 'D': + sigp++; + return make.doubleType(); + case 'F': + sigp++; + return make.floatType(); + case 'I': + sigp++; + return make.intType(); + case 'J': + sigp++; + return make.longType(); + case 'L': + sigp++; + int start = sigp; + while (signature[sigp] != ';') + sigp++; + return make.classType(Name.fromAscii(signature, start, (sigp++) - start)); + case 'S': + sigp++; + return make.shortType(); + case 'V': + sigp++; + return make.voidType(); + case 'Z': + sigp++; + return make.booleanType(); + case '[': + sigp++; + while (('0' <= signature[sigp]) && (signature[sigp] <= '9')) + sigp++; + return make.arrayType(sigToType()); + case '(': + return make.methodType(sigToTypes(')'), sigToType(), Type.EMPTY_ARRAY); + default: + global.error("bad signature: " + + SourceRepresentation.ascii2string(signature, sigp, 1)); + return Type.ErrorType; + } + } + + protected Type[] sigToTypes(char terminator) { + sigp++; + return sigToTypes(terminator, 0); + } + + protected Type[] sigToTypes(char terminator, int i) { + if (signature[sigp] == terminator) { + sigp++; + return new Type[i]; + } else { + Type t = sigToType(); + Type[] vec = sigToTypes(terminator, i+1); + vec[i] = t; + return vec; + } + } +} diff --git a/sources/scalac/transformer/LambdaLift.java b/sources/scalac/transformer/LambdaLift.java new file mode 100644 index 0000000000..56cc92e6de --- /dev/null +++ b/sources/scalac/transformer/LambdaLift.java @@ -0,0 +1,508 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.transformer; + +import java.io.*; +import java.util.HashMap; +import scalac.*; +import scalac.util.*; +import scalac.ast.*; +import scalac.symtab.*; +import Tree.*; + +/** A lambda lifting transformer + * + * @author Martin Odersky + * @version 1.0 + * + * What it does: + * Lift every class and function that's contained in another function + * out to be a member to the next enclosing class. + * Pass free variables and type variables as parameters to class constructors. + * Variables and values that are free in some function or class + * are given new unique names. + * Free mutable variables are converted to reference cells. + * All types are updated so that proxies are passed for additional free + * type variables. + */ +public class LambdaLift extends OwnerTransformer + implements Modifiers, Kinds { + + final Global global; + final Definitions definitions; + final FreeVars free; + final LambdaLiftPhase descr; + + public LambdaLift(Global global, LambdaLiftPhase descr) { + super(global, descr); + this.global = global; + this.definitions = global.definitions; + this.free = new FreeVars(global, descr); + this.descr = descr; + } + + public void apply(Unit unit) { + global.log(unit.source.toString()); + free.initialize(unit); + unit.body = transformTemplateStats(unit.body, currentOwner); + } + + /** If `sym' is a class, return its primary constructor; + * otherwise current symbol itself + */ + static Symbol asFunction(Symbol sym) { + return sym.kind == CLASS ? sym.constructor() : sym; + } + + /** `asFunction' applied to the next enclosing function or class owner. + */ + static Symbol enclFun(Symbol owner) { + Symbol sym = owner; + while (!asFunction(sym).isMethod()) sym = sym.owner(); + return asFunction(sym); + } + + /** Return SymSet from a hashmap. + */ + private static SymSet get(HashMap f, Symbol sym) { + SymSet ss = (SymSet) f.get(sym); + return ss == null ? SymSet.EMPTY : ss; + } + + /** Compute free variables map `fvs'. + * Also assign unique names to all + * value/variable/let symbols that are free in some function or class, and to + * all class/function symbols that are owned by some function. + */ + static class FreeVars extends OwnerTransformer { + + private Unit unit; + + public FreeVars(Global global, PhaseDescriptor descr) { + super(global, descr); + } + + /** A hashtable storing free variables of functions and class constructors. + */ + private HashMap/*<Symbol,SymSet>*/ fvs; + + /** A hashtable storing free type variables of functions and class constructors. + */ + private HashMap/*<Symbol,SymSet>*/ ftvs; + + /** A hashtable storing calls between functions and class constructors + */ + private HashMap/*<Symbol,SymSet>*/ called; + + /** The set of symbols that need to be renamed. + */ + private SymSet renamable; + + /** A flag to indicate whether new free variables have been found + */ + private boolean changedFreeVars; + + /** A flag to indicate whether we are in propagation phase + * (used in assertion). + */ + private boolean propagatePhase; + + /** Insert `sym' into the set of free variables of `owner' + */ + private void putFree(Symbol owner, Symbol sym) { + assert owner.isLocal(); + HashMap f = sym.isType() ? ftvs : fvs; + SymSet ss = get(f, owner); + if (!ss.contains(sym)) { + f.put(owner, ss.incl(sym)); + changedFreeVars = true; + if (global.debug) global.log(sym + " is free in " + owner); + } + } + + /** Insert `to' into the set of functions called by `from' + */ + private void putCall(Symbol from, Symbol to) { + SymSet ss = get(called, from); + if (!ss.contains(to)) { + called.put(from, ss.incl(to)); + } + } + + /** Mark symbol `sym' as being free in `owner', unless `sym' + * is defined in `owner' or there is a class between `owner's owner + * and the owner of `sym'. + * Return `true' if there is no class between `owner' and + * the owner of sym. + */ + private boolean markFree(Symbol sym, Symbol owner) { + //if (global.debug) global.log("mark " + sym + " free in " + owner);//DEBUG + if (owner.kind == NONE) { + assert propagatePhase : sym + " in " + sym.owner(); + return false; + } else if (sym.owner() == owner) { + return true; + } else if (markFree(sym, owner.owner())) { + Symbol fowner = asFunction(owner); + if (fowner.isMethod()) { + putFree(fowner, sym); + renamable = renamable.incl(sym); + if (sym.isVariable()) sym.flags |= CAPTURED; + } + return owner.kind != CLASS; + } else { + return false; + } + } + + /** Assign unique name to symbol. + * If symbol is a class assign same name to its primary constructor. + */ + private void makeUnique(Symbol sym) { + Name newname = global.freshNameCreator.newName(sym.name); + sym.name = newname; + if (sym.kind == CLASS) + sym.constructor().name = newname.toConstrName(); + } + + private Type.Map traverseTypeMap = new Type.Map() { + public Type apply(Type tp) { + switch (tp) { + case TypeRef(ThisType(_), Symbol sym, Type[] targs): + if (sym.isLocal() && sym.kind == TYPE) + markFree(sym, currentOwner); + } + return map(tp); + } + }; + + public Tree transform(Tree tree) { + global.debugPrinter.print("free ").print(tree).println().end();//debug + traverseTypeMap.apply(tree.type.widen()); + Symbol sym = tree.symbol(); + switch(tree) { + case ClassDef(_, _, _, _, _, _): + case DefDef(_, _, _, _, _, _): + if (sym.isLocal()) { + renamable = renamable.incl(sym); + } + return super.transform(tree); + + case Ident(Name name): + assert sym.owner() != null : sym + " " + name;//debug + if (sym.isLocal()) { + if (sym.isMethod()) { + Symbol f = enclFun(currentOwner); + if (f.name.length() > 0) // it is not a template function { + putCall(f, sym); + } else if (sym.kind == VAL || sym.kind == TYPE) { + markFree(sym, currentOwner); + } + } + return tree; + + default: + return super.transform(tree); + } + } + + /** Propagate free fariables from all called functions. + */ + void propagateFvs(HashMap fvs) { + Object[] fs = called.keySet().toArray(); + for (int i = 0; i < fs.length; i++) { + Symbol f = (Symbol) fs[i]; + Symbol[] calledFromF = get(called, f).toArray(); + for (int j = 0; j < calledFromF.length; j++) { + //System.out.println(f + " calls " + calledFromF[j]);//DEBUG + Symbol[] newFvs = get(fvs, calledFromF[j]).toArray(); + for (int k = 0; k < newFvs.length; k++) { + markFree(newFvs[k], f); + } + } + } + } + + /** This method re-enters all free variables into their free variable sets + * This is necessary because the names of these variables (and therefore their + * `isLess' order have changed. + */ + void restoreFvs(HashMap fvs) { + Object[] fs = fvs.keySet().toArray(); + for (int i = 0; i < fs.length; i++) { + Symbol f = (Symbol) fs[i]; + Symbol[] elems = get(fvs, f).toArray(); + SymSet elems1 = SymSet.EMPTY; + for (int j = 0; j < elems.length; j++) + elems1 = elems1.incl(elems[j]); + fvs.put(f, elems1); + } + } + + /** Compute a mapping from symbols to their free variables + * in hashtable `fvs'. Also rename all variables that need it. + */ + public void initialize(Unit unit) { + this.unit = unit; + fvs = new HashMap(); + ftvs = new HashMap(); + called = new HashMap(); + renamable = SymSet.EMPTY; + apply(unit); + + propagatePhase = true; + do { + changedFreeVars = false; + propagateFvs(fvs); + propagateFvs(ftvs); + } while (changedFreeVars); + + Symbol[] ss = renamable.toArray(); + for (int i = 0; i < ss.length; i++) { + makeUnique(ss[i]); + } + + restoreFvs(fvs); + restoreFvs(ftvs); + } + } + + private TreeList liftedDefs; + + /** Transform template and add lifted definitions to it. + */ + public Tree[] transformTemplateStats(Tree[] stats, Symbol tsym) { + TreeList prevLiftedDefs = liftedDefs; + liftedDefs = new TreeList(); + TreeList stats1 = new TreeList(super.transformTemplateStats(stats, tsym)); + stats1.append(liftedDefs); + liftedDefs = prevLiftedDefs; + return stats1.toArray(); + } + + public Tree transform(Tree tree) { + global.debugPrinter.print("lifting ").print(tree).println().end();//debug + tree.type = descr.transform(tree.type, currentOwner); + switch (tree) { + case ClassDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, Tree tpe, Template impl): + Symbol sym = tree.symbol(); + if (sym.isLocal()) { + Symbol[] newtparams = ftvsParams(sym.constructor()); + Symbol[] newparams = fvsParams(sym.constructor()); + liftSymbol(sym, newtparams, newparams); + Tree tree1 = copy.ClassDef( + tree, mods, sym.name, + addTypeParams(transform(tparams, sym), newtparams), + new ValDef[][]{addParams(transform(vparams, sym)[0], newparams)}, + transform(tpe), + transform(impl, sym)); + liftedDefs.append(tree1); + return Tree.Empty; + } else { + return copy.ClassDef( + tree, mods, sym.name, + transform(tparams, sym), transform(vparams, sym), transform(tpe), + transform(impl, sym)); + } + + case DefDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, Tree tpe, Tree rhs): + Symbol sym = tree.symbol(); + if (sym.isLocal()) { + Symbol[] newtparams = ftvsParams(sym); + Symbol[] newparams = fvsParams(sym); + liftSymbol(sym, newtparams, newparams); + Tree tree1 = copy.DefDef( + tree, mods, sym.name, + addTypeParams(transform(tparams, sym), newtparams), + new ValDef[][]{addParams(transform(vparams, sym)[0], newparams)}, + transform(tpe), + transform(rhs, sym)); + liftedDefs.append(tree1); + return Tree.Empty; + } else { + return copy.DefDef( + tree, mods, sym.name, + transform(tparams, sym), transform(vparams, sym), transform(tpe), + transform(rhs, sym)); + } + + case ValDef(int mods, Name name, Tree tpe, Tree rhs): + Symbol sym = tree.symbol(); + Name name1 = sym.name; + Tree tpe1 = transform(tpe); + Tree rhs1 = transform(rhs, sym); + if ((sym.flags & CAPTURED) != 0) { + assert sym.isLocal(); + Type unboxedType = sym.typeAt(descr.nextPhase); + Type boxedType = descr.refType(unboxedType); + tpe1 = gen.mkType(tpe.pos, boxedType); + rhs1 = gen.New( + rhs.pos, + definitions.SCALA_TYPE, + boxedType.symbol(), + new Type[]{unboxedType}, + new Tree[]{rhs1}); + } + return copy.ValDef(tree, mods, name1, tpe1, rhs1); + + case Apply(Tree fn, Tree[] args): + Symbol fsym = TreeInfo.methSymbol(fn); + Tree fn1 = transform(fn); + switch (fn1) { + case TypeApply(Tree fn2, Tree[] targs): + fn1 = copy.TypeApply( + fn1, fn2, addFreeArgs(tree.pos, get(free.ftvs, fsym), targs)); + break; + default: + Tree[] targs = addFreeArgs( + tree.pos, get(free.ftvs, fsym), Tree.EMPTY_ARRAY); + if (targs.length > 0) fn1 = gen.TypeApply(fn1, targs); + } + Tree[] args1 = transform(args); + return copy.Apply( + tree, fn1, addFreeArgs(tree.pos, get(free.fvs, fsym), args1)); + + case Ident(Name name): + Symbol sym = tree.symbol(); + Name name1 = sym.name; + if (sym.isLocal() && + (sym.kind == TYPE || (sym.kind == VAL && !sym.isMethod()))) { + sym = descr.proxy(sym, currentOwner); + } + Tree tree1 = copy.Ident(tree, name1).setSymbol(sym).setType( + sym.typeAt(descr.nextPhase)); + if ((sym.flags & CAPTURED) != 0) return gen.Select(tree1, Names.elem); + else return tree1; + + default: + return super.transform(tree); + } + } + + Symbol[] ftvsParams(Symbol owner) { + Symbol[] freevars = get(free.ftvs, owner).toArray(); + Symbol[] params = new Symbol[freevars.length]; + for (int i = 0; i < params.length; i++) { + params[i] = freevars[i].cloneSymbol(); + params[i].pos = owner.pos; + params[i].flags = PARAM | SYNTHETIC; + params[i].setOwner(owner); + params[i].setInfo(freevars[i].type()); + } + return params; + } + + Symbol[] fvsParams(Symbol owner) { + Symbol[] freevars = get(free.fvs, owner).toArray(); + Symbol[] params = new Symbol[freevars.length]; + for (int i = 0; i < params.length; i++) { + params[i] = freevars[i].cloneSymbol(); + params[i].pos = owner.pos; + params[i].flags &= CAPTURED; + params[i].flags |= PARAM | SYNTHETIC; + params[i].setOwner(owner); + params[i].setInfo(freevars[i].type()); + } + return params; + } + + /** change symbol so that + * owner = currentClass + * newparams are added + * enter symbol in scope of currentClass + */ + void liftSymbol(Symbol sym, Symbol[] newtparams, Symbol[] newparams) { + Symbol enclClass = sym.owner().enclClass(); + sym.setOwner(enclClass); + enclClass.members().enter(sym); + if (sym.isMethod()) { + if (newtparams.length != 0 || newparams.length != 0) { + sym.updateInfo( + addParams( + addTypeParams(sym.infoAt(descr.nextPhase), newtparams), + newparams)); + if (global.debug) + global.log(sym + " has now type " + sym.typeAt(descr.nextPhase)); + } + } else if (sym.kind == CLASS) { + liftSymbol(sym.constructor(), newtparams, newparams); + } else { + throw new ApplicationError(); + } + } + + Type addTypeParams(Type tp, Symbol[] newtparams) { + if (newtparams.length == 0) return tp; + switch (tp) { + case MethodType(_, _): + return Type.PolyType(newtparams, tp); + case PolyType(Symbol[] tparams, Type restpe): + Symbol[] tparams1 = new Symbol[tparams.length + newtparams.length]; + System.arraycopy(tparams, 0, tparams1, 0, tparams.length); + System.arraycopy(newtparams, 0, tparams1, tparams.length, newtparams.length); + return Type.PolyType(tparams1, restpe); + default: + throw new ApplicationError("illegal type: " + tp); + } + } + + Type addParams(Type tp, Symbol[] newparams) { + if (newparams.length == 0) return tp; + switch (tp) { + case MethodType(Symbol[] params, Type restpe): + Symbol[] params1 = new Symbol[params.length + newparams.length]; + System.arraycopy(params, 0, params1, 0, params.length); + System.arraycopy(newparams, 0, params1, params.length, newparams.length); + return Type.MethodType(params1, restpe); + case PolyType(Symbol[] tparams, Type restpe): + return Type.PolyType(tparams, addParams(restpe, newparams)); + default: + throw new ApplicationError("illegal type: " + tp); + } + } + + TypeDef[] addTypeParams(TypeDef[] tparams, Symbol[] newtparams) { + if (newtparams.length == 0) return tparams; + TypeDef[] tparams1 = new TypeDef[tparams.length + newtparams.length]; + System.arraycopy(tparams, 0, tparams1, 0, tparams.length); + for (int i = 0; i < newtparams.length; i++) { + tparams1[tparams.length + i] = (Tree.TypeDef)gen.TypeDef(newtparams[i]); + } + return tparams1; + } + + ValDef[] addParams(ValDef[] params, Symbol[] newparams) { + if (newparams.length == 0) return params; + ValDef[] params1 = new ValDef[params.length + newparams.length]; + System.arraycopy(params, 0, params1, 0, params.length); + for (int i = 0; i < newparams.length; i++) { + params1[params.length + i] = gen.Param(newparams[i]); + } + return params1; + } + + /** For all variables or type variables in `fvs', + * append proxies to argument array `args'. + */ + Tree[] addFreeArgs(int pos, SymSet fvs, Tree[] args) { + if (fvs != SymSet.EMPTY) { + Symbol[] fparams = fvs.toArray(); + Tree[] args1 = new Tree[args.length + fparams.length]; + System.arraycopy(args, 0, args1, 0, args.length); + for (int i = 0; i < fparams.length; i++) { + Symbol farg = descr.proxy(fparams[i], currentOwner); + args1[args.length + i] = + gen.Ident(pos, farg).setType(farg.typeAt(descr.nextPhase)); + } + return args1; + } else { + return args; + } + } +} diff --git a/sources/scalac/transformer/LambdaLiftPhase.java b/sources/scalac/transformer/LambdaLiftPhase.java new file mode 100644 index 0000000000..31d9eea2a8 --- /dev/null +++ b/sources/scalac/transformer/LambdaLiftPhase.java @@ -0,0 +1,135 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +\* */ + +// $Id$ + +package scalac.transformer; + +import scalac.*; +import scalac.util.*; +import scalac.parser.*; +import scalac.symtab.*; +import scalac.checkers.*; + +public class LambdaLiftPhase extends PhaseDescriptor implements Kinds, Modifiers { + + private Global global; + int nextPhase; + + public void initialize(Global global, int id) { + super.initialize(global, id); + this.global = global; + this.nextPhase = id + 1; + } + + public String name () { + return "lambdalift"; + } + + public String description () { + return "lambda lifter"; + } + + public String taskDescription() { + return "lambda lifting"; + } + + public Phase createPhase(Global global) { + return new LambdaLift(global, this); + } + + public Type transformInfo(Symbol sym, Type tp) { + Type tp1 = transform(tp, sym.owner()); + if ((sym.flags & Modifiers.CAPTURED) != 0) return refType(tp1); + else return tp1; + } + + /** Add proxies as type arguments for propagated type parameters. + */ + Type transform(Type tp, Symbol owner) { + return transformTypeMap.setOwner(owner).apply(tp); + } + + private class TransformTypeMap extends Type.Map { + Symbol owner; + Type.Map setOwner(Symbol owner) { this.owner = owner; return this; } + + public Type apply(Type tp) { + switch (tp) { + case TypeRef(Type pre, Symbol sym, Type[] targs): + switch (pre) { + case ThisType(_): + if (sym.constructor().isUpdated(nextPhase)) { + System.out.println("updated: " + sym.constructor());//debug + Symbol[] tparams = + sym.constructor().infoAt(nextPhase).typeParams(); + int i = tparams.length; + while (i > 0 && (tparams[i-1].flags & SYNTHETIC) != 0) + i--; + if (i < tparams.length) { + Type[] targs1 = new Type[tparams.length]; + System.arraycopy(map(targs), 0, targs1, 0, targs.length); + while (i < tparams.length) { + targs1[i] = proxy(tparams[i], owner).type(); + } + return Type.TypeRef(pre, sym, targs1); + } + } + } + break; + } + return map(tp); + } + + /** All symbols are mapped to themselves. + */ + public Scope map(Scope s) { return s; } + public Symbol map(Symbol s) { return s; } + public Symbol[] map(Symbol[] ss) { return ss; } + } + + private TransformTypeMap transformTypeMap = new TransformTypeMap(); + + /** Return closest enclosing (type)parameter that has same name as `fv', + * or `fv' itself if this is the closest definition. + */ + Symbol proxy(Symbol fv, Symbol owner) { + if (global.debug) + global.log("proxy " + fv + " in " + LambdaLift.asFunction(owner)); + Symbol o = owner; + while (o.kind != NONE) { + Symbol fowner = LambdaLift.asFunction(o); + if (fv.owner() == fowner) return fv; + Type ft = (fowner.isUpdated(nextPhase)) ? fowner.typeAt(nextPhase) + : fowner.type(); + Symbol[] ownerparams = fv.isType() ? ft.typeParams() + : ft.firstParams(); + for (int i = 0; i < ownerparams.length; i++) { + if (ownerparams[i].name == fv.name) + return ownerparams[i]; + } + o = o.owner(); + } + throw new ApplicationError("proxy " + fv + " in " + owner); + } + + /** The type scala.Ref[tp] + */ + Type refType(Type tp) { + Symbol refClass = global.definitions.getClass(Names.scala_Ref); + assert refClass.kind == Kinds.CLASS; + return Type.TypeRef(global.definitions.SCALA_TYPE, refClass, new Type[]{tp}); + } + + public Checker[] postCheckers(Global global) { + return new Checker[] { + new CheckSymbols(global), + new CheckTypes(global), + new CheckOwners(global) + }; + } +} diff --git a/sources/scalac/transformer/OwnerTransformer.java b/sources/scalac/transformer/OwnerTransformer.java new file mode 100644 index 0000000000..c723aeb6bb --- /dev/null +++ b/sources/scalac/transformer/OwnerTransformer.java @@ -0,0 +1,132 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.transformer; + +import java.io.*; +import java.util.*; +import scalac.*; +import scalac.util.*; +import scalac.ast.*; +import scalac.symtab.*; +import Tree.*; + + +/** A default transformer class which also maintains owner information + * + * @author Martin Odersky + * @version 1.0 + */ +public class OwnerTransformer extends Transformer { + + protected Symbol currentOwner; + + public OwnerTransformer(Global global, PhaseDescriptor descr) { + super(global, descr); + } + + public void apply(Unit unit) { + currentOwner = global.definitions.ROOT_CLASS; + unit.body = transform(unit.body); + } + + public Tree transform(Tree tree, Symbol owner) { + Symbol prevOwner = currentOwner; + currentOwner = owner; + Tree tree1 = transform(tree); + currentOwner = prevOwner; + return tree1; + } + + public TypeDef[] transform(TypeDef[] params, Symbol owner) { + Symbol prevOwner = currentOwner; + currentOwner = owner; + TypeDef[] res = transform(params); + currentOwner = prevOwner; + return res; + } + + public ValDef[][] transform(ValDef[][] params, Symbol owner) { + Symbol prevOwner = currentOwner; + currentOwner = owner; + ValDef[][] res = transform(params); + currentOwner = prevOwner; + return res; + } + + public Template transform(Template templ, Symbol owner) { + Symbol prevOwner = currentOwner; + if (owner.kind == Kinds.CLASS) + currentOwner = owner.constructor(); + Tree[] parents1 = transform(templ.parents); + currentOwner = owner; + Tree[] body1 = transformTemplateStats(templ.body, templ.symbol()); + currentOwner = prevOwner; + return copy.Template(templ, parents1, body1); + } + + public Tree[] transformTemplateStats(Tree[] ts, Symbol tsym) { + Tree[] ts1 = ts; + for (int i = 0; i < ts.length; i++) { + Tree t = transformTemplateStat(ts[i], tsym); + if (t != ts[i] && ts1 == ts) { + ts1 = new Tree[ts.length]; + System.arraycopy(ts, 0, ts1, 0, i); + } + ts1[i] = t; + } + return ts1; + } + + public Tree transformTemplateStat(Tree stat, Symbol tsym) { + return transform(stat, tsym); + } + + public Tree transform(Tree tree) { + switch(tree) { + case PackageDef(Tree packaged, Template impl): + return copy.PackageDef( + tree, transform(packaged), transform(impl, packaged.symbol())); + + case ClassDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, Tree tpe, Template impl): + return copy.ClassDef( + tree, mods, name, + transform(tparams, tree.symbol()), + transform(vparams, tree.symbol()), + transform(tpe), + transform(impl, tree.symbol())); + + case ModuleDef(int mods, Name name, Tree tpe, Template impl): + return copy.ModuleDef( + tree, mods, name, transform(tpe), + transform(impl, tree.symbol().moduleClass())); + + case DefDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, Tree tpe, Tree rhs): + return copy.DefDef( + tree, mods, name, + transform(tparams, tree.symbol()), + transform(vparams, tree.symbol()), + transform(tpe, tree.symbol()), + transform(rhs, tree.symbol())); + + case ValDef(int mods, Name name, Tree tpe, Tree rhs): + return copy.ValDef( + tree, mods, name, transform(tpe), + transform(rhs)); + + case TypeDef(int mods, Name name, TypeDef[] tparams, Tree rhs): + return copy.TypeDef( + tree, mods, name, + transform(tparams, tree.symbol()), + transform(rhs, tree.symbol())); + + default: + return super.transform(tree); + } + } +} diff --git a/sources/scalac/transformer/UnCurry.java b/sources/scalac/transformer/UnCurry.java new file mode 100644 index 0000000000..21fe579130 --- /dev/null +++ b/sources/scalac/transformer/UnCurry.java @@ -0,0 +1,180 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.transformer; + +import java.io.*; +import java.util.*; +import scalac.*; +import scalac.util.*; +import scalac.ast.*; +import scalac.symtab.*; +import Tree.*; + +/** Make all functions into one-argument functions + */ +public class UnCurry extends OwnerTransformer + implements Modifiers { + + UnCurryPhase descr; + + public UnCurry(Global global, UnCurryPhase descr) { + super(global, descr); + this.descr = descr; + } + + /** (ps_1) ... (ps_n) => (ps_1, ..., ps_n) + */ + ValDef[][] uncurry(ValDef[][] params) { + int n = 0; + for (int i = 0; i < params.length; i++) + n = n + params[i].length; + ValDef[] ps = new ValDef[n]; + int j = 0; + for (int i = 0; i < params.length; i++) { + System.arraycopy(params[i], 0, ps, j, params[i].length); + j = j + params[i].length; + } + return new ValDef[][]{ps}; + } + + /** tree of non-method type T ==> same tree with method type ()T + */ + Tree asMethod(Tree tree) { + switch (tree.type) { + case MethodType(_, _): + return tree; + default: + return tree.setType(Type.MethodType(Symbol.EMPTY_ARRAY, tree.type)); + } + } + + /** - uncurry all symbol and tree types (@see UnCurryPhase) + * - for every curried parameter list: (ps_1) ... (ps_n) ==> (ps_1, ..., ps_n) + * - for every curried application: f(args_1)...(args_n) ==> f(args_1, ..., args_n) + * - for every type application: f[Ts] ==> f[Ts]() unless followed by parameters + * - for every use of a parameterless function: f ==> f() and q.f ==> q.f() + * - for every def-parameter: def x: T ==> x: () => T + * - for every use of a def-parameter: x ==> x.apply() + * - for every argument to a def parameter `def x: T': + * if argument is not a reference to a def parameter: + * convert argument `e' to (expansion of) `() => e' + */ + public Tree transform(Tree tree) { + //uncurry type and symbol + if (tree.type != null) tree.type = descr.uncurry(tree.type); + switch (tree) { + case ClassDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, Tree tpe, Template impl): + return copy.ClassDef( + tree, mods, name, tparams, + uncurry(transform(vparams, tree.symbol())), + tpe, + transform(impl, tree.symbol())); + + case DefDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, Tree tpe, Tree rhs): + Tree rhs1 = transform(rhs, tree.symbol()); + if (global.debug) global.log(name + ":" + rhs1.type);//debug + return copy.DefDef( + tree, mods, name, tparams, + uncurry(transform(vparams, tree.symbol())), + tpe, rhs1); + + case ValDef(int mods, Name name, Tree tpe, Tree rhs): + if (tree.symbol().isDefParameter()) { + int mods1 = mods & ~ DEF; + Type newtype = global.definitions.functionType(Type.EMPTY_ARRAY, tpe.type); + Tree tpe1 = gen.mkType(tpe.pos, newtype); + return copy.ValDef(tree, mods1, name, tpe1, rhs).setType(newtype); + } else { + return tree; + } + + case TypeApply(Tree fn, Tree[] args): + Tree tree1 = asMethod(super.transform(tree)); + return gen.Apply(tree1, new Tree[0]); + + case Apply(Tree fn, Tree[] args): + // f(x)(y) ==> f(x, y) + // argument to parameterless function e => ( => e) + Type ftype = fn.type; + Tree fn1 = transform(fn); + Tree[] args1 = transformArgs(args, ftype); + switch (fn1) { + case Apply(Tree fn2, Tree[] args2): + Tree[] newargs = new Tree[args1.length + args2.length]; + System.arraycopy(args2, 0, newargs, 0, args2.length); + System.arraycopy(args1, 0, newargs, args2.length, args1.length); + return copy.Apply(tree, fn2, newargs); + default: + return copy.Apply(tree, fn1, args1); + } + + case Select(_, _): + case Ident(_): + Tree tree1 = super.transform(tree); + switch (tree1.symbol().type()) { + case PolyType(Symbol[] tparams, Type restp): + if (tparams.length == 0 && !(restp instanceof Type.MethodType)) { + return gen.Apply(asMethod(tree1), new Tree[0]); + } else { + return tree1; + } + default: + if (tree1.symbol().isDefParameter()) { + tree1.type = global.definitions.functionType( + Type.EMPTY_ARRAY, tree1.type); + return gen.Apply(gen.Select(tree1, Names.apply), new Tree[0]); + } else { + return tree1; + } + } + + default: + return super.transform(tree); + } + } + + /** Transform arguments `args' to method with type `methtype'. + */ + private Tree[] transformArgs(Tree[] args, Type methtype) { + switch (methtype) { + case MethodType(Symbol[] params, _): + for (int i = 0; i < args.length; i++) { + args[i] = transformArg(args[i], params[i]); + } + return args; + case PolyType(_, Type restp): + return transformArgs(args, restp); + default: + throw new ApplicationError(methtype); + } + } + + /** for every argument to a def parameter `def x: T': + * if argument is not a reference to a def parameter: + * convert argument `e' to (expansion of) `() => e' + */ + private Tree transformArg(Tree arg, Symbol formal) { + Tree arg1 = transform(arg); + if ((formal.flags & DEF) != 0) { + Symbol sym = arg.symbol(); + if (sym != null && (sym.flags & DEF) != 0) { + switch (arg1) { + case Apply(Select(Tree qual, Name name), Tree[] args1): + assert name == Names.apply && args1.length == 0; + return qual; + default: + global.debugPrinter.print(arg);//debug + throw new ApplicationError(); + } + } + return transform(gen.mkUnitFunction( + arg, descr.uncurry(arg.type), currentOwner)); + } else return arg1; + } +} diff --git a/sources/scalac/transformer/UnCurryPhase.java b/sources/scalac/transformer/UnCurryPhase.java new file mode 100644 index 0000000000..cb80db1ee9 --- /dev/null +++ b/sources/scalac/transformer/UnCurryPhase.java @@ -0,0 +1,117 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.transformer; + +import scalac.*; +import scalac.parser.*; +import scalac.symtab.*; +import scalac.typechecker.Infer; +import scalac.checkers.*; + +public class UnCurryPhase extends PhaseDescriptor implements Modifiers { + + private Global global; + + public void initialize(Global global, int id) { + super.initialize(global, id); + this.global = global; + } + + public String name () { + return "uncurry"; + } + + public String description () { + return "uncurry function types and applications"; + } + + public String taskDescription() { + return "uncurried"; + } + + public Phase createPhase(Global global) { + return new UnCurry(global, this); + } + + /** - return symbol's transformed type, + * - if symbol is a def parameter with transformed type T, return () => T + */ + public Type transformInfo(Symbol sym, Type tp0) { + Type tp1 = uncurry(tp0); + if (sym.isDefParameter()) return global.definitions.functionType(Type.EMPTY_ARRAY, tp1); + else return tp1; + } + + /** - (ps_1)...(ps_n)T ==> (ps_1,...,ps_n)T + */ + Type uncurry(Type tp) { + switch (tp) { + case MethodType(Symbol[] params, Type tp1): + Symbol[] uncurriedParams = uncurryParams(params); + Type uncurriedTp1 = uncurry(tp1); + switch (uncurriedTp1) { + case MethodType(Symbol[] params1, Type tp2): + Symbol[] newparams = new Symbol[uncurriedParams.length + params1.length]; + System.arraycopy(uncurriedParams, 0, newparams, 0, uncurriedParams.length); + System.arraycopy(params1, 0, newparams, uncurriedParams.length, params1.length); + return Type.MethodType(newparams, tp2); + default: + if (uncurriedParams == params && uncurriedTp1 == tp1) return tp; + else return Type.MethodType(uncurriedParams, uncurriedTp1); + } + case PolyType(Symbol[] tparams, Type tp1): + if (tp instanceof Infer.VirtualPolyType) + return uncurry(tp1); + switch (tp1) { + case MethodType(_, _): + Type newtp1 = uncurry(tp1); + if (tp1 != newtp1) return Type.PolyType(tparams, newtp1); + else return tp; + default: + Type newtp1 = Type.MethodType(Symbol.EMPTY_ARRAY, tp1); + if (tparams.length == 0) return newtp1; + else return Type.PolyType(tparams, newtp1); + } + case OverloadedType(_, _): + return new Type.Map() { + public Type apply(Type t) { return uncurry(t); } + }.map(tp); + default: + return tp; + } + } + + Symbol[] uncurryParams(Symbol[] params) { + Symbol[] params1 = params; + for (int i = 0; i < params.length; i++) { + Symbol param = params[i]; + Symbol param1 = param; + Type tp = param.info(); + Type tp1 = transformInfo(param, tp); + if (tp != tp1) { + if (params1 == params) { + params1 = new Symbol[params.length]; + System.arraycopy(params, 0, params1, 0, i); + } + param1 = param.cloneSymbol().setType(tp1); + param1.flags &= ~DEF; + } + params1[i] = param1; + } + return params1; + } + + public Checker[] postCheckers(Global global) { + return new Checker[] { + new CheckSymbols(global), + new CheckTypes(global), + new CheckOwners(global) + }; + } +} diff --git a/sources/scalac/typechecker/Analyzer.java b/sources/scalac/typechecker/Analyzer.java new file mode 100644 index 0000000000..5558239d1c --- /dev/null +++ b/sources/scalac/typechecker/Analyzer.java @@ -0,0 +1,1975 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** +** $Id$ +\* */ + +// todo: (0) propagate target type in cast. +// todo: (1) check that only stable defs override stable defs + +package scalac.typechecker; + +import scalac.*; +import scalac.util.*; +import scalac.ast.*; +import scalac.ast.printer.*; +import scalac.symtab.*; +import Tree.*; +import java.util.HashMap; +import java.util.Vector; + +public class Analyzer extends Transformer implements Modifiers, Kinds { + + private final Definitions definitions; + private final DeSugarize desugarize; + private final AnalyzerPhase descr; + final Infer infer; + + public Analyzer(Global global, AnalyzerPhase descr) { + super(global, descr); + this.definitions = global.definitions; + this.descr = descr; + this.infer = new Infer(this); + this.desugarize = new DeSugarize(this, global); + } + + /** Phase variables, used and set in transformers; + */ + private Unit unit; + private Context context; + private Type pt; + private int mode; + + public void apply() { + int errors = global.reporter.errors(); + for (int i = 0; i < global.units.length; i++) { + enterUnit(global.units[i]); + } + super.apply(); + int n = descr.newSources.size(); + while (n > 0) { + int l = global.units.length; + Unit[] newUnits = new Unit[l + n]; + System.arraycopy(global.units, 0, newUnits, 0, l); + for (int i = 0; i < n; i++) + newUnits[i + l] = (Unit)descr.newSources.get(i); + global.units = newUnits; + descr.newSources.clear(); + for (int i = l; i < newUnits.length; i++) { + apply(newUnits[i]); + } + n = descr.newSources.size(); + } + } + + public void enterUnit(Unit unit) { + enter(new Context(Tree.Empty, descr.startContext), unit); + } + + public void enter(Context context, Unit unit) { + assert this.unit == null : "start unit non null for " + unit; + this.unit = unit; + this.context = context; + descr.contexts.put(unit, context); + enterSyms(unit.body); + this.unit = null; + this.context = null; + } + + public void lateEnter(Unit unit, Symbol sym) { + assert sym.pos == Position.NOPOS : sym; + enterUnit(unit); + if (sym.pos == Position.NOPOS) { + sym.setInfo(Type.ErrorType); + String kind; + if (sym.name.isTermName()) kind = "module or method "; + else if (sym.name.isTypeName()) kind = "class "; + else kind = "constructor "; + throw new Type.Error("file " + unit.source + " does not define public " + + kind + sym.name); + } else { + descr.newSources.add(unit); + } + } + + public void apply(Unit unit) { + global.log("checking " + unit); + assert this.unit == null : "start unit non null for " + unit; + this.unit = unit; + this.context = (Context)descr.contexts.remove(unit); + assert this.context != null : "could not find context for " + unit; + //context.imports = context.outer.imports; + unit.body = transformStatSeq(unit.body, Symbol.NONE); + /** todo: check what this is for + if (global.target == global.TARGET_JAVA && unit.errors == 0) { + unit.symdata = new SymData(unit); + } + */ + this.unit = null; + this.context = null; + global.operation("checked " + unit); + } + + /** Mode constants + */ + static final int NOmode = 0x000; + static final int EXPRmode = 0x001; // these 4 modes are mutually exclusive. + static final int PATTERNmode = 0x002; + static final int CONSTRmode = 0x004; + static final int TYPEmode = 0x008; + + static final int FUNmode = 0x10; // orthogonal to above. When set + // we are looking for a method or constructor + + static final int POLYmode = 0x020; // orthogonal to above. When set + // expression types can be polymorphic. + + static final int QUALmode = 0x040; // orthogonal to above. When set + // expressions may be packages and + // Java statics modules. + +// Helper definitions --------------------------------------------------------- + + /** The qualifier type of a potential application of the `match' method. + * or NoType, if this is something else. + */ + private Type matchQualType(Tree fn) { + switch (fn) { + case Select(Tree qual, _): + if (fn.symbol() == definitions.OBJECT_TYPE.lookup(Names.match)) + return qual.type.widen(); + break; + case TypeApply(Tree fn1, _): + return matchQualType(fn1); + case Ident(_): + if (fn.symbol() == definitions.OBJECT_TYPE.lookup(Names.match)) + return context.enclClass.owner.type(); + break; + } + return fn.type == Type.ErrorType ? Type.ErrorType : Type.NoType; + } + + private Tree deepCopy(Tree tree) { + switch (tree) { + case Ident(Name name): + return make.Ident(tree.pos, name) + .setSymbol(tree.symbol()).setType(tree.type); + case Select(Tree qual, Name name): + return make.Select(tree.pos, deepCopy(qual), name) + .setSymbol(tree.symbol()).setType(tree.type); + default: + return tree; + } + } + + static Name value2TypeName(Object value) { + if (value instanceof Character) return Name.fromString("scala.Char"); + else if (value instanceof Integer) return Name.fromString("scala.Int"); + else if (value instanceof Long) return Name.fromString("scala.Long"); + else if (value instanceof Float) return Name.fromString("scala.Float"); + else if (value instanceof Double) return Name.fromString("scala.Double"); + else if (value instanceof String) return Name.fromString("java.lang.String"); + else throw new ApplicationError(); + } + + Tree error(Tree tree, String msg) { + unit.error(tree.pos, msg); + if (tree.hasSymbol()) tree = tree.setSymbol(Symbol.ERROR); + return tree.setType(Type.ErrorType); + } + + void error(int pos, String msg) { + unit.error(pos, msg); + } + + void typeError(int pos, Type found, Type req) { + String explanation = ""; + switch (found) { + case MethodType(_, Type restype): + if (infer.isCompatible(restype, req)) + explanation = "\n possible cause: missing arguments for method or constructor"; + } + error(pos, infer.typeErrorMsg("type mismatch", found, req) + explanation); + } + +// Name resolution ----------------------------------------------------------- + + /** Is `sym' accessible as a member of tree `site' in current context? + */ + boolean isAccessible(Symbol sym, Tree site) { + return + (sym.flags & (PRIVATE | PROTECTED)) == 0 + || + accessWithin(sym.owner()) + || + ((sym.flags & PRIVATE) == 0) && + site.type.symbol().isSubClass(sym.owner()) && + (site instanceof Tree.Super || + isSubClassOfEnclosing(site.type.symbol())); + } //where + + /** Are we inside definition of `owner'? + */ + boolean accessWithin(Symbol owner) { + Context c = context; + while (c != Context.NONE && c.owner != owner) { + c = c.outer.enclClass; + } + return c != Context.NONE; + } + + /** Is `clazz' a subclass of an enclosing class? + */ + boolean isSubClassOfEnclosing(Symbol clazz) { + Context c = context; + while (c != Context.NONE && !clazz.isSubClass(c.owner)) { + c = c.outer.enclClass; + } + return c != Context.NONE; + } + +// Checking methods ---------------------------------------------------------- + + /** Check that symbol's definition is well-formed. This means: + * - no conflicting modifiers + * - def modifiers only in methods + * - declarations only in classes + * - classes with abstract members have `abstract' modifier. + * - symbols with `override' modifier override some other symbol. + */ + void validate(Symbol sym) { + checkNoConflict(sym, ABSTRACT, PRIVATE); + checkNoConflict(sym, FINAL, PRIVATE); + checkNoConflict(sym, PRIVATE, PROTECTED); + checkNoConflict(sym, PRIVATE, OVERRIDE); + checkNoConflict(sym, ABSTRACT, FINAL); + if ((sym.flags & ABSTRACTCLASS) != 0 && sym.kind != CLASS) { + error(sym.pos, "`abstract' modifier can be used only for classes; " + + "\nit should be omitted for abstract members"); + } + if ((sym.flags & OVERRIDE) != 0 && sym.kind == CLASS) { + error(sym.pos, "`override' modifier ot allowed for classes"); + } + if ((sym.flags & DEF) != 0 && sym.owner().isPrimaryConstructor()) { + error(sym.pos, "`def' modifier not allowed for class parameters"); + } + if ((sym.flags & ABSTRACT) != 0) { + if (sym.owner().kind != CLASS || + (sym.owner().flags & MODUL) != 0 || + sym.owner().isAnonymousClass()) { + error(sym.pos, abstractVarNote(sym, + "only classes can have declared but undefined members")); + sym.flags &= ~ABSTRACT; + } + } + if ((sym.flags & OVERRIDE) != 0) { + int i = -1; + if (sym.owner().kind == CLASS) { + Type[] parents = sym.owner().info().parents(); + i = parents.length - 1; + while (i >= 0 && + parents[i].lookupNonPrivate(sym.name).kind == NONE) + i--; + } + if (i < 0) { + error(sym.pos, sym + " overrides nothing"); + sym.flags &= ~OVERRIDE; + } + } + } + + /** Check that + * - all parents are class types + * - supertype conforms to supertypes of all mixin types. + * - final classes are only inherited by classes which are + * nested within definition of base class, or that occur within same + * statement sequence. + */ + void validateParentClasses(Tree[] constrs, Type[] parents) { + if (parents.length == 0 || !checkClassType(constrs[0].pos, parents[0])) return; + for (int i = 1; i < parents.length; i++) { + if (!checkClassType(constrs[i].pos, parents[i])) return; + Type[] grandparents = parents[i].parents(); + if (grandparents.length > 0 && !parents[0].isSubType(grandparents[0])) + error(constrs[i].pos, "illegal inheritance;\n " + parents[0] + + "does not conform to " + parents[i] + "'s supertype"); + Symbol bsym = parents[i].symbol(); + if ((bsym.flags & FINAL) != 0) { + // are we in same scope as base type definition? + Scope.Entry e = context.scope.lookupEntry(bsym.name); + if (e.sym != bsym || e.owner != context.scope) { + // we are not within same statement sequence + Context c = context; + while (c != Context.NONE && c.owner != bsym) + c = c.outer; + if (c == Context.NONE) { + error(constrs[i].pos, "illegal inheritance from final class"); + } + } + } + } + } + + /** Check that type is a class type. + */ + private boolean checkClassType(int pos, Type tp) { + switch (tp.unalias()) { + case TypeRef(_, Symbol sym, _): + if (sym.kind == CLASS) return true; + else if (sym.kind == ERROR) return false; + break; + case ErrorType: + return false; + } + error(pos, "class type expected"); + return false; + } + + /** Check that type is an object type + */ + private Type checkObjectType(int pos, Type tp) { + if (tp.isObjectType()) return tp; + else { + if (tp != Type.ErrorType) error(pos, "object type expected"); + return Type.ErrorType; + } + } + + /** 1. Check that only parameterless (uniform) classes are inherited several times. + * 2. Check that all type instances of an inherited uniform class are the same. + * 3. Check that case classes do not inherit from case classes. + */ + void validateBaseTypes(Symbol clazz) { + if (clazz.type().parents().length > 1) + validateBaseTypes(clazz, clazz.type(), + new boolean[clazz.closure().length], 0); + } + //where + void validateBaseTypes(Symbol clazz, Type tp, boolean[] seen, int start) { + Symbol baseclazz = tp.symbol(); + if (baseclazz.kind == CLASS) { + int index = clazz.closurePos(baseclazz); + if (seen[index]) { + // check that only uniform classes are inherited several times. + if (!clazz.isCompoundSym() && !baseclazz.isTrait()) { + error(clazz.pos, "illegal inheritance;\n" + clazz + + " inherits " + baseclazz + " twice"); + } + // check no two different type instances of same class + // are inherited. + Type tp1 = clazz.closure()[index]; + if (!tp1.isSameAs(tp)) { + if (clazz.isCompoundSym()) + error(clazz.pos, + "illegal combination;\n " + "compound type " + + " combines different type instances of " + + baseclazz + ":\n" + tp + " and " + tp1); + else + error(clazz.pos, "illegal inheritance;\n " + clazz + + " inherits different type instances of " + + baseclazz + ":\n" + tp + " and " + tp1); + } + } + // check that case classes do not inherit from case classes + if (clazz.isCaseClass() && baseclazz.isCaseClass()) + error(clazz.pos, "illegal inheritance;\n " + "case " + clazz + + "inherits from other case " + baseclazz); + + seen[index] = true; + Type[] parents = tp.parents(); + for (int i = parents.length - 1; i >= start; i--) { + validateBaseTypes(clazz, parents[i].unalias(), seen, i == 0 ? 0 : 1); + } + } + } + + /** Check that found type conforms to required one. + */ + Type checkType(int pos, Type found, Type required) { + if (found.isSubType(required)) return found; + else { + typeError(pos, found, required); + if (global.debug) { + Type.debugSwitch = true; + found.isSubType(required); + Type.debugSwitch = false; + } + return Type.ErrorType; + } + } + + /** Check that type is eta-expandable (i.e. no `def' parameters) + */ + void checkEtaExpandable(int pos, Type tp) { + switch (tp) { + case MethodType(Symbol[] params, Type restype): + for (int i = 0; i < params.length; i++) { + if ((params[i].flags & DEF) != 0) + error(pos, "method with `def' parameters needs to be fully applied"); + } + checkEtaExpandable(pos, restype); + } + } + + /** Check that `sym' does not contain both `flag1' and `flag2' + */ + void checkNoConflict(Symbol sym, int flag1, int flag2) { + if ((sym.flags & (flag1 | flag2)) == (flag1 | flag2)) { + if (flag1 == ABSTRACT) + error(sym.pos, "abstract member may not have " + + Modifiers.Helper.toString(flag2) + " modifier"); + else + error(sym.pos, "illegal combination of modifiers: " + + Modifiers.Helper.toString(flag1) + " and " + + Modifiers.Helper.toString(flag2)); + } + } + + /** Check that + + /** Check that type does not refer to components defined in current scope. + */ + Type checkNoEscape(int pos, Type tp) { + try { + return checkNoEscapeMap.apply(tp); + } catch (Type.Error ex) { + error(pos, ex.msg); + return Type.ErrorType; + } + } + //where + private Type.Map checkNoEscapeMap = new Type.Map() { + public Type apply(Type t) { + switch (t.unalias()) { + case TypeRef(ThisType(_), Symbol sym, Type[] args): + Scope.Entry e = context.scope.lookupEntry(sym.name); + if (e.sym == sym && e.owner == context.scope) { + throw new Type.Error( + "type " + t + " escapes its defining scope"); + } else { + map(args); + return t; + } + case SingleType(ThisType(_), Symbol sym): + Scope.Entry e = context.scope.lookupEntry(sym.name); + if (e.sym == sym && e.owner == context.scope) { + return apply(t.widen()); + } else { + return t; + } + default: + return map(t); + } + }}; + + /** Check that tree represents a pure definition. + */ + void checkPureDef(Tree tree, Symbol clazz) { + if (!TreeInfo.isPureDef(tree) && tree.type != Type.ErrorType) + error(tree.pos, clazz + " may contain only pure definitions"); + } + + /** Check that tree represents a pure definition. + */ + void checkTrait(Tree tree, Symbol clazz) { + if (!TreeInfo.isPureConstr(tree) && tree.type != Type.ErrorType) + error(tree.pos, " " + clazz + " may inherit only from stable trait constructors"); + } + + /** Check that tree is a stable expression . + */ + Tree checkStable(Tree tree) { + if (TreeInfo.isPureExpr(tree) || tree.type == Type.ErrorType) return tree; + new TextTreePrinter().print(tree).end();//debug + System.out.println(" " + tree.type);//debug + return error(tree, "stable identifier required"); + } + + /** Check all members of class `clazz' for overriding conditions. + */ + void checkAllOverrides(Symbol clazz) { + Type[] closure = clazz.closure(); + for (int i = 0; i < closure.length; i++) { + for (Scope.SymbolIterator it = closure[i].members().iterator(); + it.hasNext();) { + Symbol other = it.next(); + Symbol member = clazz.info().lookup(other.name); + if (other != member && member.kind != NONE) + checkOverride(clazz, member, other); + if ((member.flags & ABSTRACT) != 0 && + clazz.kind == CLASS && + (clazz.flags & ABSTRACTCLASS) == 0) { + if (clazz.isAnonymousClass()) + error(clazz.pos, "object creation impossible, since " + + member + member.locationString() + " is not defined"); + else + error(clazz.pos, + clazz + abstractVarNote( + member, " needs to be abstract; it does not define " + + member + member.locationString())); + clazz.flags |= ABSTRACTCLASS; + } + } + } + } + + /** Check that all conditions for overriding `other' by `member' are met. + */ + void checkOverride(Symbol clazz, Symbol member, Symbol other) { + int pos; + if (member.owner() == clazz) pos = member.pos; + else if (!member.owner().isSubClass(other.owner())) pos = context.tree.pos; + else return; // everything was already checked elsewhere + + if ((member.flags & PRIVATE) != 0) { + overrideError(pos, member, other, "should not be private"); + } else if ((other.flags & PROTECTED) != 0 && (member.flags & PROTECTED) == 0) { + overrideError(pos, member, other, "needs `protected' modifier"); + } else if ((other.flags & FINAL) != 0) { + overrideError(pos, member, other, "cannot override final member"); + } else if ((other.flags & ABSTRACT) == 0 && ((member.flags & OVERRIDE) == 0)) { + overrideError(pos, member, other, "needs `override' modifier"); + } else { + Type self = clazz.thisType(); + switch (other.kind) { + case CLASS: + overrideError(pos, member, other, "cannot override a class"); + break; + case ALIAS: + if (!self.memberInfo(member).isSameAs(self.memberInfo(other))) + overrideTypeError(pos, member, other, self); + break; + default: + if (other.isConstructor()) + overrideError(pos, member, other, "cannot override a class constructor"); + if (!self.memberInfo(member).isSubType(self.memberInfo(other))) + overrideTypeError(pos, member, other, self); + } + } + } + + void overrideError(int pos, Symbol member, Symbol other, String msg) { + if (other.type() != Type.ErrorType && member.type() != Type.ErrorType) + error(pos, + "error overriding " + other + other.locationString() + + "; " + member + member.locationString() + " " + msg); + } + + void overrideTypeError(int pos, Symbol member, Symbol other, Type site) { + if (other.type() != Type.ErrorType && member.type() != Type.ErrorType) + error(pos, + member + member.locationString() + + infoString(member, site.memberInfo(member)) + + "\n cannot override " + other + other.locationString() + + infoString(other, site.memberInfo(other))); + } + + String infoString(Symbol sym, Type symtype) { + switch (sym.kind) { + case ALIAS: return ", which equals " + symtype; + case TYPE: return " bounded by " + symtype; + case VAL: return " of type " + symtype; + default: return ""; + } + } + + String abstractVarNote(Symbol member, String msg) { + String note = ((member.flags & MUTABLE) == 0) ? "" + : "\n(Note that variables need to be initialized to be defined)"; + return msg + note; + } + +// Entering Symbols ---------------------------------------------------------- + + /** If `tree' is a definition, create a symbol for it with a lazily + * constructed type, and enter into current scope. + */ + Symbol enterSym(Tree tree) { + // todo: handle override qualifiers + Symbol owner = context.owner; + switch (tree) { + case PackageDef(Tree packaged, Tree.Template templ): + switch (templ) { + case Template(_, Tree[] body): + pushContext(tree, context.owner, context.scope); + context.imports = null; + ((PackageDef) tree).packaged = packaged = + transform(packaged, QUALmode); + popContext(); + Symbol pkg = checkStable(packaged).symbol(); + if (pkg != null && pkg.kind != ERROR) { + if (pkg.isPackage()) { + pushContext(templ, pkg.moduleClass(), pkg.members()); + enterSyms(body); + popContext(); + } else { + error(tree.pos, "only Java packages allowed for now"); + } + } + templ.setSymbol(Symbol.NONE); + return null; + default: + throw new ApplicationError(); + } + + case ClassDef(int mods, Name name, Tree.TypeDef[] tparams, Tree.ValDef[][] vparams, _, Tree.Template templ): + ClassSymbol clazz = new ClassSymbol(tree.pos, name, owner, mods); + if (clazz.isLocalClass()) unit.mangler.setMangledName(clazz); + enterSym(tree, clazz.constructor()); + if ((mods & CASE) != 0) { + // enter case constructor method. + enterInScope( + new TermSymbol( + tree.pos, name.toTermName(), owner, mods & (ACCESSFLAGS | CASE)) + .setInfo(new LazyConstrMethodType(tree))); + } + return enterSym(tree, clazz); + + case ModuleDef(int mods, Name name, _, _): + TermSymbol modul = TermSymbol.newModule(tree.pos, name, owner, mods); + Symbol clazz = modul.moduleClass(); + clazz.setInfo(new LazyTreeType(tree)); + if (clazz.isLocalClass()) unit.mangler.setMangledName(clazz); + return enterSym(tree, modul); + + case ValDef(int mods, Name name, _, _): + return enterSym(tree, new TermSymbol(tree.pos, name, owner, mods)); + + case DefDef(int mods, Name name, _, _, _, _): + return enterSym(tree, new TermSymbol(tree.pos, name, owner, mods)); + + case TypeDef(int mods, Name name, _, _): + int kind = (mods & (ABSTRACT | PARAM)) != 0 ? TYPE : ALIAS; + TypeSymbol tsym = new TypeSymbol(kind, tree.pos, name, owner, mods); + if (kind == ALIAS) + tsym.constructor().setInfo(new LazyTreeType(tree)); + return enterSym(tree, tsym); + + case Import(Tree expr, Name[] selectors): + return enterImport(tree, + new TermSymbol( + tree.pos, + Name.fromString("import " + expr), + Symbol.NONE, SYNTHETIC)); + + default: + return null; + } + }//where + + /** Enter `sym' in current scope and make it the symbol of `tree'. + */ + private Symbol enterSym(Tree tree, Symbol sym) { + //if (global.debug) System.out.println("entering " + sym);//DEBUG + sym.setInfo(new LazyTreeType(tree)); + sym = enterInScope(sym); + tree.setSymbol(sym); + return sym; + } + + /** Make `sym' the symbol of import `tree' and create an entry in + * current imports list. + */ + private Symbol enterImport(Tree tree, Symbol sym) { + sym.setInfo(new LazyTreeType(tree)); + tree.setSymbol(sym); + context.imports = new ImportList(tree, context.scope, context.imports); + return sym; + } + + /** Enter symbol `sym' in current scope. Check for double definitions. + * Handle overloading. + */ + private Symbol enterInScope(Symbol sym) { + // handle double and overloaded definitions + Scope.Entry e = context.scope.lookupEntry(sym.name); + if (e.owner == context.scope) { + Symbol other = e.sym; + if (other.isPreloaded()) { + // symbol was preloaded from package; + // need to overwrite definition. + if (global.debug) System.out.println("overwriting " + other);//debug + sym.copyTo(other); + if (sym.isModule()) { + sym.moduleClass().copyTo( + other.moduleClass()); + sym.moduleClass().constructor().copyTo( + other.moduleClass().constructor()); + } + return other; + } else if (sym.kind == VAL && other.kind == VAL) { + // it's an overloaded definition + if (((sym.flags ^ other.flags) & SOURCEFLAGS) != 0) { + error(sym.pos, + "illegal overloaded definition of " + sym + + ": modifier lists differ in " + + Modifiers.Helper.toString( + (sym.flags ^ other.flags) & SOURCEFLAGS)); + } else { + e.setSymbol(other.overloadWith(sym)); + } + } else { + error(sym.pos, + sym.nameString() + " is already defined as " + + other + other.locationString()); + } + } else { + context.scope.enter(sym); + } + return sym; + } + + /** Enter all symbols in statement list + */ + public void enterSyms(Tree[] stats) { + for (int i = 0; i < stats.length; i++) + enterSym(stats[i]); + } + +// Definining Symbols ------------------------------------------------------- + + /** Define symbol associated with `tree' using given `context'. + */ + void defineSym(Tree tree, Unit unit, Infer infer, Context context) { + Unit savedUnit = this.unit; + this.unit = unit; + Context savedContext = this.context; + this.context = context; + int savedMode = this.mode; + this.mode = EXPRmode; + Type savedPt = this.pt; + this.pt = Type.AnyType; + + Symbol sym = tree.symbol(); + if (global.debug) System.out.println("defining " + sym);//debug + Type owntype; + switch (tree) { + case ClassDef(int mods, Name name, Tree.TypeDef[] tparams, Tree.ValDef[][] vparams, _, Tree.Template templ): + assert (mods & LOCKED) == 0 || sym.isAnonymousClass(): sym; // to catch repeated evaluations + ((ClassDef) tree).mods |= LOCKED; + + if ((mods & CASE) != 0 && vparams.length > 0) + templ.body = desugarize.addCaseElements(templ.body, vparams[0]); + + pushContext(tree, sym.constructor(), new Scope(context.scope)); + Symbol[] tparamSyms = enterParams(tparams); + Symbol[][] vparamSyms = enterParams(vparams); + Type constrtype = makeMethodType( + tparamSyms, + vparamSyms, + Type.TypeRef(sym.owner().thisType(), sym, Symbol.type(tparamSyms))); + sym.constructor().setInfo(constrtype); + // necessary so that we can access tparams + sym.constructor().flags |= INITIALIZED; + + defineTemplate(templ, sym); + owntype = templ.type; + popContext(); + break; + + case ModuleDef(int mods, Name name, Tree tpe, Tree.Template templ): + Symbol clazz = sym.moduleClass(); + defineTemplate(templ, clazz); + clazz.setInfo(templ.type); + if (tpe == Tree.Empty) owntype = clazz.type(); + else owntype = transform(tpe, TYPEmode).type; + break; + + case ValDef(int mods, Name name, Tree tpe, Tree rhs): + if (tpe == Tree.Empty) { + if (rhs == Tree.Empty) { + if ((sym.owner().flags & ACCESSOR) != 0) { + // this is the paremeter of a variable setter method. + ((ValDef) tree).tpe = tpe = + gen.mkType(tree.pos, sym.owner().accessed().type()); + } else { + error(tree.pos, "missing parameter type"); + ((ValDef) tree).tpe = tpe = + gen.mkType(tree.pos, Type.ErrorType); + } + owntype = tpe.type; + } else { + ((ValDef) tree).rhs = rhs = transform(rhs, EXPRmode); + owntype = rhs.type; + if ((sym.flags & MUTABLE) != 0) owntype = owntype.widen(); + } + } else { + owntype = transform(tpe, TYPEmode).type; + } + break; + + case DefDef(int mods, Name name, Tree.TypeDef[] tparams, Tree.ValDef[][] vparams, Tree tpe, Tree rhs): + pushContext(tree, sym, new Scope(context.scope)); + Symbol[] tparamSyms = enterParams(tparams); + Symbol[][] vparamSyms = enterParams(vparams); + Type restpe; + if (tpe == Tree.Empty) { + int rhsmode = name.isConstrName() ? CONSTRmode : EXPRmode; + ((DefDef) tree).rhs = rhs = transform(rhs, rhsmode); + restpe = rhs.type; + } else { + restpe = transform(tpe, TYPEmode).type; + } + popContext(); + owntype = makeMethodType(tparamSyms, vparamSyms, restpe); + break; + + case TypeDef(int mods, Name name, Tree.TypeDef[] tparams, Tree rhs): + if (sym.kind == TYPE) { + pushContext(rhs, context.owner, context.scope); + this.context.delayArgs = true; + owntype = transform(rhs, TYPEmode).type; + owntype.symbol().initialize();//to detect cycles + popContext(); + } else { // sym.kind == ALIAS + pushContext(tree, sym, new Scope(context.scope)); + Symbol[] tparamSyms = enterParams(tparams); + sym.constructor().setInfo(Type.PolyType(tparamSyms, Type.NoType)); + owntype = transform(rhs, TYPEmode).type; + popContext(); + } + break; + + case Import(Tree expr, Name[] selectors): + Tree expr1 = transform(expr, EXPRmode | QUALmode); + ((Import) tree).expr = expr1; + checkStable(expr1); + owntype = expr1.type; + break; + + default: + throw new ApplicationError(); + } + sym.setInfo(owntype); + validate(sym); + if (global.debug) System.out.println("defined " + sym);//debug + this.unit = savedUnit; + this.context = savedContext; + this.mode = savedMode; + this.pt = savedPt; + } + + /** Definition phase for a template. This enters all symbols in template + * into symbol table. + */ + void defineTemplate(Tree.Template templ, Symbol clazz) { + // attribute parent constructors + Tree[] constrs = transformConstrInvocations( + templ.pos, templ.parents, true, Type.AnyType); + + Type[] parents = new Type[constrs.length]; + for (int i = 0; i < parents.length; i++) + parents[i] = constrs[i].type; + + // enter all members + Scope members = new Scope(); + pushContext(templ, clazz, members); + if ((clazz.flags & CASE) != 0) + templ.body = desugarize.addCaseMethods(templ.body, clazz, parents); + templ.body = desugarize.Statements(templ.body, false); + enterSyms(templ.body); + popContext(); + + templ.type = Type.compoundType(parents, members, clazz); + } + + Symbol[] enterParams(Tree[] params) { + enterSyms(params); + return Tree.symbolOf(params); + } + + Symbol[][] enterParams(Tree[][] vparams) { + Symbol[][] vparamSyms = new Symbol[vparams.length][]; + for (int i = 0; i < vparams.length; i++) { + vparamSyms[i] = enterParams(vparams[i]); + } + return vparamSyms; + } + + Type makeMethodType(Symbol[] tparams, Symbol[][] vparams, Type restpe) { + if (tparams.length == 0 && vparams.length == 0) { + return Type.PolyType(tparams, restpe); + } else { + Type result = restpe; + for (int i = vparams.length - 1; i >= 0; i--) + result = Type.MethodType(vparams[i], result); + if (tparams.length != 0) + result = Type.PolyType(tparams, result); + return result; + } + } + + /** Re-enter type parameters in current scope. + */ + void reenterParams(Tree[] params) { + for (int i = 0; i < params.length; i++) + context.scope.enter(params[i].symbol()); + } + + /** Re-enter value parameters in current scope. + */ + void reenterParams(Tree[][] vparams) { + for (int i = 0; i < vparams.length; i++) + reenterParams(vparams[i]); + } + +// Attribution and Transform ------------------------------------------------- + + /** Attribute an identifier consisting of a simple name or an outer reference. + * @param tree The tree representing the identifier. + * @param name The name of the identifier. + */ + Tree transformIdent(Tree tree, Name name) { + // find applicable definition and assign to `sym' + Symbol sym = Symbol.NONE; + Type pre; + Type symtype; + + int stopPos = Integer.MIN_VALUE; + Context nextcontext = context; + while (sym.kind == NONE && nextcontext != Context.NONE) { + sym = nextcontext.scope.lookup(name); + if (sym.kind != NONE) { + stopPos = sym.pos; + } else { + nextcontext = nextcontext.enclClass; + if (nextcontext != Context.NONE) { + sym = nextcontext.owner.info().lookup(name); + if (sym.kind != NONE) { + stopPos = nextcontext.owner.pos; + } else { + nextcontext = nextcontext.outer; + } + } + } + } + + // find applicable import and assign to `sym1' + ImportList nextimports = context.imports; + ImportList lastimports = null; + Symbol sym1 = Symbol.NONE; + +// System.out.println("name = " + name + ", pos = " + tree.pos + ", importlist = ");//DEBUG +// for (ImportList imp = nextimports; imp != null; imp = imp.prev) { +// new TextTreePrinter().print(" ").print(imp.tree).println().end();//debug +// } + + while (nextimports != null && nextimports.tree.pos >= tree.pos) { + nextimports = nextimports.prev; + } + while (sym1.kind == NONE && + nextimports != null && nextimports.tree.pos > stopPos) { + sym1 = nextimports.importedSymbol(name); + lastimports = nextimports; + nextimports = nextimports.prev; + } + + // evaluate what was found + if (sym1.kind == NONE) { + if (sym.kind == NONE) { + return error(tree, "not found: " + NameTransformer.decode(name)); + } else { + sym.flags |= ACCESSED; + if (sym.owner().kind == CLASS) + pre = nextcontext.enclClass.owner.thisType(); + else + pre = Type.localThisType; + } + } else if (sym.kind != NONE && !sym.isPreloaded()) { + return error(tree, + "reference to " + name + " is ambiguous;\n" + + "it is both defined in " + sym.owner() + + " and imported subsequently by \n" + nextimports.tree); + } else { + // check that there are no other applicable imports in same scope. + while (nextimports != null && + nextimports.enclscope == lastimports.enclscope) { + if (!nextimports.sameImport(lastimports) && + nextimports.importedSymbol(name).kind != NONE) { + return error(tree, + "reference to " + name + " is ambiguous;\n" + + "it is imported twice in the same scope by\n " + + lastimports.tree + "\nand " + nextimports.tree); + } + nextimports = nextimports.prev; + } + sym = sym1; + sym.flags |= (ACCESSED | SELECTOR); + Tree qual = checkStable(deepCopy(lastimports.importPrefix())); + pre = qual.type; + //new TextTreePrinter().print(name + " => ").print(lastimports.tree).print("." + name).println().end();//DEBUG + tree = make.Select(tree.pos, qual, name); + } + symtype = pre.memberType(sym); + if (sym.isTerm() && (sym.flags & MUTABLE) == 0 && symtype.isObjectType()) { + //System.out.println("making single " + sym + ":" + symtype);//DEBUG + symtype = Type.singleType(pre, sym); + } + //System.out.println(name + ":" + symtype);//DEBUG + return tree.setSymbol(sym).setType(symtype); + } + + /** Attribute a selection where `tree' is `qual.name'. + * `qual' is already attributed. + */ + Tree transformSelect(Tree tree, Tree qual, Name name) { + Symbol[] uninst = Symbol.EMPTY_ARRAY; + switch (qual.type) { + case PolyType(Symbol[] tparams, Type restype): + qual = infer.mkTypeApply(qual, tparams, restype, Symbol.type(tparams)); + uninst = tparams; + } + Symbol sym = qual.type.lookup(name); + if (sym.kind == NONE) { + //System.out.println(qual.type + " has members " + qual.type.members());//DEBUG + return error(tree, + NameTransformer.decode(name) + " is not a member of " + qual.type.widen()); + } else if (!isAccessible(sym, qual)) { + return error(tree, name + " cannot be accessed in " + qual.type.widen()); + } else { + sym.flags |= (ACCESSED | SELECTOR); + Type symtype = qual.type.memberType(sym); + //System.out.println(sym.name + ":" + symtype);//debug + if (uninst.length != 0) { + switch (symtype) { + case PolyType(Symbol[] tparams, Type restype): + symtype = Type.PolyType( + tparams, new Infer.VirtualPolyType(uninst, restype)); + break; + default: + symtype = new Infer.VirtualPolyType(uninst, symtype); + } + } + if (sym.isTerm() && (sym.flags & MUTABLE) == 0 && symtype.isObjectType() && + qual.type.isStable()) + symtype = Type.singleType(qual.type, sym); + return copy.Select(tree, qual, name) + .setSymbol(sym).setType(symtype); + } + } + + /** Attribute a pattern matching expression where `pattpe' is the + * expected type of the patterns and `pt' is the expected type of the + * results. + */ + Tree transformVisitor(Tree tree, Type pattpe, Type pt) { + //System.out.println("trans visitor with " + pt);//DEBUG + switch (tree) { + case Visitor(Tree.CaseDef[] cases): + Tree.CaseDef[] cases1 = cases; + for (int i = 0; i < cases.length; i++) + cases1[i] = transformCase(cases[i], pattpe, pt); + return copy.Visitor(tree, cases1) + .setType(Type.lub(Tree.typeOf(cases1))); + default: + throw new ApplicationError(); + } + } + + /** Attribute a case where `pattpe' is the expected type of the pattern + * and `pt' is the expected type of the result. + */ + Tree.CaseDef transformCase(Tree.CaseDef tree, Type pattpe, Type pt) { + switch (tree) { + case CaseDef(Tree pat, Tree guard, Tree body): + pushContext(tree, context.owner, new Scope(context.scope)); + Tree pat1 = transform(pat, PATTERNmode, pattpe); + Tree guard1 = guard; + if (guard != Tree.Empty) + guard1 = transform(guard, EXPRmode, definitions.BOOLEAN_TYPE); + Tree body1 = transform(body, EXPRmode, pt); + popContext(); + return (Tree.CaseDef) copy.CaseDef(tree, pat1, guard1, body1) + .setType(body1.type); + default: + throw new ApplicationError(); + } + } + + Tree[] transformStatSeq(Tree[] stats, Symbol exprOwner) { + Tree[] stats1 = stats; + for (int i = 0; i < stats.length; i++) { + Tree stat = stats[i]; + if (context.owner.isCompoundSym() && !TreeInfo.isDeclaration(stat)) { + error(stat.pos, "only declarations allowed here"); + } + Tree stat1; + if (exprOwner.kind != NONE && !TreeInfo.isOwnerDefinition(stat)) { + pushContext(stat, exprOwner, context.scope); + if (TreeInfo.isDefinition(stat)) stat1 = transform(stat); + else stat1 = transform(stat, EXPRmode); + popContext(); + } else { + if (TreeInfo.isDefinition(stat)) stat1 = transform(stat); + else stat1 = transform(stat, EXPRmode); + } + if (stat1 != stat && stats1 == stats) { + stats1 = new Tree[stats.length]; + System.arraycopy(stats, 0, stats1, 0, i); + } + stats1[i] = stat1; + } + return stats1; + } + + /** Attribute a sequence of constructor invocations. + */ + Tree[] transformConstrInvocations(int pos, Tree[] constrs, + boolean delayArgs, Type pt) { + for (int i = 0; i < constrs.length; i++) { + pushContext(constrs[i], context.owner, context.scope); + context.delayArgs = delayArgs; + constrs[i] = transform(constrs[i], CONSTRmode, pt); + if (constrs[i].hasSymbol()) + constrs[i].symbol().initialize();//to detect cycles + popContext(); + } + return constrs; + } + + /** Attribute a template + */ + public Tree.Template transformTemplate(Tree.Template templ, Symbol owner) { + //System.out.println("transforming " + owner);//DEBUG + //System.out.println(owner.info());//DEBUG + Tree[] parents1 = transformConstrInvocations( + templ.pos, templ.parents, false, Type.AnyType); + if (owner.kind != ERROR) { + validateParentClasses(templ.parents, owner.info().parents()); + validateBaseTypes(owner); + } + pushContext(templ, owner, owner.members()); + templ.setSymbol(gen.localDummy(templ.pos, owner)); + Tree[] body1 = transformStatSeq(templ.body, templ.symbol()); + checkAllOverrides(owner); + popContext(); + if (owner.isTrait()) { + for (int i = 0; i < templ.parents.length; i++) + checkTrait(templ.parents[i], owner); + for (int i = 0; i < templ.body.length; i++) + checkPureDef(templ.body[i], owner); + } + Tree.Template templ1 = copy.Template(templ, parents1, body1); + templ1.setType(owner.type()); + return templ1; + } + + public Tree transformApply(Tree tree, Tree fn, Tree[] args) { + Tree fn1; + int argMode; + if ((mode & (EXPRmode | CONSTRmode)) != 0) { + fn1 = transform(fn, mode | FUNmode, Type.AnyType); + argMode = EXPRmode; + } else { + assert (mode & PATTERNmode) != 0; + fn1 = transform(fn, mode | FUNmode, pt); + argMode = PATTERNmode; + } + + // handle the case of application of match to a visitor specially + if (args.length == 1 && args[0] instanceof Visitor) { + Type pattp = matchQualType(fn1); + if (pattp == Type.ErrorType) { + return tree.setType(Type.ErrorType); + } else if (pattp != Type.NoType) { + Tree fn2 = desugarize.postMatch(fn1, context.enclClass.owner); + Tree arg1 = transformVisitor(args[0], pattp, pt); + return copy.Apply(tree, fn2, new Tree[]{arg1}) + .setType(arg1.type); + } + } + + // return prematurely if delayArgs is true and no type arguments + // need to be inferred. + if (context.delayArgs) { + switch (fn1.type) { + case MethodType(_, Type restp): + return tree.setType(restp); + } + } + + // type arguments with formals as prototypes if they exist. + fn1.type = infer.freshInstance(fn1.type); + Type[] argtypes = transformArgs( + tree.pos, fn1.symbol(), Symbol.EMPTY_ARRAY, fn1.type, argMode, args, pt); + + // propagate errors in arguments + if (argtypes == null) { + return tree.setType(Type.ErrorType); + } + for (int i = 0; i < argtypes.length; i++) { + if (argtypes[i] == Type.ErrorType) { + return tree.setType(Type.ErrorType); + } + } + + // resolve overloading + switch (fn1.type) { + case OverloadedType(Symbol[] alts, Type[] alttypes): + try { + infer.methodAlternative(fn1, alts, alttypes, argtypes, pt); + } catch (Type.Error ex) { + error(tree, ex.msg); + } + } + + switch (fn1.type) { + case PolyType(Symbol[] tparams, Type restp): + // if method is polymorphic, + // infer instance, and adapt arguments to instantiated formals + try { + fn1 = infer.methodInstance(fn1, tparams, restp, argtypes); + } catch (Type.Error ex) { + error(tree, ex.msg); + } + switch (fn1.type) { + case MethodType(Symbol[] params, Type restp1): + for (int i = 0; i < args.length; i++) { + args[i] = adapt(args[i], argMode, params[i].type()); + } + return copy.Apply(tree, fn1, args) + .setType(restp1); + } + break; + case MethodType(Symbol[] params, Type restp): + // if method is monomorphic, + // check that it can be applied to arguments. + if (infer.isApplicable(fn1.type, argtypes, Type.AnyType)) { + return copy.Apply(tree, fn1, args) + .setType(restp); + } + } + + if (fn1.type == Type.ErrorType) + return tree.setType(Type.ErrorType); + + new TextTreePrinter().print(tree).println().end();//debug + return error(tree, + infer.applyErrorMsg( + "", fn1, " cannot be applied to ", argtypes, pt)); + + } + + /** Attribute an argument list. + * @param pos Position for error reporting + * @param meth The symbol of the called method, or `null' if none exists. + * @param tparams The type parameters that need to be instantiated + * @param methtype The method's type w/o type parameters + * @param argMode The argument mode (either EXPRmode or PATTERNmode) + * @param args The actual arguments + * @param pt The proto-resulttype. + * @return The vector of instantiated argument types, or null if error. + */ + Type[] transformArgs(int pos, Symbol meth, Symbol[] tparams, Type methtype, + int argMode, Tree[] args, Type pt) { + //System.out.println("trans args " + meth + ArrayApply.toString(tparams) + ":" + methtype + "," + pt);//DEBUG + Type[] argtypes = new Type[args.length]; + switch (methtype) { + case MethodType(Symbol[] params, Type restp): + if (params.length != args.length) { + error(pos, "wrong number of arguments" + + (meth == null ? "" : " for " + meth)); + return null; + } + if (tparams.length == 0) { + for (int i = 0; i < args.length; i++) { + args[i] = transform(args[i], argMode, params[i].type()); + argtypes[i] = args[i].type; + } + } else { + // targs: the type arguments inferred from the prototype + Type[] targs = infer.protoTypeArgs(tparams, restp, pt, params); + + // argpts: prototypes for arguments + Type[] argpts = new Type[params.length]; + for (int i = 0; i < params.length; i++) + argpts[i] = params[i].type().subst(tparams, targs); + + // transform arguments with [targs/tparams]params.type as prototypes + for (int i = 0; i < args.length; i++) + args[i] = transform( + args[i], argMode | POLYmode, + params[i].type().subst(tparams, targs)); + + // targs1: same as targs except that every AnyType is mapped to + // formal parameter type. + Type[] targs1 = new Type[targs.length]; + for (int i = 0; i < targs.length; i++) + targs1[i] = (targs[i] != Type.AnyType) ? targs[i] + : tparams[i].type(); + + for (int i = 0; i < args.length; i++) { + argtypes[i] = args[i].type; + switch (argtypes[i]) { + case PolyType(Symbol[] tparams1, Type restype1): + argtypes[i] = infer.argumentTypeInstance( + tparams1, restype1, + params[i].type().subst(tparams, targs1), + argpts[i]); + } + } + } + return argtypes; + + case PolyType(Symbol[] tparams1, Type restp): + Symbol[] tparams2; + if (tparams.length == 0) tparams2 = tparams1; + else { + tparams2 = new Symbol[tparams.length + tparams1.length]; + System.arraycopy(tparams, 0, tparams2, 0, tparams.length); + System.arraycopy(tparams1, 0, tparams2, tparams.length, tparams1.length); + } + return transformArgs(pos, meth, tparams2, restp, argMode, args, pt); + + default: + for (int i = 0; i < args.length; i++) { + args[i] = transform(args[i], argMode, Type.AnyType); + argtypes[i] = args[i].type; + } + } + return argtypes; + } + + /** Atribute an expression or pattern with prototype `pt'. + * Check that expression's type conforms to `pt'. + * Resolve overloading and apply parameterless functions. + * Insert `apply' function if needed. + */ + Tree transform(Tree tree, int mode, Type pt) { + //new TextTreePrinter().print("transforming ").print(tree).println().end();//DEBUG + int savedMode = this.mode; + Type savedPt = this.pt; + this.mode = mode; + this.pt = pt; + Tree tree1 = adapt(transform(tree), mode, pt); + this.mode = savedMode; + this.pt = savedPt; + return tree1; + } + + Tree adapt(Tree tree, int mode, Type pt) { + //new TextTreePrinter().print(tree).print(" adapt " + pt).println().end();//DEBUG + switch (tree.type) { + case OverloadedType(Symbol[] alts, Type[] alttypes): + // resolve overloading + if ((mode & FUNmode) == 0) { + try { + infer.exprAlternative(tree, alts, alttypes, pt); + } catch (Type.Error ex) { + error(tree, ex.msg); + } + switch (tree.type) { + case OverloadedType(_, _): + // overload resolution failed bcs no alternative matched prototype. + typeError(tree.pos, tree.type, pt); + tree.setSymbol(Symbol.ERROR).setType(Type.ErrorType); + break; + default: + return adapt(tree, mode, pt); + } + } + break; + + case PolyType(Symbol[] tparams, Type restp): + // apply parameterless functions + // instantiate polymorphic expressions + if (tparams.length == 0) { + return adapt(tree.setType(restp), mode, pt); + } else if ((mode & (FUNmode | POLYmode)) == 0) { + try { + tree = infer.exprInstance(tree, tparams, restp, pt); + } catch (Type.Error ex) { + error(tree, ex.msg); + } + return adapt(tree, mode, pt); + } else if ((mode & EXPRmode) != 0) { + // will be instantiated later + return tree; + } + break; + + case MethodType(_, _): + // convert unapplied methods to functions. + if ((mode & (EXPRmode | FUNmode)) == EXPRmode && + infer.isCompatible(tree.type, pt)) { + checkEtaExpandable(tree.pos, tree.type); + return transform(desugarize.etaExpand(tree, tree.type), mode, pt); + } else if ((mode & (CONSTRmode | FUNmode)) == CONSTRmode) { + return error(tree, "missing arguments for class constructor"); + } + } + if ((mode & FUNmode) != 0) { + if ((mode & PATTERNmode) != 0) { + // set type to instantiated case class constructor + if (tree.type == Type.ErrorType) return tree; + Symbol clazz = tree.symbol().constructorClass(); + if (!clazz.isCaseClass()) + error(tree, clazz + " is not a case class"); + tree.type = clazz.constructor().type(); + switch (tree.type) { + case PolyType(Symbol[] tparams, Type restp): + try { + infer.constructorInstance(tree, tparams, restp, pt); + } catch (Type.Error ex) { + if (pt != Type.ErrorType) error(tree.pos, ex.msg); + tree.setType(Type.ErrorType); + } + } + return tree; + } else if ((mode & EXPRmode) != 0 && tree.type.isObjectType()) { + // insert apply method + Symbol applyMeth = tree.type.lookup(Names.apply); + if (applyMeth != Symbol.NONE && isAccessible(applyMeth, tree)) { + applyMeth.flags |= (ACCESSED | SELECTOR); + tree = make.Select(tree.pos, tree, Names.apply) + .setSymbol(applyMeth) + .setType(tree.type.memberType(applyMeth)); + return adapt(tree, mode, pt); + } + } + } else if ((mode & (QUALmode | EXPRmode)) == EXPRmode) { + // check that packages and static modules are not used as values + Symbol sym = tree.symbol(); + if (sym != null && sym.kind != ERROR && !sym.isValue() && tree.isTerm()) { + new TextTreePrinter().print(tree).println().end();//debug + error(tree.pos, tree.symbol() + " is not a value"); + } + } + + // check type against prototype + return tree.setType(checkType(tree.pos, tree.type, pt)); + } + + /** Transform expression or type with a given mode. + */ + public Tree transform(Tree tree, int mode) { + if ((mode & TYPEmode) == 0) + return transform(tree, mode, Type.AnyType); + + int savedMode = this.mode; + this.mode = mode; + Tree tree1 = transform(tree); + this.mode = savedMode; + + Symbol sym = tree1.symbol(); + if ((mode & FUNmode) == 0 && sym != null && sym.typeParams().length != 0) + return error(tree, sym + " takes type parameters."); + else + return tree1; + } + + Tree[] transform(Tree[] trees, int mode) { + for (int i = 0; i < trees.length; i++) + trees[i] = transform(trees[i], mode); + return trees; + } + + /** The main attribution function + */ + public Tree transform(Tree tree) { + Symbol sym = tree.symbol(); + if (sym != null && !sym.isInitialized()) sym.initialize(); + if (global.debug && TreeInfo.isDefinition(tree)) + System.out.println("transforming " + sym); + try { + switch (tree) { + + case Bad(): + tree.setType(Type.ErrorType); + return tree; + + case Empty: + tree.type = Type.NoType; + return tree; + + case PackageDef(Tree pkg, Tree.Template templ): + switch (templ) { + case Template(Tree[] parents, Tree[] body): + Symbol pkgSym = pkg.symbol(); + if (pkgSym != null && pkgSym.isPackage()) { + pushContext(templ, pkgSym, pkgSym.members()); + Tree[] body1 = transform(body); + popContext(); + Tree.Template templ1 = copy.Template(templ, parents, body1); + templ1.setType(Type.NoType).setSymbol(Symbol.NONE); + return copy.PackageDef(tree, pkg, templ1) + .setType(definitions.UNIT_TYPE); + } + } + return tree.setType(Type.ErrorType); + + case ClassDef(int mods, Name name, Tree.TypeDef[] tparams, Tree.ValDef[][] vparams, Tree tpe, Tree.Template templ): + pushContext(tree, sym.constructor(), new Scope(context.scope)); + reenterParams(tparams); + reenterParams(vparams); + Tree.TypeDef[] tparams1 = transform(tparams); + Tree.ValDef[][] vparams1 = transform(vparams); + Tree tpe1 = transform(tpe); + Tree.Template templ1 = transformTemplate(templ, sym); + popContext(); + return copy.ClassDef(tree, mods, name, tparams1, vparams1, tpe1, templ1) + .setType(definitions.UNIT_TYPE); + + case ModuleDef(int mods, Name name, Tree tpe, Tree.Template templ): + Tree tpe1 = transform(tpe, TYPEmode); + Tree.Template templ1 = transformTemplate(templ, sym.moduleClass()); + return copy.ModuleDef(tree, mods, name, tpe1, templ1) + .setType(definitions.UNIT_TYPE); + + case ValDef(int mods, Name name, Tree tpe, Tree rhs): + Tree tpe1 = transform(tpe, TYPEmode); + Tree rhs1 = rhs; + if (tpe1 == Tree.Empty) { + tpe1 = gen.mkType(rhs1.pos, rhs.type); + // rhs already attributed by defineSym in this case + } else if (rhs != Tree.Empty) { + rhs1 = transform(rhs1, EXPRmode, sym.type()); + } + return copy.ValDef(tree, mods, name, tpe1, rhs1) + .setType(definitions.UNIT_TYPE); + + case DefDef(int mods, Name name, Tree.TypeDef[] tparams, Tree.ValDef[][] vparams, Tree tpe, Tree rhs): + pushContext(tree, sym, new Scope(context.scope)); + reenterParams(tparams); + reenterParams(vparams); + Tree.TypeDef[] tparams1 = transform(tparams); + Tree.ValDef[][] vparams1 = transform(vparams); + Tree tpe1 = transform(tpe, TYPEmode); + Tree rhs1 = rhs; + if (tpe1 == Tree.Empty) { + tpe1 = gen.mkType(rhs1.pos, rhs1.type); + // rhs already attributed by defineSym in this case + } else if (rhs != Tree.Empty) { + rhs1 = transform(rhs, EXPRmode, + tpe1.type == Type.NoType ? Type.AnyType : tpe1.type); + } + popContext(); + return copy.DefDef(tree, mods, name, tparams1, vparams1, tpe1, rhs1) + .setType(definitions.UNIT_TYPE); + + case TypeDef(int mods, Name name, Tree.TypeDef[] tparams, Tree rhs): + pushContext(tree, sym, new Scope(context.scope)); + reenterParams(tparams); + Tree.TypeDef[] tparams1 = transform(tparams); + Tree rhs1 = transform(rhs, TYPEmode); + popContext(); + return copy.TypeDef(tree, mods, name, tparams1, rhs1) + .setType(definitions.UNIT_TYPE); + + case Import(Tree expr, Name[] selectors): + context.imports = new ImportList(tree, context.scope, context.imports); + return Tree.Empty; + + case Block(Tree[] stats): + pushContext(tree, context.owner, new Scope(context.scope)); + int lastmode = mode; + Tree[] stats1 = desugarize.Statements(stats, true); + enterSyms(stats1); + context.imports = context.outer.imports; + for (int i = 0; i < stats1.length - 1; i++) + stats1[i] = transform(stats1[i], EXPRmode); + Type tp; + if (stats1.length > 0) { + stats1[stats1.length - 1] = + transform(stats1[stats1.length - 1], lastmode, pt); + tp = checkNoEscape(tree.pos, stats1[stats1.length - 1].type); + } else { + tp = definitions.UNIT_TYPE; + } + popContext(); + return copy.Block(tree, stats1) + .setType(tp); + + case Visitor(Tree.CaseDef[] cases): + return transform(desugarize.Visitor(tree)); + + case Assign(Apply(Tree funarray, Tree[] vparam), Tree rhs): + return transform(desugarize.Update(tree)); + + case Assign(Tree lhs, Tree rhs): + Tree lhs1 = transform(lhs, EXPRmode); + Symbol varsym = lhs1.symbol(); + if (varsym != null && (varsym.flags & ACCESSOR) != 0) { + return transform(desugarize.Assign(tree.pos, lhs, rhs)); + } else if (varsym == null || (varsym.flags & MUTABLE) == 0) { + return error(tree, "assignment to non-variable"); + } else { + Tree rhs1 = transform(rhs, EXPRmode, lhs1.type); + return copy.Assign(tree, lhs1, rhs1) + .setType(definitions.UNIT_TYPE); + } + + case If(Tree cond, Tree thenp, Tree elsep): + Tree cond1 = transform(cond, EXPRmode, definitions.BOOLEAN_TYPE); + if (elsep == Tree.Empty) { + Tree thenp1 = + transform(thenp, EXPRmode, definitions.UNIT_TYPE); + Tree elsep1 = make.Block(tree.pos, Tree.EMPTY_ARRAY) + .setType(definitions.UNIT_TYPE); + return copy.If(tree, cond1, thenp1, elsep1) + .setType(definitions.UNIT_TYPE); + } else { + Tree thenp1 = transform(thenp, EXPRmode, pt); + Tree elsep1 = transform(elsep, EXPRmode, pt); + return copy.If(tree, cond1, thenp1, elsep1) + .setType(Type.lub(new Type[]{thenp1.type, elsep1.type})); + } + + case New(Tree.Template templ): + switch (templ) { + case Template(Tree[] parents, Tree[] body): + if (parents.length == 1 && body.length == 0) { + Tree parent1 = transform(parents[0], CONSTRmode, pt); + Tree.Template templ1 = (Tree.Template) + copy.Template(templ, new Tree[]{parent1}, body) + .setType(parent1.type).setSymbol(Symbol.NONE); + Type owntype = parent1.type; + if ((owntype.symbol().constructor().flags & + ABSTRACTCLASS) != 0) { + error(tree.pos, owntype.symbol() + + " is abstract; cannot be instantiated"); + } + return copy.New(tree, templ1) + .setType(owntype); + } else { + pushContext(tree, context.owner, new Scope(context.scope)); + Tree cd = make.ClassDef( + templ.pos, + 0, + Names.ANON_CLASS_NAME.toTypeName(), + Tree.ExtTypeDef.EMPTY_ARRAY, + Tree.ExtValDef.EMPTY_ARRAY_ARRAY, + Tree.Empty, + templ); + enterSym(cd); + cd = transform(cd); + Symbol clazz = cd.symbol(); + if (clazz.kind != CLASS) + return Tree.Bad().setType(Type.ErrorType); + + // compute template's type with new refinement scope. + Type[] parentTypes = clazz.info().parents(); + Scope refinement = new Scope(); + Type base = Type.compoundType(parentTypes, Scope.EMPTY); + Type tp = Type.compoundType( + parentTypes, refinement, clazz); + Scope.SymbolIterator it = clazz.members().iterator(); + while (it.hasNext()) { + Symbol sym1 = it.next(); + if (base.lookupNonPrivate(sym1.name).kind != NONE && + !base.memberType(sym1).isSameAs(sym1.type())) + refinement.enter(sym1); + } + if (refinement.elems == Scope.Entry.NONE && + parentTypes.length == 1) + tp = parentTypes[0]; + else + tp = checkNoEscape(tree.pos, tp); + + Tree alloc = + gen.Typed( + gen.New( + gen.mkRef(tree.pos, + Type.localThisType, clazz.constructor())), + tp); + popContext(); + return make.Block(tree.pos, new Tree[]{cd, alloc}) + .setType(tp); + } + default: + throw new ApplicationError(); + } + + case Typed(Tree expr, Tree tpe): + Tree tpe1 = transform(tpe, TYPEmode); + Tree expr1 = transform(expr, EXPRmode, tpe1.type); + return copy.Typed(tree, expr1, tpe1) + .setType(tpe1.type); + + case Tuple(Tree[] trees): + Tree tree1 = transform(desugarize.Tuple(tree), mode, pt); + if (trees.length > 0 && (mode & EXPRmode) != 0) + tree1 = desugarize.postTuple(tree1); + return tree1; + + case Function(Tree.ValDef[] vparams, Tree body): + pushContext(tree, context.owner, new Scope(context.scope)); + Type restype = desugarize.preFunction(vparams, pt); + enterParams(vparams); + Tree body1 = transform(body, EXPRmode, restype); + if (!infer.isFullyDefined(restype)) restype = body1.type; + popContext(); + Tree tree1 = copy.Function(tree, vparams, body1); + Tree tree2 = transform(desugarize.Function(tree1, restype)); + return desugarize.postFunction(tree2); + + case TypeApply(Tree fn, Tree[] args): + Tree fn1 = transform(fn, EXPRmode | FUNmode, Type.AnyType); + Tree[] args1 = transform(args, TYPEmode); + Type[] argtypes = Tree.typeOf(args1); + + // resolve overloading + switch (fn1.type) { + case OverloadedType(Symbol[] alts, Type[] alttypes): + try { + infer.polyAlternative(fn1, alts, alttypes, args.length); + } catch (Type.Error ex) { + error(tree, ex.msg); + } + } + + // match against arguments + switch (fn1.type) { + case PolyType(Symbol[] tparams, Type restp): + if (tparams.length == argtypes.length) { + int i = 0; + while (i < tparams.length && + (context.delayArgs || + argtypes[i].isSubType( + tparams[i].info().subst(tparams, argtypes)))) + i++; + if (i == tparams.length) { + return copy.TypeApply(tree, fn1, args1) + .setType(restp.subst(tparams, argtypes)); + } + } + break; + case ErrorType: + return tree.setType(Type.ErrorType); + } + return error(tree, + infer.toString(fn1.symbol(), fn1.type) + + " cannot be applied to " + + ArrayApply.toString(argtypes, "[", ",", "]")); + + case Apply(Tree fn, Tree[] args): + Tree tree1 = transformApply(tree, fn, args); + + // handle the case of a case method call specially. + Symbol fsym = TreeInfo.methSymbol(tree1); + if ((mode & (EXPRmode | FUNmode)) == EXPRmode && + fsym != null && (fsym.flags & CASE) != 0) { + Symbol constr = fsym.type().resultType().symbol().constructor(); + Template templ = make.Template( + tree1.pos, + new Tree[]{desugarize.toConstructor(tree1, constr)}, + Tree.EMPTY_ARRAY); + templ.setSymbol(Symbol.NONE).setType(tree1.type); + return adapt( + make.New(tree1.pos, templ).setType(tree1.type), mode, pt); + } else { + return tree1; + } + + case Super(Tree tpe): + Symbol enclClazz = context.enclClass.owner; + if (enclClazz != null) { + // we are in a class or module + Tree tpe1 = transform(tpe, TYPEmode); // ignored for now. + switch (enclClazz.info()) { + case CompoundType(Type[] parents, _): + return copy.Super(tree, tpe1) + .setType(Type.compoundType(parents, Scope.EMPTY)); + case ErrorType: + return tree.setType(Type.ErrorType); + default: + throw new ApplicationError(); + } + } else { + return error(tree, + "super can be used only in a class, module, or template"); + } + + case This(Tree qual): + if (qual == Tree.Empty) { + Symbol clazz = context.enclClass.owner; + if (clazz != null) { // we are in a class or module + return make.This( + tree.pos, + make.Ident(tree.pos, clazz.name) + .setSymbol(clazz).setType(clazz.type())) + .setType(clazz.thisType()); + } else { + return error( + tree, tree + + " can be used only in a class, module, or template"); + } + } else { + Tree qual1 = transform(qual, TYPEmode | FUNmode); + Symbol clazz = qual1.symbol(); + if (clazz.kind == CLASS) { + Context clazzContext = context.outerContext(clazz); + if (clazzContext != Context.NONE) { + return tree.setType(clazz.thisType()); + } else { + return error( + qual, clazz.name + " is not an enclosing class"); + } + } else { + return error(qual, "class identifier expected"); + } + } + + case Select(Tree qual, Name name): + Tree qual1 = transform(qual, EXPRmode | POLYmode | QUALmode); + if (name.isTypeName()) qual1 = checkStable(qual1); + return transformSelect( + tree, + adapt(qual1, EXPRmode | POLYmode | QUALmode, Type.AnyType), + name); + + case Ident(Name name): + if (mode == PATTERNmode && name.isVariable()) { + //System.out.println("pat var " + name + ":" + pt);//DEBUG + Symbol vble = new TermSymbol( + tree.pos, name, context.owner, 0).setType(pt); + if (name != Names.WILDCARD) enterInScope(vble); + return tree.setSymbol(vble).setType(pt); + } else { + return transformIdent(tree, name); + } + + case Literal(Object value): + return tree.setType(definitions.getType(value2TypeName(value))); + + case SingletonType(Tree ref): + Tree ref1 = transform(ref, EXPRmode, Type.AnyType); + return copy.SingletonType(tree, ref1) + .setType(checkObjectType(tree.pos, ref1.type)); + + case SelectFromType(Tree qual, Name name): + Tree qual1 = transform(qual, TYPEmode); + return transformSelect(tree, qual1, name); + + case CompoundType(Tree[] parents, Tree[] refinements): + Tree[] parents1 = transform(parents, TYPEmode); + Type[] ptypes = Tree.typeOf(parents); + Scope members = new Scope(); + Type self = Type.compoundType(ptypes, members); + Symbol clazz = self.symbol(); + validateBaseTypes(clazz); + pushContext(tree, clazz, members); + for (int i = 0; i < refinements.length; i++) { + enterSym(refinements[i]).flags |= OVERRIDE; + } + Tree[] refinements1 = transformStatSeq(refinements, Symbol.NONE); + checkAllOverrides(clazz); + popContext(); + return copy.CompoundType(tree, parents1, refinements1) + .setType(self); + + case AppliedType(Tree tpe, Tree[] args): + Tree tpe1 = transform(tpe, TYPEmode | FUNmode); + Tree[] args1 = transform(args, TYPEmode); + Type[] argtypes = Tree.typeOf(args); + Symbol clazz = tpe1.type.unalias().symbol(); + Symbol[] tparams = clazz.typeParams(); + if (tpe1.type != Type.ErrorType) { + if (tparams.length != args.length) { + if (tparams.length == 0) + return error(tree, tpe1.type + + " does not take type parameters"); + else + return error(tree, + "wrong number of type arguments for " + + tpe1.type); + } else { + try { + if (!context.delayArgs) + infer.checkBounds(tparams, argtypes, ""); + } catch (Type.Error ex) { + return error(tree, ex.msg); + } + } + return copy.AppliedType(tree, tpe1, args1) + .setType(Type.appliedType(tpe1.type, argtypes)); + } else { + return tpe1; + } + + case CovariantType(Tree tpe): + Tree tpe1 = transform(tpe, TYPEmode); + return copy.CovariantType(tree, tpe1) + .setType(Type.covarType(tpe1.type)); + + case FunType(_, _): + return transform(desugarize.FunType(tree)); + + case TupleType(Tree[] types): + Tree tree1 = desugarize.mkTupleType(tree.pos, types); + return transform(desugarize.mkTupleType(tree.pos, types)); + + default: + throw new ApplicationError("illegal tree: " + tree); + } + } catch (Type.Error ex) { + if (ex instanceof CyclicReference) { + if (global.debug) ex.printStackTrace();//DEBUG + CyclicReference cyc = (CyclicReference) ex; + if (cyc.info instanceof LazyTreeType) { + switch (((LazyTreeType) cyc.info).tree) { + case ValDef(_, _, _, _): + return error(tree, "recursive " + cyc.sym + " needs type"); + case DefDef(_, _, _, _, _, _): + return error(tree, "recursive " + cyc.sym + " needs result type"); + } + } + } + return error(tree, ex.msg); + } + } + +// Contexts ------------------------------------------------------------------- + + /** Push new context associated with given tree, owner, and scope on stack. + * Fields `imports' and, possibly, `enclClass' are inherited from parent. + */ + void pushContext(Tree tree, Symbol owner, Scope scope) { + context = new Context(tree, owner, scope, context); + } + + /** Pop context from stack. + */ + void popContext() { + context = context.outer; + } + +// Lazy Types ------------------------------------------------------------------ + + /** A lazy type which, when forced returns the type of a symbol defined + * in `tree'. + */ + class LazyTreeType extends Type.LazyType { + Tree tree; + Unit u; + Infer i; + Context c; + + LazyTreeType(Tree tree) { + this.tree = tree; + this.u = unit; + this.i = infer; + this.c = context; + } + public void complete(Symbol sym) { + //if (sym.isConstructor()) sym.constructorClass().initialize(); + //else if (sym.isModule()) sym.moduleClass().initialize(); + defineSym(tree, u, i, c); + } + } + + /** A lazy type for case constructor methods (whose name is a term name) + * which sets the method's type to the class constructor type. + */ + class LazyConstrMethodType extends LazyTreeType { + LazyConstrMethodType(Tree tree) { + super(tree); + } + public void complete(Symbol sym) { + sym.setInfo(tree.symbol().constructor().type()); + } + } +} + diff --git a/sources/scalac/typechecker/AnalyzerPhase.java b/sources/scalac/typechecker/AnalyzerPhase.java new file mode 100644 index 0000000000..9542da51e3 --- /dev/null +++ b/sources/scalac/typechecker/AnalyzerPhase.java @@ -0,0 +1,103 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** +** $Id$ +\* */ + +package scalac.typechecker; + +import scalac.*; +import scalac.util.*; +import scalac.ast.*; +import scalac.symtab.*; +import scalac.checkers.*; +import java.util.HashMap; +import java.util.ArrayList; + +public class AnalyzerPhase extends PhaseDescriptor { + + /* final */ Context startContext; + HashMap/*<Unit,Context>*/ contexts = new HashMap(); + ArrayList/*<Unit>*/ newSources = new ArrayList(); + + public void initialize(Global global, int id) { + super.initialize(global, id); + Definitions definitions = global.definitions; + this.startContext = new Context( + Tree.Empty, + definitions.ROOT_CLASS, + definitions.ROOT_CLASS.members(), + Context.NONE); + this.startContext.enclClass = this.startContext; + + if (!global.noimports) { + TreeFactory make = global.make; + + Tree java = make.Ident(Position.NOPOS, Names.java) + .setSymbol(definitions.JAVA) + .setType(Type.singleType(definitions.ROOT_TYPE, definitions.JAVA)); + Tree javalang = make.Select(Position.NOPOS, java, Names.lang) + .setSymbol(definitions.JAVALANG) + .setType(Type.singleType(java.type, definitions.JAVALANG)); + Tree importjavalang = make.Import( + Position.NOPOS, javalang, new Name[]{Names.WILDCARD}) + .setSymbol(definitions.JAVALANG) + .setType(definitions.UNIT_TYPE); + startContext.imports = new ImportList( + importjavalang, startContext.scope, startContext.imports); + + Tree scala = make.Ident(Position.NOPOS, Names.scala) + .setSymbol(definitions.SCALA) + .setType(Type.singleType(definitions.ROOT_TYPE, definitions.SCALA)); + Tree importscala = make.Import( + Position.NOPOS, scala, new Name[]{Names.WILDCARD}) + .setSymbol(definitions.SCALA) + .setType(definitions.UNIT_TYPE); + startContext.imports = new ImportList( + importscala, new Scope(), startContext.imports); + + scala = make.Ident(Position.NOPOS, Names.scala) + .setSymbol(definitions.SCALA) + .setType(scala.type); + Symbol scalaPredefSym = definitions.getModule(Names.scala_Predef); + Tree scalaPredef = make.Select(Position.NOPOS, scala, Names.Predef) + .setSymbol(scalaPredefSym) + .setType(Type.singleType(scala.type, scalaPredefSym)); + + Tree importscalaPredef = make.Import( + Position.NOPOS, scalaPredef, new Name[]{Names.WILDCARD}) + .setSymbol(scalaPredefSym) + .setType(definitions.UNIT_TYPE); + startContext.imports = new ImportList( + importscalaPredef, new Scope(), startContext.imports); + } + } + + public String name() { + return "analyze"; + } + + public String description () { + return "name and type analysis"; + } + + public String taskDescription() { + return "type checking"; + } + + public Phase createPhase(Global global) { + return new Analyzer(global, this); + } + + public Checker[] postCheckers(Global global) { + return new Checker[] { + /* todo: uncomment + new CheckSymbols(global), + new CheckTypes(global), + new CheckOwners(global) + */ + }; + } +} diff --git a/sources/scalac/typechecker/Context.java b/sources/scalac/typechecker/Context.java new file mode 100644 index 0000000000..3f1b30388f --- /dev/null +++ b/sources/scalac/typechecker/Context.java @@ -0,0 +1,63 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** +** $Id$ +\* */ + +package scalac.typechecker; + +import scalac.symtab.*; +import scalac.ast.Tree; + +public class Context { + Tree tree; // Tree associated with this context + Symbol owner; // The current owner + Scope scope; // The current scope + ImportList imports; // The current import list + Context outer; // The next outer context + Context enclClass = this; // The next outer context whose tree + // is a class template + boolean delayArgs = false; // delay checking of type arguments + + public Context() {} + + public Context(Tree tree, Context outer) { + this(tree, outer.owner, outer.scope, outer); + } + + public Context(Tree tree, Symbol owner, Scope scope, Context outer) { + this.tree = tree; + this.owner = owner; + this.scope = scope; + this.imports = outer.imports; + if (tree instanceof Tree.Template || + tree instanceof Tree.CompoundType) this.enclClass = this; + else this.enclClass = outer.enclClass; + this.delayArgs = outer.delayArgs; + this.outer = outer; + } + + public static Context NONE = new Context(); + + Context outerContext(Symbol clazz) { + Context c = this; + while (c != Context.NONE && c.owner != clazz) c = c.outer; + return c; + } + + boolean isTopLevel() { + switch (tree) { + case Block(_): + return false; + case Template(_, _): + return outer.tree instanceof Tree.PackageDef; + case Empty: + return true; + default: + return outer.isTopLevel(); + } + } +} + diff --git a/sources/scalac/typechecker/DeSugarize.java b/sources/scalac/typechecker/DeSugarize.java new file mode 100644 index 0000000000..fda3d0f492 --- /dev/null +++ b/sources/scalac/typechecker/DeSugarize.java @@ -0,0 +1,681 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.typechecker; + +import java.io.*; +import java.util.*; +import scalac.*; +import scalac.util.*; +import scalac.symtab.*; +import scalac.ast.*; +import scalac.ast.printer.*; +import Tree.*; + +/** A transformer for removing syntactic sugar. This transformer does + * not need any type or symbol-table information. + * + * @author Christine Roeckl, Martin Odersky + * @version 2.0 + */ +public class DeSugarize implements Kinds, Modifiers { + + /** the global environment + */ + protected Global global; + + /** the tree factory + */ + protected TreeFactory make; + + /** The copying factory + */ + protected TreeCopyFactory copy; + + /** the tree generator + */ + protected TreeGen gen; + + /** the type inferencer + */ + protected Infer infer; + + /** the name creator + */ + protected final FreshNameCreator freshNameCreator; + + /** the constructor + */ + public DeSugarize(Analyzer analyzer, Global global) { + this.global = global; + this.make = analyzer.make; + this.copy = analyzer.copy; + this.gen = analyzer.gen; + this.infer = analyzer.infer; + this.freshNameCreator = global.freshNameCreator; + } + + +// Auxiliary definitions and functions ------------------------------------------- + + /** introduce fresh variable of the form "deS$56" + */ + Name getvar() { + return freshNameCreator.newName("ds", '$'); + } + + Name setterName(Name name) { + return name.append(Names._EQ); + } + + Name parameterName(int i) { + return Name.fromString("x$" + i); + } + + Name tupleSelectorName(int i) { + return Name.fromString("_" + i); + } + + /** extract variables from a pattern + */ + void getVariables(Tree tree, ArrayList vars) { + switch(tree) { + case Ident(Name name): + if (name.isVariable()) vars.add(name); + break; + case Typed(Tree expr, Tree type): + getVariables(expr, vars); + break; + case Apply(Tree fn, Tree[] args): + switch (fn) { + case Apply(_, _): getVariables(fn, vars); + } + for (int i = 0; i < args.length; i++) + getVariables(args[i], vars); + break; + case Tuple(Tree[] elems): + for (int i = 0; i < elems.length; i++) + getVariables(elems[i], vars); + break; + default: + throw new ApplicationError ("illegal pattern", tree); + } + } + +// Transform functions ----------------------------------------------------- + + /** [T_1, ..., T_N] => scala.TupleN[+T_1, ..., +T_N] + */ + public Tree mkTupleType(int pos, Tree[] types) { + assert types.length > 0; + Tree[] types1 = new Tree[types.length]; + for (int i = 0; i < types.length; i++) + types1[i] = make.CovariantType(pos, types[i]); + return make.AppliedType(pos, + gen.mkTycon(pos, + global.definitions.getType( + Name.fromString("scala.Tuple" + types.length))), + types1); + } + + /** (T_1, ..., T_N) => T ==> scala.FunctionN[T_1, ..., T_N, +T] + */ + public Tree FunType(Tree tree) { + switch(tree) { + case FunType(Tree[] argtpes, Tree restpe): + Tree[] types = new Tree[argtpes.length + 1]; + System.arraycopy(argtpes, 0, types, 0, argtpes.length); + types[argtpes.length] = make.CovariantType(restpe.pos, restpe); + return make.AppliedType(tree.pos, + make.Select(tree.pos, + make.Ident(tree.pos, Names.scala), + Name.fromString("Function" + argtpes.length).toTypeName()), + types); + default: + throw new ApplicationError("function type expected", tree); + } + } + + /** [] ==> scala.Nil() + * [e_1,...,e_N] ==> scala.TupleN(e_1,...,e_n) (N >= 1) + * mode is either EXPRmode or PATmode + */ + public Tree Tuple(Tree tree) { + switch(tree) { + case Tuple(Tree[] trees): + Name n = trees.length == 0 ? Names.Nil + : Name.fromString("Tuple" + trees.length); + Tree select = make.Select(tree.pos, + make.Ident(tree.pos, Names.scala), n.toConstrName()); + return make.Apply(tree.pos, select, trees); + + default: + throw new ApplicationError("tuple expected", tree); + } + } + + /** constr call C of type TupleN[T_1,...,T_N] ==> (C: TupleN[+T_1,...,+T_N]) + */ + Tree postTuple(Tree tree) { + Type[] targs = tree.type.typeArgs(); + if (targs.length == 0) return tree; + else return gen.Typed(tree, global.definitions.tupleType(targs)); + } + + /** Convert method to function type. + */ + Type meth2fun(Type tp) { + switch (tp) { + case MethodType(Symbol[] params, Type restype): + return global.definitions.functionType( + Symbol.type(params), meth2fun(restype)); + default: + return tp; + } + } + + /** If `pt' is a matching function type insert missing parameters + * in `vparams' from it, and return result type, + * else return AnyType. + */ + public Type preFunction(ValDef[] vparams, Type pt) { + switch (pt) { + case TypeRef(Type pre, Symbol psym, Type[] ptargs): + if (psym.fullName().startsWith(Names.scala_Function) && + ptargs.length == vparams.length + 1) { + for (int i = 0; i < vparams.length; i++) + assignType(vparams[i], ptargs[i]); + return ptargs[vparams.length].dropVariance(); + } + } + return Type.AnyType; + } + //where + void assignType(ValDef vparam, Type pt) { + if (vparam.tpe == Tree.Empty && infer.isFullyDefined(pt)) + vparam.tpe = gen.mkType(vparam.pos, pt); + } + + /** (x_1: T_1, ..., x_n: T_N) => e ==> + * new scala.Function[T_1, ..., T_N, T] with { + * def apply(x_1: T_1, ..., x_N: T_N): T = e + * } + * where T = `restpe' + * T_i = `argtpes[i]' + * T_i's might be missing in the original tree. + */ + public Tree Function(Tree tree, Type restype) { + assert !restype.isCovarType(); + switch (tree) { + case Function(ValDef[] vparams, Tree body): + int length = vparams.length; + Tree restpe = gen.mkType(tree.pos, meth2fun(restype)); + + Tree[] argtpes = new Tree[length + 1]; + for (int i = 0; i < length; i++) + argtpes[i] = vparams[i].tpe; + argtpes[vparams.length] = restpe; + Tree constr = make.TypeApply(tree.pos, + make.Select(tree.pos, + make.Ident(tree.pos, Names.scala), + Name.fromString("Function" + length).toConstrName()), + argtpes); + + Tree applyDef = make.DefDef( + tree.pos, 0, Names.apply, + Tree.ExtTypeDef.EMPTY_ARRAY, new ValDef[][]{vparams}, + restpe, body); + + Tree result = make.New(tree.pos, + make.Template(tree.pos, new Tree[]{constr}, new Tree[]{applyDef})); + print(tree, "mkfun", result); + return result; + default: + throw new ApplicationError(); + } + } + + /** e of type FunctionN[T_1,...,T_N, T] --> + * (e: FunctionN[T_1,...,T_n, +T]) + */ + Tree postFunction(Tree tree) { + Type[] targs = tree.type.typeArgs(); + if (targs.length >= 1) { + Type[] targs1 = new Type[targs.length - 1]; + System.arraycopy(targs, 0, targs1, 0, targs1.length); + Tree result = gen.Typed( + tree, + global.definitions.functionType(targs1, targs[targs1.length])); + print(tree, "postfun", result); + return result; + } else return tree; + } + + /** match => this.match + * match[targs] => this.match[targs] + * tree is already attributed and attributes need to be preserved. + */ + Tree postMatch(Tree tree, Symbol currentclazz) { + switch (tree) { + case Ident(Name name): + return + make.Select(tree.pos, + make.This(tree.pos, Tree.Empty).setType(currentclazz.type()), + name).setSymbol(tree.symbol()).setType(tree.type); + case TypeApply(Tree fn, Tree[] args): + return copy.TypeApply(tree, postMatch(fn, currentclazz), args); + default: + return tree; + } + } + + /** { cases } ==> (x => x.match {cases}) + * only called when match has to be added + * no type for parameter x + */ + public Tree Visitor(Tree tree) { + switch(tree) { + case Visitor(CaseDef[] cases): + Name x = getvar(); + ValDef param = (ValDef) make.ValDef( + tree.pos, 0, x, Tree.Empty, Tree.Empty); + Tree xuse = make.Ident(tree.pos, x); + // x.match {cases} + Tree body = make.Apply(tree.pos, + make.Select(tree.pos, xuse, Names.match), + new Tree[]{tree}); + return make.Function(tree.pos, new ValDef[]{param}, body); + default: + throw new ApplicationError("visitor expected", tree); + } + } + + /** e = e' ==> e_=(e') + */ + public Tree Assign(int pos, Tree lhs, Tree rhs) { + Tree lhs1; + switch (lhs) { + case Ident(Name name): + lhs1 = make.Ident(lhs.pos, setterName(name)); + break; + case Select(Tree qual, Name name): + lhs1 = make.Select(lhs.pos, qual, setterName(name)); + break; + default: + throw new ApplicationError(); + } + return make.Apply(pos, lhs1, new Tree[]{rhs}); + } + + /** e(args) = e' ==> e.update(args ; e') + */ + public Tree Update(Tree tree) { + switch(tree) { + case Assign(Apply(Tree fn, Tree[] args), Tree rhs): + // e.update + Tree update = make.Select(fn.pos, fn, Names.update); + Tree[] args1 = new Tree[args.length + 1]; + System.arraycopy(args, 0, args1, 0, args.length); + args1[args.length] = rhs; + return make.Apply(tree.pos, update, args1); + default: + throw new ApplicationError(); + } + } + + /** expand pattern definitions and variable definitions in templates. + */ + public Tree[] Statements(Tree[] stats, boolean isLocal) { + boolean change = false; + for (int i = 0; i < stats.length && !change; i++) { + switch (stats[i]) { + case PatDef(_, _, _): + change = true; + break; + case ValDef(int mods, _, _, _): + change = !isLocal && (mods & MUTABLE) != 0; + } + } + if (change) { + TreeList ts = new TreeList(); + for (int i = 0; i < stats.length; i++) { + switch (stats[i]) { + case PatDef(_, _, _): + ts.append(this.PatDef(stats[i])); + break; + case ValDef(int mods, _, _, _): + if (!isLocal && (mods & MUTABLE) != 0) + ts.append(this.VarDef(stats[i])); + else + ts.append(stats[i]); + break; + default: + ts.append(stats[i]); + } + } + stats = ts.toArray(); + //TextTreePrinter p = new TextTreePrinter();//debug + //p.print("desugarized:");//debug + //for (int i = 0; i < stats.length; i++) p.print(stats[i]).println();//debug + //p.end();//debug + return stats; + } else { + return stats; + } + } + + /** expands pattern definitions + * in case pattern is a simple (typed) identifier: + * val x = e ==> val x = e + * val x: T = e ==> val x: T = e + * + * in case there are no variables in pattern + * val p = e ==> e.match (case p => ()) + * + * in case there is exactly one variable in pattern + * val x_1 = e.match (case p => (x_1)) + * + * in case there are more variables in pattern + * val p = e ==> private synthetic val t$ = e.match (case p => (x_1, ..., x_N)) + * val x_1 = t$._1 + * ... + * val x_N = t$._N + * + */ + public Tree[] PatDef(Tree tree) { + switch(tree) { + + case PatDef(int mods, Ident(Name name), Tree rhs): + // val x = e ==> val x = e + return new Tree[]{ + make.ValDef(tree.pos, mods, name, Tree.Empty, rhs)}; + + case PatDef(int mods, Typed(Ident(Name name), Tree type), Tree rhs): + // val x: T = e ==> val x: T = e + return new Tree[]{ + make.ValDef(tree.pos, mods, name, type, rhs)}; + + case PatDef(int mods, Tree pat, Tree rhs): + int pos = tree.pos; + ArrayList varlist = new ArrayList(); + getVariables(pat, varlist); + Name[] vars = new Name[varlist.size()]; + varlist.toArray(vars); + + // (x_1, ..., x_N) + Tree[] vtree = new Tree[vars.length]; + for (int i = 0; i < vars.length; i++) { + vtree[i] = make.Ident(pos, vars[i]); + } + Tree tuple = this.Tuple(make.Tuple(tree.pos, vtree)); + + // e.match (case p => (x_1, ..., x_N)) + CaseDef[] cases = {make.CaseDef(pos, pat, Tree.Empty, tuple)}; + Tree match = make.Apply(pos, + make.Select(pos, rhs, Names.match), + new Tree[]{make.Visitor(pos, cases)}); + + if (vars.length == 0) { + // e.match (case p => ()) + return new Tree[]{match}; + } else if (vars.length == 1) { + // val x_1 = e.match (case p => (x_1)) + return new Tree[]{ + make.ValDef(pos, mods, vars[0], Tree.Empty, match)}; + } else { + // t$ + Name var = getvar(); + + // private synthetic val t$ = e.match (case p => (x_1, ..., x_N)) + Tree[] res = new Tree[vars.length + 1]; + res[0] = make.ValDef(pos, PRIVATE | SYNTHETIC, var, + Tree.Empty, match); + for (int i = 0; i < vars.length; i ++) { + // val x_i = t$._i + res[i + 1] = make.ValDef( + pos, mods, vars[i], Tree.Empty, + make.Select(pos, make.Ident(pos, var), tupleSelectorName(i + 1))); + } + print(pat, " -> ", new Block(res));//debug + return res; + } + default: + throw new ApplicationError("pattern definition expected", tree); + } + } + + public Tree[] VarDef(Tree tree) { + switch (tree) { + case ValDef(int mods, Name name, Tree tpe, Tree rhs): + Name varname = Name.fromString(name + "$"); + Tree vardef1 = copy.ValDef( + tree, PRIVATE | MUTABLE | SYNTHETIC, varname, tpe, rhs); + Tree getter = make.DefDef( + tree.pos, mods | ACCESSOR, name, + Tree.ExtTypeDef.EMPTY_ARRAY, + Tree.ExtValDef.EMPTY_ARRAY_ARRAY, + tpe, + (rhs == Tree.Empty) ? Tree.Empty : make.Ident(tree.pos, varname)); + Tree setter = make.DefDef( + tree.pos, mods | ACCESSOR, setterName(name), + Tree.ExtTypeDef.EMPTY_ARRAY, + new ValDef[][]{{ + (ValDef) make.ValDef( + tree.pos, SYNTHETIC, parameterName(0), tpe, Tree.Empty)}}, + gen.mkType(tree.pos, global.definitions.UNIT_TYPE), + (rhs == Tree.Empty) ? Tree.Empty + : make.Assign( + tree.pos, + make.Ident(tree.pos, varname), + make.Ident(tree.pos, parameterName(0)))); + if (rhs == Tree.Empty) return new Tree[]{getter, setter}; + else return new Tree[]{vardef1, getter, setter}; + default: + throw new ApplicationError(); + } + } + + /** Tree represents an application of a constructor method of a case class + * (whose name is a term name). Convert this tree to application of + * the case classe's primary constructor `constr'. + */ + public Tree toConstructor(Tree tree, Symbol constr) { + switch (tree) { + case Apply(Tree fn, Tree[] args): + return copy.Apply(tree, toConstructor(fn, constr), args); + case TypeApply(Tree fn, Tree[] args): + return copy.TypeApply(tree, toConstructor(fn, constr), args); + case Ident(Name name): + return copy.Ident(tree, constr.name).setSymbol(constr); + case Select(Tree qual, Name name): + return copy.Select(tree, qual, constr.name).setSymbol(constr); + default: + throw new ApplicationError(); + } + } + + /** Expand partial function applications of type `type'. + * + * p.f(es_1)...(es_n) + * ==> { + * private synthetic val eta$f = p.f // if p is not stable + * ... + * private synthetic val eta$e_i = e_i // if e_i is not stable + * ... + * + * (ps_1 => ... => ps_m => eta$f([es_1])...([es_m])(ps_1)...(ps_m)) + * } + * tree is already attributed + */ + public Tree etaExpand(Tree tree, Type type) { + TreeList defs = new TreeList(); + Tree lambda = + toFunction(toApply(liftoutPrefix(tree, defs), type), type); + defs.append(lambda); + Tree result = make.Block(tree.pos, defs.toArray()); + print(tree, "eta", result);//debug + return result; + } + + private static String preName = "eta$"; + + /** Append to `defs' value definitions for all non-stable subexpressions + * of the function application `tree' + */ + public Tree liftoutPrefix(Tree tree, TreeList defs) { + switch (tree) { + case Ident(_): + return tree; + + case Select(Tree qual, Name name): + return copy.Select(tree, liftout(qual, defs), name); + + case TypeApply(Tree fn, Tree[] args): + return copy.TypeApply(tree, liftoutPrefix(fn, defs), args); + + case Apply(Tree fn, Tree[] args): + return copy.Apply(tree, liftoutPrefix(fn, defs), liftout(args, defs)); + + default: + throw new ApplicationError(); + } + } + + public Tree[] liftout(Tree[] trees, TreeList defs) { + Tree[] trees1 = trees; + for (int i = 0; i < trees.length; i++) { + Tree tree = trees[i]; + Tree tree1 = liftout(tree, defs); + if (tree1 != tree && trees1 == trees) { + trees1 = new Tree[trees.length]; + System.arraycopy(trees, 0, trees1, 0, trees.length); + } + trees1[i] = tree1; + } + return trees1; + } + + public Tree liftout(Tree tree, TreeList defs) { + if (!TreeInfo.isPureExpr(tree)) { + Name vname = Name.fromString(preName + defs.length()); + defs.append( + make.ValDef( + tree.pos, SYNTHETIC, vname, Tree.Empty, tree)); + return make.Ident(tree.pos, vname); + } else { + return tree; + } + } + + /** f, (syms_1)...(syms_n)T ==> f(ps_1)...(ps_n) + */ + Tree toApply(Tree tree, Type type) { + switch(type) { + case MethodType(Symbol[] vparams, Type restpe): + Tree res = make.Apply(tree.pos, tree, toIdents(vparams)); + return toApply(res, restpe); + default: + return tree; + } + } + + /** e, (syms_1)...(syms_n)T ==> (ps_1 => ... => ps_n => e) + */ + Tree toFunction(Tree tree, Type type) { + switch(type) { + case MethodType(Symbol[] vparams, Type restpe): + return this.Function( + make.Function(tree.pos, toVparams(vparams), toFunction(tree, restpe)), + restpe); + default: + return tree; + } + } + + /** Extract value parameters from type. + */ + ValDef[] toVparams(Symbol[] symbols) { + ValDef[] vpars = new ValDef[symbols.length]; + for (int i = 0; i < symbols.length; i++) { + vpars[i] = (ValDef)make.ValDef( + symbols[i].pos, 0, symbols[i].name, + gen.mkType(symbols[i].pos, symbols[i].type()), + Tree.Empty); + } + return vpars; + } + + /** Extract value identifiers from method type. + * It is assumed that all symbols are term symbols ==> make.Ident(). + */ + Tree[] toIdents(Symbol[] symbols) { + Tree[] idents = new Ident[symbols.length]; + for (int i = 0; i < symbols.length; i++) { + idents[i] = make.Ident(symbols[i].pos, symbols[i].name); + } + return idents; + } + + /** Build value element definitions and return its name. + */ + void addCaseElement(TreeList ts, ValDef vparam) { + //System.out.println("add case for " + vparam.name);//DEBUG + Name name = vparam.name; + vparam.name = Name.fromString(name + "$"); + ts.append( + make.ValDef( + vparam.pos, CASE, name, vparam.tpe, + make.Ident(vparam.pos, vparam.name))); + } + + /** add case constructor, value defintiions and access functions. + */ + Tree[] addCaseElements(Tree[] body, ValDef[] vparams) { + TreeList stats = new TreeList(); + for (int i = 0; i < vparams.length; i++) { + addCaseElement(stats, vparams[i]); + } + stats.append(body); + return stats.toArray(); + } + + /** Does list of types inherit from class scala.Algebraic? + */ + boolean inheritsAlgebraic(Type[] tps) { + Type algebraic = global.definitions.getType(Names.scala_Algebraic); + for (int i = 0; i < tps.length; i++) { + if (tps[i].isSubType(algebraic)) return true; + } + return false; + } + + /** Add toString, hashCode and == if class inherts from scala.Algebraic. + */ + Tree[] addCaseMethods(Tree[] body, Symbol clazz, Type[] parents) { + if (inheritsAlgebraic(parents)) { + /* todo uncomment and implement + TreeList stats = new TreeList(body); + Symbol[] params = clazz.constructor().firstParams(); + stats.append(toStringMethod(clazz, params)); + stats.append(hashcodeMethod(clazz, params)); + stats.append(equalsMethod(clazz, params)); + */ + } + return body; + } + + //debug + void print(Tree tree, String conv, Tree result) { + if (global.log()) { + new TextTreePrinter() + .print(tree).println() + .print(" --" + conv + "--> ").println() + .print(result).println().end(); + } + } +} diff --git a/sources/scalac/typechecker/ImportList.java b/sources/scalac/typechecker/ImportList.java new file mode 100644 index 0000000000..9e7e79110d --- /dev/null +++ b/sources/scalac/typechecker/ImportList.java @@ -0,0 +1,64 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** +** $Id$ +\* */ + +package scalac.typechecker; + +import scalac.*; +import scalac.util.*; +import scalac.symtab.*; +import scalac.ast.*; + +///////////////////////////////////////////////////////////////////////////// +// Import Lists +///////////////////////////////////////////////////////////////////////////// + +class ImportList { + Tree tree; // The import definition + Scope enclscope; // The scope in which the import occurs. + ImportList prev; // The previous active import list. + + ImportList(Tree tree, Scope enclscope, ImportList prev) { + this.tree = tree; + this.enclscope = enclscope; + this.prev = prev; + } + + Tree importPrefix() { + switch (tree) { + case Import(Tree expr, _): return expr; + default: throw new ApplicationError(); + } + } + + Type importType() { + return tree.symbol().type(); + } + + boolean sameImport(ImportList that) { + return this.importType().isSameAs(that.importType()); + } + + Symbol importedSymbol(Name name) { + Type t = this.importType(); + boolean renamed = false; + switch (tree) { + case Import(Tree expr, Name[] selectors): + for (int i = 0; i < selectors.length; i = i + 2) { + if (i + 1 < selectors.length && name == selectors[i + 1]) + return t.lookupNonPrivate(selectors[i]); + else if (name == selectors[i]) + renamed = true; + else if (selectors[i] == Names.WILDCARD && !renamed) + return t.lookupNonPrivate(name); + } + return Symbol.NONE; + default: + throw new ApplicationError(); + } + } +} diff --git a/sources/scalac/typechecker/Infer.java b/sources/scalac/typechecker/Infer.java new file mode 100644 index 0000000000..b9f2071cc0 --- /dev/null +++ b/sources/scalac/typechecker/Infer.java @@ -0,0 +1,814 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** +** $Id$ +\* */ + +package scalac.typechecker; + +import scalac.Global; +import scalac.ApplicationError; +import scalac.*; +import scalac.util.*; +import scalac.ast.*; +import scalac.symtab.*; + +public class Infer implements Modifiers, Kinds { + + Global global; + Definitions definitions; + TreeGen gen; + TreeFactory make; + Substituter substituter; + + public Infer(Transformer trans) { + this.global = trans.global; + this.definitions = global.definitions; + this.gen = trans.gen; + this.make = trans.make; + this.substituter = new Substituter(global, trans.descr, gen); + } + +// Error messages ------------------------------------------------------------- + + String applyErrorMsg(String msg1, Tree fn, + String msg2, Type[] argtypes, Type pt) { + return msg1 + toString(fn.symbol(), fn.type) + msg2 + + ArrayApply.toString(argtypes, "(", ",", ")") + + (pt == Type.AnyType ? "" : " with expected result type " + pt); + } + + String typeErrorMsg(String msg, Type found, Type req) { + return msg + + ";\n found : " + found.toLongString() + + "\n required: " + req; + } + + String overloadResolveErrorMsg(Symbol sym1, Type tpe1, Symbol sym2, Type tpe2) { + return "ambiguous reference to overloaded definition,\n" + + "both " + sym1 + ": " + tpe1 + "\n" + + "and " + sym2 + ": " + tpe2 + " match."; + } + + /** Give a string representation of symbol `sym' with type `tp' + * for error diagnostics. `sym' may be null. + */ + static String toString(Symbol sym, Type tp) { + return + (tp instanceof Type.OverloadedType ? "overloaded " : "") + + (sym == null ? "expression" : sym) + " of type " + tp; + } + +// Helper definitions --------------------------------------------------------- + + /** Is type `tp' a polymorphic method type? + */ + private boolean isPolymorphic(Type tp) { + return tp.typeParams().length > 0; + } + + /** Is type `tp' a parameterized method type? + */ + /** Is type `tp' a parameterized method type? + */ + boolean isParameterized(Type tp) { + switch (tp) { + case MethodType(_, _): return true; + default: return isPolymorphic(tp); + } + } + +// Tree Substitution ------------------------------------------------------------- + + static class Substituter extends Transformer { + + Symbol[] tparams; + Type[] targs; + TreeGen gen; + + public Substituter(Global global, PhaseDescriptor descr, TreeGen gen) { + super(global, descr); + this.gen = gen; + } + + public Tree apply(Tree tree, Symbol[] tparams, Type[] targs) { + this.tparams = tparams; + this.targs = targs; + return transform(tree); + } + + public Tree transform(Tree tree) { +// System.out.println("[" + ArrayApply.toString(targs,"",",","") + "/" + ArrayApply.toString(tparams,"",",","") + "]" + tree + "@" + tree.symbol());//DEBUG + if (tree.type == null) return tree; + tree.type = tree.type.subst(tparams, targs); + switch (tree) { + case Ident(Name name): + if (name.isTypeName()) { + Symbol sym = tree.symbol(); + for (int i = 0; i < tparams.length; i++) { + if (tparams[i].name == sym.name && + tparams[i].owner() == sym.owner()) { + return gen.mkType(tree.pos, targs[i]); + } + } + } + return tree; + default: + return super.transform(tree); + } + } + } + +// Type parameter inference ----------------------------------------------------- + + private static class NoInstance extends RuntimeException { + NoInstance(String msg) { + super(msg); + } + } + + public static class VirtualPolyType extends Type.PolyType { + VirtualPolyType(Symbol[] tparams, Type result) { + super(tparams, result); + } + public String toString() { + return + ArrayApply.toString(Symbol.defString(tparams), "[ ", ",", " ]") + + result; + } + } + + /** map every TypeVar to its constraint.inst field. + * throw a NoInstance exception if a NoType or AnyType is encountered. + */ + private static Type.Map instantiateMap = new Type.Map() { + public Type apply(Type t) { + return instantiate(t); + } + }; + + private static Type instantiate(Type tp) throws NoInstance { + switch (tp) { + case AnyType: + case NoType: + throw new NoInstance("undetermined type"); + case TypeVar(Type origin, Type.Constraint constr): + if (constr.inst != Type.NoType) return instantiate(constr.inst); + else throw new NoInstance("no unique instantiation of type variable " + + origin + " could be found"); + default: + return instantiateMap.map(tp); + } + } + + /** Map type variable to its instance, or, if `covariant' is true, + * to its upper bound (if this is not AnyType); + * or return `AnyType' if not possible. + */ + private Type instantiateUpper(Type tp, boolean covariant) throws NoInstance { + switch (tp) { + case TypeVar(Type origin, Type.Constraint constr): + if (constr.inst != Type.NoType) { + return instantiate(constr.inst); + } else if (covariant && constr.hibounds != Type.List.EMPTY) { + maximizeVar(tp); + return instantiate(constr.inst); + } + return Type.AnyType; + default: + throw new ApplicationError(); + } + } + + /** Is type fully defined, i.e. no embedded anytypes or typevars in it? + */ + public boolean isFullyDefined(Type tp) { + try { + instantiate(tp); + return true; + } catch (NoInstance ex) { + return false; + } + } + + /** Do type arguments `targs' conform to formal parameters `tparams'? + */ + private boolean isWithinBounds(Symbol[] tparams, Type[] targs) { + // check that covariant types do not appear in F-bounds. + for (int i = 0; i < targs.length; i++) { + if (targs[i].isCovarType()) { + for (int j = 0; j < tparams.length; j++) + if (tparams[j].info().contains(tparams[i])) + return false; + } + } + for (int i = 0; i < targs.length; i++) { + if (!targs[i].dropVariance().isSubType( + tparams[i].info().subst(tparams, targs))) + return false; + } + return true; + } + + /** throw a type error if arguments not within bounds. + */ + void checkBounds(Symbol[] tparams, Type[] targs, String prefix) { + if (!isWithinBounds(tparams, targs)) { + throw new Type.Error( + prefix + "type arguments " + + ArrayApply.toString(targs, "[", ",", "]") + " do not conform to " + + tparams[0].owner() + "'s type parameter bounds " + + ArrayApply.toString(Symbol.defString(tparams), "[", ",", "]")); + } + } + + /** Instantiate undetermined variable to its minimal upper bound. + * Throw a NoInstance exception if this not possible. + */ + private void maximizeVar(Type tp) throws NoInstance { + switch (tp) { + case TypeVar(Type origin, Type.Constraint constr): + if (constr.inst == Type.NoType) { + if (constr.hibounds == Type.List.EMPTY) + constr.inst = definitions.ANY_TYPE; + else if (constr.hibounds.tail == Type.List.EMPTY) + constr.inst = constr.hibounds.head; + else { + for (Type.List bs = constr.hibounds; + bs != Type.List.EMPTY && constr.inst == Type.NoType; + bs = bs.tail) { + //System.out.println("hibound: " + bs.head);//DEBUG + if (isSubSymOfAll(bs.head, constr.hibounds)) { + //System.out.println("best: " + bs.head);//DEBUG + constr.inst = bs.head.any2typevar(); + } + } + } + if (constr.inst == Type.NoType || + !isSubTypeOfAll(constr.inst, constr.hibounds)) { + throw new NoInstance( + "no unique maximal instance exists for type variable " + + origin); + } + } + return; + default: + throw new ApplicationError(); + } + } + //where + private boolean isSubSymOfAll(Type tp, Type.List tps) { + Symbol sym = tp.unalias().symbol(); + for (Type.List l = tps; l != Type.List.EMPTY; l = l.tail) { + if (!isSubSym(sym, l.head.unalias().symbol())) return false; + } + return true; + } + + private boolean isSubSym(Symbol sym, Symbol sym1) { + return + sym == sym1 || + sym.kind == ERROR || + (sym.kind == TYPE || sym.kind == CLASS) && sym.isSubClass(sym1); + } + + private boolean isSubTypeOfAll(Type tp, Type.List tps) { + for (Type.List l = tps; l != Type.List.EMPTY; l = l.tail) { + if (!tp.isSubType(l.head)) return false; + } + return true; + } + + private void minimizeVar(Type tp) { + switch (tp) { + case TypeVar(Type origin, Type.Constraint constr): + if (constr.inst == Type.NoType && constr.lobounds != Type.List.EMPTY) + constr.inst = Type.lub(constr.lobounds.toArray()); + return; + default: + throw new ApplicationError(); + } + } + + private Type[] freshVars(Symbol[] tparams) { + Type[] tvars = new Type[tparams.length]; + for (int i = 0; i < tvars.length; i++) { + tvars[i] = Type.TypeVar(tparams[i].type(), new Type.Constraint()); + } + return tvars; + } + + private Type.Map freshInstanceMap = new Type.Map() { + public Type apply(Type t) { + switch (t) { + case PolyType(Symbol[] tparams, Type restp): + Symbol[] newparams = new Symbol[tparams.length]; + for (int i = 0; i < tparams.length; i++) + newparams[i] = tparams[i].cloneSymbol(); + for (int i = 0; i < tparams.length; i++) + newparams[i].setInfo( + newparams[i].info().subst(tparams, newparams)); + return Type.PolyType( + newparams, apply(restp).subst(tparams, newparams)); + case OverloadedType(_, _): + return map(t); + default: + return t; + } + } + }; + + public Type freshInstance(Type tp) { + return freshInstanceMap.apply(tp); + } + + /** Automatically perform the following conversions on expression types: + * A method type becomes the corresponding function type. + * A nullary method type becomes its result type. + */ + private Type normalize(Type tp, Type pt) { + switch (tp) { + case MethodType(Symbol[] params, Type restype): + return global.definitions.functionType( + Symbol.type(params), normalize(restype, Type.AnyType)); + case PolyType(Symbol[] tparams, Type restype): + if (tparams.length == 0) return normalize(restype, pt); + } + return tp; + } + + boolean isCompatible(Type tp, Type pt) { + return normalize(tp, pt).isSubType(pt); + } + + private Symbol[] normalizeArgs(Type[] targs, Symbol[] tparams) { + Type.List uninstantiated = Type.List.EMPTY; + for (int i = 0; i < targs.length; i++) { + if (targs[i] == Type.NoType) { + targs[i] = tparams[i].type(); + uninstantiated = Type.List.append(uninstantiated, targs[i]); + } + } + return Type.symbol(uninstantiated.toArray()); + } + + /** Return inferred type arguments of polymorphic expression, given + * its type parameters and result type and a prototype `pt'. + * If no maximal type variables exists that make the + * instantiated type a subtype of `pt' and `lastTry' is true, return `null'. + */ + private Type[] instTypeArgs(Symbol[] tparams, Type restype, Type pt) { + Type[] tvars = freshVars(tparams); + // add all bounds except F-bounds to upper bounds of type variable. + for (int i = 0; i < tvars.length; i++) { + switch (tvars[i]) { + case TypeVar(_, Type.Constraint constr): + Type bound = tparams[i].info(); + if (!bound.containsSome(tparams)) + constr.hibounds = new Type.List(bound, Type.List.EMPTY); + } + } + Type insttype = restype.subst(tparams, tvars); + if (isCompatible(insttype, pt)) { + try { + Type[] targs = new Type[tvars.length]; + for (int i = 0; i < tvars.length; i++) { + maximizeVar(tvars[i]); + targs[i] = instantiate(tvars[i]); + } + return targs; + } catch (NoInstance ex) { + } + } + return null; + } + + /** As before, but: don't maximize. Instead map all unistantiated + * type vars to AnyType. + */ + public Type[] protoTypeArgs(Symbol[] tparams, Type restype, Type pt, + Symbol[] params) { + Type[] tvars = freshVars(tparams); + Type insttype = restype.subst(tparams, tvars); + Type[] targs = new Type[tvars.length]; + if (isCompatible(insttype, pt)) { + for (int i = 0; i < tvars.length; i++) { + targs[i] = instantiateUpper(tvars[i], isCovariant(tparams[i], params)); + } + } else { + for (int i = 0; i < tvars.length; i++) { + targs[i] = Type.AnyType; + } + } + return targs; + } + + /** Does given `tparam' occur only covariantly in symbols? + */ + private boolean isCovariant(Symbol tparam, Symbol[] syms) { + for (int i = 0; i < syms.length; i++) { + if (!isCovariant(tparam, syms[i])) return false; + } + return true; + } + + /** Does given `tparam' occur only covariantly in symbol? + */ + private boolean isCovariant(Symbol tparam, Symbol sym) { + switch (sym.kind) { + case ERROR: case VAL: case TYPE: return isCovariant(tparam, sym.info()); + case ALIAS: return !sym.info().contains(tparam); + default: return false; + } + } + + /** Does given `tparam' occur only covariantly in types? + */ + private boolean isCovariant(Symbol tparam, Type[] tps) { + for (int i = 0; i < tps.length; i++) { + if (!isCovariant(tparam, tps[i])) return false; + } + return true; + } + + /** Does given `tparam' occur only covariantly in argument types? + */ + private boolean isCovariantArgs(Symbol tparam, Type[] tps) { + for (int i = 0; i < tps.length; i++) { + switch (tps[i]) { + case CovarType(Type t): + if (!isCovariant(tparam, t)) return false; + break; + default: + if (tps[i].contains(tparam)) return false; + } + } + return true; + } + + /** Does given `tparam' occur only covariantly in type? + */ + private boolean isCovariant(Symbol tparam, Type tp) { + switch (tp) { + case ErrorType: + case AnyType: + case NoType: + case ThisType(Symbol sym): + return true; + case TypeRef(Type pre, Symbol sym, Type[] args): + return isCovariant(tparam, pre) && isCovariantArgs(tparam, args); + case SingleType(Type pre, Symbol sym): + return !pre.contains(tparam); + case CompoundType(Type[] parts, Scope members): + return isCovariant(tparam, parts) && + isCovariant(tparam, members.elements()); + default: + throw new ApplicationError(); + } + } + + /** Return inferred type arguments, given type parameters, formal parameters and + * argument types. + * If this is not possible, throw a `NoInstance' exception, or, if + * `needToSucceed' is false alternatively return `null'. + * Undetermined type arguments are represented by `NoType'. + * No check that inferred parameters conform to their bounds is made here. + */ + private Type[] methTypeArgs(Symbol[] tparams, Symbol[] params, Type[] argtypes, + boolean needToSucceed) throws NoInstance { + //System.out.println("methTypeArgs, tparams = " + ArrayApply.toString(tparams) + ", params = " + ArrayApply.toString(params) + ", type(params) = " + ArrayApply.toString(Symbol.type(params)) + ", argtypes = " + ArrayApply.toString(argtypes));//DEBUG + + Type[] tvars = freshVars(tparams); + Type[] formals = Symbol.type(params); + if (formals.length != argtypes.length) { + if (needToSucceed) + throw new NoInstance("parameter lists differ in length"); + return null; + } + for (int i = 0; i < formals.length; i++) { + if (!isCompatible(argtypes[i].subst(tparams, tvars), + formals[i].subst(tparams, tvars))) { + if (needToSucceed) + throw new NoInstance( + typeErrorMsg( + "argument expression's type is not compatible with formal parameter type", + argtypes[i].subst(tparams, tvars), + formals[i].subst(tparams, tvars))); + return null; + } + } + Type[] targs = new Type[tvars.length]; + for (int i = 0; i < tvars.length; i++) { + minimizeVar(tvars[i]); + targs[i] = (((Type.TypeVar) tvars[i]).constr.inst == Type.NoType) + ? Type.NoType + : instantiate(tvars[i]); + } + return targs; + } + + /** Create and attribute type application node. Pass arguments for that + * `tparams' prefix which is owned by the tree's symbol. If there are remaining + * type parameters, substitute corresponding type arguments for them in the + * tree. Such remaining type parameters always come from an inferred PolyType. + */ + public Tree mkTypeApply(Tree tree, Symbol[] tparams, Type restype, Type[] targs) { + Tree tree1 = tree; + Symbol sym = tree.symbol(); + int i = 0; + while (i < tparams.length && tparams[i].owner() == sym) + i++; + if (i < tparams.length) { + //new Printer().print(tree1);//DEBUG + //System.out.println(ArrayApply.toString(targs) + "/" + i + "/" + ArrayApply.toString(tparams));//DEBUG + Symbol[] tparams1 = new Symbol[tparams.length - i]; + System.arraycopy(tparams, i, tparams1, 0, tparams1.length); + Type[] targs1 = new Type[tparams.length - i]; + System.arraycopy(targs, i, targs1, 0, targs1.length); + tree1 = substituter.apply(tree1, tparams1, targs1); + } + if (0 < i) { + Tree[] argtrees = new Tree[i]; + for (int j = 0; j < i; j++) + argtrees[j] = gen.mkType(tree.pos, targs[j]); + tree1 = make.TypeApply(tree.pos, tree1, argtrees); + } + //System.out.println(Sourcefile.files[Position.file(tree1.pos)] + ": "); + return tree1.setType(restype.subst(tparams, targs)); + } + + /** Return the instantiated and normalized type of polymorphic expression + * with type `[tparams]restype', given a two prototypes `pt1', and `pt2'. + * `pt1' is the strict first attempt prototype where type parameters + * are left unchanged. `pt2' is the fall-back prototype where type parameters + * are replaced by `AnyType's. We try to instantiate first to `pt1' and then, + * if this fails, to `pt2'. If both atempts fail, a `Type.Error' is thrown. + */ + Type argumentTypeInstance(Symbol[] tparams, Type restype, Type pt1, Type pt2) + throws Type.Error { + switch (restype) { + case PolyType(Symbol[] tparams1, Type restype1): + Symbol[] tparams2 = new Symbol[tparams.length + tparams1.length]; + System.arraycopy(tparams, 0, tparams2, 0, tparams.length); + System.arraycopy(tparams1, 0, tparams2, tparams.length, tparams1.length); + return argumentTypeInstance(tparams2, restype1, pt1, pt2); + default: + if (tparams.length != 0) { + Type[] targs = instTypeArgs(tparams, restype, pt1); + if (targs == null) + targs = instTypeArgs(tparams, restype, pt2); + if (targs == null) + throw new Type.Error( + typeErrorMsg( + "polymorphic argument cannot be instantiated to formal parameter type", + Type.PolyType(tparams, restype), pt2)); + checkBounds(tparams, targs, "inferred "); + return restype.subst(tparams, targs); + } else { + return normalize(restype, pt2); + } + } + } + + /** Instantiate expression `tree' of polymorphic type with given `tparams' and + * `restype', using prototype `pt'. + */ + public Tree exprInstance(Tree tree, Symbol[] tparams, Type restype, Type pt) + throws Type.Error { + switch (restype) { + case PolyType(Symbol[] tparams1, Type restype1): + Symbol[] tparams2 = new Symbol[tparams.length + tparams1.length]; + System.arraycopy(tparams, 0, tparams2, 0, tparams.length); + System.arraycopy(tparams1, 0, tparams2, tparams.length, tparams1.length); + return exprInstance(tree, tparams2, restype1, pt); + } + Type[] targs = instTypeArgs(tparams, restype, pt); + if (targs == null) + throw new Type.Error( + "polymorphic expression of type " + tree.type + + " cannot be instantiated from expected type " + pt); + checkBounds(tparams, targs, "inferred "); + return mkTypeApply(tree, tparams, restype, targs); + } + + /** Instantiate method `tree' of polymorphic type with given `tparams' and + * `restype', so that resulting method type can be applied to + * arguments with types `argtypes'. + */ + public Tree methodInstance(Tree tree, + Symbol[] tparams, Type restype, Type[] argtypes) + throws Type.Error { + switch (restype) { + case PolyType(Symbol[] tparams1, Type restype1): + Symbol[] tparams2 = new Symbol[tparams.length + tparams1.length]; + System.arraycopy(tparams, 0, tparams2, 0, tparams.length); + System.arraycopy(tparams1, 0, tparams2, tparams.length, tparams1.length); + return methodInstance(tree, tparams2, restype1, argtypes); + case MethodType(Symbol[] params, Type restpe): + Type[] argtypes1 = Type.widen(argtypes); + Type[] targs; + try { + targs = methTypeArgs(tparams, params, argtypes1, true); + } catch (NoInstance ex) { + throw new Type.Error( + applyErrorMsg( + "no type parameters for ", tree, + " exist so that it can be applied to arguments ", + argtypes1, Type.AnyType) + + "\n --- because ---\n" + ex.getMessage()); + } + Symbol[] uninstantiated = normalizeArgs(targs, tparams); + checkBounds(tparams, targs, "inferred "); + Type restype1 = (uninstantiated.length == 0) ? restype + : Type.MethodType(params, + new VirtualPolyType(uninstantiated, restpe)); + return mkTypeApply(tree, tparams, restype1, targs); + default: + return tree; + } + } + + /** Instantiate constructor `tree' of polymorphic type with given `tparams' and + * `restype', so that its result type matches prototype `pt'. + */ + public void constructorInstance(Tree tree, + Symbol[] tparams, Type restype, Type pt) + throws Type.Error { + switch (restype) { + case PolyType(Symbol[] tparams1, Type restype1): + Symbol[] tparams2 = new Symbol[tparams.length + tparams1.length]; + System.arraycopy(tparams, 0, tparams2, 0, tparams.length); + System.arraycopy(tparams1, 0, tparams2, tparams.length, tparams1.length); + constructorInstance(tree, tparams2, restype1, pt); + return; + } + Type[] tvars = freshVars(tparams); + Type restype1 = restype.subst(tparams, tvars); + Type ctpe1 = restype1.resultType(); + if (ctpe1.isSubType(pt)) { + Type[] targs = new Type[tparams.length]; + for (int i = 0; i < tvars.length; i++) { + try { + targs[i] = instantiateUpper(tvars[i], true); + } catch (NoInstance ex) { + throw new Type.Error( + "constructor of type " + ctpe1 + + " can be instantiated in mode than one way to expected type " + + pt + + "\n --- because ---\n" + ex.getMessage()); + } + } + checkBounds(tparams, targs, "inferred "); + tree.setType(restype.subst(tparams, targs)); + //System.out.println("inferred constructor type: " + tree.type);//DEBUG + } else { + throw new Type.Error( + typeErrorMsg( + "constructor cannot be instantiated to expected type", + ctpe1, pt)); + } + } + +// Overload Resolution ------------------------------------------------------------- + + /** Is function type `ftpe' applicable to `argtypes' and + * does its result conform to `pt'? + */ + boolean isApplicable(Type ftpe, Type[] argtypes, Type pt) { + switch (ftpe) { + case MethodType(Symbol[] params, Type restpe): + return + isCompatible(restpe, pt) && + params.length == argtypes.length && + Type.isSubType(argtypes, Symbol.type(params)); + case PolyType(Symbol[] tparams, MethodType(Symbol[] params, Type restpe)): + try { + Type[] targs = methTypeArgs( + tparams, params, Type.widen(argtypes), false); + if (targs != null) { + Symbol[] uninstantiated = normalizeArgs(targs, tparams); + return + isWithinBounds(tparams, targs) && + instTypeArgs(uninstantiated, restpe.subst(tparams, targs), pt) + != null; + } + } catch (NoInstance ex) { + } + } + return false; + } + + /** Does function type `ftpe1' specialize function type `ftpe2' + * when both are alternatives in an overloaded function? + */ + boolean specializes(Type ftpe1, Type ftpe2) { + switch (ftpe1) { + case MethodType(Symbol[] params, _): + return isApplicable(ftpe2, Symbol.type(params), Type.AnyType); + case PolyType(_, MethodType(Symbol[] params, _)): + return isApplicable(ftpe2, Symbol.type(params), Type.AnyType); + default: + return false; + } + } + + /** Assign `tree' the type of the alternative which matches + * prototype `pt', if it exists. + * If several alternatives match `pt', take unique parameterless one. + * Throw a Type.Error if several such alternatives exist. + * If no alternative matches, leave `tree' unchanged. + */ + public void exprAlternative(Tree tree, Symbol[] alts, + Type[] alttypes, Type pt) + throws Type.Error { + if (alts.length == 1) { + tree.setSymbol(alts[0]).setType(alttypes[0]); + return; + } + int best = -1; + for (int i = 0; i < alttypes.length; i++) { + if (isCompatible(alttypes[i], pt) && + (best < 0 || improves(alttypes[i], alttypes[best]))) { + best = i; + } + } + if (best >= 0) { + for (int i = 0; i < alttypes.length; i++) { + if (isCompatible(alttypes[i], pt) && + best != i && !improves(alttypes[best], alttypes[i])) { + throw new Type.Error(overloadResolveErrorMsg( + alts[best], alttypes[best], alts[i], alttypes[i])); + } + } + tree.setSymbol(alts[best]).setType(alttypes[best]); + } + } + //where + private boolean improves(Type tp1, Type tp2) { + return !isParameterized(tp1) && isParameterized(tp2); + } + + /** Assign `tree' the type of an alternative + * which is applicable to `argtypes', and whose result type is + * a subtype of `pt' if it exists. + * If several applicable alternatives exist, take the + * most specialized one, or throw an error if no + * most specialized applicable alternative exists. + * If no alternative matches, leave `tree' unchanged. + */ + public void methodAlternative(Tree tree, Symbol[] alts, Type[] alttypes, + Type[] argtypes, Type pt) + throws Type.Error { + if (alts.length == 1) { + tree.setSymbol(alts[0]).setType(alttypes[0]); + return; + } + int best = -1; + for (int i = 0; i < alttypes.length; i++) { + if (isApplicable(alttypes[i], argtypes, pt) && + (best < 0 || specializes(alttypes[i], alttypes[best]))) best = i; + } + if (best >= 0) { + for (int i = 0; i < alttypes.length; i++) { + if (i != best && + isApplicable(alttypes[i], argtypes, pt) && + !(specializes(alttypes[best], alttypes[i]) && + !specializes(alttypes[i], alttypes[best]))) { + throw new Type.Error(overloadResolveErrorMsg( + alts[best], alttypes[best], alts[i], alttypes[i])); + } + } + tree.setSymbol(alts[best]).setType(alttypes[best]); + } + } + + /** Assign `tree' the type of unique polymorphic alternative with `nparams' numbers + * of type parameters, if it exists. + * throw error if several polymorphic alternatives exist. + * If no alternative matches, leave `tree' unchanged. + */ + public void polyAlternative(Tree tree, + Symbol[] alts, Type[] alttypes, int nparams) + throws Type.Error { + if (alts.length == 1) { + tree.setSymbol(alts[0]).setType(alttypes[0]); + return; + } + int i = 0; + while (i < alttypes.length && + !(alts[i].isValue() && alttypes[i].typeParams().length == nparams)) { + i++; + } + if (i < alttypes.length) { + for (int j = i + 1; j < alttypes.length; j++) { + if (alts[i].isValue() && alttypes[i].typeParams().length == nparams) + throw new Type.Error(overloadResolveErrorMsg( + alts[i], alttypes[i], alts[j], alttypes[j])); + } + tree.setSymbol(alts[i]).setType(alttypes[i]); + } + } +} + diff --git a/sources/scalac/typechecker/RefCheck.java b/sources/scalac/typechecker/RefCheck.java new file mode 100644 index 0000000000..b45a9d3a2f --- /dev/null +++ b/sources/scalac/typechecker/RefCheck.java @@ -0,0 +1,208 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** +** $Id$ +\* */ +// todo: Any cannot be inherited. +// todo: check that only stable defs override stable defs +// todo: admit vardefs w/o rhs +// ClassTemplate ::= [extends Constr {with Constr}] +// [with `(' TemplateStatSeq `)'] +// todo: pretty printer prints wrong precedence for `new' (-> lambdalift.scala). + +package scalac.typechecker; + +import java.util.HashMap; +import scalac.*; +import scalac.util.*; +import scalac.ast.*; +import scalac.symtab.*; +import Tree.*; + +/** Check that no forward reference to a term symbol extends beyond a value definition. + */ +public class RefCheck extends Transformer implements Modifiers, Kinds { + + public RefCheck(Global global, RefCheckPhase descr) { + super(global, descr); + } + + private Unit unit; + private Scope[] scopes = new Scope[4]; + private int[] maxindex = new int[4]; + private int[] refpos = new int[4]; + private Symbol[] refsym = new Symbol[4]; + private int level; + private HashMap symIndex = new HashMap(); + + void pushLevel() { + level++; + if (level == scopes.length) { + Scope[] scopes1 = new Scope[scopes.length * 2]; + int[] maxindex1 = new int[scopes.length * 2]; + int[] refpos1 = new int[scopes.length * 2]; + Symbol[] refsym1 = new Symbol[scopes.length * 2]; + System.arraycopy(scopes, 0, scopes1, 0, scopes.length); + System.arraycopy(maxindex, 0, maxindex1, 0, scopes.length); + System.arraycopy(refpos, 0, refpos1, 0, scopes.length); + System.arraycopy(refsym, 0, refsym1, 0, scopes.length); + scopes = scopes1; + maxindex = maxindex1; + refpos = refpos1; + refsym = refsym1; + } + scopes[level] = new Scope(scopes[level - 1]); + maxindex[level] = Integer.MIN_VALUE; + } + + void popLevel() { + scopes[level] = null; + level --; + } + + public void apply(Unit unit) { + this.unit = unit; + level = 0; + scopes[0] = new Scope(); + maxindex[0] = Integer.MIN_VALUE; + unit.body = transformStats(unit.body); + scopes[0] = null; + symIndex.clear(); + } + + /** compensate for renaming during addition of access functions + */ + Name normalize(Name name) { + return (name.endsWith(Name.fromString("$"))) + ? name.subName(0, name.length() - 1) + : name; + } + + void enterSyms(Tree[] stats) { + for (int i = 0; i < stats.length; i++) { + enterSym(stats[i], i); + } + } + + void enterSym(Tree stat, int index) { + Symbol sym = null; + switch (stat) { + case ClassDef(_, _, _, _, _, _): + sym = stat.symbol().constructor(); + break; + case DefDef(_, _, _, _, _, _): + case ModuleDef(_, _, _, _): + case ValDef(_, _, _, _): + sym = stat.symbol(); + } + if (sym != null) { + scopes[level].enter(sym); + symIndex.put(sym, new Integer(index)); + } + } + + public Tree[] transformStats(Tree[] stats) { + pushLevel(); + enterSyms(stats); + int i = 0; + while (i < stats.length) { + Tree[] newstat = transformStat(stats[i], i); + if (newstat != null) { + Tree[] newstats = new Tree[stats.length + newstat.length - 1]; + System.arraycopy(stats, 0, newstats, 0, i); + System.arraycopy(newstat, 0, newstats, i, newstat.length); + System.arraycopy(stats, i + 1, newstats, i + newstat.length, + stats.length - i - 1); + i = i + newstat.length; + stats = newstats; + } else { + i = i + 1; + } + } + popLevel(); + return stats; + } + + /** The main checking functions + */ + public Tree[] transformStat(Tree tree, int index) { + Tree resultTree; + switch (tree) { + case ModuleDef(int mods, Name name, Tree tpe, Tree.Template templ): + Symbol sym = tree.symbol(); + // local modules are not yet supported but the interpreter + // accepts modules at the console level + if (sym.isLocal()) unit.error("local modules are not yet supported"); + + Tree[] result = new Tree[2]; + result[0] = make.ClassDef( + tree.pos, + mods | FINAL | MODUL, + name.toTypeName(), + new Tree.TypeDef[0], + new Tree.ValDef[][]{{}}, + Tree.Empty, + templ) + .setSymbol(sym.moduleClass()).setType(tree.type); + result[1] = make.ValDef( + tree.pos, + mods | MODUL, + name, + (tpe == Tree.Empty) ? gen.mkType(tree.pos, sym.type()) : tpe, + gen.New( + tree.pos, + sym.type().prefix(), + sym.moduleClass(), + Tree.EMPTY_ARRAY)) + .setSymbol(sym).setType(tree.type); + return transform(result); + + case ValDef(int mods, Name name, Tree tpe, Tree rhs): + Symbol sym = tree.symbol(); + resultTree = transform(tree); + //todo: handle variables + if (index <= maxindex[level]) { + if (Global.instance.debug) + System.out.println(refsym[level] + ":" + refsym[level].type()); + unit.error( + refpos[level], + "forward reference extends over definition of value " + + normalize(name)); + } + break; + default: + resultTree = transform(tree); + } + return (resultTree == tree) ? null : new Tree[]{resultTree}; + } + + public Tree transform(Tree tree) { + switch (tree) { + case Template(Tree[] bases, Tree[] body): + Tree[] bases1 = transform(bases); + Tree[] body1 = transformStats(body); + return copy.Template(tree, bases1, body1); + case Block(Tree[] stats): + Tree[] stats1 = transformStats(stats); + return copy.Block(tree, stats1); + case Ident(Name name): + Scope.Entry e = scopes[level].lookupEntry(name); + if (tree.symbol() == e.sym) { + int i = level; + while (scopes[i] != e.owner) i--; + int symindex = ((Integer) symIndex.get(tree.symbol())).intValue(); + if (maxindex[i] < symindex) { + refpos[i] = tree.pos; + refsym[i] = e.sym; + maxindex[i] = symindex; + } + } + return tree; + default: + return super.transform(tree); + } + } +} + diff --git a/sources/scalac/typechecker/RefCheckPhase.java b/sources/scalac/typechecker/RefCheckPhase.java new file mode 100644 index 0000000000..6c1fdb08a2 --- /dev/null +++ b/sources/scalac/typechecker/RefCheckPhase.java @@ -0,0 +1,42 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** +** $Id$ +\* */ + +package scalac.typechecker; + +import scalac.*; +import scalac.ast.*; +import scalac.checkers.*; + +public class RefCheckPhase extends PhaseDescriptor { + + public String name() { + return "refcheck"; + } + + public String description () { + return "reference checking"; + } + + public String taskDescription () { + return "reference checking"; + } + + public Phase createPhase(Global global) { + return new RefCheck(global, this); + } + + public Checker[] postCheckers(Global global) { + return new Checker[] { + /* todo: uncomment + new CheckSymbols(global), + new CheckTypes(global), + new CheckOwners(global) + */ + }; + } +} diff --git a/sources/scalac/util/AbstractFile.java b/sources/scalac/util/AbstractFile.java new file mode 100644 index 0000000000..2487e07ca3 --- /dev/null +++ b/sources/scalac/util/AbstractFile.java @@ -0,0 +1,557 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.util; + +import java.io.*; +import java.util.*; +import java.util.zip.*; +import java.util.jar.*; + +public abstract class AbstractFile { + + /** separator + */ + protected char separator = File.separatorChar; + + /** table of all opened jar-files + */ + protected static Hashtable opened = new Hashtable(); + + /** get name of the file + */ + public abstract String getName(); + + /** get path of the file + */ + public abstract String getPath(); + + /** does the file exist? + */ + public abstract boolean exists(); + + /** is the file a directory? + */ + public abstract boolean isDirectory(); + + /** read content of the file into a byte[] buffer + */ + public abstract byte[] read() throws IOException; + + /** list contents of a directory + */ + public abstract String[] list() throws IOException; + + /** open a new file + */ + public abstract AbstractFile open(String name); + + /** return an input stream for the file + */ + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(read()); + } + + /** open file 'name' in directory 'dirname' + */ + public static AbstractFile open(String dirname, String name) { + AbstractFile res; + if (dirname == null) + res = new PlainFile(new File(name)); + else if (dirname.endsWith(".zip")) { + AbstractFile dir = (AbstractFile)opened.get(dirname); + if (dir == null) { + dir = new ZipDir(new File(dirname)); + if (dir.isDirectory()) + opened.put(dirname, dir); + } + res = (name == null) ? dir : dir.open(name); + } else if (dirname.endsWith(".jar")) { + AbstractFile dir = (AbstractFile)opened.get(dirname); + if (dir == null) { + dir = new JarArchive(new File(dirname)); + if (dir.isDirectory()) + opened.put(dirname, dir); + } + res = (name == null) ? dir : dir.open(name); + } else if (name == null) + res = new PlainFile(new File(dirname)); + else + res = new PlainFile(new File(dirname, name)); + if (!res.exists()) + res = null; + return res; + } + + /** create file given by a fully qualified name from root directory `outdir'; + * create intermediate directories if they do not exist already + */ + public static File create(File outdir, String name, + String suffix) throws IOException { + int start = 0; + int end = name.indexOf('.'); + while (end >= start) { + outdir = new File(outdir, name.substring(start, end)); + if (!outdir.exists()) + outdir.mkdir(); + start = end + 1; + end = name.indexOf('.', start); + } + return new File(outdir, name.substring(start) + suffix); + } +} + +class PlainFile extends AbstractFile { + File f; + + PlainFile(File f) { + this.f = f; + } + + public String getName() { + return f.getName(); + } + + public String getPath() { + return f.getPath(); + } + + public boolean exists() { + return f.exists(); + } + + public boolean isDirectory() { + return f.isDirectory(); + } + + public byte[] read() throws IOException { + FileInputStream in = new FileInputStream(f); + int rest = (int)f.length(); + byte[] buf = new byte[rest]; + do { + int res = in.read(buf, buf.length - rest, rest); + if (res == -1) + throw new IOException("read error"); + rest -= res; + } while (rest > 0); + in.close(); + return buf; + } + + public String[] list() throws IOException { + File[] fs = f.listFiles(); + if (fs == null) + return new String[0]; + String[] res = new String[fs.length]; + for (int i = 0; i < fs.length; i++) { + res[i] = fs[i].getName(); + if (fs[i].isDirectory() && + !res[i].endsWith("/")) + res[i] = res[i] + "/"; + } + return res; + } + + public AbstractFile open(String name) { + return new PlainFile(new File(f, name)); + } +} + +class ZippedFile extends AbstractFile { + ZipDir dir; + ZipEntry zipEntry; + + { + separator = '/'; + } + + ZippedFile(ZipDir dir, String name) { + this.dir = dir; + if (dir.zipFile != null) { + name = name.replace(File.separatorChar, separator); + zipEntry = this.dir.zipFile.getEntry(name); + if (zipEntry == null) + zipEntry = this.dir.zipFile.getEntry(name + separator); + } + } + + public String getName() { + return zipEntry.getName(); + } + + public String getPath() { + return dir.getPath() + "(" + zipEntry.getName() + ")"; + } + + public boolean exists() { + return (zipEntry != null); + } + + public boolean isDirectory() { + return zipEntry.isDirectory(); + } + + public byte[] read() throws IOException { + InputStream in = dir.zipFile.getInputStream(zipEntry); + int rest = (int)zipEntry.getSize(); + byte[] buf = new byte[rest]; + do { + int res = in.read(buf, buf.length - rest, rest); + if (res == -1) + throw new IOException("read error"); + rest -= res; + } while (rest > 0); + in.close(); + return buf; + } + + public String[] list() throws IOException { + if (!isDirectory()) + throw new IOException("not a directory"); + return dir.list(zipEntry.getName()); + } + + public AbstractFile open(String name) { + String pathname = zipEntry.getName(); + return new ZippedFile(dir, pathname + name); + } +} + +class ZipDir extends AbstractFile { + File f; + ZipFile zipFile; + + { + separator = '/'; + } + + ZipDir(File f) { + this.f = f; + try { + zipFile = new ZipFile(f); + } catch (ZipException e) { + } catch (IOException e) {} + } + + public String getName() { + return f.getName(); + } + + public String getPath() { + return f.getPath(); + } + + public boolean exists() { + return (zipFile != null); + } + + public boolean isDirectory() { + return (zipFile != null); + } + + public byte[] read() throws IOException { + throw new IOException("cannot read directory"); + } + + public String[] list(String prefix) { + int n = 0; + for (Enumeration enum = zipFile.entries(); enum.hasMoreElements();) { + ZipEntry e = (ZipEntry)enum.nextElement(); + if (e.getName().startsWith(prefix)) { + String candidate = e.getName().substring(prefix.length()); + if (candidate.indexOf(separator) < 0) + n++; + } + } + String[] filenames = new String[n]; + n = 0; + for (Enumeration enum = zipFile.entries(); enum.hasMoreElements();) { + ZipEntry e = (ZipEntry)enum.nextElement(); + if (e.getName().startsWith(prefix)) { + String candidate = e.getName().substring(prefix.length()); + if (candidate.indexOf(separator) < 0) + filenames[n++] = candidate; + } + } + return filenames; + } + + public String[] list() throws IOException { + return list(""); + } + + public AbstractFile open(String name) { + return new ZippedFile(this, name); + } +} + +final class JarArchive extends AbstractFile { + File f; + JarFile jarFile; + HashMap entries; + + public final static String[] EMPTY = new String[0]; + + + JarArchive(File f) { + try { + jarFile = new JarFile(this.f = f); + } + catch (ZipException e) {} + catch (IOException e) {} + } + + public String getName() { + return f.getName(); + } + + public String getPath() { + return f.getPath(); + } + + public boolean exists() { + return jarFile != null; + } + + public boolean isDirectory() { + return jarFile != null; + } + + public byte[] read() throws IOException { + throw new IOException("cannot read archive"); + } + + private void load() { + entries = new HashMap(); + if (jarFile == null) + return; + Enumeration enum = jarFile.entries(); + while (enum.hasMoreElements()) { + String candidate = ((JarEntry)enum.nextElement()).getName(); + int i = candidate.indexOf('/'); + int j = 0; + HashMap files = entries; + while (i >= 0) { + String dirname = candidate.substring(j, j = (i + 1)); + JarDirEntry dir = (JarDirEntry)files.get(dirname); + if (dir == null) + files.put(dirname, dir = new JarDirEntry( + candidate.substring(0, j))); + files = dir.entries; + i = candidate.indexOf('/', j); + } + if (j < (candidate.length() - 1)) { + String filename = candidate.substring(j); + JarFileEntry file = (JarFileEntry)files.get(filename); + if (file == null) + files.put(filename, new JarFileEntry(candidate)); + } + } + } + + public String[] list(String prefix) { + prefix = prefix.replace(File.separatorChar, '/'); + if (entries == null) + load(); + int i = prefix.indexOf('/'); + int j = 0; + HashMap files = entries; + while (i >= 0) { + String dirname = prefix.substring(j, j = (i + 1)); + JarDirEntry dir = (JarDirEntry)files.get(dirname); + if (dir == null) + return EMPTY; + files = dir.entries; + i = prefix.indexOf('/', j); + } + if (j < (prefix.length() - 1)) { + String filename = prefix.substring(j); + return (files.get(filename) != null) ? new String[]{prefix} + : EMPTY; + } else + return (String[])files.keySet().toArray(new String[files.size()]); + } + + public String[] list() throws IOException { + return list(""); + } + + public AbstractFile open(String name) { + if (entries == null) + load(); + name = name.replace(File.separatorChar, '/'); + int i = name.indexOf('/'); + int j = 0; + int namelen = name.length(); + HashMap files = entries; + while (i >= 0) { + String dirname = name.substring(j, j = (i + 1)); + if (files != null) { + JarDirEntry dir = (JarDirEntry)files.get(dirname); + if (dir == null) + files = null; + else if (j == namelen) + return dir; + else + files = dir.entries; + } + i = name.indexOf('/', j); + } + if (j < (namelen - 1)) { + String filename = name.substring(j); + if (files == null) + return new NoJarFileEntry(name); + JarFileEntry file = (JarFileEntry)files.get(filename); + if (file == null) + return new NoJarFileEntry(name); + else + return file; + } else + return new NoJarDirEntry(name); + } + + static class NoJarDirEntry extends AbstractFile { + String name; + + NoJarDirEntry(String name) { + this.name = name; + } + + public String getName() { + return name.substring( + name.lastIndexOf('/', name.length() - 2) + 1); + } + + public String getPath() { + return name; + } + + public String getFullName() { + return name; + } + + public boolean exists() { + return false; + } + + public boolean isDirectory() { + return true; + } + + public String[] list() throws IOException { + throw new IOException("not a directory"); + } + + public byte[] read() throws IOException { + throw new IOException("cannot read archive"); + } + + public AbstractFile open(String fname) { + throw new Error("cannot open archive entry"); + } + } + + final class JarDirEntry extends NoJarDirEntry { + HashMap entries; + + JarDirEntry(String name) { + super(name); + this.entries = new HashMap(); + } + + public String getPath() { + return JarArchive.this.getPath() + "(" + name + ")"; + } + + public boolean exists() { + return true; + } + + public String[] list() throws IOException { + return JarArchive.this.list(name); + } + + public AbstractFile open(String fname) { + fname = fname.replace(File.separatorChar, '/'); + return JarArchive.this.open(name + fname); + } + } + + static class NoJarFileEntry extends AbstractFile { + String name; + + NoJarFileEntry(String name) { + this.name = name; + } + + public String getName() { + return name.substring( + name.lastIndexOf('/', name.length() - 1) + 1); + } + + public String getFullName() { + return name; + } + + public String getPath() { + return name; + } + + public boolean exists() { + return false; + } + + public boolean isDirectory() { + return false; + } + + public String[] list() throws IOException { + throw new IOException("not a directory"); + } + + public byte[] read() throws IOException { + throw new IOException("cannot read archive"); + } + + public AbstractFile open(String fname) { + throw new Error("not a directory"); + } + } + + final class JarFileEntry extends NoJarFileEntry { + + JarFileEntry(String name) { + super(name); + } + + public String getPath() { + return JarArchive.this.getPath() + "(" + name + ")"; + } + + public boolean exists() { + return true; + } + + public byte[] read() throws IOException { + JarEntry jarEntry = jarFile.getJarEntry(name); + if (jarEntry == null) + throw new IOException("unable to read " + name); + InputStream in = jarFile.getInputStream(jarEntry); + int rest = (int)jarEntry.getSize(); + byte[] buf = new byte[rest]; + do { + int res = in.read(buf, buf.length - rest, rest); + if (res == -1) + throw new IOException("read error"); + rest -= res; + } while (rest > 0); + in.close(); + return buf; + } + } +} diff --git a/sources/scalac/util/AbstractFileReader.java b/sources/scalac/util/AbstractFileReader.java new file mode 100644 index 0000000000..0d18292838 --- /dev/null +++ b/sources/scalac/util/AbstractFileReader.java @@ -0,0 +1,107 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.util; + +import java.io.*; + + +public class AbstractFileReader { + + /** the buffer containing the file + */ + public byte[] buf; + + /** the current input pointer + */ + public int bp; + + /** constructor + */ + public AbstractFileReader(AbstractFile f) throws IOException { + buf = f.read(); + bp = 0; + } + + /** return byte at offset 'pos' + */ + public byte byteAt(int pos) { + return buf[pos]; + } + + /** read a byte + */ + public byte nextByte() { + return buf[bp++]; + } + + /** read some bytes + */ + public byte[] nextBytes(int len) { + byte[] res = new byte[len]; + System.arraycopy(buf, bp, res, 0, len); + bp += len; + return res; + } + + /** read a character + */ + public char nextChar() { + return + (char)(((buf[bp++] & 0xff) << 8) + + (buf[bp++] & 0xff)); + } + + /** read an integer + */ + public int nextInt() { + return ((buf[bp++] & 0xff) << 24) + + ((buf[bp++] & 0xff) << 16) + + ((buf[bp++] & 0xff) << 8) + + (buf[bp++] & 0xff); + } + + /** extract a character at position bp from buf + */ + public char getChar(int bp) { + return (char)(((buf[bp] & 0xff) << 8) + (buf[bp+1] & 0xff)); + } + + /** extract an integer at position bp from buf + */ + public int getInt(int bp) { + return ((buf[bp ] & 0xff) << 24) + + ((buf[bp+1] & 0xff) << 16) + + ((buf[bp+2] & 0xff) << 8) + + (buf[bp+3] & 0xff); + } + + /** extract a long integer at position bp from buf + */ + public long getLong(int bp) { + return ((long)(getInt(bp)) << 32) + (getInt(bp + 4) & 0xffffffffL); + } + + /** extract a float at position bp from buf + */ + public strictfp float getFloat(int bp) { + return Float.intBitsToFloat(getInt(bp)); + } + + /** extract a double at position bp from buf + */ + public strictfp double getDouble(int bp) { + return Double.longBitsToDouble(getLong(bp)); + } + + /** skip next 'n' bytes + */ + public void skip(int n) { + bp += n; + } +} diff --git a/sources/scalac/util/ArrayApply.java b/sources/scalac/util/ArrayApply.java new file mode 100644 index 0000000000..087278af5b --- /dev/null +++ b/sources/scalac/util/ArrayApply.java @@ -0,0 +1,54 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scalac.util; + +public abstract class ArrayApply { + + //######################################################################## + // Arrays interface - Object + + public static String toString(Object[] src) { + return append(new StringBuffer(), src).toString(); + } + + public static String toString(Object[] src, String infix) { + return append(new StringBuffer(), src, infix).toString(); + } + + public static String toString(Object[] src, String prefix, String infix, + String suffix) + { + return append(new StringBuffer(), src, prefix,infix,suffix).toString(); + } + + //######################################################################## + // Arrays interface - StringBuffer + + public static StringBuffer append(StringBuffer buffer, Object[] src) { + return append(buffer, src, "[", ",", "]"); + } + + public static StringBuffer append(StringBuffer buffer, Object[] src, + String infix) + { + return append(buffer, src, "", infix, ""); + } + + public static StringBuffer append(StringBuffer buffer, Object[] src, + String prefix, String infix, String suffix) + { + buffer.append(prefix); + for (int i = 0; i < src.length; i++) { + if (i > 0) buffer.append(infix); + buffer.append(src[i]); + } + buffer.append(suffix); + return buffer; + } +} diff --git a/sources/scalac/util/ClassPath.java b/sources/scalac/util/ClassPath.java new file mode 100644 index 0000000000..b083c5485b --- /dev/null +++ b/sources/scalac/util/ClassPath.java @@ -0,0 +1,161 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.util; + +import java.io.*; +import java.util.*; + + +public class ClassPath { + + /** the character separating files + */ + protected static String FILE_SEP = File.separator; + + /** the separator in class path specifications + */ + protected static String PATH_SEP = + System.getProperty("path.separator"); + + /** the default class path + */ + public static String CLASS_PATH = + System.getProperty("scala.class.path", + System.getProperty("java.class.path")); + + /** the default source path + */ + public static String SOURCE_PATH = null; + + /** the default boot class path + */ + public static String BOOT_PATH = + appendPath(appendPath("", System.getProperty("sun.boot.class.path")), + System.getProperty("scala.boot.class.path")).substring(1); + + /** the default extension path + */ + public static String EXTENSION_PATH = + System.getProperty("java.ext.dirs"); + + /** the various class path roots + */ + protected String[] root; + + /** print searches in the class path + */ + public boolean printSearch; + + + /** classpath constructor + */ + public ClassPath() { + this(CLASS_PATH); + } + + public ClassPath(String classpath) { + this(classpath, SOURCE_PATH, BOOT_PATH, EXTENSION_PATH); + } + + public ClassPath(String classpath, String sourcepath, + String bootclasspath, String extdirs) + { + String path = ""; + path = appendPath(path, bootclasspath); + path = appendExtDirs(path, extdirs); + path = appendPath(path, classpath); + path = appendPath(path, sourcepath); + root = parse(path.substring(1)); + } + + /** append an additional path + */ + protected static String appendPath(String path, String addpath) { + return addpath == null ? path : path + PATH_SEP + addpath; + } + + /** append files from the extension directories + */ + protected String appendExtDirs(String path, String extdirs) { + if (extdirs != null) { + extdirs += PATH_SEP; + int length = extdirs.length(); + int i = 0; + while (i < length) { + int k = extdirs.indexOf(PATH_SEP, i); + String dirname = extdirs.substring(i, k); + String[] ext; + if ((dirname != null) && + (dirname.length() > 0) && + ((ext = new File(dirname).list()) != null)) { + if (!dirname.endsWith(FILE_SEP)) + dirname += FILE_SEP; + for (int j = 0; j < ext.length; j++) + if (ext[j].endsWith(".jar") || + ext[j].endsWith(".zip")) + path = appendPath(path, dirname + ext[j]); + } + i = k + 1; + } + } + return path; + } + + /** parse a class path specification and return an array + * of existing class file locations + */ + protected String[] parse(String path) { + path += PATH_SEP; + Vector components = new Vector(); + int i = 0; + while (i < path.length()) { + int j = path.indexOf(PATH_SEP, i); + String subpath = path.substring(i, j); + if (new File(subpath).exists()) + components.add(subpath); + i = j + 1; + } + return (String[])components.toArray( + new String[components.size()]); + } + + /** find file with given name in class path and return an abstract + * file representation + */ + public AbstractFile openFile(String name) throws FileNotFoundException { + if (printSearch) + System.out.println("looking for " + name); + for (int i = 0; i < root.length; i++) { + if (printSearch) + System.out.println(" in " + root[i]); + AbstractFile f = AbstractFile.open(root[i], name); + if (f != null) + return f; + } + throw new FileNotFoundException("file '" + name + + "' not found in classpath"); + } + + public String[] components() { + return root; + } + + /** return a textual representation of this class path + */ + public String toString() { + if (root.length == 0) + return ""; + else if (root.length == 1) + return root[0]; + String path = root[0]; + for (int i = 1; i < root.length; i++) + path += PATH_SEP + root[i]; + return path; + } +} diff --git a/sources/scalac/util/Debug.java b/sources/scalac/util/Debug.java new file mode 100644 index 0000000000..4597463aab --- /dev/null +++ b/sources/scalac/util/Debug.java @@ -0,0 +1,465 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +\* */ + +// $Id$ + +package scalac.util; + +import java.util.HashMap; + +import java.lang.reflect.Modifier; +import java.lang.reflect.Method; +import java.lang.reflect.Field; + +import scalac.Global; +import scalac.ApplicationError; +import scalac.symtab.Type; +import scalac.symtab.Symbol; +import scalac.symtab.Scope; + +/** + * Debugging class, used e.g. to obtain string representations of + * compiler data structures that are not "pretty-printed" and thus + * easier to relate to the source code. + * + * All methods are static to be easily useable in any context. + * + * @author Michel Schinz + * @version 1.0 + */ + +public abstract class Debug { + + //######################################################################## + // Debug interface - abort + + public static Error abort() { + return new ApplicationError(); + } + + public static Error abort(String message) { + return new ApplicationError(message); + } + + public static Error abort(Object object) { + return new ApplicationError(object); + } + + public static Error abort(Throwable cause) { + return new ApplicationError(cause); + } + + public static Error abort(String message, Object object) { + return new ApplicationError(message, object); + } + + public static Error abort(String message, Throwable cause) { + return new ApplicationError(message, cause); + } + + public static Error abort(Object object, Throwable cause) { + return new ApplicationError(object, cause); + } + + public static Error abort(String message, Object object, Throwable cause) { + return new ApplicationError(message, object, cause); + } + + //######################################################################## + // Debug interface - log + + public static boolean log(String message) { + return Global.instance.log(message); + } + + public static boolean log(String message, Object object) { + return log(message + ": " + show(object)); + } + + //######################################################################## + // Debug interface - handlers + + public static final HashMap/*<Class,DebugHandler>*/ handlers; + + static { + handlers = new HashMap(); + handlers.put(String.class, DebugToStringHandler.instance); + handlers.put(Type.class, DebugType.instance); + handlers.put(Symbol.class, DebugSymbol.instance); + handlers.put(Scope.class, DebugScope.instance); + } + + public static DebugHandler getHandler(Object that) { + if (that == null) return DebugDefaultHandler.instance; + return getHandler(that.getClass()); + } + + public static DebugHandler getHandler(Class clasz) { + if (clasz == null) return DebugDefaultHandler.instance; + Object handler = handlers.get(clasz); + if (handler != null) return (DebugHandler)handler; + return getHandler(clasz.getSuperclass()); + } + + //######################################################################## + // Debug interface - toString + + public static String toString(Object that) { + return show(that); + } + + //######################################################################## + // Debug interface - show + + public static String show(Object that) { + return show(that, getHandler(that)); + } + + public static String show(Object that, DebugHandler handler) { + StringBuffer buffer = new StringBuffer(); + handler.append0(buffer, that); + return buffer.toString(); + } + + //######################################################################## + // Debug interface - append + + public static void append(StringBuffer buffer, Object that) { + getHandler(that).append0(buffer, that); + } + + public static void appendDefault(StringBuffer buffer, Object that) { + if (that == null) { buffer.append("null"); return; } + if (!that.getClass().isArray()) { + appendObject(buffer, that); + } else { + appendArray(buffer, (Object[])that); + } + } + + public static void appendObject(StringBuffer buffer, Object that) { + if (that == null) { buffer.append("null"); return; } + buffer.append(getClassName(that)); + Class owner = null; + if (hasToString(that.getClass())) { + buffer.append('('); + buffer.append(that); + buffer.append(')'); + } else { + int code = System.identityHashCode(that); + buffer.append('@'); + buffer.append(Integer.toHexString(code)); + } + } + + public static void appendArray(StringBuffer buffer, Object[] that) { + if (that == null) { buffer.append("null"); return; } + buffer.append('['); + for (int i = 0; i < that.length; i++) { + if (i > 0) buffer.append(','); + append(buffer, that[i]); + } + buffer.append(']'); + } + + //######################################################################## + // Debug interface - utils + + public static final Class OBJECT_CLASS = Object.class; + + public static boolean hasToString(Class clasz) { + try { + Method toString = clasz.getMethod("toString", new Class[0]); + return toString.getDeclaringClass() != OBJECT_CLASS; + } catch (NoSuchMethodException excpetion) { + return false; + } catch (SecurityException excpetion) { + return false; + } + } + + public static String getClassName(Object object) { + if (object == null) return "null"; + Class clasz = object.getClass(); + String name = clasz.getName(); + if (!name.endsWith("$$Var")) return name; + Class superclass = clasz.getSuperclass(); + Field[] fields = superclass.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + try { + Field field = fields[i]; + if (field.getType() != clasz) continue; + if (!Modifier.isStatic(field.getModifiers())) continue; + Object value = field.get(null); + if (value != object) continue; + return name + "[" + field.getName() + "]"; + } catch (IllegalAccessException exception) { + // continue + } + } + return name; + } + + //######################################################################## +} + +public interface DebugHandler { + + //######################################################################## + // DebugHandler interface + + public void append0(StringBuffer buffer, Object that); + + //######################################################################## +} + +public abstract class DebugAbstractHandler implements DebugHandler { + + //######################################################################## + // DebugAbstractHandler interface + + public String show(Object that) { + return Debug.show(that, this); + } + + //######################################################################## +} + +public class DebugDefaultHandler extends DebugAbstractHandler { + + //######################################################################## + // DebugDefaultHandler interface + + public static DebugDefaultHandler instance = new DebugDefaultHandler(); + + //######################################################################## + // DebugHandler interface + + public void append0(StringBuffer buffer, Object that) { + Debug.appendDefault(buffer, that); + } + + //######################################################################## +} + +public class DebugToStringHandler extends DebugAbstractHandler { + + //######################################################################## + // DebugToStringHandler interface + + public static DebugToStringHandler instance = new DebugToStringHandler(); + + //######################################################################## + // DebugHandler interface + + public void append0(StringBuffer buffer, Object that) { + buffer.append(that); + } + + //######################################################################## +} + +public class DebugObject extends DebugAbstractHandler { + + //######################################################################## + // DebugObject interface + + public static DebugObject instance = new DebugObject(); + + public void append1(StringBuffer buffer, Object that) { + Debug.appendObject(buffer, that); + } + + //######################################################################## + // DebugHandler interface + + public void append0(StringBuffer buffer, Object that) { + append1(buffer, (Object)that); + } + + //######################################################################## +} + +public class DebugArray extends DebugAbstractHandler { + + //######################################################################## + // DebugArray interface + + public static DebugArray instance = new DebugArray(); + + public void append1(StringBuffer buffer, Object[] that) { + Debug.appendArray(buffer, that); + } + + //######################################################################## + // DebugHandler interface + + public void append0(StringBuffer buffer, Object that) { + append1(buffer, (Object[])that); + } + + //######################################################################## +} + +public class DebugType extends DebugAbstractHandler { + + //######################################################################## + // DebugType interface + + public static DebugType instance = new DebugType(); + + public void append1(StringBuffer buffer, Type that) { + switch (that) { + + case ErrorType: + buffer.append("ErrorType"); + return; + + case AnyType: + buffer.append("AnyType"); + return; + + case NoType: + buffer.append("NoType"); + return; + + case ThisType(Symbol symbol): + buffer.append("ThisType("); + Debug.append(buffer, symbol); + buffer.append(')'); + return; + + case TypeRef(Type prefix, Symbol symbol, Type[] args) : + buffer.append("TypeRef("); + Debug.append(buffer, prefix); + buffer.append(','); + Debug.append(buffer, symbol); + buffer.append(','); + Debug.append(buffer, args); + buffer.append(')'); + return; + + case SingleType(Type prefix, Symbol symbol): + buffer.append("SingleType("); + Debug.append(buffer, prefix); + buffer.append(','); + Debug.append(buffer, symbol); + buffer.append(')'); + return; + + case CompoundType(Type[] basetypes, Scope members): + buffer.append("CompoundType("); + Debug.append(buffer, basetypes); + buffer.append(','); + Debug.append(buffer, members); + buffer.append(')'); + return; + + case MethodType(Symbol[] vparams, Type result): + buffer.append("MethodType("); + Debug.append(buffer, vparams); + buffer.append(','); + Debug.append(buffer, result); + buffer.append(')'); + return; + + case PolyType(Symbol[] tparams, Type result): + buffer.append("PolyType("); + Debug.append(buffer, tparams); + buffer.append(','); + Debug.append(buffer, result); + buffer.append(')'); + return; + + case CovarType(Type tp): + buffer.append("CovarType("); + Debug.append(buffer, tp); + buffer.append(')'); + return; + + case OverloadedType(Symbol[] alts, Type[] alttypes): + buffer.append("OverloadedType("); + Debug.append(buffer, alts); + buffer.append(','); + Debug.append(buffer, alttypes); + buffer.append(')'); + return; + + default: + Debug.appendDefault(buffer, that); + return; + } + } + + //######################################################################## + // DebugHandler interface + + public void append0(StringBuffer buffer, Object that) { + append1(buffer, (Type)that); + } + + //######################################################################## +} + +public class DebugSymbol extends DebugAbstractHandler { + + //######################################################################## + // DebugSymbol interface + + public static DebugSymbol instance = new DebugSymbol(); + + public void append1(StringBuffer buffer, Symbol that) { + if (that != Symbol.NONE && that != Symbol.ERROR) { + buffer.append(that.kindString()).append(' '); + if (that.owner() != Global.instance.definitions.ROOT_CLASS) { + buffer.append(that.owner().fullName()); + buffer.append("::"); + } + } + buffer.append(that.name); + if (Global.instance.uniqid) { + buffer.append('#'); + buffer.append(Global.instance.uniqueID.id(that)); + } + } + + //######################################################################## + // DebugHandler interface + + public void append0(StringBuffer buffer, Object that) { + append1(buffer, (Symbol)that); + } + + //######################################################################## +} + +public class DebugScope extends DebugAbstractHandler { + + //######################################################################## + // DebugScope interface + + public static DebugScope instance = new DebugScope(); + + public void append1(StringBuffer buffer, Scope that) { + buffer.append('{'); + for (Scope.SymbolIterator i = that.iterator(); i.hasNext();) { + Debug.append(buffer, i.next()); + if (i.hasNext()) buffer.append(','); + } + buffer.append('}'); + } + + //######################################################################## + // DebugHandler interface + + public void append0(StringBuffer buffer, Object that) { + append1(buffer, (Scope)that); + } + + //######################################################################## +} + diff --git a/sources/scalac/util/FreshNameCreator.java b/sources/scalac/util/FreshNameCreator.java new file mode 100644 index 0000000000..fb69ad5c33 --- /dev/null +++ b/sources/scalac/util/FreshNameCreator.java @@ -0,0 +1,59 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scalac.util; + +import java.util.HashMap; + +public class FreshNameCreator { + + protected int counter = 0; + protected HashMap counters = new HashMap(); + + /** + * Create a fresh name with the given prefix. It is guaranteed + * that the returned name has never been returned by a previous + * call to this function with the same separator character (which + * has to be a non-digit). + */ + public Name newName(String prefix, char separator) { + prefix += separator; + Integer ival = (Integer)counters.get(prefix); + if (ival == null) + counters.put(prefix, ival = new Integer(0)); + else + counters.put(prefix, ival = new Integer(ival.intValue() + 1)); + return Name.fromString(prefix + ival); + } + + /** Same, with `$' as the separator character + */ + public Name newName(String prefix) { + return newName(prefix, '$'); + } + + /** Same, but with a name as prefix. The new name is a type + * (respectively, constructor) name if the prefix is one. + */ + public Name newName(Name prefixName, char separator) { + Name name = newName(prefixName.toString(), separator); + if (prefixName.isTypeName()) return name.toTypeName(); + else if (prefixName.isConstrName()) return name.toConstrName(); + else return name; + } + + /** Same, with `$' as the separator character + */ + public Name newName(Name prefix) { + return newName(prefix, '$'); + } + + public Name newName() { + return Name.fromString("$" + (counter++) + "$"); + } +} diff --git a/sources/scalac/util/Name.java b/sources/scalac/util/Name.java new file mode 100644 index 0000000000..0c04fe2dcb --- /dev/null +++ b/sources/scalac/util/Name.java @@ -0,0 +1,391 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.util; + +public final class Name { + +/** address in the name memory + */ + public int index; + +/** length of the name + */ + private int len; + +/** next name in the same hash bucket + */ + private Name next; + + private final static int HASH_SIZE = 0x8000; + private final static int HASH_MASK = 0x7FFF; + private final static int NAME_SIZE = 0x20000; + +/** memory to store all names sequentially + */ + public static byte[] names = new byte[NAME_SIZE]; + private static int nc = 0; + +/** hashtable for finding term names quickly + */ + private static Name[] termHashtable = new Name[HASH_SIZE]; + +/** hashtable for finding type names quickly + */ + private static Name[] typeHashtable = new Name[HASH_SIZE]; + +/** hashtable for finding constructor names quickly + */ + private static Name[] constrHashtable = new Name[HASH_SIZE]; + +/** Constructor + */ + Name(int index, int len, Name[] table, int hash) { + this.index = index; + this.len = len; + this.next = table[hash]; + table[hash] = this; + } + +/** the hashcode of a name + */ + private static int hashValue(byte cs[], int offset, int len) { + if (len > 0) + return + len * (41 * 41 * 41) + + cs[offset] * (41 * 41) + + cs[offset + len - 1] * 41 + + cs[offset + (len >> 1)]; + else + return 0; + } + +/** is (the ascii representation of) name equal to + * cs[offset..offset+len-1]? + */ + private static boolean equals(int index, byte cs[], int offset, int len) { + int i = 0; + while ((i < len) && (names[index + i] == cs[offset + i])) + i++; + return i == len; + } + +/** the empty name + */ + public static final Name EMPTY = fromString(""); + +/** create a term name from the bytes in cs[offset..offset+len-1]. + * assume that bytes are in ascii format. + */ + public static Name fromAscii(byte cs[], int offset, int len) { + int h = hashValue(cs, offset, len) & HASH_MASK; + Name n = termHashtable[h]; + while ((n != null) && (n.len != len || !equals(n.index, cs, offset, len))) + n = n.next; + if (n == null) { + n = new Name(nc, len, termHashtable, h); + for (int i = 0; i < len; i++) + { + if (nc == names.length) + { + byte[] newnames = new byte[names.length * 2]; + System.arraycopy(names, 0, newnames, 0, names.length); + names = newnames; + } + names[nc++] = cs[offset + i]; + } + if (len == 0) + nc++; + } + return n; + } + +/** create a name from the bytes in cs[offset..offset+len-1]; + * assume that characters are in source format + */ + public static Name fromSource(byte cs[], int offset, int len) { + byte[] ascii = new byte[len * 2]; + int alen = SourceRepresentation.source2ascii(cs, offset, len, ascii); + return fromAscii(ascii, 0, alen); + } + +/** create a name from the characters in string s + */ + public static Name fromString(String s) { + byte[] source = SourceRepresentation.string2source(s); + return fromSource(source, 0, source.length); + } + +/** copy bytes of this name to buffer cs, starting at offset + */ + public void copyAscii(byte cs[], int offset) { + System.arraycopy(names, index, cs, offset, len); + } + +/** return the ascii representation of this name + */ + public byte[] toAscii() { + byte[] ascii = new byte[len]; + System.arraycopy(names, index, ascii, 0, len); + return ascii; + } + +/** return the source representation of this name + */ + public byte[] toSource() { + return SourceRepresentation.string2source(toString()); + } + +/** return the string representation of this name + */ + public String toString() { + return SourceRepresentation.ascii2string(names, index, len); + } + +/** is this name a term name? + */ + public boolean isTermName() { + int h = hashValue(names, index, len) & HASH_MASK; + Name n = termHashtable[h]; + while (n != null && n != this) + n = n.next; + return n == this; + } + +/** is this name a type name? + */ + public boolean isTypeName() { + int h = hashValue(names, index, len) & HASH_MASK; + Name n = typeHashtable[h]; + while (n != null && n != this) + n = n.next; + return n == this; + } + +/** is this name a type name? + */ + public boolean isConstrName() { + int h = hashValue(names, index, len) & HASH_MASK; + Name n = constrHashtable[h]; + while (n != null && n != this) + n = n.next; + return n == this; + } + +/** create a term name corresponding to this name + */ + public Name toTermName() { + int h = hashValue(names, index, len) & HASH_MASK; + Name n = termHashtable[h]; + while (n != null && n.index != index) + n = n.next; + if (n == null) { + n = new Name(index, len, termHashtable, h); + } + return n; + } + +/** create a type name corresponding to this name + */ + public Name toTypeName() { + int h = hashValue(names, index, len) & HASH_MASK; + Name n = typeHashtable[h]; + while (n != null && n.index != index) + n = n.next; + if (n == null) { + n = new Name(index, len, typeHashtable, h); + } + return n; + } + +/** create a constructor name corresponding to this name + */ + public Name toConstrName() { + int h = hashValue(names, index, len) & HASH_MASK; + Name n = constrHashtable[h]; + while (n != null && n.index != index) + n = n.next; + if (n == null) { + n = new Name(index, len, constrHashtable, h); + } + return n; + } + +/** return the string hash value of this name + */ + public int hashCode() { + return index; + } + +/** returns the length of this name + */ + public int length() { + return len; + } + +/** returns i'th byte of this name + */ + public byte sub(int i) { + return names[index + i]; + } + +/** returns first occurrence of byte b in this name, len if not found + */ + public int pos(byte b) { + return pos(b, 0); + } + +/** returns first occurrence of byte b in this name from `start', len if not found + */ + public int pos(byte b, int start) { + int i = start; + while (i < len && names[index + i] != b) + i++; + return i; + } + +/** returns last occurrence of byte b in this name, -1 if not found. + */ + public int lastPos(byte b) { + int i = len - 1; + while (i >= 0 && names[index + i] != b) + i--; + return i; + } + +/** does this name start with prefix? + */ + public boolean startsWith(Name prefix) { + int i = 0; + while ((i < prefix.len) && + (i < len) && + (names[index + i] == names[prefix.index + i])) + i++; + return i == prefix.len; + } + +/** does this name end with suffix? + */ + public boolean endsWith(Name suffix) { + int i = len - 1; + int j = suffix.len - 1; + while ((j >= 0) && (i >= 0) && + (names[index + i] == names[suffix.index + j])) { + i--; + j--; + } + return j < 0; + } + +/** returns the subName starting at position start, excluding position end + */ + public Name subName(int start, int end) { + if (end < start) + end = start; + byte[] ascii = new byte[end - start]; + System.arraycopy(names, index + start, ascii, 0, end - start); + return fromAscii(ascii, 0, ascii.length); + } + +/** replace all `from' characters with `to' + */ + public Name replace(byte from, byte to) { + byte[] ascii = new byte[len]; + copyAscii(ascii, 0); + for (int i = 0; i < len; i++) + if (ascii[i] == from) ascii[i] = to; + return fromAscii(ascii, 0, len); + } + +/** returns the concatenation of this name and n + */ + public Name append(Name n) { + byte[] ascii = new byte[len + n.len]; + copyAscii(ascii, 0); + n.copyAscii(ascii, len); + return fromAscii(ascii, 0, ascii.length); + } + +/** returns the concatenation of all names in ns + */ + public static Name concat(Name ns[]) { + int len = 0; + for (int i = 0; i < ns.length; i++) + len = len + ns[i].len; + byte[] ascii = new byte[len]; + len = 0; + for (int i = 0; i < ns.length; i++) { + ns[i].copyAscii(ascii, len); + len = len + ns[i].len; + } + return fromAscii(ascii, 0, len); + } + +/** is this name an operator? + */ + public boolean isOperator() { + return !(((names[index] >= 'a') && (names[index] <= 'z')) || + ((names[index] >= 'A') && (names[index] <= 'Z')) || + (names[index] == '$') || + (names[index] == '_')); + } + +/** is this name a variable identifier? + */ + public boolean isVariable() { + return ((names[index] >= 'a') && (names[index] <= 'z')) || + (names[index] == '_') && + this != Names.null_; + } + + public static final Name ERROR = Name.fromString("<error>"); + +/** precedence of this name + */ + public int precedence() { + if (this == ERROR) + return -1; + byte ch = names[index]; + if (((ch >= 'A') && (ch <= 'Z')) || + ((ch >= 'a') && (ch <= 'z'))) + return 1; + switch (ch) { + case '|': + return 2; + case '^': + return 3; + case '&': + return 4; + case '<': + case '>': + return 5; + case '=': + case '!': + return 6; + case ':': + return 7; + case '+': + case '-': + return 8; + case '*': + case '/': + case '%': + return 9; + default: + return 10; + } + } + + public static final int TOP_PRECEDENCE = 11; + +/** is this operator left associative + */ + public boolean leftAssoc() { + return names[index] != ':'; + } +} diff --git a/sources/scalac/util/NameTransformer.java b/sources/scalac/util/NameTransformer.java new file mode 100644 index 0000000000..c9b5e2183f --- /dev/null +++ b/sources/scalac/util/NameTransformer.java @@ -0,0 +1,111 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.util; + +/** A name transformer for replacing operator symbols in names by predefined + * names of the form $opname. + * + * @author Martin Odersky, Christine Roeckl + * @version 1.1 + */ +public class NameTransformer { + + public static String[] operatorName = new String[128]; + + static { + operatorName['~'] = "$tilde"; + operatorName['='] = "$eq"; + operatorName['<'] = "$less"; + operatorName['>'] = "$greater"; + operatorName['!'] = "$bang"; + operatorName['#'] = "$hash"; + operatorName['%'] = "$percent"; + operatorName['^'] = "$up"; + operatorName['&'] = "$amp"; + operatorName['|'] = "$bar"; + operatorName['*'] = "$times"; + operatorName['/'] = "$div"; + operatorName['+'] = "$plus"; + operatorName['-'] = "$minus"; + operatorName[':'] = "$colon"; + } + + /** Replace operator symbols by corresponding "$op_name" in names. + */ + public static Name encode(Name name) { + int i = name.index; + int len = i + name.length(); + StringBuffer res = new StringBuffer(); + while (i < len) { + byte c = Name.names[i++]; + String nop = operatorName[c]; + if (nop == null) + res.append((char)c); + else + res.append(nop); + } + return Name.fromString(res.toString()); + } + + /** Replace "$op_name" by corresponding operator symbols in names. + */ + public static Name decode(Name name) { + int i = name.index; + int length = i + name.length(); + StringBuffer res = new StringBuffer(); + while (i < length) { + byte c = Name.names[i++]; + if ((char)c == '$') { + String op = cutOut(i, length); + res.append(decode("$" + op)); + i = i + op.length(); + } + else res.append((char)c); + } + return Name.fromString(res.toString()); + } + + /** Decodes (a prefix of) a string. + */ + public static String decode(String string) { + for (int i = string.length(); i > 0; i--) { + String prefix = string.substring(0, i); + String c = lookup(prefix); + if (c != null) { + String suffix = string.substring(i, string.length()); + return c + suffix; + } + } + return string; + } + + /** Cuts out the name of an operator plus succeeding characters. + * Does NOT return the preceeding '$' symbol. + */ + private static String cutOut(int pos, int length) { + int i = pos; // avoid side-effects + StringBuffer res = new StringBuffer(); + while (i < length) { + char c = (char)Name.names[i++]; + if (c == '$') return res.toString(); + else res.append(c); + } + return res.toString(); + } + + /** Looks up the array entry for the operator name. + */ + private static String lookup(String string) { + for (int i = 0; i < 128; i++) { + if ((operatorName[i] != null) && (string.compareTo(operatorName[i]) == 0)) + return String.valueOf((char)i); + } + return null; + } +} diff --git a/sources/scalac/util/Names.java b/sources/scalac/util/Names.java new file mode 100644 index 0000000000..a05aad45de --- /dev/null +++ b/sources/scalac/util/Names.java @@ -0,0 +1,98 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** +** $Id$ +\* */ +package scalac.util; + +public class Names { + + public static final Name ERROR = Name.ERROR; + public static final Name EMPTY = Name.EMPTY; + public static final Name WILDCARD = Name.fromString("_"); + public static final Name COMPOUND_NAME = Name.fromString("<ct>"); + public static final Name ANON_CLASS_NAME = Name.fromString("$class"); + + public static final Name _EQ = NameTransformer.encode(Name.fromString("_=")); + public static final Name MINUS = NameTransformer.encode(Name.fromString("-")); + public static final Name PLUS = NameTransformer.encode(Name.fromString("+")); + public static final Name BANG = NameTransformer.encode(Name.fromString("!")); + public static final Name TILDE = NameTransformer.encode(Name.fromString("~")); + public static final Name EQEQ = NameTransformer.encode(Name.fromString("==")); + public static final Name BANGEQ = NameTransformer.encode(Name.fromString("!=")); + public static final Name BARBAR = NameTransformer.encode(Name.fromString("||")); + public static final Name AMPAMP = NameTransformer.encode(Name.fromString("&&")); + public static final Name COLONCOLON = NameTransformer.encode(Name.fromString("::")); + + public static final Name Any = Name.fromString("Any"); + public static final Name AnyVal = Name.fromString("AnyVal"); + public static final Name AnyRef = Name.fromString("AnyRef"); + public static final Name Array = Name.fromString("Array"); + public static final Name Byte = Name.fromString("Byte"); + public static final Name Char = Name.fromString("Char"); + public static final Name Boolean = Name.fromString("Boolean"); + public static final Name Double = Name.fromString("Double"); + public static final Name False = Name.fromString("False"); + public static final Name Float = Name.fromString("Float"); + public static final Name Function = Name.fromString("Function"); + public static final Name Int = Name.fromString("Int"); + public static final Name Long = Name.fromString("Long"); + public static final Name Nil = Name.fromString("Nil"); + public static final Name Object = Name.fromString("Object"); + public static final Name Predef = Name.fromString("Predef"); + public static final Name Short = Name.fromString("Short"); + public static final Name String = Name.fromString("String"); + public static final Name Throwable = Name.fromString("Throwable"); + public static final Name True = Name.fromString("True"); + public static final Name Tuple = Name.fromString("Tuple"); + public static final Name Unit = Name.fromString("Unit"); + public static final Name apply = Name.fromString("apply"); + public static final Name as = Name.fromString("as"); + public static final Name elem = Name.fromString("elem"); + public static final Name filter = Name.fromString("filter"); + public static final Name flatmap = Name.fromString("flatMap"); + public static final Name foreach = Name.fromString("foreach"); + public static final Name hashCode = Name.fromString("hashCode"); + public static final Name is = Name.fromString("is"); + public static final Name java = Name.fromString("java"); + public static final Name java_lang = Name.fromString("java.lang"); + public static final Name java_lang_Object = Name.fromString("java.lang.Object"); + public static final Name java_lang_String = Name.fromString("java.lang.String"); + public static final Name java_lang_Throwable = Name.fromString("java.lang.Throwable"); + public static final Name lang = Name.fromString("lang"); + public static final Name match = Name.fromString("match"); + public static final Name map = Name.fromString("map"); + public static final Name null_ = Name.fromString("null"); + public static final Name predef = Name.fromString("predef"); + public static final Name runtime = Name.fromString("runtime"); + public static final Name scala = Name.fromString("scala"); + public static final Name scala_Algebraic = Name.fromString("scala.Algebraic"); + public static final Name scala_Any = Name.fromString("scala.Any"); + public static final Name scala_AnyRef = Name.fromString("scala.AnyRef"); + public static final Name scala_AnyVal = Name.fromString("scala.AnyVal"); + public static final Name scala_Array = Name.fromString("scala.Array"); + public static final Name scala_Boolean = Name.fromString("scala.Boolean"); + public static final Name scala_Byte = Name.fromString("scala.Byte"); + public static final Name scala_Case = Name.fromString("scala.Case"); + public static final Name scala_Char = Name.fromString("scala.Char"); + public static final Name scala_Double = Name.fromString("scala.Double"); + public static final Name scala_Float = Name.fromString("scala.Float"); + public static final Name scala_Function = Name.fromString("scala.Function"); + public static final Name scala_Int = Name.fromString("scala.Int"); + public static final Name scala_Long = Name.fromString("scala.Long"); + public static final Name scala_Nil = Name.fromString("scala.Nil"); + public static final Name scala_Object = Name.fromString("scala.Object"); + public static final Name scala_Predef = Name.fromString("scala.Predef"); + public static final Name scala_Ref = Name.fromString("scala.Ref"); + public static final Name scala_Short = Name.fromString("scala.Short"); + public static final Name scala_Tuple = Name.fromString("scala.Tuple"); + public static final Name scala_Unit = Name.fromString("scala.Unit"); + public static final Name scala_runtime = Name.fromString("scala.runtime"); + public static final Name toString = Name.fromString("toString"); + public static final Name this_ = Name.fromString("this"); + public static final Name throw_ = Name.fromString("throw"); + public static final Name update = Name.fromString("update"); +} + diff --git a/sources/scalac/util/OptionParser.java b/sources/scalac/util/OptionParser.java new file mode 100644 index 0000000000..01eecb2208 --- /dev/null +++ b/sources/scalac/util/OptionParser.java @@ -0,0 +1,546 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scalac.util; + +import java.text.Format; +import java.text.MessageFormat; +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.StringTokenizer; + +import scalac.Main; +import scalac.PhaseDescriptor; +//import scalac.optimizer.OptimizePhase; + +public class CommandParser { + + private final String product; + private final String version; + private final String syntax; + private final Reporter reporter; + private final List/*<ArgumentParser>*/ parsers; + + public CommandParser(String product, String version, String syntax, + Reporter reporter) + { + this.product = product; + this.version = version; + this.syntax = syntax; + this.reporter = reporter; + this.parsers = new ArrayList(); + } + + public String product() { + return product; + } + + public String version() { + return version; + } + + public String syntax() { + return syntax; + } + + public Reporter reporter() { + return reporter; + } + + public boolean add(ArgumentParser parser) { + return parsers.add(parser); + } + + public void add(int index, ArgumentParser parser) { + parsers.add(index, parser); + } + + public boolean remove(ArgumentParser parser) { + return parsers.remove(parser); + } + + public List parsers() { + return parsers; + } + + public boolean parse(String[] args) { + int errors = reporter.errors(); + for (int i = 0; i < args.length; ) { + for (int j = 0; j < parsers.size(); j++) { + ArgumentParser parser = (ArgumentParser)parsers.get(j); + if (parser.matches(args, i)) { + i = parser.consume(args, i); + break; + } + } + } + return reporter.errors() == errors; + } + + public String getHelpMessage() { + Format format = new MessageFormat(" {0}\t {1}"); + List options = new ArrayList(parsers.size()); + for (int i = 0; i < parsers.size(); i++) { + if (!(parsers.get(i) instanceof OptionParser)) continue; + OptionParser parser = (OptionParser)parsers.get(i); + String option = parser.getHelpMessage(format); + if (option != null) options.add(option); + } + StringBuffer buffer = new StringBuffer(); + buffer.append("usage: ").append(product()); + if (options.size() > 0) buffer.append(" <options>"); + if (syntax != null) buffer.append(' ').append(syntax); + buffer.append(Strings.EOL); + if (options.size() > 0) { + buffer.append("where possible options include:"); + buffer.append(Strings.EOL); + buffer.append(Strings.format(options)); + } + return buffer.toString(); + } + + public void error(String message) { + reporter.error(product + ": " + message); + } + + public void warning(String message) { + reporter.warning(product + ": " + message); + } + + public void note(String message) { + reporter.note(product + ": " + message); + } +} + +public abstract class ArgumentParser { + + public final CommandParser command; + + public ArgumentParser(CommandParser command) { + this.command = command; + } + + public abstract boolean matches(String[] args, int index); + public abstract int consume(String[] args, int index); + +} + +public class UnknownArgumentParser extends ArgumentParser { + + public UnknownArgumentParser(CommandParser command) { + super(command); + } + + public boolean matches(String[] args, int index) { + return true; + } + + public int consume(String[] args, int index) { + command.error("don't known what to do with '" + args[index] + "'"); + return index + 1; + } +} + +public class ScalaFileArgumentParser extends ArgumentParser { + + public final List list; + + public ScalaFileArgumentParser(CommandParser command) { + super(command); + this.list = new ArrayList(); + } + + public boolean matches(String[] args, int index) { + return args[index].endsWith(".scala"); + } + + public int consume(String[] args, int index) { + list.add(args[index]); + return index + 1; + } + + public String[] toArray() { + return (String[])list.toArray(new String[list.size()]); + } +} + +public class ScalaProgramArgumentParser extends ArgumentParser { + + public String main; + public String[] args; + + public ScalaProgramArgumentParser(CommandParser command) { + super(command); + } + + public boolean matches(String[] args, int index) { + return args[index].equals("--"); + } + + public int consume(String[] args, int index) { + if (index + 1 < args.length) { + this.main = args[index + 1]; + this.args = new String[args.length - index - 2]; + System.arraycopy(args, index + 2, this.args, 0, this.args.length); + return args.length; + } else { + command.error("option --: missing module name"); + return args.length; + } + } +} + +public abstract class OptionParser extends ArgumentParser { + + public final String option; + public final String description; + + public OptionParser(CommandParser command, String option, + String description) + { + super(command); + this.option = option; + this.description = description; + } + + public String getHelpSyntax() { + return "-" + option; + } + + public String getHelpDescription() { + return description; + } + + public void getHelpMessageArgs(List args) { + args.add(getHelpSyntax()); + args.add(getHelpDescription()); + } + + public String getHelpMessage(Format format) { + if (description == null) return null; + List args = new ArrayList(); + getHelpMessageArgs(args); + return format.format(args.toArray()); + } + + public void error(String message) { + command.error("option -" + option + ": " + message); + } + + public void warning(String message) { + command.warning("option -" + option + ": " + message); + } + + public void note(String message) { + command.note("option -" + option + ": " + message); + } +} +/* +public class OptimizeOptionParser extends OptionParser { + + private final OptimizePhase optimizer; + public boolean optimize; + + public OptimizeOptionParser(CommandParser command, + String option, String description, OptimizePhase optimizer) + { + super(command, option, description); + this.optimizer = optimizer; + this.optimize = false; + } + + public boolean matches(String[] args, int index) { + return args[index].equals("-" + option); + } + + public int consume(String[] args, int index) { + optimizer.setOptions(args[index].substring(1 + option.length())); + optimize = true; + return index + 1; + } + + public String getHelpSyntax() { + return super.getHelpSyntax() + "[:<options>]"; + } +} +*/ +public class VersionOptionParser extends OptionParser { + + private final String version; + + public VersionOptionParser(CommandParser command, + String option, String description, String version) + { + super(command, option, description); + this.version = version; + } + + public boolean matches(String[] args, int index) { + return args[index].equals("-" + option); + } + + public int consume(String[] args, int index) { + System.out.println(version); + System.exit(0); + return index + 1; + } +} + +public class HelpOptionParser extends OptionParser { + + public HelpOptionParser(CommandParser command, + String option, String description) + { + super(command, option, description); + } + + public boolean matches(String[] args, int index) { + return args[index].equals("-?") || + args[index].equals("-" + option) || + args[index].equals("--" + option); + } + + public int consume(String[] args, int index) { + System.out.println(command.getHelpMessage()); + System.exit(0); + return index + 1; + } + + public String getHelpSyntax() { + return "-? " + super.getHelpSyntax(); + } +} + +public class UnknownOptionParser extends OptionParser { + + public UnknownOptionParser(CommandParser command) { + super(command, "", null); + } + + public boolean matches(String[] args, int index) { + return args[index].startsWith("-"); + } + + public int consume(String[] args, int index) { + command.error("unknown option " + args[index]); + return index + 1; + } +} + +public class BooleanOptionParser extends OptionParser { + + public boolean value; + + public BooleanOptionParser(CommandParser command, + String option, String description, boolean value) + { + super(command, option, description); + this.value = value; + } + + public boolean matches(String[] args, int index) { + return args[index].equals("-" + option); + } + + public int consume(String[] args, int index) { + value = true; + return index + 1; + } +} + +public class StringOptionParser extends OptionParser { + + public String value; + public String argument; + + public StringOptionParser(CommandParser command, + String option, String description, String argument, String value) + { + super(command, option, description); + this.argument = argument; + this.value = value; + } + + public boolean matches(String[] args, int index) { + return args[index].equals("-" + option); + } + + public int consume(String[] args, int index) { + if (index + 1 < args.length) { + value = args[index + 1]; + return index + 2; + } else { + error("missing argument"); + return index + 1; + } + } + + public String getHelpSyntax() { + String syntax = super.getHelpSyntax(); + if (argument != null) syntax = syntax + " <" + argument + ">"; + return syntax; + } +} + +public class PhaseSetOptionParser extends OptionParser { + + private final PhaseDescriptor[] phases; + private final int flag; + private final PrefixMatcher matcher; + + public PhaseSetOptionParser(CommandParser command, + String option, String description, PhaseDescriptor[] phases, int flag) + { + super(command, option, description); + this.phases = phases; + this.flag = flag; + this.matcher = new PrefixMatcher(); + for (int i = 0; i < phases.length; i++) { + PhaseDescriptor phase = phases[i]; + matcher.insert(phase.name(), phase, phase.description()); + } + } + + public boolean matches(String[] args, int index) { + return args[index].startsWith("-" + option + ":"); + } + + public int consume(String[] args, int index) { + StringTokenizer tokens = new StringTokenizer( + args[index].substring(option.length() + 2), ","); + while (tokens.hasMoreTokens()) consumePhase(tokens.nextToken()); + return index + 1; + } + + public void consumePhase(String token) { + if (token.equals("all")) { + for (int i = 0; i < phases.length; i++) phases[i].flags |= flag; + return; + } + PhaseDescriptor phase = lookup(getPhaseName(token)); + if (phase != null) { + boolean before = getBeforeFlag(token); + boolean after = getAfterFlag(token) || !before; + if (before) phase.flags |= flag << 16; + if (after) phase.flags |= flag; + } + } + + public PhaseDescriptor lookup(String name) { + if (name.length() == 0) { + error("illegal zero-length phase name"); + return null; + } + PrefixMatcher.Entry[] entries = matcher.lookup(name); + if (entries.length == 1) return (PhaseDescriptor)entries[0].value; + error(matcher.getErrorMessage(name, entries, "phase name")); + return null; + } + + public boolean getBeforeFlag(String token) { + for (int i = token.length(); 0 < i--; ) { + switch (token.charAt(i)) { + case '-': return true; + case '+': continue; + default : return false; + } + } + return false; + } + + public boolean getAfterFlag(String token) { + for (int i = token.length(); 0 < i--; ) { + switch (token.charAt(i)) { + case '-': continue; + case '+': return true; + default : return false; + } + } + return false; + } + + public String getPhaseName(String token) { + for (int i = token.length(); 0 < i--; ) { + switch (token.charAt(i)) { + case '-': continue; + case '+': continue; + default : return token.substring(0, i + 1); + } + } + return ""; + } + + public String getHelpSyntax() { + return super.getHelpSyntax() + ":<phases>"; + } +} + +public class PrintOptionParser extends PhaseSetOptionParser { + + public boolean tokens; + + public PrintOptionParser(CommandParser command, + String option, String description, PhaseDescriptor[] phases, int flag) + { + super(command, option, description, phases, flag); + this.tokens = false; + } + + public void consumePhase(String token) { + if ("tokens".equals(token)) + tokens = true; + else + super.consumePhase(token); + } + +} + +public class ChoiceOptionParser extends OptionParser { + + public final String argument; + public final String[] choices; + + public String value; + + public ChoiceOptionParser(CommandParser command, + String option, String description, String argument, String[] choices, + String value) + { + super(command, option, description); + this.argument = argument; + this.choices = choices; + this.value = value; + } + + public boolean matches(String[] args, int index) { + return args[index].startsWith("-" + option + ":"); + } + + public int consume(String[] args, int index) { + String choice = args[index].substring(option.length() + 2); + boolean found = false; + for (int i = 0; i < choices.length; i++) { + if (choices[i].equals(choice)) { found = true; break; } + } + if (found) { + value = choice; + } else if (choice.length() > 0) { + error("unknown " + argument + " '" + choice + "'"); + } else { + error("missing " + argument); + } + return index + 1; + } + + public String getHelpSyntax() { + String syntax = super.getHelpSyntax(); + if (argument != null) syntax = syntax + ":<" + argument + ">"; + return syntax; + } +} diff --git a/sources/scalac/util/Position.java b/sources/scalac/util/Position.java new file mode 100644 index 0000000000..c2cd7f72d3 --- /dev/null +++ b/sources/scalac/util/Position.java @@ -0,0 +1,54 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id$ +\* */ + +package scalac.util; + + +public final class Position { + +/** source file positions are integers in the format: + * line-number << LINESHIFT + column-number + * NOPOS represents an undefined position. + */ + public static final int LINESHIFT = 10; + public static final int FILESHIFT = 26; + public static final int COLUMNMASK = 1023; + public static final int LINEMASK = 0xffff; + +/** predefined positions + */ + public static final int NOPOS = 0; + +/** first position in a source file + */ + public static final int FIRSTPOS = (1 << LINESHIFT) + 1; + +/** encode a line and column number into a single int + */ + public static int encode(int line, int col, int file) { + return (file << FILESHIFT) | (line << LINESHIFT) | col; + } + +/** get the file id of an encoded position + */ + public static int file(int pos) { + return pos >>> FILESHIFT; + } + +/** get the line number out of an encoded position + */ + public static int line(int pos) { + return (pos >>> LINESHIFT) & LINEMASK; + } + +/** return the column number of an encoded position + */ + public static int column(int pos) { + return pos & COLUMNMASK; + } +} diff --git a/sources/scalac/util/PrefixMatcher.java b/sources/scalac/util/PrefixMatcher.java new file mode 100644 index 0000000000..1b0acd8d7a --- /dev/null +++ b/sources/scalac/util/PrefixMatcher.java @@ -0,0 +1,114 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scalac.util; + +import java.util.Map; +import java.util.HashMap; +import java.util.List; +import java.util.ArrayList; + +public class PrefixMatcher { + + public static class Entry { + + private Entry prev; + private Entry next; + + public final String key; + public final Object value; + public final String argument; + public final String description; + + public Entry(String key, Object value, String argument, + String description) + { + this.key = key; + this.value = value; + this.argument = argument; + this.description = description; + } + } + + private final Map entries; + private Entry first; + private Entry last; + + public PrefixMatcher() { + this.entries = new HashMap(); + } + + public void insert(String key, Object value) { + insert(key, value, null, null); + } + + public void insert(String key, Object value, String description) { + insert(key, value, null, description); + } + + public void insert(String key, Object value, String argument, + String description) + { + assert key != null && !entries.containsKey(key) : key; + Entry entry = new Entry(key, value, argument, description); + if (first == null) { + first = last = entry; + } else { + last.next = entry; + entry.prev = last; + last = entry; + } + entries.put(key, entry); + } + + public Entry[] lookup(String key) { + Object value = entries.get(key); + if (value != null) return new Entry[] { (Entry)value }; + List list = new ArrayList(); + for (Entry i = first; i != null; i = i.next) { + if (i.key.startsWith(key)) list.add(i); + } + return (Entry[])list.toArray(new Entry[list.size()]); + } + + public String getErrorMessage(String key, Entry[] entries, String what) { + switch (entries.length) { + case 0: + return "unknown " + what + " '" + key + "'"; + case 1: + return null; + case 2: + return "ambigous " + what + " '" + key + "', both '" + + entries[0].key + "' and '" + entries[1].key + "' match"; + default: + StringBuffer buffer = new StringBuffer(); + buffer.append("ambigous ").append(what); + buffer.append(" '").append(key).append("'"); + for (int i = 0; i < entries.length; i++) { + buffer.append(i < entries.length - 1 ? ", " : " and "); + buffer.append('\'').append(entries[i].key).append('\''); + } + buffer.append(" match"); + return buffer.toString(); + } + } + + public List getHelpStrings(String separator1, String separator2) { + List strings = new ArrayList(); + for (Entry entry = first; entry != null; entry = entry.next) { + if (entry.description != null) + if (entry.argument != null) + strings.add(entry.key + separator1 + entry.argument + + separator2 + entry.description); + else + strings.add(entry.key + separator2 + entry.description); + } + return strings; + } + +} diff --git a/sources/scalac/util/Reporter.java b/sources/scalac/util/Reporter.java new file mode 100644 index 0000000000..a1fb832e28 --- /dev/null +++ b/sources/scalac/util/Reporter.java @@ -0,0 +1,192 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scalac.util; + +import java.io.InputStreamReader; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.IOException; +import scalac.ApplicationError; + +public class Reporter { + + //######################################################################## + // Private state + + private final BufferedReader reader; + private final PrintWriter writer; + + /** Number of errors issued totally */ + private int errors; + /** Number of warning issued totally */ + private int warnings; + /** Number of notes issued totally */ + private int notes; + + //######################################################################## + // Reporter constructors + + public Reporter() { + this( + new BufferedReader(new InputStreamReader(System.in)), + new PrintWriter(System.err, true)); + } + + public Reporter(BufferedReader reader, PrintWriter writer) { + this.reader = reader; + this.writer = writer; + this.prompt = false; + this.nowarn = false; + this.verbose = false; + this.errors = 0; + this.notes = 0; + } + + //######################################################################## + // Reporter state + + /** Whether warnings should be issued */ + public boolean nowarn; + /** Whether notes and information messages should be issued */ + public boolean verbose; + /** Whether a prompt should be displayed after errors and warnings */ + public boolean prompt; + + //######################################################################## + // Reporter interface - query + + /** Return the number of errors issued totally */ + public int errors() { + return errors; + } + + /** Return the number of warnings issued totally */ + public int warnings() { + return warnings; + } + + /** Return the number of notes issued totally */ + public int notes() { + return notes; + } + + /** Return the number of errors issued totally as a string */ + public String getErrorCountString() { + return getCountString(errors, "error"); + } + + /** Return the number of warnings issued totally as a string */ + public String getWarningCountString() { + return getCountString(warnings, "warning"); + } + + /** Return the number of notes issued totally as a string */ + public String getNoteCountString() { + return getCountString(notes, "note"); + } + + public String getCountString(int count, String what) { + switch (count) { + case 0: return "no " + what + "s"; + case 1: return "one " + what; + case 2: return "two " + what + "s"; + case 3: return "three " + what + "s"; + case 4: return "four " + what + "s"; + default: return count + " " + what + "s"; + } + } + + //######################################################################## + // Reporter interface - report + + /** Reset all counters */ + public void resetCounters() { + errors = 0; + warnings = 0; + notes = 0; + } + + /** Issue a message */ + public void report(String message) { + writer.println(message); + } + + /** Issue a message */ + public void inform(String message) { + if (verbose) report(message); + } + + /** Issue an error */ + public void error(String message) { + error(message, false); + } + + /** Issue an error if it is not hidden */ + public void error(String message, boolean hidden) { + if (!hidden || prompt) report(message); + if (!hidden) errors++; + if (prompt) failOnDemand(); + } + + /** Issue a warning */ + public void warning(String message) { + warning(message, false); + } + + /** Issue a warning if it is not hidden */ + public void warning(String message, boolean hidden) { + if (nowarn) return; + if (!hidden || prompt) report(message); + if (!hidden) warnings++; + if (prompt) failOnDemand(); + } + + /** Issue a note */ + public void note(String message) { + note(message, false); + } + + /** Issue a note if it is not hidden */ + public void note(String message, boolean hidden) { + if (!hidden) report(message); + if (!hidden) notes++; + } + + public void printSummary() { + if (errors() > 0) report(getErrorCountString() + " found"); + if (warnings() > 0) report(getWarningCountString() + " found"); + if (notes() > 0) report(getNoteCountString() + " found"); + } + + //######################################################################## + // Reporter interface - fail + + /** Fail only if requested */ + public void failOnDemand() { + failOnDemand("user abort"); + } + + /** Fail only if requested */ + public void failOnDemand(String message) { + try { + while (true) { + writer.print("r)esume, a)bort: "); + String line = reader.readLine(); + if (line == null) continue; else line = line.toLowerCase(); + if ("abort".startsWith(line)) + throw new ApplicationError(message); + if ("resume".startsWith(line)) return; + } + } catch (IOException e) { + throw new ApplicationError("input read error"); + } + } + + //######################################################################## +} diff --git a/sources/scalac/util/SourceRepresentation.java b/sources/scalac/util/SourceRepresentation.java new file mode 100644 index 0000000000..7c6ee44107 --- /dev/null +++ b/sources/scalac/util/SourceRepresentation.java @@ -0,0 +1,205 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +** $Id +\* */ + +package scalac.util; + +public final class SourceRepresentation { + + public static int digit2int(byte ch, int base) { + if ('0' <= ch && ch <= '9' && ch < '0' + base) + return ch - '0'; + else if ('A' <= ch && ch < 'A' + base - 10) + return ch - 'A' + 10; + else if ('a' <= ch && ch < 'a' + base - 10) + return ch - 'a' + 10; + else + return -1; + } + + public static byte int2digit(int x) { + if (x <= 9) + return (byte)(x + '0'); + else + return (byte)(x - 10 + 'A'); + } + +/* the next 4 functions convert between three fundamental name + * representations: + * - string each character 16 bit, + * - source characters outside 0..127 are represented by + * unicode escapes, \ u X X X X + * - ascii characters outside 0..127 are represented by two or three + * byte sequences with high bit set (as in class file format). + */ + +/** convert source bytes in source[offset..offset+len-1] to ascii. + */ + public static int source2ascii(byte source[], int offset, int len, byte ascii[]) { + int j = 0; + int i = 0; + while (i < len) { + if (source[offset + i] == '\\' && i + 1 < len) { + i++; + switch (source[offset + i]) { + case 'n': + ascii[j++] = (byte)'\n'; i++; continue; + case 't': + ascii[j++] = (byte)'\t'; i++; continue; + case 'b': + ascii[j++] = (byte)'\b'; i++; continue; + case 'r': + ascii[j++] = (byte)'\r'; i++; continue; + case 'f': + ascii[j++] = (byte)'\f'; i++; continue; + case 'u': + if (i + 4 < len) { + int code = 0; + int k = 1; + int d = 0; + while (k <= 4 && d >= 0) { + d = digit2int(source[offset + i + k], 16); + code = code * 16 + d; + k++; + } + if (d >= 0) { + if (code <= 0x7F) + ascii[j++] = (byte)code; + else + if (code <= 0x3FF) { + ascii[j++] = (byte)(0xC0 | (code >> 6)); + ascii[j++] = (byte)(0x80 | (code & 0x3F)); + } else { + ascii[j++] = (byte)(0xE0 | (code >> 12)); + ascii[j++] = (byte)(0x80 | + ((code >> 6) & 0x3F)); + ascii[j++] = (byte)(0x80 | (code & 0x3F)); + } + i = i + 5; + continue; + } + } + } + } + byte b = source[offset + i++]; + if (b >= 0) + ascii[j++] = b; + else { + ascii[j++] = (byte)(0xC0 | ((b >> 6) & 0x3)); + ascii[j++] = (byte)(0x80 | (b & 0x3F)); + } + } + return j; + } + +/** convert ascii bytes in ascii[offset..offset+len-1] to a string + */ + public static String ascii2string(byte ascii[], int offset, int len) { + char cs[] = new char[len]; + int i = offset; + int j = 0; + len += offset; + while (i < len) { + int b = ascii[i++] & 0xFF; + if (b >= 0xE0) { + b = ((b & 0x0F) << 12) | (ascii[i++] & 0x3F) << 6; + b = b | (ascii[i++] & 0x3F); + } + else + if (b >= 0xC0) + b = ((b & 0x1F) << 6) | (ascii[i++] & 0x3F); + cs[j++] = (char)b; + } + return new String(cs, 0, j); + } + +/** convert string to array of source bytes + */ + public static byte[] string2source(String s) { + byte[] source = new byte[s.length() * 6]; + int j = 0; + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + switch (ch) { + case '\n': + source[j++] = (byte)'\\'; + source[j++] = (byte)'n'; + break; + case '\t': + source[j++] = (byte)'\\'; + source[j++] = (byte)'t'; + break; + case '\b': + source[j++] = (byte)'\\'; + source[j++] = (byte)'b'; + break; + case '\r': + source[j++] = (byte)'\\'; + source[j++] = (byte)'r'; + break; + case '\f': + source[j++] = (byte)'\\'; + source[j++] = (byte)'f'; + break; + case '\"': + source[j++] = (byte)'\\'; + source[j++] = (byte)'\"'; + break; + case '\'': + source[j++] = (byte)'\\'; + source[j++] = (byte)'\''; + break; + case '\\': + source[j++] = (byte)'\\'; + source[j++] = (byte)'\\'; + break; + default: + if ((' ' <= ch) && (ch <= 127)) + source[j++] = (byte)ch; + else { + source[j++] = (byte)'\\'; + source[j++] = (byte)'u'; + source[j++] = int2digit((ch >> 12) & 0xF); + source[j++] = int2digit((ch >> 8) & 0xF); + source[j++] = int2digit((ch >> 4) & 0xF); + source[j++] = int2digit(ch & 0xF); + } + } + } + byte[] res = new byte[j]; + System.arraycopy(source, 0, res, 0, j); + return res; + } + +/** convert string to array of ascii bytes + */ + public static byte[] string2ascii(String s) { + byte[] source = string2source(s); + byte[] ascii = new byte[source.length * 2]; + int alen = source2ascii(source, 0, source.length, ascii); + byte[] res = new byte[alen]; + System.arraycopy(ascii, 0, res, 0, alen); + return res; + } + +/** escape all characters outside 32..127 in string s + */ + public static String escape(String s) { + try { + return new String(string2source(s), "8859_1"); + } catch (java.io.UnsupportedEncodingException e) { + throw new InternalError(e.getMessage()); + } + } + +/** escape character c, if outside 32..127. + */ + public static String escape(char c) { + char[] s = {c}; + return escape(new String(s)); + } +} diff --git a/sources/scalac/util/Strings.java b/sources/scalac/util/Strings.java new file mode 100644 index 0000000000..540a61020e --- /dev/null +++ b/sources/scalac/util/Strings.java @@ -0,0 +1,102 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +\* */ + +// $Id$ + +package scalac.util; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.List; +import java.util.ArrayList; +import java.util.Iterator; + +public abstract class Strings { + + //######################################################################## + // Strings constants + + final public static String[] NONE = new String[0]; + + //######################################################################## + // Strings interface + + /** The line separator */ + public static String EOL = System.getProperty("line.separator", "\n"); + + /** Returns a string where all tabs have been replaced by white + * spaces to make the corresponding fields the same width. + */ + public static String format(List strings) { + List[] lines = new List[strings.size()]; + List widths = new ArrayList(); + int height = 0; + for (Iterator iterator = strings.iterator(); iterator.hasNext(); ) { + String string = (String)iterator.next(); + List line = lines[height++] = new ArrayList(); + for (int last = 0; last < string.length(); ) { + int next = string.indexOf('\t', last); + if (next < 0) next = string.length(); + String substring = string.substring(last, next); + int index = line.size(); + if (index == widths.size()) widths.add(new Integer(0)); + int width = ((Integer)widths.get(index)).intValue(); + widths.set( + index, new Integer(Math.max(width, substring.length()))); + line.add(substring); + last = next + 1; + } + } + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < lines.length; i++) { + List line = lines[i]; + for (int j = 0; true; j++) { + String string = (String)line.get(j); + buffer.append(string); + if (j == line.size() - 1) break; + int width = ((Integer)widths.get(j)).intValue(); + for (int k = string.length(); k<width; k++) buffer.append(' '); + } + buffer.append(EOL); + } + return buffer.toString(); + } + + /** Returns the first char of the string or -1 if the string is empty. */ + public static int firstChar(String string) { + return string.length() == 0 ? -1 : string.charAt(0); + } + + /** Returns the last char of the string or -1 if the string is empty. */ + public static int lastChar(String string) { + return string.length() == 0 ? -1 : string.charAt(string.length() - 1); + } + + /** Returns a copy of the string, with leading whitespace omitted. */ + public static String trimLeading(String string) { + for (int i = 0; i < string.length(); i++) + if (string.charAt(i) > ' ') return string.substring(i); + return ""; + } + + /** Returns a copy of the string, with trailing whitespace omitted. */ + public static String trimTrailing(String string) { + for (int i = string.length() - 1; i >= 0; i--) + if (string.charAt(i) > ' ') return string.substring(0, i + 1); + return ""; + } + + /** Returns the stack trace of the exception */ + public static String stackTrace(Throwable exception) { + StringWriter buffer = new StringWriter(); + PrintWriter writer = new PrintWriter(buffer); + exception.printStackTrace(writer); + writer.close(); + return buffer.toString(); + } + + //######################################################################## +} diff --git a/sources/scalac/util/UniqueID.java b/sources/scalac/util/UniqueID.java new file mode 100644 index 0000000000..59e31a9de6 --- /dev/null +++ b/sources/scalac/util/UniqueID.java @@ -0,0 +1,30 @@ +/* ____ ____ ____ ____ ______ *\ +** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala ** +** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL ** +** /_____/\____/\___/\____/____/ ** +** ** +\* */ + +// $Id$ + +package scalac.util; + +import java.util.*; + +/** + * Class to assign unique and small numbers to objects, based on their + * identity. + * + * @author Michel Schinz + * @version 1.0 + */ + +public class UniqueID { + protected Map ids = new HashMap(); + + public int id(Object obj) { + if (! ids.containsKey(obj)) + ids.put(obj, new Integer(ids.size())); + return ((Integer)ids.get(obj)).intValue(); + } +} |