Skip to content

Commit

Permalink
Clean up method detection type traits using concepts
Browse files Browse the repository at this point in the history
  • Loading branch information
MikePopoloski committed Jun 25, 2023
1 parent af2b67e commit e7ea86d
Show file tree
Hide file tree
Showing 13 changed files with 23 additions and 108 deletions.
30 changes: 5 additions & 25 deletions include/slang/ast/ASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,28 +36,8 @@

namespace slang::ast {

/// Contains various helper traits for detecting AST visit methods.
struct SLANG_EXPORT ASTDetectors {
/// Detects whether T has a method of the form handle(Arg)
template<typename T, typename Arg>
using handle_t = decltype(std::declval<T>().handle(std::declval<Arg>()));

/// Detects whether T has an operator(T, Arg)
template<typename T, typename Arg>
using op_t = decltype(std::declval<T>()(std::declval<T>(), std::declval<Arg>()));

/// Detects whether T has a method of the form getBody()
template<typename T>
using getBody_t = decltype(std::declval<T>().getBody());

/// Detects whether T has a method of the form visitExprs(Arg)
template<typename T, typename Arg>
using visitExprs_t = decltype(std::declval<T>().visitExprs(std::declval<Arg>()));

/// Detects whether T has a method of the form visitStmts(Arg)
template<typename T, typename Arg>
using visitStmts_t = decltype(std::declval<T>().visitStmts(std::declval<Arg>()));
};
template<typename T, typename TVisitor>
concept HasVisitExprs = requires(const T& t, TVisitor&& visitor) { t.visitExprs(visitor); };

/// @brief A base class for AST visitors
///
Expand All @@ -73,7 +53,7 @@ struct SLANG_EXPORT ASTDetectors {
/// in your handler.
///
template<typename TDerived, bool VisitStatements, bool VisitExpressions>
class ASTVisitor : public ASTDetectors {
class ASTVisitor {
#define DERIVED *static_cast<TDerived*>(this)
public:
/// The visit() entry point for visiting AST nodes.
Expand All @@ -91,11 +71,11 @@ class ASTVisitor : public ASTDetectors {
/// You can invoke this from custom node handlers to get the default behavior.
template<typename T>
void visitDefault(const T& t) {
if constexpr (VisitExpressions && is_detected_v<visitExprs_t, T, TDerived>) {
if constexpr (VisitExpressions && HasVisitExprs<T, TDerived>) {
t.visitExprs(DERIVED);
}

if constexpr (VisitStatements && is_detected_v<visitStmts_t, T, TDerived>) {
if constexpr (VisitStatements && requires { t.visitStmts(DERIVED); }) {
t.visitStmts(DERIVED);
}

Expand Down
5 changes: 1 addition & 4 deletions include/slang/syntax/SyntaxVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,11 @@ namespace slang::syntax {
/// node types you want to handle.
template<typename TDerived>
class SyntaxVisitor {
template<typename T, typename Arg>
using handle_t = decltype(std::declval<T>().handle(std::declval<Arg&&>()));

public:
/// Visit the provided node, of static type T.
template<typename T>
void visit(T&& t) {
if constexpr (is_detected_v<handle_t, TDerived, T>)
if constexpr (requires { DERIVED->handle(t); })
DERIVED->handle(t);
else
DERIVED->visitDefault(t);
Expand Down
42 changes: 0 additions & 42 deletions include/slang/util/TypeTraits.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,48 +11,6 @@

namespace slang {

namespace detail {
template<class Default, class AlwaysVoid, template<class...> class Op, class... Args>
struct detector {
using value_t = std::false_type;
using type = Default;
};

template<class Default, template<class...> class Op, class... Args>
struct detector<Default, std::void_t<Op<Args...>>, Op, Args...> {
using value_t = std::true_type;
using type = Op<Args...>;
};

} // namespace detail

struct nonesuch {
~nonesuch() = delete;
nonesuch(nonesuch const&) = delete;
void operator=(nonesuch const&) = delete;
};

template<template<class...> class Op, class... Args>
using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;

template<template<class...> class Op, class... Args>
using detected_t = typename detail::detector<nonesuch, void, Op, Args...>::type;

template<class Default, template<class...> class Op, class... Args>
using detected_or = detail::detector<Default, void, Op, Args...>;

template<template<class...> class Op, class... Args>
constexpr bool is_detected_v = is_detected<Op, Args...>::value;

template<typename TIter>
using IteratorCategory = typename std::iterator_traits<TIter>::iterator_category;

template<typename TIter, typename = void>
inline constexpr bool is_iterator_v = false;

template<typename TIter>
inline constexpr bool is_iterator_v<TIter, std::void_t<IteratorCategory<TIter>>> = true;

/// A simple implementation of a type index that can stand in for std::type_index
/// to allow building without RTTI enabled.
class SLANG_EXPORT type_index {
Expand Down
10 changes: 3 additions & 7 deletions scripts/syntax_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,8 @@ def generateSyntax(builddir, alltypes, kindmap):
// SPDX-License-Identifier: MIT
//------------------------------------------------------------------------------
#include "slang/syntax/AllSyntax.h"
#include "slang/util/TypeTraits.h"
#include <type_traits>
// This file contains all parse tree syntax node generated definitions.
// It is auto-generated by the syntax_gen.py script under the scripts/ directory.
Expand Down Expand Up @@ -774,8 +775,6 @@ def generateSyntaxClone(builddir, alltypes, kindmap):
//------------------------------------------------------------------------------
#include "slang/syntax/AllSyntax.h"
#include "slang/util/TypeTraits.h"
// This file contains all syntax generated clone implementations.
// It is auto-generated by the syntax_gen.py script under the scripts/ directory.
Expand Down Expand Up @@ -848,12 +847,9 @@ def generateSyntaxClone(builddir, alltypes, kindmap):
"""namespace slang::syntax {
struct CloneVisitor {
template<typename U>
using clone_t = decltype(std::declval<U>().clone(std::declval<BumpAllocator&>()));
template<typename T>
SyntaxNode* visit(const T& node, BumpAllocator& alloc) {
if constexpr (is_detected_v<clone_t, T>) {
if constexpr (requires { node.clone(alloc); }) {
return node.clone(alloc);
} else {
return shallow::clone(node, alloc);
Expand Down
4 changes: 2 additions & 2 deletions source/ast/Constraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ struct DistVarVisitor {
break;
}
default:
if constexpr (is_detected_v<ASTDetectors::visitExprs_t, T, DistVarVisitor>)
if constexpr (HasVisitExprs<T, DistVarVisitor>)
expr.visitExprs(*this);
break;
}
Expand Down Expand Up @@ -153,7 +153,7 @@ struct ConstraintExprVisitor {
}
}

if constexpr (is_detected_v<ASTDetectors::visitExprs_t, T, ConstraintExprVisitor>) {
if constexpr (HasVisitExprs<T, ConstraintExprVisitor>) {
// Inside call and select expressions we don't care about the types.
// This allows things like selections of associative arrays and built-in methods
// on arrays of strings to work as long as the actual operators at the top level
Expand Down
2 changes: 1 addition & 1 deletion source/ast/ElabVisitors.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ struct DiagnosticVisitor : public ASTVisitor<DiagnosticVisitor, false, false> {
attr->getValue();
}

if constexpr (is_detected_v<getBody_t, T>) {
if constexpr (requires { symbol.getBody(); }) {
auto& body = symbol.getBody();
if (body.bad())
return true;
Expand Down
17 changes: 4 additions & 13 deletions source/ast/Expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,10 @@ struct EvalVisitor {
};

class LValueVisitor {
template<typename T, typename Arg>
using evalLValue_t = decltype(std::declval<T>().evalLValueImpl(std::declval<Arg>()));

public:
template<typename T>
LValue visit(const T& expr, EvalContext& context) {
if constexpr (is_detected_v<evalLValue_t, T, EvalContext&>) {
if constexpr (requires { expr.evalLValueImpl(context); }) {
if (expr.bad())
return nullptr;

Expand All @@ -70,13 +67,10 @@ class LValueVisitor {
};

class EffectiveWidthVisitor {
template<typename T>
using getEffectiveWidth_t = decltype(std::declval<T>().getEffectiveWidthImpl());

public:
template<typename T>
std::optional<bitwidth_t> visit(const T& expr) {
if constexpr (is_detected_v<getEffectiveWidth_t, T>) {
if constexpr (requires { expr.getEffectiveWidthImpl(); }) {
if (expr.bad())
return std::nullopt;

Expand All @@ -99,7 +93,7 @@ struct HierarchicalVisitor {
if (expr.kind == ExpressionKind::HierarchicalValue) {
any = true;
}
else if constexpr (is_detected_v<ASTDetectors::visitExprs_t, T, HierarchicalVisitor>) {
else if constexpr (HasVisitExprs<T, HierarchicalVisitor>) {
expr.visitExprs(*this);
}
}
Expand All @@ -121,9 +115,6 @@ using namespace syntax;
// the type of one branch of an expression tree can bubble up and then propagate
// back down a different branch, which is also implemented here.
struct Expression::PropagationVisitor {
template<typename T, typename... Args>
using propagate_t = decltype(std::declval<T>().propagateType(std::declval<Args>()...));

const ASTContext& context;
const Type& newType;
SourceLocation assignmentLoc;
Expand All @@ -148,7 +139,7 @@ struct Expression::PropagationVisitor {
// check if the conversion should be pushed further down the tree. Otherwise we
// should insert the implicit conversion here.
bool needConversion = !newType.isEquivalent(*expr.type);
if constexpr (is_detected_v<propagate_t, T, const ASTContext&, const Type&>) {
if constexpr (requires { expr.propagateType(context, newType); }) {
if ((newType.isFloating() && expr.type->isFloating()) ||
(newType.isIntegral() && expr.type->isIntegral()) || newType.isString() ||
expr.kind == ExpressionKind::OpenRange) {
Expand Down
2 changes: 1 addition & 1 deletion source/ast/builtins/MiscSystemFuncs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ struct SequenceMethodExprVisitor {
}
}

if constexpr (is_detected_v<ASTDetectors::visitExprs_t, T, SequenceMethodExprVisitor>)
if constexpr (HasVisitExprs<T, SequenceMethodExprVisitor>)
expr.visitExprs(*this);
}

Expand Down
3 changes: 1 addition & 2 deletions source/ast/expressions/AssertionExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,7 @@ struct SampledValueExprVisitor {
break;
}
default:
if constexpr (is_detected_v<ASTDetectors::visitExprs_t, T,
SampledValueExprVisitor>)
if constexpr (HasVisitExprs<T, SampledValueExprVisitor>)
expr.visitExprs(*this);
break;
}
Expand Down
5 changes: 2 additions & 3 deletions source/ast/symbols/MemberSymbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,8 +438,7 @@ struct ExpressionVarVisitor {
break;
}
default:
if constexpr (is_detected_v<ASTDetectors::visitExprs_t, T,
ExpressionVarVisitor>) {
if constexpr (HasVisitExprs<T, ExpressionVarVisitor>) {
expr.visitExprs(*this);
}
break;
Expand Down Expand Up @@ -2103,7 +2102,7 @@ struct NetAliasVisitor {
context.addDiag(diag::NetAliasHierarchical, expr.sourceRange);
break;
default:
if constexpr (is_detected_v<ASTDetectors::visitExprs_t, T, NetAliasVisitor>)
if constexpr (HasVisitExprs<T, NetAliasVisitor>)
expr.visitExprs(*this);
break;
}
Expand Down
3 changes: 1 addition & 2 deletions source/ast/symbols/PortSymbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1192,8 +1192,7 @@ struct PortBackrefVisitor {
expr.template as<NamedValueExpression>().symbol.addPortBackref(port);
break;
default:
if constexpr (is_detected_v<ASTDetectors::visitExprs_t, T,
PortBackrefVisitor>) {
if constexpr (HasVisitExprs<T, PortBackrefVisitor>) {
expr.visitExprs(*this);
}
break;
Expand Down
3 changes: 1 addition & 2 deletions source/ast/symbols/SpecifySymbols.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,7 @@ struct SpecifyConditionVisitor {
case ExpressionKind::UnaryOp:
case ExpressionKind::BinaryOp:
case ExpressionKind::Conversion:
if constexpr (is_detected_v<ASTDetectors::visitExprs_t, T,
SpecifyConditionVisitor>)
if constexpr (HasVisitExprs<T, SpecifyConditionVisitor>)
expr.visitExprs(*this);

if (expr.kind == ExpressionKind::UnaryOp) {
Expand Down
5 changes: 1 addition & 4 deletions source/ast/types/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,9 @@ using namespace syntax;
namespace {

struct GetDefaultVisitor {
template<typename T>
using getDefault_t = decltype(std::declval<T>().getDefaultValueImpl());

template<typename T>
ConstantValue visit([[maybe_unused]] const T& type) {
if constexpr (is_detected_v<getDefault_t, T>) {
if constexpr (requires { type.getDefaultValueImpl(); }) {
return type.getDefaultValueImpl();
}
else {
Expand Down

0 comments on commit e7ea86d

Please sign in to comment.