diff --git a/IDEHelper/Compiler/BfAst.h b/IDEHelper/Compiler/BfAst.h index 3b5f7695..fb2d4c11 100644 --- a/IDEHelper/Compiler/BfAst.h +++ b/IDEHelper/Compiler/BfAst.h @@ -338,6 +338,7 @@ class BfMemberReferenceExpression; class BfDynamicCastExpression; class BfCheckTypeExpression; class BfConstructorDeclaration; +class BfAutoConstructorDeclaration; class BfDestructorDeclaration; class BfQualifiedTypeReference; class BfUsingDirective; @@ -2282,6 +2283,7 @@ public: BfTokenNode* mTypeNode; BfIdentifierNode* mNameNode; BfAstNode* mDefineNode; + BfAutoConstructorDeclaration* mAutoCtor; BfGenericParamsDeclaration* mGenericParams; BfGenericConstraintsDeclaration* mGenericConstraintsDeclaration; bool mIgnoreDeclaration; @@ -2994,6 +2996,12 @@ public: }; BF_AST_DECL(BfConstructorDeclaration, BfMethodDeclaration); +class BfAutoConstructorDeclaration : public BfConstructorDeclaration +{ +public: + BF_AST_TYPE(BfAutoConstructorDeclaration, BfConstructorDeclaration); +}; BF_AST_DECL(BfAutoConstructorDeclaration, BfConstructorDeclaration); + class BfDestructorDeclaration : public BfMethodDeclaration { public: diff --git a/IDEHelper/Compiler/BfDefBuilder.cpp b/IDEHelper/Compiler/BfDefBuilder.cpp index 5ae400ce..67489bd6 100644 --- a/IDEHelper/Compiler/BfDefBuilder.cpp +++ b/IDEHelper/Compiler/BfDefBuilder.cpp @@ -489,6 +489,13 @@ BfMethodDef* BfDefBuilder::CreateMethodDef(BfMethodDeclaration* methodDeclaratio methodDef->mIsVirtual = true; } + bool isAutoCtor = false; + if (auto autoCtorDeclaration = BfNodeDynCast(methodDeclaration)) + { + methodDef->mProtection = BfProtection_Public; + isAutoCtor = true; + } + if (auto ctorDeclaration = BfNodeDynCast(methodDeclaration)) { methodDef->mIsMutating = true; @@ -645,8 +652,16 @@ BfMethodDef* BfDefBuilder::CreateMethodDef(BfMethodDeclaration* methodDeclaratio paramDef->mMethodGenericParamIdx = mSystem->GetGenericParamIdx(methodDef->mGenericParams, paramDef->mTypeRef); if (paramDecl->mModToken == NULL) paramDef->mParamKind = BfParamKind_Normal; - else // + else if (paramDecl->mModToken->mToken == BfToken_Params) paramDef->mParamKind = BfParamKind_Params; + else if ((paramDecl->mModToken->mToken == BfToken_ReadOnly) && (isAutoCtor)) + { + // Readonly specifier + } + else + { + Fail(StrFormat("Invalid use of '%s' specifier", BfTokenToString(paramDecl->mModToken->mToken)), paramDecl->mModToken); + } if ((mCurTypeDef->mIsFunction) && (paramIdx == 0) && (paramDef->mName == "this")) { @@ -707,6 +722,23 @@ BfMethodDef* BfDefBuilder::CreateMethodDef(BfMethodDeclaration* methodDeclaratio } } + if (isAutoCtor) + { + for (auto paramDef : methodDef->mParams) + { + auto fieldDef = new BfFieldDef(); + fieldDef->mName = paramDef->mName; + fieldDef->mTypeRef = paramDef->mTypeRef; + fieldDef->mProtection = BfProtection_Public; + fieldDef->mDeclaringType = mCurTypeDef; + fieldDef->mIdx = mCurTypeDef->mFields.mSize; + if ((paramDef->mParamDeclaration->mModToken != NULL) && + (paramDef->mParamDeclaration->mModToken->mToken == BfToken_ReadOnly)) + fieldDef->mIsReadOnly = true; + mCurTypeDef->mFields.Add(fieldDef); + } + } + ParseAttributes(methodDeclaration->mAttributes, methodDef); return methodDef; } @@ -1819,6 +1851,9 @@ void BfDefBuilder::Visit(BfTypeDeclaration* typeDeclaration) } } + if (typeDeclaration->mAutoCtor != NULL) + VisitChildNoRef(typeDeclaration->mAutoCtor); + if (auto defineBlock = BfNodeDynCast(typeDeclaration->mDefineNode)) { for (auto& member : *defineBlock) @@ -1829,8 +1864,9 @@ void BfDefBuilder::Visit(BfTypeDeclaration* typeDeclaration) else if (auto defineTokenNode = BfNodeDynCast(typeDeclaration->mDefineNode)) { if (defineTokenNode->GetToken() == BfToken_Semicolon) - { - mCurTypeDef->mIsOpaque = true; + { + if (typeDeclaration->mAutoCtor == NULL) + mCurTypeDef->mIsOpaque = true; } } diff --git a/IDEHelper/Compiler/BfElementVisitor.cpp b/IDEHelper/Compiler/BfElementVisitor.cpp index 93a00fbc..8378ed16 100644 --- a/IDEHelper/Compiler/BfElementVisitor.cpp +++ b/IDEHelper/Compiler/BfElementVisitor.cpp @@ -1147,6 +1147,7 @@ void BfElementVisitor::Visit(BfTypeDeclaration* typeDeclaration) VisitChild(typeDeclaration->mTypeNode); VisitChild(typeDeclaration->mNameNode); VisitChild(typeDeclaration->mColonToken); + VisitChild(typeDeclaration->mAutoCtor); for (auto& baseClass : typeDeclaration->mBaseClasses) VisitChild(baseClass); for (auto& comma : typeDeclaration->mBaseClassCommas) diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index 93d9db73..1982b77a 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -3876,7 +3876,7 @@ void BfModule::ResolveConstField(BfTypeInstance* typeInstance, BfFieldInstance* BfType* BfModule::ResolveVarFieldType(BfTypeInstance* typeInstance, BfFieldInstance* fieldInstance, BfFieldDef* field) { - bool isDeclType = BfNodeDynCastExact(field->mFieldDeclaration->mTypeRef) != NULL; + bool isDeclType = (field->mFieldDeclaration != NULL) && BfNodeDynCastExact(field->mFieldDeclaration->mTypeRef) != NULL; auto fieldType = fieldInstance->GetResolvedType(); if ((field->mIsConst) && (!isDeclType)) @@ -16155,7 +16155,7 @@ void BfModule::EmitCtorBody(bool& skipBody) } EmitInitBlocks(_CheckInitBlock); - + if (hadInlineInitBlock) { RestoreScopeState(); @@ -16229,6 +16229,34 @@ void BfModule::EmitCtorBody(bool& skipBody) } } + if (!methodInstance->mIsAutocompleteMethod) + { + if (auto autoDecl = BfNodeDynCast(methodDeclaration)) + { + BfExprEvaluator exprEvaluator(this); + for (int paramIdx = 0; paramIdx < methodDef->mParams.mSize; paramIdx++) + { + auto paramDef = methodDef->mParams[paramIdx]; + auto& fieldInstance = mCurTypeInstance->mFieldInstances[paramIdx]; + BF_ASSERT(paramDef->mName == fieldInstance.GetFieldDef()->mName); + if (fieldInstance.mDataIdx < 0) + continue; + + auto localVar = mCurMethodState->mLocals[paramIdx + 1]; + BF_ASSERT(localVar->mName == paramDef->mName); + auto localVal = exprEvaluator.LoadLocal(localVar); + + if (paramDef->mParamKind != BfParamKind_Normal) + continue; + + auto thisVal = GetThis(); + auto fieldPtr = mBfIRBuilder->CreateInBoundsGEP(thisVal.mValue, 0, fieldInstance.mDataIdx); + mBfIRBuilder->CreateAlignedStore(localVar->mValue, fieldPtr, localVar->mResolvedType->mAlign); + MarkFieldInitialized(&fieldInstance); + } + } + } + // Call base ctor (if applicable) BfTypeInstance* targetType = NULL; BfAstNode* targetRefNode = NULL; @@ -19521,7 +19549,11 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) for (auto localVar : mCurMethodState->mLocals) { - if ((skipEndChecks) || (methodDef->mBody == NULL)) + if (auto autoCtorDecl = BfNodeDynCast(methodDeclaration)) + { + // + } + else if ((skipEndChecks) || (methodDef->mBody == NULL)) break; LocalVariableDone(localVar, true); } @@ -21751,7 +21783,12 @@ genericParam->mExternType = GetPrimitiveType(BfTypeCode_Var); { if (methodDeclaration->mEndSemicolon == NULL) { - AssertParseErrorState(); + if (auto autoCtorDecl = BfNodeDynCast(methodDeclaration)) + { + // + } + else + AssertParseErrorState(); } else { diff --git a/IDEHelper/Compiler/BfPrinter.cpp b/IDEHelper/Compiler/BfPrinter.cpp index df341908..b3323aca 100644 --- a/IDEHelper/Compiler/BfPrinter.cpp +++ b/IDEHelper/Compiler/BfPrinter.cpp @@ -2758,6 +2758,7 @@ void BfPrinter::Visit(BfTypeDeclaration* typeDeclaration) ExpectSpace(); QueueVisitChild(typeDeclaration->mNameNode); QueueVisitChild(typeDeclaration->mGenericParams); + QueueVisitChild(typeDeclaration->mAutoCtor); if (typeDeclaration->mColonToken != NULL) { ExpectSpace(); diff --git a/IDEHelper/Compiler/BfReducer.cpp b/IDEHelper/Compiler/BfReducer.cpp index 1aff4c71..ef271607 100644 --- a/IDEHelper/Compiler/BfReducer.cpp +++ b/IDEHelper/Compiler/BfReducer.cpp @@ -8420,6 +8420,14 @@ BfAstNode* BfReducer::CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDi if (baseTypeIdx > 0) { + if (auto tokenNode = BfNodeDynCast(nextNode)) + { + if (tokenNode->mToken == BfToken_Semicolon) + { + break; + } + } + BfTokenNode* commaToken = NULL; if (typeDeclaration->mGenericParams != NULL) { @@ -8438,6 +8446,32 @@ BfAstNode* BfReducer::CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDi baseClassCommas.push_back(commaToken); } + if (auto tokenNode = BfNodeDynCast(mVisitorPos.GetNext())) + { + if (tokenNode->mToken == BfToken_This) + { + mVisitorPos.MoveNext(); + auto ctorDecl = mAlloc->Alloc(); + BfDeferredAstSizedArray params(ctorDecl->mParams, mAlloc); + BfDeferredAstSizedArray commas(ctorDecl->mCommas, mAlloc); + ctorDecl->mReturnType = NULL; + ReplaceNode(tokenNode, ctorDecl); + MEMBER_SET(ctorDecl, mThisToken, tokenNode); + ParseMethod(ctorDecl, ¶ms, &commas); + + if (typeDeclaration->mAutoCtor == NULL) + { + MEMBER_SET(typeDeclaration, mAutoCtor, ctorDecl); + } + else + { + Fail("Only one auto-constructor is allowed", ctorDecl); + AddErrorNode(ctorDecl); + } + continue; + } + } + auto baseType = CreateTypeRefAfter(typeDeclaration); if (baseType == NULL) break; @@ -8879,7 +8913,7 @@ BfTokenNode* BfReducer::ParseMethodParams(BfAstNode* node, SizedArrayImpl(ctorDecl)) + return true; + if ((endToken != NULL) && (endToken->GetToken() == BfToken_Semicolon)) { MEMBER_SET_CHECKED_BOOL(methodDeclaration, mEndSemicolon, endToken); diff --git a/IDEHelper/Tests/src/Structs.bf b/IDEHelper/Tests/src/Structs.bf index a1b13b1c..9fe14660 100644 --- a/IDEHelper/Tests/src/Structs.bf +++ b/IDEHelper/Tests/src/Structs.bf @@ -124,6 +124,17 @@ namespace Tests Dictionary dict; } + struct StructL : this(int a, int b); + + struct StructM : this(readonly int a, readonly int b) + { + public int c; + this + { + c = 100; + } + } + [Test] static void TestBasics() { @@ -142,6 +153,21 @@ namespace Tests sb0 = .{ mA = 3, mB = 4 }; Test.Assert(sb0.mA == 3); Test.Assert(sb0.mB == 4); + + StructL sl = .(12, 23); + Test.Assert(sl.a == 12); + Test.Assert(sl.b == 23); + + StructM sm = .(12, 23); + [IgnoreErrors] + { + sm.a += 100; + sm.b += 100; + sm.c += 100; + } + Test.Assert(sm.a == 12); + Test.Assert(sm.b == 23); + Test.Assert(sm.c == 200); } [Test]