diff options
| author | Yong He <yonghe@outlook.com> | 2024-04-01 15:56:02 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-04-01 15:56:02 -0700 |
| commit | 2c4f9810327d58023e9ec44f579cd78adf56317b (patch) | |
| tree | b5498a74bd7d01fd2e4c321a0d2e551d5f024d6d /source | |
| parent | 65ac9f3a9ddcb8bcfc099ffb29beaa9a92ba1f53 (diff) | |
Allow bit operators on enum types. (#3862)
* Allow bit operators on enum types.
* Fix.
Diffstat (limited to 'source')
| -rw-r--r-- | source/compiler-core/slang-doc-extractor.cpp | 5 | ||||
| -rw-r--r-- | source/slang/core.meta.slang | 82 | ||||
| -rw-r--r-- | source/slang/slang-ast-modifier.h | 6 | ||||
| -rw-r--r-- | source/slang/slang-ast-support-types.h | 14 | ||||
| -rw-r--r-- | source/slang/slang-check-conversion.cpp | 19 | ||||
| -rw-r--r-- | source/slang/slang-check-decl.cpp | 124 | ||||
| -rw-r--r-- | source/slang/slang-check-impl.h | 12 | ||||
| -rw-r--r-- | source/slang/slang-doc-ast.cpp | 4 | ||||
| -rw-r--r-- | source/slang/slang-language-server.cpp | 77 |
9 files changed, 246 insertions, 97 deletions
diff --git a/source/compiler-core/slang-doc-extractor.cpp b/source/compiler-core/slang-doc-extractor.cpp index ffbfc9904..9a625f3e0 100644 --- a/source/compiler-core/slang-doc-extractor.cpp +++ b/source/compiler-core/slang-doc-extractor.cpp @@ -766,7 +766,10 @@ SlangResult DocMarkupExtractor::extract(const SearchItemInput* inputs, Index inp // Find the new view sourceView = sourceManager->findSourceView(loc); if (!sourceView) - return SLANG_FAIL; + { + entry.searchStyle = SearchStyle::None; + continue; + } // We want only one view per SourceFile SourceFile* sourceFile = sourceView->getSourceFile(); diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index 583dd5923..dcb1a159b 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -76,8 +76,13 @@ syntax __extern_cpp : ExternCppModifier; interface IComparable { + __builtin_requirement($( (int)BuiltinRequirementKind::Equals)) bool equals(This other); + + __builtin_requirement($( (int)BuiltinRequirementKind::LessThan)) bool lessThan(This other); + + __builtin_requirement($( (int)BuiltinRequirementKind::LessThanOrEquals)) bool lessThanOrEquals(This other); } @@ -107,15 +112,34 @@ interface IArithmetic : IComparable interface ILogical : IComparable { + __builtin_requirement($( (int)BuiltinRequirementKind::Shl) ) This shl(int value); + + __builtin_requirement($( (int)BuiltinRequirementKind::Shr) ) This shr(int value); + + __builtin_requirement($( (int)BuiltinRequirementKind::BitAnd) ) This bitAnd(This other); + + __builtin_requirement($( (int)BuiltinRequirementKind::BitOr) ) This bitOr(This other); + + __builtin_requirement($( (int)BuiltinRequirementKind::BitXor) ) This bitXor(This other); + + __builtin_requirement($( (int)BuiltinRequirementKind::BitNot) ) This bitNot(); + + __builtin_requirement($( (int)BuiltinRequirementKind::And) ) This and(This other); + + __builtin_requirement($( (int)BuiltinRequirementKind::Or) ) This or(This other); + + __builtin_requirement($( (int)BuiltinRequirementKind::Not) ) This not(); + + __builtin_requirement($( (int)BuiltinRequirementKind::InitLogicalFromInt) ) __init(int val); } @@ -336,66 +360,19 @@ interface __BuiltinFloatingPointType : __BuiltinRealType, IFloat // A type resulting from an `enum` declaration. [builtin] __magic_type(EnumTypeType) -interface __EnumType +interface __EnumType : ILogical { // The type of tags for this `enum` // // Note: using `__Tag` instead of `Tag` to avoid any // conflict if a user had an `enum` case called `Tag` associatedtype __Tag : __BuiltinIntegerType; -}; -// Use an extension to declare that every `enum` type -// inherits an initializer based on the tag type. -// -// Note: there is an important and subtle point here. -// If we declared these initializers inside the `interface` -// declaration above, then they would implicitly be -// *requirements* of the `__EnumType` interface, and any -// type that declares conformance to it would need to -// provide implementations. That would put the onus on -// the semantic checker to synthesize such initializers -// when conforming an `enum` type to `__EnumType` (just -// as it currently synthesizes the `__Tag` requirement. -// Putting the declaration in an `extension` makes them -// concrete declerations rather than interface requirements. -// (Admittedly, they are "concrete" declarations with -// no bodies, because currently all initializers are -// assumed to be intrinsics). -// -// TODO: It might be more accurate to express this as: -// -// __generic<T:__EnumType> extension T { ... } -// -// That alternative would express an extension of every -// type that conforms to `__EnumType`, rather than an -// extension of `__EnumType` itself. The distinction -// is subtle, and unfortunately not one the Slang type -// checker is equiped to handle right now. For now we -// will stick with the syntax that actually works, even -// if it might be the less technically correct one. -// -// -extension __EnumType -{ - // TODO: this should be a single initializer using - // the `__Tag` associated type from the `__EnumType` - // interface, but right now the scoping for looking - // up that type isn't working right. - // - __intrinsic_op($(kIROp_IntCast)) - __init(int value); - __intrinsic_op($(kIROp_IntCast)) - __init(uint value); -} - -// A type resulting from an `enum` declaration -// with the `[flags]` attribute. -[builtin] -interface __FlagsEnumType : __EnumType -{ + __builtin_requirement($( (int)BuiltinRequirementKind::InitLogicalFromInt) ) + __init(__Tag value); }; + interface IArray<T> { int getCount(); @@ -2404,6 +2381,9 @@ attribute_syntax [disable_array_flattening] : DisableArrayFlatteningAttribute; __attributeTarget(EnumDecl) attribute_syntax [UnscopedEnum] : UnscopedEnumAttribute; +__attributeTarget(EnumDecl) +attribute_syntax[Flags] : FlagsAttribute; + // Statement Attributes __attributeTarget(LoopStmt) diff --git a/source/slang/slang-ast-modifier.h b/source/slang/slang-ast-modifier.h index 4ccca36ea..376307260 100644 --- a/source/slang/slang-ast-modifier.h +++ b/source/slang/slang-ast-modifier.h @@ -717,6 +717,12 @@ class UnscopedEnumAttribute : public Attribute SLANG_AST_CLASS(UnscopedEnumAttribute) }; + // Marks a enum to have `flags` semantics, where each enum case is a bitfield. +class FlagsAttribute : public Attribute +{ + SLANG_AST_CLASS(FlagsAttribute); +}; + // [[vk_push_constant]] [[push_constant]] class PushConstantAttribute : public Attribute { diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h index 93fc3365d..4b3a6cc9f 100644 --- a/source/slang/slang-ast-support-types.h +++ b/source/slang/slang-ast-support-types.h @@ -1606,6 +1606,20 @@ namespace Slang DZeroFunc, ///< The `IDifferentiable.dzero` function requirement DAddFunc, ///< The `IDifferentiable.dadd` function requirement DMulFunc, ///< The `IDifferentiable.dmul` function requirement + + InitLogicalFromInt, ///< The `ILogical.__init` mtehod. + Equals, ///< The `ILogical.equals` mtehod. + LessThan, ///< The `ILogical.lessThan` mtehod. + LessThanOrEquals, ///< The `ILogical.lessThanOrEquals` mtehod. + Shl, ///< The `ILogical.shl` mtehod. + Shr, ///< The `ILogical.shr` mtehod. + BitAnd, ///< The `ILogical.bitAnd` mtehod. + BitOr, ///< The `ILogical.bitOr` mtehod. + BitXor, ///< The `ILogical.bitXor` mtehod. + BitNot, ///< The `ILogical.bitNot` mtehod. + And, ///< The `ILogical.and` mtehod. + Or, ///< The `ILogical.or` mtehod. + Not, ///< The `ILogical.not` mtehod. }; enum class FunctionDifferentiableLevel diff --git a/source/slang/slang-check-conversion.cpp b/source/slang/slang-check-conversion.cpp index e55a88077..7dcc8975c 100644 --- a/source/slang/slang-check-conversion.cpp +++ b/source/slang/slang-check-conversion.cpp @@ -894,6 +894,25 @@ namespace Slang } return true; } + + // A enum type can be converted into its underlying tag type. + if (auto enumDecl = isEnumType(fromType)) + { + Type* tagType = enumDecl->tagType; + if (tagType == toType) + { + if (outCost) + { + *outCost = kConversionCost_RankPromotion; + } + if (outToExpr) + { + *outToExpr = fromExpr; + } + return true; + } + } + // matrix type with different layouts are convertible if (auto fromMatrixType = as<MatrixExpressionType>(fromType)) { diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp index 78eccbc8c..07c8b0cba 100644 --- a/source/slang/slang-check-decl.cpp +++ b/source/slang/slang-check-decl.cpp @@ -1335,6 +1335,15 @@ namespace Slang return arrayType->isUnsized(); } + EnumDecl* isEnumType(Type* type) + { + if (auto declRefType = as<DeclRefType>(type)) + { + return as<EnumDecl>(declRefType->getDeclRef().getDecl()); + } + return nullptr; + } + bool SemanticsVisitor::shouldSkipChecking(Decl* decl, DeclCheckState state) { if (state < DeclCheckState::DefinitionChecked) @@ -3591,13 +3600,14 @@ namespace Slang } } - FuncDecl* SemanticsVisitor::synthesizeMethodSignatureForRequirementWitness( + FunctionDeclBase* SemanticsVisitor::synthesizeMethodSignatureForRequirementWitness( ConformanceCheckingContext* context, - DeclRef<FuncDecl> requiredMemberDeclRef, + DeclRef<FunctionDeclBase> requiredMemberDeclRef, List<Expr*>& synArgs, ThisExpr*& synThis) { - auto synFuncDecl = m_astBuilder->create<FuncDecl>(); + FunctionDeclBase* synFuncDecl = as<FunctionDeclBase>(m_astBuilder->createByNodeType(requiredMemberDeclRef.getDecl()->astNodeType)); + SLANG_ASSERT(synFuncDecl); synFuncDecl->ownedScope = m_astBuilder->create<Scope>(); synFuncDecl->ownedScope->containerDecl = synFuncDecl; synFuncDecl->ownedScope->parent = getScope(context->parentDecl); @@ -3913,6 +3923,14 @@ namespace Slang { SLANG_UNUSED(satisfyingMemberLookupResult); + if (as<EnumDecl>(context->parentDecl)) + { + if (auto builtinRequirement = requiredMemberDeclRef.getDecl()->findModifier<BuiltinRequirementModifier>()) + { + return trySynthesizeEnumTypeMethodRequirementWitness(context, requiredMemberDeclRef, witnessTable, builtinRequirement->kind); + } + } + bool isInWrapperType = isWrapperTypeDecl(context->parentDecl); if (!isInWrapperType) { @@ -4593,6 +4611,21 @@ namespace Slang requiredFuncDeclRef, witnessTable, SynthesisPattern::AllInductive); + case BuiltinRequirementKind::And: + case BuiltinRequirementKind::Or: + case BuiltinRequirementKind::Not: + case BuiltinRequirementKind::BitAnd: + case BuiltinRequirementKind::BitNot: + case BuiltinRequirementKind::BitOr: + case BuiltinRequirementKind::BitXor: + case BuiltinRequirementKind::Shl: + case BuiltinRequirementKind::Shr: + case BuiltinRequirementKind::Equals: + case BuiltinRequirementKind::LessThan: + case BuiltinRequirementKind::LessThanOrEquals: + if (isEnumType(context->conformingType)) + return trySynthesizeEnumTypeMethodRequirementWitness(context, requiredFuncDeclRef, witnessTable, builtinAttr->kind); + break; } } return false; @@ -4739,6 +4772,70 @@ namespace Slang return synth.emitAssignStmt(leftValue, synth.emitInvokeExpr(callee, _Move(args))); } + bool SemanticsVisitor::trySynthesizeEnumTypeMethodRequirementWitness(ConformanceCheckingContext* context, + DeclRef<FunctionDeclBase> funcDeclRef, + RefPtr<WitnessTable> witnessTable, + BuiltinRequirementKind requirementKind) + { + List<Expr*> synArgs; + ThisExpr* synThis = nullptr; + auto synFunc = synthesizeMethodSignatureForRequirementWitness( + context, funcDeclRef, synArgs, synThis); + auto intrinsicOpModifier = getASTBuilder()->create<IntrinsicOpModifier>(); + switch (requirementKind) + { + case BuiltinRequirementKind::And: + intrinsicOpModifier->op = kIROp_And; + break; + case BuiltinRequirementKind::Or: + intrinsicOpModifier->op = kIROp_Or; + break; + case BuiltinRequirementKind::Not: + intrinsicOpModifier->op = kIROp_Not; + break; + case BuiltinRequirementKind::BitAnd: + intrinsicOpModifier->op = kIROp_BitAnd; + break; + case BuiltinRequirementKind::BitNot: + intrinsicOpModifier->op = kIROp_BitNot; + break; + case BuiltinRequirementKind::BitOr: + intrinsicOpModifier->op = kIROp_BitOr; + break; + case BuiltinRequirementKind::BitXor: + intrinsicOpModifier->op = kIROp_BitXor; + break; + case BuiltinRequirementKind::Shl: + intrinsicOpModifier->op = kIROp_Lsh; + break; + case BuiltinRequirementKind::Shr: + intrinsicOpModifier->op = kIROp_Rsh; + break; + case BuiltinRequirementKind::Equals: + intrinsicOpModifier->op = kIROp_Eql; + break; + case BuiltinRequirementKind::LessThan: + intrinsicOpModifier->op = kIROp_Less; + break; + case BuiltinRequirementKind::LessThanOrEquals: + intrinsicOpModifier->op = kIROp_Leq; + break; + case BuiltinRequirementKind::InitLogicalFromInt: + intrinsicOpModifier->op = kIROp_IntCast; + break; + default: + SLANG_ASSERT("unknown builtin requirement kind."); + } + synFunc->parentDecl = context->parentDecl; + synFunc->loc = context->parentDecl->closingSourceLoc; + synFunc->nameAndLoc.loc = synFunc->loc; + context->parentDecl->members.add(synFunc); + context->parentDecl->invalidateMemberDictionary(); + addModifier(synFunc, intrinsicOpModifier); + witnessTable->add(funcDeclRef.getDecl(), RequirementWitness(m_astBuilder->getDirectDeclRef(synFunc))); + return true; + } + bool SemanticsVisitor::trySynthesizeDifferentialMethodRequirementWitness( ConformanceCheckingContext* context, DeclRef<Decl> requirementDeclRef, @@ -4801,8 +4898,8 @@ namespace Slang } else if (auto funcDeclRef = requirementDeclRef.as<FuncDecl>()) { - synFunc = synthesizeMethodSignatureForRequirementWitness( - context, funcDeclRef, synArgs, synThis); + synFunc = as<FuncDecl>(synthesizeMethodSignatureForRequirementWitness( + context, funcDeclRef, synArgs, synThis)); } SLANG_ASSERT(synFunc); @@ -6083,6 +6180,8 @@ namespace Slang auto tagType = decl->tagType; + auto isEnumFlags = decl->hasModifier<FlagsAttribute>(); + // Check the enum cases in order. for(auto caseDecl : decl->getMembersOfType<EnumCaseDecl>()) { @@ -6102,7 +6201,7 @@ namespace Slang // For any enum case that didn't provide an explicit // tag value, derived an appropriate tag value. - IntegerLiteralValue defaultTag = 0; + IntegerLiteralValue defaultTag = isEnumFlags ? 1 : 0; for(auto caseDecl : decl->getMembersOfType<EnumCaseDecl>()) { if(auto explicitTagValExpr = caseDecl->tagExpr) @@ -6146,10 +6245,15 @@ namespace Slang // Default tag for the next case will be one more than // for the most recent case. // - // TODO: We might consider adding a `[flags]` attribute - // that modifies this behavior to be `defaultTagForCase <<= 1`. - // - defaultTag++; + if (!isEnumFlags) + defaultTag++; + else + { + if (defaultTag == 0) + defaultTag = 1; + else + defaultTag <<= 1; + } } } diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h index 945618f36..002ef1f71 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -1612,9 +1612,9 @@ namespace Slang CallableDecl* synthesized, List<Expr*>& synArgs); - FuncDecl* synthesizeMethodSignatureForRequirementWitness( + FunctionDeclBase* synthesizeMethodSignatureForRequirementWitness( ConformanceCheckingContext* context, - DeclRef<FuncDecl> requiredMemberDeclRef, + DeclRef<FunctionDeclBase> requiredMemberDeclRef, List<Expr*>& synArgs, ThisExpr*& synThis); @@ -1729,6 +1729,12 @@ namespace Slang DeclRef<AssocTypeDecl> requirementDeclRef, RefPtr<WitnessTable> witnessTable); + /// Attempt to synthesize function requirements for enum types to make them conform to `ILogical`. + bool trySynthesizeEnumTypeMethodRequirementWitness(ConformanceCheckingContext* context, + DeclRef<FunctionDeclBase> requirementDeclRef, + RefPtr<WitnessTable> witnessTable, + BuiltinRequirementKind requirementKind); + struct DifferentiableMemberInfo { Decl* memberDecl; @@ -2751,6 +2757,8 @@ namespace Slang bool isUnsizedArrayType(Type* type); + EnumDecl* isEnumType(Type* type); + DeclVisibility getDeclVisibility(Decl* decl); void diagnoseCapabilityProvenance(DiagnosticSink* sink, Decl* decl, CapabilityAtom missingAtom); diff --git a/source/slang/slang-doc-ast.cpp b/source/slang/slang-doc-ast.cpp index dfe3b321c..172f31b32 100644 --- a/source/slang/slang-doc-ast.cpp +++ b/source/slang/slang-doc-ast.cpp @@ -107,6 +107,10 @@ SlangResult ASTMarkupUtil::extract(ModuleDecl* moduleDecl, SourceManager* source SLANG_ASSERT(item.sourceLoc.isValid()); item.searchStyle = getSearchStyle(decl); + + // Don't generate documentation for synthesized members. + if (getText(decl->getName()).startsWith("$__syn")) + item.searchStyle = DocMarkupExtractor::SearchStyle::None; } DocMarkupExtractor extractor; diff --git a/source/slang/slang-language-server.cpp b/source/slang/slang-language-server.cpp index 9cfef50a0..8f71666dd 100644 --- a/source/slang/slang-language-server.cpp +++ b/source/slang/slang-language-server.cpp @@ -291,51 +291,62 @@ String getDeclSignatureString(DeclRef<Decl> declRef, WorkspaceVersion* version) ASTPrinter::OptionFlag::SimplifiedBuiltinType); printer.getStringBuilder() << getDeclKindString(declRef); printer.addDeclSignature(declRef); - if (auto varDecl = as<VarDeclBase>(declRef.getDecl())) - { - auto& sb = printer.getStringBuilder(); - if (!varDecl->findModifier<ConstModifier>() && !as<LetDecl>(declRef.getDecl())) - return printer.getString(); - - if (auto litExpr = as<LiteralExpr>(varDecl->initExpr)) - { - sb << " = " << litExpr->token.getContent(); - } - else if (auto isTypeDecl = as<IsTypeExpr>(varDecl->initExpr)) + auto printInitExpr = [&](Module* module, Type* declType, Expr* initExpr) { - if (isTypeDecl->constantVal) + auto& sb = printer.getStringBuilder(); + + if (auto litExpr = as<LiteralExpr>(initExpr)) { - sb << " = " << (isTypeDecl->constantVal->value ? "true" : "false"); + if (litExpr->token.type != TokenType::Unknown) + sb << " = " << litExpr->token.getContent(); + else if (auto intLit = as<IntegerLiteralExpr>(litExpr)) + sb << " = " << intLit->value; } - } - else if (varDecl->initExpr) - { - DiagnosticSink sink; - SharedSemanticsContext semanticContext(version->linkage, getModule(varDecl), &sink); - SemanticsVisitor semanticsVisitor(&semanticContext); - if (auto intVal = semanticsVisitor.tryFoldIntegerConstantExpression( - declRef.substitute(version->linkage->getASTBuilder(), varDecl->initExpr), - SemanticsVisitor::ConstantFoldingKind::LinkTime, nullptr)) + else if (auto isTypeDecl = as<IsTypeExpr>(initExpr)) { - if (auto constantInt = as<ConstantIntVal>(intVal)) + if (isTypeDecl->constantVal) { - sb << " = "; - if (isBoolType(varDecl->getType())) + sb << " = " << (isTypeDecl->constantVal->value ? "true" : "false"); + } + } + else if (initExpr) + { + DiagnosticSink sink; + SharedSemanticsContext semanticContext(version->linkage, module, &sink); + SemanticsVisitor semanticsVisitor(&semanticContext); + if (auto intVal = semanticsVisitor.tryFoldIntegerConstantExpression( + declRef.substitute(version->linkage->getASTBuilder(), initExpr), + SemanticsVisitor::ConstantFoldingKind::LinkTime, nullptr)) + { + if (auto constantInt = as<ConstantIntVal>(intVal)) { - sb << (constantInt->getValue() ? "true" : "false"); + sb << " = "; + if (isBoolType(declType)) + { + sb << (constantInt->getValue() ? "true" : "false"); + } + else + { + sb << constantInt->getValue(); + } } else { - sb << constantInt->getValue(); + sb << " = "; + intVal->toText(sb); } } - else - { - sb << " = "; - intVal->toText(sb); - } } - } + }; + if (auto varDecl = as<VarDeclBase>(declRef.getDecl())) + { + if (!varDecl->findModifier<ConstModifier>() && !as<LetDecl>(declRef.getDecl())) + return printer.getString(); + printInitExpr(getModule(varDecl), varDecl->type, varDecl->initExpr); + } + else if (auto enumCase = as<EnumCaseDecl>(declRef.getDecl())) + { + printInitExpr(getModule(enumCase), nullptr, enumCase->tagExpr); } return printer.getString(); } |
