diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 41cb9bff18c1691a7d4eb09164fbac176daceb6f..3d4eb474521dfca14f913945747f9e38100a677c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -36,11 +36,23 @@ smoke-test-suite:
   stage: smoke-test
   script: make smoke-test-suite
 
-static-analysis-with-emper-io:
-  stage: smoke-test
-  script: make static-analysis
-  variables:
-      EMPER_IO: "true"
+fast-static-analysis:
+   stage: smoke-test
+   script: make fast-static-analysis
+   variables:
+       EMPER_IO: "true"
+
+iwyu:
+   stage: smoke-test
+   script: make iwyu
+   variables:
+       EMPER_IO: "true"
+
+clang-tidy:
+   stage: smoke-test
+   script: make tidy
+   variables:
+       EMPER_IO: "true"
 
 .test:
   stage: test
diff --git a/Makefile b/Makefile
index e8d9aa05b7027f10b151a1ec13258e198cdca583..432d6fcf5d6b134b92a05c2ac3053c74cb6198df 100644
--- a/Makefile
+++ b/Makefile
@@ -41,8 +41,11 @@ else
 $(warning old mesion version $(MESON_VERSION) detected, meson >= 0.52 required for clang-tidy)
 endif
 
+.PHONY: fast-static-analysis
+fast-static-analysis: all check-format check-license doc
+
 .PHONY: static-analysis
-static-analysis: all check-format check-license doc
+static-analysis: fast-static-analysis
 	$(NINJA) -C build $(STATIC_ANALYSIS_NINJA_TARGETS)
 
 .PHONY: smoke-test-suite
@@ -80,6 +83,10 @@ check-license:
 format: all
 	$(NINJA) -C build clang-format
 
+.PHONY: tidy
+tidy: all
+	$(NINJA) -C build clang-tidy
+
 PHONY: iwyu
 iwyu: all
 	$(NINJA) -C build $@
diff --git a/emper/Actor.hpp b/emper/Actor.hpp
index 80fa5f7b25c42cc748ddb53838c21d92ee3f28f5..2a7bf281a3561903d42907aeaf30491e7b2a34f9 100644
--- a/emper/Actor.hpp
+++ b/emper/Actor.hpp
@@ -57,6 +57,8 @@ class Actor {
 	void stop() { setState(Stopped); }
 
  public:
+	virtual ~Actor() = default;
+
 	template <CallerEnvironment callerEnvironment = CallerEnvironment::EMPER>
 	void start() {
 		if (state.load(std::memory_order_acquire) != Stopped) return;
diff --git a/emper/BinaryPrivateSemaphore.hpp b/emper/BinaryPrivateSemaphore.hpp
index 5dcee79238888069be9423fe115fd5292d25425f..83dfad8b3ab7e4ffd6198f4e3aef5d857059004f 100644
--- a/emper/BinaryPrivateSemaphore.hpp
+++ b/emper/BinaryPrivateSemaphore.hpp
@@ -8,7 +8,7 @@
 
 class Context;
 
-class BinaryPrivateSemaphore : public PrivateSemaphore {
+class BinaryPrivateSemaphore final : public PrivateSemaphore {
  private:
 	enum State {
 		initial,
@@ -34,6 +34,8 @@ class BinaryPrivateSemaphore : public PrivateSemaphore {
  public:
 	BinaryPrivateSemaphore() : bpsState(initial) {}
 
+	~BinaryPrivateSemaphore() override = default;
+
 	void wait() override;
 
 	void reset() { bpsState = initial; };
diff --git a/emper/Dispatcher.hpp b/emper/Dispatcher.hpp
index 175240c5b74fb933d08bcd4ad58efeebe4c172b8..c3b524275cf29e5f857852a2aea3c3e9dbd17107 100644
--- a/emper/Dispatcher.hpp
+++ b/emper/Dispatcher.hpp
@@ -47,6 +47,8 @@ class Dispatcher : public Logger<LogSubsystem::DISP> {
  public:
 	Dispatcher(Runtime& runtime) : runtime(runtime) {}
 
+	virtual ~Dispatcher() = default;
+
 	static auto getCurrentFiber() -> Fiber& {
 		Fiber* fiber = getCurrentFiberPtr();
 		return *fiber;
diff --git a/emper/PrivateSemaphore.hpp b/emper/PrivateSemaphore.hpp
index 8e89d21d5d850732ff1f88fce1b6847a18cd9895..8c19341d4e772fb46e3d0661b3ce1ce2f273d41c 100644
--- a/emper/PrivateSemaphore.hpp
+++ b/emper/PrivateSemaphore.hpp
@@ -18,6 +18,8 @@ class PrivateSemaphore : protected Blockable<LogSubsystem::PS> {
 		LOGD("constructed by fiber " << Dispatcher::getCurrentFiber());
 	}
 
+	virtual ~PrivateSemaphore() = default;
+
 	[[noreturn]] void unblockAndExit(Context* context) { contextManager.discardAndResume(context); }
 
 	virtual auto signalInternal() -> Context* = 0;
diff --git a/emper/RuntimeStrategy.hpp b/emper/RuntimeStrategy.hpp
index d152c2bf45db6bb142e8adaca790a5660ac202f3..2cbe48bdc9a1a49f9a680dcadfdce0c4f52da49f 100644
--- a/emper/RuntimeStrategy.hpp
+++ b/emper/RuntimeStrategy.hpp
@@ -17,6 +17,9 @@ class RuntimeStrategy {
 
 	virtual auto getDispatcher() -> Dispatcher& = 0;
 
+ protected:
+	virtual ~RuntimeStrategy() = default;
+
  public:
 	virtual auto getStats() -> std::shared_ptr<RuntimeStrategyStats> = 0;
 };
diff --git a/emper/RuntimeStrategyFactory.hpp b/emper/RuntimeStrategyFactory.hpp
index 3007109219296cd3202756728cda44c353c47067..1e6f37a13c39ed4604a3b7feec977e9a14c2229d 100644
--- a/emper/RuntimeStrategyFactory.hpp
+++ b/emper/RuntimeStrategyFactory.hpp
@@ -7,6 +7,9 @@
 class Runtime;
 
 class RuntimeStrategyFactory {
+ protected:
+	virtual ~RuntimeStrategyFactory() = default;
+
  public:
 	virtual auto constructRuntimeStrategy(Runtime& runtime) -> RuntimeStrategy* = 0;
 };
diff --git a/emper/Scheduler.hpp b/emper/Scheduler.hpp
index 9bcb9e4d6663852329f47948e6527aa4f4c4127b..85fb8d80dc8b67d1b586c68764991221da6d67e6 100644
--- a/emper/Scheduler.hpp
+++ b/emper/Scheduler.hpp
@@ -25,6 +25,8 @@ class Scheduler : public Logger<LogSubsystem::SCHED> {
 	Runtime& runtime;
 	Scheduler(Runtime& runtime);
 
+	virtual ~Scheduler() = default;
+
 	void addNewWorkerHook(const std::function<void(void)>& hook);
 
 	static inline auto getAffinityBuffer(Fiber& fiber) -> workeraffinity_t* {
diff --git a/emper/strategies/AbstractWorkStealingStrategy.hpp b/emper/strategies/AbstractWorkStealingStrategy.hpp
index fbaf054f3d4e8ea4b0a24dc4b30d169335ed3840..090b7ffb48706e2428bd0ac1a1e62e1cfd25122d 100644
--- a/emper/strategies/AbstractWorkStealingStrategy.hpp
+++ b/emper/strategies/AbstractWorkStealingStrategy.hpp
@@ -19,6 +19,9 @@ class AbstractWorkStealingStrategy : public RuntimeStrategy {
 	std::atomic<std::uint64_t> nextFiberStolen;
 	std::atomic<std::uint64_t> nextFiberFromAnywhereQueue;
 
+ protected:
+	~AbstractWorkStealingStrategy() override = default;
+
 	friend AbstractWorkStealingScheduler;
 	friend AbstractWorkStealingStats;
 };
diff --git a/emper/strategies/ws/WsStrategy.hpp b/emper/strategies/ws/WsStrategy.hpp
index 1bba10249436b20c52c601ba088bb26ee956a0e4..1a8d701a8a15f65bd7483c14498d9f3e08c72dff 100644
--- a/emper/strategies/ws/WsStrategy.hpp
+++ b/emper/strategies/ws/WsStrategy.hpp
@@ -13,13 +13,15 @@ class RuntimeStrategyStats;
 class WsStrategyFactory;
 class WsStrategyStats;
 
-class WsStrategy : public AbstractWorkStealingStrategy {
+class WsStrategy final : public AbstractWorkStealingStrategy {
  private:
 	WsScheduler scheduler;
 	WsDispatcher dispatcher;
 
 	WsStrategy(Runtime& runtime);
 
+	~WsStrategy() override = default;
+
 	auto getScheduler() -> WsScheduler& override;
 
 	auto getDispatcher() -> WsDispatcher& override;
diff --git a/meson.build b/meson.build
index 4606577258f53fd58731e541888a27eef688d4ae..7c5fee1e2cfcea5099ca0c61b0fd9ec502c42bc1 100644
--- a/meson.build
+++ b/meson.build
@@ -8,9 +8,6 @@ project('EMPER', 'c', 'cpp',
 		  'werror=true',
 		])
 
-# TODO: Re-enable that warning.
-add_project_arguments('-Wno-non-virtual-dtor', language: 'cpp')
-
 thread_dep = dependency('threads')
 uring_dep = dependency('liburing')
 emper_dependencies = [thread_dep, uring_dep]