Synthesized vs Inherited Attributes in Attribute Grammars (with Examples)

Synthesized vs Inherited Attributes in Attribute Grammars (with Examples)

Verified Sources
Nov 4, 2026

In compiler construction, attribute grammars extend a context-free grammar by attaching attributes to grammar symbols and defining rules that compute those attributes. Two common categories are synthesized attributes and inherited attributes.

A helpful mental model is the parse tree direction of information flow:

  • Synthesized attributes: children compute first; parent “collects” results.
  • Inherited attributes: parent (or left sibling) supplies context to children before they compute.

A classic example is type checking: bottom-up computation can synthesize an expression’s type, while context-sensitive information (e.g., “expected type” or symbol table scope) is often inherited.


Visualizing information flow

⚠️ Note: Although attribute grammars can mix both kinds, the key distinction is direction: synthesized values flow upward; inherited values flow downward (often top-down).

Attribute Grammars: Synthesized and Inherited Attributes (overview)

Core definitions (what to remember)

  • Synthesized attributes are typically evaluated after children, because they depend on child values.
  • Inherited attributes are evaluated before the dependent child, because the child depends on context from above.

In notation used in compiler textbooks, attributes are associated with symbols in productions, and semantic rules are written to compute them. A production like:

A→B CA \rightarrow B\ C

may have:

  • a synthesized attribute on AA computed from attributes of BB and CC, e.g., A.type=combine(B.type,C.type)A.type = combine(B.type, C.type)
  • inherited attributes on BB or CC computed from AA or from each other, e.g., B.env=A.envB.env = A.env

Pro Tip: Decide direction first

When designing semantic rules, ask: does this value come from children (synthesized) or from surrounding context like scope/expected type (inherited)?

How to determine whether an attribute is synthesized or inherited

  1. 1
    Step 1

    Choose a production such as A→B CA \rightarrow B\ C and identify which symbols you want to attach meaning to.

  2. 2
    Step 2

    If X.attrX.attr is computed using only child information (values attached to BB and/or CC), it is synthesized into XX. If it uses parent context (values attached to AA) or is computed in an order before visiting children, it is inherited by the children.

  3. 3
    Step 3

    Semantic rules like A.attr=f(B.attr,C.attr)A.attr = f(B.attr, C.attr) indicate synthesized attributes for AA. Rules like B.attr=g(A.attr)B.attr = g(A.attr) indicate inherited attributes for BB.

  4. 4
    Step 4

    Synthesized-only definitions are typically easier to evaluate bottom-up; inherited dependencies often require top-down (or carefully ordered) evaluation.

Example 1: Synthesized attribute for expression type (bottom-up)

Assume a simple language fragment with expressions:

  • integer literals
  • addition: ErightarrowE+EE \\rightarrow E + E
  • multiplication: ErightarrowE∗EE \\rightarrow E * E

We want an attribute E.typeE.type that synthesizes the type of each expression node.

Grammar sketch

ErightarrowidErightarrownumErightarrowE+EErightarrowE∗E\begin{aligned} E &\\rightarrow id \\ E &\\rightarrow num \\ E &\\rightarrow E + E \\ E &\\rightarrow E * E \end{aligned}

Synthesized attribute semantic rules

  • For an integer literal:
    • E.type=textIntE.type = \\text{Int}
  • For addition:
    • E.type=textIntE.type = \\text{Int} if both children types are Int; otherwise error.
    • Formally: if E1.type=textIntE_1.type = \\text{Int} and E2.type=textIntE_2.type = \\text{Int} then E.type=textIntE.type = \\text{Int}.
  • For multiplication similarly.

This is synthesized because the parent’s type depends on child types: E.type=f(E1.type,E2.type)E.type = f(E_1.type, E_2.type).

Mini parse-tree walkthrough

For input: 3 + 4 * 5, the parse tree (simplified) nests multiplications inside additions. Bottom-up:

  1. 3 synthesizes type=Inttype=Int
  2. 4 synthesizes type=Inttype=Int, 5 synthesizes type=Inttype=Int, so 4*5 synthesizes type=Inttype=Int
  3. Now 3 + (4*5) synthesizes type=Inttype=Int

No external context is needed if the symbol table for id is also available via a synthesized lookup (or via a separate attribute approach).

Example 2: Inherited attribute for scope/environment (top-down)

Consider identifiers and scoping rules, like blocks:

StmtrightarrowLLrightarrowL ; Stmt,∣,StmtStmtrightarrowid=ExprExprrightarrowid,∣,num\begin{aligned} Stmt &\\rightarrow \\{ L \\} \\ L &\\rightarrow L\ ;\ Stmt \\,\\ |\\ \\, Stmt \\ Stmt &\\rightarrow id = Expr \\ Expr &\\rightarrow id \\, | \\, num \end{aligned}

Suppose we attach an environment attribute env to expressions and statements so that id can be resolved.

We model env as inherited: the enclosing block provides it to its contents.

  • Let Block.env be built at the block level.
  • Then each child statement receives Stmt.env = Block.env.

Inherited semantic rules (conceptual)

  • If a block creates a new scope, it extends the parent environment:
    • textBlock.env=extend(textParent.env,textBlock.locals)\\text{Block.env} = extend(\\text{Parent.env}, \\text{Block.locals})
  • Then for child statements:
    • Stmt.env=Block.envStmt.env = Block.env
  • Finally, inside an expression with an identifier:
    • Expr.type=lookup(Expr.id,Expr.env)Expr.type = lookup(Expr.id, Expr.env)

Because Expr.type depends on Expr.env provided from above, env is inherited.

Common pitfall: mixing up which node owns the computation

If you write a rule where a child depends on a parent value, that parent-to-child attribute is inherited. Don’t accidentally label it synthesized just because it ultimately influences a parent result.

Example 3: Mixed attributes (both synthesized and inherited)

Type checking with operator typing often uses both:

  • Inherited attribute: expected type passed from context (e.g., assignment).
  • Synthesized attribute: computed actual type of expressions.

For assignment:

Stmtrightarrowid=ExprStmt \\rightarrow id = Expr
  • Let Stmt.expected be inherited from assignment context (or be the type of id).
  • Alternatively, you can treat the LHS type as inherited into RHS.

One consistent design

  • Use an inherited attribute for the expected type:
    • Expr.expected=Stmt.lhsTypeExpr.expected = Stmt.lhsType
  • Use a synthesized attribute for the actual type:
    • Expr.type=typeOf(Expr...)Expr.type = typeOf(Expr...)
  • Then check compatibility:
    • If Expr.typeExpr.type is compatible with Expr.expectedExpr.expected, accept; else error.

This pattern is common in semantic analysis because it naturally separates:

  • what information the context provides (inherited)
  • what the subtree computes (synthesized)

Directionality summary: synthesized vs inherited

How information typically flows in attribute grammar semantic rules.

Typical evaluation flow in practice

Propagate inherited context

Top-down phase

Scopes, expected types, and environment mappings flow into children."

Compute synthesized results

Bottom-up phase

Types/values are computed from children and returned to parents."

Validate mixed dependencies

Constraint checking

Compatibility checks combine inherited expectations with synthesized outcomes."

FAQ: tricky points

Knowledge Check

Question 1 of 4
Q1Single choice

In an attribute grammar, E.typeE.type is computed using only the types of the two child expressions in E→E+EE \rightarrow E + E. What kind of attribute is E.typeE.type?