aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/cps/CodeGen.h13
-rw-r--r--include/cps/FunctionGen.h5
-rw-r--r--include/cps/SourceUnit.h13
-rw-r--r--include/cps/TypeSystem.h3
-rw-r--r--lib/CodeGen/CodeGen.cc143
-rw-r--r--lib/CodeGen/Designator.cc8
-rw-r--r--lib/CodeGen/FunctionGen.cc4
-rw-r--r--lib/CodeGen/StatementGen.cc5
-rw-r--r--lib/CodeGen/TypeSystem.cc5
-rw-r--r--tools/driver/main.cc103
10 files changed, 245 insertions, 57 deletions
diff --git a/include/cps/CodeGen.h b/include/cps/CodeGen.h
index 9a0b2ef..6e0fab1 100644
--- a/include/cps/CodeGen.h
+++ b/include/cps/CodeGen.h
@@ -5,6 +5,7 @@
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "cps/AST.h"
+#include "cps/SourceUnit.h"
#include "cps/RuntimeCheck.h"
#include "cps/FunctionGen.h"
#include "cps/TypeSystem.h"
@@ -32,6 +33,14 @@ class CodeGen {
std::unique_ptr<llvm::Module> TheModule;
std::unique_ptr<llvm::IRBuilder<>> Builder;
int DiagnosticLine = 0;
+ const std::vector<SourceUnit> *Units = nullptr;
+ size_t CurrentUnit = 0;
+ size_t MainUnit = 0;
+ IdentifierMap<size_t> TypeOwners;
+ std::vector<IdentifierMap<SymbolInfo>> Exports;
+ bool isVisible(size_t Owner) const;
+ void selectUnit(size_t Unit);
+ void importSymbols();
IdentifierMap<SymbolInfo> Symbols;
IdentifierMap<IdentifierMap<FunctionDefAST*>> ClassMethods;
std::string CurrentClass;
@@ -123,7 +132,7 @@ class CodeGen {
void emitCInput(llvm::Value *Addr, const TypeInfo *Info);
// TYPE-system registration (Designator.cc) and operators (TypeCheckGen.cc).
- void runTypePrePass(const std::vector<std::unique_ptr<StmtAST>> &Statements);
+ void runTypePrePass(const std::vector<std::unique_ptr<StmtAST>> &Statements, int Phase);
llvm::Value *emitUserKindBinaryOp(int Op, llvm::Value *L, const TypeInfo *LT,
llvm::Value *R, const TypeInfo *RT, int Line);
@@ -161,6 +170,8 @@ public:
CodeGen();
~CodeGen();
void compile(const std::vector<std::unique_ptr<StmtAST>> &Statements);
+ void compile(const std::vector<SourceUnit> &Sources);
+ size_t getMainUnit() const { return MainUnit; }
void print(llvm::raw_ostream &Output);
const llvm::Module &getModule() const { return *TheModule; }
bool hadError() const { return HadError; }
diff --git a/include/cps/FunctionGen.h b/include/cps/FunctionGen.h
index 777ab7d..eb4a839 100644
--- a/include/cps/FunctionGen.h
+++ b/include/cps/FunctionGen.h
@@ -38,6 +38,7 @@ struct FuncSig {
std::string Name;
std::string ReturnTypeName;
std::vector<ParamSig> Params;
+ size_t OwnerUnit = 0;
};
class FunctionGen {
@@ -63,6 +64,8 @@ private:
void reportError(const char *Fmt, ...);
IdentifierMap<FuncSig> Signatures;
+ size_t CurrentUnit = 0;
+ std::function<bool(size_t)> Visible;
ArrayParamBinder BindArrayParam;
std::function<void(llvm::Value *, const std::string &, int)> RegisterCopy;
@@ -86,6 +89,8 @@ public:
RegisterCopy = std::move(Registrar);
}
+ void setCurrentUnit(size_t Unit) { CurrentUnit = Unit; }
+ void setVisibility(std::function<bool(size_t)> Predicate) { Visible = std::move(Predicate); }
const FuncSig *getSignature(const std::string &Name) const;
llvm::Function *emitPrototype(PrototypeAST *Proto);
diff --git a/include/cps/SourceUnit.h b/include/cps/SourceUnit.h
new file mode 100644
index 0000000..db9ad30
--- /dev/null
+++ b/include/cps/SourceUnit.h
@@ -0,0 +1,13 @@
+#pragma once
+#include "cps/AST.h"
+#include <string>
+#include <vector>
+
+namespace cps {
+// The caller owns the parsed statements for the duration of compilation.
+struct SourceUnit {
+ std::string Path;
+ const std::vector<std::unique_ptr<StmtAST>> &Statements;
+ std::vector<bool> Visible;
+};
+}
diff --git a/include/cps/TypeSystem.h b/include/cps/TypeSystem.h
index d358794..d699067 100644
--- a/include/cps/TypeSystem.h
+++ b/include/cps/TypeSystem.h
@@ -5,6 +5,7 @@
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Type.h"
#include <cstdint>
+#include <functional>
#include <map>
#include <string>
#include <utility>
@@ -118,6 +119,7 @@ class TypeSystem {
IdentifierMap<TypeInfo> Types;
IdentifierMap<EnumConstant> EnumConstants;
+ std::function<bool(const std::string &)> Visible;
void registerBuiltins();
const TypeInfo *registerType(const std::string &TypeName,
llvm::Type *LLVMType,
@@ -126,6 +128,7 @@ class TypeSystem {
public:
explicit TypeSystem(llvm::LLVMContext &Ctx);
+ void setVisibility(std::function<bool(const std::string &)> Predicate) { Visible = std::move(Predicate); }
const TypeInfo *resolve(const std::string &TypeName) const;
// Two-pass user-type registration (pass A then pass B, both in source
diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc
index d3459ef..e7116dd 100644
--- a/lib/CodeGen/CodeGen.cc
+++ b/lib/CodeGen/CodeGen.cc
@@ -5,6 +5,7 @@
#include "cps/MemoryHandler.h"
#include "cps/ExceptionHandler.h"
#include "Builtins.h"
+#include "cps/TypeAST.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Verifier.h"
#include <cstdarg>
@@ -152,7 +153,84 @@ const TypeInfo *CodeGen::resolveType(const std::string &TypeName) const {
return Types->resolve(TypeName);
}
+bool CodeGen::isVisible(size_t Owner) const {
+ return !Units || (*Units)[CurrentUnit].Visible[Owner];
+}
+
+void CodeGen::selectUnit(size_t Unit) {
+ CurrentUnit = Unit;
+ DiagnosticSource = Units->size() > 1 ? (*Units)[Unit].Path : "";
+ FuncGen->setCurrentUnit(Unit);
+}
+
+void CodeGen::importSymbols() {
+ Symbols.clear();
+ for (size_t I = 0; I < Exports.size(); ++I) {
+ if (I != CurrentUnit && isVisible(I))
+ Symbols.insert(Exports[I].begin(), Exports[I].end());
+ }
+}
+
void CodeGen::compile(const std::vector<std::unique_ptr<StmtAST>> &Statements) {
+ compile(std::vector<SourceUnit>{{"", Statements, {true}}});
+}
+
+void CodeGen::compile(const std::vector<SourceUnit> &Sources) {
+ SaveAndRestore<const std::vector<SourceUnit> *> SourceContext(Units, &Sources);
+ SaveAndRestore<std::string> SourceName(DiagnosticSource);
+ if (Sources.empty()) { reportError("no main program"); return; }
+ Exports.resize(Sources.size());
+ Types->setVisibility([this](const std::string &Name) {
+ auto It = TypeOwners.find(Name);
+ return It == TypeOwners.end() || isVisible(It->second);
+ });
+ FuncGen->setVisibility([this](size_t Owner) { return isVisible(Owner); });
+
+ IdentifierMap<size_t> Definitions;
+ std::optional<size_t> CodeUnit;
+ auto Reserve = [&](const std::string &Name) {
+ auto [It, Added] = Definitions.emplace(Name, CurrentUnit);
+ if (!Added && It->second != CurrentUnit)
+ reportError("Duplicate definition %s in %s and %s", Name.c_str(),
+ Sources[It->second].Path.c_str(), Sources[CurrentUnit].Path.c_str());
+ };
+ for (size_t I = 0; I < Sources.size(); ++I) {
+ selectUnit(I);
+ for (const auto &S : Sources[I].Statements) {
+ SaveAndRestore<int> Location(DiagnosticLine, S->getLine());
+ std::string TypeName;
+ if (auto *F = dynamic_cast<FunctionDefAST*>(S.get())) {
+ Reserve(F->getProto()->getName());
+ } else if (auto *E = dynamic_cast<EnumTypeDeclAST*>(S.get())) {
+ TypeName = E->getName();
+ for (const auto &V : E->getValueNames()) Reserve(V);
+ } else if (auto *R = dynamic_cast<RecordTypeDeclAST*>(S.get())) {
+ TypeName = R->getName();
+ } else if (auto *P = dynamic_cast<PointerTypeDeclAST*>(S.get())) {
+ TypeName = P->getName();
+ } else if (auto *C = dynamic_cast<CTypeDeclAST*>(S.get())) {
+ TypeName = C->getName();
+ } else if (auto *C = dynamic_cast<ConstantStmtAST*>(S.get())) {
+ Reserve(C->getName());
+ } else if (auto *G = dynamic_cast<GlobalBlockAST*>(S.get())) {
+ for (const auto &Body : G->getBody())
+ if (auto *D = dynamic_cast<DeclareStmtAST*>(Body.get())) Reserve(D->getName());
+ } else if (!dynamic_cast<IncludeStmtAST*>(S.get())) {
+ if (CodeUnit && *CodeUnit != I) {
+ reportError("Top-level code in both %s and %s",
+ Sources[*CodeUnit].Path.c_str(), Sources[I].Path.c_str());
+ return;
+ }
+ CodeUnit = I;
+ }
+ if (!TypeName.empty()) {
+ Reserve(TypeName);
+ TypeOwners.emplace(TypeName, I);
+ }
+ }
+ }
+ if (HadError) return;
+ MainUnit = CodeUnit.value_or(0);
FunctionType *FT = FunctionType::get(Type::getInt32Ty(*TheContext), false);
Function *F = Function::Create(FT, Function::ExternalLinkage, "main", TheModule.get());
BasicBlock *BB = BasicBlock::Create(*TheContext, "entry", F);
@@ -162,28 +240,61 @@ void CodeGen::compile(const std::vector<std::unique_ptr<StmtAST>> &Statements) {
BasicBlock *UnhandledBB = BasicBlock::Create(*TheContext, "unhandled_exception", F);
Exceptions->pushHandler(UnhandledBB);
- // User types first: prototypes may name them in parameters/returns.
- runTypePrePass(Statements);
+ // Reserve names across every unit before resolving any type payload.
+ for (size_t I = 0; I < Sources.size(); ++I) {
+ selectUnit(I);
+ runTypePrePass(Sources[I].Statements, 0);
+ }
+ // Complete included units first. The visited set makes include cycles harmless.
+ std::vector<bool> Visited(Sources.size(), false);
+ std::function<void(size_t)> DefineTypes = [&](size_t I) {
+ if (Visited[I]) return;
+ Visited[I] = true;
+ for (size_t J = 0; J < Sources.size(); ++J)
+ if (J != I && Sources[I].Visible[J]) DefineTypes(J);
+ selectUnit(I);
+ runTypePrePass(Sources[I].Statements, 1);
+ runTypePrePass(Sources[I].Statements, 2);
+ };
+ for (size_t I = 0; I < Sources.size(); ++I) DefineTypes(I);
- // Pre-register every top-level FUNCTION/PROCEDURE prototype so calls may
- // precede definitions.
- for (const auto &Stmt : Statements) {
- SaveAndRestore<int> Location(DiagnosticLine, Stmt->getLine());
- if (auto *FuncDef = dynamic_cast<FunctionDefAST*>(Stmt.get())) {
- FuncGen->emitPrototype(FuncDef->getProto());
+ for (size_t I = 0; I < Sources.size(); ++I) {
+ selectUnit(I);
+ for (const auto &Stmt : Sources[I].Statements) {
+ SaveAndRestore<int> Location(DiagnosticLine, Stmt->getLine());
+ if (auto *FuncDef = dynamic_cast<FunctionDefAST*>(Stmt.get()))
+ FuncGen->emitPrototype(FuncDef->getProto());
+ if (auto *Class = dynamic_cast<ClassTypeDeclAST*>(Stmt.get())) {
+ for (const auto &Method : Class->getMethods()) {
+ SaveAndRestore<int> MethodLocation(DiagnosticLine, Method->getLine());
+ FuncGen->emitPrototype(Method->getProto());
+ }
+ }
}
}
- for (const auto &[Class, Methods] : ClassMethods) {
- for (const auto &[Name, Method] : Methods) {
- SaveAndRestore<int> Location(DiagnosticLine, Method->getLine());
- FuncGen->emitPrototype(Method->getProto());
+ // Constants are literal exports. Own constants are still installed only
+ // when their declaration is encountered during body emission.
+ for (size_t I = 0; I < Sources.size(); ++I) {
+ selectUnit(I);
+ Symbols.clear();
+ for (const auto &Stmt : Sources[I].Statements) {
+ if (auto *Constant = dynamic_cast<ConstantStmtAST*>(Stmt.get())) {
+ SaveAndRestore<int> Location(DiagnosticLine, Stmt->getLine());
+ emitConstantStmt(Constant);
+ }
}
+ Exports[I] = Symbols;
}
-
- for (const auto &Stmt : Statements) {
- SaveAndRestore<int> Location(DiagnosticLine, Stmt->getLine());
- emitStmt(Stmt.get());
+ for (size_t I = 0; I < Sources.size(); ++I) {
+ selectUnit(I);
+ importSymbols();
+ for (const auto &Stmt : Sources[I].Statements) emitStmt(Stmt.get());
+ }
+ selectUnit(MainUnit);
+ if (!CodeUnit && !HadError) {
+ SaveAndRestore<int> Location(DiagnosticLine, Sources.size() > 1 ? 1 : 0);
+ reportError("no main program");
}
if (!Builder->GetInsertBlock()->hasTerminator()) {
diff --git a/lib/CodeGen/Designator.cc b/lib/CodeGen/Designator.cc
index 5f54a87..b31492b 100644
--- a/lib/CodeGen/Designator.cc
+++ b/lib/CodeGen/Designator.cc
@@ -11,10 +11,10 @@ using namespace cps;
// (TYPE NodePtr = ^Node before TYPE Node — required for linked structures);
// by-value record fields still require their record type to be complete,
// which pass B's source order enforces.
-void CodeGen::runTypePrePass(const std::vector<std::unique_ptr<StmtAST>> &Statements) {
+void CodeGen::runTypePrePass(const std::vector<std::unique_ptr<StmtAST>> &Statements, int Phase) {
std::string Err;
- for (const auto &S : Statements) {
+ if (Phase == 0) for (const auto &S : Statements) {
SaveAndRestore<int> Location(DiagnosticLine, S->getLine());
bool Ok = true;
if (auto *C = dynamic_cast<CTypeDeclAST*>(S.get()))
@@ -29,7 +29,7 @@ void CodeGen::runTypePrePass(const std::vector<std::unique_ptr<StmtAST>> &Statem
if (!Ok) reportError("%s", Err.c_str());
}
- for (const auto &S : Statements) {
+ if (Phase == 1) for (const auto &S : Statements) {
SaveAndRestore<int> Location(DiagnosticLine, S->getLine());
bool Ok = true;
if (auto *E = dynamic_cast<EnumTypeDeclAST*>(S.get())) {
@@ -42,7 +42,7 @@ void CodeGen::runTypePrePass(const std::vector<std::unique_ptr<StmtAST>> &Statem
if (!Ok) reportError("%s", Err.c_str());
}
- for (const auto &S : Statements) {
+ if (Phase == 2) for (const auto &S : Statements) {
auto *Class = dynamic_cast<ClassTypeDeclAST*>(S.get());
if (!Class) continue;
auto &Methods = ClassMethods[Class->getName()];
diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc
index b66b55a..cc88a78 100644
--- a/lib/CodeGen/FunctionGen.cc
+++ b/lib/CodeGen/FunctionGen.cc
@@ -106,7 +106,8 @@ void FunctionGen::createArgumentAllocas(Function *F, const std::vector<ParamDecl
const FuncSig *FunctionGen::getSignature(const std::string &Name) const {
auto It = Signatures.find(Name);
- return It == Signatures.end() ? nullptr : &It->second;
+ return It == Signatures.end() || (Visible && !Visible(It->second.OwnerUnit))
+ ? nullptr : &It->second;
}
Function *FunctionGen::emitPrototype(PrototypeAST *Proto) {
@@ -134,6 +135,7 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) {
}
FuncSig Sig;
+ Sig.OwnerUnit = CurrentUnit;
Sig.Name = Proto->getLinkName();
Sig.ReturnTypeName = Proto->getReturnType();
diff --git a/lib/CodeGen/StatementGen.cc b/lib/CodeGen/StatementGen.cc
index 529f7f0..5edb303 100644
--- a/lib/CodeGen/StatementGen.cc
+++ b/lib/CodeGen/StatementGen.cc
@@ -13,8 +13,9 @@ void CodeGen::emitStmt(StmtAST *Stmt) {
SaveAndRestore<int> Location(DiagnosticLine, Stmt && Stmt->getLine() ? Stmt->getLine() : DiagnosticLine);
if (!Stmt) return;
- if (dynamic_cast<IncludeStmtAST*>(Stmt) || dynamic_cast<GlobalBlockAST*>(Stmt)) {
- reportError("INCLUDE and GLOBAL are not supported yet");
+ if (dynamic_cast<IncludeStmtAST*>(Stmt)) return;
+ if (dynamic_cast<GlobalBlockAST*>(Stmt)) {
+ reportError("GLOBAL is not supported yet");
return;
}
diff --git a/lib/CodeGen/TypeSystem.cc b/lib/CodeGen/TypeSystem.cc
index 27814c2..34b8d2d 100644
--- a/lib/CodeGen/TypeSystem.cc
+++ b/lib/CodeGen/TypeSystem.cc
@@ -111,7 +111,7 @@ bool TypeSystem::sameType(const std::string &Left, const std::string &Right) con
const TypeInfo *TypeSystem::resolve(const std::string &TypeName) const {
auto It = Types.find(TypeName);
- if (It == Types.end()) {
+ if (It == Types.end() || (Visible && !Visible(TypeName))) {
return nullptr;
}
return &It->second;
@@ -271,7 +271,8 @@ bool TypeSystem::definePointer(const std::string &Name,
const EnumConstant *TypeSystem::lookupEnumConstant(const std::string &Name) const {
auto It = EnumConstants.find(Name);
- return It == EnumConstants.end() ? nullptr : &It->second;
+ return It == EnumConstants.end() || (Visible && !Visible(It->second.TypeName))
+ ? nullptr : &It->second;
}
Type *TypeSystem::getLLVMType(const std::string &TypeName) const {
diff --git a/tools/driver/main.cc b/tools/driver/main.cc
index a81fb30..0b3033f 100644
--- a/tools/driver/main.cc
+++ b/tools/driver/main.cc
@@ -1,4 +1,5 @@
#include "cps/Lexer.h"
+#include "cps/Diagnostics.h"
#include "cps/Parser.h"
#include "cps/CodeGen.h"
#include "NativeCompiler.h"
@@ -85,7 +86,7 @@ int main(int argc, char **argv) {
}
}
- std::string InputPath;
+ std::vector<std::string> InputPaths;
std::string OutputPath;
bool Run = false;
bool EmitIR = false;
@@ -108,15 +109,12 @@ int main(int argc, char **argv) {
} else if (Arg.empty() || Arg[0] == '-') {
llvm::errs() << "cpsc: error: unknown argument '" << Arg << "'\n";
return compilationTerminated();
- } else if (!InputPath.empty()) {
- llvm::errs() << "cpsc: error: only one source file is supported\n";
- return compilationTerminated();
} else {
- InputPath = Arg;
+ InputPaths.push_back(Arg);
}
}
- if (InputPath.empty()) {
+ if (InputPaths.empty()) {
llvm::errs() << "cpsc: fatal error: no input files\n";
return compilationTerminated();
}
@@ -138,37 +136,80 @@ int main(int argc, char **argv) {
#endif
}
const bool Save = !Run || EmitIR || !OutputPath.empty();
- if (OutputPath.empty() && Save) {
- llvm::SmallString<256> DefaultPath(InputPath);
- llvm::sys::path::replace_extension(DefaultPath, EmitIR ? ".ll" : ".out");
- OutputPath = std::string(DefaultPath);
- }
-
- std::ifstream Input(InputPath, std::ios::binary);
- if (!Input) {
- llvm::errs() << "cpsc: fatal error: cannot open source file '" << InputPath << "'\n";
- return compilationTerminated();
- }
- if (Save && llvm::sys::fs::equivalent(InputPath, OutputPath)) {
- llvm::errs() << "cpsc: error: output file must not be the source file\n";
- return compilationTerminated();
+ std::vector<std::vector<std::unique_ptr<cps::StmtAST>>> Programs(InputPaths.size());
+ std::vector<cps::SourceUnit> Units;
+ for (size_t I = 0; I < InputPaths.size(); ++I) {
+ const auto &InputPath = InputPaths[I];
+ for (size_t J = 0; J < I; ++J) {
+ if (llvm::sys::fs::equivalent(InputPath, InputPaths[J])) {
+ llvm::errs() << "cpsc: error: duplicate input file '" << InputPath << "'\n";
+ return compilationTerminated();
+ }
+ }
+ std::ifstream Input(InputPath, std::ios::binary);
+ if (!Input) {
+ llvm::errs() << "cpsc: fatal error: cannot open source file '" << InputPath << "'\n";
+ return compilationTerminated();
+ }
+ cps::DiagnosticSource = InputPaths.size() > 1 ? InputPath : "";
+ cps::Lexer Lex(Input);
+ cps::Parser Parser(Lex);
+ Programs[I] = Parser.Parse();
+ if (Input.bad()) {
+ llvm::errs() << "cpsc: fatal error: cannot read source file '" << InputPath << "'\n";
+ return compilationTerminated();
+ }
+ if (Lex.hadError() || Parser.hadError()) return compilationTerminated();
+ Units.push_back({InputPath, Programs[I], std::vector<bool>(InputPaths.size(), false)});
+ Units.back().Visible[I] = true;
}
-
- cps::Lexer Lex(Input);
-
- cps::Parser Parser(Lex);
- auto Statements = Parser.Parse();
- if (Input.bad()) {
- llvm::errs() << "cpsc: fatal error: cannot read source file '" << InputPath << "'\n";
- return compilationTerminated();
+ for (size_t I = 0; I < Units.size(); ++I) {
+ cps::DiagnosticSource = Units.size() > 1 ? Units[I].Path : "";
+ for (const auto &Stmt : Units[I].Statements) {
+ auto *Include = dynamic_cast<cps::IncludeStmtAST*>(Stmt.get());
+ if (!Include) continue;
+ llvm::SmallString<256> Path(Include->getPath());
+ if (!llvm::sys::path::is_absolute(Path)) {
+ Path = llvm::sys::path::parent_path(Units[I].Path);
+ llvm::sys::path::append(Path, Include->getPath());
+ }
+ bool Found = false;
+ for (size_t J = 0; J < Units.size(); ++J) {
+ if (llvm::sys::fs::equivalent(Path, Units[J].Path)) {
+ Units[I].Visible[J] = true;
+ Found = true;
+ }
+ }
+ if (!Found) {
+ cps::sourceDiagnostic(Stmt->getLine(),
+ "error: cannot INCLUDE '%s': not given on the command line at line %d\n",
+ Include->getPath().c_str(), Stmt->getLine());
+ return compilationTerminated();
+ }
+ }
}
- if (Lex.hadError() || Parser.hadError()) return compilationTerminated();
+ for (size_t K = 0; K < Units.size(); ++K)
+ for (size_t I = 0; I < Units.size(); ++I)
+ for (size_t J = 0; J < Units.size(); ++J)
+ Units[I].Visible[J] = Units[I].Visible[J] ||
+ (Units[I].Visible[K] && Units[K].Visible[J]);
cps::CodeGen CG;
- CG.compile(Statements);
-
+ CG.compile(Units);
if (CG.hadError()) return compilationTerminated();
+ if (OutputPath.empty() && Save) {
+ llvm::SmallString<256> DefaultPath(InputPaths[CG.getMainUnit()]);
+ llvm::sys::path::replace_extension(DefaultPath, EmitIR ? ".ll" : ".out");
+ OutputPath = std::string(DefaultPath);
+ }
+ for (const auto &InputPath : InputPaths) {
+ if (Save && llvm::sys::fs::equivalent(InputPath, OutputPath)) {
+ llvm::errs() << "cpsc: error: output file must not be the source file\n";
+ return compilationTerminated();
+ }
+ }
+
if (Save && !EmitIR) {
if (!compileNative(CG, OutputPath, argv[0])) return compilationTerminated();
if (!Run) return 0;