Просмотр исходного кода

Initial const eval feature release

Brian Fiete 4 лет назад
Родитель
Сommit
ff1f8aff3f

+ 6 - 0
BeefLibs/corlib/src/Attribute.bf

@@ -260,6 +260,12 @@ namespace System
 
 	}
 
+	[AttributeUsage(.Method | .Invocation)]
+	public struct ConstEvalAttribute : Attribute
+	{
+
+	}
+
 	[AttributeUsage(.Method /*2*/)]
 	public struct IntrinsicAttribute : Attribute
 	{

+ 16 - 0
BeefLibs/corlib/src/Compiler.bf

@@ -34,5 +34,21 @@ namespace System
 
 		[LinkName("#IsConstEval")]
 		public static extern bool IsConstEval;
+
+		[LinkName("#IsBuilding")]
+		public static extern bool IsBuilding;
+
+		[LinkName("#IsReified")]
+		public static extern bool IsReified;
+
+		[LinkName("#CompileRev")]
+		public static extern int32 CompileRev;
+
+		[ConstEval]
+		public static void Assert(bool cond)
+		{
+			if (!cond)
+				Runtime.FatalError("Assert failed");
+		}
 	}
 }

+ 32 - 0
BeefLibs/corlib/src/Reflection/FieldInfo.bf

@@ -36,6 +36,30 @@ namespace System.Reflection
 	        }
 	    }
 
+		public bool IsConst
+		{
+			get
+			{
+				return mFieldData.mFlags.HasFlag(.Const);
+			}
+		}
+
+		public bool IsStatic
+		{
+			get
+			{
+				return mFieldData.mFlags.HasFlag(.Static);
+			}
+		}
+
+		public bool IsInstanceField
+		{
+			get
+			{
+				return !mFieldData.mFlags.HasFlag(.Static) && !mFieldData.mFlags.HasFlag(.Const);
+			}
+		}
+
 		public StringView Name
 		{
 			get
@@ -520,6 +544,14 @@ namespace System.Reflection
 	            }
 	        }
 
+			public int32 Index
+			{
+				get
+				{
+					return mIdx;
+				}
+			}
+
 			public Result<FieldInfo> GetNext() mut
 			{
 				if (!MoveNext())

+ 7 - 1
BeefLibs/corlib/src/Type.bf

@@ -454,14 +454,20 @@ namespace System
         {
             return (int32)mTypeId;
         }
-        
+
+		static extern Type ConstEval_GetTypeById(int32 typeId);
+
         protected static Type GetType(TypeId typeId)
         {
+			if (Compiler.IsConstEval)
+				return ConstEval_GetTypeById((.)typeId);
             return sTypes[(int32)typeId];
         }
 
 		protected static Type GetType_(int32 typeId)
 		{
+			if (Compiler.IsConstEval)
+				return ConstEval_GetTypeById(typeId);
 		    return sTypes[typeId];
 		}
 

+ 18 - 0
BeefySysLib/util/PerfTimer.h

@@ -148,6 +148,24 @@ public:
 	~AutoPerfRecordAndPrint();
 };
 
+class AutoTimer
+{
+public:
+	uint32 mStartTick;
+	int* mTimePtr;
+
+public:
+	AutoTimer(int& timeRef)
+	{
+		mTimePtr = &timeRef;
+		mStartTick = BFTickCount();
+	}
+
+	~AutoTimer()
+	{
+		*mTimePtr += BFTickCount() - mStartTick;
+	}
+};
 
 class DebugTimeGuard
 {

+ 6 - 0
IDE/mintest/minlib/src/System/Attribute.bf

@@ -231,6 +231,12 @@ namespace System
 
 	}
 
+	[AttributeUsage(.Method | .Invocation)]
+	public struct ConstEvalAttribute : Attribute
+	{
+
+	}
+
 	[AttributeUsage(.Method /*2*/)]
 	public struct IntrinsicAttribute : Attribute
 	{

+ 6 - 0
IDE/src/IDEApp.bf

@@ -11781,6 +11781,9 @@ namespace IDE
 				bool didClean = false;
                 if (mWantsBeefClean)
                 {
+					mBfBuildCompiler?.CancelBackground();
+					mBfResolveCompiler?.CancelBackground();
+
                     if ((!mBfBuildCompiler.HasQueuedCommands()) &&
 						((mBfResolveCompiler == null) || (!mBfResolveCompiler.HasQueuedCommands())))
                     {
@@ -11857,6 +11860,9 @@ namespace IDE
 
                 if (mWantsClean)
                 {
+					mBfBuildCompiler?.CancelBackground();
+					mBfResolveCompiler?.CancelBackground();
+
                     if ((!mBfBuildCompiler.HasQueuedCommands()) && (!mBfResolveCompiler.HasQueuedCommands())
 #if IDE_C_SUPPORT
                         && (!mDepClang.HasQueuedCommands()) && (!mResolveClang.HasQueuedCommands())

+ 12 - 0
IDE/src/ui/SourceViewPanel.bf

@@ -1027,6 +1027,18 @@ namespace IDE.ui
 					doBackground = true;
 			}
 
+			// If there's a long-running const eval then cancel that first
+			if (!mDeferredResolveResults.IsEmpty)
+			{
+				for (var result in mDeferredResolveResults)
+				{
+					if (result.mResolveType == .ClassifyFullRefresh)
+					{
+						bfCompiler.RequestCancelBackground();
+					}
+				}
+			}
+
             if (mIsClang)
             {
 #if !IDE_C_SUPPORT

+ 18 - 0
IDE/src/ui/StatusBar.bf

@@ -26,6 +26,9 @@ namespace IDE.ui
 		public int mDirtyDelay;
 		public int mStatusBoxUpdateCnt = -1;
 
+		public int32 mResolveStuckTicks;
+		public float mResolveLastPct = -1;
+
         public this()
         {
             mConfigComboBox = new DarkComboBox();
@@ -225,8 +228,19 @@ namespace IDE.ui
 #endif
                 )
 			{
+				float completionPct = gApp.mBfResolveCompiler.GetCompletionPercentage();
+				if (completionPct != mResolveLastPct)
+				{
+					mResolveStuckTicks = 0;
+					mResolveLastPct = completionPct;
+				}
+				else
+					mResolveStuckTicks++;
+
 				MarkDirtyEx();
 			}
+			else
+				mResolveStuckTicks = 0;
         }
 
         public override void Draw(Graphics g)        
@@ -380,6 +394,10 @@ namespace IDE.ui
 			{
 				DrawStatusBox("Custom Build Commands...", gApp.mBuildContext.mUpdateCnt);
 			}
+			else if (mResolveStuckTicks > 300)
+			{
+				DrawStatusBox("Const Evaluation");
+			}
 			else
 				mStatusBoxUpdateCnt = -1;
 

+ 2 - 2
IDEHelper/Compiler/BfAutoComplete.cpp

@@ -470,8 +470,8 @@ void BfAutoComplete::AddMethod(BfTypeInstance* typeInstance, BfMethodDef* method
 	if (auto entryAdded = AddEntry(entry, filter))
 	{
 		if (methodDecl != NULL)
-		{
-			if ((methodInstance != NULL) && (methodInstance->mMethodDef->mIsLocalMethod) && (GetCursorIdx(methodDecl) == methodDecl->mReturnType->mSrcEnd))
+		{			
+			if ((methodInstance != NULL) && (methodInstance->mMethodDef->mIsLocalMethod) && (methodDecl->mReturnType != NULL) && (GetCursorIdx(methodDecl) == methodDecl->mReturnType->mSrcEnd))
 			{
 				// This isn't really a local method decl, it just looks like one
 				return;

+ 10 - 1
IDEHelper/Compiler/BfCompiler.cpp

@@ -397,6 +397,7 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly)
 	mActionTypeDef = NULL;
 	mEnumTypeDef = NULL;
 	mFriendAttributeTypeDef = NULL;
+	mConstEvalAttributeTypeDef = NULL;
 	mNoExtensionAttributeTypeDef = NULL;
 	mCheckedAttributeTypeDef = NULL;
 	mUncheckedAttributeTypeDef = NULL;
@@ -406,7 +407,8 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly)
 	mGenericIEnumeratorTypeDef = NULL;
 	mGenericIRefEnumeratorTypeDef = NULL;
 	mInlineAttributeTypeDef = NULL;
-	mInternalTypeDef = NULL;
+	mThreadTypeDef = NULL;
+	mInternalTypeDef = NULL;	
 	mDiagnosticsDebugTypeDef = NULL;
 	mIDisposableTypeDef = NULL;
 	mIPrintableTypeDef = NULL;
@@ -6596,6 +6598,7 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory)
 	mActionTypeDef = _GetRequiredType("System.Action");
 	mEnumTypeDef = _GetRequiredType("System.Enum");
 	mFriendAttributeTypeDef = _GetRequiredType("System.FriendAttribute");
+	mConstEvalAttributeTypeDef = _GetRequiredType("System.ConstEvalAttribute");
 	mNoExtensionAttributeTypeDef = _GetRequiredType("System.NoExtensionAttribute");
 	mCheckedAttributeTypeDef = _GetRequiredType("System.CheckedAttribute");
 	mUncheckedAttributeTypeDef = _GetRequiredType("System.UncheckedAttribute");
@@ -6606,6 +6609,7 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory)
 	mGenericIEnumeratorTypeDef = _GetRequiredType("System.Collections.IEnumerator", 1);
 	mGenericIRefEnumeratorTypeDef = _GetRequiredType("System.Collections.IRefEnumerator", 1);	
 	mInlineAttributeTypeDef = _GetRequiredType("System.InlineAttribute");
+	mThreadTypeDef = _GetRequiredType("System.Threading.Thread");
 	mInternalTypeDef = _GetRequiredType("System.Internal");
 	mDiagnosticsDebugTypeDef = _GetRequiredType("System.Diagnostics.Debug");
 	mIDisposableTypeDef = _GetRequiredType("System.IDisposable");
@@ -7258,6 +7262,11 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory)
 	mPassInstance->OutputLine(StrFormat(":low %d module%s built, %d object file%s generated", 
 		numModulesWritten, (numModulesWritten != 1) ? "s" : "",
 		numObjFilesWritten, (numObjFilesWritten != 1) ? "s" : ""));
+
+	if ((mCEMachine != NULL) && (!mIsResolveOnly) && (mCEMachine->mRevisionExecuteTime > 0))
+	{
+		mPassInstance->OutputLine(StrFormat(":med Const evaluation time: %0.2fs", mCEMachine->mRevisionExecuteTime / 1000.0f));
+	}
 	
 	BpLeave();	
 	mPassInstance->WriteErrorSummary();

+ 2 - 0
IDEHelper/Compiler/BfCompiler.h

@@ -359,6 +359,7 @@ public:
 	BfTypeDef* mGenericIEnumeratorTypeDef;
 	BfTypeDef* mGenericIRefEnumeratorTypeDef;
 	
+	BfTypeDef* mThreadTypeDef;
 	BfTypeDef* mInternalTypeDef;
 	BfTypeDef* mDiagnosticsDebugTypeDef;
 	BfTypeDef* mIDisposableTypeDef;
@@ -401,6 +402,7 @@ public:
 	BfTypeDef* mDisableChecksAttributeTypeDef;
 	BfTypeDef* mDisableObjectAccessChecksAttributeTypeDef;
 	BfTypeDef* mFriendAttributeTypeDef;
+	BfTypeDef* mConstEvalAttributeTypeDef;
 	BfTypeDef* mNoExtensionAttributeTypeDef;
 	BfTypeDef* mCheckedAttributeTypeDef;
 	BfTypeDef* mUncheckedAttributeTypeDef;	

+ 3 - 3
IDEHelper/Compiler/BfConstResolver.cpp

@@ -36,7 +36,7 @@ BfConstResolver::BfConstResolver(BfModule* bfModule) : BfExprEvaluator(bfModule)
 
 BfTypedValue BfConstResolver::Resolve(BfExpression* expr, BfType* wantType, BfConstResolveFlags flags)
 {	
-	mBfEvalExprFlags = (BfEvalExprFlags)(mBfEvalExprFlags | BfEvalExprFlags_ConstExpr);
+	mBfEvalExprFlags = (BfEvalExprFlags)(mBfEvalExprFlags | BfEvalExprFlags_ConstEval);
 
 	// Handle the 'int[?] val = .(1, 2, 3)' case
 	if ((flags & BfConstResolveFlag_ArrayInitSize) != 0)
@@ -76,7 +76,7 @@ BfTypedValue BfConstResolver::Resolve(BfExpression* expr, BfType* wantType, BfCo
 			}
 			else
 			{
-				mResult = BfTypedValue(mModule->mBfIRBuilder->GetUndefConstValue(BfTypeCode_IntPtr), mModule->GetPrimitiveType(BfTypeCode_IntPtr));
+				mResult = BfTypedValue(mModule->mBfIRBuilder->GetUndefConstValue(mModule->mBfIRBuilder->GetPrimitiveType(BfTypeCode_IntPtr)), mModule->GetPrimitiveType(BfTypeCode_IntPtr));
 				return mResult;
 			}
 		}
@@ -176,7 +176,7 @@ BfTypedValue BfConstResolver::Resolve(BfExpression* expr, BfType* wantType, BfCo
 					BfTypedValue result;
 					result.mKind = BfTypedValueKind_Value;
 					result.mType = genericTypeConstraint;
-					result.mValue = mModule->mBfIRBuilder->GetUndefConstValue(primType->mTypeDef->mTypeCode);
+					result.mValue = mModule->mBfIRBuilder->GetUndefConstValue(mModule->mBfIRBuilder->GetPrimitiveType(primType->mTypeDef->mTypeCode));
 					return result;
 				}
 			}

+ 2 - 0
IDEHelper/Compiler/BfDefBuilder.cpp

@@ -821,6 +821,8 @@ void BfDefBuilder::ParseAttributes(BfAttributeDirective* attributes, BfMethodDef
 				methodDef->mIsNoReturn = true;
 			else if (typeRefName == "SkipCall")
 				methodDef->mIsSkipCall = true;
+			else if (typeRefName == "ConstEval")
+				methodDef->mIsConstEval = true;
 			else if (typeRefName == "NoShow")
 				methodDef->mIsNoShow = true;
 			else if (typeRefName == "NoDiscard")

+ 161 - 27
IDEHelper/Compiler/BfExprEvaluator.cpp

@@ -2630,14 +2630,14 @@ BfAutoComplete* BfExprEvaluator::GetAutoComplete()
 
 bool BfExprEvaluator::IsConstEval()
 {
-	return (mModule->mIsConstModule) || ((mBfEvalExprFlags & BfEvalExprFlags_ConstExpr) != 0);
+	return (mModule->mIsConstModule) || ((mBfEvalExprFlags & BfEvalExprFlags_ConstEval) != 0);
 }
 
 bool BfExprEvaluator::IsConstEvalEntry()
 {
 	if (mModule->mIsConstModule)
 		return false;
-	return ((mBfEvalExprFlags & BfEvalExprFlags_ConstExpr) != 0);
+	return ((mBfEvalExprFlags & BfEvalExprFlags_ConstEval) != 0);
 }
 
 int BfExprEvaluator::GetStructRetIdx(BfMethodInstance* methodInstance, bool forceStatic)
@@ -3263,6 +3263,11 @@ void BfExprEvaluator::Visit(BfLiteralExpression* literalExpr)
 
 void BfExprEvaluator::Visit(BfStringInterpolationExpression* stringInterpolationExpression)
 {
+	if (IsConstEvalEntry())
+	{
+		mModule->Fail("Const evaluation of string interpolation not allowed", stringInterpolationExpression);
+	}
+
 	if ((mBfEvalExprFlags & BfEvalExprFlags_StringInterpolateFormat) != 0)
 	{
 		BfVariant variant;
@@ -4993,6 +4998,20 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance*
 			}
 		}
 
+		if ((mBfEvalExprFlags & BfEvalExprFlags_ConstEval) != 0)
+		{
+			if (methodInstance->mReturnType->IsInstanceOf(mModule->mCompiler->mSpanTypeDef))
+			{
+				if (mExpectingType->IsSizedArray())
+				{
+					return BfTypedValue(mModule->mBfIRBuilder->GetUndefConstValue(mModule->mBfIRBuilder->MapType(mExpectingType)), mExpectingType);
+				}
+			}
+			if (methodInstance->mReturnType->IsValuelessType())
+				return BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), methodInstance->mReturnType);
+			return BfTypedValue(mModule->mBfIRBuilder->GetUndefConstValue(mModule->mBfIRBuilder->MapType(methodInstance->mReturnType)), methodInstance->mReturnType);
+		}
+
 		BfTypedValue result;
 		if (sret != NULL)
 			result = *sret;
@@ -5009,15 +5028,15 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance*
 		{						
 			mModule->mCompiler->mCEMachine->QueueMethod(methodInstance, func);
 		}
-		else if ((mBfEvalExprFlags & BfEvalExprFlags_ConstExpr) != 0)
+		else if ((mBfEvalExprFlags & BfEvalExprFlags_ConstEval) != 0)
 		{
 			if (mFunctionBindResult != NULL)
 			{
 				forceBind = true;
 			}
-			else if (mUsedAsStatement)
+			else if ((mBfEvalExprFlags & BfEvalExprFlags_InCascade) != 0)
 			{
-				// Don't allow use in a cascade
+				mModule->Fail("Const evaluation not allowed with cascade operator", targetSrc);
 			}
 			else
 			{
@@ -5648,7 +5667,7 @@ void BfExprEvaluator::PushArg(BfTypedValue argVal, SizedArrayImpl<BfIRValue>& ir
 	{		
 		if (argVal.mType->IsComposite())
 		{	
-			if ((mBfEvalExprFlags & BfEvalExprFlags_ConstExpr) != 0)
+			if ((mBfEvalExprFlags & BfEvalExprFlags_ConstEval) != 0)
 			{
 				// Const eval entry - we want any incoming consts as they are
 			}
@@ -6121,7 +6140,24 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
 					if (methodDef->mMethodType == BfMethodType_Extension)
 						numElements++;
 
-					if (wantType->IsArray())
+					if (IsConstEvalEntry())
+					{
+						if ((wantType->IsArray()) || (wantType->IsInstanceOf(mModule->mCompiler->mSpanTypeDef)))
+						{
+							auto genericTypeInst = wantType->ToGenericTypeInstance();
+							expandedParamsElementType = genericTypeInst->mGenericTypeInfo->mTypeGenericArguments[0];
+
+							auto irSizedArrayType = mModule->mBfIRBuilder->GetSizedArrayType(mModule->mBfIRBuilder->MapType(expandedParamsElementType), numElements);
+
+							Array<BfIRValue> values;
+							for (int i = 0; i < numElements; i++)
+								values.Add(mModule->mBfIRBuilder->GetFakeVal());
+							expandedParamsArray = BfTypedValue(mModule->mBfIRBuilder->CreateConstAgg(irSizedArrayType, values), wantType);
+
+							PushArg(expandedParamsArray, irArgs);
+						}
+					}
+					else if (wantType->IsArray())
 					{
 						BfArrayType* arrayType = (BfArrayType*)wantType;
 						mModule->PopulateType(arrayType, BfPopulateType_DataAndMethods);
@@ -6152,9 +6188,9 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
 
 						PushArg(expandedParamsArray, irArgs);
 					}
-					else
+					else if (wantType->IsInstanceOf(mModule->mCompiler->mSpanTypeDef))
 					{
-						auto genericTypeInst = wantType->ToGenericTypeInstance();
+						auto genericTypeInst = wantType->ToGenericTypeInstance();						
 						expandedParamsElementType = genericTypeInst->mGenericTypeInfo->mTypeGenericArguments[0];
 
 						expandedParamsArray = BfTypedValue(mModule->CreateAlloca(wantType), wantType, true);
@@ -6538,7 +6574,14 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
 		{
 			if (argValue)
 			{
-				if (expandedParamAlloca)
+				if (IsConstEvalEntry())
+				{
+					auto constant = mModule->mBfIRBuilder->GetConstant(expandedParamsArray.mValue);
+					BF_ASSERT(constant->mConstType == BfConstType_Agg);
+					auto constAgg = (BfConstantAgg*)constant;
+					constAgg->mValues[extendedParamIdx] = argValue.mValue;
+				}
+				else if (expandedParamAlloca)
 				{
 					argValue = mModule->LoadValue(argValue);
 					auto addr = mModule->mBfIRBuilder->CreateInBoundsGEP(expandedParamAlloca, extendedParamIdx);					
@@ -7965,7 +8008,9 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp
 			mResultLocalVar = NULL;
 			mResultFieldInstance = NULL;
 			mResultLocalVarRefNode = NULL;									
-			MatchConstructor(targetSrc, methodBoundExpr, structInst, resolvedTypeInstance, argValues, false, false);
+			auto result = MatchConstructor(targetSrc, methodBoundExpr, structInst, resolvedTypeInstance, argValues, false, false);
+			if ((result) && (!result.mType->IsVoid()))
+				return result;
 			mModule->ValidateAllocation(resolvedTypeInstance, targetSrc);
 			
 			mModule->AddDependency(resolvedTypeInstance, mModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_LocalUsage);
@@ -8368,7 +8413,26 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp
 			mModule->MethodToString(moduleMethodInstance.mMethodInstance).c_str(), mModule->MethodToString(mModule->mCurMethodInstance).c_str()), targetSrc);
 	}
 
-	auto result = CreateCall(targetSrc, callTarget, origTarget, methodDef, moduleMethodInstance, bypassVirtual, argValues.mResolvedArgs, skipThis);
+
+	BfTypedValue result;
+	
+	//
+	{
+		SetAndRestoreValue<BfEvalExprFlags> prevEvalExprFlag(mBfEvalExprFlags);
+
+		if ((mModule->mAttributeState != NULL) && (mModule->mAttributeState->mCustomAttributes != NULL) && (mModule->mAttributeState->mCustomAttributes->Contains(mModule->mCompiler->mConstEvalAttributeTypeDef)))
+		{
+			mModule->mAttributeState->mUsed = true;
+			mBfEvalExprFlags = (BfEvalExprFlags)(mBfEvalExprFlags | BfEvalExprFlags_ConstEval);
+		}
+		else if (moduleMethodInstance.mMethodInstance->mMethodDef->mIsConstEval)
+		{
+			mBfEvalExprFlags = (BfEvalExprFlags)(mBfEvalExprFlags | BfEvalExprFlags_ConstEval);
+		}
+
+		result = CreateCall(targetSrc, callTarget, origTarget, methodDef, moduleMethodInstance, bypassVirtual, argValues.mResolvedArgs, skipThis);
+	}
+	
 	if (result)
 	{
 		if (result.mType->IsRef())
@@ -9474,7 +9538,7 @@ void BfExprEvaluator::DoTypeIntAttr(BfTypeReference* typeRef, BfToken token)
 		// We do this so we know it's a constant but we can't assume anything about its value
 		//  We make the value an Int32 which doesn't match the IntPtr type, but it allows us to 
 		//  assume it can be implicitly cased to int32
-		mResult = BfTypedValue(mModule->mBfIRBuilder->GetUndefConstValue(BfTypeCode_Int32), sizeType);
+		mResult = BfTypedValue(mModule->mBfIRBuilder->GetUndefConstValue(mModule->mBfIRBuilder->MapType(sizeType)), sizeType);
 	}
 	else
 		mResult = BfTypedValue(mModule->GetConstValue(attrVal, sizeType), sizeType);
@@ -11918,7 +11982,7 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam
 		mModule->mCurMethodState->mHotDataReferenceBuilder->mInnerMethods.Add(methodInstance->mHotMethod);
 
 	methodState.Reset();
-	
+		
 	lambdaInstance = new BfLambdaInstance();
 	rootMethodState->mLambdaCache[cacheNodeList] = lambdaInstance;
 	lambdaInstance->mDelegateTypeInstance = delegateTypeInstance;
@@ -12022,6 +12086,8 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam
 			mModule->mCurMethodState->mHotDataReferenceBuilder->mInnerMethods.Add(dtorMethodInstance->mHotMethod);
 		lambdaInstance->mDtorMethodInstance = dtorMethodInstance;
 		lambdaInstance->mDtorFunc = dtorFunc;
+		
+		dtorMethodInstance->mMethodInstanceGroup = NULL;
 	}
 
 	prevClosureState.Restore();
@@ -12043,7 +12109,7 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam
 	if (processMethods)
 		rootMethodState->mDeferredLambdaInstances.Add(lambdaInstance);
 
-	methodInstance->mMethodInstanceGroup = NULL;
+	methodInstance->mMethodInstanceGroup = NULL;	
 
 	return lambdaInstance;
 }
@@ -13199,7 +13265,7 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs
 		{
 			allocValue = mModule->AllocFromType(resolvedTypeRef, allocTarget, appendSizeValue, BfIRValue(), 0, BfAllocFlags_None, allocAlign);
 		}
-		if (((mBfEvalExprFlags & BfEvalExprFlags_ConstExpr) != 0) && (mModule->mCompiler->mCEMachine != NULL))
+		if (((mBfEvalExprFlags & BfEvalExprFlags_ConstEval) != 0) && (mModule->mCompiler->mCEMachine != NULL))
 		{
 			mModule->mCompiler->mCEMachine->SetAppendAllocInfo(mModule, allocValue, appendSizeValue);
 		}
@@ -13250,7 +13316,7 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs
 					{
 						mModule->AssertErrorState();
 					}
-					else if ((mBfEvalExprFlags & BfEvalExprFlags_ConstExpr) == 0)
+					else if ((mBfEvalExprFlags & BfEvalExprFlags_ConstEval) == 0)
 					{
 						SizedArray<BfIRValue, 1> irArgs;
 						irArgs.push_back(mResult.mValue);
@@ -13328,7 +13394,7 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs
 		}
 	}
 
-	if (((mBfEvalExprFlags & BfEvalExprFlags_ConstExpr) != 0) && (mModule->mCompiler->mCEMachine != NULL))
+	if (((mBfEvalExprFlags & BfEvalExprFlags_ConstEval) != 0) && (mModule->mCompiler->mCEMachine != NULL))
 	{
 		mModule->mCompiler->mCEMachine->ClearAppendAllocInfo();
 	}
@@ -15362,6 +15428,7 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
 
 	int methodCount = 0;
 	bool mayBeSkipCall = false;
+	bool mayBeConstEvalCall = false;
 	if (thisValue.mType != NULL)
 	{
 		if (thisValue.mType->IsAllocType())
@@ -15379,6 +15446,8 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
 				{
 					if (methodDef->mIsSkipCall)
 						mayBeSkipCall = true;
+					if (methodDef->mIsConstEval)
+						mayBeConstEvalCall = true;
 					methodDef = methodDef->mNextWithSameName;
 				}
 			}
@@ -15404,7 +15473,7 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
 
 	BfResolveArgFlags resolveArgsFlags = (BfResolveArgFlags)(BfResolveArgFlag_DeferFixits | BfResolveArgFlag_AllowUnresolvedTypes);	
 	resolveArgsFlags = (BfResolveArgFlags)(resolveArgsFlags | BfResolveArgFlag_DeferParamEval);
-	if (mayBeSkipCall)
+	if ((mayBeSkipCall) || (mayBeConstEvalCall))
 		resolveArgsFlags = (BfResolveArgFlags)(resolveArgsFlags | BfResolveArgFlag_DeferParamValues);
 
 	static int sCallIdx = 0;
@@ -15430,10 +15499,13 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
 		}
 	}
 
-	if ((isCascade) && (cascadeOperatorToken != NULL) && ((mBfEvalExprFlags & BfEvalExprFlags_ConstExpr) != 0))
+	if ((isCascade) && (cascadeOperatorToken != NULL) && ((mBfEvalExprFlags & BfEvalExprFlags_ConstEval) != 0))
 		mModule->Fail("Cascade operator cannot be used in const evaluation", cascadeOperatorToken);
 
 	SetAndRestoreValue<bool> prevUsedAsStatement(mUsedAsStatement, mUsedAsStatement || isCascade);
+	SetAndRestoreValue<BfEvalExprFlags> prevEvalExprFlags(mBfEvalExprFlags);
+	if (isCascade)
+		mBfEvalExprFlags = (BfEvalExprFlags)(mBfEvalExprFlags | BfEvalExprFlags_InCascade);
 	ResolveArgValues(argValues, resolveArgsFlags);
 	mResult = MatchMethod(methodNodeSrc, methodBoundExpr, thisValue, allowImplicitThis, bypassVirtual, targetFunctionName, argValues, methodGenericArguments, checkedKind);		
 	argValues.HandleFixits(mModule);
@@ -15840,7 +15912,7 @@ BfTypedValue BfExprEvaluator::GetResult(bool clearResult, bool resolveGenericTyp
 					else
 					{
 						BF_ASSERT(mModule->mCurMethodInstance->mIsUnspecialized);
-						mResult = BfTypedValue(mModule->mBfIRBuilder->GetUndefConstValue(BfTypeCode_IntPtr), mModule->GetPrimitiveType(BfTypeCode_IntPtr));
+						mResult = BfTypedValue(mModule->mBfIRBuilder->GetUndefConstValue(mModule->mBfIRBuilder->GetPrimitiveType(BfTypeCode_IntPtr)), mModule->GetPrimitiveType(BfTypeCode_IntPtr));
 						handled = true;
 					}
 				}
@@ -17166,8 +17238,8 @@ void BfExprEvaluator::InitializedSizedArray(BfSizedArrayType* arrayType, BfToken
 			{
 				if (checkArrayType->mElementCount == -1)
 				{
-					mModule->Fail("Initializers not supported for unknown-sized arrays", valueExprs[0]);
-					failedAt.Add(depth);
+// 					mModule->Fail("Initializers not supported for unknown-sized arrays", valueExprs[0]);
+// 					failedAt.Add(depth);
 				}
 				else if (initCountDiff > 0)
 				{
@@ -17453,6 +17525,10 @@ void BfExprEvaluator::InitializedSizedArray(BfSizedArrayType* arrayType, BfToken
 				members.push_back(defaultVal.mValue);				
 			}
 			
+			auto allocArrayType = checkArrayType;
+			if (checkArrayType->IsUndefSizedArray())
+				allocArrayType = mModule->CreateSizedArrayType(checkArrayType->GetUnderlyingType(), (int)members.size());
+
 			if (checkArrayType->mElementType->IsStruct())
 			{
 				// This fixed cases where we have non-size-aligned initializers. Assume zero-initialized
@@ -17629,19 +17705,63 @@ void BfExprEvaluator::Visit(BfTupleExpression* tupleExpr)
 		mResult = *mReceivingValue;
 		mReceivingValue = NULL;
 		curTupleValue = mResult.mValue;
-	}
+	}	
 	else
 	{
+		int valueIdx = -1;
+		bool isExactConst = true;		
+
+		for (int fieldIdx = 0; fieldIdx < (int)tupleType->mFieldInstances.size(); fieldIdx++)
+		{
+			BfFieldInstance* fieldInstance = &tupleType->mFieldInstances[fieldIdx];
+			if (fieldInstance->mDataIdx < 0)
+				continue;
+			++valueIdx;
+			auto typedValue = typedValues[valueIdx];
+			if (typedValue.mType != fieldInstance->mResolvedType)
+			{
+				isExactConst = false;
+				break;
+			}
+			if (!typedValue.mValue.IsConst())
+			{
+				isExactConst = false;
+				break;
+			}
+		}
+
+		if (isExactConst)
+		{	
+			mModule->PopulateType(tupleType);
+
+			Array<BfIRValue> irValues;
+			irValues.Resize(typedValues.mSize + 1);
+			irValues[0] = mModule->mBfIRBuilder->CreateConstStructZero(mModule->mBfIRBuilder->MapType(tupleType->mBaseType));
+
+			for (int fieldIdx = 0; fieldIdx < (int)tupleType->mFieldInstances.size(); fieldIdx++)
+			{
+				BfFieldInstance* fieldInstance = &tupleType->mFieldInstances[fieldIdx];
+				if (fieldInstance->mDataIdx < 0)
+					continue;
+				irValues[fieldInstance->mDataIdx] = typedValues[fieldIdx].mValue;
+			}
+			
+			mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConstAgg(mModule->mBfIRBuilder->MapType(tupleType), irValues), tupleType);
+			return;
+		}
+		
 		curTupleValue = mModule->CreateAlloca(tupleType);
 		mResultIsTempComposite = true;
-		mResult = BfTypedValue(curTupleValue, tupleType, BfTypedValueKind_TempAddr);
+		mResult = BfTypedValue(curTupleValue, tupleType, BfTypedValueKind_TempAddr);		
 	}
 
-	for (int valueIdx = 0; valueIdx < (int)typedValues.size(); valueIdx++)
+	int valueIdx = -1;
+	for (int fieldIdx = 0; fieldIdx < (int)tupleType->mFieldInstances.size(); fieldIdx++)
 	{
-		BfFieldInstance* fieldInstance = &tupleType->mFieldInstances[valueIdx];
+		BfFieldInstance* fieldInstance = &tupleType->mFieldInstances[fieldIdx];
 		if (fieldInstance->mResolvedType->IsValuelessType())
 			continue;
+		++valueIdx;
 		auto typedVal = typedValues[valueIdx];
 		if (!typedVal)
 		{
@@ -20511,6 +20631,20 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod
 			
 			if (!handled)
 			{
+				if ((leftValue.mType->IsUndefSizedArray()) || (rightValue.mType->IsUndefSizedArray()))
+				{
+					if ((leftValue.mType->IsSizedArray()) && (rightValue.mType->IsSizedArray() &&
+						(leftValue.mType->GetUnderlyingType() == rightValue.mType->GetUnderlyingType())))
+					{
+						if (BfBinOpEqualityCheck(binaryOp))
+						{
+							auto boolType = mModule->GetPrimitiveType(BfTypeCode_Boolean);
+							mResult = mModule->GetDefaultTypedValue(boolType, false, BfDefaultValueKind_Undef);
+							return;
+						}
+					}
+				}
+
 				mModule->Fail(StrFormat("Operator '%s' cannot be applied to operands of type '%s' and '%s'",
 					BfGetOpName(binaryOp),
 					mModule->TypeToString(leftValue.mType).c_str(),

+ 16 - 10
IDEHelper/Compiler/BfIRBuilder.cpp

@@ -175,9 +175,7 @@ USING_NS_BF;
 	auto constRHS = GetConstantById(rhs.mId); \
 	if ((constLHS->mConstType == BfConstType_Undef) || (constRHS->mConstType == BfConstType_Undef)) \
 	{ \
-		if (((constLHS->mConstType == BfConstType_Undef) || (constLHS->mTypeCode < BfTypeCode_Length)) && \
-			((constRHS->mConstType == BfConstType_Undef) || (constRHS->mTypeCode < BfTypeCode_Length))) \
-			return GetUndefConstValue(BfTypeCode_Boolean); \
+		return GetUndefConstValue(MapType(mModule->GetPrimitiveType(BfTypeCode_Boolean))); \
 	} \
 	if ((constLHS->mTypeCode < BfTypeCode_Length) && (constRHS->mTypeCode < BfTypeCode_Length)) \
 	{ \
@@ -654,6 +652,14 @@ BfIRValue BfIRConstHolder::CreateConst(BfConstant* fromConst, BfIRConstHolder* f
 		}
 		return CreateConstAgg(constAgg->mType, copiedVals);
 	}
+	else if (fromConst->mConstType == BfConstType_Undef)
+	{
+		auto constUndef = (BfConstantUndef*)fromConst;
+		BF_ASSERT(constUndef->mType.mKind != BfIRTypeData::TypeKind_Stream);
+		if (constUndef->mType.mKind == BfIRTypeData::TypeKind_Stream)
+			return GetUndefConstValue(BfIRValue());
+		return GetUndefConstValue(constUndef->mType);
+	}
 	else if ((IsInt(fromConst->mTypeCode)) || (fromConst->mTypeCode == BfTypeCode_Boolean) || (fromConst->mTypeCode == BfTypeCode_StringId))
 	{		
 		return CreateConst(fromConst->mTypeCode, fromConst->mUInt64);		
@@ -778,11 +784,11 @@ BfIRValue BfIRConstHolder::CreateTypeOf(BfType* type)
 	return irValue;
 }
 
-BfIRValue BfIRConstHolder::GetUndefConstValue(BfTypeCode typeCode)
+BfIRValue BfIRConstHolder::GetUndefConstValue(BfIRType irType)
 {	
 	auto constUndef = mTempAlloc.Alloc<BfConstantUndef>();
 	constUndef->mConstType = BfConstType_Undef;	
-	constUndef->mTypeCode = typeCode;
+	constUndef->mType = irType;
 
 	BfIRValue undefVal(BfIRValueFlags_Const, mTempAlloc.GetChunkedId(constUndef));		
 #ifdef CHECK_CONSTHOLDER
@@ -3461,12 +3467,11 @@ BfIRFunction BfIRBuilder::GetFakeFunction()
 }
 
 BfIRType BfIRBuilder::GetPrimitiveType(BfTypeCode typeCode)
-{	
-	
+{
 	FixTypeCode(typeCode);
-	BfIRType retType = WriteCmd(BfIRCmd_PrimitiveType, typeCode);
-	NEW_CMD_INSERTED_IRTYPE;
-	return retType;
+ 	BfIRType retType = WriteCmd(BfIRCmd_PrimitiveType, typeCode);
+ 	NEW_CMD_INSERTED_IRTYPE;
+ 	return retType;
 }
 
 BfIRType BfIRBuilder::CreateStructType(const StringImpl& name)
@@ -3495,6 +3500,7 @@ BfIRType BfIRBuilder::MapType(BfType* type, BfIRPopulateType populateType)
 	{				
 		PopulateType(type, populateType);
 	}
+	BF_ASSERT(type->mTypeId > 0);
 	BfIRType retType;
 	retType.mKind = BfIRType::TypeKind_TypeId;
 	retType.mId = type->mTypeId;

+ 2 - 2
IDEHelper/Compiler/BfIRBuilder.h

@@ -815,7 +815,7 @@ struct BfConstantSizedArrayType
 struct BfConstantUndef
 {
 	BfConstType mConstType;
-	BfTypeCode mTypeCode;
+	BfIRType mType;
 };
 
 struct BfConstantBitCast
@@ -903,7 +903,7 @@ public:
 	BfIRValue CreateConstArrayZero(BfIRType type, int count);
 	BfIRValue CreateConstArrayZero(int count);
 	BfIRValue CreateTypeOf(BfType* type);
-	BfIRValue GetUndefConstValue(BfTypeCode typeCode);	
+	BfIRValue GetUndefConstValue(BfIRType type);	
 };
 
 enum BfIRPopulateType

+ 59 - 28
IDEHelper/Compiler/BfModule.cpp

@@ -83,8 +83,8 @@ BfLocalMethod::~BfLocalMethod()
 		BF_ASSERT(mSource->mRefCount >= 0);
 	}
 
-	delete mMethodDef;
-	delete mMethodInstanceGroup;	
+	delete mMethodInstanceGroup;
+	delete mMethodDef;	
 }
 
 void BfDeferredLocalAssignData::ExtendFrom(BfDeferredLocalAssignData* outerLocalAssignData, bool doChain)
@@ -1134,7 +1134,7 @@ void BfModule::EnsureIRBuilder(bool dbgVerifyCodeGen)
 			//  code as we walk the AST
 			//mBfIRBuilder->mDbgVerifyCodeGen = true;			
 			if (
-                (mModuleName == "-")
+                (mModuleName == "System_StringView")
 				//|| (mModuleName == "BeefTest2_ClearColorValue")
 				//|| (mModuleName == "Tests_FuncRefs")
 				)
@@ -1441,12 +1441,12 @@ BfTypedValue BfModule::GetDefaultTypedValue(BfType* type, bool allowRef, BfDefau
 
 	if (defaultValueKind == BfDefaultValueKind_Undef)
 	{
-		auto primType = type->ToPrimitiveType();
-		if (primType != NULL)
-		{
-			return BfTypedValue(mBfIRBuilder->GetUndefConstValue(primType->mTypeDef->mTypeCode), type);
-		}
-		return BfTypedValue(mBfIRBuilder->CreateUndefValue(mBfIRBuilder->MapType(type)), type);
+// 		auto primType = type->ToPrimitiveType();
+// 		if (primType != NULL)
+// 		{
+// 			return BfTypedValue(mBfIRBuilder->GetUndefConstValue( primType), type);
+// 		}
+		return BfTypedValue(mBfIRBuilder->GetUndefConstValue(mBfIRBuilder->MapType(type)), type);
 	}
 
 	BfTypedValue typedValue;
@@ -5390,7 +5390,7 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary<int, int>& usedStrin
 			}
 		}
 
-		if ((!typeInstance->IsInterface()) && (typeInstance->mVirtualMethodTableSize > 0) && (needsVData))
+		if ((!mIsConstModule) && (!typeInstance->IsInterface()) && (typeInstance->mVirtualMethodTableSize > 0) && (needsVData))
 		{
 			int startTabIdx = (int)vData.size();
 			
@@ -7925,7 +7925,7 @@ BfTypedValue BfModule::CreateValueFromExpression(BfExprEvaluator& exprEvaluator,
 					BfTypedValue result;
 					result.mKind = BfTypedValueKind_Value;
 					result.mType = genericTypeConstraint;
-					result.mValue = mBfIRBuilder->GetUndefConstValue(primType->mTypeDef->mTypeCode);
+					result.mValue = mBfIRBuilder->GetUndefConstValue(mBfIRBuilder->MapType(primType));
 					typedVal = result;
 					handled = true;
 				}				
@@ -12064,6 +12064,11 @@ void BfModule::AddMethodReference(const BfMethodRef& methodRef, BfGetMethodInsta
 
 BfModuleMethodInstance BfModule::ReferenceExternalMethodInstance(BfMethodInstance* methodInstance, BfGetMethodInstanceFlags flags)
 {	
+	if (mIsConstModule)
+	{
+		NOP;
+	}
+
 	if ((flags & BfGetMethodInstanceFlag_ResultNotUsed) != 0)
 		return BfModuleMethodInstance(methodInstance, BfIRValue());
 
@@ -12257,6 +12262,11 @@ BfModule* BfModule::GetOrCreateMethodModule(BfMethodInstance* methodInstance)
 
 BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfMethodDef* methodDef, const BfTypeVector& methodGenericArguments, BfGetMethodInstanceFlags flags, BfTypeInstance* foreignType)
 {
+	if (mIsConstModule)
+	{
+		NOP;
+	}
+
 	if (methodDef->mMethodType == BfMethodType_Init)
 		return BfModuleMethodInstance();
 
@@ -12338,7 +12348,7 @@ BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfM
 		if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mMethodInfoEx != NULL) && (mCurMethodInstance->mMethodInfoEx->mMinDependDepth >= 32))
 			flags = (BfGetMethodInstanceFlags)(flags | BfGetMethodInstanceFlag_DepthExceeded);
 
-		if ((!mIsReified) && (instModule->mIsReified))
+		if ((!mIsConstModule) && (!mIsReified) && (instModule->mIsReified))
 		{
 			BF_ASSERT(!mCompiler->mIsResolveOnly);
 			// A resolve-only module is specializing a method from a type in a reified module, 
@@ -12351,7 +12361,7 @@ BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfM
 		}
 		else
 		{
-			if ((mIsReified) && (!instModule->mIsReified))
+			if ((!mIsConstModule) && (mIsReified) && (!instModule->mIsReified))
 			{
 				if (!typeInst->IsUnspecializedTypeVariation())
 				{
@@ -13080,6 +13090,10 @@ void BfModule::SetupMethodIdHash(BfMethodInstance* methodInstance)
 	}	
 
 	methodInstance->mIdHash = (int64)hashCtx.Finish64();
+	if (methodInstance->mIdHash == 0x3400000029LL)
+	{
+		NOP;
+	}	
 }
 
 BfIRValue BfModule::GetInterfaceSlotNum(BfTypeInstance* ifaceType)
@@ -13134,6 +13148,18 @@ BfTypedValue BfModule::GetCompilerFieldValue(const StringImpl& str)
 	{
 		return BfTypedValue(mBfIRBuilder->CreateConst(BfTypeCode_Boolean, mIsConstModule ? 1 : 0), GetPrimitiveType(BfTypeCode_Boolean));
 	}
+	if (str == "#IsBuilding")
+	{
+		return BfTypedValue(mBfIRBuilder->CreateConst(BfTypeCode_Boolean, (!mCompiler->mIsResolveOnly) ? 1 : 0), GetPrimitiveType(BfTypeCode_Boolean));
+	}
+	if (str == "#IsReified")
+	{
+		return BfTypedValue(mBfIRBuilder->CreateConst(BfTypeCode_Boolean, mIsReified ? 1 : 0), GetPrimitiveType(BfTypeCode_Boolean));
+	}
+	if (str == "#CompileRev")
+	{
+		return BfTypedValue(mBfIRBuilder->CreateConst(BfTypeCode_Int32, mCompiler->mRevision), GetPrimitiveType(BfTypeCode_Int32));
+	}
 	if (str == "#ModuleName")
 	{
 		return BfTypedValue(GetStringObjectValue(mModuleName), ResolveTypeDef(mCompiler->mStringTypeDef));
@@ -14504,7 +14530,7 @@ void BfModule::CreateDelegateInvokeMethod()
 	if (mCurMethodInstance->mReturnType->IsValueType())
 		mBfIRBuilder->PopulateType(mCurMethodInstance->mReturnType, BfIRPopulateType_Full);
 		
-	if ((!mIsConstModule) && (mCurMethodInstance->GetStructRetIdx() != 0))
+	if ((mIsConstModule) || (mCurMethodInstance->GetStructRetIdx() != 0))
 		memberFuncArgs.push_back(BfIRValue()); // Push 'target'
 
 	int thisIdx = 0;
@@ -17078,11 +17104,13 @@ void BfModule::ProcessMethod_ProcessDeferredLocals(int startIdx)
 				BP_ZONE_F("ProcessMethod lambdaInstance %s", lambdaInstance->mMethodInstance->mMethodDef->mName.c_str());
 				lambdaInstance->mMethodInstance->mMethodInstanceGroup = &methodInstanceGroup;
 				ProcessMethod(lambdaInstance->mMethodInstance);
+				lambdaInstance->mMethodInstance->mMethodInstanceGroup = NULL;
 
 				if (lambdaInstance->mDtorMethodInstance != NULL)
 				{
 					lambdaInstance->mDtorMethodInstance->mMethodInstanceGroup = &methodInstanceGroup;
 					ProcessMethod(lambdaInstance->mDtorMethodInstance);
+					lambdaInstance->mDtorMethodInstance->mMethodInstanceGroup = NULL;
 				}
 			}
 		}
@@ -17546,22 +17574,25 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup)
 
 	BF_ASSERT(methodInstance->mMethodInstanceGroup->mOnDemandKind != BfMethodOnDemandKind_NotSet);
 
-	if ((methodInstance->mMethodInstanceGroup->mOnDemandKind != BfMethodOnDemandKind_AlwaysInclude) &&
-		(methodInstance->mMethodInstanceGroup->mOnDemandKind != BfMethodOnDemandKind_Referenced))
+	if (!mIsConstModule)
 	{
-		methodInstance->mMethodInstanceGroup->mOnDemandKind = BfMethodOnDemandKind_Referenced;
-		
-		auto owningModule = methodInstance->GetOwner()->mModule;
-
-		if (owningModule->mIsScratchModule)
+		if ((methodInstance->mMethodInstanceGroup->mOnDemandKind != BfMethodOnDemandKind_AlwaysInclude) &&
+			(methodInstance->mMethodInstanceGroup->mOnDemandKind != BfMethodOnDemandKind_Referenced))
 		{
-			BF_ASSERT(owningModule->mOnDemandMethodCount == 0);
-		}
-		else
-        {
-			BF_ASSERT((owningModule->mOnDemandMethodCount > 0) || (!HasCompiledOutput()) || (owningModule->mExtensionCount > 0));
-            if (owningModule->mOnDemandMethodCount > 0)
-				owningModule->mOnDemandMethodCount--;
+			methodInstance->mMethodInstanceGroup->mOnDemandKind = BfMethodOnDemandKind_Referenced;
+
+			auto owningModule = methodInstance->GetOwner()->mModule;
+
+			if (owningModule->mIsScratchModule)
+			{
+				BF_ASSERT(owningModule->mOnDemandMethodCount == 0);
+			}
+			else
+			{
+				BF_ASSERT((owningModule->mOnDemandMethodCount > 0) || (!HasCompiledOutput()) || (owningModule->mExtensionCount > 0));
+				if (owningModule->mOnDemandMethodCount > 0)
+					owningModule->mOnDemandMethodCount--;
+			}
 		}
 	}
 

+ 7 - 4
IDEHelper/Compiler/BfModule.h

@@ -69,7 +69,8 @@ enum BfEvalExprFlags
 	BfEvalExprFlags_AllowNonConst = 0x10000,
 	BfEvalExprFlags_StringInterpolateFormat = 0x20000,
 	BfEvalExprFlags_NoLookupError = 0x40000,
-	BfEvalExprFlags_ConstExpr = 0x80000,
+	BfEvalExprFlags_ConstEval = 0x80000,
+	BfEvalExprFlags_InCascade = 0x100000,
 };
 
 enum BfCastFlags
@@ -904,13 +905,15 @@ public:
 
 	~BfLambdaInstance()
 	{
-		delete mMethodInstance->mMethodDef;
+		auto methodDef = mMethodInstance->mMethodDef;
 		delete mMethodInstance;
+		delete methodDef;		
 		
 		if (mDtorMethodInstance != NULL)
 		{
-			delete mDtorMethodInstance->mMethodDef;
-			delete mDtorMethodInstance;			
+			auto methodDef = mDtorMethodInstance->mMethodDef;
+			delete mDtorMethodInstance;
+			delete methodDef;
 		}
 	}
 };

+ 7 - 6
IDEHelper/Compiler/BfModuleTypeUtils.cpp

@@ -10169,12 +10169,12 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp
 
 			if (allowCast)
 			{
-				if (ignoreWrites)
+				if ((ignoreWrites) && (!typedVal.mValue.IsConst()))
 					return mBfIRBuilder->GetFakeVal();
 				return mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapType(toType));
 			}
 		}
-	}	
+	}
 
 	// MethodRef -> Function
 	if ((typedVal.mType->IsMethodRef()) && (toType->IsFunction()))
@@ -10566,12 +10566,13 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp
 
 					auto undefConst = (BfConstantUndef*)constant;
 					
-					auto fakeVal = GetFakeTypedValue(GetPrimitiveType(undefConst->mTypeCode));
-					// Why did we have this BfCastFlags_Explicit? It broke creating errors on things like "int16 val = TCount;"
-					//auto val = CastToValue(srcNode, fakeVal, toType, (BfCastFlags)(castFlags | BfCastFlags_Explicit));
+					BF_ASSERT(undefConst->mType.mKind == BfIRTypeData::TypeKind_TypeId);
+					auto bfType = mContext->mTypes[undefConst->mType.mId];
+
+					auto fakeVal = GetFakeTypedValue(bfType);
 					auto val = CastToValue(srcNode, fakeVal, toType, castFlags);
 					if (val)
-						return val;
+						return mBfIRBuilder->GetUndefConstValue(mBfIRBuilder->MapType(toType));
 				}
 			}
 		}

+ 1 - 1
IDEHelper/Compiler/BfReducer.cpp

@@ -5441,7 +5441,7 @@ BfExpression* BfReducer::CreateAttributedExpression(BfTokenNode* tokenNode, bool
 
 	if (!onlyAllowIdentifier)
 	{		
-		BfExpression* expr = CreateExpressionAfter(attrib);
+		BfExpression* expr = CreateExpressionAfter(attrib, CreateExprFlags_EarlyExit);
 		if (expr != NULL)
 		{
 			if (auto identifier = BfNodeDynCast<BfIdentifierNode>(expr))

+ 7 - 0
IDEHelper/Compiler/BfResolvedTypeUtils.cpp

@@ -535,6 +535,13 @@ BfMethodInfoEx::~BfMethodInfoEx()
 
 BfMethodInstance::~BfMethodInstance()
 {		
+	if (mMethodInstanceGroup != NULL)
+		BfLogSys(GetOwner()->mModule->mSystem, "BfMethodInstance::~BfMethodInstance %p Local:%d InCEMachine:%d\n", this, mMethodDef->mIsLocalMethod, mInCEMachine);
+	else
+	{
+		BF_ASSERT(!mMethodDef->mIsLocalMethod);
+	}
+
 	if (mInCEMachine)
 	{
 		auto module = GetOwner()->mModule;

+ 7 - 2
IDEHelper/Compiler/BfStmtEvaluator.cpp

@@ -6438,10 +6438,15 @@ void BfModule::Visit(BfForEachStatement* forEachStmt)
 				methodMatcher.CheckType(itrInterface, itr, false);
 			methodMatcher.TryDevirtualizeCall(itr);
 			exprEvaluator.mReceivingValue = &nextResult;			
-			auto retVal = exprEvaluator.CreateCall(&methodMatcher, itr);
+			auto retVal = exprEvaluator.CreateCall(&methodMatcher, itr);			
 			if (exprEvaluator.mReceivingValue != NULL)
 			{
-				AssertErrorState();
+				if (mIsConstModule)
+				{
+					mBfIRBuilder->CreateStore(retVal.mValue, nextResult.mValue);
+				}
+				else
+					AssertErrorState();
 			}
 			if ((retVal) && (!retVal.mType->IsVar()))
 			{

+ 2 - 0
IDEHelper/Compiler/BfSystem.h

@@ -733,6 +733,7 @@ public:
 	bool mIsNoSplat;
 	bool mIsNoReflect;
 	bool mIsSkipCall;
+	bool mIsConstEval;
 	bool mIsOperator;
 	bool mIsExtern;	
 	bool mIsNoDiscard;
@@ -761,6 +762,7 @@ public:
 		mIsNoSplat = false;
 		mIsNoReflect = false;
 		mIsSkipCall = false;
+		mIsConstEval = false;
 		mIsOperator = false;
 		mIsExtern = false;
 		mIsNoDiscard = false;

+ 308 - 89
IDEHelper/Compiler/CeMachine.cpp

@@ -214,6 +214,13 @@ CeFunction::~CeFunction()
 
 //////////////////////////////////////////////////////////////////////////
 
+CeFunctionInfo::~CeFunctionInfo()
+{
+	delete mCeFunction;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
 #define CE_GET(T) *((T*)(mPtr += sizeof(T)) - 1)
 
 void CeDumpContext::DumpOperandInfo(CeOperandInfoKind operandInfoKind)
@@ -2151,6 +2158,10 @@ void CeBuilder::Build()
 
 						break;
 					}
+					else
+					{
+						mcAgg = GetOperand(castedInst->mAggVal);
+					}
 
 					auto aggType = mcAgg.mType;
 					int byteOffset = 0;
@@ -2590,8 +2601,10 @@ CeMachine::CeMachine(BfCompiler* compiler)
 	mCeModule = NULL;		
 	mRevision = 0;
 	mExecuteId = 0;
+	mRevisionExecuteTime = 0;
 	mCurTargetSrc = NULL;
 	mCurModule = NULL;
+	mCurMethodInstance = NULL;
 	mCurExpectingType = NULL;
 	mHeap = NULL;
 	mStringCharsOffset = -1;
@@ -2613,7 +2626,7 @@ CeMachine::~CeMachine()
 
 BfError* CeMachine::Fail(const StringImpl& error)
 {
-	auto bfError = mCurModule->Fail("Unable to const-evaluate function", mCurTargetSrc);
+	auto bfError = mCurModule->Fail(StrFormat("Unable to const-evaluate %s", mCurModule->MethodToString(mCurMethodInstance).c_str()), mCurTargetSrc);
 	if (bfError == NULL)
 		return NULL;
 	mCompiler->mPassInstance->MoreInfo(error);
@@ -2622,7 +2635,7 @@ BfError* CeMachine::Fail(const StringImpl& error)
 
 BfError* CeMachine::Fail(const CeFrame& curFrame, const StringImpl& str)
 {
-	auto bfError = mCurModule->Fail("Unable to const-evaluate function", mCurTargetSrc);
+	auto bfError = mCurModule->Fail(StrFormat("Unable to const-evaluate %s", mCurModule->MethodToString(mCurMethodInstance).c_str()), mCurTargetSrc);
 	if (bfError == NULL)
 		return NULL;
 
@@ -2696,7 +2709,11 @@ void CeMachine::Init()
 	mCeModule->mIsSpecialModule = true;
 	//mCeModule->mIsScratchModule = true;
 	mCeModule->mIsConstModule = true;
-	mCeModule->mIsReified = true;
+	//mCeModule->mIsReified = true;
+	if (mCompiler->mIsResolveOnly)
+		mCeModule->mIsReified = true;
+	else
+		mCeModule->mIsReified = false;
 	mCeModule->Init();
 
 	mCeModule->mBfIRBuilder = new BfIRBuilder(mCeModule);
@@ -2731,6 +2748,28 @@ bool CeMachine::CeFree(addr_ce addr)
 #endif
 }
 
+addr_ce CeMachine::CeAllocArray(BfArrayType* arrayType, int count, addr_ce& elemsAddr)
+{
+	mCeModule->PopulateType(arrayType);
+
+	BfType* elemType = arrayType->GetUnderlyingType();
+	auto countOffset = arrayType->mBaseType->mFieldInstances[0].mDataOffset;
+	auto elemOffset = arrayType->mFieldInstances[0].mDataOffset;	
+
+	int allocSize = elemOffset + elemType->GetStride() * count;
+
+	uint8* mem = CeMalloc(allocSize);
+
+	memset(mem, 0, allocSize);
+
+	*(int32*)(mem) = arrayType->mTypeId;
+	*(int32*)(mem + countOffset) = count;
+
+	elemsAddr = (addr_ce)(mem + elemOffset - mMemory.mVals);
+
+	return (addr_ce)(mem - mMemory.mVals);
+}
+
 addr_ce CeMachine::GetConstantData(BeConstant* constant)
 {
 	auto writeConstant = constant;
@@ -2889,6 +2928,7 @@ BeModule* CeMachine::GetBeModule()
 
 void CeMachine::CompileStarted()
 {
+	mRevisionExecuteTime = 0;
 	mRevision++;
 	if (mCeModule != NULL)
 	{
@@ -2912,37 +2952,48 @@ void CeMachine::DerefMethodInfo(CeFunctionInfo* ceFunctionInfo)
 
 void CeMachine::RemoveMethod(BfMethodInstance* methodInstance)
 {
+	BfLogSys(methodInstance->GetOwner()->mModule->mSystem, "CeMachine::RemoveMethod %p\n", methodInstance);
+
 	auto itr = mFunctions.Find(methodInstance);
 	auto ceFunctionInfo = itr->mValue;
 	BF_ASSERT(itr != mFunctions.end());
 	if (itr != mFunctions.end())
-	{		
-		auto ceFunction = ceFunctionInfo->mCeFunction;
-		for (auto& callEntry : ceFunction->mCallTable)
+	{
+		if (ceFunctionInfo->mMethodInstance == methodInstance)
 		{
-			if (callEntry.mFunctionInfo != NULL)
-				DerefMethodInfo(callEntry.mFunctionInfo);			
-		}
-		delete ceFunction;
-		ceFunctionInfo->mCeFunction = NULL;
-		ceFunctionInfo->mMethodInstance = NULL;
+			auto ceFunction = ceFunctionInfo->mCeFunction;
+			for (auto& callEntry : ceFunction->mCallTable)
+			{
+				if (callEntry.mFunctionInfo != NULL)
+					DerefMethodInfo(callEntry.mFunctionInfo);
+			}
+			delete ceFunction;
+			ceFunctionInfo->mCeFunction = NULL;
+			ceFunctionInfo->mMethodInstance = NULL;
 
-		if (ceFunctionInfo->mRefCount > 1)
-		{
-			// Generate a methodref
-			ceFunctionInfo->mMethodRef = methodInstance;
-		}
+			if (methodInstance->mMethodDef->mIsLocalMethod)
+			{
+				// We can't rebuild these anyway
+			}
+			else if (ceFunctionInfo->mRefCount > 1)
+			{
+				// Generate a methodref
+				ceFunctionInfo->mMethodRef = methodInstance;
+			}
 
-		DerefMethodInfo(ceFunctionInfo);
+			DerefMethodInfo(ceFunctionInfo);
+		}		
 
 		mFunctions.Remove(itr);
 	}
+
+	CheckFunctions();
 }
 
 //#define CE_GETC(T) *((T*)(addr += sizeof(T)) - 1)
 #define CE_GETC(T) *(T*)(mMemory.mVals + addr)
 
-bool CeMachine::WriteConstant(BfModule* module, addr_ce addr, BfConstant* constant, BfType* type)
+bool CeMachine::WriteConstant(BfModule* module, addr_ce addr, BfConstant* constant, BfType* type, bool isParams)
 {	
 	switch (constant->mTypeCode)
 	{
@@ -2987,8 +3038,56 @@ bool CeMachine::WriteConstant(BfModule* module, addr_ce addr, BfConstant* consta
 		{
 			return false;
 		}
-		else
+		else if (type->IsArray())
+		{
+			auto elemType = type->GetUnderlyingType();
+
+			addr_ce elemsAddr = 0;
+			addr_ce arrayAddr = CeAllocArray((BfArrayType*)type, aggConstant->mValues.size(), elemsAddr);			
+
+			for (int i = 0; i < (int)aggConstant->mValues.size(); i++)
+			{
+				auto fieldConstant = module->mBfIRBuilder->GetConstant(aggConstant->mValues[i]);
+				if (fieldConstant == NULL)
+					return false;
+				WriteConstant(module, elemsAddr + i * elemType->GetStride(), fieldConstant, elemType);
+			}
+			
+			if (mCeModule->mSystem->mPtrSize == 4)
+				CE_GETC(int32) = arrayAddr;
+			else
+				CE_GETC(int64) = arrayAddr;
+
+			return true;
+		}		
+		else if ((type->IsInstanceOf(module->mCompiler->mSpanTypeDef)) && (isParams))
 		{
+			auto elemType = type->GetUnderlyingType();
+			addr_ce elemsAddr = CeMalloc(elemType->GetStride() * aggConstant->mValues.size()) - mMemory.mVals;
+
+			for (int i = 0; i < (int)aggConstant->mValues.size(); i++)
+			{
+				auto fieldConstant = module->mBfIRBuilder->GetConstant(aggConstant->mValues[i]);
+				if (fieldConstant == NULL)
+					return false;
+				WriteConstant(module, elemsAddr + i * elemType->GetStride(), fieldConstant, elemType);
+			}
+
+			if (mCeModule->mSystem->mPtrSize == 4)
+			{
+				CE_GETC(int32) = elemsAddr;
+				addr += 4;
+				CE_GETC(int32) = (int32)aggConstant->mValues.size();
+			}
+			else
+			{
+				CE_GETC(int32) = elemsAddr;
+				addr += 8;
+				CE_GETC(int64) = (int32)aggConstant->mValues.size();
+			}
+		}
+		else
+		{			
 			BF_ASSERT(type->IsStruct());
 
 			module->PopulateType(type);
@@ -3087,7 +3186,40 @@ CeErrorKind CeMachine::WriteConstant(CeConstStructData& data, BeConstant* constV
 		}
 
 		if (globalVar->mInitializer == NULL)
-			return CeErrorKind_GlobalVariable;
+		{
+			auto ptr = data.mData.GrowUninitialized(mCeModule->mSystem->mPtrSize);
+			int64 addr64 = (addr_ce)0;
+			memcpy(ptr, &addr64, mCeModule->mSystem->mPtrSize);
+			return CeErrorKind_None;
+
+			//TODO: Add this global variable in there and fixup
+
+// 			CeStaticFieldInfo* staticFieldInfoPtr = NULL;
+// 			if (mCeMachine->mStaticFieldMap.TryGetValue(globalVar->mName, &staticFieldInfoPtr))
+// 			{
+// 				CeStaticFieldInfo* staticFieldInfo = staticFieldInfoPtr;
+// 
+// 				int* staticFieldTableIdxPtr = NULL;
+// 				if (mStaticFieldMap.TryAdd(globalVar, NULL, &staticFieldTableIdxPtr))
+// 				{
+// 					CeStaticFieldEntry staticFieldEntry;
+// 					staticFieldEntry.mTypeId = staticFieldInfo->mFieldInstance->mOwner->mTypeId;
+// 					staticFieldEntry.mName = globalVar->mName;
+// 					staticFieldEntry.mSize = globalVar->mType->mSize;
+// 					*staticFieldTableIdxPtr = (int)mCeFunction->mStaticFieldTable.size();
+// 					mCeFunction->mStaticFieldTable.Add(staticFieldEntry);
+// 				}
+// 
+// 				auto result = FrameAlloc(mCeMachine->GetBeContext()->GetPointerTo(globalVar->mType));
+// 
+// 				Emit(CeOp_GetStaticField);
+// 				EmitFrameOffset(result);
+// 				Emit((int32)*staticFieldTableIdxPtr);
+// 
+// 				return result;
+// 			}
+//			return CeErrorKind_GlobalVariable;
+		}
 
 		
 		BF_ASSERT(!data.mQueueFixups);
@@ -3375,7 +3507,7 @@ BfIRValue CeMachine::CreateConstant(BfModule* module, uint8* ptr, BfType* bfType
 
 		if (typeInst->IsInstanceOf(mCeModule->mCompiler->mSpanTypeDef))
 		{
-			if ((outType != NULL) && (mCurExpectingType->IsSizedArray()))
+			if ((outType != NULL) && ((mCurExpectingType == NULL) || (mCurExpectingType->IsSizedArray())))
 			{				
 				module->PopulateType(typeInst);
 
@@ -3402,6 +3534,9 @@ BfIRValue CeMachine::CreateConstant(BfModule* module, uint8* ptr, BfType* bfType
 				*outType = module->CreateSizedArrayType(elemType, lenVal);
 				return instResult;
 			}
+
+			Fail(StrFormat("Span return type '%s' must be received by a sized array", module->TypeToString(typeInst).c_str()));
+			return BfIRValue();
 		}
 
 		if (typeInst->IsObjectOrInterface())
@@ -3590,7 +3725,7 @@ BfIRValue CeMachine::CreateConstant(BfModule* module, uint8* ptr, BfType* bfType
 
 bool CeMachine::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* startFramePtr)
 {
-	mExecuteId++;	
+	mExecuteId++;
 
 	CeFunction* ceFunction = startFunction;
 	uint8* memStart = &mMemory[0];	
@@ -3598,7 +3733,9 @@ bool CeMachine::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
 	uint8* instPtr = (ceFunction->mCode.IsEmpty()) ? NULL : &ceFunction->mCode[0];
 	uint8* stackPtr = startStackPtr;
 	uint8* framePtr = startFramePtr;
-		
+	
+	volatile bool* cancelPtr = &mCompiler->mCanceling;
+
 	auto _GetCurFrame = [&]()
 	{
 		CeFrame ceFrame;
@@ -3659,32 +3796,6 @@ bool CeMachine::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
 				handled = true;
 				return true;
 			}
-			else if (checkFunction->mFunctionKind == CeFunctionKind_DebugWrite)
-			{
-				int32 ptrVal = *(int32*)((uint8*)stackPtr + 0);
-				auto size = *(int32*)(stackPtr + mCeModule->mSystem->mPtrSize);
-				CE_CHECKADDR(ptrVal, size);
-				char* strPtr = (char*)(ptrVal + memStart);
-				String str;
-				str.Insert(0, strPtr, size);
-				OutputDebugStr(str);
-				handled = true;
-				return true;
-			}
-			else if (checkFunction->mFunctionKind == CeFunctionKind_DebugWrite_Int)
-			{
-				int32 intVal = *(int32*)((uint8*)stackPtr + 0);
-				OutputDebugStrF("Debug Val: %d\n", intVal);
-				handled = true;
-				return true;
-			}
-			else if (checkFunction->mFunctionKind == CeFunctionKind_GetReflectType)
-			{
-				int32 typeId = *(int32*)((uint8*)stackPtr + mCeModule->mSystem->mPtrSize);
-				auto reflectType = GetReflectType(typeId);
-				_FixVariables();
-				*(addr_ce*)(stackPtr + 0) = reflectType;
-			}
 			else if (checkFunction->mFunctionKind == CeFunctionKind_FatalError)
 			{
 				int32 strInstAddr = *(int32*)((uint8*)stackPtr + 0);
@@ -3725,6 +3836,67 @@ bool CeMachine::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
 
 				return false;
 			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_DebugWrite)
+			{
+				int32 ptrVal = *(int32*)((uint8*)stackPtr + 0);
+				auto size = *(int32*)(stackPtr + mCeModule->mSystem->mPtrSize);
+				CE_CHECKADDR(ptrVal, size);
+				char* strPtr = (char*)(ptrVal + memStart);
+				String str;
+				str.Insert(0, strPtr, size);
+				OutputDebugStr(str);
+				handled = true;
+				return true;
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_DebugWrite_Int)
+			{
+				int32 intVal = *(int32*)((uint8*)stackPtr + 0);
+				OutputDebugStrF("Debug Val: %d\n", intVal);
+				handled = true;
+				return true;
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_GetReflectType)
+			{
+				addr_ce objAddr = *(addr_ce*)((uint8*)stackPtr + mCeModule->mSystem->mPtrSize);
+				CE_CHECKADDR(addr_ce, 4);
+				int32 typeId = *(int32*)(objAddr + memStart);
+
+				auto reflectType = GetReflectType(typeId);
+				_FixVariables();
+				*(addr_ce*)(stackPtr + 0) = reflectType;
+				handled = true;
+				return true;
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_GetReflectTypeById)
+			{
+				int32 typeId = *(int32*)((uint8*)stackPtr + mCeModule->mSystem->mPtrSize);
+				auto reflectType = GetReflectType(typeId);
+				_FixVariables();
+				*(addr_ce*)(stackPtr + 0) = reflectType;
+				handled = true;
+				return true;
+			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_Sleep)
+			{
+				int32 sleepMS = *(int32*)((uint8*)stackPtr);
+				while (sleepMS > 0)
+				{
+					if (*cancelPtr)
+						break;
+
+					if (sleepMS > 200)
+					{
+						Sleep(200);
+						sleepMS -= 200;
+						continue;
+					}
+					Sleep(sleepMS);
+					break;
+				}
+				
+				handled = true;
+				return true;
+			}			
 			else if (checkFunction->mFunctionKind == CeFunctionKind_Char32_ToLower)
 			{
 				int32& result = *(int32*)((uint8*)stackPtr + 0);
@@ -3847,9 +4019,7 @@ bool CeMachine::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
 			return false;
 		if (handled)
 			return true;
-	}
-
-	volatile bool* cancelPtr = &mCompiler->mCanceling;
+	}	
 
 	int callCount = 0;
 	int instIdx = 0;
@@ -3858,7 +4028,7 @@ bool CeMachine::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
 	{		
 		if (*cancelPtr)
 		{
-			_Fail("Cancelled");
+			_Fail("Compilation cancelled");
 			return false;
 		}
 
@@ -4989,6 +5159,8 @@ bool CeMachine::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
 
 void CeMachine::PrepareFunction(CeFunction* ceFunction, CeBuilder* parentBuilder)
 {
+	AutoTimer autoTimer(mRevisionExecuteTime);
+
 	if (mHeap == NULL)
 		mHeap = new	ContiguousHeap();
 
@@ -5007,6 +5179,13 @@ void CeMachine::PrepareFunction(CeFunction* ceFunction, CeBuilder* parentBuilder
 					ceFunction->mFunctionKind = CeFunctionKind_GetReflectType;
 				}
 			}
+			else if (owner->IsInstanceOf(mCeModule->mCompiler->mTypeTypeDef))
+			{
+				if (methodDef->mName == "ConstEval_GetTypeById")
+				{
+					ceFunction->mFunctionKind = CeFunctionKind_GetReflectTypeById;
+				}
+			}
 			else if (owner->IsInstanceOf(mCeModule->mCompiler->mDiagnosticsDebugTypeDef))
 			{
 				if (methodDef->mName == "Write")
@@ -5017,6 +5196,11 @@ void CeMachine::PrepareFunction(CeFunction* ceFunction, CeBuilder* parentBuilder
 						ceFunction->mFunctionKind = CeFunctionKind_DebugWrite;
 				}
 			}
+			else if (owner->IsInstanceOf(mCeModule->mCompiler->mThreadTypeDef))
+			{
+				if (methodDef->mName == "SleepInternal")
+					ceFunction->mFunctionKind = CeFunctionKind_Sleep;
+			}
 			else if (owner->IsInstanceOf(mCeModule->mCompiler->mInternalTypeDef))
 			{
 				if (methodDef->mName == "ThrowIndexOutOfRange")
@@ -5063,7 +5247,7 @@ void CeMachine::PrepareFunction(CeFunction* ceFunction, CeBuilder* parentBuilder
 	ceBuilder.mCeFunction = ceFunction;
 	ceBuilder.Build();
 
-	if (!ceFunction->mCode.IsEmpty())
+	/*if (!ceFunction->mCode.IsEmpty())
 	{
 		CeDumpContext dumpCtx;
 		dumpCtx.mCeFunction = ceFunction;
@@ -5073,6 +5257,14 @@ void CeMachine::PrepareFunction(CeFunction* ceFunction, CeBuilder* parentBuilder
 		dumpCtx.Dump();
 
 		OutputDebugStrF("Code for %s:\n%s\n", ceBuilder.mBeFunction->mName.c_str(), dumpCtx.mStr.c_str());
+	}*/
+}
+
+void CeMachine::CheckFunctions()
+{
+	for (auto kv : mFunctions)
+	{
+		BF_ASSERT((((intptr)(void*)kv.mKey->mMethodDef) & 0xFF) != 0xDD);
 	}
 }
 
@@ -5084,6 +5276,8 @@ CeFunction* CeMachine::GetFunction(BfMethodInstance* methodInstance, BfIRValue f
 			return NULL;
 	}
 
+	CheckFunctions();
+
 	CeFunctionInfo** functionInfoPtr = NULL;
 	CeFunctionInfo* ceFunctionInfo = NULL;
 	CeFunction* ceFunction = NULL;
@@ -5093,6 +5287,11 @@ CeFunction* CeMachine::GetFunction(BfMethodInstance* methodInstance, BfIRValue f
 		BF_ASSERT(ceFunctionInfo->mCeFunction != NULL);
 		return ceFunctionInfo->mCeFunction;
 	}
+
+	BF_ASSERT(!methodInstance->mInCEMachine);
+	methodInstance->mInCEMachine = true;
+
+	BfLogSys(mCeModule->mSystem, "CeMachine::GetFunction %p\n", methodInstance);
 	
 	if (!func)
 	{
@@ -5130,8 +5329,7 @@ CeFunction* CeMachine::GetFunction(BfMethodInstance* methodInstance, BfIRValue f
 		added = true;
 		auto module = methodInstance->GetOwner()->mModule;
 		
-		BF_ASSERT(!methodInstance->mInCEMachine);
-		methodInstance->mInCEMachine = true;			
+		BF_ASSERT(ceFunctionInfo->mCeFunction == NULL);
 
 		ceFunction = new CeFunction();
 		ceFunction->mCeFunctionInfo = ceFunctionInfo;
@@ -5140,6 +5338,8 @@ CeFunction* CeMachine::GetFunction(BfMethodInstance* methodInstance, BfIRValue f
 		ceFunctionInfo->mMethodInstance = methodInstance;
 		ceFunctionInfo->mCeFunction = ceFunction;
 	}
+	
+
 	return ceFunction;
 }
 
@@ -5188,14 +5388,24 @@ void CeMachine::ClearAppendAllocInfo()
 }
 
 BfTypedValue CeMachine::Call(BfAstNode* targetSrc, BfModule* module, BfMethodInstance* methodInstance, const BfSizedArray<BfIRValue>& args, CeEvalFlags flags, BfType* expectingType)
-{	
+{
+	AutoTimer autoTimer(mRevisionExecuteTime);
+
 	// DISABLED
-	return BfTypedValue();
+	//return BfTypedValue();
+
+	SetAndRestoreValue<BfAstNode*> prevTargetSrc(mCurTargetSrc, targetSrc);
+	SetAndRestoreValue<BfModule*> prevModule(mCurModule, module);
+	SetAndRestoreValue<BfMethodInstance*> prevMethodInstance(mCurMethodInstance, methodInstance);
+	SetAndRestoreValue<BfType*> prevExpectingType(mCurExpectingType, expectingType);
 
 	if (mAppendAllocInfo != NULL)
 	{
 		if ((mAppendAllocInfo->mAppendSizeValue) && (!mAppendAllocInfo->mAppendSizeValue.IsConst()))
+		{
+			Fail("Non-constant append alloc");
 			return BfTypedValue();
+		}
 	}
 
 	int thisArgIdx = -1;
@@ -5206,22 +5416,39 @@ BfTypedValue CeMachine::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns
 			thisArgIdx = 0;
 		if ((methodInstance->GetParamCount() >= 1) && (methodInstance->GetParamKind(0) == BfParamKind_AppendIdx))
 			appendAllocIdx = 1;
-	}
+	}	
 
-	for (int argIdx = 0; argIdx < (int)args.size(); argIdx++)
+	int paramCompositeSize = 0;
+	int paramIdx = methodInstance->GetParamCount();
+	for (int argIdx = (int)args.size() - 1; argIdx >= 0; argIdx--)
 	{
+		BfType* paramType = NULL;
+		while (true)
+		{
+			paramIdx--;
+			paramType = methodInstance->GetParamType(paramIdx);
+			if (paramType->IsTypedPrimitive())
+				paramType = paramType->GetUnderlyingType();
+			if (!paramType->IsValuelessType())
+				break;
+		}
+		if (paramType->IsComposite())
+		{
+			auto paramTypeInst = paramType->ToTypeInstance();
+			paramCompositeSize += paramTypeInst->mInstSize;
+		}
+
 		auto arg = args[argIdx];
 		if (!arg.IsConst())
 		{
 			if ((argIdx != thisArgIdx) && (argIdx != appendAllocIdx))
+			{
+				Fail(StrFormat("Non-constant argument for param '%s'", methodInstance->GetParamName(paramIdx).c_str()));
 				return BfTypedValue();
+			}
 		}
 	}
-	
-	SetAndRestoreValue<BfAstNode*> prevTargetSrc(mCurTargetSrc, targetSrc);
-	SetAndRestoreValue<BfModule*> prevModule(mCurModule, module);
-	SetAndRestoreValue<BfType*> prevExpectingType(mCurExpectingType, expectingType);
-
+		
 	BF_ASSERT(mCallStack.IsEmpty());
 	
 	auto methodDef = methodInstance->mMethodDef;	
@@ -5285,28 +5512,9 @@ BfTypedValue CeMachine::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns
 		memStart += memOffset;
 		stackPtr += memOffset;		
 	};
-
+	
 	addr_ce compositeStartAddr = stackPtr - memStart;
-	int paramIdx = methodInstance->GetParamCount();
-	for (int argIdx = (int)args.size() - 1; argIdx >= 0; argIdx--)
-	{
-		BfType* paramType = NULL;		
-		while (true)
-		{
-			paramIdx--;
-			paramType = methodInstance->GetParamType(paramIdx);
-			if (paramType->IsTypedPrimitive())
-				paramType = paramType->GetUnderlyingType();
-			if (!paramType->IsValuelessType())
-				break;
-		}		
-		if (paramType->IsComposite())
-		{
-			auto paramTypeInst = paramType->ToTypeInstance();
-			stackPtr -= paramTypeInst->mInstSize;
-		}
-	}
-
+	stackPtr -= paramCompositeSize;
 	addr_ce useCompositeAddr = compositeStartAddr;
 	paramIdx = methodInstance->GetParamCount();
 	for (int argIdx = (int)args.size() - 1; argIdx >= 0; argIdx--)
@@ -5322,6 +5530,7 @@ BfTypedValue CeMachine::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns
 				break;
 		}				
 		
+		bool isParams = methodInstance->GetParamKind(paramIdx) == BfParamKind_Params;
  		auto arg = args[argIdx];
 		if (!arg.IsConst())
 		{
@@ -5351,8 +5560,11 @@ BfTypedValue CeMachine::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns
 		{
 			auto paramTypeInst = paramType->ToTypeInstance();
 			useCompositeAddr -= paramTypeInst->mInstSize;
-			if (!WriteConstant(module, useCompositeAddr, constant, paramType))
+			if (!WriteConstant(module, useCompositeAddr, constant, paramType, isParams))
+			{
+				Fail(StrFormat("Failed to process argument for param '%s'", methodInstance->GetParamName(paramIdx).c_str()));
 				return BfTypedValue();
+			}
 			_FixVariables();
 
 			stackPtr -= mCeModule->mSystem->mPtrSize;
@@ -5362,8 +5574,11 @@ BfTypedValue CeMachine::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns
 		else
 		{
 			stackPtr -= paramType->mSize;
-			if (!WriteConstant(module, stackPtr - memStart, constant, paramType))
+			if (!WriteConstant(module, stackPtr - memStart, constant, paramType, isParams))
+			{
+				Fail(StrFormat("Failed to process argument for param '%s'", methodInstance->GetParamName(paramIdx).c_str()));
 				return BfTypedValue();
+			}
 			_FixVariables();
 		}						
  	}
@@ -5405,6 +5620,10 @@ BfTypedValue CeMachine::Call(BfAstNode* targetSrc, BfModule* module, BfMethodIns
 			BfIRValue constVal = CreateConstant(module, retPtr, returnType, &usedReturnType);
 			if (constVal)
 				returnValue = BfTypedValue(constVal, usedReturnType);
+			else
+			{
+				Fail("Failed to encode return argument");
+			}
 		}
 		else
 		{

+ 11 - 2
IDEHelper/Compiler/CeMachine.h

@@ -202,6 +202,8 @@ public:
 		mCeFunction = NULL;
 		mRefCount = 0;
 	}
+
+	~CeFunctionInfo();
 };
 
 class CeCallEntry
@@ -247,6 +249,8 @@ enum CeFunctionKind
 	CeFunctionKind_DebugWrite,
 	CeFunctionKind_DebugWrite_Int,
 	CeFunctionKind_GetReflectType,
+	CeFunctionKind_GetReflectTypeById,
+	CeFunctionKind_Sleep,
 	CeFunctionKind_Char32_ToLower,
 	CeFunctionKind_Char32_ToUpper,
 	CeFunctionKind_Char32_IsLower,
@@ -595,7 +599,8 @@ public:
 	BfCompiler* mCompiler;
 	BfModule* mCeModule;
 	int mRevision;
-	int mExecuteId;
+	int mRevisionExecuteTime;
+	int mExecuteId;	
 
 	// These are only valid for the current execution
 	ContiguousHeap* mHeap;
@@ -610,6 +615,7 @@ public:
 	CeAppendAllocInfo* mAppendAllocInfo;
 	
 	BfAstNode* mCurTargetSrc;
+	BfMethodInstance* mCurMethodInstance;
 	BfModule* mCurModule;	
 	BfType* mCurExpectingType;
 
@@ -623,6 +629,7 @@ public:
 	void Init();	
 	uint8* CeMalloc(int size);
 	bool CeFree(addr_ce addr);
+	addr_ce CeAllocArray(BfArrayType* arrayType, int count, addr_ce& elemsAddr);	
 	addr_ce GetReflectType(int typeId);
 	addr_ce GetString(int stringId);
 	addr_ce GetConstantData(BeConstant* constant);
@@ -633,13 +640,15 @@ public:
 	BeModule* GetBeModule();
 	void DerefMethodInfo(CeFunctionInfo* ceFunctionInfo);
 	void RemoveMethod(BfMethodInstance* methodInstance);		
-	bool WriteConstant(BfModule* module, addr_ce addr, BfConstant* constant, BfType* type);
+	bool WriteConstant(BfModule* module, addr_ce addr, BfConstant* constant, BfType* type, bool isParams = false);
 	CeErrorKind WriteConstant(CeConstStructData& data, BeConstant* constVal);
 	BfIRValue CreateConstant(BfModule* module, uint8* ptr, BfType* type, BfType** outType = NULL);
 	void CreateFunction(BfMethodInstance* methodInstance, CeFunction* ceFunction);		
 	bool Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* startFramePtr);
 
 	void PrepareFunction(CeFunction* methodInstance, CeBuilder* parentBuilder);	
+
+	void CheckFunctions();
 	CeFunction* GetFunction(BfMethodInstance* methodInstance, BfIRValue func, bool& added);
 	CeFunction* GetPreparedFunction(BfMethodInstance* methodInstance);
 	

+ 159 - 0
IDEHelper/Tests/src/ConstEval.bf

@@ -0,0 +1,159 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+
+namespace Tests
+{
+	class ConstEval
+	{
+		struct StructA
+		{
+			public int32 mA;
+			public int32 mB;
+
+			public static int32 sVal = 1000;
+
+			public this(int32 a, int32 b)
+			{
+				mA = a + sVal;
+				mB = b;
+			}
+		}
+
+		const String cStrA = "Abc";
+		const String cStrB = GetStringA(cStrA, 12, 23);
+
+		[ConstEval]
+		static String GetStringA(String str, int a, int b)
+		{
+			// Const-eval functions can return scope-allocated data
+			return scope $"{str}:{a}:{b}";
+		}
+
+		static int32 Factorial(int32 n)
+		{
+		    return n <= 1 ? 1 : (n * Factorial(n - 1));
+		}
+
+		static int32 Fibonacci(int32 n)
+		{
+			return n <= 1 ? n : Fibonacci(n - 1) + Fibonacci(n - 2);
+		}
+
+		public static String GetTypeDesc<T>()
+		{
+			String str = scope .();
+
+			T val = default;
+			val.GetType().GetFullName(str);
+			str.Append(" ");
+
+			for (var fieldInfo in typeof(T).GetFields())
+			{
+				if (!fieldInfo.IsInstanceField)
+					continue;
+				if (@fieldInfo.Index > 0)
+					str.Append(", ");
+				fieldInfo.FieldType.GetFullName(str);
+				str.Append(" ");
+				str.Append(fieldInfo.Name);
+			}
+
+			return str;
+		}
+
+		public static Span<int32> GetSorted(String numList)
+		{
+			List<int32> list = scope .();
+			for (var sv in numList.Split(','))
+			{
+				sv.Trim();
+				if (int32.Parse(sv) case .Ok(let val))
+					list.Add(val);
+			}
+			list.Sort();
+			return list;
+		}
+
+		public static int32 MethodA()
+		{
+			mixin Add10(var a)
+			{
+				a += 10
+			}
+
+			int32 a = 1;
+
+			void Inc()
+			{
+				a++;
+			}
+
+			for (int i < 2)
+			{
+				defer { a *= 2; }
+				Inc();
+				Add10!(a);
+			}
+
+			return a;
+		}
+
+		// This method can only be const evaluated
+		[ConstEval]
+		public static int32 ConstSum(params int32[] vals)
+		{
+			int32 sum = 0;
+			for (let val in vals)
+				sum += val;
+			return sum;
+		}
+
+		public static int32 MethodB()
+		{
+			// Returns different results depending on whether we are const-evaluating
+			return Compiler.IsConstEval ? 1 : 0;
+		}
+
+		public static int32 MethodC(StructA sa = .(1, 2), (int32, int32) tup = (20, 30), int32[2] arr = .(300, 400))
+		{
+			return sa.mA + sa.mB + tup.0 + tup.1 + arr[0] + arr[1];
+		}
+
+		[Test]
+		public static void TestBasics()
+		{
+			const int fac = Factorial(8);
+			Test.Assert(fac == 40320);
+			const int fib = Fibonacci(27);
+			Test.Assert(fib == 196418);
+			
+			// Generated string has reference equality to equivalent literal
+			Test.Assert(cStrB === "Abc:12:23");
+
+			const String strC = GetTypeDesc<StructA>();
+			Test.Assert(strC === "Tests.ConstEval.StructA int32 mA, int32 mB");
+
+			// Const Span<T> results can be used to initialize statically-sized arrays
+			const int32[?] iArr = GetSorted("8, 1, 3, 7");
+			Compiler.Assert(iArr == .(1, 3, 7, 8));
+			Test.Assert(iArr == .(1, 3, 7, 8));
+
+			let val0 = [ConstEval]MethodA();
+			Compiler.Assert(val0 == 70);
+			let val1 = MethodA();
+			// 'val1' is a read-only variable, but not const, so the following line would fail
+			//Compiler.Assert(val1 == 24);
+			Test.Assert(val1 == 70);
+
+			// This method is marked as ConstEval so it always evaluates as const
+			let val2 = ConstSum(3, 20, 100);
+			Compiler.Assert(val2 == 123);
+
+			Test.Assert(MethodB() == 0);
+			Test.Assert([ConstEval]MethodB() == 1);
+
+			Test.Assert(MethodC() == 1753);
+		}
+	}
+}