From 06a70d35fe771819182acdf959f6db5c5e081bcb Mon Sep 17 00:00:00 2001 From: Merlin <36685500+Merlin-san@users.noreply.github.com> Date: Mon, 13 Apr 2020 14:43:43 -0700 Subject: [PATCH] Various bug fixes and better error reporting - Allow use of empty statements - Allow use of '+' operator as an identity operation - Update not supported exception to reflect the current support for jagged arrays, but not multidimensional arrays - Allow empty conditions in `for` loops - Prevent compile from continuing on errors while building class definitions - Handle more cases where someone attempts to access undefined names and fix some cases where the name would not be propagated correctly for the error message --- .../UdonSharp/Editor/UdonSharpASTVisitor.cs | 31 +++++++++++----- Assets/UdonSharp/Editor/UdonSharpCompiler.cs | 35 ++++++++++--------- .../Editor/UdonSharpExpressionCapture.cs | 31 +++++++++++----- 3 files changed, 64 insertions(+), 33 deletions(-) diff --git a/Assets/UdonSharp/Editor/UdonSharpASTVisitor.cs b/Assets/UdonSharp/Editor/UdonSharpASTVisitor.cs index ee0d86a7..bcbec40b 100644 --- a/Assets/UdonSharp/Editor/UdonSharpASTVisitor.cs +++ b/Assets/UdonSharp/Editor/UdonSharpASTVisitor.cs @@ -286,6 +286,11 @@ public override void VisitUsingDirective(UsingDirectiveSyntax node) } } + public override void VisitEmptyStatement(EmptyStatementSyntax node) + { + UpdateSyntaxNode(node); + } + public override void VisitBlock(BlockSyntax node) { UpdateSyntaxNode(node); @@ -564,7 +569,7 @@ public override void VisitElementAccessExpression(ElementAccessExpressionSyntax Visit(node.Expression); if (node.ArgumentList.Arguments.Count != 1) - throw new System.NotSupportedException("UdonSharp does not support multidimensional or jagged array accesses yet"); + throw new System.NotSupportedException("UdonSharp does not support multidimensional accesses yet"); SymbolDefinition indexerSymbol = null; @@ -1051,6 +1056,13 @@ public override void VisitPrefixUnaryExpression(PrefixUnaryExpressionSyntax node { Visit(node.Operand); + if (node.OperatorToken.Kind() == SyntaxKind.PlusToken || node.OperatorToken.Kind() == SyntaxKind.UnaryPlusExpression) + { + if (topScope != null) + topScope.SetToLocalSymbol(operandCapture.ExecuteGet()); + return; + } + List operatorMethods = new List(); switch (node.OperatorToken.Kind()) @@ -2006,15 +2018,18 @@ public override void VisitForStatement(ForStatementSyntax node) JumpLabel forLoopEnd = visitorContext.labelTable.GetNewJumpLabel("forLoopEnd"); - SymbolDefinition conditionSymbol = null; - using (ExpressionCaptureScope conditionScope = new ExpressionCaptureScope(visitorContext, null)) + if (node.Condition != null) { - Visit(node.Condition); - conditionSymbol = HandleImplicitBoolCast(conditionScope.ExecuteGet()); - } + SymbolDefinition conditionSymbol = null; + using (ExpressionCaptureScope conditionScope = new ExpressionCaptureScope(visitorContext, null)) + { + Visit(node.Condition); + conditionSymbol = HandleImplicitBoolCast(conditionScope.ExecuteGet()); + } - visitorContext.uasmBuilder.AddPush(conditionSymbol); - visitorContext.uasmBuilder.AddJumpIfFalse(forLoopEnd); + visitorContext.uasmBuilder.AddPush(conditionSymbol); + visitorContext.uasmBuilder.AddJumpIfFalse(forLoopEnd); + } visitorContext.continueLabelStack.Push(forLoopContinue); visitorContext.breakLabelStack.Push(forLoopEnd); diff --git a/Assets/UdonSharp/Editor/UdonSharpCompiler.cs b/Assets/UdonSharp/Editor/UdonSharpCompiler.cs index 7f011f4d..aadbc13a 100644 --- a/Assets/UdonSharp/Editor/UdonSharpCompiler.cs +++ b/Assets/UdonSharp/Editor/UdonSharpCompiler.cs @@ -48,27 +48,30 @@ public void Compile() if (classDefinitions == null) totalErrorCount++; - foreach (CompilationModule module in modules) - { - EditorUtility.DisplayProgressBar("UdonSharp Compile", - $"Compiling {AssetDatabase.GetAssetPath(module.programAsset.sourceCsScript)}...", - Mathf.Clamp01((moduleCounter++ / (float)modules.Length) + Random.Range(0.01f, 1f / modules.Length))); // Make it look like we're doing work :D - - int moduleErrorCount = module.Compile(classDefinitions); - totalErrorCount += moduleErrorCount; - } - if (totalErrorCount == 0) { - EditorUtility.DisplayProgressBar("UdonSharp Compile", "Assigning constants...", 1f); - int initializerErrorCount = AssignHeapConstants(); - totalErrorCount += initializerErrorCount; + foreach (CompilationModule module in modules) + { + EditorUtility.DisplayProgressBar("UdonSharp Compile", + $"Compiling {AssetDatabase.GetAssetPath(module.programAsset.sourceCsScript)}...", + Mathf.Clamp01((moduleCounter++ / (float)modules.Length) + Random.Range(0.01f, 1f / modules.Length))); // Make it look like we're doing work :D - if (initializerErrorCount == 0) + int moduleErrorCount = module.Compile(classDefinitions); + totalErrorCount += moduleErrorCount; + } + + if (totalErrorCount == 0) { - foreach (CompilationModule module in modules) + EditorUtility.DisplayProgressBar("UdonSharp Compile", "Assigning constants...", 1f); + int initializerErrorCount = AssignHeapConstants(); + totalErrorCount += initializerErrorCount; + + if (initializerErrorCount == 0) { - module.programAsset.ApplyProgram(); + foreach (CompilationModule module in modules) + { + module.programAsset.ApplyProgram(); + } } } } diff --git a/Assets/UdonSharp/Editor/UdonSharpExpressionCapture.cs b/Assets/UdonSharp/Editor/UdonSharpExpressionCapture.cs index 68594186..0afc608e 100644 --- a/Assets/UdonSharp/Editor/UdonSharpExpressionCapture.cs +++ b/Assets/UdonSharp/Editor/UdonSharpExpressionCapture.cs @@ -106,7 +106,7 @@ public ExpressionCaptureScope(ASTVisitorContext context, ExpressionCaptureScope public void Dispose() { if (parentScope != null) - parentScope.InheretScope(this); + parentScope.InheritScope(this); Debug.Assert(visitorContext.topCaptureScope == this); if (visitorContext.topCaptureScope == this) @@ -115,10 +115,9 @@ public void Dispose() disposed = true; } - private void InheretScope(ExpressionCaptureScope childScope) + private void InheritScope(ExpressionCaptureScope childScope) { if (captureArchetype != ExpressionCaptureArchetype.Unknown || - childScope.captureArchetype == ExpressionCaptureArchetype.Unknown || childScope.captureArchetype == ExpressionCaptureArchetype.This || childScope.captureArchetype == ExpressionCaptureArchetype.Method || childScope.captureArchetype == ExpressionCaptureArchetype.Namespace) @@ -136,6 +135,17 @@ private void InheretScope(ExpressionCaptureScope childScope) captureExternUserField = childScope.captureExternUserField; captureExternUserMethod = childScope.captureExternUserMethod; InternalMethodHandler = childScope.InternalMethodHandler; + unresolvedAccessChain = childScope.unresolvedAccessChain; + } + + private void CheckScopeValidity() + { + if (IsUnknownArchetype()) + { + string[] unresolvedTokens = unresolvedAccessChain.Split('.'); + string invalidName = unresolvedTokens.Length > 1 ? unresolvedTokens[unresolvedTokens.Length - 2] : unresolvedTokens[0]; + throw new System.Exception($"The name '{invalidName}' does not exist in the current context"); + } } public void SetToLocalSymbol(SymbolDefinition symbol) @@ -292,6 +302,8 @@ public SymbolDefinition ExecuteGet() return captureLocalSymbol; SymbolDefinition outSymbol = null; + + CheckScopeValidity(); if (captureArchetype == ExpressionCaptureArchetype.Property) { @@ -376,12 +388,6 @@ public SymbolDefinition ExecuteGet() // Capture type should still be valid from the last transition outSymbol = visitorContext.topTable.CreateConstSymbol(captureType, GetEnumValue()); } - else if (captureArchetype == ExpressionCaptureArchetype.Unknown) - { - string[] unresolvedTokens = unresolvedAccessChain.Split('.'); - string invalidName = unresolvedTokens.Length > 1 ? unresolvedTokens[unresolvedTokens.Length - 2] : unresolvedTokens[0]; - throw new System.Exception($"The name '{invalidName}' does not exist in the current context"); - } else { throw new System.Exception("Get can only be run on Fields, Properties, Local Symbols, array indexers, and the `this` keyword"); @@ -392,6 +398,8 @@ public SymbolDefinition ExecuteGet() public void ExecuteSet(SymbolDefinition value, bool explicitCast = false) { + CheckScopeValidity(); + SymbolDefinition convertedValue = CastSymbolToType(value, GetReturnType(true), explicitCast); // If it's a local symbol, it's just a simple COPY @@ -472,6 +480,8 @@ public void ExecuteSet(SymbolDefinition value, bool explicitCast = false) // Just a stub for now that will be extended to avoid the COPY instruction when possible public void ExecuteSetDirect(ExpressionCaptureScope valueExpression, bool explicitCast = false) { + CheckScopeValidity(); + ExecuteSet(valueExpression.ExecuteGet(), explicitCast); } @@ -1354,12 +1364,15 @@ public SymbolDefinition Invoke(SymbolDefinition[] invokeParams) } else { + CheckScopeValidity(); throw new System.Exception($"Cannot call invoke on archetype {captureArchetype}"); } } public System.Type GetReturnType(bool getUserType = false) { + CheckScopeValidity(); + if (captureArchetype == ExpressionCaptureArchetype.Method) throw new System.Exception("Cannot infer return type from method without function arguments");