Commit 88ffe246 authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

[torque] handle souce position information in a global context

Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng
Change-Id: I20e30f0c19c887b1e093b02e39c7bd3d53d15182
Reviewed-on: https://chromium-review.googlesource.com/1054073
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarDaniel Clifford <danno@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53221}
parent 0b6ad251
......@@ -3098,6 +3098,7 @@ if (current_toolchain == v8_snapshot_toolchain) {
"src/torque/ast-generator.cc",
"src/torque/ast-generator.h",
"src/torque/ast.h",
"src/torque/contextual.h",
"src/torque/declarable.cc",
"src/torque/declarable.h",
"src/torque/declaration-visitor.cc",
......
......@@ -747,7 +747,7 @@ antlrcpp::Any AstGenerator::visitDiagnosticStatement(
void AstGenerator::visitSourceFile(SourceFileContext* context) {
source_file_context_ = context;
current_source_file_ = ast_.AddSource(context->name);
current_source_file_ = SourceFileMap::Get().AddSource(context->name);
for (auto* declaration : context->file->children) {
ast_.declarations().push_back(declaration->accept(this).as<Declaration*>());
}
......
......@@ -11,6 +11,7 @@
#include <vector>
#include "src/base/optional.h"
#include "src/torque/contextual.h"
namespace v8 {
namespace internal {
......@@ -24,6 +25,8 @@ struct SourcePosition {
int column;
};
DECLARE_CONTEXTUAL_VARIABLE(CurrentSourcePosition, SourcePosition)
#define AST_EXPRESSION_NODE_KIND_LIST(V) \
V(CallExpression) \
V(LogicalOrExpression) \
......@@ -185,7 +188,7 @@ struct ExplicitModuleDeclaration : ModuleDeclaration {
std::string name;
};
class SourceFileMap {
class SourceFileMap : public ContextualClass<SourceFileMap> {
public:
SourceFileMap() {}
const std::string& GetSource(SourceId id) const {
......@@ -197,20 +200,22 @@ class SourceFileMap {
std::to_string(pos.column);
}
private:
friend class Ast;
SourceId AddSource(std::string path) {
sources_.push_back(std::move(path));
return static_cast<SourceId>(sources_.size() - 1);
}
private:
std::vector<std::string> sources_;
};
inline std::string PositionAsString(SourcePosition pos) {
return SourceFileMap::Get().PositionAsString(pos);
}
class Ast {
public:
Ast()
: default_module_{SourcePosition(), {}},
source_file_map_(new SourceFileMap()) {}
Ast() : default_module_{SourcePosition(), {}} {}
std::vector<Declaration*>& declarations() {
return default_module_.declarations;
......@@ -221,15 +226,10 @@ class Ast {
void AddNode(std::unique_ptr<AstNode> node) {
nodes_.emplace_back(std::move(node));
}
SourceId AddSource(std::string path) {
return source_file_map_->AddSource(path);
}
DefaultModuleDeclaration* default_module() { return &default_module_; }
SourceFileMap* source_file_map() { return &*source_file_map_; }
private:
DefaultModuleDeclaration default_module_;
std::unique_ptr<SourceFileMap> source_file_map_;
std::vector<std::unique_ptr<AstNode>> nodes_;
};
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_TORQUE_CONTEXTUAL_H_
#define V8_TORQUE_CONTEXTUAL_H_
#include <type_traits>
namespace v8 {
namespace internal {
namespace torque {
// Contextual variables store a value in one or more stack-allocated scopes and
// allow global access to the most recent active scope on the current
// call-stack.
template <class Derived, class VarType>
class ContextualVariable {
public:
// A {Scope} contains a new object of type {T} and gives
// ContextualVariable::Get() access to it. Upon destruction, the contextual
// variable is restored to the state before the {Scope} was created. Scopes
// have to follow a stack discipline: A {Scope} has to be destructed before
// any older scope is destructed.
class Scope {
public:
explicit Scope(VarType x = VarType())
: current_(std::move(x)), previous_(top_) {
top_ = &current_;
}
~Scope() {
// Ensure stack discipline.
DCHECK_EQ(&current_, top_);
top_ = previous_;
}
private:
VarType current_;
VarType* previous_;
static_assert(std::is_base_of<ContextualVariable, Derived>::value,
"Curiously Recurring Template Pattern");
};
// Access the most recent active {Scope}. There has to be an active {Scope}
// for this contextual variable.
static VarType& Get() {
DCHECK_NOT_NULL(top_);
return *top_;
}
private:
static thread_local VarType* top_;
};
template <class Derived, class VarType>
thread_local VarType* ContextualVariable<Derived, VarType>::top_ = nullptr;
// Usage: DECLARE_CONTEXTUAL_VARIABLE(VarName, VarType)
#define DECLARE_CONTEXTUAL_VARIABLE(VarName, ...) \
struct VarName \
: v8::internal::torque::ContextualVariable<VarName, __VA_ARGS__> {};
// By inheriting from {ContextualClass} a class can become a contextual variable
// of itself.
template <class T>
using ContextualClass = ContextualVariable<T, T>;
} // namespace torque
} // namespace internal
} // namespace v8
#endif // V8_TORQUE_CONTEXTUAL_H_
This diff is collapsed.
......@@ -74,14 +74,13 @@ class DeclarationVisitor : public FileVisitor {
std::string generates =
decl->generates ? *decl->generates : std::string("");
declarations()->DeclareAbstractType(decl->pos, decl->name, generates,
extends_ptr);
declarations()->DeclareAbstractType(decl->name, generates, extends_ptr);
if (decl->constexpr_generates) {
std::string constexpr_name =
std::string(CONSTEXPR_TYPE_PREFIX) + decl->name;
declarations()->DeclareAbstractType(
decl->pos, constexpr_name, *decl->constexpr_generates, &(decl->name));
constexpr_name, *decl->constexpr_generates, &(decl->name));
}
}
......@@ -118,14 +117,11 @@ class DeclarationVisitor : public FileVisitor {
void Visit(VarDeclarationStatement* stmt) {
std::string variable_name = stmt->name;
const Type* type = declarations()->GetType(stmt->pos, stmt->type);
const Type* type = declarations()->GetType(stmt->type);
if (type->IsConstexpr()) {
std::stringstream stream;
stream << "cannot declare variable with constexpr type at "
<< PositionAsString(stmt->pos);
ReportError(stream.str());
ReportError("cannot declare variable with constexpr type");
}
declarations()->DeclareVariable(stmt->pos, variable_name, type);
declarations()->DeclareVariable(variable_name, type);
if (global_context_.verbose()) {
std::cout << "declared variable " << variable_name << " with type "
<< type << "\n";
......@@ -134,21 +130,20 @@ class DeclarationVisitor : public FileVisitor {
Visit(*stmt->initializer);
if (global_context_.verbose()) {
std::cout << "variable has initialization expression at "
<< PositionAsString(stmt->pos) << "\n";
<< CurrentPositionAsString() << "\n";
}
}
}
void Visit(ConstDeclaration* decl) {
declarations()->DeclareConstant(
decl->pos, decl->name, declarations()->GetType(decl->pos, decl->type),
decl->literal);
decl->name, declarations()->GetType(decl->type), decl->literal);
}
void Visit(LogicalOrExpression* expr) {
{
Declarations::NodeScopeActivator scope(declarations(), expr->left);
declarations()->DeclareLabel(expr->pos, kFalseLabelName);
declarations()->DeclareLabel(kFalseLabelName);
Visit(expr->left);
}
Visit(expr->right);
......@@ -157,7 +152,7 @@ class DeclarationVisitor : public FileVisitor {
void Visit(LogicalAndExpression* expr) {
{
Declarations::NodeScopeActivator scope(declarations(), expr->left);
declarations()->DeclareLabel(expr->pos, kTrueLabelName);
declarations()->DeclareLabel(kTrueLabelName);
Visit(expr->left);
}
Visit(expr->right);
......@@ -171,8 +166,8 @@ class DeclarationVisitor : public FileVisitor {
// visiting the conditional expression, those label-based
// macro conditionals will be able to find them through normal
// label lookups.
declarations()->DeclareLabel(node->pos, kTrueLabelName);
declarations()->DeclareLabel(node->pos, kFalseLabelName);
declarations()->DeclareLabel(kTrueLabelName);
declarations()->DeclareLabel(kFalseLabelName);
Visit(node);
}
......@@ -314,7 +309,7 @@ class DeclarationVisitor : public FileVisitor {
void MarkLocationModified(Expression* location) {
if (IdentifierExpression* id = IdentifierExpression::cast(location)) {
const Value* value = declarations()->LookupValue(id->pos, id->name);
const Value* value = declarations()->LookupValue(id->name);
if (value->IsVariable()) {
const Variable* variable = Variable::cast(value);
bool was_live = MarkVariableModified(variable);
......@@ -340,21 +335,21 @@ class DeclarationVisitor : public FileVisitor {
return was_live_in_preceeding_split;
}
void DeclareSignature(SourcePosition pos, const Signature& signature) {
void DeclareSignature(const Signature& signature) {
auto name_iterator = signature.parameter_names.begin();
for (auto t : signature.types()) {
const std::string& name(*name_iterator++);
declarations()->DeclareParameter(pos, name,
GetParameterVariableFromName(name), t);
declarations()->DeclareParameter(name, GetParameterVariableFromName(name),
t);
}
for (auto& label : signature.labels) {
auto label_params = label.types;
Label* new_label = declarations()->DeclareLabel(pos, label.name);
Label* new_label = declarations()->DeclareLabel(label.name);
size_t i = 0;
for (auto var_type : label_params) {
std::string var_name = label.name + std::to_string(i++);
new_label->AddVariable(
declarations()->DeclareVariable(pos, var_name, var_type));
declarations()->DeclareVariable(var_name, var_type));
}
}
}
......
This diff is collapsed.
......@@ -17,95 +17,80 @@ namespace torque {
class Declarations {
public:
explicit Declarations(SourceFileMap* source_file_map)
: source_file_map_(source_file_map),
unique_declaration_number_(0),
Declarations()
: unique_declaration_number_(0),
current_generic_specialization_(nullptr) {}
Declarable* Lookup(const std::string& name) { return chain_.Lookup(name); }
Declarable* TryLookup(const std::string& name) { return chain_.Lookup(name); }
Declarable* Lookup(SourcePosition pos, const std::string& name) {
Declarable* d = Lookup(name);
Declarable* Lookup(const std::string& name) {
Declarable* d = TryLookup(name);
if (d == nullptr) {
std::stringstream s;
s << "cannot find \"" << name << "\" at " << PositionAsString(pos);
s << "cannot find \"" << name << "\"";
ReportError(s.str());
}
return d;
}
Declarable* LookupGlobalScope(const std::string& name) {
return chain_.LookupGlobalScope(name);
}
Declarable* LookupGlobalScope(SourcePosition pos, const std::string& name) {
Declarable* d = chain_.LookupGlobalScope(name);
if (d == nullptr) {
std::stringstream s;
s << "cannot find \"" << name << "\" in global scope at "
<< PositionAsString(pos);
s << "cannot find \"" << name << "\" in global scope";
ReportError(s.str());
}
return d;
}
const Type* LookupType(SourcePosition pos, const std::string& name);
const Type* LookupType(const std::string& name);
const Type* LookupGlobalType(const std::string& name);
const Type* LookupGlobalType(SourcePosition pos, const std::string& name);
const Type* GetType(SourcePosition pos, TypeExpression* type_expression);
const Type* GetType(TypeExpression* type_expression);
const Type* GetFunctionPointerType(SourcePosition pos,
TypeVector argument_types,
const Type* GetFunctionPointerType(TypeVector argument_types,
const Type* return_type);
Builtin* FindSomeInternalBuiltinWithType(const FunctionPointerType* type);
Value* LookupValue(SourcePosition pos, const std::string& name);
Value* LookupValue(const std::string& name);
Macro* LookupMacro(SourcePosition pos, const std::string& name,
const TypeVector& types);
Macro* LookupMacro(const std::string& name, const TypeVector& types);
Builtin* LookupBuiltin(SourcePosition pos, const std::string& name);
Builtin* LookupBuiltin(const std::string& name);
Label* LookupLabel(SourcePosition pos, const std::string& name);
Label* LookupLabel(const std::string& name);
Generic* LookupGeneric(const SourcePosition& pos, const std::string& name);
Generic* LookupGeneric(const std::string& name);
const AbstractType* DeclareAbstractType(SourcePosition pos,
const std::string& name,
const AbstractType* DeclareAbstractType(const std::string& name,
const std::string& generated,
const std::string* parent = nullptr);
void DeclareTypeAlias(SourcePosition pos, const std::string& name,
const Type* aliased_type);
void DeclareTypeAlias(const std::string& name, const Type* aliased_type);
Label* DeclareLabel(SourcePosition pos, const std::string& name);
Label* DeclareLabel(const std::string& name);
Macro* DeclareMacro(SourcePosition pos, const std::string& name,
const Signature& signature);
Macro* DeclareMacro(const std::string& name, const Signature& signature);
Builtin* DeclareBuiltin(SourcePosition pos, const std::string& name,
Builtin::Kind kind, bool external,
const Signature& signature);
Builtin* DeclareBuiltin(const std::string& name, Builtin::Kind kind,
bool external, const Signature& signature);
RuntimeFunction* DeclareRuntimeFunction(SourcePosition pos,
const std::string& name,
RuntimeFunction* DeclareRuntimeFunction(const std::string& name,
const Signature& signature);
Variable* DeclareVariable(SourcePosition pos, const std::string& var,
const Type* type);
Variable* DeclareVariable(const std::string& var, const Type* type);
Parameter* DeclareParameter(SourcePosition pos, const std::string& name,
Parameter* DeclareParameter(const std::string& name,
const std::string& mangled_name,
const Type* type);
Label* DeclarePrivateLabel(SourcePosition pos, const std::string& name);
Label* DeclarePrivateLabel(const std::string& name);
void DeclareConstant(SourcePosition pos, const std::string& name,
const Type* type, const std::string& value);
void DeclareConstant(const std::string& name, const Type* type,
const std::string& value);
Generic* DeclareGeneric(SourcePosition pos, const std::string& name,
Module* module, GenericDeclaration* generic);
Generic* DeclareGeneric(const std::string& name, Module* module,
GenericDeclaration* generic);
TypeVector GetCurrentSpecializationTypeNamesVector();
......@@ -115,10 +100,6 @@ class Declarations {
return chain_.GetLiveVariables();
}
std::string PositionAsString(SourcePosition pos) {
return source_file_map_->PositionAsString(pos);
}
Statement* next_body() const { return next_body_; }
void PrintScopeChain() { chain_.Print(); }
......@@ -144,10 +125,8 @@ class Declarations {
int GetNextUniqueDeclarationNumber() { return unique_declaration_number_++; }
void CheckAlreadyDeclared(SourcePosition pos, const std::string& name,
const char* new_type);
void CheckAlreadyDeclared(const std::string& name, const char* new_type);
SourceFileMap* source_file_map_;
int unique_declaration_number_;
ScopeChain chain_;
const SpecializationKey* current_generic_specialization_;
......
......@@ -15,13 +15,13 @@ Signature FileVisitor::MakeSignature(CallableNode* decl,
Declarations::NodeScopeActivator scope(declarations(), decl);
LabelDeclarationVector definition_vector;
for (auto label : signature->labels) {
LabelDeclaration def = {label.name, GetTypeVector(decl->pos, label.types)};
LabelDeclaration def = {label.name, GetTypeVector(label.types)};
definition_vector.push_back(def);
}
Signature result{signature->parameters.names,
{GetTypeVector(decl->pos, signature->parameters.types),
{GetTypeVector(signature->parameters.types),
signature->parameters.has_varargs},
declarations()->GetType(decl->pos, signature->return_type),
declarations()->GetType(signature->return_type),
definition_vector};
return result;
}
......@@ -36,10 +36,10 @@ std::string FileVisitor::GetGeneratedCallableName(
return result;
}
Callable* FileVisitor::LookupCall(SourcePosition pos, const std::string& name,
Callable* FileVisitor::LookupCall(const std::string& name,
const TypeVector& parameter_types) {
Callable* result = nullptr;
Declarable* declarable = declarations()->Lookup(pos, name);
Declarable* declarable = declarations()->Lookup(name);
if (declarable->IsBuiltin()) {
result = Builtin::cast(declarable);
} else if (declarable->IsRuntimeFunction()) {
......@@ -52,8 +52,7 @@ Callable* FileVisitor::LookupCall(SourcePosition pos, const std::string& name,
std::stringstream stream;
stream << "multiple matching matching parameter list for macro "
<< name << ": (" << parameter_types << ") and ("
<< result->signature().parameter_types << ") at "
<< PositionAsString(pos);
<< result->signature().parameter_types << ")";
ReportError(stream.str());
}
result = m;
......@@ -62,16 +61,14 @@ Callable* FileVisitor::LookupCall(SourcePosition pos, const std::string& name,
if (result == nullptr) {
std::stringstream stream;
stream << "no matching matching parameter list for macro " << name
<< ": call parameters were (" << parameter_types << ") at "
<< PositionAsString(pos);
<< ": call parameters were (" << parameter_types << ")";
ReportError(stream.str());
}
} else {
std::stringstream stream;
stream << "can't call " << declarable->type_name() << " " << name
<< " because it's not callable"
<< ": call parameters were (" << parameter_types << ") at "
<< PositionAsString(pos);
<< ": call parameters were (" << parameter_types << ")";
ReportError(stream.str());
}
......
......@@ -26,11 +26,10 @@ class FileVisitor {
declarations_(global_context.declarations()),
module_(global_context.GetDefaultModule()) {}
TypeVector GetTypeVector(SourcePosition pos,
const std::vector<TypeExpression*>& v) {
TypeVector GetTypeVector(const std::vector<TypeExpression*>& v) {
TypeVector result;
for (TypeExpression* t : v) {
result.push_back(declarations()->GetType(pos, t));
result.push_back(declarations()->GetType(t));
}
return result;
}
......@@ -70,11 +69,7 @@ class FileVisitor {
return std::string("p_") + name;
}
std::string PositionAsString(SourcePosition pos) {
return global_context_.ast()->source_file_map()->PositionAsString(pos);
}
Callable* LookupCall(SourcePosition pos, const std::string& name,
Callable* LookupCall(const std::string& name,
const TypeVector& parameter_types);
Signature MakeSignature(CallableNode* decl,
......
......@@ -56,7 +56,6 @@ class GlobalContext {
explicit GlobalContext(Ast ast)
: verbose_(false),
next_label_number_(0),
declarations_(ast.source_file_map()),
type_oracle_(&declarations_),
default_module_(GetModule("base")),
ast_(std::move(ast)) {}
......@@ -115,10 +114,6 @@ class GlobalContext {
std::map<std::string, std::vector<OperationHandler>> op_handlers_;
std::string PositionAsString(SourcePosition pos) {
return declarations()->PositionAsString(pos);
}
Declarations* declarations() { return &declarations_; }
Ast* ast() { return &ast_; }
......
This diff is collapsed.
......@@ -39,8 +39,7 @@ class ImplementationVisitor : public FileVisitor {
LocationReference GetLocationReference(LocationExpression* location);
LocationReference GetLocationReference(IdentifierExpression* expr) {
return LocationReference(declarations()->LookupValue(expr->pos, expr->name),
{}, {});
return LocationReference(declarations()->LookupValue(expr->name), {}, {});
}
LocationReference GetLocationReference(FieldAccessExpression* expr) {
return LocationReference({}, Visit(expr->object), {});
......@@ -56,8 +55,7 @@ class ImplementationVisitor : public FileVisitor {
Value* value = reference.value;
if (value->IsVariable() && !Variable::cast(value)->IsDefined()) {
std::stringstream s;
s << "\"" << value->name() << "\" is used before it is defined at "
<< PositionAsString(expr->pos);
s << "\"" << value->name() << "\" is used before it is defined";
ReportError(s.str());
}
return VisitResult({value->type(), value->GetValueForRead()});
......@@ -66,22 +64,21 @@ class ImplementationVisitor : public FileVisitor {
LocationReference reference) {
Arguments arguments;
arguments.parameters = {reference.base};
return GenerateOperation(expr->pos, std::string(".") + expr->field,
arguments);
return GenerateOperation(std::string(".") + expr->field, arguments);
}
VisitResult GenerateFetchFromLocation(ElementAccessExpression* expr,
LocationReference reference) {
Arguments arguments;
arguments.parameters = {reference.base, reference.index};
return GenerateOperation(expr->pos, "[]", arguments);
return GenerateOperation("[]", arguments);
}
VisitResult GetBuiltinCode(SourcePosition pos, Builtin* builtin);
VisitResult GetBuiltinCode(Builtin* builtin);
VisitResult Visit(IdentifierExpression* expr) {
if (Builtin* builtin =
Builtin::DynamicCast(declarations()->Lookup(expr->name))) {
return GetBuiltinCode(expr->pos, builtin);
return GetBuiltinCode(builtin);
}
return GenerateFetchFromLocation(expr, GetLocationReference(expr));
}
......@@ -183,13 +180,11 @@ class ImplementationVisitor : public FileVisitor {
void GenerateChangedVarsFromControlSplit(AstNode* node);
const Type* GetCommonType(SourcePosition pos, const Type* left,
const Type* right);
const Type* GetCommonType(const Type* left, const Type* right);
VisitResult GenerateCopy(const VisitResult& to_copy);
void GenerateAssignToVariable(SourcePosition pos, Variable* var,
VisitResult value);
void GenerateAssignToVariable(Variable* var, VisitResult value);
void GenerateAssignToLocation(LocationExpression* location,
const LocationReference& reference,
......@@ -200,14 +195,13 @@ class ImplementationVisitor : public FileVisitor {
const base::Optional<const Type*>& type,
const base::Optional<VisitResult>& initialization = {});
void GenerateParameter(SourcePosition pos, const std::string& parameter_name);
void GenerateParameter(const std::string& parameter_name);
void GenerateParameterList(SourcePosition pos, const NameVector& list,
size_t first = 0);
void GenerateParameterList(const NameVector& list, size_t first = 0);
VisitResult GenerateCall(SourcePosition pos, const std::string& callable_name,
VisitResult GenerateCall(const std::string& callable_name,
const Arguments& parameters, bool tail_call);
VisitResult GeneratePointerCall(SourcePosition pos, Expression* callee,
VisitResult GeneratePointerCall(Expression* callee,
const Arguments& parameters, bool tail_call);
bool GenerateLabeledStatementBlocks(
......@@ -222,17 +216,15 @@ class ImplementationVisitor : public FileVisitor {
const std::vector<Statement*>& statement_blocks,
Label* merge_label);
void GenerateMacroFunctionDeclaration(std::ostream& o, SourcePosition pos,
void GenerateMacroFunctionDeclaration(std::ostream& o,
const std::string& macro_prefix,
Macro* macro);
VisitResult GenerateOperation(SourcePosition pos,
const std::string& operation,
VisitResult GenerateOperation(const std::string& operation,
Arguments arguments,
base::Optional<const Type*> return_type = {});
VisitResult GenerateImplicitConvert(SourcePosition pos,
const Type* destination_type,
VisitResult GenerateImplicitConvert(const Type* destination_type,
VisitResult source);
void Specialize(const SpecializationKey& key, CallableNode* callable,
......@@ -253,7 +245,7 @@ class ImplementationVisitor : public FileVisitor {
void GenerateLabelGoto(Label* label);
std::vector<Label*> LabelsFromIdentifiers(
SourcePosition pos, const std::vector<std::string>& names);
const std::vector<std::string>& names);
std::ostream& source_out() { return module_->source_stream(); }
......
......@@ -46,6 +46,7 @@ int WrappedMain(int argc, const char** argv) {
size_t lexer_errors = 0;
auto error_strategy = std::make_shared<FailedParseErrorStrategy>();
bool verbose = false;
SourceFileMap::Scope scope;
for (int i = 1; i < argc; ++i) {
// Check for options
if (!strcmp("-o", argv[i])) {
......
......@@ -7,14 +7,19 @@
#include <iostream>
#include <string>
#include "src/torque/ast.h"
#include "src/torque/utils.h"
namespace v8 {
namespace internal {
namespace torque {
std::string CurrentPositionAsString() {
return PositionAsString(CurrentSourcePosition::Get());
}
void ReportError(const std::string& error) {
std::cerr << error << std::endl;
std::cerr << CurrentPositionAsString() << ": Torque error: " << error << "\n";
throw(-1);
}
......
......@@ -25,6 +25,8 @@ std::string DashifyString(const std::string& underscore_string);
void ReplaceFileContentsIfDifferent(const std::string& file_path,
const std::string& contents);
std::string CurrentPositionAsString();
template <class T>
class Deduplicator {
public:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment