mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-09 20:12:21 +02:00
Reworked 'using' - now properly supports nulls, requires IDisposable
This commit is contained in:
parent
7c44884cf0
commit
179e67194d
5 changed files with 55 additions and 18 deletions
|
@ -400,6 +400,7 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly)
|
||||||
mGenericIRefEnumeratorTypeDef = NULL;
|
mGenericIRefEnumeratorTypeDef = NULL;
|
||||||
mInlineAttributeTypeDef = NULL;
|
mInlineAttributeTypeDef = NULL;
|
||||||
mInternalTypeDef = NULL;
|
mInternalTypeDef = NULL;
|
||||||
|
mIDisposableTypeDef = NULL;
|
||||||
mIPrintableTypeDef = NULL;
|
mIPrintableTypeDef = NULL;
|
||||||
mIHashableTypeDef = NULL;
|
mIHashableTypeDef = NULL;
|
||||||
mLinkNameAttributeTypeDef = NULL;
|
mLinkNameAttributeTypeDef = NULL;
|
||||||
|
@ -5908,6 +5909,7 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory)
|
||||||
mGenericIRefEnumeratorTypeDef = _GetRequiredType("System.Collections.Generic.IRefEnumerator");
|
mGenericIRefEnumeratorTypeDef = _GetRequiredType("System.Collections.Generic.IRefEnumerator");
|
||||||
mInlineAttributeTypeDef = _GetRequiredType("System.InlineAttribute");
|
mInlineAttributeTypeDef = _GetRequiredType("System.InlineAttribute");
|
||||||
mInternalTypeDef = _GetRequiredType("System.Internal");
|
mInternalTypeDef = _GetRequiredType("System.Internal");
|
||||||
|
mIDisposableTypeDef = _GetRequiredType("System.IDisposable");
|
||||||
mIPrintableTypeDef = _GetRequiredType("System.IPrintable");
|
mIPrintableTypeDef = _GetRequiredType("System.IPrintable");
|
||||||
mIHashableTypeDef = _GetRequiredType("System.IHashable");
|
mIHashableTypeDef = _GetRequiredType("System.IHashable");
|
||||||
mLinkNameAttributeTypeDef = _GetRequiredType("System.LinkNameAttribute");
|
mLinkNameAttributeTypeDef = _GetRequiredType("System.LinkNameAttribute");
|
||||||
|
|
|
@ -351,6 +351,7 @@ public:
|
||||||
BfTypeDef* mGenericIRefEnumeratorTypeDef;
|
BfTypeDef* mGenericIRefEnumeratorTypeDef;
|
||||||
|
|
||||||
BfTypeDef* mInternalTypeDef;
|
BfTypeDef* mInternalTypeDef;
|
||||||
|
BfTypeDef* mIDisposableTypeDef;
|
||||||
BfTypeDef* mIPrintableTypeDef;
|
BfTypeDef* mIPrintableTypeDef;
|
||||||
BfTypeDef* mIHashableTypeDef;
|
BfTypeDef* mIHashableTypeDef;
|
||||||
|
|
||||||
|
|
|
@ -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)))
|
if ((mModule->mAttributeState != NULL) && (mModule->mAttributeState->mCustomAttributes != NULL) && (mModule->mAttributeState->mCustomAttributes->Contains(mModule->mCompiler->mDisableObjectAccessChecksAttributeTypeDef)))
|
||||||
doAccessCheck = false;
|
doAccessCheck = false;
|
||||||
if ((doAccessCheck) && (!isSkipCall))
|
if ((doAccessCheck) && (!isSkipCall) && (prevBindResult.mPrevVal == NULL))
|
||||||
mModule->EmitObjectAccessCheck(target);
|
mModule->EmitObjectAccessCheck(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8292,6 +8292,19 @@ void BfExprEvaluator::Visit(BfDynamicCastExpression* dynCastExpr)
|
||||||
}
|
}
|
||||||
|
|
||||||
auto boolType = mModule->GetPrimitiveType(BfTypeCode_Boolean);
|
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())
|
if (targetValue.mType->IsValueTypeOrValueTypePtr())
|
||||||
{
|
{
|
||||||
mModule->Warn(0, StrFormat("Type '%s' is not applicable for dynamic casting",
|
mModule->Warn(0, StrFormat("Type '%s' is not applicable for dynamic casting",
|
||||||
|
|
|
@ -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 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
|
// we are now assuming any relevant warnings will be given at the declaration site
|
||||||
return NULL;
|
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)
|
if (bfError != NULL)
|
||||||
{
|
{
|
||||||
bfError->mProject = mProject;
|
bfError->mProject = mProject;
|
||||||
|
|
|
@ -517,7 +517,7 @@ void BfModule::AddDeferredBlock(BfBlock* block, BfScopeData* scopeData, Array<Bf
|
||||||
deferredCallEntry->mDeferredBlock = block;
|
deferredCallEntry->mDeferredBlock = block;
|
||||||
if (captures != NULL)
|
if (captures != NULL)
|
||||||
deferredCallEntry->mCaptures = *captures;
|
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)
|
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)
|
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;
|
exprEvaluator.mFunctionBindResult = &functionBindResult;
|
||||||
BfResolvedArgs resolvedArgs;
|
SizedArray<BfResolvedArg, 0> resolvedArgs;
|
||||||
exprEvaluator.MatchMethod(usingStmt->mVariableDeclaration, NULL, embeddedValue, false, false, "Dispose", resolvedArgs, NULL);
|
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)
|
if (functionBindResult.mMethodInstance != NULL)
|
||||||
{
|
{
|
||||||
moduleMethodInstance = BfModuleMethodInstance(functionBindResult.mMethodInstance, functionBindResult.mFunc);
|
moduleMethodInstance = BfModuleMethodInstance(functionBindResult.mMethodInstance, functionBindResult.mFunc);
|
||||||
AddDeferredCall(moduleMethodInstance, functionBindResult.mIRArgs, mCurMethodState->mCurScope);
|
AddDeferredCall(moduleMethodInstance, functionBindResult.mIRArgs, mCurMethodState->mCurScope, NULL, false, mayBeNull);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue