Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,26 @@ git_override(
remote = "https://github.com/The-OpenROAD-Project/qt_bazel_prebuilts",
)

archive_override(
module_name = "sv-lang",
integrity = "sha256-Pj8AVITB12EZCg7lD3wYZpuHgcexydYPQsOUh77CWqg=",
remote_file_integrity = {
"BUILD.bazel": "sha256-aTxrPJ+ycNG86YervJHcs0/jW9qXzQAIOdBeS/oZOOU=",
"MODULE.bazel": "sha256-Sf7p8HXHyD+Dp5fOXMY7jB4PqXDKgYS9XxfEa8VTRjI=",
"private/slang/export_headers/BUILD.bazel": "sha256-R6Dv/CqeG3BKpy2/F7RraVg/6QpVp9R2+itU1P0jUDE=",
"private/slang/version_info/BUILD.bazel": "sha256-DgIsIglnPC4vIqxSFt2orPq/I7SvAo3oiRrLFcHaS+Q=",
"private/slang/version_info/VersionInfo.cpp.in": "sha256-lEtZRUGNz46yF8M8sBgjL1N4Zr+KXioy8sv/0DmCSfE=",
},
remote_file_urls = {
"BUILD.bazel": ["https://bcr.bazel.build/modules/sv-lang/10.0.1-20260316-f04e8156/overlay/BUILD.bazel"],
"MODULE.bazel": ["https://bcr.bazel.build/modules/sv-lang/10.0.1-20260316-f04e8156/overlay/MODULE.bazel"],
"private/slang/export_headers/BUILD.bazel": ["https://bcr.bazel.build/modules/sv-lang/10.0.1-20260316-f04e8156/overlay/private/slang/export_headers/BUILD.bazel"],
"private/slang/version_info/BUILD.bazel": ["https://bcr.bazel.build/modules/sv-lang/10.0.1-20260316-f04e8156/overlay/private/slang/version_info/BUILD.bazel"],
"private/slang/version_info/VersionInfo.cpp.in": ["https://bcr.bazel.build/modules/sv-lang/10.0.1-20260316-f04e8156/overlay/private/slang/version_info/VersionInfo.cpp.in"],
},
urls = ["https://github.com/MikePopoloski/slang/archive/f04e81565793c768b747a8fd058f3e7aeceee1b5.tar.gz"],
)

# TODO: mikex - Remove this override once fixed upstream in https://registry.bazel.build/modules/sv-lang.
# rules_pycross is a transitive dependency of OpenROAD (via sv-lang).
# We override rules_pycross to 0.8.3 here to incorporate the fix from
Expand Down
2 changes: 0 additions & 2 deletions MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions xls/dslx/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ cc_library(
"//xls/dslx/type_system_v2:inference_table",
"//xls/dslx/type_system_v2:inference_table_converter",
"//xls/dslx/type_system_v2:trait_deriver",
"@abseil-cpp//absl/base",
"@abseil-cpp//absl/container:flat_hash_map",
"@abseil-cpp//absl/container:flat_hash_set",
"@abseil-cpp//absl/log",
Expand All @@ -474,6 +475,7 @@ cc_library(
"@abseil-cpp//absl/status:statusor",
"@abseil-cpp//absl/strings",
"@abseil-cpp//absl/strings:str_format",
"@abseil-cpp//absl/types:optional_ref",
"@abseil-cpp//absl/types:span",
],
)
Expand Down
8 changes: 2 additions & 6 deletions xls/dslx/frontend/ast_cloner_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2377,9 +2377,7 @@ fn divmod() {})*";
EXPECT_EQ(kProgram, clone->ToString());
}

// https://github.com/google/xls/issues/2876 - Re-enable when `use` is supported
// in TIv2.
TEST(AstClonerTest, DISABLED_Use) {
TEST(AstClonerTest, Use) {
constexpr std::string_view kProgram =
R"(#![feature(use_syntax)]

Expand Down Expand Up @@ -2486,9 +2484,7 @@ fn main() -> u32 {
EXPECT_EQ(import->name_def().definer(), import);
}

// https://github.com/google/xls/issues/2876 - Re-enable when `use` is supported
// in TIv2.
TEST(AstClonerTest, DISABLED_UseSetsNameDefDefinerOnClone) {
TEST(AstClonerTest, UseSetsNameDefDefinerOnClone) {
// Minimal use of a single module to keep the test crisp while exercising
// UseTreeEntry leaf definer behavior and typechecking via import.
constexpr std::string_view kImportedProgram = R"(pub const D = u32:0; )";
Expand Down
6 changes: 6 additions & 0 deletions xls/dslx/frontend/bindings.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,12 @@ class Bindings {
std::holds_alternative<SumDef*>(*bn);
}

// Returns whether 'name' is bound via a `use` statement.
bool ResolveNodeIsUseBound(std::string_view name) const {
std::optional<BoundNode> bn = ResolveNode(name);
return bn.has_value() && std::holds_alternative<UseTreeEntry*>(*bn);
}

// As above, but flags a ParseError() if the binding cannot be resolved,
// attributing the source of the binding resolution as span.
absl::StatusOr<BoundNode> ResolveNodeOrError(
Expand Down
25 changes: 22 additions & 3 deletions xls/dslx/frontend/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1880,9 +1880,10 @@ absl::StatusOr<Expr*> Parser::ParseCastOrEnumRefOrStructInstanceOrToken(
}
}
XLS_ASSIGN_OR_RETURN(bool peek_is_oparen, PeekTokenIs(TokenKind::kOParen));
if (peek_is_oparen) {
// Looks like a built-in function call with name overlapping with a type;
// probably `token()`.
// Looks like a built-in function call with name overlapping with a type;
// probably `token()`. Don't take this path if it would drop parametrics.
if (peek_is_oparen &&
(type_ref == nullptr || type_ref->parametrics().empty())) {
return ParseNameRef(bindings, &tok);
}
return ParseCast(bindings, type);
Expand Down Expand Up @@ -2786,6 +2787,24 @@ absl::StatusOr<Expr*> Parser::ParseTermLhs(Bindings& outer_bindings,
// my_struct as a type
XLS_ASSIGN_OR_RETURN(lhs, ParseCastOrEnumRefOrStructInstanceOrToken(
outer_bindings, restrictions));
} else if (peek->kind() == TokenKind::kIdentifier &&
outer_bindings.ResolveNodeIsUseBound(*peek->GetValue())) {
// A `use`-bound name may be a type or a value, which is not known until
// the import resolves. Try parsing as type and fall back to parsing as
// value.
VLOG(5) << "ParseTerm, kind is identifier bound by `use`";
Transaction use_type_txn(this, &outer_bindings);
absl::StatusOr<Expr*> as_type = ParseCastOrEnumRefOrStructInstanceOrToken(
*use_type_txn.bindings(), restrictions);
if (as_type.ok()) {
use_type_txn.Commit();
lhs = *as_type;
} else {
use_type_txn.Rollback();
XLS_ASSIGN_OR_RETURN(auto name_or_colon_ref,
ParseNameOrColonRef(outer_bindings));
lhs = ToExprNode(name_or_colon_ref);
}
} else if (peek->kind() == TokenKind::kIdentifier || peek_is_kw_in ||
peek_is_kw_out || peek_is_kw_self) {
VLOG(5) << "ParseTerm, kind is identifier but not a known type";
Expand Down
38 changes: 38 additions & 0 deletions xls/dslx/frontend/parser_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2421,6 +2421,15 @@ fn main() -> u32 {
})");
}

TEST_F(ParserTest, UseFunctionInvokedWithExplicitParametrics) {
RoundTrip(R"(#![feature(use_syntax)]

use foo::{bar, N};
fn main() -> u32 {
bar<N>(1)
})");
}

TEST_F(ParserTest, UseWithNestedLevels) {
RoundTrip(R"(#![feature(use_syntax)]

Expand Down Expand Up @@ -4750,6 +4759,35 @@ const x)";
"shadowing an existing definition from <no-file>:1:1-1:10"));
}

TEST_F(ParserTest, ConstShadowsUse) {
constexpr std::string_view kProgram = R"(#![feature(use_syntax)]
use foo::C;
const C = u32:5;
)";
EXPECT_THAT(Parse(kProgram),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("Constant definition is shadowing an existing "
"definition")));
}

TEST_F(ParserTest, MemberShadowsUse) {
EXPECT_THAT(Parse(R"(#![feature(use_syntax)]
use foo::S;
struct S {}
)"),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("already contains a member named `S`")));
}

TEST_F(ParserTest, UseShadowsMember) {
EXPECT_THAT(Parse(R"(#![feature(use_syntax)]
struct S {}
use foo::S;
)"),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("already contains a member named `S`")));
}

TEST_F(ParserTest, ConstParseErrorIncludesContext) {
constexpr std::string_view kProgram = R"(const x = 1)";
Scanner s{file_table_, Fileno(0), std::string(kProgram)};
Expand Down
60 changes: 60 additions & 0 deletions xls/dslx/import_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@
#include <utility>
#include <vector>

#include "absl/base/casts.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_join.h"
#include "absl/types/optional_ref.h"
#include "absl/types/span.h"
#include "xls/dslx/bytecode/bytecode_cache_interface.h"
#include "xls/dslx/frontend/ast.h"
Expand Down Expand Up @@ -142,6 +144,20 @@ class ImportTokens {
std::vector<std::string> pieces_;
};

// The result of resolving a `use` leaf (see `DoImportViaUse`).
struct UseImportResult {
// The `ModuleInfo`s that were imported as we traversed. Note that there can
// be more than one if there is a chain of `pub use` statements.
ModuleInfo* imported_module;

// If the `use` statement was referring to an entity inside of the enclosing
// module, it is given here.
//
// If this is nullptr, then it is implied that the module was the entity being
// referred to.
ModuleMember* imported_member;
};

// Wrapper around a `{subject: module_info}` mapping that modules can be
// imported into.
class ImportData {
Expand Down Expand Up @@ -185,6 +201,49 @@ class ImportData {
// preventing its AST nodes from being deleted.
void KeepAlive(std::unique_ptr<ModuleInfo> module_info);

// Records the resolution of a `use` leaf, so that later references to
// the bound name can be resolved via lookup.
void NoteUseImportResult(const UseTreeEntry* leaf, UseImportResult result) {
use_import_results_[leaf] = result;
}

// Returns the previously-noted resolution of the given `use` leaf, if any.
std::optional<UseImportResult> GetUseImportResult(
const UseTreeEntry* leaf) const {
auto it = use_import_results_.find(leaf);
if (it == use_import_results_.end()) {
return std::nullopt;
}
return it->second;
}

// If `node` is a `UseTreeEntry` that names a specific member,
// returns that member. Otherwise returns `std::nullopt`.
std::optional<const AstNode*> ResolveUseImportedTarget(
const AstNode* node) const {
if (node != nullptr && node->kind() == AstNodeKind::kUseTreeEntry) {
const auto* use_tree_entry = absl::down_cast<const UseTreeEntry*>(node);
if (std::optional<UseImportResult> result =
GetUseImportResult(use_tree_entry);
result.has_value() && result->imported_member != nullptr) {
return ToAstNode(*result->imported_member);
}
}
return std::nullopt;
}

// If `node` is a `UseTreeEntry` that names a module, return that module's
// `ModuleInfo`. Otherwise returns `std::nullopt`.
absl::optional_ref<ModuleInfo> GetUseImportedModuleInfo(
const UseTreeEntry* use_tree_entry) const {
if (std::optional<UseImportResult> result =
GetUseImportResult(use_tree_entry);
result.has_value() && result->imported_member == nullptr) {
return result->imported_module;
}
return std::nullopt;
}

// Returns the `TraitDeriver` to use for traits that are declared in the
// builtins module.
TraitDeriver* GetBuiltinTraitDeriver() const {
Expand Down Expand Up @@ -373,6 +432,7 @@ class ImportData {
// Modules that were discarded after being imported. We keep them to
// avoid use-after-free errors.
std::vector<std::unique_ptr<ModuleInfo>> discarded_modules_;
absl::flat_hash_map<const UseTreeEntry*, UseImportResult> use_import_results_;
absl::flat_hash_map<std::string, ModuleInfo*> path_to_module_info_;
absl::flat_hash_map<Module*, std::unique_ptr<InterpBindings>>
top_level_bindings_;
Expand Down
15 changes: 13 additions & 2 deletions xls/dslx/import_routines.cc
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,10 @@ absl::StatusOr<UseImportResult> DoImportViaUse(
if (dslx_path.ok()) {
XLS_ASSIGN_OR_RETURN(ModuleInfo * module_info,
to_module_info(attempted.back(), dslx_path.value()));
return UseImportResult{.imported_module = module_info,
UseImportResult result{.imported_module = module_info,
.imported_member = nullptr};
import_data->NoteUseImportResult(&subject.use_tree_entry(), result);
return result;
}

// 2. If that is not present, check whether the NameDef refers to an entity
Expand Down Expand Up @@ -334,8 +336,17 @@ absl::StatusOr<UseImportResult> DoImportViaUse(
name_def_span.ToString(file_table), member_name,
import_tokens.ToString()));
}
return UseImportResult{.imported_module = module_info_ptr,
if (!IsPublic(**member)) {
return absl::InvalidArgumentError(absl::StrFormat(
"ImportError: %s Attempted to refer to a module member %s that is "
"not public",
name_def_span.ToString(file_table),
absl::StrJoin(subject.identifiers(), "::")));
}
UseImportResult result{.imported_module = module_info_ptr,
.imported_member = member.value()};
import_data->NoteUseImportResult(&subject.use_tree_entry(), result);
return result;
}

return absl::NotFoundError(absl::StrFormat(
Expand Down
13 changes: 0 additions & 13 deletions xls/dslx/import_routines.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,6 @@ absl::StatusOr<ModuleInfo*> DoImport(const TypecheckModuleFn& ftypecheck,
const Span& import_span,
VirtualizableFilesystem& vfs);

struct UseImportResult {
// The `ModuleInfo`s that were imported as we traversed. Note that there can
// be more that one if there is a chain of `pub use` statements.
ModuleInfo* imported_module;

// If the `use` statement was referring to an entity inside of the enclosing
// module, it is given here.
//
// If this is nullptr, then it is implied that the module was the entity being
// referred to.
ModuleMember* imported_member;
};

// Imports a subject of a `use` statement -- note that this imports a single
// `subject` -- generally we linearize a `use` tree into elements and import
// each one via this routine in sequence.
Expand Down
2 changes: 2 additions & 0 deletions xls/dslx/ir_convert/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -271,13 +271,15 @@ cc_test(
"//xls/dslx:create_import_data",
"//xls/dslx:import_data",
"//xls/dslx:parse_and_typecheck",
"//xls/dslx:virtualizable_file_system",
"//xls/dslx/run_routines",
"//xls/dslx/run_routines:run_comparator",
"//xls/dslx/type_system:typecheck_test_utils",
"//xls/ir",
"//xls/ir:channel",
"//xls/ir:ir_matcher",
"@abseil-cpp//absl/base",
"@abseil-cpp//absl/container:flat_hash_map",
"@abseil-cpp//absl/status",
"@abseil-cpp//absl/status:status_matchers",
"@abseil-cpp//absl/status:statusor",
Expand Down
6 changes: 0 additions & 6 deletions xls/dslx/ir_convert/extract_conversion_order_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,6 @@ fn main() -> u32 { f(u2:0) }
}

TEST(ExtractConversionOrderTest, UseTreeEntryCallInParametric) {
// TODO: https://github.com/google/xls/issues/2876 - TIv2 does not yet support
// "use" syntax.
if (kDefaultTypeInferenceVersion == TypeInferenceVersion::kVersion2) {
return;
}

constexpr std::string_view kProgram = R"(#![feature(use_syntax)]
use std::is_pow2;
fn f<N: u32>(x: bits[N]) -> bool { is_pow2(x) }
Expand Down
Loading
Loading