private void Analyze(IStatement statement, ReachabilityGraph graph, VariableScope scope) { switch (statement) { default: throw ExhaustiveMatch.Failed(statement); case IVariableDeclarationStatement stmt: { var initializer = AnalyzeAssignmentSource(stmt.Initializer, graph, scope); var variable = VariableDeclared(stmt, graph, scope); // TODO this variable's references effectively go away when it is no longer live // TODO how does the idea of in use variables work with variables? graph.Assign(variable, initializer); if (!stmt.VariableIsLiveAfter.Result) { // Variable is dead, effectively it can be removed variable?.Dead(); } } break; case IExpressionStatement stmt: Analyze(stmt.Expression, graph, scope); break; case IResultStatement exp: // TODO deal with passing the result to the block Analyze(exp.Expression, graph, scope); break; } }
public void Emit(DeclarationIL declaration, Code code) { switch (declaration) { default: throw ExhaustiveMatch.Failed(declaration); case FunctionIL function: if (function.IsExternal) { EmitExternalFunctionSignature(function, code); } else { EmitFunction(function, code); } break; case MethodDeclarationIL method: EmitMethod(method, code); break; case ConstructorIL constructor: EmitConstructor(constructor, code); break; case ClassIL type: EmitType(type, code); break; case FieldIL _: // fields are emitted as part of the type break; } }
internal void MarkReferencedObjects() { foreach (var reference in References.Where(r => !r.IsReleased)) { var effectiveAccess = reference.EffectiveAccess(); var referent = reference.Referent; switch (effectiveAccess) { default: throw ExhaustiveMatch.Failed(effectiveAccess); case Access.ReadOnly: if (reference.IsUsed) { referent.MarkReadOnly(); } else { // If not used, we still need to recurse into it. We can identify it still. referent.MarkIdentifiable(); } break; case Access.Identify: referent.MarkIdentifiable(); break; case Access.Mutable: referent.MarkMutable(); break; } } }
public void AssignFrom(StackPlace place, ReferenceCapability referenceCapability) { if (place.Graph != Graph) { throw new ArgumentException("Must be part of the same graph", nameof(place)); } switch (referenceCapability) { default: throw ExhaustiveMatch.Failed(referenceCapability); case ReferenceCapability.Owned: case ReferenceCapability.OwnedMutable: case ReferenceCapability.Isolated: case ReferenceCapability.IsolatedMutable: case ReferenceCapability.Held: case ReferenceCapability.HeldMutable: MoveFrom(place); break; case ReferenceCapability.Shared: ShareFrom(place); break; case ReferenceCapability.Borrowed: BorrowFrom(place); break; case ReferenceCapability.Identity: IdentityFrom(place); break; } }
private void ResolveReachabilityAnnotation( CodeFile file, IParameterNameSyntax syntax, FixedSet <BindingSymbol> symbols) { switch (syntax) { default: throw ExhaustiveMatch.Failed(syntax); case INamedParameterNameSyntax syn: { var referencedSymbol = symbols.OfType <VariableSymbol>().SingleOrDefault(s => s.Name == syn.Name); syn.ReferencedSymbol.Fulfill(referencedSymbol); if (referencedSymbol is null) { diagnostics.Add(NameBindingError.CouldNotBindParameterName(file, syn.Span)); } } break; case ISelfParameterNameSyntax syn: { var referencedSymbol = symbols.OfType <SelfParameterSymbol>().SingleOrDefault(); syn.ReferencedSymbol.Fulfill(referencedSymbol); if (referencedSymbol is null) { diagnostics.Add(NameBindingError.CouldNotBindParameterName(file, syn.Span)); } } break; } }
/// <summary> /// If the type has not been resolved, this resolves it. This function /// also watches for type cycles and reports an error. /// </summary> private void BuildEntitySymbol(IEntityDeclarationSyntax entity) { switch (entity) { default: throw ExhaustiveMatch.Failed(entity); case IMethodDeclarationSyntax method: BuildMethodSymbol(method); break; case IConstructorDeclarationSyntax constructor: BuildConstructorSymbol(constructor); break; case IAssociatedFunctionDeclarationSyntax associatedFunction: BuildAssociatedFunctionSymbol(associatedFunction); break; case IFieldDeclarationSyntax field: BuildFieldSymbol(field); break; case IFunctionDeclarationSyntax syn: BuildFunctionSymbol(syn); break; case IClassDeclarationSyntax syn: BuildClassSymbol(syn); break; } }
public void Example(Shape shape) { switch (shape) { default: throw ExhaustiveMatch.Failed(shape); case Square square: Console.WriteLine("Square: " + square); break; case Circle circle: Console.WriteLine("Circle: " + circle); break; //case EquilateralTriangle equilateralTriangle: // Console.WriteLine("EquilateralTriangle: " + equilateralTriangle); // break; case Triangle triangle: Console.WriteLine("Triangle: " + triangle); break; //case string s: // Console.WriteLine("string: " + s); // break; } }
private void BuildParameterSymbols( InvocableSymbol containingSymbol, IEnumerable <IConstructorParameterSyntax> parameters, IEnumerable <DataType> types) { foreach (var(param, type) in parameters.Zip(types)) { switch (param) { default: throw ExhaustiveMatch.Failed(param); case INamedParameterSyntax namedParam: { var symbol = new VariableSymbol(containingSymbol, namedParam.Name, namedParam.DeclarationNumber.Result, namedParam.IsMutableBinding, type); namedParam.Symbol.Fulfill(symbol); symbolTree.Add(symbol); } break; case IFieldParameterSyntax _: // Referenced field already assigned break; } } }
public void Disassemble(DeclarationIL declaration, AssemblyBuilder builder) { switch (declaration) { default: throw ExhaustiveMatch.Failed(declaration); case FunctionIL function: Disassemble(function, builder); break; case MethodDeclarationIL method: Disassemble(method, builder); break; case ConstructorIL constructor: Disassemble(constructor, builder); break; case ClassIL type: Disassemble(type, builder); break; case FieldIL field: Disassemble(field, builder); break; } }
private DeclarationIL Build(IDeclaration declaration, ISymbolTree symbolTree) { if (declarationsIL.TryGetValue(declaration.Symbol, out var declarationIL)) { return(declarationIL); } switch (declaration) { default: throw ExhaustiveMatch.Failed(declaration); case IFunctionDeclaration function: { var il = ilFactory.CreateGraph(function); declarationIL = new FunctionIL(false, false, function.Symbol, BuildParameters(function.Parameters), il); break; } case IAssociatedFunctionDeclaration associatedFunction: { var il = ilFactory.CreateGraph(associatedFunction); declarationIL = new FunctionIL(false, true, associatedFunction.Symbol, BuildParameters(associatedFunction.Parameters), il); break; } case IConcreteMethodDeclaration method: { var il = ilFactory.CreateGraph(method); declarationIL = new MethodDeclarationIL(method.Symbol, BuildParameter(method.SelfParameter), BuildParameters(method.Parameters), il); break; } case IAbstractMethodDeclaration method: { declarationIL = new MethodDeclarationIL(method.Symbol, BuildParameter(method.SelfParameter), BuildParameters(method.Parameters), null); break; } case IConstructorDeclaration constructor: { var il = ilFactory.CreateGraph(constructor); var parameters = BuildConstructorParameters(constructor); var fieldInitializations = BuildFieldInitializations(constructor); declarationIL = new ConstructorIL(constructor.Symbol, parameters, fieldInitializations, il); break; } case IFieldDeclaration fieldDeclaration: declarationIL = new FieldIL(fieldDeclaration.Symbol); break; case IClassDeclaration classDeclaration: declarationIL = new ClassIL(classDeclaration.Symbol, BuildClassMembers(classDeclaration, symbolTree)); break; } declarationsIL.Add(declaration.Symbol, declarationIL); return(declarationIL); }
public VariableFlags IdentifierName( INameExpression nameExpression, VariableFlags possiblyMoved) { var symbol = nameExpression.ReferencedSymbol; if (possiblyMoved[symbol] == true) { diagnostics.Add(SemanticError.UseOfPossiblyMovedValue(file, nameExpression.Span)); } var valueSemantics = nameExpression.Semantics; // TODO this isn't correct, but for now fields don't have proper move, borrow handling //?? nameExpression.Type.Assigned().OldValueSemantics; switch (valueSemantics) { case ExpressionSemantics.Move: case ExpressionSemantics.Acquire: return(possiblyMoved.Set(symbol, true)); case ExpressionSemantics.Copy: case ExpressionSemantics.Borrow: case ExpressionSemantics.Share: case ExpressionSemantics.Void: case ExpressionSemantics.Never: case ExpressionSemantics.CreateReference: // If it were move or copy, that would have been set to the ExpressionSemantics // Not moving value return(possiblyMoved); default: throw ExhaustiveMatch.Failed(valueSemantics); } }
/// <remarks> /// It wouldn't make sense to get all declarations including non-member because /// that includes namespace declarations. However, some namespaces come from /// the implicit namespace of a compilation unit or are implicitly declared, /// so it wouldn't give a full list of the namespaces. /// </remarks> private static IEnumerable <IEntityDeclarationSyntax> GetEntityDeclarations( FixedSet <ICompilationUnitSyntax> compilationUnits) { var declarations = new Queue <IDeclarationSyntax>(); declarations.EnqueueRange(compilationUnits.SelectMany(cu => cu.Declarations)); while (declarations.TryDequeue(out var declaration)) { switch (declaration) { default: throw ExhaustiveMatch.Failed(declaration); case IMemberDeclarationSyntax syn: yield return(syn); break; case IFunctionDeclarationSyntax syn: yield return(syn); break; case INamespaceDeclarationSyntax syn: declarations.EnqueueRange(syn.Declarations); break; case IClassDeclarationSyntax syn: yield return(syn); declarations.EnqueueRange(syn.Members); break; } } }
private static bool CompileCode( Project project, string cacheDir, string codePath, object consoleLock) { var compiler = new CLangCompiler(); var runtimeLibrarySourcePath = System.IO.Path.Combine(cacheDir, CodeEmitter.RuntimeLibraryCodeFileName); File.WriteAllText(runtimeLibrarySourcePath, CodeEmitter.RuntimeLibraryCode, Encoding.UTF8); var runtimeLibraryHeaderPath = System.IO.Path.Combine(cacheDir, CodeEmitter.RuntimeLibraryHeaderFileName); File.WriteAllText(runtimeLibraryHeaderPath, CodeEmitter.RuntimeLibraryHeader, Encoding.UTF8); var sourceFiles = new[] { codePath, runtimeLibrarySourcePath }; var headerSearchPaths = new[] { cacheDir }; string outputPath = project.Template switch { ProjectTemplate.App => Path.ChangeExtension(codePath, "exe"), ProjectTemplate.Lib => Path.ChangeExtension(codePath, "dll"), _ => throw ExhaustiveMatch.Failed(project.Template) }; lock (consoleLock) { Console.WriteLine($"CLang Compiling {project.Name} ({project.Path})..."); var exitCode = compiler.Compile(ConsoleCompilerOutput.Instance, sourceFiles, headerSearchPaths, outputPath); return(exitCode == 0); } }
public static string ToInstructionString(this BooleanLogicOperator @operator) { return(@operator switch { BooleanLogicOperator.And => "AND", BooleanLogicOperator.Or => "OR", _ => throw ExhaustiveMatch.Failed(@operator) });
private static INonMemberDeclaration BuildNonMemberDeclaration(INonMemberEntityDeclarationSyntax entity) { return(entity switch { IClassDeclarationSyntax syn => BuildClass(syn), IFunctionDeclarationSyntax syn => BuildFunction(syn), _ => throw ExhaustiveMatch.Failed(entity) });
public static string ToSymbolString(this AccessOperator @operator) { return(@operator switch { AccessOperator.Standard => ".", AccessOperator.Conditional => "?.", _ => throw ExhaustiveMatch.Failed(@operator) });
public override string ToString() { return(Fixity switch { UnaryOperatorFixity.Prefix => $"{Operator.ToSymbolString()}{Operand.ToGroupedString(ExpressionPrecedence)}", UnaryOperatorFixity.Postfix => $"{Operand.ToGroupedString(ExpressionPrecedence)}{Operator.ToSymbolString()}", _ => throw ExhaustiveMatch.Failed(Fixity) });
private static ParameterIL BuildParameter(IParameter parameter) { return(parameter switch { INamedParameter namedParameter => new NamedParameterIL(namedParameter.Symbol), ISelfParameter selfParameter => new SelfParameterIL(selfParameter.Symbol), IFieldParameter fieldParameter => new FieldParameterIL(fieldParameter.ReferencedSymbol), _ => throw ExhaustiveMatch.Failed(parameter) });
private void ResolveBodyTypes(IEntityDeclarationSyntax declaration) { switch (declaration) { default: throw ExhaustiveMatch.Failed(declaration); case IFunctionDeclarationSyntax function: { var resolver = new BasicBodyAnalyzer(function, symbolTreeBuilder, symbolTrees, stringSymbol, diagnostics, function.Symbol.Result.ReturnDataType); resolver.ResolveTypes(function.Body); break; } case IAssociatedFunctionDeclarationSyntax associatedFunction: { var resolver = new BasicBodyAnalyzer(associatedFunction, symbolTreeBuilder, symbolTrees, stringSymbol, diagnostics, associatedFunction.Symbol.Result.ReturnDataType); resolver.ResolveTypes(associatedFunction.Body); break; } case IConcreteMethodDeclarationSyntax method: { var resolver = new BasicBodyAnalyzer(method, symbolTreeBuilder, symbolTrees, stringSymbol, diagnostics, method.Symbol.Result.ReturnDataType); resolver.ResolveTypes(method.Body); break; } case IAbstractMethodDeclarationSyntax _: // has no body, so nothing to resolve break; case IFieldDeclarationSyntax field: if (field.Initializer != null) { var resolver = new BasicBodyAnalyzer(field, symbolTreeBuilder, symbolTrees, stringSymbol, diagnostics); resolver.CheckType(ref field.Initializer, field.Symbol.Result.DataType); } break; case IConstructorDeclarationSyntax constructor: { var resolver = new BasicBodyAnalyzer(constructor, symbolTreeBuilder, symbolTrees, stringSymbol, diagnostics, constructor.ImplicitSelfParameter.Symbol.Result.DataType); resolver.ResolveTypes(constructor.Body); break; } case IClassDeclarationSyntax _: // body of class is processed as separate items break; } }
/// <summary> /// Convert an expression that yields a value into an operand for another instruction /// </summary> private Operand ConvertToOperand(IExpression expression) { switch (expression) { default: throw ExhaustiveMatch.Failed(expression); case ISelfExpression exp: return(graph.SelfVariable.Reference(exp.Span)); case INameExpression exp: { var symbol = exp.ReferencedSymbol; return(graph.VariableFor(symbol).Reference(exp.Span)); } case IBorrowExpression exp: return(ConvertToOperand(exp.Referent)); case IShareExpression exp: return(ConvertToOperand(exp.Referent)); case IMoveExpression exp: return(ConvertToOperand(exp.Referent)); case IAssignmentExpression _: case IBinaryOperatorExpression _: case IUnaryOperatorExpression _: case IFieldAccessExpression _: case IMethodInvocationExpression _: case INewObjectExpression _: case IImplicitNumericConversionExpression _: case IIntegerLiteralExpression _: case IStringLiteralExpression _: case IBoolLiteralExpression _: case INoneLiteralExpression _: case IImplicitImmutabilityConversionExpression _: case IImplicitOptionalConversionExpression _: case IUnsafeExpression _: case IBlockExpression _: case IImplicitNoneConversionExpression _: case IBreakExpression _: case INextExpression _: case IReturnExpression _: case IIfExpression _: case IFunctionInvocationExpression _: case IForeachExpression _: case ILoopExpression _: case IWhileExpression _: { var tempVar = graph.Let(expression.DataType.Assigned().Known(), CurrentScope); ConvertIntoPlace(expression, tempVar.Place(expression.Span)); return(tempVar.Reference(expression.Span)); } } }
public void EnumValueGivesEnumMessage() { const string expectedMessage = "'ExhaustiveMatching.Tests.Fakes.ExampleEnum.Yes' was not matched. Match is supposed to be exhaustive."; var ex = ExhaustiveMatch.Failed(ExampleEnum.Yes); Assert.Equal(expectedMessage, ex.Message); Assert.Equal(ExampleEnum.Yes, ex.FailedValue); Assert.Equal(typeof(ExampleEnum), ex.MatchingType); }
public void NullValueForEnumTypeGivesNullObjectMessage() { const string expectedMessage = "The value 'null' was not matched when matching a 'System.Enum'. Match is supposed to be exhaustive."; var ex = ExhaustiveMatch.Failed <Enum>(null); Assert.Equal(expectedMessage, ex.Message); Assert.Null(ex.FailedValue); Assert.Equal(typeof(Enum), ex.MatchingType); }
public static string ToInstructionString(this NumericInstructionOperator @operator) { return(@operator switch { NumericInstructionOperator.Add => "ADD", NumericInstructionOperator.Subtract => "SUB", NumericInstructionOperator.Multiply => "MUL", NumericInstructionOperator.Divide => "DIV", _ => throw ExhaustiveMatch.Failed(@operator) });
public void WithoutValueGivesGenericMessage() { const string expectedMessage = "A match that was supposed to be exhaustive failed to match."; var ex = ExhaustiveMatch.Failed(); Assert.Equal(expectedMessage, ex.Message); Assert.Null(ex.FailedValue); Assert.Null(ex.MatchingType); }
public void NullValueForNullableEnumGivesNullObjectMessage() { const string expectedMessage = "The value 'null' was not matched when matching a 'ExhaustiveMatching.Tests.Fakes.ExampleEnum?'. Match is supposed to be exhaustive."; ExampleEnum? value = default; var ex = ExhaustiveMatch.Failed(value); Assert.Equal(expectedMessage, ex.Message); Assert.Null(ex.FailedValue); Assert.Equal(typeof(ExampleEnum?), ex.MatchingType); }
public string Convert(ParameterIL parameter) { var type = typeConverter.Convert(parameter.DataType); return(parameter switch { NamedParameterIL param => $"{type} {nameMangler.Mangle(param.Symbol.Name)}", SelfParameterIL _ => $"{type} {nameMangler.SelfName}", FieldParameterIL param => $"{type} {nameMangler.Mangle(param.InitializeField.Name)}", _ => throw ExhaustiveMatch.Failed(parameter) });
public void ObjectValueGivesTypeMessage() { const string expectedMessage = "Object of type 'System.String' was not matched when matching a 'System.ICloneable'. Match is supposed to be exhaustive."; var value = (ICloneable)"TestingValue"; var ex = ExhaustiveMatch.Failed(value); Assert.Equal(expectedMessage, ex.Message); Assert.Equal(value, ex.FailedValue); Assert.Equal(typeof(ICloneable), ex.MatchingType); }
public static string ToSymbolString(this AssignmentOperator @operator) { return(@operator switch { AssignmentOperator.Simple => "=", AssignmentOperator.Plus => "+=", AssignmentOperator.Minus => "-=", AssignmentOperator.Asterisk => "*=", AssignmentOperator.Slash => "/=", _ => throw ExhaustiveMatch.Failed(@operator) });
public void NullObjectGivesNullObjectMessage() { const string expectedMessage = "The value 'null' was not matched when matching a 'System.ICloneable'. Match is supposed to be exhaustive."; ICloneable value = default; var ex = ExhaustiveMatch.Failed(value); Assert.Equal(expectedMessage, ex.Message); Assert.Null(ex.FailedValue); Assert.Equal(typeof(ICloneable), ex.MatchingType); }
public void InvalidEnumValueGivesInvalidEnumMessage() { const ExampleEnum invalidValue = (ExampleEnum)123; const string expectedMessage = "The value 123 is not valid for enum type 'ExhaustiveMatching.Tests.Fakes.ExampleEnum'. Match is supposed to be exhaustive."; var ex = ExhaustiveMatch.Failed(invalidValue); Assert.Equal(expectedMessage, ex.Message); Assert.Equal(invalidValue, ex.FailedValue); Assert.Equal(typeof(ExampleEnum), ex.MatchingType); }