1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 03:28:20 +02:00

Added support for global:: lookups

This commit is contained in:
Brian Fiete 2025-05-18 12:13:15 +02:00
parent ee50457885
commit 958a1630aa
13 changed files with 202 additions and 28 deletions

View file

@ -1611,6 +1611,8 @@ const char* Beefy::BfTokenToString(BfToken token)
return ";";
case BfToken_Colon:
return ":";
case BfToken_ColonColon:
return "::";
case BfToken_Comma:
return ",";
case BfToken_Dot:

View file

@ -304,6 +304,7 @@ enum BfToken : uint8
BfToken_RDblChevron,
BfToken_Semicolon,
BfToken_Colon,
BfToken_ColonColon,
BfToken_Comma,
BfToken_Dot,
BfToken_DotDot,
@ -2014,6 +2015,13 @@ public:
BfTokenNode* mColonToken;
BfAstNode* mTargetNode; // . : or identifier
BfAttributeDirective* mAttributes;
BfAstNode* GetTargetNode()
{
if ((mColonToken != NULL) && (mColonToken->mToken == BfToken_ColonColon))
return mColonToken;
return mTargetNode;
}
}; BF_AST_DECL(BfScopeNode, BfAstNode);
class BfNewNode : public BfAstNode
@ -2108,6 +2116,12 @@ public:
ASTREF(BfIdentifierNode*) mLeft;
ASTREF(BfTokenNode*) mDot;
ASTREF(BfIdentifierNode*) mRight;
bool IsGlobalLookup()
{
return (mDot != NULL) && (mDot->mToken == BfToken_ColonColon);
}
}; BF_AST_DECL(BfQualifiedNameNode, BfIdentifierNode);
class BfUsingDirective : public BfStatement
@ -2599,6 +2613,12 @@ public:
ASTREF(BfTypeReference*) mLeft;
ASTREF(BfTokenNode*) mDot;
ASTREF(BfTypeReference*) mRight;
bool IsGlobalLookup()
{
return (mDot != NULL) && (mDot->mToken == BfToken_ColonColon);
}
}; BF_AST_DECL(BfQualifiedTypeReference, BfTypeReference);
class BfResolvedTypeReference : public BfTypeReference
@ -3036,6 +3056,13 @@ public:
BfAstNode* mTargetNode;
BfAstNode* GetScopeNameNode()
{
if ((mColonToken != NULL) && (mColonToken->mToken == BfToken_ColonColon))
return mColonToken;
return mScopeName;
}
// virtual bool IsMissingSemicolon() override
// {
// return BfNodeDynCastExact<BfBlock>(mTargetNode) == NULL;
@ -3059,6 +3086,13 @@ public:
BfAstNode* mTarget;
BfTokenNode* mColonToken;
BfAstNode* mScopeName; // :, mixin, or identifier
BfAstNode* GetScopeNameNode()
{
if ((mColonToken != NULL) && (mColonToken->mToken == BfToken_ColonColon))
return mColonToken;
return mScopeName;
}
}; BF_AST_DECL(BfScopedInvocationTarget, BfAstNode);
class BfInvocationExpression : public BfMethodBoundExpression

View file

@ -1853,7 +1853,7 @@ void BfAutoComplete::CheckIdentifier(BfAstNode* identifierNode, bool isInExpress
{
"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", "not", "null",
"fixed", "for", "function", "global", "if", "implicit", "in", "internal", "is", "isconst", "new", "mixin", "not", "null",
"offsetof", "out", "params", "readonly", "ref", "rettype", "return",
"sealed", "sizeof", "scope", "static", "strideof", "struct", "switch", /*"this",*/ "try", "true", "typeof", "unchecked",
"using", "var", "virtual", "volatile", "where", "while",
@ -1873,7 +1873,7 @@ void BfAutoComplete::CheckIdentifier(BfAstNode* identifierNode, bool isInExpress
const char* tokens[] =
{
"abstract", "append", "base", "class", "concrete", "const",
"delegate", "extern", "enum", "explicit", "extension", "function",
"delegate", "extern", "enum", "explicit", "extension", "function", "global",
"interface", "in", "implicit", "internal", "mixin", "namespace", "new",
"operator", "out", "override", "params", "private", "protected", "public", "readonly", "ref", "rettype", "return",
"scope", "sealed", "static", "struct", "this", "typealias",
@ -1926,6 +1926,16 @@ bool BfAutoComplete::CheckMemberReference(BfAstNode* target, BfAstNode* dotToken
if (!WantsEntries())
return false;
bool isGlobalLookup = true;
if (auto dotTokenNode = BfNodeDynCast<BfTokenNode>(dotToken))
{
if (dotTokenNode->mToken == BfToken_ColonColon)
{
CheckNode(memberName, false, false);
return false;
}
}
BfAttributedIdentifierNode* attrIdentifier = NULL;
bool isAutocompletingName = false;
if ((attrIdentifier = BfNodeDynCast<BfAttributedIdentifierNode>(memberName)))

View file

@ -17223,8 +17223,8 @@ void BfExprEvaluator::ResolveAllocTarget(BfAllocTarget& allocTarget, BfAstNode*
{
if (auto scopeNode = BfNodeDynCast<BfScopeNode>(allocNode))
{
newToken = scopeNode->mScopeToken;
allocTarget.mScopeData = mModule->FindScope(scopeNode->mTargetNode, true);
newToken = scopeNode->mScopeToken;
allocTarget.mScopeData = mModule->FindScope(scopeNode->GetTargetNode(), true);
if (autoComplete != NULL)
{
@ -17799,7 +17799,9 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo
}
auto targetNameNode = targetSrc;
if (scopedInvocationTarget != NULL)
targetNameNode = scopedInvocationTarget->mTarget;
{
targetNameNode = scopedInvocationTarget->GetScopeNameNode();
}
while (true)
{
@ -18214,7 +18216,7 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo
auto checkNode = origTargetSrc;
if (scopedInvocationTarget != NULL)
{
auto targetScope = mModule->FindScope(scopedInvocationTarget->mScopeName, curMethodState->mMixinState);
auto targetScope = mModule->FindScope(scopedInvocationTarget->GetScopeNameNode(), curMethodState->mMixinState);
if (targetScope != NULL)
{
mixinState->mTargetScope = targetScope;
@ -19106,7 +19108,20 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
if (GetAutoComplete() != NULL)
GetAutoComplete()->CheckMemberReference(qualifiedName->mLeft, qualifiedName->mDot, qualifiedName->mRight);
if (qualifiedName->mLeft->GetSrcLength() == 4)
bool isGlobalLookup = false;
if (auto qualifiedNameNode = BfNodeDynCast<BfQualifiedNameNode>(target))
{
if (qualifiedNameNode->IsGlobalLookup())
{
if (auto subQualifiedNameNode = BfNodeDynCast<BfQualifiedNameNode>(qualifiedNameNode->mRight))
{
qualifiedName = subQualifiedNameNode;
isGlobalLookup = true;
}
}
}
if ((!isGlobalLookup) && (qualifiedName->mLeft->GetSrcLength() == 4))
{
if (CheckIsBase(qualifiedName->mLeft))
bypassVirtual = true;
@ -19127,7 +19142,8 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
targetFunctionName = qualifiedName->mRight->ToString();
bool hadError = false;
thisValue = LookupIdentifier(qualifiedName->mLeft, true, &hadError);
if (!isGlobalLookup)
thisValue = LookupIdentifier(qualifiedName->mLeft, true, &hadError);
CheckResultForReading(thisValue);
if (mPropDef != NULL)
@ -19141,18 +19157,22 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m
if ((!thisValue) && (mPropDef == NULL))
{
auto typeLookupFlags = (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoResolveGenericParam | BfResolveTypeRefFlag_AllowGlobalContainer | BfResolveTypeRefFlag_IgnoreLookupError);
if (isGlobalLookup)
typeLookupFlags = (BfResolveTypeRefFlags)(typeLookupFlags | BfResolveTypeRefFlag_GlobalLookup);
// Identifier not found. Static method? Just check speculatively don't throw error
BfType* type;
{
//SetAndRestoreValue<bool> prevIgnoreErrors(mModule->mIgnoreErrors, true);
type = mModule->ResolveTypeRef(qualifiedName->mLeft, NULL, BfPopulateType_DataAndMethods, (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoResolveGenericParam | BfResolveTypeRefFlag_AllowGlobalContainer | BfResolveTypeRefFlag_IgnoreLookupError));
type = mModule->ResolveTypeRef(qualifiedName->mLeft, NULL, BfPopulateType_DataAndMethods, typeLookupFlags);
}
if (type == NULL)
{
//SetAndRestoreValue<bool> prevIgnoreErrors(mModule->mIgnoreErrors, true);
type = mModule->ResolveTypeRef(qualifiedName, methodGenericArguments, BfPopulateType_DataAndMethods, (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoResolveGenericParam | BfResolveTypeRefFlag_AllowGlobalContainer | BfResolveTypeRefFlag_IgnoreLookupError));
type = mModule->ResolveTypeRef(qualifiedName, methodGenericArguments, BfPopulateType_DataAndMethods, typeLookupFlags);
if (type != NULL)
{
// This is a CTOR call, treat it as such

View file

@ -16568,7 +16568,7 @@ BfScopeData* BfModule::FindScope(BfAstNode* scopeName, BfMixinState* fromMixinSt
if (auto tokenNode = BfNodeDynCast<BfTokenNode>(scopeName))
{
if (tokenNode->GetToken() == BfToken_Colon)
if ((tokenNode->GetToken() == BfToken_Colon) || (tokenNode->GetToken() == BfToken_ColonColon))
{
if ((!allowAcrossDeferredBlock) && (mCurMethodState->mInDeferredBlock))
{

View file

@ -10519,12 +10519,15 @@ BfTypeDef* BfModule::FindTypeDefRaw(const BfAtomComposite& findName, int numGene
if (mSystem->mTypeDefs.TryGet(findName, NULL))
mSystem->FindTypeDef(findName, numGenericArgs, useProject, BfAtomComposite(), allowPrivate, &lookupCtx);
for (auto& checkNamespace : useTypeDef->mNamespaceSearch)
if ((resolveFlags & BfResolveTypeRefFlag_GlobalLookup) == 0)
{
BfAtom* atom = findName.mParts[0];
BfAtom* prevAtom = checkNamespace.mParts[checkNamespace.mSize - 1];
if (atom->mPrevNamesMap.ContainsKey(prevAtom))
mSystem->FindTypeDef(findName, numGenericArgs, useProject, checkNamespace, allowPrivate, &lookupCtx);
for (auto& checkNamespace : useTypeDef->mNamespaceSearch)
{
BfAtom* atom = findName.mParts[0];
BfAtom* prevAtom = checkNamespace.mParts[checkNamespace.mSize - 1];
if (atom->mPrevNamesMap.ContainsKey(prevAtom))
mSystem->FindTypeDef(findName, numGenericArgs, useProject, checkNamespace, allowPrivate, &lookupCtx);
}
}
}
@ -10550,7 +10553,7 @@ BfTypeDef* BfModule::FindTypeDefRaw(const BfAtomComposite& findName, int numGene
}
}
if ((!lookupCtx.HasValidMatch()) && (typeInstance == NULL))
if ((!lookupCtx.HasValidMatch()) && (typeInstance == NULL) && ((resolveFlags & BfResolveTypeRefFlag_GlobalLookup) == 0))
{
if (useTypeDef->mOuterType != NULL)
return FindTypeDefRaw(findName, numGenericArgs, typeInstance, useTypeDef->mOuterType, error);
@ -10591,6 +10594,12 @@ BfTypeDef* BfModule::FindTypeDef(const BfAtomComposite& findName, int numGeneric
if (useTypeDef != NULL)
useTypeDef = useTypeDef->GetDefinition();
if ((resolveFlags & BfResolveTypeRefFlag_GlobalLookup) != 0)
{
// No need to cache
return FindTypeDefRaw(findName, numGenericArgs, typeInstance, useTypeDef, error, NULL, resolveFlags);
}
if ((typeInstance == NULL) && (useTypeDef == NULL))
{
BfProject* project = NULL;
@ -10648,7 +10657,7 @@ BfTypeDef* BfModule::FindTypeDef(const BfAtomComposite& findName, int numGeneric
BfTypeLookupEntry typeLookupEntry;
typeLookupEntry.mName.Reference(findName);
typeLookupEntry.mNumGenericParams = numGenericArgs;
typeLookupEntry.mFlags = ((resolveFlags & BfResolveTypeRefFlag_SpecializedProject) != 0) ? BfTypeLookupEntry::Flags_SpecializedProject : BfTypeLookupEntry::Flags_None;
typeLookupEntry.mFlags = ((resolveFlags & BfResolveTypeRefFlag_SpecializedProject) != 0) ? BfTypeLookupEntry::Flags_SpecializedProject : BfTypeLookupEntry::Flags_None;
typeLookupEntry.mUseTypeDef = useTypeDef;
BfTypeLookupEntry* typeLookupEntryPtr = NULL;
@ -11589,6 +11598,12 @@ BfType* BfModule::ResolveTypeRef_Ref(BfTypeReference* typeRef, BfPopulateType po
if (auto qualifiedTypeRef = BfNodeDynCast<BfQualifiedTypeReference>(typeRef))
{
if (qualifiedTypeRef->IsGlobalLookup())
{
resolveFlags = (BfResolveTypeRefFlags)(resolveFlags | BfResolveTypeRefFlag_GlobalLookup);
return ResolveTypeRef_Ref(qualifiedTypeRef->mRight, populateType, resolveFlags, numGenericArgs);
}
//TODO: Determine why we had this prevIgnoreErrors set here. It causes things like IEnumerator<Hey.Test<INVALIDNAME>> not fail
// properly on INVALIDNAME
SetAndRestoreValue<bool> prevIgnoreErrors(mIgnoreErrors, /*true*/mIgnoreErrors);

View file

@ -2630,6 +2630,13 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate, bool disablePrepro
mToken = BfToken_Dot;
mSyntaxToken = BfSyntaxToken_Token;
}
else if (mSrc[mSrcIdx] == ':')
{
mSrcIdx++;
mTokenEnd = mSrcIdx;
mToken = BfToken_ColonColon;
mSyntaxToken = BfSyntaxToken_Token;
}
else
{
mToken = BfToken_Colon;

View file

@ -4248,6 +4248,11 @@ BfAstNode* BfReducer::DoCreateStatement(BfAstNode* node, CreateStmtFlags createS
FailAfter("Expected scope name", deferStmt);
}
}
else if (nextTokenNode->GetToken() == BfToken_ColonColon)
{
MEMBER_SET(deferStmt, mColonToken, nextTokenNode);
mVisitorPos.MoveNext();
}
else if (nextTokenNode->GetToken() == BfToken_LParen)
{
mPassInstance->Warn(0, "Syntax deprecated", nextTokenNode);
@ -5868,7 +5873,7 @@ BfTypeReference* BfReducer::CreateRefTypeRef(BfTypeReference* elementType, BfTok
return refTypeRef;
}
BfIdentifierNode* BfReducer::CompactQualifiedName(BfAstNode* leftNode)
BfIdentifierNode* BfReducer::CompactQualifiedName(BfAstNode* leftNode, bool allowGlobalLookup)
{
AssertCurrentNode(leftNode);
@ -5879,9 +5884,15 @@ BfIdentifierNode* BfReducer::CompactQualifiedName(BfAstNode* leftNode)
auto leftIdentifier = (BfIdentifierNode*)leftNode;
while (true)
{
bool isGlobalLookup = false;
auto nextToken = mVisitorPos.Get(mVisitorPos.mReadPos + 1);
auto tokenNode = BfNodeDynCast<BfTokenNode>(nextToken);
if ((tokenNode == NULL) || ((tokenNode->GetToken() != BfToken_Dot) /*&& (tokenNode->GetToken() != BfToken_QuestionDot)*/))
if ((tokenNode != NULL) && (tokenNode->mToken == BfToken_ColonColon) && (leftNode->IsExact<BfIdentifierNode>()) && (leftNode->Equals("global")) && (allowGlobalLookup))
{
isGlobalLookup = true;
}
else if ((tokenNode == NULL) || ((tokenNode->GetToken() != BfToken_Dot) /*&& (tokenNode->GetToken() != BfToken_QuestionDot)*/))
return leftIdentifier;
auto nextNextToken = mVisitorPos.Get(mVisitorPos.mReadPos + 2);
@ -5906,6 +5917,22 @@ BfIdentifierNode* BfReducer::CompactQualifiedName(BfAstNode* leftNode)
return leftIdentifier;
}
if (isGlobalLookup)
{
// For 'global::', we put 'global' on the left and the rest on the right which is different from normal
mVisitorPos.MoveNext(); // past .
mVisitorPos.MoveNext(); // past right
auto rightSide = CompactQualifiedName(rightIdentifier, false);
auto qualifiedNameNode = mAlloc->Alloc<BfQualifiedNameNode>();
ReplaceNode(leftIdentifier, qualifiedNameNode);
qualifiedNameNode->mLeft = leftIdentifier;
MEMBER_SET(qualifiedNameNode, mDot, tokenNode);
MEMBER_SET(qualifiedNameNode, mRight, rightSide);
return qualifiedNameNode;
}
// If the previous dotted span failed (IE: had chevrons) then don't insert qualified names in the middle of it
auto prevNodeToken = BfNodeDynCast<BfTokenNode>(prevNode);
if ((prevNodeToken != NULL) &&
@ -7935,7 +7962,7 @@ BfInvocationExpression* BfReducer::CreateInvocationExpression(BfAstNode* target,
if (auto nextToken = BfNodeDynCast<BfTokenNode>(mVisitorPos.GetNext()))
{
if (nextToken->GetToken() == BfToken_Colon)
if ((nextToken->GetToken() == BfToken_Colon) || (nextToken->GetToken() == BfToken_ColonColon))
{
auto scopedInvocationTarget = CreateScopedInvocationTarget(invocationExpr->mTarget, nextToken);
invocationExpr->SetSrcEnd(scopedInvocationTarget->GetSrcEnd());
@ -8317,6 +8344,10 @@ BfScopedInvocationTarget* BfReducer::CreateScopedInvocationTarget(BfAstNode*& ta
MEMBER_SET(scopedInvocationTarget, mColonToken, colonToken);
mVisitorPos.MoveNext();
if (colonToken->mToken == BfToken_ColonColon)
return scopedInvocationTarget;
if (auto nextToken = BfNodeDynCast<BfTokenNode>(mVisitorPos.GetNext()))
{
if ((nextToken->GetToken() == BfToken_Colon) || (nextToken->GetToken() == BfToken_Mixin))
@ -8473,7 +8504,7 @@ BfAstNode* BfReducer::CreateAllocNode(BfTokenNode* allocToken)
auto nextToken = BfNodeDynCast<BfTokenNode>(mVisitorPos.GetNext());
if (nextToken == NULL)
return allocToken;
if ((nextToken->mToken != BfToken_Colon) && (nextToken->mToken != BfToken_LBracket))
if ((nextToken->mToken != BfToken_Colon) && (nextToken->mToken != BfToken_ColonColon) && (nextToken->mToken != BfToken_LBracket))
return allocToken;
auto scopeNode = mAlloc->Alloc<BfScopeNode>();
@ -8504,6 +8535,11 @@ BfAstNode* BfReducer::CreateAllocNode(BfTokenNode* allocToken)
FailAfter("Expected scope name", scopeNode);
}
}
else if (nextToken->mToken == BfToken_ColonColon)
{
MEMBER_SET(scopeNode, mColonToken, nextToken);
mVisitorPos.MoveNext();
}
nextToken = BfNodeDynCast<BfTokenNode>(mVisitorPos.GetNext());
if (nextToken == NULL)

View file

@ -231,7 +231,7 @@ public:
void ReadPropertyBlock(BfPropertyDeclaration* propertyDeclaration, BfBlock* block);
BfAstNode* ReadTypeMember(BfTokenNode* node, bool declStarted = false, int depth = 0, BfAstNode* deferredHeadNode = NULL);
BfAstNode* ReadTypeMember(BfAstNode* node, bool declStarted = false, int depth = 0, BfAstNode* deferredHeadNode = NULL);
BfIdentifierNode* CompactQualifiedName(BfAstNode* leftNode);
BfIdentifierNode* CompactQualifiedName(BfAstNode* leftNode, bool allowGlobalLookup = true);
void TryIdentifierConvert(int readPos);
void CreateQualifiedNames(BfAstNode* node);
BfFieldDtorDeclaration* CreateFieldDtorDeclaration(BfAstNode* srcNode);

View file

@ -42,7 +42,8 @@ enum BfResolveTypeRefFlags
BfResolveTypeRefFlag_AllowUnboundGeneric = 0x40000,
BfResolveTypeRefFlag_ForceUnboundGeneric = 0x80000,
BfResolveTypeRefFlag_IgnoreProtection = 0x100000,
BfResolveTypeRefFlag_SpecializedProject = 0x200000
BfResolveTypeRefFlag_SpecializedProject = 0x200000,
BfResolveTypeRefFlag_GlobalLookup = 0x400000
};
enum BfTypeNameFlags : uint16
@ -1735,7 +1736,7 @@ struct BfTypeLookupEntry
enum Flags : uint8
{
Flags_None,
Flags_SpecializedProject
Flags_SpecializedProject,
};
BfAtomComposite mName;

View file

@ -283,8 +283,10 @@ void BfSourceClassifier::Visit(BfIdentifierNode* identifier)
void BfSourceClassifier::Visit(BfQualifiedNameNode* qualifiedName)
{
Visit((BfAstNode*)qualifiedName);
VisitChild(qualifiedName->mLeft);
if (qualifiedName->IsGlobalLookup())
SetElementType(qualifiedName->mLeft, BfSourceElementType_Namespace);
VisitChild(qualifiedName->mDot);
VisitChild(qualifiedName->mRight);
if (BfNodeIsExact<BfIdentifierNode>(qualifiedName->mRight))
@ -350,6 +352,8 @@ void BfSourceClassifier::Visit(BfQualifiedTypeReference* qualifiedType)
Visit((BfAstNode*)qualifiedType);
VisitChild(qualifiedType->mLeft);
if (qualifiedType->IsGlobalLookup())
SetElementType(qualifiedType->mLeft, BfSourceElementType_Namespace);
VisitChild(qualifiedType->mDot);
VisitChild(qualifiedType->mRight);
}
@ -495,6 +499,8 @@ void BfSourceClassifier::Visit(BfInvocationExpression* invocationExpr)
if (auto qualifiedName = BfNodeDynCast<BfQualifiedNameNode>(target))
{
VisitChild(qualifiedName->mLeft);
if (qualifiedName->IsGlobalLookup())
SetElementType(qualifiedName->mLeft, BfSourceElementType_Namespace);
VisitChild(qualifiedName->mDot);
VisitChild(qualifiedName->mRight);
identifier = qualifiedName->mRight;

View file

@ -7387,6 +7387,8 @@ void BfModule::Visit(BfDeferStatement* deferStmt)
BfScopeData* scope = NULL;
auto scopeNameNode = deferStmt->GetScopeNameNode();
if (deferStmt->mScopeToken != NULL)
{
if (deferStmt->mScopeToken->GetToken() == BfToken_Scope)
@ -7394,9 +7396,9 @@ void BfModule::Visit(BfDeferStatement* deferStmt)
else
scope = &mCurMethodState->mHeadScope;
}
else if (deferStmt->mScopeName != NULL)
else if (scopeNameNode != NULL)
{
scope = FindScope(deferStmt->mScopeName, true);
scope = FindScope(scopeNameNode, true);
if (scope == NULL)
{

View file

@ -0,0 +1,41 @@
#pragma warning disable 168
namespace Tests;
namespace A.B
{
class Zonk<T>
{
public static int sVal = 123;
}
static
{
public static void Main()
{
global::B.Init();
global::A.B.Zonk<int>.sVal = 234;
global::B.MethodT<float>();
global::System.String str = null;
}
}
}
namespace B
{
static
{
public static void Init()
{
}
public static void MethodT<T>()
{
}
}
}
class Lookups
{
}