diff --git a/bindings/python/TypeBindings.cpp b/bindings/python/TypeBindings.cpp index 6e457ed10..5c451865b 100644 --- a/bindings/python/TypeBindings.cpp +++ b/bindings/python/TypeBindings.cpp @@ -246,10 +246,10 @@ void registerTypes(py::module_& m) { .def_property_readonly("modport", [](const VirtualInterfaceType& self) { return self.modport; }); - EXPOSE_ENUM(m, ForwardTypedefCategory); + EXPOSE_ENUM(m, ForwardTypeRestriction); py::class_(m, "ForwardingTypedefSymbol") - .def_readonly("category", &ForwardingTypedefSymbol::category) + .def_readonly("typeRestriction", &ForwardingTypedefSymbol::typeRestriction) .def_readonly("visibility", &ForwardingTypedefSymbol::visibility) .def_property_readonly("nextForwardDecl", [](const ForwardingTypedefSymbol& self) { return self.getNextForwardDecl(); diff --git a/include/slang/ast/SemanticFacts.h b/include/slang/ast/SemanticFacts.h index cb1026f50..02ad599b0 100644 --- a/include/slang/ast/SemanticFacts.h +++ b/include/slang/ast/SemanticFacts.h @@ -19,6 +19,7 @@ class ASTSerializer; class ASTContext; class Scope; class TimingControl; +class Type; enum class SymbolKind; #define LIFETIME(x) x(Automatic) x(Static) @@ -102,6 +103,10 @@ SLANG_ENUM(ChargeStrength, CS) SLANG_ENUM(DriveStrength, DS) #undef DS +#define FTR(x) x(None) x(Enum) x(Struct) x(Union) x(Class) x(InterfaceClass) +SLANG_ENUM(ForwardTypeRestriction, FTR); +#undef FTR + /// A set of flags that control how assignments are checked. enum class SLANG_EXPORT AssignFlags : uint8_t { /// No special assignment behavior specified. @@ -165,6 +170,10 @@ class SLANG_EXPORT SemanticFacts { static std::pair, std::optional> getDriveStrength( const syntax::NetStrengthSyntax& syntax); + static ForwardTypeRestriction getTypeRestriction(syntax::ForwardTypeRestrictionSyntax& syntax); + static ForwardTypeRestriction getTypeRestriction(const Type& type); + static std::string_view getTypeRestrictionText(ForwardTypeRestriction typeRestriction); + static void populateTimeScale(TimeScale& timeScale, const Scope& scope, const syntax::TimeUnitsDeclarationSyntax& syntax, std::optional& unitsRange, diff --git a/include/slang/ast/symbols/ParameterSymbols.h b/include/slang/ast/symbols/ParameterSymbols.h index 181a91af2..80c9cfe9f 100644 --- a/include/slang/ast/symbols/ParameterSymbols.h +++ b/include/slang/ast/symbols/ParameterSymbols.h @@ -7,6 +7,7 @@ //------------------------------------------------------------------------------ #pragma once +#include "slang/ast/SemanticFacts.h" #include "slang/ast/symbols/ValueSymbol.h" #include "slang/syntax/SyntaxFwd.h" @@ -68,9 +69,10 @@ class SLANG_EXPORT ParameterSymbol : public ValueSymbol, public ParameterSymbolB class SLANG_EXPORT TypeParameterSymbol : public Symbol, public ParameterSymbolBase { public: DeclaredType targetType; + ForwardTypeRestriction typeRestriction; TypeParameterSymbol(const Scope& scope, std::string_view name, SourceLocation loc, bool isLocal, - bool isPort); + bool isPort, ForwardTypeRestriction typeRestriction); static void fromSyntax(const Scope& scope, const syntax::TypeParameterDeclarationSyntax& syntax, bool isLocal, bool isPort, @@ -80,6 +82,7 @@ class SLANG_EXPORT TypeParameterSymbol : public Symbol, public ParameterSymbolBa const Type& getTypeAlias() const { return *typeAlias; } bool isOverridden() const; + void checkTypeRestriction() const; void serializeTo(ASTSerializer& serializer) const; diff --git a/include/slang/ast/types/AllTypes.h b/include/slang/ast/types/AllTypes.h index c2d719107..3391ba629 100644 --- a/include/slang/ast/types/AllTypes.h +++ b/include/slang/ast/types/AllTypes.h @@ -428,36 +428,29 @@ class SLANG_EXPORT VirtualInterfaceType : public Type { static bool isKind(SymbolKind kind) { return kind == SymbolKind::VirtualInterfaceType; } }; -#define CATEGORY(x) x(None) x(Enum) x(Struct) x(Union) x(Class) x(InterfaceClass) -SLANG_ENUM(ForwardTypedefCategory, CATEGORY); -#undef CATEGORY - /// A forward declaration of a user-defined type name. A given type name can have /// an arbitrary number of forward declarations in the same scope, so each symbol /// forms a linked list, headed by the actual type definition. class SLANG_EXPORT ForwardingTypedefSymbol : public Symbol { public: - ForwardTypedefCategory category; + ForwardTypeRestriction typeRestriction; std::optional visibility; ForwardingTypedefSymbol(std::string_view name, SourceLocation loc, - ForwardTypedefCategory category) : + ForwardTypeRestriction typeRestriction) : Symbol(SymbolKind::ForwardingTypedef, name, loc), - category(category) {} + typeRestriction(typeRestriction) {} static ForwardingTypedefSymbol& fromSyntax( const Scope& scope, const syntax::ForwardTypedefDeclarationSyntax& syntax); - static ForwardingTypedefSymbol& fromSyntax( - const Scope& scope, const syntax::ForwardInterfaceClassTypedefDeclarationSyntax& syntax); - static ForwardingTypedefSymbol& fromSyntax( const Scope& scope, const syntax::ClassPropertyDeclarationSyntax& syntax); void addForwardDecl(const ForwardingTypedefSymbol& decl) const; const ForwardingTypedefSymbol* getNextForwardDecl() const { return next; } - void checkType(ForwardTypedefCategory checkCategory, Visibility checkVisibility, + void checkType(ForwardTypeRestriction checkRestriction, Visibility checkVisibility, SourceLocation declLoc) const; void serializeTo(ASTSerializer& serializer) const; diff --git a/include/slang/parsing/Parser.h b/include/slang/parsing/Parser.h index d917d48c3..192637346 100644 --- a/include/slang/parsing/Parser.h +++ b/include/slang/parsing/Parser.h @@ -274,6 +274,7 @@ class SLANG_EXPORT Parser : ParserBase, syntax::SyntaxFacts { syntax::MemberSyntax* parseClockingItem(); syntax::MemberSyntax& parseClockingDeclaration(AttrList attributes); syntax::MemberSyntax& parseDefaultDisable(AttrList attributes); + syntax::ForwardTypeRestrictionSyntax* parseTypeRestriction(bool isExpected); syntax::MemberSyntax& parseVariableDeclaration(AttrList attributes); syntax::DataDeclarationSyntax& parseDataDeclaration(AttrList attributes); syntax::LocalVariableDeclarationSyntax& parseLocalVariableDeclaration(); diff --git a/include/slang/syntax/SyntaxTree.h b/include/slang/syntax/SyntaxTree.h index f4a4a3111..3123322d5 100644 --- a/include/slang/syntax/SyntaxTree.h +++ b/include/slang/syntax/SyntaxTree.h @@ -90,12 +90,23 @@ class SLANG_EXPORT SyntaxTree { std::string_view name = "source", std::string_view path = ""); + /// Creates a syntax tree by guessing at what might be in the given source snippet. + /// @a text is the actual source code text. + /// @a options is a bag of lexer, preprocessor, and parser options. + /// @a name is an optional name to give to the loaded source buffer. + /// @a path is an optional path to give to the loaded source buffer. + /// @return the created and parsed syntax tree. + static std::shared_ptr fromText(std::string_view text, const Bag& options, + std::string_view name = "source"sv, + std::string_view path = ""); + /// Creates a syntax tree by guessing at what might be in the given source snippet. /// @a text is the actual source code text. /// @a sourceManager is the manager that owns all of the loaded source code. /// @a name is an optional name to give to the loaded source buffer. /// @a path is an optional path to give to the loaded source buffer. /// @a options is an optional bag of lexer, preprocessor, and parser options. + /// @a library the source library to associated with the parsed tree /// @return the created and parsed syntax tree. static std::shared_ptr fromText(std::string_view text, SourceManager& sourceManager, std::string_view name = "source"sv, diff --git a/scripts/diagnostics.txt b/scripts/diagnostics.txt index 30c18d4f1..17dda6921 100644 --- a/scripts/diagnostics.txt +++ b/scripts/diagnostics.txt @@ -504,6 +504,7 @@ error ConfigDupTop "config design specifies more than one top cell named '{}'" error ConfigOverrideTop "config rule can't override a top cell with a different target" error ConfigInstanceUnderOtherConfig "config instance rule applies to an instance that is within a hierarchy specified by another config" error ConfigParamsForPrimitive "cannot provide parameter assignments for primitive instance" +error TypeRestrictionMismatch "type restriction '{}' does not match assigned type {}" error FatalTask "$fatal encountered{}" error ErrorTask "$error encountered{}" error StaticAssert "static assertion failed{}" diff --git a/scripts/syntax.txt b/scripts/syntax.txt index 1b63491a3..d2dc47e74 100644 --- a/scripts/syntax.txt +++ b/scripts/syntax.txt @@ -503,6 +503,10 @@ DataType type separated_list declarators token semi +ForwardTypeRestriction +token keyword1 +token keyword2 + TypedefDeclaration base=Member token typedefKeyword DataType type @@ -512,14 +516,7 @@ token semi ForwardTypedefDeclaration base=Member token typedefKeyword -token keyword -token name -token semi - -ForwardInterfaceClassTypedefDeclaration base=Member -token typedefKeyword -token interfaceKeyword -token classKeyword +ForwardTypeRestriction? typeRestriction token name token semi @@ -608,6 +605,7 @@ EqualsTypeClause? assignment TypeParameterDeclaration base=ParameterDeclarationBase token typeKeyword +ForwardTypeRestriction? typeRestriction separated_list declarators ParameterDeclarationStatement base=Member diff --git a/source/ast/ElabVisitors.h b/source/ast/ElabVisitors.h index 6be3375fa..0b4221962 100644 --- a/source/ast/ElabVisitors.h +++ b/source/ast/ElabVisitors.h @@ -107,6 +107,12 @@ struct DiagnosticVisitor : public ASTVisitor { symbol.getType(); } + void handle(const TypeParameterSymbol& symbol) { + if (!handleDefault(symbol)) + return; + symbol.checkTypeRestriction(); + } + void handle(const ContinuousAssignSymbol& symbol) { if (!handleDefault(symbol)) return; diff --git a/source/ast/Scope.cpp b/source/ast/Scope.cpp index 6d42fe5a1..d7304a2e5 100644 --- a/source/ast/Scope.cpp +++ b/source/ast/Scope.cpp @@ -356,13 +356,6 @@ void Scope::addMembers(const SyntaxNode& syntax) { getOrAddDeferredData().addForwardingTypedef(symbol); break; } - case SyntaxKind::ForwardInterfaceClassTypedefDeclaration: { - auto& symbol = ForwardingTypedefSymbol::fromSyntax( - *this, syntax.as()); - addMember(symbol); - getOrAddDeferredData().addForwardingTypedef(symbol); - break; - } case SyntaxKind::GenerateRegion: for (auto member : syntax.as().members) addMembers(*member); @@ -397,8 +390,7 @@ void Scope::addMembers(const SyntaxNode& syntax) { case SyntaxKind::TypedefDeclaration: addMember(TypeAliasType::fromSyntax(*this, cpd)); break; - case SyntaxKind::ForwardTypedefDeclaration: - case SyntaxKind::ForwardInterfaceClassTypedefDeclaration: { + case SyntaxKind::ForwardTypedefDeclaration: { auto& symbol = ForwardingTypedefSymbol::fromSyntax(*this, cpd); addMember(symbol); getOrAddDeferredData().addForwardingTypedef(symbol); diff --git a/source/ast/SemanticFacts.cpp b/source/ast/SemanticFacts.cpp index 42c805570..1d638b2fa 100644 --- a/source/ast/SemanticFacts.cpp +++ b/source/ast/SemanticFacts.cpp @@ -10,6 +10,8 @@ #include "slang/ast/ASTSerializer.h" #include "slang/ast/Scope.h" #include "slang/ast/TimingControl.h" +#include "slang/ast/symbols/ClassSymbols.h" +#include "slang/ast/types/Type.h" #include "slang/diagnostics/DeclarationsDiags.h" #include "slang/diagnostics/PreprocessorDiags.h" #include "slang/syntax/AllSyntax.h" @@ -200,6 +202,61 @@ StatementBlockKind SemanticFacts::getStatementBlockKind(const BlockStatementSynt } } +ForwardTypeRestriction SemanticFacts::getTypeRestriction( + syntax::ForwardTypeRestrictionSyntax& syntax) { + switch (syntax.keyword1.kind) { + case TokenKind::EnumKeyword: + return ForwardTypeRestriction::Enum; + case TokenKind::StructKeyword: + return ForwardTypeRestriction::Struct; + case TokenKind::UnionKeyword: + return ForwardTypeRestriction::Union; + case TokenKind::ClassKeyword: + return ForwardTypeRestriction::Class; + case TokenKind::InterfaceKeyword: + return ForwardTypeRestriction::InterfaceClass; + default: + return ForwardTypeRestriction::None; + } +} + +ForwardTypeRestriction SemanticFacts::getTypeRestriction(const Type& type) { + auto& ct = type.getCanonicalType(); + switch (ct.kind) { + case SymbolKind::PackedStructType: + case SymbolKind::UnpackedStructType: + return ForwardTypeRestriction::Struct; + case SymbolKind::PackedUnionType: + case SymbolKind::UnpackedUnionType: + return ForwardTypeRestriction::Union; + case SymbolKind::EnumType: + return ForwardTypeRestriction::Enum; + case SymbolKind::ClassType: + if (ct.as().isInterface) + return ForwardTypeRestriction::InterfaceClass; + return ForwardTypeRestriction::Class; + default: + return ForwardTypeRestriction::None; + } +} + +std::string_view SemanticFacts::getTypeRestrictionText(ForwardTypeRestriction typeRestriction) { + switch (typeRestriction) { + case ForwardTypeRestriction::Enum: + return "enum"sv; + case ForwardTypeRestriction::Struct: + return "struct"sv; + case ForwardTypeRestriction::Union: + return "union"sv; + case ForwardTypeRestriction::Class: + return "class"sv; + case ForwardTypeRestriction::InterfaceClass: + return "interface class"sv; + default: + return ""sv; + } +} + void SemanticFacts::populateTimeScale(TimeScale& timeScale, const Scope& scope, const TimeUnitsDeclarationSyntax& syntax, std::optional& unitsRange, diff --git a/source/ast/Statements.cpp b/source/ast/Statements.cpp index d1a87cc34..ce19fc8f6 100644 --- a/source/ast/Statements.cpp +++ b/source/ast/Statements.cpp @@ -581,7 +581,6 @@ std::span Statement::createAndAddBlockItems( case SyntaxKind::DataDeclaration: case SyntaxKind::TypedefDeclaration: case SyntaxKind::ForwardTypedefDeclaration: - case SyntaxKind::ForwardInterfaceClassTypedefDeclaration: case SyntaxKind::PackageImportDeclaration: case SyntaxKind::ParameterDeclarationStatement: case SyntaxKind::LetDeclaration: diff --git a/source/ast/symbols/ClassSymbols.cpp b/source/ast/symbols/ClassSymbols.cpp index bdfb458c5..27e2bff91 100644 --- a/source/ast/symbols/ClassSymbols.cpp +++ b/source/ast/symbols/ClassSymbols.cpp @@ -130,10 +130,10 @@ void ClassType::addForwardDecl(const ForwardingTypedefSymbol& decl) const { void ClassType::checkForwardDecls() const { if (firstForward) { - auto category = ForwardTypedefCategory::Class; + auto typeRestriction = ForwardTypeRestriction::Class; if (isInterface) - category = ForwardTypedefCategory::InterfaceClass; - firstForward->checkType(category, Visibility::Public, location); + typeRestriction = ForwardTypeRestriction::InterfaceClass; + firstForward->checkType(typeRestriction, Visibility::Public, location); } } @@ -909,10 +909,10 @@ void GenericClassDefSymbol::addForwardDecl(const ForwardingTypedefSymbol& decl) void GenericClassDefSymbol::checkForwardDecls() const { if (firstForward) { - auto category = ForwardTypedefCategory::Class; + auto typeRestriction = ForwardTypeRestriction::Class; if (isInterface) - category = ForwardTypedefCategory::InterfaceClass; - firstForward->checkType(category, Visibility::Public, location); + typeRestriction = ForwardTypeRestriction::InterfaceClass; + firstForward->checkType(typeRestriction, Visibility::Public, location); } } diff --git a/source/ast/symbols/ParameterBuilder.cpp b/source/ast/symbols/ParameterBuilder.cpp index c3ce7f26c..720e8511b 100644 --- a/source/ast/symbols/ParameterBuilder.cpp +++ b/source/ast/symbols/ParameterBuilder.cpp @@ -145,8 +145,13 @@ const ParameterSymbolBase& ParameterBuilder::createParam( std::tie(newInitializer, isFromConfig) = it->second; if (decl.isTypeParam) { + auto typeRestriction = ForwardTypeRestriction::None; + if (decl.hasSyntax && decl.typeSyntax && decl.typeSyntax->typeRestriction) + typeRestriction = SemanticFacts::getTypeRestriction(*decl.typeSyntax->typeRestriction); + auto param = comp.emplace(newScope, decl.name, decl.location, - decl.isLocalParam, decl.isPortParam); + decl.isLocalParam, decl.isPortParam, + typeRestriction); param->setAttributes(scope, decl.attributes); auto& tt = param->targetType; diff --git a/source/ast/symbols/ParameterSymbols.cpp b/source/ast/symbols/ParameterSymbols.cpp index d3b0a9a57..152d30381 100644 --- a/source/ast/symbols/ParameterSymbols.cpp +++ b/source/ast/symbols/ParameterSymbols.cpp @@ -167,9 +167,11 @@ static DeclaredTypeFlags getTypeParamFlags(const Scope& scope) { } TypeParameterSymbol::TypeParameterSymbol(const Scope& scope, std::string_view name, - SourceLocation loc, bool isLocal, bool isPort) : + SourceLocation loc, bool isLocal, bool isPort, + ForwardTypeRestriction typeRestriction) : Symbol(SymbolKind::TypeParameter, name, loc), - ParameterSymbolBase(*this, isLocal, isPort), targetType(*this, getTypeParamFlags(scope)) { + ParameterSymbolBase(*this, isLocal, isPort), targetType(*this, getTypeParamFlags(scope)), + typeRestriction(typeRestriction) { auto alias = scope.getCompilation().emplace(name, loc); alias->setParent(scope); @@ -181,11 +183,16 @@ void TypeParameterSymbol::fromSyntax(const Scope& scope, const TypeParameterDeclarationSyntax& syntax, bool isLocal, bool isPort, SmallVectorBase& results) { auto& comp = scope.getCompilation(); + auto typeRestriction = ForwardTypeRestriction::None; + if (syntax.typeRestriction) + typeRestriction = SemanticFacts::getTypeRestriction(*syntax.typeRestriction); + for (auto decl : syntax.declarators) { auto name = decl->name.valueText(); auto loc = decl->name.location(); - auto param = comp.emplace(scope, name, loc, isLocal, isPort); + auto param = comp.emplace(scope, name, loc, isLocal, isPort, + typeRestriction); param->setSyntax(*decl); if (!decl->assignment) { @@ -203,6 +210,24 @@ void TypeParameterSymbol::fromSyntax(const Scope& scope, } } +void TypeParameterSymbol::checkTypeRestriction() const { + if (typeRestriction != ForwardTypeRestriction::None) { + auto& type = targetType.getType(); + if (!type.isError() && typeRestriction != SemanticFacts::getTypeRestriction(type)) { + auto scope = getParentScope(); + auto typeSyntax = targetType.getTypeSyntax(); + SLANG_ASSERT(scope && typeSyntax); + + auto& diag = scope->addDiag(diag::TypeRestrictionMismatch, typeSyntax->sourceRange()); + diag << SemanticFacts::getTypeRestrictionText(typeRestriction); + diag << type; + + if (isOverridden()) + diag.addNote(diag::NoteDeclarationHere, location); + } + } +} + bool TypeParameterSymbol::isOverridden() const { return getDeclaredType()->getFlags().has(DeclaredTypeFlags::TypeOverridden); } diff --git a/source/ast/types/AllTypes.cpp b/source/ast/types/AllTypes.cpp index 3ab24d7a1..1dbe7c796 100644 --- a/source/ast/types/AllTypes.cpp +++ b/source/ast/types/AllTypes.cpp @@ -1146,40 +1146,13 @@ ConstantValue VirtualInterfaceType::getDefaultValueImpl() const { ForwardingTypedefSymbol& ForwardingTypedefSymbol::fromSyntax( const Scope& scope, const ForwardTypedefDeclarationSyntax& syntax) { - ForwardTypedefCategory category; - switch (syntax.keyword.kind) { - case TokenKind::EnumKeyword: - category = ForwardTypedefCategory::Enum; - break; - case TokenKind::StructKeyword: - category = ForwardTypedefCategory::Struct; - break; - case TokenKind::UnionKeyword: - category = ForwardTypedefCategory::Union; - break; - case TokenKind::ClassKeyword: - category = ForwardTypedefCategory::Class; - break; - default: - category = ForwardTypedefCategory::None; - break; - } + auto typeRestriction = ForwardTypeRestriction::None; + if (syntax.typeRestriction) + typeRestriction = SemanticFacts::getTypeRestriction(*syntax.typeRestriction); auto& comp = scope.getCompilation(); auto result = comp.emplace(syntax.name.valueText(), - syntax.name.location(), category); - result->setSyntax(syntax); - result->setAttributes(scope, syntax.attributes); - return *result; -} - -ForwardingTypedefSymbol& ForwardingTypedefSymbol::fromSyntax( - const Scope& scope, const ForwardInterfaceClassTypedefDeclarationSyntax& syntax) { - - auto& comp = scope.getCompilation(); - auto result = comp.emplace(syntax.name.valueText(), - syntax.name.location(), - ForwardTypedefCategory::InterfaceClass); + syntax.name.location(), typeRestriction); result->setSyntax(syntax); result->setAttributes(scope, syntax.attributes); return *result; @@ -1188,22 +1161,14 @@ ForwardingTypedefSymbol& ForwardingTypedefSymbol::fromSyntax( ForwardingTypedefSymbol& ForwardingTypedefSymbol::fromSyntax( const Scope& scope, const ClassPropertyDeclarationSyntax& syntax) { - ForwardingTypedefSymbol* result; - if (syntax.declaration->kind == SyntaxKind::ForwardInterfaceClassTypedefDeclaration) { - result = &fromSyntax( - scope, syntax.declaration->as()); - } - else { - result = &fromSyntax(scope, syntax.declaration->as()); - } - + auto& result = fromSyntax(scope, syntax.declaration->as()); for (Token qual : syntax.qualifiers) { switch (qual.kind) { case TokenKind::LocalKeyword: - result->visibility = Visibility::Local; + result.visibility = Visibility::Local; break; case TokenKind::ProtectedKeyword: - result->visibility = Visibility::Protected; + result.visibility = Visibility::Protected; break; default: // Everything else is not allowed on typedefs; the parser will issue @@ -1212,8 +1177,8 @@ ForwardingTypedefSymbol& ForwardingTypedefSymbol::fromSyntax( } } - result->setAttributes(scope, syntax.attributes); - return *result; + result.setAttributes(scope, syntax.attributes); + return result; } void ForwardingTypedefSymbol::addForwardDecl(const ForwardingTypedefSymbol& decl) const { @@ -1223,30 +1188,12 @@ void ForwardingTypedefSymbol::addForwardDecl(const ForwardingTypedefSymbol& decl next->addForwardDecl(decl); } -void ForwardingTypedefSymbol::checkType(ForwardTypedefCategory checkCategory, +void ForwardingTypedefSymbol::checkType(ForwardTypeRestriction checkRestriction, Visibility checkVisibility, SourceLocation declLoc) const { - if (category != ForwardTypedefCategory::None && checkCategory != ForwardTypedefCategory::None && - category != checkCategory) { + if (typeRestriction != ForwardTypeRestriction::None && + checkRestriction != ForwardTypeRestriction::None && typeRestriction != checkRestriction) { auto& diag = getParentScope()->addDiag(diag::ForwardTypedefDoesNotMatch, location); - switch (category) { - case ForwardTypedefCategory::Enum: - diag << "enum"sv; - break; - case ForwardTypedefCategory::Struct: - diag << "struct"sv; - break; - case ForwardTypedefCategory::Union: - diag << "union"sv; - break; - case ForwardTypedefCategory::Class: - diag << "class"sv; - break; - case ForwardTypedefCategory::InterfaceClass: - diag << "interface class"sv; - break; - default: - SLANG_UNREACHABLE; - } + diag << SemanticFacts::getTypeRestrictionText(typeRestriction); diag.addNote(diag::NoteDeclarationHere, declLoc); return; } @@ -1258,11 +1205,11 @@ void ForwardingTypedefSymbol::checkType(ForwardTypedefCategory checkCategory, } if (next) - next->checkType(checkCategory, checkVisibility, declLoc); + next->checkType(checkRestriction, checkVisibility, declLoc); } void ForwardingTypedefSymbol::serializeTo(ASTSerializer& serializer) const { - serializer.write("category", toString(category)); + serializer.write("category", toString(typeRestriction)); if (next) serializer.write("next", *next); } @@ -1313,32 +1260,10 @@ void TypeAliasType::addForwardDecl(const ForwardingTypedefSymbol& decl) const { } void TypeAliasType::checkForwardDecls() const { - auto& ct = targetType.getType().getCanonicalType(); - ForwardTypedefCategory category; - switch (ct.kind) { - case SymbolKind::PackedStructType: - case SymbolKind::UnpackedStructType: - category = ForwardTypedefCategory::Struct; - break; - case SymbolKind::PackedUnionType: - case SymbolKind::UnpackedUnionType: - category = ForwardTypedefCategory::Union; - break; - case SymbolKind::EnumType: - category = ForwardTypedefCategory::Enum; - break; - case SymbolKind::ClassType: - category = ForwardTypedefCategory::Class; - if (ct.as().isInterface) - category = ForwardTypedefCategory::InterfaceClass; - break; - default: - category = ForwardTypedefCategory::None; - break; + if (firstForward) { + firstForward->checkType(SemanticFacts::getTypeRestriction(targetType.getType()), visibility, + location); } - - if (firstForward) - firstForward->checkType(category, visibility, location); } ConstantValue TypeAliasType::getDefaultValueImpl() const { diff --git a/source/parsing/Parser.cpp b/source/parsing/Parser.cpp index 67ce5677b..6cbec6c6b 100644 --- a/source/parsing/Parser.cpp +++ b/source/parsing/Parser.cpp @@ -742,41 +742,38 @@ MemberSyntax& Parser::parseNetDeclaration(AttrList attributes) { declarators, semi); } +ForwardTypeRestrictionSyntax* Parser::parseTypeRestriction(bool isExpected) { + switch (peek().kind) { + case TokenKind::EnumKeyword: + case TokenKind::StructKeyword: + case TokenKind::UnionKeyword: + case TokenKind::ClassKeyword: + if (isExpected || + (peek(1).kind == TokenKind::Identifier && peek(2).kind == TokenKind::Semicolon)) { + return &factory.forwardTypeRestriction(consume(), Token()); + } + break; + case TokenKind::InterfaceKeyword: { + auto interfaceKeyword = consume(); + auto classKeyword = expect(TokenKind::ClassKeyword); + return &factory.forwardTypeRestriction(interfaceKeyword, classKeyword); + } + default: + break; + } + return nullptr; +} + MemberSyntax& Parser::parseVariableDeclaration(AttrList attributes) { switch (peek().kind) { case TokenKind::TypedefKeyword: { auto typedefKeyword = consume(); - switch (peek().kind) { - case TokenKind::EnumKeyword: - case TokenKind::StructKeyword: - case TokenKind::UnionKeyword: - case TokenKind::ClassKeyword: - if (peek(1).kind == TokenKind::Identifier && - peek(2).kind == TokenKind::Semicolon) { - - auto keyword = consume(); - auto name = consume(); - return factory.forwardTypedefDeclaration(attributes, typedefKeyword, - keyword, name, consume()); - } - break; - case TokenKind::InterfaceKeyword: { - auto interfaceKeyword = consume(); - auto classKeyword = expect(TokenKind::ClassKeyword); - auto name = expect(TokenKind::Identifier); - return factory.forwardInterfaceClassTypedefDeclaration( - attributes, typedefKeyword, interfaceKeyword, classKeyword, name, - expect(TokenKind::Semicolon)); - } - case TokenKind::Identifier: - if (peek(1).kind == TokenKind::Semicolon) { - auto name = consume(); - return factory.forwardTypedefDeclaration(attributes, typedefKeyword, - Token(), name, consume()); - } - break; - default: - break; + auto restriction = parseTypeRestriction(/* isExpected */ false); + if (restriction || + (peek(TokenKind::Identifier) && peek(1).kind == TokenKind::Semicolon)) { + auto name = expect(TokenKind::Identifier); + return factory.forwardTypedefDeclaration(attributes, typedefKeyword, restriction, + name, expect(TokenKind::Semicolon)); } auto& type = parseDataType(); @@ -985,6 +982,11 @@ ParameterDeclarationBaseSyntax& Parser::parseParameterDecl(Token keyword, Token* // this is actually a type reference for a normal parameter. if (peek(TokenKind::TypeKeyword) && peek(1).kind != TokenKind::OpenParenthesis) { auto typeKeyword = consume(); + auto restriction = parseTypeRestriction(/* isExpected */ true); + if (restriction && parseOptions.languageVersion < LanguageVersion::v1800_2023) { + addDiag(diag::WrongLanguageVersion, restriction->sourceRange()) + << toString(parseOptions.languageVersion); + } SmallVector decls; if (semi) { @@ -1005,7 +1007,8 @@ ParameterDeclarationBaseSyntax& Parser::parseParameterDecl(Token keyword, Token* } } - return factory.typeParameterDeclaration(keyword, typeKeyword, decls.copy(alloc)); + return factory.typeParameterDeclaration(keyword, typeKeyword, restriction, + decls.copy(alloc)); } else { auto& type = parseDataType(TypeOptions::AllowImplicit); diff --git a/source/syntax/SyntaxFacts.cpp b/source/syntax/SyntaxFacts.cpp index ffd474c8b..32081d554 100644 --- a/source/syntax/SyntaxFacts.cpp +++ b/source/syntax/SyntaxFacts.cpp @@ -1232,7 +1232,6 @@ static bool isModuleOrPackageDecl(SyntaxKind kind) { case SyntaxKind::NetTypeDeclaration: case SyntaxKind::TypedefDeclaration: case SyntaxKind::ForwardTypedefDeclaration: - case SyntaxKind::ForwardInterfaceClassTypedefDeclaration: case SyntaxKind::PackageImportDeclaration: case SyntaxKind::DataDeclaration: case SyntaxKind::TaskDeclaration: @@ -1439,7 +1438,6 @@ bool SyntaxFacts::isAllowedInChecker(SyntaxKind kind) { case SyntaxKind::NetTypeDeclaration: case SyntaxKind::TypedefDeclaration: case SyntaxKind::ForwardTypedefDeclaration: - case SyntaxKind::ForwardInterfaceClassTypedefDeclaration: case SyntaxKind::PackageImportDeclaration: case SyntaxKind::DataDeclaration: case SyntaxKind::FunctionDeclaration: diff --git a/source/syntax/SyntaxTree.cpp b/source/syntax/SyntaxTree.cpp index ccf906585..5c856d9c0 100644 --- a/source/syntax/SyntaxTree.cpp +++ b/source/syntax/SyntaxTree.cpp @@ -64,6 +64,11 @@ std::shared_ptr SyntaxTree::fromText(std::string_view text, std::str return fromText(text, getDefaultSourceManager(), name, path); } +std::shared_ptr SyntaxTree::fromText(std::string_view text, const Bag& options, + std::string_view name, std::string_view path) { + return fromText(text, getDefaultSourceManager(), name, path, options); +} + std::shared_ptr SyntaxTree::fromText(std::string_view text, SourceManager& sourceManager, std::string_view name, std::string_view path, diff --git a/tests/unittests/Test.cpp b/tests/unittests/Test.cpp index 60841fe3a..7f2a86fd0 100644 --- a/tests/unittests/Test.cpp +++ b/tests/unittests/Test.cpp @@ -77,13 +77,8 @@ Diagnostics filterWarnings(const Diagnostics& diags) { Token lexToken(std::string_view text, LanguageVersion languageVersion) { diagnostics.clear(); - LexerOptions lo; - lo.languageVersion = languageVersion; - - PreprocessorOptions po; - po.languageVersion = languageVersion; - - Preprocessor preprocessor(getSourceManager(), alloc, diagnostics, Bag(lo, po)); + auto options = optionsFor(languageVersion); + Preprocessor preprocessor(getSourceManager(), alloc, diagnostics, options); preprocessor.pushSource(text); Token token = preprocessor.next(); @@ -153,13 +148,31 @@ const ExpressionSyntax& parseExpression(const std::string& text) { return parser.parseExpression(); } -const CompilationUnitSyntax& parseCompilationUnit(const std::string& text) { +Bag optionsFor(LanguageVersion version) { + PreprocessorOptions ppo; + ppo.languageVersion = version; + + LexerOptions lo; + lo.languageVersion = version; + + ParserOptions po; + po.languageVersion = version; + + CompilationOptions co; + co.languageVersion = version; + + return {ppo, lo, po, co}; +} + +const CompilationUnitSyntax& parseCompilationUnit(const std::string& text, + LanguageVersion languageVersion) { diagnostics.clear(); - Preprocessor preprocessor(getSourceManager(), alloc, diagnostics); + auto options = optionsFor(languageVersion); + Preprocessor preprocessor(getSourceManager(), alloc, diagnostics, options); preprocessor.pushSource(text); - Parser parser(preprocessor); + Parser parser(preprocessor, options); return parser.parseCompilationUnit(); } diff --git a/tests/unittests/Test.h b/tests/unittests/Test.h index 10caadf43..549d4f962 100644 --- a/tests/unittests/Test.h +++ b/tests/unittests/Test.h @@ -85,12 +85,15 @@ Token lexToken(std::string_view text, LanguageVersion languageVersion = LanguageVersion::v1800_2017); Token lexRawToken(std::string_view text); +Bag optionsFor(LanguageVersion version); + const ModuleDeclarationSyntax& parseModule(const std::string& text); const ClassDeclarationSyntax& parseClass(const std::string& text); const MemberSyntax& parseMember(const std::string& text); const StatementSyntax& parseStatement(const std::string& text); const ExpressionSyntax& parseExpression(const std::string& text); -const CompilationUnitSyntax& parseCompilationUnit(const std::string& text); +const CompilationUnitSyntax& parseCompilationUnit( + const std::string& text, LanguageVersion languageVersion = LanguageVersion::v1800_2017); const InstanceSymbol& evalModule(std::shared_ptr syntax, Compilation& compilation); class LogicExactlyEqualMatcher : public Catch::Matchers::MatcherGenericBase { diff --git a/tests/unittests/ast/ExpressionTests.cpp b/tests/unittests/ast/ExpressionTests.cpp index 53a476f20..d50bdc51d 100644 --- a/tests/unittests/ast/ExpressionTests.cpp +++ b/tests/unittests/ast/ExpressionTests.cpp @@ -3250,22 +3250,16 @@ endmodule } TEST_CASE("v1800-2023: Unsized integer literals can be any bit width") { - ParserOptions parseOptions; - parseOptions.languageVersion = LanguageVersion::v1800_2023; - + auto options = optionsFor(LanguageVersion::v1800_2023); auto tree = SyntaxTree::fromText(R"( module m; localparam a = 'h7_0000_0000; localparam b = 4294967296; endmodule )", - SyntaxTree::getDefaultSourceManager(), "source"sv, "", - parseOptions); - - CompilationOptions compOptions; - compOptions.languageVersion = LanguageVersion::v1800_2023; + options); - Compilation compilation(compOptions); + Compilation compilation(options); compilation.addSyntaxTree(tree); NO_COMPILATION_ERRORS; diff --git a/tests/unittests/ast/MemberTests.cpp b/tests/unittests/ast/MemberTests.cpp index ccbb837a1..e0ec33b3d 100644 --- a/tests/unittests/ast/MemberTests.cpp +++ b/tests/unittests/ast/MemberTests.cpp @@ -2556,3 +2556,29 @@ endmodule compilation.addSyntaxTree(tree); NO_COMPILATION_ERRORS; } + +TEST_CASE("v1800-2023: type parameter with type restriction errors") { + auto options = optionsFor(LanguageVersion::v1800_2023); + auto tree = SyntaxTree::fromText(R"( +module m #(parameter type enum foo, type class c)(); +endmodule + +class C; +endclass + +module top; + typedef struct { logic l; } asdf_t; + m #(.foo(enum { A, B }), .c(C)) m1(); + m #(.foo(int), .c(asdf_t)) m2(); +endmodule +)", + options); + + Compilation compilation(options); + compilation.addSyntaxTree(tree); + + auto& diags = compilation.getAllDiagnostics(); + REQUIRE(diags.size() == 2); + CHECK(diags[0].code == diag::TypeRestrictionMismatch); + CHECK(diags[1].code == diag::TypeRestrictionMismatch); +} diff --git a/tests/unittests/parsing/MemberParsingTests.cpp b/tests/unittests/parsing/MemberParsingTests.cpp index ff1f4dbec..3f0f6630c 100644 --- a/tests/unittests/parsing/MemberParsingTests.cpp +++ b/tests/unittests/parsing/MemberParsingTests.cpp @@ -1212,3 +1212,29 @@ endconfig CHECK(diagnostics[0].code == diag::NoCommaInList); CHECK(diagnostics[1].code == diag::NoCommaInList); } + +TEST_CASE("type parameter with type restriction parsing disallowed in 2017") { + auto& text = R"( +module m #(parameter type enum foo)(); +endmodule +)"; + + parseCompilationUnit(text); + + REQUIRE(diagnostics.size() == 1); + CHECK(diagnostics[0].code == diag::WrongLanguageVersion); +} + +TEST_CASE("v1800-2023: type parameter with type restriction parsing") { + auto& text = R"( +module m #(parameter type enum foo, + type struct s, + type union u, + type class c, + type interface class i)(); +endmodule +)"; + + parseCompilationUnit(text, LanguageVersion::v1800_2023); + CHECK_DIAGNOSTICS_EMPTY; +}