Skip to content

IOperation to Expression Mapping

Reference table mapping Roslyn IOperation types to System.Linq.Expressions.Expression factory methods, as implemented in ExpressionTreeEmitter.

Roslyn version: Microsoft.CodeAnalysis.CSharp 5.0.0 Target: System.Linq.Expressions (netstandard2.0+)

Overview

ExpressionTreeEmitter walks the Roslyn IOperation tree for a member body and emits imperative C# code that builds the equivalent Expression<TDelegate> using factory methods. It receives the original syntax and original semantic model -- no syntax preprocessing is required. Transparent syntax wrappers (checked(), unchecked(), parenthesized, null-forgiving !) are unwrapped before GetOperation is called.

Legend

StatusMeaning
ImplementedHandled in ExpressionTreeEmitter.EmitOperation() dispatch
Not yet implementedHas a clear path to implementation; currently falls through to EmitUnsupported()
N/ANo Expression.* equivalent exists; not applicable to expression trees

Unrecognized operations fall through to EmitUnsupported() which emits Expression.Default(typeof(T)) and reports diagnostic EXP0008.


Core Expression Operations

IOperationExpression FactoryStatusNotes
ILiteralOperationExpression.Constant(value, typeof(T))ImplementedSpecial formatting for float.NaN, double.PositiveInfinity, char/string escaping, numeric suffixes.
IParameterReferenceOperationExpression.Parameter(typeof(T), "name")ImplementedLooks up pre-created ParameterExpression from _symbolToVar cache.
IInstanceReferenceOperationParameterExpression for @thisImplementedReturns the _thisVarName parameter.
ILocalReferenceOperationExpression.Variable(typeof(T), "name")ImplementedLooks up from _localToVar cache, populated by EmitBlock variable declarations.
IParenthesizedOperation(transparent)ImplementedUnwrapped: recursively emits the inner Operand.

Member Access

IOperationExpression FactoryStatusNotes
IPropertyReferenceOperationExpression.Property(instance, PropertyInfo)ImplementedCached via ReflectionFieldCache.EnsurePropertyInfo().
IFieldReferenceOperationExpression.Field(instance, FieldInfo)ImplementedCached via ReflectionFieldCache.EnsureFieldInfo().
IArrayElementReferenceOperationExpression.ArrayIndex(array, index) / Expression.ArrayAccess(array, indices)ImplementedSingle index uses ArrayIndex; multiple uses ArrayAccess.
IEventReferenceOperation--N/AEvents cannot be represented in expression trees.

Invocation and Creation

IOperationExpression FactoryStatusNotes
IInvocationOperationExpression.Call(instance, MethodInfo, args)ImplementedStatic/instance dispatch. Generic methods resolved via MakeGenericMethod(). Enum method expansion to ternary chains.
IObjectCreationOperationExpression.New(ConstructorInfo, args)ImplementedWith member initializer: Expression.MemberInit. With collection initializer: Expression.ListInit via Expression.ElementInit.
IAnonymousObjectCreationOperationExpression.New(ctor, args, members)Implemented (interceptor path)Uses inline runtime reflection via generic type parameters (e.g., typeof(TResult).GetConstructors()[0]). Nested anonymous types fall back to EXP0008.
IDelegateCreationOperation(transparent)ImplementedEmits its Target operation directly.
IArrayCreationOperationExpression.NewArrayInit / Expression.NewArrayBoundsImplementedWith initializer: NewArrayInit; without: NewArrayBounds.

Operators

IOperationExpression FactoryStatusNotes
IBinaryOperationExpression.MakeBinary(ExpressionType, left, right)ImplementedMaps BinaryOperatorKind to ExpressionType. Supports checked variants (AddChecked, SubtractChecked, MultiplyChecked) when IsChecked is true. Unrecognized operators report EXP0009 diagnostic.
IUnaryOperationExpression.MakeUnary(ExpressionType, operand, type)ImplementedSupports NegateChecked when IsChecked is true. Unrecognized operators report EXP0009.
IIncrementOrDecrementOperationExpression.Assign(target, MakeBinary(Add/Sub, target, 1))Implemented++/-- emitted as assign + binary. Supports checked variants.
ICompoundAssignmentOperationExpression.Assign(target, MakeBinary(op, target, value))Implemented+=, -=, etc. emitted as assign + binary. Supports checked variants.

Binary Operator Kind Mapping

BinaryOperatorKindExpressionTypeChecked variant
AddAddAddChecked
SubtractSubtractSubtractChecked
MultiplyMultiplyMultiplyChecked
DivideDivide--
RemainderModulo--
LeftShiftLeftShift--
RightShiftRightShift--
And (bitwise)And--
Or (bitwise)Or--
ExclusiveOrExclusiveOr--
ConditionalAnd (&&)AndAlso--
ConditionalOr (||)OrElse--
EqualsEqual--
NotEqualsNotEqual--
LessThanLessThan--
LessThanOrEqualLessThanOrEqual--
GreaterThanGreaterThan--
GreaterThanOrEqualGreaterThanOrEqual--
(unrecognized)--Reports EXP0009 diagnostic

Unary Operator Kind Mapping

UnaryOperatorKindExpressionTypeChecked variant
BitwiseNegation (~)OnesComplement--
Not (!)Not--
Plus (+)UnaryPlus--
Minus (-)NegateNegateChecked
(unrecognized)--Reports EXP0009 diagnostic

Type Operations

IOperationExpression FactoryStatusNotes
IConversionOperationExpression.Convert(expr, typeof(T))ImplementedIdentity conversions skipped. ConvertChecked emitted when IsChecked is true. User-defined operators pass MethodInfo.
ITypeOfOperationExpression.Constant(typeof(T), typeof(Type))Implemented
IDefaultValueOperationExpression.Default(typeof(T))Implemented
IIsTypeOperationExpression.TypeIs(expr, typeof(T))ImplementedSimple is Type check.

Conditional and Null Operations

IOperationExpression FactoryStatusNotes
IConditionalOperationExpression.Condition / Expression.IfThen / Expression.IfThenElseImplementedTernary ? : uses Condition. Statement-form if/else uses IfThen/IfThenElse (void-typed).
IConditionalAccessOperationExpression.Condition(notNullCheck, whenNotNull, default)Implemented?. operator with receiver stack for chained access. Handles Nullable<T> .Value injection.
IConditionalAccessInstanceOperation(resolved to receiver)ImplementedPops from receiver stack during conditional access processing.
ICoalesceOperationExpression.Coalesce(left, right)Implemented?? operator.

Pattern Matching

IOperationExpression FactoryStatusNotes
IIsPatternOperationDispatches to pattern-specific emissionImplementedFull pattern matching dispatch.
IConstantPatternOperationExpression.Equal(expr, Expression.Constant(value))Implemented
ITypePatternOperationExpression.TypeIs(expr, typeof(T))Implemented
IDeclarationPatternOperationExpression.TypeIs / cast substitution in switch armsImplementedNamed declarations in switch arms substitute with Expression.Convert.
IRelationalPatternOperationExpression.MakeBinary(op, expr, constant)ImplementedUnrecognized operators report EXP0009.
INegatedPatternOperationExpression.Not(innerPatternExpr)Implemented
IBinaryPatternOperationExpression.AndAlso / Expression.OrElseImplemented
IRecursivePatternOperationNull-check + type guard + property/positional conditionsImplementedPositional patterns resolve via Deconstruct parameters or ItemN fields.
IDiscardPatternOperationExpression.Constant(true)Implemented
IListPatternOperationCount/Length check + indexed element pattern checksImplementedFixed-length and slice patterns. Requires Count/Length property and indexer on operand type.
ISlicePatternOperation(handled within list pattern)ImplementedSlice (..) adjusts index calculations for elements after the slice.

Switch Expressions

IOperationExpression FactoryStatusNotes
ISwitchExpressionOperationNested Expression.Condition chainImplementedArms processed in reverse. Discard arm becomes innermost fallback. Declaration patterns in arms substitute with cast.

Tuple Operations

IOperationExpression FactoryStatusNotes
ITupleOperationExpression.New(ValueTuple<...> ctor, elements)ImplementedHandles 1-7 elements directly. 8+ elements use nested ValueTuple for Rest argument.
ITupleBinaryOperationElement-wise Equal + AndAlso / NotImplemented(a, b) == (c, d) becomes AndAlso(Equal(a.Item1, c.Item1), Equal(a.Item2, c.Item2)). != wraps in Not.

Index and Range

IOperationExpression FactoryStatusNotes
IUnaryOperation (Hat)Expression.New(Index ctor, value, true)Implemented^n becomes new Index(n, fromEnd: true).
IRangeOperationExpression.New(Range ctor, start, end)Implementeda..b becomes new Range(start, end). Implicit start/end produce new Index(0, false) / new Index(0, true). Operand int to Index conversions handled by EmitConversion.

Collection Expressions

IOperationExpression FactoryStatusNotes
ICollectionExpressionOperationExpression.NewArrayInit / Expression.ListInitImplementedC# 12 collection expressions. Without spread: arrays use NewArrayInit, collections use ListInit with Add. With spread: segments are concatenated via Enumerable.Concat<T> and materialized via Enumerable.ToArray<T> / ToList<T>.
ISpreadOperation(handled within collection expression)ImplementedSpread operand emitted directly as an IEnumerable<T> segment.

String Interpolation

IOperationExpression FactoryStatusNotes
IInterpolatedStringOperationExpression.Call(string.Concat, ...)ImplementedParts flattened to string expressions, non-string parts wrapped in .ToString(). Format specifiers use ToString(format). Left-fold reduction via string.Concat(string, string). Alignment specifiers fall through to EmitUnsupported.

Block Body Operations

IOperationExpression FactoryStatusNotes
IBlockOperationExpression.Block(variables, statements)ImplementedLocal variable declarations, assignments, returns, expression statements. Requires AllowBlockBody = true.
IReturnOperation(result expression)ImplementedExtracts the returned value as the block's result.
IExpressionStatementOperation(transparent)ImplementedUnwraps to inner operation.
ISimpleAssignmentOperationExpression.Assign(target, value)ImplementedStandalone assignment in block bodies.

Loop Operations

IOperationExpression FactoryStatusNotes
IForEachLoopOperationExpression.Loop with enumerator patternImplementedGetEnumerator/MoveNext/Current pattern. ConvertLoopsToLinq transformer rewrites to LINQ for providers that cannot handle LoopExpression.
IForLoopOperationExpression.Loop with init/condition/incrementImplemented
IWhileLoopOperationExpression.Loop with conditionImplementedSupports both while (condition-at-top) and do-while (condition-at-bottom).

Nested Lambda and Delegate Operations

IOperationExpression FactoryStatusNotes
IAnonymousFunctionOperationExpression.Lambda<TDelegate>(body, params)ImplementedNested lambdas with scoped parameters.
IDelegateCreationOperation(transparent)ImplementedEmits its Target operation directly.

Not Yet Implemented

IOperationTarget Expression FactoryNotes
IThrowOperationExpression.Throw(expr, typeof(T))throw expressions. Expression.Throw exists but not all LINQ providers support it. Detected early by block body validation (EXP0006).

Not Applicable to Expression Trees

These operations are rejected early by block body validation (EXP0005/EXP0006) when inside [Expressive(AllowBlockBody = true)] members.

IOperationReason
IEventReferenceOperationEvents cannot appear in expression trees
ICoalesceAssignmentOperation??= is an assignment
IAddressOfOperationPointers are not supported in expression trees
IDynamicInvocationOperationDynamic dispatch has no expression tree equivalent
IDynamicMemberReferenceOperationDynamic dispatch has no expression tree equivalent
IDynamicIndexerAccessOperationDynamic dispatch has no expression tree equivalent
IDynamicObjectCreationOperationDynamic dispatch has no expression tree equivalent
IFunctionPointerInvocationOperationFunction pointers are not supported in expression trees
IAwaitOperationawait has no Expression.* equivalent
IDeconstructionAssignmentOperation(a, b) = ... is a destructuring assignment
ITryOperationtry/catch/finally has no expression tree equivalent
IUsingOperationusing/dispose pattern not representable
ILockOperationlock has no expression tree equivalent

Limitations and Known Issues

1. No Explicit Nullable Lifting

The liftToNull parameter in Expression.MakeBinary() is always hardcoded to false. The emitter does not inspect IBinaryOperation.IsLifted. In practice, the .NET expression tree runtime infers lifting from operand types, so this works for standard operators. It may produce incorrect behavior for user-defined operators on nullable types where liftToNull semantics differ.

2. IOperation Null Fallback

If SemanticModel.GetOperation() returns null for the body syntax (after unwrapping transparent syntax), the emitter reports EXP0008 and produces Expression.Default(typeof(ReturnType)) as the lambda body.

Released under the MIT License.