Skip to content
Merged
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
1 change: 1 addition & 0 deletions bindings/python/CompBindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ void registerCompilation(py::module_& m, py::module_& ast, py::module_& driver)
.value("AllowVirtualIfaceWithOverride", CompilationFlags::AllowVirtualIfaceWithOverride)
.value("AllowArrayConcatAssignPattern", CompilationFlags::AllowArrayConcatAssignPattern)
.value("AllowCrossAutoBinMax", CompilationFlags::AllowCrossAutoBinMax)
.value("AllowInvalidTop", CompilationFlags::AllowInvalidTop)
.finalize();

py::classh<CompilationOptions>(ast, "CompilationOptions")
Expand Down
7 changes: 5 additions & 2 deletions include/slang/ast/Compilation.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,12 @@ enum class SLANG_EXPORT CompilationFlags {
/// instead of a net. By default slang follows the LRM and treats such ports as nets,
/// which for example allows them to be connected to `inout` ports. Some tools treat them
/// as variables instead; enabling this flag selects that behavior.
InferInputPortsAsVars = 1 << 19
InferInputPortsAsVars = 1 << 19,

/// Allow top-level modules to be selected even when their parameters have no defaults.
AllowInvalidTop = 1 << 20,
};
SLANG_BITMASK(CompilationFlags, InferInputPortsAsVars)
SLANG_BITMASK(CompilationFlags, AllowInvalidTop)

/// Contains various options that can control compilation behavior.
struct SLANG_EXPORT CompilationOptions {
Expand Down
3 changes: 1 addition & 2 deletions include/slang/ast/symbols/InstanceSymbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,7 @@ class SLANG_EXPORT InstanceSymbol final : public InstanceSymbolBase {
static InstanceSymbol& createDefault(
Compilation& compilation, const DefinitionSymbol& definition,
const HierarchyOverrideNode* hierarchyOverrideNode = nullptr,
const ConfigBlockSymbol* configBlock = nullptr, const ConfigRule* configRule = nullptr,
SourceLocation locationOverride = {});
const ConfigBlockSymbol* configBlock = nullptr, const ConfigRule* configRule = nullptr);

/// Creates a placeholder instance for a virtual interface type declaration.
static const InstanceSymbol& createVirtual(
Expand Down
2 changes: 2 additions & 0 deletions source/ast/Compilation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ const RootSymbol& Compilation::getRoot(bool skipDefParamsAndBinds) {
auto guard = ScopeGuard([this] { finalizing = false; });

auto isValidTop = [&](auto& definition) {
if (hasFlag(CompilationFlags::AllowInvalidTop))
return true;
// All parameters must have defaults.
for (auto& param : definition.parameters) {
if (!param.hasDefault() &&
Expand Down
26 changes: 18 additions & 8 deletions source/ast/symbols/InstanceSymbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,12 +373,11 @@ InstanceSymbol::InstanceSymbol(Compilation& compilation, std::string_view name,
InstanceSymbol& InstanceSymbol::createDefault(Compilation& comp, const DefinitionSymbol& definition,
const HierarchyOverrideNode* hierarchyOverrideNode,
const ConfigBlockSymbol* configBlock,
const ConfigRule* configRule,
SourceLocation locationOverride) {
auto loc = locationOverride ? locationOverride : definition.location;
auto& body = InstanceBodySymbol::fromDefinition(comp, definition, loc, InstanceFlags::None,
hierarchyOverrideNode, configBlock, configRule);
auto& result = *comp.emplace<InstanceSymbol>(definition.name, loc, body, 0u);
const ConfigRule* configRule) {
auto& body = InstanceBodySymbol::fromDefinition(comp, definition, definition.location,
InstanceFlags::None, hierarchyOverrideNode,
configBlock, configRule);
auto& result = *comp.emplace<InstanceSymbol>(definition.name, definition.location, body, 0u);

if (configBlock) {
auto rc = comp.emplace<ResolvedConfig>(*configBlock, result);
Expand Down Expand Up @@ -861,8 +860,15 @@ static Symbol* recurseDefaultIfaceInst(Compilation& comp, const InterfacePortSym
std::span<const ConstantRange>::iterator it,
std::span<const ConstantRange>::iterator end) {
if (it == end) {
auto& result = InstanceSymbol::createDefault(comp, *port.interfaceDef, nullptr, nullptr,
nullptr, port.location);
auto& def = *port.interfaceDef;
ParameterBuilder paramBuilder(*def.getParentScope(), def.name, def.parameters);
if (comp.hasFlag(CompilationFlags::AllowInvalidTop)) {
paramBuilder.setSuppressErrors(true);
}
auto& body = InstanceBodySymbol::fromDefinition(comp, def, port.location, paramBuilder,
InstanceFlags::None);

auto& result = *comp.emplace<InstanceSymbol>(port.name, port.location, body, 0u);

if (!firstInst)
firstInst = &result;
Expand Down Expand Up @@ -964,6 +970,10 @@ InstanceBodySymbol& InstanceBodySymbol::fromDefinition(
ParameterBuilder paramBuilder(*definition.getParentScope(), definition.name,
definition.parameters);
paramBuilder.setForceInvalidValues(flags.has(InstanceFlags::Uninstantiated));
if (compilation.hasFlag(CompilationFlags::AllowInvalidTop) &&
instanceLoc == definition.location) {
paramBuilder.setSuppressErrors(true);
}
if (hierarchyOverrideNode)
paramBuilder.setOverrides(hierarchyOverrideNode);

Expand Down
37 changes: 37 additions & 0 deletions tests/unittests/ast/HierarchyTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -983,6 +983,43 @@ endmodule
CHECK(unusedDefs[1]->name == "nottop");
}

TEST_CASE("Allow invalid top module parameters") {
auto text = R"(
module top #(parameter int p, parameter type t);
endmodule
)";

// Without the flag the explicitly requested top is rejected for having
// non-defaulted parameters.
{
CompilationOptions options;
options.topModules.emplace("top"sv);

Compilation compilation(options);
compilation.addSyntaxTree(SyntaxTree::fromText(text));

auto& diags = compilation.getAllDiagnostics();
REQUIRE(diags.size() == 1);
CHECK(diags[0].code == diag::InvalidTopModule);
}

// With the flag it elaborates anyway; the missing parameters get error types
// rather than producing errors.
{
CompilationOptions options;
options.flags |= CompilationFlags::AllowInvalidTop;
options.topModules.emplace("top"sv);

Compilation compilation(options);
compilation.addSyntaxTree(SyntaxTree::fromText(text));
NO_COMPILATION_ERRORS;

auto& top = *compilation.getRoot().topInstances[0];
CHECK(top.body.find<ParameterSymbol>("p").getValue().bad());
CHECK(top.body.find<TypeParameterSymbol>("t").targetType.getType().isError());
}
}

TEST_CASE("No top warning") {
auto tree = SyntaxTree::fromText(R"(
)");
Expand Down
Loading