module volt.semantic.extyper

Code Map

module volt.semantic.extyper;


enum Parent
{
	NA,
	Call,
	Identifier,
	AssignTarget,
	AssignSource,
}

enum StoreSource
{
	Instance,
	Identifier,
	StaticPostfix,
}

class GotoReplacer : NullVisitor
{
public:
	exp: ir.Exp;
	originalExp: ir.Exp;
	ctx: Context;
	ss: ir.SwitchStatement;
	isArraySwitch: bool;


public:
	this(ctx: Context, ss: ir.SwitchStatement, isArraySwitch: bool) { }
	fn enter(gs: ir.GotoStatement) Status { }
	fn enter(bs: ir.BlockStatement) Status { }
}

//! If type casting were to be strict, type T could only go to type T
//! without an explicit cast. Implicit casts are places where the language
//! deems automatic conversion safe enough to insert casts for the user.
class ExTyper : NullVisitor, Pass
{
public:
	ctx: Context;


public:
	this(lp: LanguagePass) { }
	fn transform(m: ir.Module) { }
	fn close() { }
	//! For out of band checking of Variables.
	fn resolve(current: ir.Scope, v: ir.Variable) { }
	//! For out of band checking of Functions.
	fn resolve(current: ir.Scope, func: ir.Function) { }
	fn transform(current: ir.Scope, ti: ir.TemplateInstance) { }
	fn transform(current: ir.Scope, a: ir.Attribute) { }
	fn transform(current: ir.Scope, ed: ir.EnumDeclaration) { }
	fn resolve(ed: ir.EnumDeclaration, prevExp: ir.Exp) { }
	fn enter(tlb: ir.TopLevelBlock) Status { }
	fn enter(m: ir.Module) Status { }
	fn leave(m: ir.Module) Status { }
	fn enter(a: ir.Alias) Status { }
	fn enter(s: ir.Struct) Status { }
	fn leave(s: ir.Struct) Status { }
	fn enter(i: ir._Interface) Status { }
	fn leave(i: ir._Interface) Status { }
	fn enter(u: ir.Union) Status { }
	fn leave(u: ir.Union) Status { }
	fn enter(c: ir.Class) Status { }
	fn leave(c: ir.Class) Status { }
	fn enter(e: ir.Enum) Status { }
	fn leave(e: ir.Enum) Status { }
	fn enter(ed: ir.EnumDeclaration) Status { }
	fn enter(v: ir.Variable) Status { }
	fn enter(func: ir.Function) Status { }
	fn enter(n: ir.AssertStatement) Status { }
	fn enter(ti: ir.TemplateInstance) Status { }
	fn leave(ti: ir.TemplateInstance) Status { }
	fn leave(n: ir.Function) Status { }
	fn enter(n: ir.FunctionParam) Status { }
	fn leave(n: ir.FunctionParam) Status { }
	fn enter(n: ir.TypeOf) Status { }
	fn leave(n: ir.TypeOf) Status { }
	fn enter(n: ir.IfStatement) Status { }
	fn leave(n: ir.IfStatement) Status { }
	fn enter(n: ir.DoStatement) Status { }
	fn leave(n: ir.DoStatement) Status { }
	fn enter(n: ir.ForStatement) Status { }
	fn leave(n: ir.ForStatement) Status { }
	fn enter(n: ir.TryStatement) Status { }
	fn leave(n: ir.TryStatement) Status { }
	fn enter(n: ir.ExpStatement) Status { }
	fn leave(n: ir.ExpStatement) Status { }
	fn enter(n: ir.WithStatement) Status { }
	fn leave(n: ir.WithStatement) Status { }
	fn enter(n: ir.GotoStatement) Status { }
	fn leave(n: ir.GotoStatement) Status { }
	fn enter(n: ir.ThrowStatement) Status { }
	fn leave(n: ir.ThrowStatement) Status { }
	fn enter(n: ir.BlockStatement) Status { }
	fn leave(n: ir.BlockStatement) Status { }
	fn enter(n: ir.WhileStatement) Status { }
	fn leave(n: ir.WhileStatement) Status { }
	fn enter(n: ir.SwitchStatement) Status { }
	fn leave(n: ir.SwitchStatement) Status { }
	fn leave(n: ir.AssertStatement) Status { }
	fn enter(n: ir.ReturnStatement) Status { }
	fn leave(n: ir.ReturnStatement) Status { }
	fn enter(n: ir.ForeachStatement) Status { }
	fn leave(n: ir.ForeachStatement) Status { }
	fn visit(n: ir.BreakStatement) Status { }
	fn visit(n: ir.ContinueStatement) Status { }
	fn enter(exp: ir.Exp, ir.IsExp) Status { }
	fn leave(exp: ir.Exp, ir.IsExp) Status { }
	fn enter(exp: ir.Exp, ir.BinOp) Status { }
	fn leave(exp: ir.Exp, ir.BinOp) Status { }
	fn enter(exp: ir.Exp, ir.Unary) Status { }
	fn leave(exp: ir.Exp, ir.Unary) Status { }
	fn enter(exp: ir.Exp, ir.Typeid) Status { }
	fn leave(exp: ir.Exp, ir.Typeid) Status { }
	fn enter(exp: ir.Exp, ir.Postfix) Status { }
	fn leave(exp: ir.Exp, ir.Postfix) Status { }
	fn enter(exp: ir.Exp, ir.Ternary) Status { }
	fn leave(exp: ir.Exp, ir.Ternary) Status { }
	fn enter(exp: ir.Exp, ir.TypeExp) Status { }
	fn leave(exp: ir.Exp, ir.TypeExp) Status { }
	fn enter(exp: ir.Exp, ir.VaArgExp) Status { }
	fn leave(exp: ir.Exp, ir.VaArgExp) Status { }
	fn enter(exp: ir.Exp, ir.Constant) Status { }
	fn leave(exp: ir.Exp, ir.Constant) Status { }
	fn enter(exp: ir.Exp, ir.AssocArray) Status { }
	fn leave(exp: ir.Exp, ir.AssocArray) Status { }
	fn enter(exp: ir.Exp, ir.ArrayLiteral) Status { }
	fn leave(exp: ir.Exp, ir.ArrayLiteral) Status { }
	fn enter(exp: ir.Exp, ir.StructLiteral) Status { }
	fn leave(exp: ir.Exp, ir.StructLiteral) Status { }
	fn visit(exp: ir.Exp, ir.TokenExp) Status { }
	fn visit(exp: ir.Exp, ir.ExpReference) Status { }
	fn visit(exp: ir.Exp, ir.IdentifierExp) Status { }
}

struct ArrayCase
{
public:
	originalExps: ir.Exp[];
	_case: ir.SwitchCase;
	lastIf: ir.IfStatement;
	lastI: size_t;
}

//! Does what the name implies.
fn appendDefaultArguments(ctx: Context, loc: ir.Location, arguments: ir.Exp[], func: ir.Function) { }
fn classifyRelationship(child: ir.Exp, parent: ir.Exp) Parent { }
fn handleStore(ctx: Context, ident: string, exp: ir.Exp, store: ir.Store, child: ir.Exp, parent: Parent, via: StoreSource) ir.Type { }
fn handleFunctionStore(ctx: Context, ident: string, exp: ir.Exp, store: ir.Store, child: ir.Exp, parent: Parent, via: StoreSource) ir.Type { }
fn handleValueStore(ctx: Context, ident: string, exp: ir.Exp, store: ir.Store, child: ir.Exp, via: StoreSource) ir.Type { }
fn handleFunctionParamStore(ctx: Context, ident: string, exp: ir.Exp, store: ir.Store, child: ir.Exp, parent: Parent, via: StoreSource) ir.Type { }
fn handleEnumDeclarationStore(ctx: Context, ident: string, exp: ir.Exp, store: ir.Store, child: ir.Exp, parent: Parent, via: StoreSource) ir.Type { }
fn handleTypeStore(ctx: Context, ident: string, exp: ir.Exp, store: ir.Store, child: ir.Exp, parent: Parent, via: StoreSource) ir.Type { }
fn handleScopeStore(ctx: Context, ident: string, exp: ir.Exp, store: ir.Store, child: ir.Exp, parent: Parent, via: StoreSource) ir.Type { }
//! If qname has a child of name leaf, returns an expression looking it
//! up. Otherwise, null is returned.
fn withLookup(ctx: Context, withExp: ir.Exp, leaf: string) ir.Exp { }
//! Replace IdentifierExps with another exp, often ExpReference.
fn extypeIdentifierExp(ctx: Context, e: ir.Exp, parent: Parent) ir.Type { }
fn replaceAAPostfixesIfNeeded(ctx: Context, exp: ir.Exp, postfix: ir.Postfix) ir.Type { }
fn handleArgumentLabelsIfNeeded(ctx: Context, arguments: ir.Exp[], argumentLabels: string[], argumentTags: ir.Postfix.TagKind[], func: ir.Function, exp: ir.Exp) { }
//! Turns identifier postfixes into CreateDelegates, and resolves property
//! function calls in postfixes, type safe varargs, and explicit
//! constructor calls.
fn extypePostfixLeave(ctx: Context, exp: ir.Exp, postfix: ir.Postfix, parent: Parent) ir.Type { }
fn extypePostfixCall(ctx: Context, exp: ir.Exp, postfix: ir.Postfix) { }
fn checkCallRefOutTags(ctx: Context, ftype: ir.CallableType, arguments: ir.Exp[], argumentTags: ir.Postfix.TagKind[]) { }
//! This function acts as a extyperExpReference function would do, but it
//! also takes a extra type context which is used for the cases when
//! looking up Member variables via Types.
fn replaceExpReferenceIfNeeded(ctx: Context, exp: ir.Exp, eRef: ir.ExpReference) { }
//! Turn identifier postfixes into .ident.
fn consumeIdentsIfScopesOrTypes(ctx: Context, postfixes: ir.Postfix[], exp: ir.Exp, parent: Parent) ir.Type { }
fn extypePostfixIndex(ctx: Context, exp: ir.Exp, postfix: ir.Postfix) { }
//! This function will check for ufcs functions on a Identifier postfix, it
//! assumes we have already looked for a field and not found anything.
fn postfixIdentifierUFCS(ctx: Context, exp: ir.Exp, postfix: ir.Postfix, parent: Parent) { }
fn builtInField(ctx: Context, exp: ir.Exp, child: ir.Exp, type: ir.Type, field: string) ir.Type { }
//! Rewrite exp if the store contains any property functions, works for
//! both PostfixExp and IdentifierExp.
fn rewriteIfPropertyStore(exp: ir.Exp, child: ir.Exp, name: string, parent: Parent, funcs: ir.Function[]) bool { }
//! Handling cases:
fn extypePostfixIdentifier(ctx: Context, exp: ir.Exp, postfix: ir.Postfix, parent: Parent) ir.Type { }
fn extypePostfix(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
//! Stops casting to an overloaded function name, casting from null, and
//! wires up some runtime magic needed for classes.
fn extypeUnaryCastTo(ctx: Context, exp: ir.Exp, unary: ir.Unary) { }
//! Type new expressions.
fn extypeUnaryNew(ctx: Context, exp: ir.Exp, _unary: ir.Unary) { }
//! Lower 'new foo[0 .. $]' expressions to BuiltinExps.
fn extypeUnaryDup(ctx: Context, exp: ir.Exp, _unary: ir.Unary) { }
fn extypeUnary(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
//! Everyone's favourite: integer promotion! :D! In general, converts to
//! the largest type needed in a binary expression.
fn extypeBinOp(ctx: Context, bin: ir.BinOp, lprim: ir.PrimitiveType, rprim: ir.PrimitiveType) ir.Type { }
fn rewriteCall(ctx: Context, exp: ir.Exp, func: ir.Function, args: ir.Exp[], left: ir.Exp) { }
//! Given the name of an overloaded operator, rewrite it to a function
//! call.. extypePostfixCall is called on the rewritten expression. 'left'
//! is an expression of which the function will be a member. Returns the
//! function that was called, or null if nothing was rewritten.
fn rewriteOperator(ctx: Context, exp: ir.Exp, overloadName: string, left: ir.Exp, args: ir.Exp[]) ir.Function { }
fn maybeInvertCmp(op: ir.BinOp.Op, invert: bool) ir.BinOp.Op { }
//! If the given binop is working on an aggregate that overloads that
//! operator, rewrite a call to that overload.
fn opOverloadRewrite(ctx: Context, binop: ir.BinOp, exp: ir.Exp) ir.Type { }
//! If this postfix operates on an aggregate with a postfix operator
//! overload, rewrite it.
fn opOverloadRewritePostfix(ctx: Context, pfix: ir.Postfix, exp: ir.Exp) ir.Type { }
//! If this unary is a Minus, and it's operating on an aggregate that has
//! opNeg, rewrite it.
fn opOverloadRewriteUnary(ctx: Context, unary: ir.Unary, exp: ir.Exp) ir.Type { }
fn extypeBinOpPropertyAssign(ctx: Context, binop: ir.BinOp, exp: ir.Exp) ir.Type { }
//! Ensure concatentation is sound.
fn extypeCat(ctx: Context, lexp: ir.Exp, rexp: ir.Exp, left: ir.ArrayType, right: ir.Type) { }
fn rewriteOpIndexAssign(ctx: Context, binop: ir.BinOp, exp: ir.Exp) bool { }
//! If exp is an AccessExp that is effectively const, or an AccessExp with
//! an effectively const AccessExp ancestor, return true. Otherwise, return
//! false.
fn effectiveConstAccessExp(exp: ir.Exp) bool { }
//! Handles logical operators (making a && b result in a bool), binary of
//! storage types, otherwise forwards to assign or primitive specific
//! functions.
fn extypeBinOp(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn evaluateIsExp(ctx: Context, isExp: ir.IsExp) ir.Constant { }
fn evaluateTraitsWord(ctx: Context, isExp: ir.IsExp, type: ir.Type) ir.Constant { }
fn extypeIsExp(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeTernary(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeStructLiteral(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeConstant(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeTypeExp(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeAssocArray(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeVaArgExp(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeExpReference(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeArrayLiteral(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeTypeid(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeTokenExp(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeStringImport(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeStoreExp(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeRunExp(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
//! Check the types of a composable string, error if bad.
fn extypeComposableString(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypePropertyExp(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeBuiltinExp(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeAccessExp(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeAssert(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeStatementExp(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeFunctionLiteral(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeUnionLiteral(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeClassLiteral(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extype(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeUnchecked(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type { }
fn extypeBlockStatement(ctx: Context, bs: ir.BlockStatement) { }
//! Ensure that a thrown type inherits from Throwable.
fn extypeThrowStatement(ctx: Context, n: ir.Node) { }
fn extypeTemplateInstance(ctx: Context, ti: ir.TemplateInstance) { }
//! Some extypeSwitchStatment utility functions.
fn addExp(ctx: Context, ss: ir.SwitchStatement, element: ir.Exp, exp: ir.Exp, sz: size_t, intArrayData: u32[], longArrayData: u64[]) { }
//! Some extypeSwitchStatment utility functions.
fn getExpHash(ctx: Context, ss: ir.SwitchStatement, exp: ir.Exp) u32 { }
//! Some extypeSwitchStatment utility functions.
fn replaceWithHashIfNeeded(ctx: Context, ss: ir.SwitchStatement, _case: ir.SwitchCase, arrayCases: ArrayCase[u32], i: size_t, condVar: ir.Variable, toRemove: size_t[], exp: ir.Exp) { }
//! Same as the above function, but handle the multi-case case.
fn replaceExpsWithHashIfNeeded(ctx: Context, ss: ir.SwitchStatement, _case: ir.SwitchCase, arrayCases: ArrayCase[u32], i: size_t, condVar: ir.Variable, exps: ir.Exp[]) { }
//! Ensure that a given switch statement is semantically sound. Errors on
//! bad final switches (doesn't cover all enum members, not on an enum at
//! all), and checks for doubled up cases.
fn extypeSwitchStatement(ctx: Context, n: ir.Node) { }
fn extypeConditionStatement(ctx: Context, n: ir.Node) { }
//! Merge with below function.
fn extypeForeachStatement(ctx: Context, n: ir.Node) { }
//! Process the types and expressions on a foreach. Foreaches become for
//! loops before the backend sees them, but they still need to be made
//! valid by the extyper.
fn processForeach(ctx: Context, fes: ir.ForeachStatement) { }
fn extypeWithStatement(ctx: Context, n: ir.Node) { }
fn extypeReturnStatement(ctx: Context, n: ir.Node) { }
fn extypeIfStatement(ctx: Context, n: ir.Node) { }
fn extypeForStatement(ctx: Context, n: ir.Node) { }
fn extypeWhileStatement(ctx: Context, n: ir.Node) { }
fn extypeDoStatement(ctx: Context, n: ir.Node) { }
fn extypeAssertStatement(ctx: Context, n: ir.Node) { }
fn extypeTryStatement(ctx: Context, n: ir.Node) { }
fn extypeGotoStatement(ctx: Context, n: ir.Node) { }
fn actualizeFunction(ctx: Context, func: ir.Function) { }
//! Helper function to call into the ExTyper version.
fn resolveType(lp: LanguagePass, current: ir.Scope, type: ir.Type) ir.Type { }
//! Flattens storage types, ensure that there are no unresolved
//! TypeRefences in the given type and in general ensures that the type is
//! ready to be consumed.
fn resolveType(ctx: Context, type: ir.Type) ir.Type { }
fn doResolveType(ctx: Context, type: ir.Type, ct: ir.CallableType, ctIndex: size_t) { }
fn doResolveAmbiguousArrayType(ctx: Context, type: ir.Type) { }
fn doResolveAA(ctx: Context, type: ir.Type) { }
//! Resolves an alias, either setting the myalias field or turning it into
//! a type.
fn resolveAlias(lp: LanguagePass, a: ir.Alias) { }
//! Will make sure that the Enum's type is set, and as such will resolve
//! the first member since it decides the type of the rest of the enum.
fn resolveEnum(lp: LanguagePass, e: ir.Enum) { }
//! Check that a non integral enum is valid.
fn checkNonIntegralEnum(lp: LanguagePass, e: ir.Enum) { }
//! Resolves a Variable.
fn resolveVariable(ctx: Context, v: ir.Variable) { }
fn lazyParseBlock(ctx: Context, current: ir.Scope, tokens: ir.Token[], blockStatement: ir.BlockStatement) { }
fn resolveFunction(ctx: Context, func: ir.Function) { }
fn resolveStruct(lp: LanguagePass, s: ir.Struct) { }
fn resolveUnion(lp: LanguagePass, u: ir.Union) { }
//! Check a if a given aggregate contains an erroneous default constructor
//! or destructor.
fn checkDefaultFootors(ctx: Context, agg: ir.Aggregate) { }
//! Check a given Aggregate's anonymous structs/unions (if any) for name
//! collisions.
fn checkAnonymousVariables(ctx: Context, agg: ir.Aggregate) { }
fn isInternalVariable(c: ir.Class, v: ir.Variable) bool { }
fn writeVariableAssignsIntoCtors(ctx: Context, _class: ir.Class) { }
//! Given a expression and a type, if the expression is a literal, tag it
//! (and its subexpressions) with the type.
fn tagLiteralType(exp: ir.Exp, type: ir.Type) { }
//! Given a switch statement, replace 'goto case' with an explicit jump to
//! the next case, or hash the expression if needed.
fn replaceGotoCase(ctx: Context, ss: ir.SwitchStatement, arraySwitch: bool) { }
fn appendDefaultArguments(ctx: Context, loc: ir.Location, arguments: ir.Exp[], func: ir.Function)

Does what the name implies.

Checks if func is null and is okay with more arguments the parameters.

fn withLookup(ctx: Context, withExp: ir.Exp, leaf: string) ir.Exp

If qname has a child of name leaf, returns an expression looking it up. Otherwise, null is returned.

fn extypeIdentifierExp(ctx: Context, e: ir.Exp, parent: Parent) ir.Type

Replace IdentifierExps with another exp, often ExpReference.

fn dereferenceInitialClass(postfix: ir.Postfix, type: ir.Type)

Given a.foo, if a is a pointer to a class, turn it into (*a).foo.

fn rewriteHomogenousVariadic(ctx: Context, asFunctionType: ir.CallableType, arguments: ir.Exp[])

Rewrite a call to a homogenous variadic if needed. Makes individual parameters at the end into an array.

fn extypePostfixLeave(ctx: Context, exp: ir.Exp, postfix: ir.Postfix, parent: Parent) ir.Type

Turns identifier postfixes into CreateDelegates, and resolves property function calls in postfixes, type safe varargs, and explicit constructor calls.

fn replaceExpReferenceIfNeeded(ctx: Context, exp: ir.Exp, eRef: ir.ExpReference)

This function acts as a extyperExpReference function would do, but it also takes a extra type context which is used for the cases when looking up Member variables via Types.

pkg.mod.Class.member = 4;

Even though FunctionSets might need rewriting they are not rewritten directly but instead this function is called after they have been rewritten and the ExpReference has been resolved to a single Function.

fn consumeIdentsIfScopesOrTypes(ctx: Context, postfixes: ir.Postfix[], exp: ir.Exp, parent: Parent) ir.Type

Turn identifier postfixes into .ident.

fn postfixIdentifierUFCS(ctx: Context, exp: ir.Exp, postfix: ir.Postfix, parent: Parent)

This function will check for ufcs functions on a Identifier postfix, it assumes we have already looked for a field and not found anything.

Volt does not support property ufcs functions.

fn rewriteIfPropertyStore(exp: ir.Exp, child: ir.Exp, name: string, parent: Parent, funcs: ir.Function[]) bool

Rewrite exp if the store contains any property functions, works for both PostfixExp and IdentifierExp.

Child can be null.

fn extypePostfixIdentifier(ctx: Context, exp: ir.Exp, postfix: ir.Postfix, parent: Parent) ir.Type

Handling cases:

inst.field ( Any parent ) inst.inbuilt<field/prop> ( Any parent (no set inbuilt in Volt) ) inst.prop ( Any parent ) inst.method ( Any parent but Postfix.Op.Call )

Check if there is a call on these cases.

inst.inbuilt() ( Postfix.Op.Call ) inst.method() ( Postfix.Op.Call ) inst.ufcs() ( Postfix.Op.Call )

Error otherwise.

fn extypeUnaryCastTo(ctx: Context, exp: ir.Exp, unary: ir.Unary)

Stops casting to an overloaded function name, casting from null, and wires up some runtime magic needed for classes.

fn extypeUnaryNew(ctx: Context, exp: ir.Exp, _unary: ir.Unary)

Type new expressions.

fn extypeUnaryDup(ctx: Context, exp: ir.Exp, _unary: ir.Unary)

Lower 'new foo[0 .. $]' expressions to BuiltinExps.

fn extypeBinOp(ctx: Context, bin: ir.BinOp, lprim: ir.PrimitiveType, rprim: ir.PrimitiveType) ir.Type

Everyone's favourite: integer promotion! :D! In general, converts to the largest type needed in a binary expression.

fn rewriteOperator(ctx: Context, exp: ir.Exp, overloadName: string, left: ir.Exp, args: ir.Exp[]) ir.Function

Given the name of an overloaded operator, rewrite it to a function call.. extypePostfixCall is called on the rewritten expression. 'left' is an expression of which the function will be a member. Returns the function that was called, or null if nothing was rewritten.

fn opOverloadRewrite(ctx: Context, binop: ir.BinOp, exp: ir.Exp) ir.Type

If the given binop is working on an aggregate that overloads that operator, rewrite a call to that overload.

fn opOverloadRewritePostfix(ctx: Context, pfix: ir.Postfix, exp: ir.Exp) ir.Type

If this postfix operates on an aggregate with a postfix operator overload, rewrite it.

fn opOverloadRewriteUnary(ctx: Context, unary: ir.Unary, exp: ir.Exp) ir.Type

If this unary is a Minus, and it's operating on an aggregate that has opNeg, rewrite it.

fn extypeCat(ctx: Context, lexp: ir.Exp, rexp: ir.Exp, left: ir.ArrayType, right: ir.Type)

Ensure concatentation is sound.

fn effectiveConstAccessExp(exp: ir.Exp) bool

If exp is an AccessExp that is effectively const, or an AccessExp with an effectively const AccessExp ancestor, return true. Otherwise, return false.

fn extypeBinOp(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type

Handles logical operators (making a && b result in a bool), binary of storage types, otherwise forwards to assign or primitive specific functions.

fn extypeComposableString(ctx: Context, exp: ir.Exp, parent: Parent) ir.Type

Check the types of a composable string, error if bad.

fn extypeThrowStatement(ctx: Context, n: ir.Node)

Ensure that a thrown type inherits from Throwable.

fn addExp(ctx: Context, ss: ir.SwitchStatement, element: ir.Exp, exp: ir.Exp, sz: size_t, intArrayData: u32[], longArrayData: u64[])

Some extypeSwitchStatment utility functions.

fn getExpHash(ctx: Context, ss: ir.SwitchStatement, exp: ir.Exp) u32

Some extypeSwitchStatment utility functions.

fn replaceWithHashIfNeeded(ctx: Context, ss: ir.SwitchStatement, _case: ir.SwitchCase, arrayCases: ArrayCase[u32], i: size_t, condVar: ir.Variable, toRemove: size_t[], exp: ir.Exp)

Some extypeSwitchStatment utility functions.

fn replaceExpsWithHashIfNeeded(ctx: Context, ss: ir.SwitchStatement, _case: ir.SwitchCase, arrayCases: ArrayCase[u32], i: size_t, condVar: ir.Variable, exps: ir.Exp[])

Same as the above function, but handle the multi-case case.

fn extypeSwitchStatement(ctx: Context, n: ir.Node)

Ensure that a given switch statement is semantically sound. Errors on bad final switches (doesn't cover all enum members, not on an enum at all), and checks for doubled up cases.

oldCondition is the switches condition prior to the extyper being run on it. It's a bit of a hack, but we need the unprocessed enum to evaluate final switches.

fn extypeForeachStatement(ctx: Context, n: ir.Node)

Merge with below function.

fn processForeach(ctx: Context, fes: ir.ForeachStatement)

Process the types and expressions on a foreach. Foreaches become for loops before the backend sees them, but they still need to be made valid by the extyper.

fn resolveType(lp: LanguagePass, current: ir.Scope, type: ir.Type) ir.Type

Helper function to call into the ExTyper version.

fn resolveType(ctx: Context, type: ir.Type) ir.Type

Flattens storage types, ensure that there are no unresolved TypeRefences in the given type and in general ensures that the type is ready to be consumed.

Stops when encountering the first resolved TypeReference.

fn resolveAlias(lp: LanguagePass, a: ir.Alias)

Resolves an alias, either setting the myalias field or turning it into a type.

fn resolveEnum(lp: LanguagePass, e: ir.Enum)

Will make sure that the Enum's type is set, and as such will resolve the first member since it decides the type of the rest of the enum.

fn checkNonIntegralEnum(lp: LanguagePass, e: ir.Enum)

Check that a non integral enum is valid.

A non integral enum is an enum with a base that is not integral. It is only valid if:

  • Every member of the enum has an explicit assign set. If that condition is not met, this function will generate an error.
fn resolveVariable(ctx: Context, v: ir.Variable)

Resolves a Variable.

fn checkDefaultFootors(ctx: Context, agg: ir.Aggregate)

Check a if a given aggregate contains an erroneous default constructor or destructor.

fn checkAnonymousVariables(ctx: Context, agg: ir.Aggregate)

Check a given Aggregate's anonymous structs/unions (if any) for name collisions.

fn tagLiteralType(exp: ir.Exp, type: ir.Type)

Given a expression and a type, if the expression is a literal, tag it (and its subexpressions) with the type.

fn replaceGotoCase(ctx: Context, ss: ir.SwitchStatement, arraySwitch: bool)

Given a switch statement, replace 'goto case' with an explicit jump to the next case, or hash the expression if needed.

class ExTyper : NullVisitor, Pass

If type casting were to be strict, type T could only go to type T without an explicit cast. Implicit casts are places where the language deems automatic conversion safe enough to insert casts for the user.

Thus, the primary job of extyper ('explicit typer') is to insert casts where an implicit conversion has taken place.

The second job of extyper is to make any implicit or inferred types or expressions concrete -- for example, to make const i = 2 become const int = 2.

fn resolve(current: ir.Scope, v: ir.Variable)

For out of band checking of Variables.

fn resolve(current: ir.Scope, func: ir.Function)

For out of band checking of Functions.