Skip to content

Commit 73090d4

Browse files
committed
Compilation: Add AllowInvalidTop flag
stack-info: PR: #1900, branch: AndrewNolte/stack/17
1 parent e3ca9d6 commit 73090d4

8 files changed

Lines changed: 80 additions & 16 deletions

File tree

include/slang/ast/Compilation.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,12 @@ enum class SLANG_EXPORT CompilationFlags {
141141
/// instead of a net. By default slang follows the LRM and treats such ports as nets,
142142
/// which for example allows them to be connected to `inout` ports. Some tools treat them
143143
/// as variables instead; enabling this flag selects that behavior.
144-
InferInputPortsAsVars = 1 << 19
144+
InferInputPortsAsVars = 1 << 19,
145+
146+
/// Allow top-level modules to be selected even when their parameters have no defaults.
147+
AllowInvalidTop = 1 << 20,
145148
};
146-
SLANG_BITMASK(CompilationFlags, InferInputPortsAsVars)
149+
SLANG_BITMASK(CompilationFlags, AllowInvalidTop)
147150

148151
/// Contains various options that can control compilation behavior.
149152
struct SLANG_EXPORT CompilationOptions {

include/slang/ast/symbols/InstanceSymbols.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,7 @@ class SLANG_EXPORT InstanceSymbol final : public InstanceSymbolBase {
145145
static InstanceSymbol& createDefault(
146146
Compilation& compilation, const DefinitionSymbol& definition,
147147
const HierarchyOverrideNode* hierarchyOverrideNode = nullptr,
148-
const ConfigBlockSymbol* configBlock = nullptr, const ConfigRule* configRule = nullptr,
149-
SourceLocation locationOverride = {});
148+
const ConfigBlockSymbol* configBlock = nullptr, const ConfigRule* configRule = nullptr);
150149

151150
/// Creates a placeholder instance for a virtual interface type declaration.
152151
static const InstanceSymbol& createVirtual(

source/ast/Compilation.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,8 @@ const RootSymbol& Compilation::getRoot(bool skipDefParamsAndBinds) {
316316
auto guard = ScopeGuard([this] { finalizing = false; });
317317

318318
auto isValidTop = [&](auto& definition) {
319+
if (hasFlag(CompilationFlags::AllowInvalidTop))
320+
return true;
319321
// All parameters must have defaults.
320322
for (auto& param : definition.parameters) {
321323
if (!param.hasDefault() &&

source/ast/symbols/InstanceSymbols.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -373,12 +373,11 @@ InstanceSymbol::InstanceSymbol(Compilation& compilation, std::string_view name,
373373
InstanceSymbol& InstanceSymbol::createDefault(Compilation& comp, const DefinitionSymbol& definition,
374374
const HierarchyOverrideNode* hierarchyOverrideNode,
375375
const ConfigBlockSymbol* configBlock,
376-
const ConfigRule* configRule,
377-
SourceLocation locationOverride) {
378-
auto loc = locationOverride ? locationOverride : definition.location;
379-
auto& body = InstanceBodySymbol::fromDefinition(comp, definition, loc, InstanceFlags::None,
380-
hierarchyOverrideNode, configBlock, configRule);
381-
auto& result = *comp.emplace<InstanceSymbol>(definition.name, loc, body, 0u);
376+
const ConfigRule* configRule) {
377+
auto& body = InstanceBodySymbol::fromDefinition(comp, definition, definition.location,
378+
InstanceFlags::None, hierarchyOverrideNode,
379+
configBlock, configRule);
380+
auto& result = *comp.emplace<InstanceSymbol>(definition.name, definition.location, body, 0u);
382381

383382
if (configBlock) {
384383
auto rc = comp.emplace<ResolvedConfig>(*configBlock, result);
@@ -861,8 +860,13 @@ static Symbol* recurseDefaultIfaceInst(Compilation& comp, const InterfacePortSym
861860
std::span<const ConstantRange>::iterator it,
862861
std::span<const ConstantRange>::iterator end) {
863862
if (it == end) {
864-
auto& result = InstanceSymbol::createDefault(comp, *port.interfaceDef, nullptr, nullptr,
865-
nullptr, port.location);
863+
auto& def = *port.interfaceDef;
864+
ParameterBuilder paramBuilder(*def.getParentScope(), def.name, def.parameters);
865+
paramBuilder.setUseInvalidForMissing(true);
866+
auto& body = InstanceBodySymbol::fromDefinition(comp, def, port.location, paramBuilder,
867+
InstanceFlags::None);
868+
869+
auto& result = *comp.emplace<InstanceSymbol>(port.name, port.location, body, 0u);
866870

867871
if (!firstInst)
868872
firstInst = &result;
@@ -964,6 +968,10 @@ InstanceBodySymbol& InstanceBodySymbol::fromDefinition(
964968
ParameterBuilder paramBuilder(*definition.getParentScope(), definition.name,
965969
definition.parameters);
966970
paramBuilder.setForceInvalidValues(flags.has(InstanceFlags::Uninstantiated));
971+
if (compilation.hasFlag(CompilationFlags::AllowInvalidTop) &&
972+
instanceLoc == definition.location) {
973+
paramBuilder.setUseInvalidForMissing(true);
974+
}
967975
if (hierarchyOverrideNode)
968976
paramBuilder.setOverrides(hierarchyOverrideNode);
969977

source/ast/symbols/ParameterBuilder.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,12 @@ const ParameterSymbolBase& ParameterBuilder::createParam(
229229
}
230230
else if (param->isPortParam() && !tt.getTypeSyntax() &&
231231
(decl.hasSyntax || !decl.givenType)) {
232-
reportError(*param);
232+
if (useInvalidForMissing) {
233+
tt.setType(comp.getErrorType());
234+
}
235+
else {
236+
reportError(*param);
237+
}
233238
}
234239
}
235240

@@ -308,7 +313,12 @@ const ParameterSymbolBase& ParameterBuilder::createParam(
308313
}
309314
}
310315
else if (param->isPortParam() && !declType.getInitializerSyntax()) {
311-
reportError(*param);
316+
if (useInvalidForMissing) {
317+
param->setValue(comp, nullptr, /* needsCoercion */ false);
318+
}
319+
else {
320+
reportError(*param);
321+
}
312322
}
313323
}
314324

source/ast/symbols/ParameterBuilder.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@ class ParameterBuilder {
3131

3232
void setAssignments(const syntax::ParameterValueAssignmentSyntax& syntax, bool isFromConfig);
3333
void setOverrides(const HierarchyOverrideNode* newVal) { overrideNode = newVal; }
34+
/// Force invalid values (error type) for all parameters.
3435
void setForceInvalidValues(bool set) { forceInvalidValues = set; }
36+
/// Set invalid values (error type) for parameters that are missing values, rather than
37+
/// reporting errors.
38+
void setUseInvalidForMissing(bool set) { useInvalidForMissing = set; }
39+
/// Suppress error reporting for missing parameter values.
3540
void setSuppressErrors(bool set) { suppressErrors = set; }
3641
void setInstanceContext(const ASTContext& context) { instanceContext = &context; }
3742
void setConfigScope(const Scope& confScope) { configScope = &confScope; }
@@ -58,6 +63,7 @@ class ParameterBuilder {
5863
const HierarchyOverrideNode* overrideNode = nullptr;
5964
const Scope* configScope = nullptr;
6065
bool forceInvalidValues = false;
66+
bool useInvalidForMissing = false;
6167
bool suppressErrors = false;
6268
bool anyErrors = false;
6369
};

tests/unittests/ast/HierarchyTests.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,43 @@ endmodule
983983
CHECK(unusedDefs[1]->name == "nottop");
984984
}
985985

986+
TEST_CASE("Allow invalid top module parameters") {
987+
auto text = R"(
988+
module top #(parameter int p, parameter type t);
989+
endmodule
990+
)";
991+
992+
// Without the flag the explicitly requested top is rejected for having
993+
// non-defaulted parameters.
994+
{
995+
CompilationOptions options;
996+
options.topModules.emplace("top"sv);
997+
998+
Compilation compilation(options);
999+
compilation.addSyntaxTree(SyntaxTree::fromText(text));
1000+
1001+
auto& diags = compilation.getAllDiagnostics();
1002+
REQUIRE(diags.size() == 1);
1003+
CHECK(diags[0].code == diag::InvalidTopModule);
1004+
}
1005+
1006+
// With the flag it elaborates anyway; the missing parameters get error types
1007+
// rather than producing errors.
1008+
{
1009+
CompilationOptions options;
1010+
options.flags |= CompilationFlags::AllowInvalidTop;
1011+
options.topModules.emplace("top"sv);
1012+
1013+
Compilation compilation(options);
1014+
compilation.addSyntaxTree(SyntaxTree::fromText(text));
1015+
NO_COMPILATION_ERRORS;
1016+
1017+
auto& top = *compilation.getRoot().topInstances[0];
1018+
CHECK(top.body.find<ParameterSymbol>("p").getValue().bad());
1019+
CHECK(top.body.find<TypeParameterSymbol>("t").targetType.getType().isError());
1020+
}
1021+
}
1022+
9861023
TEST_CASE("No top warning") {
9871024
auto tree = SyntaxTree::fromText(R"(
9881025
)");

tests/unittests/ast/InterfaceTests.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -580,9 +580,8 @@ endmodule
580580
compilation.addSyntaxTree(tree);
581581

582582
auto& diags = compilation.getAllDiagnostics();
583-
REQUIRE(diags.size() == 2);
583+
REQUIRE(diags.size() == 1);
584584
CHECK(diags[0].code == diag::InvalidModportAccess);
585-
CHECK(diags[1].code == diag::ParamHasNoValue);
586585
}
587586

588587
TEST_CASE("Interface-based typedef") {

0 commit comments

Comments
 (0)