Definition

EXTENDS <base_fb> makes a FUNCTION_BLOCK a derived FB of a single named base FB. The derived FB:

  1. Inherits all of the base FB’s VAR / VAR_INPUT / VAR_OUTPUT / VAR_IN_OUT members.
  2. Inherits all of the base FB’s non-PRIVATE methods.
  3. May add new members and new methods.
  4. May OVERRIDE any non-FINAL inherited method with a matching signature.
  5. May call the base implementation explicitly via SUPER.<method>(...) inside an override.

Inheritance is single only — an FB extends at most one base FB. Multiple-inheritance from interfaces is available via IMPLEMENTS, which is orthogonal: an FB can both EXTENDS BaseFb and IMPLEMENTS IFoo, IBar.

Syntax

FUNCTION_BLOCK [ABSTRACT | FINAL] <derived_name> EXTENDS <base_fb> [IMPLEMENTS <iface>, ...]
    <var_blocks>
    METHOD [PUBLIC | PROTECTED] [OVERRIDE | FINAL] <method_name> [: <return_type>]
        ...
    END_METHOD
    ...
END_FUNCTION_BLOCK
  • ABSTRACT — the derived FB still cannot be instantiated (one or more methods remain abstract).
  • FINAL — no further FB can extend this one.
  • OVERRIDE — explicit acknowledgement that this method overrides a base-class method with the same signature. The IEC standard recommends but does not strictly mandate the modifier; ForgeIEC’s emitter rejects accidental shadowing (a method without OVERRIDE whose signature matches a base method) as a likely error.

Example (roadmap — not yet compilable)

FUNCTION_BLOCK SavingsAccount EXTENDS BankAccount
VAR
    interestRate : LREAL := 0.02;
END_VAR

METHOD PUBLIC AddInterest
    balance := balance * (1.0 + interestRate);   (* inherited member *)
END_METHOD

METHOD PUBLIC OVERRIDE Withdraw : BOOL
VAR_INPUT
    amount : LREAL;
END_VAR
    (* Savings: refuse to overdraft. Delegate to base for the
       balance update, then enforce the no-overdraft policy. *)
    IF balance < amount THEN
        Withdraw := FALSE;
    ELSE
        Withdraw := SUPER.Withdraw(amount := amount);
    END_IF;
END_METHOD
END_FUNCTION_BLOCK

Semantics

  • Member layout — the derived FB’s data layout starts with the base FB’s members, in their declaration order, followed by the derived FB’s own members. This guarantees that a reference to the base FB can address an instance of the derived FB without offset adjustment.
  • Method dispatch — when a derived FB overrides a base method, the override wins at every call site that addresses the derived FB instance, including calls through a base- typed or interface-typed reference. This is dynamic dispatch.
  • SUPER — only legal inside a method body. Calls the base FB’s version of a method by name, bypassing the override resolution. Useful for “do the base behaviour and add something on top” patterns.
  • Initialization — the base FB’s initial-value list runs before the derived FB’s. The derived FB cannot change the base’s initial values inline; if it needs different starting values, it must reset them in the cycle body or via an initialisation method.
  • Type compatibility — a variable of base-FB type may point at any derived-FB instance. The reverse (base instance assigned to a derived-typed variable) is illegal unless you go through an explicit downcast (a third-edition feature not yet in the keyword tables above).

IEC reference

IEC 61131-3 third edition (2013), clause 6.5.5 — “Inheritance”. The keyword EXTENDS is reserved. SUPER, OVERRIDE, FINAL, ABSTRACT are reserved (clauses 6.5.5.2 through 6.5.5.5).

matiec conformance

❌ Not supported. matiec’s grammar treats EXTENDS as an unknown identifier. The parser breaks on the FB header line. For matiec-compatible code, replace inheritance with the composition workaround in the first llm_signals block: the derived FB carries the base FB as a member and forwards calls manually. You lose dynamic dispatch and override sugar; you keep matiec compatibility.

ForgeIEC notes

  • Cycle behaviour vs OO behaviour — the implicit FB-cycle (the body that runs every task cycle when the FB is named as a step in a task’s POU list) is not a virtual method. A derived FB’s body runs in place of the base’s body; there is no SUPER-call on the implicit cycle. If you need layered cycle behaviour, declare an explicit method called e.g. Cycle and call SUPER.Cycle() from the override. The implicit cycle body is then a stub that calls THIS.Cycle().
  • C++ codegen mapping — the roadmap maps a FUNCTION_BLOCK Derived EXTENDS Base to class GenDerived : public GenBase { ... }. Override methods carry override; FINAL carries final; ABSTRACT makes the method pure virtual. Multiple-interface implementation adds further base-class entries (class GenDerived : public GenBase, public IFoo, public IBar); since interfaces are state-free, the diamond problem does not arise.
  • Migration path from composition — the workaround in the llm_signals block (composition over inheritance) is forward-compatible. Once the OOP codegen lands, you can convert each base : BaseFb; member + forwarder boilerplate to an actual EXTENDS BaseFb declaration with a mechanical refactor. Behaviour stays the same; the call sites get cleaner.

See also

  • METHOD — method declarations, including the OVERRIDE / FINAL / ABSTRACT modifiers.
  • INTERFACE — multiple-interface implementation via IMPLEMENTS, orthogonal to FB inheritance.
  • IMPLEMENTS — the keyword that pairs with EXTENDS when an FB both inherits and implements interfaces.