2
0
Brian Fiete 3 жил өмнө
parent
commit
1d2811f50d

+ 13 - 0
BeefLibs/corlib/src/GC.bf

@@ -149,6 +149,19 @@ namespace System
 		public static void ExcludeThreadId(int thereadId) {}
 #endif
 
+		static void MarkAppendedObject(Object obj)
+		{
+#if BF_ENABLE_REALTIME_LEAK_CHECK
+			ClassVData* maskedVData = (ClassVData*)(void*)(obj.[Friend]mClassVData & ~(int)0xFF);
+			if (maskedVData == null)
+				return;
+#else
+			if (obj.mClassVData == null)
+				return;
+#endif
+			obj.[Friend]GCMarkMembers();
+		}
+
 		static void MarkDerefedObject(Object* obj)
 		{
 #if BF_ENABLE_REALTIME_LEAK_CHECK

+ 2 - 0
BeefLibs/corlib/src/Internal.bf

@@ -89,6 +89,8 @@ namespace System
 		[CallingConvention(.Cdecl), NoReturn]
 		public static extern void ThrowIndexOutOfRange(int stackOffset = 0);
 		[CallingConvention(.Cdecl), NoReturn]
+		public static extern void ThrowObjectNotInitialized(int stackOffset = 0);
+		[CallingConvention(.Cdecl), NoReturn]
 		public static extern void FatalError(String error, int stackOffset = 0);
 		[Intrinsic("memcpy")]
 		public static extern void MemCpy(void* dest, void* src, int length, int32 align = 1, bool isVolatile = false);

+ 12 - 2
BeefLibs/corlib/src/Reflection/FieldInfo.bf

@@ -8,7 +8,8 @@ namespace System.Reflection
 		public enum Error
 		{
 			InvalidTargetType,
-			InvalidValueType
+			InvalidValueType,
+			AppendedField
 		}
 
 	    TypeInstance mTypeInstance;
@@ -24,6 +25,7 @@ namespace System.Reflection
 	    public int32 MemberOffset => (int32)mFieldData.mData;
 	    public Type FieldType => Type.[Friend]GetType(mFieldData.mFieldTypeId);
 		public bool IsConst => mFieldData.mFlags.HasFlag(.Const);
+		public bool IsAppended => mFieldData.mFlags.HasFlag(.Appended);
 		public bool IsEnumCase => mFieldData.mFlags.HasFlag(.EnumCase);
 		public bool IsReadOnly => mFieldData.mFlags.HasFlag(.ReadOnly);
 		public bool IsStatic => mFieldData.mFlags.HasFlag(.Static);
@@ -86,7 +88,12 @@ namespace System.Reflection
 			if (valueType == fieldType)
 			{
 				if (valueType.IsObject)
+				{
+					if (mFieldData.mFlags.HasFlag(.Appended))
+						return .Err(.AppendedField);
+
 					*((void**)dataAddr) = Internal.UnsafeCastToPtr(value);
+				}
 				else
 					Internal.MemCpy(dataAddr, valueDataAddr, fieldType.[Friend]mSize);
 			}
@@ -385,7 +392,10 @@ namespace System.Reflection
 			if (typeCode == TypeCode.Object)
 			{
 				value.[Friend]mStructType = 0;
-			    value.[Friend]mData = *(int*)targetDataAddr;
+				if (mFieldData.mFlags.HasFlag(.Appended))
+			    	value.[Friend]mData = (int)targetDataAddr;
+				else
+					value.[Friend]mData = *(int*)targetDataAddr;
 			}
 			else
 			{

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

@@ -1480,6 +1480,7 @@ namespace System.Reflection
 		EnumDiscriminator		= 0x0200,
 		EnumCase				= 0x0400,
 		ReadOnly				= 0x0800,
+		Appended				= 0x1000,
     }
 
 	public enum MethodFlags : uint16

+ 25 - 0
BeefRT/rt/Internal.cpp

@@ -86,6 +86,7 @@ namespace bf
 			BFRT_EXPORT static void ObjectDynCheck(Object* object, int typeId, bool allowNull);
 			BFRT_EXPORT static void ObjectDynCheckFailed(Object* object, int typeId);			
 			BFRT_EXPORT static void ThrowIndexOutOfRange(intptr stackOffset);
+			BFRT_EXPORT static void ThrowObjectNotInitialized(intptr stackOffset);
 			BFRT_EXPORT static void FatalError(String* error, intptr stackOffset = 0);
 			BFRT_EXPORT static void MemCpy(void* dest, void* src, intptr length);
 			BFRT_EXPORT static void MemMove(void* dest, void* src, intptr length);
@@ -423,6 +424,30 @@ void Internal::ThrowIndexOutOfRange(intptr stackOffset)
 	Internal_FatalError("Index out of range");
 }
 
+void Internal::ThrowObjectNotInitialized(intptr stackOffset)
+{
+	if (gClientPipe != NULL)
+	{
+		if (gTestBreakOnFailure)
+		{
+			SETUP_ERROR("Object not initialized", (int)(2 + stackOffset));
+			BF_DEBUG_BREAK();
+		}
+
+		Beefy::String str = ":TestFail\tObject not initialized\n";
+		TestString(str);
+		exit(1);
+	}
+
+	if ((stackOffset != -1) && (::IsDebuggerPresent()))
+	{
+		SETUP_ERROR("Object not initialized", (int)(2 + stackOffset));
+		BF_DEBUG_BREAK();
+	}
+
+	Internal_FatalError("Object not initialized");
+}
+
 void Internal::FatalError(bf::System::String* error, intptr stackOffset)
 {		
 	if (gClientPipe != NULL)

+ 4 - 0
IDE/src/ui/SourceEditWidgetContent.bf

@@ -4942,6 +4942,10 @@ namespace IDE.ui
 						menuItem.SetDisabled(!isPaused);
 						menuItem.mOnMenuItemSelected.Add(new (evt) => IDEApp.sApp.ShowDisassemblyAtCursor());
 
+						menuItem = menu.AddItem("Set Next Statement");
+						menuItem.SetDisabled(!isPaused);
+						menuItem.mOnMenuItemSelected.Add(new (evt) => IDEApp.sApp.[Friend]SetNextStatement());
+
 					    var stepIntoSpecificMenu = menu.AddItem("Step into Specific");
 						stepIntoSpecificMenu.SetDisabled(!isPaused);
 						stepIntoSpecificMenu.IsParent = true;

+ 1 - 1
IDEHelper/Compiler/BfAst.h

@@ -3169,7 +3169,7 @@ public:
 	BfAstNode* mConstSpecifier; // Could be 'const' or 'using'	
 	BfTokenNode* mVolatileSpecifier;
 	BfTokenNode* mNewSpecifier;
-	BfTokenNode* mExternSpecifier;
+	BfTokenNode* mExternSpecifier; // Could be 'extern' or 'append'
 	BfTypeReference* mTypeRef;
 	BfIdentifierNode* mNameNode;
 	BfTokenNode* mEqualsNode;

+ 2 - 2
IDEHelper/Compiler/BfAutoComplete.cpp

@@ -1741,7 +1741,7 @@ void BfAutoComplete::CheckIdentifier(BfAstNode* identifierNode, bool isInExpress
 	{		
 		const char* tokens [] =
 		{
-			"alignof", "as", "asm", "base", "break", "case", "catch", "checked", "continue", "const", "default", "defer",
+			"alignof", "append", "as", "asm", "base", "break", "case", "catch", "checked", "continue", "const", "default", "defer",
 			"delegate", "delete", "do", "else", "false", "finally", 
 			"fixed", "for", "function", "if", "implicit", "in", "internal", "is", "isconst", "new", "mixin", "null",
 			"offsetof", "out", "params", "readonly", "ref", "rettype", "return",
@@ -1762,7 +1762,7 @@ void BfAutoComplete::CheckIdentifier(BfAstNode* identifierNode, bool isInExpress
 	{
 		const char* tokens[] =
 		{
-			"abstract", "base", "class", "const", 
+			"abstract", "append", "base", "class", "const", 
 			"delegate", "extern", "enum", "explicit", "extension", "function",
 			"interface", "in", "implicit", "internal", "mixin", "namespace", "new",
 			"operator", "out", "override", "params", "private", "protected", "public", "readonly", "ref", "rettype", "return",

+ 12 - 1
IDEHelper/Compiler/BfDefBuilder.cpp

@@ -1184,7 +1184,8 @@ void BfDefBuilder::Visit(BfFieldDeclaration* fieldDeclaration)
 		fieldDef->mProtection = BfProtection_Public;	
 	fieldDef->mIsReadOnly = fieldDeclaration->mReadOnlySpecifier != NULL;	
 	fieldDef->mIsInline = (fieldDeclaration->mReadOnlySpecifier != NULL) && (fieldDeclaration->mReadOnlySpecifier->GetToken() == BfToken_Inline);
-	fieldDef->mIsExtern = (fieldDeclaration->mExternSpecifier != NULL);
+	fieldDef->mIsExtern = (fieldDeclaration->mExternSpecifier != NULL) && (fieldDeclaration->mExternSpecifier->mToken == BfToken_Extern);
+	fieldDef->mIsAppend = (fieldDeclaration->mExternSpecifier != NULL) && (fieldDeclaration->mExternSpecifier->mToken == BfToken_Append);
 	auto constSpecifierToken = BfNodeDynCast<BfTokenNode>(fieldDeclaration->mConstSpecifier);
 	fieldDef->mIsConst = ((constSpecifierToken != NULL) && (constSpecifierToken->mToken == BfToken_Const)) || (isEnumEntryDecl);	
 	if (auto usingSpecifier = BfNodeDynCast<BfUsingSpecifierNode>(fieldDeclaration->mConstSpecifier))
@@ -2197,6 +2198,11 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString)
 					}
 					if (field->GetFieldDeclaration()->mFieldDtor != NULL)
 						needsStaticDtor = true;
+					if (field->mIsAppend)
+					{
+						needsStaticInit = true;
+						needsStaticDtor = true;
+					}
 				}
 			}
 
@@ -2222,6 +2228,11 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString)
 			hasNonStaticField = true;
 			if (field->GetInitializer() != NULL)
 				needsDefaultCtor = true;
+			if (field->mIsAppend)
+			{
+				needsDefaultCtor = true;
+				needsDtor = true;
+			}
 			if (auto fieldDecl = field->GetFieldDeclaration())
 			{
 				if (fieldDecl->mFieldDtor != NULL)

+ 66 - 18
IDEHelper/Compiler/BfExprEvaluator.cpp

@@ -5008,6 +5008,42 @@ BfTypedValue BfExprEvaluator::LoadField(BfAstNode* targetSrc, BfTypedValue targe
 		bool isStaticCtor = (mModule->mCurMethodInstance != NULL) &&
 			(mModule->mCurMethodInstance->mMethodDef->IsCtorOrInit()) &&
 			(mModule->mCurMethodInstance->mMethodDef->mIsStatic);
+
+		if ((mModule->mCompiler->mOptions.mRuntimeChecks) && (fieldInstance->IsAppendedObject()) && (!mModule->mBfIRBuilder->mIgnoreWrites) &&
+			(!mModule->IsSkippingExtraResolveChecks()))
+		{
+			auto intType = mModule->GetPrimitiveType(BfTypeCode_IntPtr);
+			auto intPtrType = mModule->CreatePointerType(intType);
+			auto intPtrVal = mModule->mBfIRBuilder->CreateBitCast(retVal.mValue, mModule->mBfIRBuilder->MapType(intPtrType));
+			auto intVal = mModule->mBfIRBuilder->CreateLoad(intPtrVal);
+
+			auto oobBlock = mModule->mBfIRBuilder->CreateBlock("oob", true);
+			auto contBlock = mModule->mBfIRBuilder->CreateBlock("cont", true);
+
+			auto cmpRes = mModule->mBfIRBuilder->CreateCmpEQ(intVal, mModule->mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 0));
+			mModule->mBfIRBuilder->CreateCondBr(cmpRes, oobBlock, contBlock);
+
+			mModule->mBfIRBuilder->SetInsertPoint(oobBlock);
+			auto internalType = mModule->ResolveTypeDef(mModule->mCompiler->mInternalTypeDef);
+			auto oobFunc = mModule->GetMethodByName(internalType->ToTypeInstance(), "ThrowObjectNotInitialized");
+			if (oobFunc.mFunc)
+			{
+				if (mModule->mIsComptimeModule)
+					mModule->mCompiler->mCeMachine->QueueMethod(oobFunc.mMethodInstance, oobFunc.mFunc);
+
+				SizedArray<BfIRValue, 1> args;
+				args.push_back(mModule->GetConstValue(0));
+				mModule->mBfIRBuilder->CreateCall(oobFunc.mFunc, args);
+				mModule->mBfIRBuilder->CreateUnreachable();				
+			}
+			else
+			{
+				mModule->Fail("System.Internal class must contain method 'ThrowObjectNotInitialized'", fieldDef->GetRefNode());
+			}
+
+			mModule->mBfIRBuilder->SetInsertPoint(contBlock);
+		}
+
 		if ((fieldDef->mIsReadOnly) && (!isStaticCtor))
 		{
 			if (retVal.IsAddr())
@@ -5135,8 +5171,16 @@ BfTypedValue BfExprEvaluator::LoadField(BfAstNode* targetSrc, BfTypedValue targe
 		if ((targetValue.IsAddr()) && (!typeInstance->IsValueType()))
 			targetValue = mModule->LoadValue(targetValue);
 
-		retVal = BfTypedValue(mModule->mBfIRBuilder->CreateInBoundsGEP(targetValue.mValue, 0, fieldInstance->mDataIdx/*, fieldDef->mName*/),
-			resolvedFieldType, target.IsReadOnly() ? BfTypedValueKind_ReadOnlyAddr : BfTypedValueKind_Addr);
+		if (fieldInstance->IsAppendedObject())
+		{
+			auto elemPtr = mModule->mBfIRBuilder->CreateInBoundsGEP(targetValue.mValue, 0, fieldInstance->mDataIdx);
+			retVal = BfTypedValue(mModule->mBfIRBuilder->CreateBitCast(elemPtr, mModule->mBfIRBuilder->MapType(resolvedFieldType)), resolvedFieldType);
+		}
+		else
+		{
+			retVal = BfTypedValue(mModule->mBfIRBuilder->CreateInBoundsGEP(targetValue.mValue, 0, fieldInstance->mDataIdx/*, fieldDef->mName*/),
+				resolvedFieldType, target.IsReadOnly() ? BfTypedValueKind_ReadOnlyAddr : BfTypedValueKind_Addr);
+		}
 	}
 
 	if (!retVal.IsSplat())
@@ -9649,15 +9693,24 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp
 
 		if (resolvedTypeInstance != NULL)
 		{
-			if ((!resolvedTypeInstance->IsStruct()) && (!resolvedTypeInstance->IsTypedPrimitive()))
+			if ((mBfEvalExprFlags & BfEvalExprFlags_AppendFieldInitializer) == 0)
 			{
-				if (mModule->PreFail())
-					mModule->Fail("Objects must be allocated through 'new' or 'scope'", targetSrc);
-				return BfTypedValue();
+				if ((!resolvedTypeInstance->IsStruct()) && (!resolvedTypeInstance->IsTypedPrimitive()))
+				{
+					if (mModule->PreFail())
+						mModule->Fail("Objects must be allocated through 'new' or 'scope'", targetSrc);
+					return BfTypedValue();
+				}
 			}
 			
 			if (auto identifier = BfNodeDynCastExact<BfIdentifierNode>(targetSrc))
-				mModule->SetElementType(identifier, resolvedTypeInstance->IsEnum() ? BfSourceElementType_Type : BfSourceElementType_Struct);
+			{
+				auto elementType = resolvedTypeInstance->IsEnum() ? BfSourceElementType_Type : BfSourceElementType_Struct;
+				if (resolvedTypeInstance->IsObject())
+					elementType = BfSourceElementType_RefType;
+				mModule->SetElementType(identifier, elementType);
+			}
+
 			if (mModule->mCompiler->mResolvePassData != NULL)
 			{
 				if (!BfNodeIsA<BfMemberReferenceExpression>(targetSrc))
@@ -9688,7 +9741,7 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp
 			mResultLocalVar = NULL;
 			mResultFieldInstance = NULL;
 			mResultLocalVarRefNode = NULL;									
-			auto result = MatchConstructor(targetSrc, methodBoundExpr, structInst, resolvedTypeInstance, argValues, false, false);
+			auto result = MatchConstructor(targetSrc, methodBoundExpr, structInst, resolvedTypeInstance, argValues, false, resolvedTypeInstance->IsObject());
 			if ((result) && (!result.mType->IsVoid()))
 				return result;
 			mModule->ValidateAllocation(resolvedTypeInstance, targetSrc);
@@ -17564,7 +17617,7 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
 							else
 								mResult = BfTypedValue(mModule->CreateAlloca(expectingType), expectingType, BfTypedValueKind_TempAddr);
 							
-							auto ctorResult = MatchConstructor(target, methodBoundExpr, mResult, expectingType->ToTypeInstance(), argValues, false, false);																					
+							auto ctorResult = MatchConstructor(target, methodBoundExpr, mResult, expectingType->ToTypeInstance(), argValues, false, false);
 							if ((ctorResult) && (!ctorResult.mType->IsVoid()))
 								mResult = ctorResult;							
 							mModule->ValidateAllocation(expectingType, invocationExpr->mTarget);
@@ -17599,7 +17652,7 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
 				{
 					// Allow
 				}
-				else
+				else if ((mBfEvalExprFlags & BfEvalExprFlags_AppendFieldInitializer) == 0)
 				{
 					gaveUnqualifiedDotError = true;
 					if (mModule->PreFail())
@@ -21419,25 +21472,20 @@ void BfExprEvaluator::Visit(BfIndexerExpression* indexerExpr)
 				auto oobFunc = mModule->GetMethodByName(internalType->ToTypeInstance(), "ThrowIndexOutOfRange");
 				if (oobFunc.mFunc)
 				{
-					/*if (!mModule->mCompiler->mIsResolveOnly)
-					{
-						OutputDebugStrF("-OOB %d %d\n", oobFunc.mFunc.mId, oobFunc.mFunc.mFlags);
-					}*/
-
 					if (mModule->mIsComptimeModule)
 						mModule->mCompiler->mCeMachine->QueueMethod(oobFunc.mMethodInstance, oobFunc.mFunc);
 
 					SizedArray<BfIRValue, 1> args;
 					args.push_back(mModule->GetConstValue(0));
 					mModule->mBfIRBuilder->CreateCall(oobFunc.mFunc, args);
-					mModule->mBfIRBuilder->CreateUnreachable();
-
-					mModule->mBfIRBuilder->SetInsertPoint(contBlock);
+					mModule->mBfIRBuilder->CreateUnreachable();					
 				}
 				else
 				{
 					mModule->Fail("System.Internal class must contain method 'ThrowIndexOutOfRange'");
 				}
+
+				mModule->mBfIRBuilder->SetInsertPoint(contBlock);
 			}
 		}
 		

+ 24 - 1
IDEHelper/Compiler/BfIRBuilder.cpp

@@ -3203,6 +3203,9 @@ void BfIRBuilder::CreateDbgTypeDefinition(BfType* type)
 			PopulateType(resolvedFieldType, BfIRPopulateType_Eventually_Full);		
 		resolvedFieldDIType = DbgGetType(resolvedFieldType);
 
+		if (fieldInstance->IsAppendedObject())
+			resolvedFieldDIType = DbgGetTypeInst(resolvedFieldType->ToTypeInstance());
+
 		if ((fieldDef == NULL) && (typeInstance->IsPayloadEnum()))
 		{
 			orderedFields.push_back(fieldInstance);
@@ -3409,7 +3412,7 @@ void BfIRBuilder::CreateDbgTypeDefinition(BfType* type)
 					if (fieldDef->mHasMultiDefs)
 						fieldName += "$" + fieldDef->mDeclaringType->mProject->mName;
 					auto memberType = DbgCreateMemberType(diForwardDecl, fieldName, fileDIScope, lineNum,
-						resolvedFieldType->mSize * 8, resolvedFieldType->mAlign * 8, fieldInstance->mDataOffset * 8,
+						fieldInstance->mDataSize * 8, resolvedFieldType->mAlign * 8, fieldInstance->mDataOffset * 8,
 						flags, resolvedFieldDIType);
 					diFieldTypes.push_back(memberType);
 				}
@@ -3712,6 +3715,9 @@ void BfIRBuilder::CreateTypeDefinition_Data(BfModule* populateModule, BfTypeInst
 		if ((fieldDef != NULL) && (resolvedFieldType->IsStruct()))
 			PopulateType(resolvedFieldType, BfIRPopulateType_Eventually_Full);
 
+		if (fieldInstance->IsAppendedObject())
+			PopulateType(resolvedFieldType, BfIRPopulateType_Eventually_Full);
+
 		if ((fieldDef == NULL) && (typeInstance->IsPayloadEnum()))
 		{
 			orderedFields.push_back(fieldInstance);
@@ -3761,10 +3767,27 @@ void BfIRBuilder::CreateTypeDefinition_Data(BfModule* populateModule, BfTypeInst
 		if (fieldInstance == NULL)
 			continue;
 
+		auto fieldDef = fieldInstance->GetFieldDef();
+
 		auto resolvedFieldType = fieldInstance->GetResolvedType();
 
 		BfIRType resolvedFieldIRType = MapType(resolvedFieldType);
 
+		if (fieldInstance->IsAppendedObject())
+		{
+			auto fieldTypeInst = fieldInstance->mResolvedType->ToTypeInstance();
+
+			if (fieldInstance->mDataSize != fieldTypeInst->mInstSize)
+			{
+				SizedArray<BfIRType, 2> types;
+				types.push_back(MapTypeInst(fieldTypeInst));
+				types.push_back(GetSizedArrayType(GetPrimitiveType(BfTypeCode_Int8), fieldInstance->mDataSize - fieldTypeInst->mInstSize));
+				resolvedFieldIRType = CreateStructType(types);
+			}
+			else
+				resolvedFieldIRType = MapTypeInst(fieldTypeInst);		
+		}
+
 		if (fieldInstance->mDataOffset > dataPos)
 		{
 			int fillSize = fieldInstance->mDataOffset - dataPos;

+ 263 - 20
IDEHelper/Compiler/BfModule.cpp

@@ -4048,8 +4048,18 @@ void BfModule::CreateStaticField(BfFieldInstance* fieldInstance, bool isThreadLo
 		BfMangler::Mangle(staticVarName, mCompiler->GetMangleKind(), fieldInstance);
 		if ((!fieldType->IsValuelessType()) && (!staticVarName.StartsWith("#")))
 		{
+			BfIRType irType;
+
+			if (fieldInstance->IsAppendedObject())
+			{
+				irType = mBfIRBuilder->MapTypeInst(fieldType->ToTypeInstance(), BfIRPopulateType_Eventually_Full);
+				initValue = mBfIRBuilder->CreateConstAggZero(irType);
+			}
+			else
+				irType = mBfIRBuilder->MapType(fieldType, BfIRPopulateType_Eventually_Full);
+
 			BfIRValue globalVar = mBfIRBuilder->CreateGlobalVariable(					
-				mBfIRBuilder->MapType(fieldType, BfIRPopulateType_Eventually_Full),
+				irType,
 				false,
 				BfIRLinkageType_External,
 				initValue,
@@ -4551,6 +4561,83 @@ BfTypedValue BfModule::GetFieldInitializerValue(BfFieldInstance* fieldInstance,
 	return result;
 }
 
+void BfModule::AppendedObjectInit(BfFieldInstance* fieldInst)
+{
+	BfExprEvaluator exprEvaluator(this);
+
+	bool failed = false;
+
+	auto fieldDef = fieldInst->GetFieldDef();
+	auto initializer = fieldDef->GetInitializer();
+
+	BfResolvedArgs resolvedArgs;
+	if (auto invocationExpr = BfNodeDynCast<BfInvocationExpression>(initializer))
+	{
+		bool isDot = false;
+
+		if (auto memberRefExpr = BfNodeDynCast<BfMemberReferenceExpression>(invocationExpr->mTarget))
+			isDot = (memberRefExpr->mTarget == NULL) && (memberRefExpr->mMemberName == NULL);
+
+		if (!isDot)
+		{
+			auto resolvedType = ResolveTypeRef(invocationExpr->mTarget, {});
+			if ((resolvedType == NULL) || (resolvedType != fieldInst->mResolvedType))
+				failed = true;
+		}
+
+		SetAndRestoreValue<BfType*> prevExpectingType(exprEvaluator.mExpectingType, fieldInst->mResolvedType);
+
+		resolvedArgs.Init(invocationExpr->mOpenParen, &invocationExpr->mArguments, &invocationExpr->mCommas, invocationExpr->mCloseParen);
+		exprEvaluator.ResolveArgValues(resolvedArgs, BfResolveArgsFlag_DeferParamEval);
+	}
+	else if (initializer != NULL)
+	{
+		GetFieldInitializerValue(fieldInst);
+		failed = true;
+	}
+
+	if (failed)
+		Fail("Append fields can only be initialized with a call to their constructor", initializer);
+
+	auto intType = GetPrimitiveType(BfTypeCode_IntPtr);
+	auto int8Type = mBfIRBuilder->GetPrimitiveType(BfTypeCode_Int8);
+	auto ptrType = mBfIRBuilder->GetPointerTo(int8Type);
+	auto ptrPtrType = mBfIRBuilder->GetPointerTo(ptrType);
+
+
+	BfIRValue fieldAddr;
+	if (fieldDef->mIsStatic)
+	{
+		fieldAddr = ReferenceStaticField(fieldInst).mValue;
+	}
+	else
+		fieldAddr = mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[0]->mValue, 0, fieldInst->mDataIdx);
+	auto thisValue = BfTypedValue(mBfIRBuilder->CreateBitCast(fieldAddr, mBfIRBuilder->MapType(fieldInst->mResolvedType)), fieldInst->mResolvedType);
+
+	auto indexVal = BfTypedValue(CreateAlloca(intType), CreateRefType(intType));
+	auto intThisVal = mBfIRBuilder->CreatePtrToInt(thisValue.mValue, (intType->mSize == 4) ? BfTypeCode_Int32 : BfTypeCode_Int64);
+	mBfIRBuilder->CreateStore(intThisVal, indexVal.mValue);
+
+	auto ctorResult = exprEvaluator.MatchConstructor(fieldDef->GetNameNode(), NULL, thisValue, fieldInst->mResolvedType->ToTypeInstance(), resolvedArgs, false, true, &indexVal);
+
+	auto vObjectAddr = mBfIRBuilder->CreateInBoundsGEP(thisValue.mValue, 0, 0);
+
+	auto vDataRef = CreateClassVDataGlobal(fieldInst->mResolvedType->ToTypeInstance());
+
+	auto destAddr = mBfIRBuilder->CreateBitCast(vObjectAddr, ptrPtrType);
+	auto srcVal = mBfIRBuilder->CreateBitCast(vDataRef, ptrType);
+	mBfIRBuilder->CreateStore(srcVal, destAddr);
+
+	if ((mCompiler->mOptions.mObjectHasDebugFlags) && (!mIsComptimeModule))
+	{
+		auto int8Type = mBfIRBuilder->GetPrimitiveType(BfTypeCode_Int8);
+		auto ptrType = mBfIRBuilder->GetPointerTo(int8Type);
+
+		auto thisFlagsPtr = mBfIRBuilder->CreateBitCast(thisValue.mValue, ptrType);
+		mBfIRBuilder->CreateStore(GetConstValue8(BfObjectFlag_AppendAlloc), thisFlagsPtr);
+	}
+}
+
 void BfModule::CheckInterfaceMethod(BfMethodInstance* methodInstance)
 {	
 	
@@ -5570,7 +5657,9 @@ BfIRValue BfModule::CreateFieldData(BfFieldInstance* fieldInstance, int customAt
 	if (fieldDef->IsEnumCaseEntry())
 		fieldFlags = (BfFieldFlags)(fieldFlags | BfFieldFlags_EnumCase);
 	if (fieldDef->mIsReadOnly)
-		fieldFlags = (BfFieldFlags)(fieldFlags | BfFieldFlags_ReadOnly);	
+		fieldFlags = (BfFieldFlags)(fieldFlags | BfFieldFlags_ReadOnly);
+	if (fieldInstance->IsAppendedObject())
+		fieldFlags = (BfFieldFlags)(fieldFlags | BfFieldFlags_Appended);
 	
 	BfIRValue constValue;
 	BfIRValue constValue2;
@@ -5608,8 +5697,13 @@ BfIRValue BfModule::CreateFieldData(BfFieldInstance* fieldInstance, int customAt
 			}
 		}
 
-		if ((refVal.IsAddr()) && (!isComptimeArg))
-			constValue = mBfIRBuilder->CreatePtrToInt(refVal.mValue, BfTypeCode_IntPtr);
+		if (!isComptimeArg)
+		{
+			if (refVal.IsAddr())
+				constValue = mBfIRBuilder->CreatePtrToInt(refVal.mValue, BfTypeCode_IntPtr);
+			else if (fieldInstance->IsAppendedObject())
+				constValue = mBfIRBuilder->CreatePtrToInt(refVal.mValue, BfTypeCode_IntPtr);
+		}
 	}
 
 	if (!constValue)
@@ -11240,7 +11334,7 @@ StringT<128> BfModule::MethodToString(BfMethodInstance* methodInst, BfMethodName
 		for (int paramIdx = 0; paramIdx < (int)methodInst->GetParamCount(); paramIdx++)
 		{
 			int paramKind = methodInst->GetParamKind(paramIdx);
-			if (paramKind == BfParamKind_ImplicitCapture)
+			if ((paramKind == BfParamKind_ImplicitCapture) || (paramKind == BfParamKind_AppendIdx))
 				continue;
 
 			if (dispParamIdx > 0)
@@ -14799,6 +14893,9 @@ BfTypedValue BfModule::ReferenceStaticField(BfFieldInstance* fieldInstance)
 		{
 			BfIRType irType = mBfIRBuilder->MapType(typeType);
 
+			if (fieldInstance->IsAppendedObject())
+				irType = mBfIRBuilder->MapTypeInst(typeType->ToTypeInstance());
+
 			SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, mBfIRBuilder->mIgnoreWrites || staticVarName.StartsWith('#'));
 
 			globalValue = mBfIRBuilder->CreateGlobalVariable(
@@ -14822,7 +14919,7 @@ BfTypedValue BfModule::ReferenceStaticField(BfFieldInstance* fieldInstance)
 
 	if (fieldDef->mIsVolatile)
 		return BfTypedValue(globalValue, type, BfTypedValueKind_VolatileAddr);
-	return BfTypedValue(globalValue, type, !fieldDef->mIsConst);
+	return BfTypedValue(globalValue, type, !fieldDef->mIsConst && !fieldInstance->IsAppendedObject());
 }
 
 BfFieldInstance* BfModule::GetFieldInstance(BfTypeInstance* typeInst, int fieldIdx, const char* fieldName)
@@ -16381,13 +16478,13 @@ void BfModule::CalcAppendAlign(BfMethodInstance* methodInst)
 	methodInst->mAppendAllocAlign = 1;
 }
 
-BfTypedValue BfModule::TryConstCalcAppend(BfMethodInstance* methodInst, SizedArrayImpl<BfIRValue>& args)
+BfTypedValue BfModule::TryConstCalcAppend(BfMethodInstance* methodInst, SizedArrayImpl<BfIRValue>& args, bool force)
 {
 	BP_ZONE("BfModule::TryConstCalcAppend");
 
 	BF_ASSERT(methodInst->mMethodDef->mMethodType == BfMethodType_CtorCalcAppend);
 
-	if ((mCompiler->mIsResolveOnly) && (!mIsComptimeModule))
+	if ((mCompiler->mIsResolveOnly) && (!mIsComptimeModule) && (!force))
 		return BfTypedValue();
 
 	// We want to regenerate all ctor calls when the method internals change
@@ -16476,6 +16573,7 @@ BfTypedValue BfModule::TryConstCalcAppend(BfMethodInstance* methodInst, SizedArr
 		BfConstResolveState constResolveState;
 		constResolveState.mMethodInstance = methodInst;
 		constResolveState.mPrevConstResolveState = mCurMethodState->mConstResolveState;
+		constResolveState.mInCalcAppend = true;
 
 		SetAndRestoreValue<bool> ignoreWrites(mBfIRBuilder->mIgnoreWrites, true);
 		BfMethodState methodState;
@@ -16630,7 +16728,7 @@ BfTypedValue BfModule::CallBaseCtorCalc(bool constOnly)
 	BF_ASSERT(bindResult.mIRArgs[0].IsFake());
 	bindResult.mIRArgs.RemoveAt(0);
 	auto calcAppendMethodModule = GetMethodInstanceAtIdx(bindResult.mMethodInstance->GetOwner(), bindResult.mMethodInstance->mMethodDef->mIdx + 1, BF_METHODNAME_CALCAPPEND);	
-	BfTypedValue appendSizeTypedValue = TryConstCalcAppend(calcAppendMethodModule.mMethodInstance, bindResult.mIRArgs);
+	BfTypedValue appendSizeTypedValue = TryConstCalcAppend(calcAppendMethodModule.mMethodInstance, bindResult.mIRArgs, true);
 	BF_ASSERT(calcAppendMethodModule.mMethodInstance->mAppendAllocAlign >= 0);
 	mCurMethodInstance->mAppendAllocAlign = BF_MAX((int)mCurMethodInstance->mAppendAllocAlign, calcAppendMethodModule.mMethodInstance->mAppendAllocAlign);
 	BF_ASSERT(calcAppendMethodModule.mMethodInstance->mEndingAppendAllocAlign > -1);
@@ -16757,7 +16855,10 @@ void BfModule::CreateStaticCtor()
 				{					
 					continue;
 				}
-				GetFieldInitializerValue(fieldInst, NULL, NULL, NULL, true);				
+				if (fieldInst->IsAppendedObject())
+					AppendedObjectInit(fieldInst);
+				else
+					GetFieldInitializerValue(fieldInst, NULL, NULL, NULL, true);
 			}
 		}
 
@@ -16788,8 +16889,13 @@ void BfModule::CreateStaticCtor()
 							if ((!BfNodeIsA<BfVarTypeReference>(fieldDef->mTypeRef)) && (!BfNodeIsA<BfLetTypeReference>(fieldDef->mTypeRef)))
 							{
 								wantType = ResolveTypeRef(fieldDef->mTypeRef, BfPopulateType_Identity, BfResolveTypeRefFlag_AllowInferredSizedArray);
-							}							
-							CreateValueFromExpression(fieldDef->GetInitializer(), wantType, BfEvalExprFlags_FieldInitializer);
+							}
+
+							BfEvalExprFlags exprFlags = BfEvalExprFlags_FieldInitializer;
+							if (fieldDef->mIsAppend)
+								exprFlags = (BfEvalExprFlags)(exprFlags | BfEvalExprFlags_AppendFieldInitializer);
+
+							CreateValueFromExpression(fieldDef->GetInitializer(), wantType, exprFlags);
 						}						
 					}
 				}
@@ -16876,6 +16982,66 @@ void BfModule::EmitDtorBody()
 			if (fieldDef != NULL)
 				fieldDecl = fieldDef->GetFieldDeclaration();
 
+			if ((fieldDef != NULL) && (fieldDef->mIsStatic == methodDef->mIsStatic) && (fieldInst->IsAppendedObject()))
+			{
+				auto refNode = fieldDef->GetRefNode();
+				UpdateSrcPos(refNode);
+
+				auto objectType = mContext->mBfObjectType;
+				BfTypeInstance* checkTypeInst = mCurTypeInstance->ToTypeInstance();
+
+				BfTypedValue val;
+				if (fieldDef->mIsStatic)
+					val = ReferenceStaticField(fieldInst);
+				else
+				{
+					auto fieldAddr = mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[0]->mValue, 0, fieldInst->mDataIdx);
+					val = BfTypedValue(mBfIRBuilder->CreateBitCast(fieldAddr, mBfIRBuilder->MapType(fieldInst->mResolvedType)), fieldInst->mResolvedType);
+				}
+
+				bool allowPrivate = checkTypeInst == mCurTypeInstance;
+				bool allowProtected = allowPrivate || TypeIsSubTypeOf(mCurTypeInstance, checkTypeInst);
+				while (checkTypeInst != NULL)
+				{
+					auto dtorMethodDef = checkTypeInst->mTypeDef->GetMethodByName("~this");
+					if (dtorMethodDef)
+					{
+						if (!CheckProtection(dtorMethodDef->mProtection, checkTypeInst->mTypeDef, allowProtected, allowPrivate))
+						{
+							auto error = Fail(StrFormat("'%s.~this()' is inaccessible due to its protection level", TypeToString(checkTypeInst).c_str()), refNode); // CS0122																																												
+						}
+					}
+					checkTypeInst = checkTypeInst->mBaseType;
+					allowPrivate = false;
+				}
+				
+				// call dtor
+				BfExprEvaluator expressionEvaluator(this);
+				PopulateType(val.mType);
+				PopulateType(objectType, BfPopulateType_DataAndMethods);
+
+				if (objectType->mVirtualMethodTable.size() == 0)
+				{
+					if (!mCompiler->IsAutocomplete())
+						AssertErrorState();
+				}
+				else if (!IsSkippingExtraResolveChecks())
+				{
+					BfMethodInstance* methodInstance = objectType->mVirtualMethodTable[mCompiler->GetVTableMethodOffset() + 0].mImplementingMethod;
+					BF_ASSERT(methodInstance->mMethodDef->mName == "~this");
+					SizedArray<BfIRValue, 4> llvmArgs;
+					llvmArgs.push_back(mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapType(objectType)));
+					expressionEvaluator.CreateCall(refNode, methodInstance, mBfIRBuilder->GetFakeVal(), false, llvmArgs);
+				}
+
+				if ((mCompiler->mOptions.mObjectHasDebugFlags) && (!mIsComptimeModule))
+				{
+					auto int8PtrType = CreatePointerType(GetPrimitiveType(BfTypeCode_Int8));					
+					auto int8PtrVal = mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapType(int8PtrType));
+					mBfIRBuilder->CreateStore(GetConstValue8(BfObjectFlag_Deleted), int8PtrVal);
+				}
+			}
+
 			if ((fieldDef != NULL) && (fieldDef->mIsStatic == methodDef->mIsStatic) && (fieldDecl != NULL) && (fieldDecl->mFieldDtor != NULL))
 			{
 				if (fieldDef->mDeclaringType != mCurMethodInstance->mMethodDef->mDeclaringType)
@@ -17730,6 +17896,13 @@ void BfModule::EmitCtorBody(bool& skipBody)
 						continue;
 					auto initializer = fieldDef->GetInitializer();
 
+					if (fieldInst->IsAppendedObject())
+					{
+						UpdateSrcPos(fieldDef->GetNameNode());
+						AppendedObjectInit(fieldInst);						
+						continue;
+					}
+
 					if (initializer == NULL)
 					{
 						continue;
@@ -17819,7 +17992,12 @@ void BfModule::EmitCtorBody(bool& skipBody)
 							if ((wantType != NULL) &&
 								((wantType->IsVar()) || (wantType->IsLet()) || (wantType->IsRef())))
 								wantType = NULL;
-							CreateValueFromExpression(initializer, wantType, BfEvalExprFlags_FieldInitializer);
+
+							BfEvalExprFlags exprFlags = BfEvalExprFlags_FieldInitializer;
+							if (fieldDef->mIsAppend)
+								exprFlags = (BfEvalExprFlags)(exprFlags | BfEvalExprFlags_AppendFieldInitializer);
+
+							CreateValueFromExpression(initializer, wantType, exprFlags);
 						}
 					}
 
@@ -18375,6 +18553,21 @@ void BfModule::EmitIteratorBlock(bool& skipBody)
 		return;	
 }
 
+void BfModule::EmitGCMarkAppended(BfTypedValue markVal)
+{	
+	auto gcType = ResolveTypeDef(mCompiler->mGCTypeDef, BfPopulateType_DataAndMethods);
+	if (gcType == NULL)
+		return;
+	BfModuleMethodInstance markFromGCThreadMethodInstance = GetMethodByName(gcType->ToTypeInstance(), "MarkAppendedObject", 1);
+	if (!markFromGCThreadMethodInstance)
+		return;
+	
+	SizedArray<BfIRValue, 1> args;
+	args.push_back(mBfIRBuilder->CreateBitCast(markVal.mValue, mBfIRBuilder->MapType(mContext->mBfObjectType)));
+	BfExprEvaluator exprEvaluator(this);
+	exprEvaluator.CreateCall(NULL, markFromGCThreadMethodInstance.mMethodInstance, markFromGCThreadMethodInstance.mFunc, false, args);	
+}
+
 void BfModule::EmitGCMarkValue(BfTypedValue markVal, BfModuleMethodInstance markFromGCThreadMethodInstance)
 {
 	auto fieldType = markVal.mType;
@@ -19048,7 +19241,7 @@ void BfModule::ProcessMethod_ProcessDeferredLocals(int startIdx)
 	}
 }
 
-void BfModule::EmitGCMarkValue(BfTypedValue& thisValue, BfType* checkType, int memberDepth, int curOffset, HashSet<int>& objectOffsets, BfModuleMethodInstance markFromGCThreadMethodInstance)
+void BfModule::EmitGCMarkValue(BfTypedValue& thisValue, BfType* checkType, int memberDepth, int curOffset, HashSet<int>& objectOffsets, BfModuleMethodInstance markFromGCThreadMethodInstance, bool isAppendObject)
 {
 	if (checkType->IsComposite())
 		PopulateType(checkType, BfPopulateType_Data);
@@ -19184,7 +19377,10 @@ void BfModule::EmitGCMarkValue(BfTypedValue& thisValue, BfType* checkType, int m
 			markValue = BfTypedValue(mBfIRBuilder->CreateBitCast(offsetValue, mBfIRBuilder->MapType(memberPtrType)), memberType, true);
 		}
 
-		EmitGCMarkValue(markValue, markFromGCThreadMethodInstance);
+		if (isAppendObject)
+			EmitGCMarkAppended(markValue);
+		else
+			EmitGCMarkValue(markValue, markFromGCThreadMethodInstance);
 		return;
 	}
 
@@ -19219,7 +19415,8 @@ void BfModule::EmitGCMarkValue(BfTypedValue& thisValue, BfType* checkType, int m
 			if ((fieldDef->mDeclaringType->mTypeDeclaration != methodDef->mDeclaringType->mTypeDeclaration))
 				continue;
 		}
-		EmitGCMarkValue(thisValue, fieldInst.mResolvedType, memberDepth + 1, curOffset + fieldInst.mDataOffset, objectOffsets, markFromGCThreadMethodInstance);
+		
+		EmitGCMarkValue(thisValue, fieldInst.mResolvedType, memberDepth + 1, curOffset + fieldInst.mDataOffset, objectOffsets, markFromGCThreadMethodInstance, fieldInst.IsAppendedObject());
 	}
 	
 	if ((typeInstance->mBaseType != NULL) && (typeInstance->mBaseType != mContext->mBfObjectType))
@@ -19373,11 +19570,25 @@ void BfModule::EmitGCMarkMembers()
 						}
 						else if (!fieldDef->mIsStatic)
 						{
-							markVal = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(thisValue.mValue, 0, fieldInst.mDataIdx/*, fieldDef->mName*/), fieldInst.mResolvedType, true);
+							if (fieldInst.IsAppendedObject())
+							{
+								auto fieldAddr = mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[0]->mValue, 0, fieldInst.mDataIdx);
+								auto val = mBfIRBuilder->CreateBitCast(fieldAddr, mBfIRBuilder->MapType(mContext->mBfObjectType));
+								markVal = BfTypedValue(mBfIRBuilder->CreateBitCast(fieldAddr, mBfIRBuilder->MapType(fieldInst.mResolvedType)), fieldInst.mResolvedType);
+							}
+							else
+							{
+								markVal = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(thisValue.mValue, 0, fieldInst.mDataIdx/*, fieldDef->mName*/), fieldInst.mResolvedType, true);
+							}
 						}
 
 						if (markVal)
-							EmitGCMarkValue(markVal, markFromGCThreadMethodInstance);
+						{
+							if (fieldInst.IsAppendedObject())
+								EmitGCMarkAppended(markVal);
+							else
+								EmitGCMarkValue(markVal, markFromGCThreadMethodInstance);
+						}
 					}
 				}
 			}
@@ -20881,15 +21092,47 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup,
 				}
 			}
 		}
+		auto int8PtrType = CreatePointerType(GetPrimitiveType(BfTypeCode_Int8));
+
 		int curSize = mCurTypeInstance->mInstSize;
 		if (curSize > prevSize)
-		{
-			auto int8PtrType = CreatePointerType(GetPrimitiveType(BfTypeCode_Int8));
+		{			
 			auto int8PtrVal = mBfIRBuilder->CreateBitCast(thisVal.mValue, mBfIRBuilder->MapType(int8PtrType));
 			int8PtrVal = mBfIRBuilder->CreateInBoundsGEP(int8PtrVal, GetConstValue(prevSize));
 			mBfIRBuilder->CreateMemSet(int8PtrVal, GetConstValue8(0), GetConstValue(curSize - prevSize), GetConstValue(mCurTypeInstance->mInstAlign));
 		}
 
+		if ((mCompiler->mOptions.mObjectHasDebugFlags) && (!mIsComptimeModule))
+		{
+			auto useThis = mCurMethodState->mLocals[0]->mValue;
+			auto useThisType = mCurTypeInstance;
+
+			auto checkTypeInst = mCurTypeInstance;
+			while (checkTypeInst != NULL)
+			{
+				for (auto& fieldInstance : checkTypeInst->mFieldInstances)
+				{
+					auto fieldDef = fieldInstance.GetFieldDef();
+					if ((fieldDef == NULL) || (fieldDef->mIsStatic))
+						continue;
+					if (fieldInstance.IsAppendedObject())
+					{
+						if (checkTypeInst != useThisType)
+						{
+							useThis = mBfIRBuilder->CreateBitCast(useThis, mBfIRBuilder->MapType(checkTypeInst));
+							useThisType = checkTypeInst;
+						}
+
+						BfIRValue fieldAddr = mBfIRBuilder->CreateInBoundsGEP(useThis, 0, fieldInstance.mDataIdx);
+						auto int8PtrVal = mBfIRBuilder->CreateBitCast(fieldAddr, mBfIRBuilder->MapType(int8PtrType));
+						mBfIRBuilder->CreateStore(GetConstValue8(BfObjectFlag_Deleted), int8PtrVal);
+					}
+				}
+
+				checkTypeInst = checkTypeInst->mBaseType;
+			}
+		}
+
 		skipUpdateSrcPos = true;
 	}
 	else if (((methodDef->mMethodType == BfMethodType_Ctor) || (methodDef->mMethodType == BfMethodType_CtorNoBody)) && (!hasExternSpecifier))

+ 7 - 2
IDEHelper/Compiler/BfModule.h

@@ -88,6 +88,7 @@ enum BfEvalExprFlags
 	BfEvalExprFlags_FromConversionOp_Explicit = 0x10000000,
 	BfEvalExprFlags_AllowGenericConstValue = 0x20000000,
 	BfEvalExprFlags_IsExpressionBody = 0x40000000,
+	BfEvalExprFlags_AppendFieldInitializer = 0x80000000,
 
 	BfEvalExprFlags_InheritFlags = BfEvalExprFlags_NoAutoComplete | BfEvalExprFlags_Comptime | BfEvalExprFlags_DeclType
 };
@@ -919,11 +920,13 @@ class BfConstResolveState
 public:
 	BfMethodInstance* mMethodInstance;
 	BfConstResolveState* mPrevConstResolveState;
+	bool mInCalcAppend;
 
 	BfConstResolveState()
 	{
 		mMethodInstance = NULL;
 		mPrevConstResolveState = NULL;
+		mInCalcAppend = false;
 	}
 };
 
@@ -1840,6 +1843,7 @@ public:
 	void CreateStaticField(BfFieldInstance* fieldInstance, bool isThreadLocal = false);
 	void ResolveConstField(BfTypeInstance* typeInst, BfFieldInstance* fieldInstance, BfFieldDef* field, bool forceResolve = false);
 	BfTypedValue GetFieldInitializerValue(BfFieldInstance* fieldInstance, BfExpression* initializer = NULL, BfFieldDef* fieldDef = NULL, BfType* fieldType = NULL, bool doStore = false);
+	void AppendedObjectInit(BfFieldInstance* fieldInstance);
 	void MarkFieldInitialized(BfFieldInstance* fieldInstance);
 	bool IsThreadLocal(BfFieldInstance* fieldInstance);
 	BfType* ResolveVarFieldType(BfTypeInstance* typeInst, BfFieldInstance* fieldInstance, BfFieldDef* field);	
@@ -1967,7 +1971,7 @@ public:
 	void AddMethodToWorkList(BfMethodInstance* methodInstance);
 	bool IsInterestedInMethod(BfTypeInstance* typeInstance, BfMethodDef* methodDef);
 	void CalcAppendAlign(BfMethodInstance* methodInst);
-	BfTypedValue TryConstCalcAppend(BfMethodInstance* methodInst, SizedArrayImpl<BfIRValue>& args);
+	BfTypedValue TryConstCalcAppend(BfMethodInstance* methodInst, SizedArrayImpl<BfIRValue>& args, bool force = false);
 	BfTypedValue CallBaseCtorCalc(bool constOnly);
 	void EmitCtorCalcAppend();	
 	void CreateStaticCtor();	
@@ -1980,7 +1984,8 @@ public:
 	void EmitDtorBody();
 	void EmitEnumToStringBody();
 	void EmitTupleToStringBody();		
-	void EmitGCMarkValue(BfTypedValue& thisValue, BfType* checkType, int memberDepth, int curOffset, HashSet<int>& objectOffsets, BfModuleMethodInstance markFromGCThreadMethodInstance);
+	void EmitGCMarkAppended(BfTypedValue markVal);
+	void EmitGCMarkValue(BfTypedValue& thisValue, BfType* checkType, int memberDepth, int curOffset, HashSet<int>& objectOffsets, BfModuleMethodInstance markFromGCThreadMethodInstance, bool isAppendObject = false);
 	void EmitGCMarkValue(BfTypedValue markVal, BfModuleMethodInstance markFromGCThreadMethodInstance);
 	void EmitGCMarkMembers();
 	void EmitGCFindTLSMembers();

+ 93 - 4
IDEHelper/Compiler/BfModuleTypeUtils.cpp

@@ -820,6 +820,9 @@ void BfModule::InitType(BfType* resolvedTypeRef, BfPopulateType populateType)
 void BfModule::AddFieldDependency(BfTypeInstance* typeInstance, BfFieldInstance* fieldInstance, BfType* fieldType)
 {
 	auto depFlag = fieldType->IsValueType() ? BfDependencyMap::DependencyFlag_ValueTypeMemberData : BfDependencyMap::DependencyFlag_PtrMemberData;
+	if (fieldInstance->IsAppendedObject())
+		depFlag = BfDependencyMap::DependencyFlag_ValueTypeMemberData;
+
 	AddDependency(fieldType, typeInstance, depFlag);
 
 	if ((fieldType->IsStruct()) && (fieldType->IsGenericTypeInstance()))
@@ -4565,6 +4568,12 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy
 								
 				auto initializer = field->GetInitializer();
 
+				if ((field->mIsAppend) && (!resolvedTypeRef->IsObject()))
+					Fail("Appended objects can only be declared in class types", field->GetFieldDeclaration()->mExternSpecifier, true);
+
+				if ((field->mIsAppend) && (isUnion))
+					Fail("Appended objects cannot be declared in unions", field->GetFieldDeclaration()->mExternSpecifier, true);
+				
 				if (field->IsEnumCaseEntry())
 				{
 					if (typeInstance->IsEnum())
@@ -5109,6 +5118,83 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy
 
 						int dataSize = resolvedFieldType->mSize;
 						int alignSize = resolvedFieldType->mAlign;
+
+						if (fieldInstance->IsAppendedObject())
+						{
+							SetAndRestoreValue<BfFieldDef*> prevTypeRef(mContext->mCurTypeState->mCurFieldDef, fieldDef);
+							SetAndRestoreValue<BfTypeState::ResolveKind> prevResolveKind(mContext->mCurTypeState->mResolveKind, BfTypeState::ResolveKind_FieldType);
+
+							auto fieldTypeInst = resolvedFieldType->ToTypeInstance();
+							dataSize = fieldTypeInst->mInstSize;
+							alignSize = fieldTypeInst->mInstAlign;
+
+							if ((typeInstance != NULL) && (fieldTypeInst->mTypeDef->mIsAbstract))
+							{
+								Fail("Cannot create an instance of an abstract class", nameRefNode);
+							}
+
+							SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true);
+							BfMethodState methodState;
+							SetAndRestoreValue<BfMethodState*> prevMethodState(mCurMethodState, &methodState);
+							methodState.mTempKind = BfMethodState::TempKind_NonStatic;
+							
+							BfTypedValue appendIndexValue;
+							BfExprEvaluator exprEvaluator(this);
+
+							BfResolvedArgs resolvedArgs;
+
+							auto fieldDecl = fieldDef->GetFieldDeclaration();
+							if (auto invocationExpr = BfNodeDynCast<BfInvocationExpression>(fieldDecl->mInitializer))
+							{
+								resolvedArgs.Init(invocationExpr->mOpenParen, &invocationExpr->mArguments, &invocationExpr->mCommas, invocationExpr->mCloseParen);
+								exprEvaluator.ResolveArgValues(resolvedArgs, BfResolveArgsFlag_DeferParamEval);
+							}
+
+							BfFunctionBindResult bindResult;
+							bindResult.mSkipThis = true;
+							bindResult.mWantsArgs = true;
+							SetAndRestoreValue<BfFunctionBindResult*> prevBindResult(exprEvaluator.mFunctionBindResult, &bindResult);
+							
+							BfTypedValue emptyThis(mBfIRBuilder->GetFakeVal(), resolvedTypeRef, resolvedTypeRef->IsStruct());
+
+							exprEvaluator.mBfEvalExprFlags = BfEvalExprFlags_Comptime;
+							auto ctorResult = exprEvaluator.MatchConstructor(nameRefNode, NULL, emptyThis, fieldTypeInst, resolvedArgs, false, true);
+
+							if ((bindResult.mMethodInstance != NULL) && (bindResult.mMethodInstance->mMethodDef->mHasAppend))
+							{
+								auto calcAppendMethodModule = GetMethodInstanceAtIdx(bindResult.mMethodInstance->GetOwner(), bindResult.mMethodInstance->mMethodDef->mIdx + 1, BF_METHODNAME_CALCAPPEND);
+
+								SizedArray<BfIRValue, 2> irArgs;
+								if (bindResult.mIRArgs.size() > 1)
+									irArgs.Insert(0, &bindResult.mIRArgs[1], bindResult.mIRArgs.size() - 1);
+								BfTypedValue appendSizeTypedValue = TryConstCalcAppend(calcAppendMethodModule.mMethodInstance, irArgs, true);
+								if (appendSizeTypedValue)
+								{
+									int appendAlign = calcAppendMethodModule.mMethodInstance->mAppendAllocAlign;
+									dataSize = BF_ALIGN(dataSize, appendAlign);
+									alignSize = BF_MAX(alignSize, appendAlign);
+
+									auto constant = mBfIRBuilder->GetConstant(appendSizeTypedValue.mValue);
+									if (constant != NULL)
+									{
+										dataSize += constant->mInt32;
+									}
+								}
+								else
+								{
+									Fail(StrFormat("Append constructor '%s' does not result in a constant size", MethodToString(bindResult.mMethodInstance).c_str()), nameRefNode);
+								}
+								
+							}
+						}
+						else if (fieldDef->mIsAppend)
+						{
+							if (typeInstance->IsObject())
+								Fail("Append fields can only be declared in classes", nameRefNode, true);
+							else if ((!resolvedFieldType->IsObject()) && (!resolvedFieldType->IsGenericParam()))
+								Fail("Append fields must be classes", nameRefNode, true);
+						}
+
 						fieldInstance->mDataSize = dataSize;
 						if (!isUnion)
 						{
@@ -5244,7 +5330,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy
 					alignBuckets[alignBits].RemoveAt(0);
 					dataFieldVec.push_back(fieldInst);
 					curSize = BF_ALIGN(curSize, fieldInst->GetAlign(packing));
-					curSize += fieldInst->mResolvedType->mSize;
+					curSize += fieldInst->mDataSize;
 					foundEntry = true;
 
 					if (!isHighestBucket)
@@ -5265,7 +5351,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy
 						 	alignBuckets[alignBits].RemoveAt(0);
 						 	dataFieldVec.push_back(fieldInst);
 						 	curSize = BF_ALIGN(curSize, fieldInst->GetAlign(packing));
-						 	curSize += fieldInst->mResolvedType->mSize;
+						 	curSize += fieldInst->mDataSize;
 							break;
 						}
 					}
@@ -5281,9 +5367,12 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy
 			auto resolvedFieldType = fieldInstance->GetResolvedType();
 
 			BF_ASSERT(resolvedFieldType->mSize >= 0);
-			int dataSize = resolvedFieldType->mSize;
+			
+			if (fieldInstance->mDataSize == 0)			
+				fieldInstance->mDataSize = resolvedFieldType->mSize;			
+
+			int dataSize = fieldInstance->mDataSize;
 			int alignSize = fieldInstance->GetAlign(packing);
-			fieldInstance->mDataSize = dataSize;
 
 			int nextDataPos = dataPos;			
 			nextDataPos = (dataPos + (alignSize - 1)) & ~(alignSize - 1);

+ 25 - 0
IDEHelper/Compiler/BfReducer.cpp

@@ -6319,6 +6319,7 @@ BfAstNode* BfReducer::ReadTypeMember(BfTokenNode* tokenNode, bool declStarted, i
 	case BfToken_Override:
 	case BfToken_Abstract:
 	case BfToken_Concrete:
+	case BfToken_Append:
 	case BfToken_Extern:
 	case BfToken_New:
 	case BfToken_Implicit:
@@ -6575,6 +6576,30 @@ BfAstNode* BfReducer::ReadTypeMember(BfTokenNode* tokenNode, bool declStarted, i
 
 		if (token == BfToken_Extern)
 		{
+			if ((fieldDecl->mExternSpecifier != NULL) && (fieldDecl->mExternSpecifier->mToken == BfToken_Append))
+ 			{
+ 				Fail("Extern cannot be used with 'append' specified", tokenNode);
+ 			}
+ 			else if (fieldDecl->mExternSpecifier != NULL)
+ 			{
+				Fail("Extern already specified", tokenNode);
+ 			}
+
+			MEMBER_SET(fieldDecl, mExternSpecifier, tokenNode);
+			handled = true;
+		}
+
+		if (token == BfToken_Append)
+		{
+			if ((fieldDecl->mExternSpecifier != NULL) && (fieldDecl->mExternSpecifier->mToken == BfToken_Extern))
+			{
+				Fail("Append cannot be used with 'extern' specified", tokenNode);
+			}
+			else if (fieldDecl->mExternSpecifier != NULL)
+			{
+				Fail("Append already specified", tokenNode);
+			}
+
 			MEMBER_SET(fieldDecl, mExternSpecifier, tokenNode);
 			handled = true;
 		}

+ 8 - 0
IDEHelper/Compiler/BfResolvedTypeUtils.cpp

@@ -498,6 +498,8 @@ void BfFieldInstance::GetDataRange(int& dataIdx, int& dataCount)
 int BfFieldInstance::GetAlign(int packing)
 {
 	int align = mResolvedType->mAlign;
+	if (IsAppendedObject())
+		align = mResolvedType->ToTypeInstance()->mInstAlign;
 	if (packing > 0)
 		align = BF_MIN(align, packing);
 	if (mCustomAttributes != NULL)
@@ -528,6 +530,12 @@ int BfFieldInstance::GetAlign(int packing)
 	return align;
 }
 
+bool BfFieldInstance::IsAppendedObject()
+{
+	auto fieldDef = GetFieldDef();
+	return (fieldDef != NULL) && (fieldDef->mIsAppend) && (mResolvedType->IsObject()) && (mOwner->IsObject());
+}
+
 //////////////////////////////////////////////////////////////////////////
 
 int64 BfDeferredMethodCallData::GenerateMethodId(BfModule* module, int64 methodId)

+ 1 - 0
IDEHelper/Compiler/BfResolvedTypeUtils.h

@@ -1469,6 +1469,7 @@ public:
 	void SetResolvedType(BfType* type);
 	void GetDataRange(int& dataIdx, int& dataCount);
 	int GetAlign(int packing);
+	bool IsAppendedObject();
 };
 
 enum BfMethodRefKind

+ 7 - 0
IDEHelper/Compiler/BfStmtEvaluator.cpp

@@ -1929,6 +1929,13 @@ BfLocalVariable* BfModule::HandleVariableDeclaration(BfVariableDeclaration* varD
 	BfLocalVariable* localVar = AddLocalVariableDef(localDef, true, false, BfIRValue(), initType);
 	if (wantsStore)
 		mBfIRBuilder->CreateAlignedStore(initValue.mValue, localVar->mAddr, localVar->mResolvedType->mAlign);
+
+	if ((mCurMethodState->mConstResolveState != NULL) && (mCurMethodState->mConstResolveState->mInCalcAppend))
+	{
+		if (localDef->mValue.IsConst())
+			localDef->mConstValue = localDef->mValue;
+	}
+
 	return localVar;
 }
 

+ 4 - 1
IDEHelper/Compiler/BfSystem.h

@@ -610,6 +610,7 @@ public:
 	bool mIsInline;
 	bool mIsVolatile;
 	bool mIsExtern;	
+	bool mIsAppend;
 	bool mIsProperty;	
 	BfAstNode* mFieldDeclaration;
 	// It may seem that fields and properties don't need a 'mNextWithSameName', but with extensions it's possible
@@ -625,6 +626,7 @@ public:
 		mUsingProtection = BfProtection_Hidden;
 		mIsInline = false;
 		mIsExtern = false;
+		mIsAppend = false;
 		mIsVolatile = false;
 		mIsProperty = false;
 		mFieldDeclaration = NULL;
@@ -1680,7 +1682,8 @@ enum BfFieldFlags
 	BfFieldFlags_EnumPayload = 0x100,
 	BfFieldFlags_EnumDiscriminator = 0x200,
 	BfFieldFlags_EnumCase = 0x400,
-	BfFieldFlags_ReadOnly = 0x800
+	BfFieldFlags_ReadOnly = 0x800,
+	BfFieldFlags_Appended = 0x1000
 };
 
 enum BfReflectKind

+ 7 - 0
IDEHelper/Compiler/CeMachine.cpp

@@ -5667,6 +5667,11 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8*
 				Fail(_GetCurFrame(), "Array out of bounds");
 				return false;
 			}
+			else if (checkFunction->mFunctionKind == CeFunctionKind_OOB)
+			{
+				Fail(_GetCurFrame(), "Object not initialized");
+				return false;
+			}
 			else if (checkFunction->mFunctionKind == CeFunctionKind_Malloc)
 			{
 				int64 size;
@@ -9340,6 +9345,8 @@ void CeMachine::CheckFunctionKind(CeFunction* ceFunction)
 			{
 				if (methodDef->mName == "ThrowIndexOutOfRange")
 					ceFunction->mFunctionKind = CeFunctionKind_OOB;
+				else if (methodDef->mName == "ThrowObjectNotInitialized")
+					ceFunction->mFunctionKind = CeFunctionKind_ObjectNotInitialized;
 				else if (methodDef->mName == "FatalError")
 					ceFunction->mFunctionKind = CeFunctionKind_FatalError;
 				else if (methodDef->mName == "Dbg_RawAlloc")

+ 1 - 0
IDEHelper/Compiler/CeMachine.h

@@ -422,6 +422,7 @@ enum CeFunctionKind
 	CeFunctionKind_Normal,
 	CeFunctionKind_Extern,
 	CeFunctionKind_OOB,
+	CeFunctionKind_ObjectNotInitialized,
 	CeFunctionKind_Malloc,
 	CeFunctionKind_Free,
 	CeFunctionKind_DynCheckFailed,

+ 18 - 0
IDEHelper/Tests/src/Append.bf

@@ -76,6 +76,13 @@ namespace Tests
 			}
 		}
 
+		class ClassF
+		{
+			public int mA = 123;
+			public append String mB = .(mA);
+			public int mC = 234;
+		}
+
 		static void CheckData(Object obj, int lastAllocSize, uint8[] data)
 		{
 			int objSize = typeof(Object).InstanceSize;
@@ -110,6 +117,17 @@ namespace Tests
 				0x80, 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10
 				));
 			delete:trackedAlloc ce;
+
+			int sizeDiff = Math.Abs(typeof(ClassF).InstanceSize - (1024 + sizeof(int)*5));
+			Test.Assert(sizeDiff < 32);
+
+			ClassF cf = scope .();
+			cf.mB.Append("Abc");
+			Test.Assert(cf.mA == 123);
+			Test.Assert(cf.mB == "Abc");
+			Test.Assert(cf.mB.AllocSize == 1024);
+			Test.Assert(cf.mC == 234);
+			cf.mB.Append('!', 2048);
 		}
 	}
 }