1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-09 03:52:19 +02:00

Reworked 'using' - now properly supports nulls, requires IDisposable

This commit is contained in:
Brian Fiete 2020-02-17 05:39:05 -08:00
parent 7c44884cf0
commit 179e67194d
5 changed files with 55 additions and 18 deletions

View file

@ -400,6 +400,7 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly)
mGenericIRefEnumeratorTypeDef = NULL;
mInlineAttributeTypeDef = NULL;
mInternalTypeDef = NULL;
mIDisposableTypeDef = NULL;
mIPrintableTypeDef = NULL;
mIHashableTypeDef = NULL;
mLinkNameAttributeTypeDef = NULL;
@ -5908,6 +5909,7 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory)
mGenericIRefEnumeratorTypeDef = _GetRequiredType("System.Collections.Generic.IRefEnumerator");
mInlineAttributeTypeDef = _GetRequiredType("System.InlineAttribute");
mInternalTypeDef = _GetRequiredType("System.Internal");
mIDisposableTypeDef = _GetRequiredType("System.IDisposable");
mIPrintableTypeDef = _GetRequiredType("System.IPrintable");
mIHashableTypeDef = _GetRequiredType("System.IHashable");
mLinkNameAttributeTypeDef = _GetRequiredType("System.LinkNameAttribute");

View file

@ -351,6 +351,7 @@ public:
BfTypeDef* mGenericIRefEnumeratorTypeDef;
BfTypeDef* mInternalTypeDef;
BfTypeDef* mIDisposableTypeDef;
BfTypeDef* mIPrintableTypeDef;
BfTypeDef* mIHashableTypeDef;

View file

@ -4964,7 +4964,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu
}
if ((mModule->mAttributeState != NULL) && (mModule->mAttributeState->mCustomAttributes != NULL) && (mModule->mAttributeState->mCustomAttributes->Contains(mModule->mCompiler->mDisableObjectAccessChecksAttributeTypeDef)))
doAccessCheck = false;
if ((doAccessCheck) && (!isSkipCall))
if ((doAccessCheck) && (!isSkipCall) && (prevBindResult.mPrevVal == NULL))
mModule->EmitObjectAccessCheck(target);
}
@ -8292,6 +8292,19 @@ void BfExprEvaluator::Visit(BfDynamicCastExpression* dynCastExpr)
}
auto boolType = mModule->GetPrimitiveType(BfTypeCode_Boolean);
if ((targetValue.mType->IsNullable()) && (targetType->IsInterface()))
{
mResult = mModule->Cast(dynCastExpr, targetValue, targetType, BfCastFlags_SilentFail);
if (!mResult)
{
mModule->Warn(0, StrFormat("Conversion from '%s' to '%s' will always be null",
mModule->TypeToString(targetValue.mType).c_str(), mModule->TypeToString(targetType).c_str()), dynCastExpr->mAsToken);
mResult = BfTypedValue(mModule->GetDefaultValue(targetType), targetType);
}
return;
}
if (targetValue.mType->IsValueTypeOrValueTypePtr())
{
mModule->Warn(0, StrFormat("Type '%s' is not applicable for dynamic casting",

View file

@ -2688,16 +2688,13 @@ BfError* BfModule::Warn(int warningNum, const StringImpl& warning, BfAstNode* re
// We used to bubble up warnings into the mixin injection site, BUT
// we are now assuming any relevant warnings will be given at the declaration site
return NULL;
// AddFailType(mCurTypeInstance);
//
// BfError* bfError = mCompiler->mPassInstance->Warn(warningNum, "Warning while injecting mixin", mCurMethodState->mMixinState->GetRoot()->mSource);
// if (bfError == NULL)
// return NULL;
// mHadBuildWarning = true;
// return mCompiler->mPassInstance->MoreInfo(warning, refNode);
}
BfError* bfError = mCompiler->mPassInstance->WarnAt(warningNum, warning, refNode->GetSourceData(), refNode->GetSrcStart(), refNode->GetSrcLength());
BfError* bfError;
if (refNode != NULL)
bfError = mCompiler->mPassInstance->WarnAt(warningNum, warning, refNode->GetSourceData(), refNode->GetSrcStart(), refNode->GetSrcLength());
else
bfError = mCompiler->mPassInstance->Warn(warningNum, warning);
if (bfError != NULL)
{
bfError->mProject = mProject;

View file

@ -517,7 +517,7 @@ void BfModule::AddDeferredBlock(BfBlock* block, BfScopeData* scopeData, Array<Bf
deferredCallEntry->mDeferredBlock = block;
if (captures != NULL)
deferredCallEntry->mCaptures = *captures;
AddDeferredCallEntry(deferredCallEntry, scopeData); \
AddDeferredCallEntry(deferredCallEntry, scopeData);
}
BfDeferredCallEntry* BfModule::AddDeferredCall(const BfModuleMethodInstance& moduleMethodInstance, SizedArrayImpl<BfIRValue>& llvmArgs, BfScopeData* scopeData, BfAstNode* srcNode, bool bypassVirtual, bool doNullCheck)
@ -5015,13 +5015,37 @@ void BfModule::Visit(BfUsingStatement* usingStmt)
if (!failed)
{
auto iDisposableType = ResolveTypeDef(mCompiler->mIDisposableTypeDef)->ToTypeInstance();
auto dispMethod = GetMethodByName(iDisposableType, "Dispose");
if ((!dispMethod) || (!CanCast(embeddedValue, iDisposableType)))
{
Fail(StrFormat("Type '%s' must be implicitly convertible to 'System.IDisposable' for use in 'using' statement", TypeToString(embeddedValue.mType).c_str()), usingStmt->mVariableDeclaration);
failed = true;
}
else
{
bool mayBeNull = true;
if (embeddedValue.mType->IsStruct())
{
// It's possible that a struct can convert to an IDisposable through a conversion operator that CAN
// return null, so the only way we can know we are not null is if we are a struct that directly
// implements the interface
if (TypeIsSubTypeOf(embeddedValue.mType->ToTypeInstance(), iDisposableType))
mayBeNull = false;
}
exprEvaluator.mFunctionBindResult = &functionBindResult;
BfResolvedArgs resolvedArgs;
exprEvaluator.MatchMethod(usingStmt->mVariableDeclaration, NULL, embeddedValue, false, false, "Dispose", resolvedArgs, NULL);
SizedArray<BfResolvedArg, 0> resolvedArgs;
BfMethodMatcher methodMatcher(usingStmt->mVariableDeclaration, this, dispMethod.mMethodInstance, resolvedArgs);
methodMatcher.CheckType(iDisposableType, embeddedValue, false);
methodMatcher.TryDevirtualizeCall(embeddedValue);
auto retVal = exprEvaluator.CreateCall(&methodMatcher, embeddedValue);
if (functionBindResult.mMethodInstance != NULL)
{
moduleMethodInstance = BfModuleMethodInstance(functionBindResult.mMethodInstance, functionBindResult.mFunc);
AddDeferredCall(moduleMethodInstance, functionBindResult.mIRArgs, mCurMethodState->mCurScope);
AddDeferredCall(moduleMethodInstance, functionBindResult.mIRArgs, mCurMethodState->mCurScope, NULL, false, mayBeNull);
}
}
}