diff --git a/BeefLibs/corlib/src/Result.bf b/BeefLibs/corlib/src/Result.bf index ee35e2d8..30aede4a 100644 --- a/BeefLibs/corlib/src/Result.bf +++ b/BeefLibs/corlib/src/Result.bf @@ -59,6 +59,15 @@ namespace System return default(T); } + public static nullable(T) operator?(Self val) + { + switch (val) + { + case .Ok(let inner): return inner; + case .Err: return null; + } + } + [SkipCall] public void Dispose() { @@ -151,6 +160,15 @@ namespace System return default(T); } + public static nullable(T) operator?(Self val) + { + switch (val) + { + case .Ok(let inner): return inner; + case .Err: return null; + } + } + [SkipCall] public void Dispose() { diff --git a/IDE/mintest/minlib/src/System/Result.bf b/IDE/mintest/minlib/src/System/Result.bf index 23cdb249..beb45b70 100644 --- a/IDE/mintest/minlib/src/System/Result.bf +++ b/IDE/mintest/minlib/src/System/Result.bf @@ -50,6 +50,15 @@ namespace System return default(T); } + public static nullable(T) operator?(Self val) + { + switch (val) + { + case .Ok(let inner): return inner; + case .Err: return null; + } + } + [SkipCall] public void Dispose() { @@ -84,6 +93,18 @@ namespace System } } + /*extension Result where T : class + { + public static T operator?(Self val) + { + switch (val) + { + case .Ok(let inner): return inner; + case .Err: return default; + } + } + }*/ + enum Result { case Ok(T val); @@ -138,6 +159,15 @@ namespace System return default(T); } + public static nullable(T) operator?(Self val) + { + switch (val) + { + case .Ok(let inner): return inner; + case .Err: return null; + } + } + [SkipCall] public void Dispose() { diff --git a/IDEHelper/Compiler/BfAst.h b/IDEHelper/Compiler/BfAst.h index 4974c60e..9eeb5d56 100644 --- a/IDEHelper/Compiler/BfAst.h +++ b/IDEHelper/Compiler/BfAst.h @@ -149,6 +149,7 @@ enum BfToken : uint8 BfToken_Namespace, BfToken_New, BfToken_Null, + BfToken_Nullable, BfToken_Operator, BfToken_Out, BfToken_Override, @@ -359,7 +360,7 @@ class BfGenericConstraintsDeclaration; class BfAttributeDirective; class BfNullableTypeRef; class BfRefTypeRef; -class BfRetTypeTypeRef; +class BfModifiedTypeRef; class BfConstTypeRef; class BfConstExprTypeRef; class BfInlineAsmStatement; @@ -443,7 +444,7 @@ public: virtual void Visit(BfConstTypeRef* typeRef); virtual void Visit(BfConstExprTypeRef* typeRef); virtual void Visit(BfRefTypeRef* typeRef); - virtual void Visit(BfRetTypeTypeRef* typeRef); + virtual void Visit(BfModifiedTypeRef* typeRef); virtual void Visit(BfArrayTypeRef* typeRef); virtual void Visit(BfGenericInstanceTypeRef* typeRef); virtual void Visit(BfTupleTypeRef* typeRef); @@ -1737,10 +1738,11 @@ enum BfUnaryOp BfUnaryOp_Decrement, BfUnaryOp_PostIncrement, BfUnaryOp_PostDecrement, + BfUnaryOp_NullConditional, BfUnaryOp_Ref, BfUnaryOp_Out, BfUnaryOp_Mut, - BfUnaryOp_Params, + BfUnaryOp_Params, }; class BfTokenNode : public BfAstNode @@ -2317,15 +2319,15 @@ public: ASTREF(BfTypeReference*) mElementType; }; BF_AST_DECL(BfElementedTypeRef, BfTypeReference); -class BfRetTypeTypeRef : public BfElementedTypeRef +class BfModifiedTypeRef : public BfElementedTypeRef { public: - BF_AST_TYPE(BfRetTypeTypeRef, BfElementedTypeRef); + BF_AST_TYPE(BfModifiedTypeRef, BfElementedTypeRef); BfTokenNode* mRetTypeToken; BfTokenNode* mOpenParen; BfTokenNode* mCloseParen; -}; BF_AST_DECL(BfRetTypeTypeRef, BfElementedTypeRef); +}; BF_AST_DECL(BfModifiedTypeRef, BfElementedTypeRef); class BfArrayTypeRef : public BfElementedTypeRef { @@ -3195,6 +3197,7 @@ BfBinaryOp BfGetFlippedBinaryOp(BfBinaryOp origOp); int BfGetBinaryOpPrecendence(BfBinaryOp binOp); const char* BfGetOpName(BfBinaryOp binOp); const char* BfGetOpName(BfUnaryOp unaryOp); +bool BfCanOverloadOperator(BfUnaryOp unaryOp); BfBinaryOp BfTokenToBinaryOp(BfToken token); BfUnaryOp BfTokenToUnaryOp(BfToken token); BfAssignmentOp BfTokenToAssignmentOp(BfToken token); diff --git a/IDEHelper/Compiler/BfAutoComplete.cpp b/IDEHelper/Compiler/BfAutoComplete.cpp index 0caaa01e..158853ae 100644 --- a/IDEHelper/Compiler/BfAutoComplete.cpp +++ b/IDEHelper/Compiler/BfAutoComplete.cpp @@ -1511,6 +1511,16 @@ bool BfAutoComplete::CheckMemberReference(BfAstNode* target, BfAstNode* dotToken { if (dotTokenNode->GetToken() == BfToken_QuestionDot) { + if (!targetValue.mType->IsNullable()) + { + // We need this for Result + SetAndRestoreValue prevIgnore(mModule->mBfIRBuilder->mIgnoreWrites, true); + BfExprEvaluator exprEvaluator(mModule); + auto opResult = exprEvaluator.PerformUnaryOperation_TryOperator(targetValue, NULL, BfUnaryOp_NullConditional, dotTokenNode); + if (opResult) + targetValue = opResult; + } + // ?. should look inside nullable types if (targetValue.mType->IsNullable()) { @@ -1518,7 +1528,7 @@ bool BfAutoComplete::CheckMemberReference(BfAstNode* target, BfAstNode* dotToken targetValue = mModule->MakeAddressable(targetValue); BfIRValue valuePtr = mModule->mBfIRBuilder->CreateInBoundsGEP(targetValue.mValue, 0, 1); // mValue targetValue = BfTypedValue(valuePtr, nullableType->mTypeGenericArguments[0], true); - } + } } } diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index 5770167d..a0a45bdc 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -4150,7 +4150,7 @@ void BfCompiler::AddToRebuildTypeList(BfTypeInstance* typeInst, HashSetIsGenericTypeInstance()) || ((typeInst->IsUnspecializedType()) && (!typeInst->IsUnspecializedTypeVariation()))); - if ((typeInst->IsClosure()) || (typeInst->IsConcreteInterfaceType()) || (typeInst->IsRetTypeType())) + if ((typeInst->IsClosure()) || (typeInst->IsConcreteInterfaceType()) || (typeInst->IsModifiedTypeType())) allowRebuild = false; if (allowRebuild) rebuildTypeInstList.Add(typeInst); diff --git a/IDEHelper/Compiler/BfContext.h b/IDEHelper/Compiler/BfContext.h index ea558fdf..aca7b2fb 100644 --- a/IDEHelper/Compiler/BfContext.h +++ b/IDEHelper/Compiler/BfContext.h @@ -370,7 +370,7 @@ public: BfAllocPool mBoxedTypePool; BfAllocPool mTupleTypePool; BfAllocPool mRefTypePool; - BfAllocPool mRetTypeTypePool; + BfAllocPool mRetTypeTypePool; BfAllocPool mGenericTypeInstancePool; BfAllocPool mGenericTypeAliasPool; BfAllocPool mArrayTypeInstancePool; diff --git a/IDEHelper/Compiler/BfElementVisitor.cpp b/IDEHelper/Compiler/BfElementVisitor.cpp index 43be35fd..43ea9023 100644 --- a/IDEHelper/Compiler/BfElementVisitor.cpp +++ b/IDEHelper/Compiler/BfElementVisitor.cpp @@ -325,7 +325,7 @@ void BfElementVisitor::Visit(BfRefTypeRef* typeRef) VisitChild(typeRef->mElementType); } -void BfElementVisitor::Visit(BfRetTypeTypeRef * typeRef) +void BfElementVisitor::Visit(BfModifiedTypeRef * typeRef) { Visit((BfTypeReference*)typeRef); // Skip the Elemented part so we can put the element in the right spot diff --git a/IDEHelper/Compiler/BfElementVisitor.h b/IDEHelper/Compiler/BfElementVisitor.h index c7c9d74d..4a92faf3 100644 --- a/IDEHelper/Compiler/BfElementVisitor.h +++ b/IDEHelper/Compiler/BfElementVisitor.h @@ -54,7 +54,7 @@ public: virtual void Visit(BfConstTypeRef* typeRef); virtual void Visit(BfConstExprTypeRef* typeRef); virtual void Visit(BfRefTypeRef* typeRef); - virtual void Visit(BfRetTypeTypeRef* typeRef); + virtual void Visit(BfModifiedTypeRef* typeRef); virtual void Visit(BfArrayTypeRef* typeRef); virtual void Visit(BfGenericInstanceTypeRef* typeRef); virtual void Visit(BfTupleTypeRef* typeRef); diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index a1c9e73a..678bc798 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -6695,7 +6695,7 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp delegateFailed = false; if (mModule->mCurMethodInstance->mIsUnspecialized) { - auto retTypeType = mModule->CreateRetTypeType(fieldVal.mType); + auto retTypeType = mModule->CreateModifiedTypeType(fieldVal.mType, BfToken_RetType); return mModule->GetFakeTypedValue(retTypeType); } } @@ -15918,6 +15918,10 @@ BfTypedValue BfExprEvaluator::SetupNullConditional(BfTypedValue thisValue, BfTok mModule->Fail("Null conditional reference not valid for static field references", dotToken); return thisValue; } + + auto opResult = PerformUnaryOperation_TryOperator(thisValue, NULL, BfUnaryOp_NullConditional, dotToken); + if (opResult) + thisValue = opResult; //TODO: But make null conditional work for Nullable types if (thisValue.mType->IsNullable()) @@ -15955,7 +15959,8 @@ BfTypedValue BfExprEvaluator::SetupNullConditional(BfTypedValue thisValue, BfTok mModule->AddBasicBlock(pendingNullCond->mCheckBB); } - BfIRValue isNotNull; + BfIRValue isNotNull; + if (thisValue.mType->IsNullable()) { BfGenericTypeInstance* nullableType = (BfGenericTypeInstance*)thisValue.mType->ToTypeInstance(); @@ -16645,6 +16650,123 @@ void BfExprEvaluator::PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp BfExprEvaluator::PerformUnaryOperation_OnResult(unaryOpExpr, unaryOp, opToken); } +BfTypedValue BfExprEvaluator::PerformUnaryOperation_TryOperator(const BfTypedValue& inValue, BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken) +{ + if ((!inValue.mType->IsTypeInstance()) && (!inValue.mType->IsGenericParam())) + return BfTypedValue(); + + SizedArray args; + BfResolvedArg resolvedArg; + resolvedArg.mTypedValue = inValue; + args.push_back(resolvedArg); + BfMethodMatcher methodMatcher(opToken, mModule, "", args, NULL); + BfBaseClassWalker baseClassWalker(inValue.mType, NULL, mModule); + + BfUnaryOp findOp = unaryOp; + bool isPostOp = false; + + if (findOp == BfUnaryOp_PostIncrement) + { + findOp = BfUnaryOp_Increment; + isPostOp = true; + } + + if (findOp == BfUnaryOp_PostDecrement) + { + findOp = BfUnaryOp_Decrement; + isPostOp = true; + } + + BfType* bestSelfType = NULL; + while (true) + { + auto entry = baseClassWalker.Next(); + auto checkType = entry.mTypeInstance; + if (checkType == NULL) + break; + for (auto operatorDef : checkType->mTypeDef->mOperators) + { + if (operatorDef->mOperatorDeclaration->mUnaryOp == findOp) + { + if (!methodMatcher.IsMemberAccessible(checkType, operatorDef->mDeclaringType)) + continue; + if (methodMatcher.CheckMethod(NULL, checkType, operatorDef, false)) + methodMatcher.mSelfType = entry.mSrcType; + } + } + } + + if (methodMatcher.mBestMethodDef == NULL) + { + // Check method generic constraints + if ((mModule->mCurMethodInstance != NULL) && (mModule->mCurMethodInstance->mIsUnspecialized) && (mModule->mCurMethodInstance->mMethodInfoEx != NULL)) + { + for (int genericParamIdx = 0; genericParamIdx < mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++) + { + auto genericParam = mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams[genericParamIdx]; + for (auto& opConstraint : genericParam->mOperatorConstraints) + { + if (opConstraint.mUnaryOp == findOp) + { + if (mModule->CanCast(args[0].mTypedValue, opConstraint.mRightType)) + { + return BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), genericParam->mExternType); + } + } + } + } + } + + // Check type generic constraints + if ((mModule->mCurTypeInstance->IsGenericTypeInstance()) && (mModule->mCurTypeInstance->IsUnspecializedType())) + { + auto genericTypeInst = (BfGenericTypeInstance*)mModule->mCurTypeInstance; + for (int genericParamIdx = 0; genericParamIdx < genericTypeInst->mGenericParams.size(); genericParamIdx++) + { + auto genericParam = mModule->GetGenericTypeParamInstance(genericParamIdx); + for (auto& opConstraint : genericParam->mOperatorConstraints) + { + if (opConstraint.mUnaryOp == findOp) + { + if (mModule->CanCast(args[0].mTypedValue, opConstraint.mRightType)) + { + return BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), genericParam->mExternType); + } + } + } + } + } + + return BfTypedValue(); + } + + if (!baseClassWalker.mMayBeFromInterface) + mModule->SetElementType(opToken, BfSourceElementType_Method); + + auto methodDef = methodMatcher.mBestMethodDef; + auto autoComplete = GetAutoComplete(); + if ((autoComplete != NULL) && (autoComplete->IsAutocompleteNode(opToken))) + { + auto operatorDecl = BfNodeDynCast(methodDef->mMethodDeclaration); + if ((operatorDecl != NULL) && (operatorDecl->mOpTypeToken != NULL)) + autoComplete->SetDefinitionLocation(operatorDecl->mOpTypeToken); + } + + SizedArray argSrcs; + argSrcs.push_back(unaryOpExpr); + auto result = CreateCall(&methodMatcher, BfTypedValue()); + + if ((result.mType != NULL) && (methodMatcher.mSelfType != NULL) && (result.mType->IsSelf())) + { + BF_ASSERT(mModule->IsInGeneric()); + result = mModule->GetDefaultTypedValue(methodMatcher.mSelfType); + } + + if (isPostOp) + result = args[0].mTypedValue; + return result; +} + void BfExprEvaluator::PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken) { BfAstNode* propSrc = mPropSrc; @@ -16666,118 +16788,14 @@ void BfExprEvaluator::PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, return; } - if ((mResult.mType->IsTypeInstance()) || (mResult.mType->IsGenericParam())) + if (BfCanOverloadOperator(unaryOp)) { - SizedArray args; - BfResolvedArg resolvedArg; - resolvedArg.mTypedValue = mResult; - args.push_back(resolvedArg); - BfMethodMatcher methodMatcher(opToken, mModule, "", args, NULL); - BfBaseClassWalker baseClassWalker(mResult.mType, NULL, mModule); - - BfUnaryOp findOp = unaryOp; - bool isPostOp = false; - - if (findOp == BfUnaryOp_PostIncrement) + auto opResult = PerformUnaryOperation_TryOperator(mResult, unaryOpExpr, unaryOp, opToken); + if (opResult) { - findOp = BfUnaryOp_Increment; - isPostOp = true; - } - - if (findOp == BfUnaryOp_PostDecrement) - { - findOp = BfUnaryOp_Decrement; - isPostOp = true; - } - - BfType* bestSelfType = NULL; - while (true) - { - auto entry = baseClassWalker.Next(); - auto checkType = entry.mTypeInstance; - if (checkType == NULL) - break; - for (auto operatorDef : checkType->mTypeDef->mOperators) - { - if (operatorDef->mOperatorDeclaration->mUnaryOp == findOp) - { - if (!methodMatcher.IsMemberAccessible(checkType, operatorDef->mDeclaringType)) - continue; - if (methodMatcher.CheckMethod(NULL, checkType, operatorDef, false)) - methodMatcher.mSelfType = entry.mSrcType; - } - } - } - - if (methodMatcher.mBestMethodDef != NULL) - { - if (!baseClassWalker.mMayBeFromInterface) - mModule->SetElementType(opToken, BfSourceElementType_Method); - - auto methodDef = methodMatcher.mBestMethodDef; - auto autoComplete = GetAutoComplete(); - if ((autoComplete != NULL) && (autoComplete->IsAutocompleteNode(opToken))) - { - auto operatorDecl = BfNodeDynCast(methodDef->mMethodDeclaration); - if ((operatorDecl != NULL) && (operatorDecl->mOpTypeToken != NULL)) - autoComplete->SetDefinitionLocation(operatorDecl->mOpTypeToken); - } - - SizedArray argSrcs; - argSrcs.push_back(unaryOpExpr); - mResult = CreateCall(&methodMatcher, BfTypedValue()); - - if ((mResult.mType != NULL) && (methodMatcher.mSelfType != NULL) && (mResult.mType->IsSelf())) - { - BF_ASSERT(mModule->IsInGeneric()); - mResult = mModule->GetDefaultTypedValue(methodMatcher.mSelfType); - } - - if (isPostOp) - mResult = args[0].mTypedValue; + mResult = opResult; return; } - - // Check method generic constraints - if ((mModule->mCurMethodInstance != NULL) && (mModule->mCurMethodInstance->mIsUnspecialized) && (mModule->mCurMethodInstance->mMethodInfoEx != NULL)) - { - for (int genericParamIdx = 0; genericParamIdx < mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++) - { - auto genericParam = mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams[genericParamIdx]; - for (auto& opConstraint : genericParam->mOperatorConstraints) - { - if (opConstraint.mUnaryOp == findOp) - { - if (mModule->CanCast(args[0].mTypedValue, opConstraint.mRightType)) - { - mResult = BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), genericParam->mExternType); - return; - } - } - } - } - } - - // Check type generic constraints - if ((mModule->mCurTypeInstance->IsGenericTypeInstance()) && (mModule->mCurTypeInstance->IsUnspecializedType())) - { - auto genericTypeInst = (BfGenericTypeInstance*)mModule->mCurTypeInstance; - for (int genericParamIdx = 0; genericParamIdx < genericTypeInst->mGenericParams.size(); genericParamIdx++) - { - auto genericParam = mModule->GetGenericTypeParamInstance(genericParamIdx); - for (auto& opConstraint : genericParam->mOperatorConstraints) - { - if (opConstraint.mUnaryOp == findOp) - { - if (mModule->CanCast(args[0].mTypedValue, opConstraint.mRightType)) - { - mResult = BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), genericParam->mExternType); - return; - } - } - } - } - } } bool numericFail = false; diff --git a/IDEHelper/Compiler/BfExprEvaluator.h b/IDEHelper/Compiler/BfExprEvaluator.h index 6493db14..1c154d22 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.h +++ b/IDEHelper/Compiler/BfExprEvaluator.h @@ -389,6 +389,7 @@ public: void VisitLambdaBodies(BfAstNode* body, BfFieldDtorDeclaration* fieldDtor); void FixitAddMember(BfTypeInstance* typeInst, BfType* fieldType, const StringImpl& fieldName, bool isStatic); void PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken); + BfTypedValue PerformUnaryOperation_TryOperator(const BfTypedValue& inValue, BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken); void PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, BfUnaryOp unaryOp, BfTokenNode* opToken); void PerformAssignment(BfAssignmentExpression* assignExpr, bool evaluatedLeft, BfTypedValue rightValue, BfTypedValue* outCascadeValue = NULL); void PopulateDeferrredTupleAssignData(BfTupleExpression* tupleExr, DeferredTupleAssignData& deferredTupleAssignData); diff --git a/IDEHelper/Compiler/BfIRBuilder.cpp b/IDEHelper/Compiler/BfIRBuilder.cpp index c18cfea7..490a6b76 100644 --- a/IDEHelper/Compiler/BfIRBuilder.cpp +++ b/IDEHelper/Compiler/BfIRBuilder.cpp @@ -2050,7 +2050,7 @@ void BfIRBuilder::CreateTypeDeclaration(BfType* type, bool forceDbgDefine) trackDIType = true; } } - else if ((type->IsGenericParam()) || (type->IsRetTypeType())) + else if ((type->IsGenericParam()) || (type->IsModifiedTypeType())) { //mModule->PopulateType(mModule->mContext->mBfObjectType, BfPopulateType_Declaration); irType = MapType(mModule->mContext->mBfObjectType); diff --git a/IDEHelper/Compiler/BfMangler.cpp b/IDEHelper/Compiler/BfMangler.cpp index 984b2141..92ea4e0b 100644 --- a/IDEHelper/Compiler/BfMangler.cpp +++ b/IDEHelper/Compiler/BfMangler.cpp @@ -539,10 +539,15 @@ void BfGNUMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfType AddPrefix(mangleContext, name, startIdx, "R"); return; } - else if (type->IsRetTypeType()) + else if (type->IsModifiedTypeType()) { - BfRetTypeType* retTypeType = (BfRetTypeType*)type; - name += "U7rettype"; + BfModifiedTypeType* retTypeType = (BfModifiedTypeType*)type; + if (retTypeType->mModifiedKind == BfToken_RetType) + name += "U7rettype"; + else if (retTypeType->mModifiedKind == BfToken_Nullable) + name += "U8nullable"; + else + BF_FATAL("Unhandled"); Mangle(mangleContext, name, retTypeType->mElementType); return; } @@ -1573,10 +1578,15 @@ void BfMSMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfType* name += "out$"; Mangle(mangleContext, name, refType->mElementType); } - else if (type->IsRetTypeType()) + else if (type->IsModifiedTypeType()) { - auto retType = (BfRetTypeType*)type; - name += "rettype$"; + auto retType = (BfModifiedTypeType*)type; + if (retType->mModifiedKind == BfToken_RetType) + name += "rettype$"; + else if (retType->mModifiedKind == BfToken_Nullable) + name += "nullable$"; + else + BF_FATAL("Unhandled"); Mangle(mangleContext, name, retType->mElementType); } else if (type->IsConcreteInterfaceType()) diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index 863f73f1..87837607 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -1352,7 +1352,7 @@ BfIRValue BfModule::GetDefaultValue(BfType* type) } if (type->IsPointer() || type->IsObjectOrInterface() || type->IsGenericParam() || type->IsVar() || type->IsRef() || type->IsNull() || - type->IsRetTypeType() || type->IsConcreteInterfaceType()) + type->IsModifiedTypeType() || type->IsConcreteInterfaceType()) return mBfIRBuilder->CreateConstNull(mBfIRBuilder->MapType(type)); if ((type->IsIntegral()) || (type->IsBoolean())) { diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index 04301cdf..d6571f2d 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -1628,7 +1628,7 @@ public: BfTupleType* CreateTupleType(const BfTypeVector& fieldTypes, const Array& fieldNames); BfTupleType* SantizeTupleType(BfTupleType* tupleType); BfRefType* CreateRefType(BfType* resolvedTypeRef, BfRefType::RefKind refKind = BfRefType::RefKind_Ref); - BfRetTypeType* CreateRetTypeType(BfType* resolvedTypeRef); + BfModifiedTypeType* CreateModifiedTypeType(BfType* resolvedTypeRef, BfToken modifiedKind); BfConcreteInterfaceType* CreateConcreteInterfaceType(BfTypeInstance* interfaceType); BfTypeInstance* GetWrappedStructType(BfType* type, bool allowSpecialized = true); BfTypeInstance* GetPrimitiveStructType(BfTypeCode typeCode); diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index bc43f103..82c1f3c7 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -1023,10 +1023,10 @@ bool BfModule::PopulateType(BfType* resolvedTypeRef, BfPopulateType populateType return true; } - if (resolvedTypeRef->IsRetTypeType()) + if (resolvedTypeRef->IsModifiedTypeType()) { - BfRetTypeType* retTypeType = (BfRetTypeType*)resolvedTypeRef; - BF_ASSERT(retTypeType->mElementType->IsGenericParam()); + BfModifiedTypeType* retTypeType = (BfModifiedTypeType*)resolvedTypeRef; + BF_ASSERT(retTypeType->mElementType->IsGenericParam()); resolvedTypeRef->mSize = mContext->mBfObjectType->mSize; resolvedTypeRef->mAlign = mContext->mBfObjectType->mAlign; resolvedTypeRef->mDefineState = BfTypeDefineState_Defined; @@ -4967,15 +4967,16 @@ BfRefType* BfModule::CreateRefType(BfType* resolvedTypeRef, BfRefType::RefKind r return (BfRefType*)resolvedRefType; } -BfRetTypeType* BfModule::CreateRetTypeType(BfType* resolvedTypeRef) +BfModifiedTypeType* BfModule::CreateModifiedTypeType(BfType* resolvedTypeRef, BfToken modifiedKind) { auto retTypeType = mContext->mRetTypeTypePool.Get(); retTypeType->mContext = mContext; + retTypeType->mModifiedKind = modifiedKind; retTypeType->mElementType = resolvedTypeRef; auto resolvedRetTypeType = ResolveType(retTypeType); if (resolvedRetTypeType != retTypeType) mContext->mRetTypeTypePool.GiveBack(retTypeType); - return (BfRetTypeType*)resolvedRetTypeType; + return (BfModifiedTypeType*)resolvedRetTypeType; } BfConcreteInterfaceType* BfModule::CreateConcreteInterfaceType(BfTypeInstance* interfaceType) @@ -7147,66 +7148,98 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula return ResolveTypeResult(typeRef, resolvedTypeRef->mType, populateType, resolveFlags); } - if (auto retTypeTypeRef = BfNodeDynCastExact(typeRef)) - { - bool allowThrough = false; - BfType* resolvedType = NULL; - if (retTypeTypeRef->mElementType != NULL) + if (auto retTypeTypeRef = BfNodeDynCastExact(typeRef)) + { + if (retTypeTypeRef->mRetTypeToken->mToken == BfToken_RetType) { - auto innerType = ResolveTypeRef(retTypeTypeRef->mElementType, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowGenericParamConstValue); - if (innerType != NULL) + bool allowThrough = false; + BfType* resolvedType = NULL; + if (retTypeTypeRef->mElementType != NULL) { - if ((innerType->IsDelegate()) || (innerType->IsFunction())) + auto innerType = ResolveTypeRef(retTypeTypeRef->mElementType, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowGenericParamConstValue); + if (innerType != NULL) { - PopulateType(innerType, BfPopulateType_DataAndMethods); - BfMethodInstance* invokeMethodInstance = GetRawMethodInstanceAtIdx(innerType->ToTypeInstance(), 0, "Invoke"); - if (invokeMethodInstance != NULL) + if ((innerType->IsDelegate()) || (innerType->IsFunction())) { - resolvedType = invokeMethodInstance->mReturnType; - return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags); - } - } - else if (innerType->IsGenericParam()) - { - if ((mCurTypeInstance != NULL) && (mCurTypeInstance->IsUnspecializedTypeVariation())) - { - // We could have case where we have "rettype(@T0)" and @T0 gets a type variation of @M0, but we can't do a - // GetGenericParamInstance on that - allowThrough = true; - } - else - { - auto genericParamInstance = GetGenericParamInstance((BfGenericParamType*)innerType); - if (genericParamInstance->mTypeConstraint != NULL) + PopulateType(innerType, BfPopulateType_DataAndMethods); + BfMethodInstance* invokeMethodInstance = GetRawMethodInstanceAtIdx(innerType->ToTypeInstance(), 0, "Invoke"); + if (invokeMethodInstance != NULL) { - if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction())) + resolvedType = invokeMethodInstance->mReturnType; + return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags); + } + } + else if (innerType->IsGenericParam()) + { + if ((mCurTypeInstance != NULL) && (mCurTypeInstance->IsUnspecializedTypeVariation())) + { + // We could have case where we have "rettype(@T0)" and @T0 gets a type variation of @M0, but we can't do a + // GetGenericParamInstance on that + allowThrough = true; + } + else + { + auto genericParamInstance = GetGenericParamInstance((BfGenericParamType*)innerType); + if (genericParamInstance->mTypeConstraint != NULL) { - resolvedType = GetDelegateReturnType(genericParamInstance->mTypeConstraint); - return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags); - } - else if ((genericParamInstance->mTypeConstraint->IsTypeInstance()) && - ((genericParamInstance->mTypeConstraint->ToTypeInstance()->mTypeDef == mCompiler->mDelegateTypeDef) || - (genericParamInstance->mTypeConstraint->ToTypeInstance()->mTypeDef == mCompiler->mFunctionTypeDef))) - { - allowThrough = true; + if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction())) + { + resolvedType = GetDelegateReturnType(genericParamInstance->mTypeConstraint); + return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags); + } + else if ((genericParamInstance->mTypeConstraint->IsTypeInstance()) && + ((genericParamInstance->mTypeConstraint->ToTypeInstance()->mTypeDef == mCompiler->mDelegateTypeDef) || + (genericParamInstance->mTypeConstraint->ToTypeInstance()->mTypeDef == mCompiler->mFunctionTypeDef))) + { + allowThrough = true; + } } } } - } - else if (innerType->IsMethodRef()) - { - auto methodRefType = (BfMethodRefType*)innerType; - resolvedType = methodRefType->mMethodRef->mReturnType; - return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags); + else if (innerType->IsMethodRef()) + { + auto methodRefType = (BfMethodRefType*)innerType; + resolvedType = methodRefType->mMethodRef->mReturnType; + return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags); + } } } - } - if (!allowThrough) - { - Fail("'rettype' can only be used on delegate or function types", retTypeTypeRef->mRetTypeToken); - return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags); + if (!allowThrough) + { + Fail("'rettype' can only be used on delegate or function types", retTypeTypeRef->mRetTypeToken); + return ResolveTypeResult(typeRef, resolvedType, populateType, resolveFlags); + } } + else if (retTypeTypeRef->mRetTypeToken->mToken == BfToken_Nullable) + { + bool allowThrough = false; + BfType* resolvedType = NULL; + if (retTypeTypeRef->mElementType != NULL) + { + resolvedType = ResolveTypeRef(retTypeTypeRef->mElementType, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowGenericParamConstValue); + } + + if ((resolvedType != NULL) && (resolvedType->IsGenericParam())) + { + //resolvedType = CreateModifiedTypeType(resolvedType, BfToken_Nullable); + BfTypeVector typeVec; + typeVec.push_back(resolvedType); + resolvedType = ResolveTypeDef(mCompiler->mNullableTypeDef, typeVec, BfPopulateType_Declaration); + } + else if ((resolvedType != NULL) && (resolvedType->IsValueType())) + { + BfTypeVector typeVec; + typeVec.push_back(resolvedType); + resolvedType = ResolveTypeDef(mCompiler->mNullableTypeDef, typeVec, BfPopulateType_Declaration); + } + if (resolvedType != NULL) + PopulateType(resolvedType, populateType); + + return resolvedType; + } + else + BF_FATAL("Unhandled"); } if (auto refTypeRef = BfNodeDynCastExact(typeRef)) @@ -7799,9 +7832,10 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula BF_ASSERT(BfResolvedTypeSet::Hash(genericParamType, &lookupCtx) == resolvedEntry->mHash); return ResolveTypeResult(typeRef, genericParamType, populateType, resolveFlags); } - else if (auto retTypeTypeRef = BfNodeDynCast(typeRef)) + else if (auto retTypeTypeRef = BfNodeDynCast(typeRef)) { - auto retTypeType = new BfRetTypeType(); + auto retTypeType = new BfModifiedTypeType(); + retTypeType->mModifiedKind = retTypeTypeRef->mRetTypeToken->mToken; retTypeType->mElementType = ResolveTypeRef(retTypeTypeRef->mElementType, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowGenericParamConstValue); // We know this is a generic param type, it can't fail to resolve BF_ASSERT(retTypeType->mElementType); @@ -8467,7 +8501,7 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp ((toType->IsInterface()) || (toType == mContext->mBfObjectType))) { // Make sure there's no conversion operator before we box - if ((!typedVal.mType->IsRef()) && (!typedVal.mType->IsRetTypeType())) + if ((!typedVal.mType->IsRef()) && (!typedVal.mType->IsModifiedTypeType())) mayBeBox = true; } @@ -10060,7 +10094,7 @@ void BfModule::DoTypeToString(StringImpl& str, BfType* resolvedType, BfTypeNameF str += "]"; return; } - else if ((resolvedType->IsNullable()) && (!resolvedType->IsUnspecializedType())) + else if (resolvedType->IsNullable()) { auto genericType = (BfGenericTypeInstance*)resolvedType; auto elementType = genericType->mTypeGenericArguments[0]; @@ -10508,10 +10542,11 @@ void BfModule::DoTypeToString(StringImpl& str, BfType* resolvedType, BfTypeNameF return; } } - else if (resolvedType->IsRetTypeType()) + else if (resolvedType->IsModifiedTypeType()) { - auto retTypeType = (BfRetTypeType*)resolvedType; - str += "rettype("; + auto retTypeType = (BfModifiedTypeType*)resolvedType; + str += BfTokenToString(retTypeType->mModifiedKind); + str += "("; DoTypeToString(str, retTypeType->mElementType, typeNameFlags, genericMethodNameOverrides); str += ")"; return; diff --git a/IDEHelper/Compiler/BfParser.cpp b/IDEHelper/Compiler/BfParser.cpp index 8b21aab1..f5bdcca7 100644 --- a/IDEHelper/Compiler/BfParser.cpp +++ b/IDEHelper/Compiler/BfParser.cpp @@ -2781,9 +2781,11 @@ void BfParser::NextToken(int endIdx) if (SrcPtrHasToken("new")) mToken = BfToken_New; break; - case TOKEN_HASH('n', 'u', 'l', 'l'): + case TOKEN_HASH('n', 'u', 'l', 'l'): if (SrcPtrHasToken("null")) mToken = BfToken_Null; + else if (SrcPtrHasToken("nullable")) + mToken = BfToken_Nullable; break; case TOKEN_HASH('o', 'p', 'e', 'r'): if (SrcPtrHasToken("operator")) diff --git a/IDEHelper/Compiler/BfReducer.cpp b/IDEHelper/Compiler/BfReducer.cpp index 1ba57491..00ab32d4 100644 --- a/IDEHelper/Compiler/BfReducer.cpp +++ b/IDEHelper/Compiler/BfReducer.cpp @@ -267,7 +267,7 @@ bool BfReducer::IsTypeReference(BfAstNode* checkNode, BfToken successToken, int* { // Tuple start } - else if ((checkToken == BfToken_Decltype) || (checkToken == BfToken_RetType)) + else if ((checkToken == BfToken_Decltype) || (checkToken == BfToken_RetType) || (checkToken == BfToken_Nullable)) { // Decltype start } @@ -760,7 +760,7 @@ bool BfReducer::IsTypeReference(BfAstNode* checkNode, BfToken successToken, int* checkIdx = funcEndNode; continue; } - else if ((checkToken == BfToken_Decltype) || (checkToken == BfToken_RetType)) + else if ((checkToken == BfToken_Decltype) || (checkToken == BfToken_RetType) || (checkToken == BfToken_Nullable)) { int endNodeIdx = checkIdx + 1; @@ -4168,6 +4168,7 @@ bool BfReducer::IsTerminatingExpression(BfAstNode* node) case BfToken_Scope: case BfToken_New: case BfToken_RetType: + case BfToken_Nullable: case BfToken_SizeOf: case BfToken_This: case BfToken_TypeOf: @@ -4572,9 +4573,9 @@ BfTypeReference* BfReducer::DoCreateTypeRef(BfAstNode* firstNode, CreateTypeRefF return elementType; } } - else if (token == BfToken_RetType) + else if ((token == BfToken_RetType) || (token == BfToken_Nullable)) { - auto retTypeTypeRef = mAlloc->Alloc(); + auto retTypeTypeRef = mAlloc->Alloc(); ReplaceNode(firstNode, retTypeTypeRef); MEMBER_SET(retTypeTypeRef, mRetTypeToken, tokenNode); @@ -6269,6 +6270,7 @@ BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, int depth) else if ((token == BfToken_Var) || (token == BfToken_Let) || (token == BfToken_RetType) || + (token == BfToken_Nullable) || (token == BfToken_Decltype) || (token == BfToken_LParen)) { diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp index d62f1adb..7ca5f766 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp @@ -2199,9 +2199,9 @@ int BfResolvedTypeSet::Hash(BfType* type, LookupContext* ctx, bool allowRef) int elemHash = Hash(refType->mElementType, ctx) ^ (HASH_VAL_REF + (int)refType->mRefKind); return (elemHash << 5) - elemHash; } - else if (type->IsRetTypeType()) + else if (type->IsModifiedTypeType()) { - auto retTypeType = (BfRetTypeType*)type; + auto retTypeType = (BfModifiedTypeType*)type; int elemHash = Hash(retTypeType->mElementType, ctx) ^ HASH_RETTYPE; return (elemHash << 5) - elemHash; } @@ -2621,7 +2621,7 @@ int BfResolvedTypeSet::Hash(BfTypeReference* typeRef, LookupContext* ctx, BfHash auto primType = ctx->mModule->GetPrimitiveType(BfTypeCode_Let); return Hash(primType, ctx); } - else if (auto retTypeTypeRef = BfNodeDynCastExact(typeRef)) + else if (auto retTypeTypeRef = BfNodeDynCastExact(typeRef)) { // Don't cause infinite loop, but if we have an inner 'rettype' then try to directly resolve that -- // Only use the HAS_RETTYPE for root-level rettype insertions @@ -2882,13 +2882,14 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfType* rhs, LookupContext* ctx) BfRefType* rhsRefType = (BfRefType*)rhs; return (lhsRefType->mElementType == rhsRefType->mElementType) && (lhsRefType->mRefKind == rhsRefType->mRefKind); } - else if (lhs->IsRetTypeType()) + else if (lhs->IsModifiedTypeType()) { - if (!rhs->IsRetTypeType()) + if (!rhs->IsModifiedTypeType()) return false; - BfRetTypeType* lhsRetTypeType = (BfRetTypeType*)lhs; - BfRetTypeType* rhsRetTypeType = (BfRetTypeType*)rhs; - return (lhsRetTypeType->mElementType == rhsRetTypeType->mElementType); + BfModifiedTypeType* lhsRetTypeType = (BfModifiedTypeType*)lhs; + BfModifiedTypeType* rhsRetTypeType = (BfModifiedTypeType*)rhs; + return (lhsRetTypeType->mModifiedKind == rhsRetTypeType->mModifiedKind) && + (lhsRetTypeType->mElementType == rhsRetTypeType->mElementType); } else if (lhs->IsConcreteInterfaceType()) { @@ -3093,7 +3094,7 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfTypeReference* rhs, LookupContext* if (ctx->mRootTypeRef != rhs) { - if (auto retTypeRef = BfNodeDynCastExact(rhs)) + if (auto retTypeRef = BfNodeDynCastExact(rhs)) { auto resolvedType = ctx->mModule->ResolveTypeRef(rhs); return lhs == resolvedType; @@ -3341,12 +3342,14 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfTypeReference* rhs, LookupContext* return (lhsRefType->mRefKind == refKind) && Equals(lhsRefType->mElementType, rhsRefTypeRef->mElementType, ctx); } - else if (lhs->IsRetTypeType()) + else if (lhs->IsModifiedTypeType()) { - auto lhsRetTypeType = (BfRetTypeType*)lhs; - auto rhsRetTypeTypeRef = BfNodeDynCastExact(rhs); + auto lhsRetTypeType = (BfModifiedTypeType*)lhs; + auto rhsRetTypeTypeRef = BfNodeDynCastExact(rhs); if (rhsRetTypeTypeRef == NULL) return false; + if (lhsRetTypeType->mModifiedKind != rhsRetTypeTypeRef->mRetTypeToken->mToken) + return false; return Equals(lhsRetTypeType->mElementType, rhsRetTypeTypeRef->mElementType, ctx); } else if (lhs->IsConcreteInterfaceType()) diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.h b/IDEHelper/Compiler/BfResolvedTypeUtils.h index 369713d5..799c6b1f 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.h +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.h @@ -489,7 +489,7 @@ public: virtual bool IsTuple() { return false; } virtual bool IsOnDemand() { return false; } virtual bool IsTemporary() { return false; } - virtual bool IsRetTypeType() { return false; } + virtual bool IsModifiedTypeType() { return false; } virtual bool IsConcreteInterfaceType() { return false; } virtual bool IsTypeAlias() { return false; } virtual bool HasPackingHoles() { return false; } @@ -921,13 +921,14 @@ public: virtual bool IsReified() override { return false; } }; -// This just captures rettype(T) since it can't be resolved directly -class BfRetTypeType : public BfType +// This just captures rettype(T)/nullable(T) since it can't be resolved directly +class BfModifiedTypeType : public BfType { public: + BfToken mModifiedKind; BfType* mElementType; - virtual bool IsRetTypeType() override { return true; } + virtual bool IsModifiedTypeType() override { return true; } virtual bool CanBeValuelessType() override { return true; } virtual bool IsValuelessType() override { return true; }