Skip to content

tensor_inv wrapper ctor lacks rank gate (rank-3 silently constructible) #292

Description

@petlenz

Context

Surfaced in code review of PR #291 (#287 rank-4 inv-diff scalar-arg case).

The inv() factory in include/numsim_cas/tensor/tensor_functions.h:467 rejects rank ≠ 2 and rank ≠ 4 at construction with a clear invalid_expression_error. The tensor_inv wrapper constructor in include/numsim_cas/tensor/wrappers/tensor_inv.h:14-85, however, runs no rank check itself — it only branches on r == 2 / r == 4 for space propagation and silently leaves the space empty otherwise.

So make_expression<tensor_inv>(rank3_tensor) (bypassing the factory) builds a "valid-looking" rank-3 tensor_inv AST node that misbehaves anywhere downstream:

  • Visitors (differentiation, evaluator, printer) hit the wrong rank path.
  • The space-propagation block silently produces a rank-3 tensor_inv with no space tag and the wrong algebraic implications.
  • The diff visitor in Rank-4 tensor_inv in scalar-arg diff visitor (#287) #291 has a belt-and-braces throw that catches it for that particular visitor — but every other consumer is silently broken.

Fix

Mirror the factory's rank gate in the wrapper ctor:

template <typename Expr>
explicit tensor_inv(Expr &&_expr)
    : base(std::forward<Expr>(_expr), _expr.get().dim(), _expr.get().rank()) {
  if (this->rank() != 2 && this->rank() != 4) {
    throw invalid_expression_error(
        "tensor_inv: only rank-2 and rank-4 tensors are supported (got rank " +
        std::to_string(this->rank()) + ")");
  }
  // ... existing space and algebra-assumption propagation
}

Throws are the codebase convention here (matches inv() factory's invalid_expression_error style) and — unlike assert — fires in Release builds too.

Test plan

  • Lock-in test: EXPECT_THROW(make_expression<tensor_inv>(rank3_T), invalid_expression_error).
  • The belt-and-braces throw in tensor_differentiation_wrt_scalar.cpp:248-253 becomes unreachable; either:
    (a) Keep it with a comment "should be caught by wrapper ctor; double-guard for direct base-class construction".
    (b) Replace with assert(r == 2 || r == 4).
  • The corresponding test TensorDiffWrtScalarTest.InvRank3ThrowsNotImplemented becomes redundant; can either be kept as a defense-in-depth witness or replaced by a wrapper-level direct test.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions