Skip to content

Commit 583de45

Browse files
Merge pull request google#1842 from xlsynth:cdleary/2024-12-15-use-typecheck-interp
PiperOrigin-RevId: 716833766
2 parents 2b5e137 + b34f3ca commit 583de45

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+997
-103
lines changed

xls/dslx/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ cc_library(
3939
hdrs = ["virtualizable_file_system.h"],
4040
deps = [
4141
"//xls/common/file:filesystem",
42+
"@com_google_absl//absl/container:flat_hash_map",
43+
"@com_google_absl//absl/log",
4244
"@com_google_absl//absl/status",
4345
"@com_google_absl//absl/status:statusor",
4446
"@com_google_absl//absl/strings:str_format",

xls/dslx/bytecode/bytecode.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ class Bytecode {
5151
kAnd,
5252
// Invokes the function given in the Bytecode's data argument. Arguments are
5353
// given on the stack with deeper elements being earlier in the arg list
54-
// (rightmost arg is TOS0 because we evaluate args left-to-right).
54+
// (rightmost arg is TOS1 because we evaluate args left-to-right, TOS0 is
55+
// the callee).
5556
kCall,
5657
// Casts the element on top of the stack to the type given in the optional
5758
// arg.

xls/dslx/bytecode/bytecode_emitter.cc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,49 @@ absl::Status BytecodeEmitter::HandleNameRef(const NameRef* node) {
13291329
return absl::OkStatus();
13301330
}
13311331

1332+
absl::StatusOr<InterpValue> BytecodeEmitter::HandleExternRef(
1333+
const NameRef& name_ref, const NameDef& name_def,
1334+
UseTreeEntry& use_tree_entry) {
1335+
XLS_ASSIGN_OR_RETURN(const ImportedInfo* imported_info,
1336+
type_info_->GetImportedOrError(&use_tree_entry));
1337+
Module* referenced_module = imported_info->module;
1338+
std::optional<ModuleMember*> member =
1339+
referenced_module->FindMemberWithName(name_def.identifier());
1340+
XLS_RET_CHECK(member.has_value());
1341+
1342+
// Note: we currently do not support re-exporting a `use` binding, when we do,
1343+
// this will need to potentially walk transitively to the definition.
1344+
std::optional<InterpValue> value = absl::visit(
1345+
Visitor{[&](Function* f) -> std::optional<InterpValue> {
1346+
// Type checking should have validated we refer to public
1347+
// members.
1348+
CHECK(f->is_public()) << f->ToString();
1349+
return InterpValue::MakeFunction(
1350+
InterpValue::UserFnData{f->owner(), f});
1351+
},
1352+
[&](ConstantDef* cd) -> std::optional<InterpValue> {
1353+
// Type checking should have validated we refer to public
1354+
// members.
1355+
CHECK(cd->is_public()) << cd->ToString();
1356+
std::optional<InterpValue> value =
1357+
imported_info->type_info->GetConstExprOption(cd->value());
1358+
// ConstantDef should always have a ConstExpr value associated
1359+
// with it.
1360+
CHECK(value.has_value());
1361+
return value;
1362+
},
1363+
[&](auto) -> std::optional<InterpValue> { return std::nullopt; }},
1364+
*member.value());
1365+
if (value.has_value()) {
1366+
return value.value();
1367+
}
1368+
return absl::InternalError(absl::StrFormat(
1369+
"Unhandled external reference to `%s` kind `%s` via `%s` @ %s",
1370+
name_def.identifier(), GetModuleMemberTypeName(*member.value()),
1371+
use_tree_entry.parent()->ToString(),
1372+
name_def.span().ToString(file_table())));
1373+
}
1374+
13321375
absl::StatusOr<std::variant<InterpValue, Bytecode::SlotIndex>>
13331376
BytecodeEmitter::HandleNameRefInternal(const NameRef* node) {
13341377
AnyNameDef any_name_def = node->name_def();
@@ -1350,6 +1393,11 @@ BytecodeEmitter::HandleNameRefInternal(const NameRef* node) {
13501393
CHECK(name_def != nullptr);
13511394
AstNode* definer = name_def->definer();
13521395

1396+
if (auto* use_tree_entry = dynamic_cast<UseTreeEntry*>(definer);
1397+
use_tree_entry != nullptr) {
1398+
return HandleExternRef(*node, *name_def, *use_tree_entry);
1399+
}
1400+
13531401
// Emit function and constant refs directly so that they can be
13541402
// stack elements without having to load slots with them.
13551403
if (auto* f = dynamic_cast<Function*>(definer); f != nullptr) {

xls/dslx/bytecode/bytecode_emitter.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,14 @@ class BytecodeEmitter : public ExprVisitor {
122122
absl::Status HandleLet(const Let* node) override;
123123
absl::Status HandleMatch(const Match* node) override;
124124
absl::Status HandleNameRef(const NameRef* node) override;
125+
125126
absl::StatusOr<std::variant<InterpValue, Bytecode::SlotIndex>>
126127
HandleNameRefInternal(const NameRef* node);
128+
129+
absl::StatusOr<InterpValue> HandleExternRef(const NameRef& name_ref,
130+
const NameDef& name_def,
131+
UseTreeEntry& use_tree_entry);
132+
127133
absl::Status HandleNumber(const Number* node) override;
128134
struct FormattedInterpValue {
129135
InterpValue value;

xls/dslx/constexpr_evaluator.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,24 @@ absl::Status ConstexprEvaluator::HandleMatch(const Match* expr) {
442442
return InterpretExpr(expr);
443443
}
444444

445+
absl::Status ConstexprEvaluator::HandleExternRef(const NameRef* name_ref,
446+
const NameDef* name_def,
447+
UseTreeEntry* use_tree_entry) {
448+
const FileTable& file_table = *name_ref->owner()->file_table();
449+
LOG(ERROR) << "HandleExternRef: " << name_ref->ToString() << " @ "
450+
<< name_ref->span().ToString(file_table);
451+
return absl::OkStatus();
452+
}
453+
445454
absl::Status ConstexprEvaluator::HandleNameRef(const NameRef* expr) {
455+
if (std::holds_alternative<const NameDef*>(expr->name_def())) {
456+
auto* name_def = std::get<const NameDef*>(expr->name_def());
457+
if (auto* use_tree_entry = dynamic_cast<UseTreeEntry*>(name_def->definer());
458+
use_tree_entry != nullptr) {
459+
return HandleExternRef(expr, name_def, use_tree_entry);
460+
}
461+
}
462+
446463
AstNode* name_def = ToAstNode(expr->name_def());
447464

448465
if (type_info_->IsKnownNonConstExpr(name_def) ||

xls/dslx/constexpr_evaluator.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ class ConstexprEvaluator : public xls::dslx::ExprVisitor {
102102
absl::Status HandleXlsTuple(const XlsTuple* expr) override;
103103

104104
private:
105+
absl::Status HandleExternRef(const NameRef* name_ref, const NameDef* name_def,
106+
UseTreeEntry* use_tree_entry);
107+
105108
ConstexprEvaluator(ImportData* import_data, TypeInfo* type_info,
106109
WarningCollector* warning_collector,
107110
ParametricEnv bindings, const Type* type)

xls/dslx/create_import_data.cc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,14 @@ ImportData CreateImportData(
3838
return import_data;
3939
}
4040

41-
ImportData CreateImportDataForTest() {
42-
ImportData import_data(xls::kDefaultDslxStdlibPath,
43-
/*additional_search_paths=*/{}, kDefaultWarningsSet,
44-
std::make_unique<RealFilesystem>());
41+
ImportData CreateImportDataForTest(
42+
std::unique_ptr<VirtualizableFilesystem> vfs) {
43+
if (vfs == nullptr) {
44+
vfs = std::make_unique<RealFilesystem>();
45+
}
46+
absl::Span<const std::filesystem::path> additional_search_paths = {};
47+
ImportData import_data(xls::kDefaultDslxStdlibPath, additional_search_paths,
48+
kDefaultWarningsSet, std::move(vfs));
4549
import_data.SetBytecodeCache(std::make_unique<BytecodeCache>(&import_data));
4650
return import_data;
4751
}

xls/dslx/create_import_data.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ ImportData CreateImportData(
3636

3737
// Creates an ImportData with reasonable defaults (standard path to the stdlib
3838
// and no additional search paths).
39-
ImportData CreateImportDataForTest();
39+
ImportData CreateImportDataForTest(
40+
std::unique_ptr<VirtualizableFilesystem> vfs = nullptr);
4041

4142
std::unique_ptr<ImportData> CreateImportDataPtrForTest();
4243

xls/dslx/frontend/ast.cc

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,27 @@ std::string UseInteriorEntry::ToString() const {
829829
return absl::StrCat(identifier_, "::", subtrees_str);
830830
}
831831

832+
void UseTreeEntry::LinearizeToSubjects(std::vector<std::string>& prefix,
833+
std::vector<UseSubject>& results) {
834+
return absl::visit(
835+
Visitor{
836+
[&](const UseInteriorEntry& interior) {
837+
prefix.push_back(std::string{interior.identifier()});
838+
for (UseTreeEntry* subtree : interior.subtrees()) {
839+
subtree->LinearizeToSubjects(prefix, results);
840+
}
841+
prefix.pop_back();
842+
},
843+
[&](NameDef* name_def) {
844+
CHECK(name_def != nullptr);
845+
UseSubject result(prefix, *name_def, *this);
846+
result.mutable_identifiers().push_back(name_def->identifier());
847+
results.push_back(std::move(result));
848+
},
849+
},
850+
payload_);
851+
}
852+
832853
std::string UseTreeEntry::ToString() const {
833854
return absl::visit(
834855
Visitor{
@@ -870,6 +891,16 @@ std::vector<std::string> UseTreeEntry::GetLeafIdentifiers() const {
870891
return result;
871892
}
872893

894+
UseSubject::UseSubject(std::vector<std::string> identifiers, NameDef& name_def,
895+
UseTreeEntry& use_tree_entry)
896+
: identifiers_(std::move(identifiers)),
897+
name_def_(&name_def),
898+
use_tree_entry_(&use_tree_entry) {}
899+
900+
std::string UseSubject::ToErrorString() const {
901+
return absl::StrCat("`", absl::StrJoin(identifiers_, "::"), "`");
902+
}
903+
873904
absl::Status UseTreeEntry::Accept(AstNodeVisitor* v) const {
874905
return v->HandleUseTreeEntry(this);
875906
}

xls/dslx/frontend/ast.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,41 @@ class UseInteriorEntry {
13601360
std::vector<UseTreeEntry*> subtrees_;
13611361
};
13621362

1363+
// Represents a record of a "leaf" imported by a `use` statement into the module
1364+
// scope -- note the `name_def`, this is the name definition that will be bound
1365+
// in the module scope.
1366+
//
1367+
// A `use` like `use foo::{bar, baz}` will result in two `UseSubject`s.
1368+
class UseSubject {
1369+
public:
1370+
UseSubject(std::vector<std::string> identifiers, NameDef& name_def,
1371+
UseTreeEntry& use_tree_entry);
1372+
1373+
absl::Span<std::string const> identifiers() const { return identifiers_; }
1374+
const NameDef& name_def() const { return *name_def_; }
1375+
const UseTreeEntry& use_tree_entry() const { return *use_tree_entry_; }
1376+
UseTreeEntry& use_tree_entry() { return *use_tree_entry_; }
1377+
1378+
std::vector<std::string>& mutable_identifiers() { return identifiers_; }
1379+
1380+
// Returns a string that represents the subject of the `use` statement in
1381+
// the form of a colon-ref; e.g. `foo::bar::baz`.
1382+
//
1383+
// Note that the returned value is surrounded in backticks.
1384+
std::string ToErrorString() const;
1385+
1386+
private:
1387+
// The identifier in the subject path; e.g. in `use foo::bar::baz` the
1388+
// identifiers are {`foo`, `bar`, `baz`}.
1389+
std::vector<std::string> identifiers_;
1390+
1391+
// The name definition that will be bound in the module scope.
1392+
const NameDef* name_def_;
1393+
1394+
// Use tree entry that wraps the `name_def`.
1395+
UseTreeEntry* use_tree_entry_;
1396+
};
1397+
13631398
// Arbitrary entry (interior or leaf) in the `use` construct tree.
13641399
class UseTreeEntry : public AstNode {
13651400
public:
@@ -1385,6 +1420,11 @@ class UseTreeEntry : public AstNode {
13851420
}
13861421
const Span& span() const { return span_; }
13871422

1423+
// Note: this is non-const because we capture a mutable AST node pointer in
1424+
// the results.
1425+
void LinearizeToSubjects(std::vector<std::string>& prefix,
1426+
std::vector<UseSubject>& results);
1427+
13881428
private:
13891429
std::variant<UseInteriorEntry, NameDef*> payload_;
13901430
Span span_;
@@ -1426,13 +1466,27 @@ class Use : public AstNode {
14261466

14271467
std::vector<AstNode*> GetChildren(bool want_types) const override;
14281468

1469+
// Returns all the identifiers of the `NameDef`s at the leaves of the tree.
14291470
std::vector<std::string> GetLeafIdentifiers() const {
14301471
return root_->GetLeafIdentifiers();
14311472
}
14321473
std::vector<NameDef*> GetLeafNameDefs() const {
14331474
return root_->GetLeafNameDefs();
14341475
}
14351476

1477+
// Returns a vector of `UseSubject`s that represent the "subjects" (i.e.
1478+
// individual imported entities that get a name binding in the module) of
1479+
// the `use` statement.
1480+
//
1481+
// Note: this is non-const because we capture a mutable AST node pointer in
1482+
// the results.
1483+
std::vector<UseSubject> LinearizeToSubjects() {
1484+
std::vector<std::string> prefix;
1485+
std::vector<UseSubject> results;
1486+
root_->LinearizeToSubjects(prefix, results);
1487+
return results;
1488+
}
1489+
14361490
UseTreeEntry& root() { return *root_; }
14371491
const UseTreeEntry& root() const { return *root_; }
14381492
const Span& span() const { return span_; }

0 commit comments

Comments
 (0)