Skip to content

Commit b1e82f4

Browse files
committed
Rework typeViaContractName and nativeMembers fragment.
1 parent 193ea90 commit b1e82f4

4 files changed

Lines changed: 80 additions & 29 deletions

File tree

libsolidity/analysis/TypeChecker.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4054,7 +4054,7 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
40544054

40554055
FunctionType const* functionType = dynamic_cast<FunctionType const*>(
40564056
functionDefinition.libraryFunction() ?
4057-
functionDefinition.typeViaContractName() :
4057+
functionDefinition.typeViaContractName(Declaration::ContractNameAccessKind::Library) :
40584058
functionDefinition.type()
40594059
);
40604060

libsolidity/ast/AST.cpp

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -491,17 +491,47 @@ Type const* FunctionDefinition::type() const
491491
return TypeProvider::function(*this, FunctionType::Kind::Internal);
492492
}
493493

494-
Type const* FunctionDefinition::typeViaContractName() const
494+
Type const* FunctionDefinition::typeViaContractName(ContractNameAccessKind _accessKind) const
495495
{
496-
if (libraryFunction())
496+
switch (_accessKind)
497497
{
498-
if (isPublic())
499-
return FunctionType(*this).asExternallyCallableFunction(true);
500-
else
501-
return TypeProvider::function(*this, FunctionType::Kind::Internal);
498+
case ContractNameAccessKind::Local:
499+
{
500+
solAssert(!libraryFunction(), "Library member cannot be accessed from local/deriving scope");
501+
solAssert(visibility() > Visibility::Private, "Private non-library member is not visible via contract type name");
502+
503+
if (!Declaration::isVisibleInContract() || !isImplemented())
504+
// If is external or has no implementation, it cannot be called using contract type name. In case of accessing
505+
// via contract type name, only declaration is available, to be used in non calling context. I.e. to access
506+
// function selector `C.foo.selector` where foo has external visibility.
507+
return TypeProvider::function(*this, FunctionType::Kind::Declaration);
508+
else
509+
// If call is in local (or deriving) scope, function is visible in contract (non-external) and it has an
510+
// implementation, internal call is used.
511+
return type();
512+
}
513+
case ContractNameAccessKind::Foreign:
514+
{
515+
solAssert(!libraryFunction(), "Library members can only be accessed via library name.");
516+
solAssert(isVisibleViaContractInstance(), "Externally invisible member accessed via contract name.");
517+
// Foreign contract member function being accessed via contract type name, cannot be called.
518+
return TypeProvider::function(*this, FunctionType::Kind::Declaration);
519+
}
520+
case ContractNameAccessKind::Library:
521+
{
522+
// Private library members can be accessed in context of `using` statement.
523+
solAssert(libraryFunction(), "Non-library members cannot be accessed via library name.");
524+
// In case of library contract, member call kind depends on its visibility.
525+
if (isPublic())
526+
// When Lib.foo is public or external, an external call (delegate call) is used.
527+
return FunctionType(*this).asExternallyCallableFunction(true);
528+
else
529+
// For private or internal visibility, internal call is used.
530+
return type();
531+
}
532+
default:
533+
solAssert(false, "Unimplemented contract member access kind.");
502534
}
503-
else
504-
return TypeProvider::function(*this, FunctionType::Kind::Declaration);
505535
}
506536

507537
std::string FunctionDefinition::externalSignature() const
@@ -952,7 +982,7 @@ FunctionType const* UnaryOperation::userDefinedFunctionType() const
952982
FunctionDefinition const* userDefinedFunction = *annotation().userDefinedFunction;
953983
return dynamic_cast<FunctionType const*>(
954984
userDefinedFunction->libraryFunction() ?
955-
userDefinedFunction->typeViaContractName() :
985+
userDefinedFunction->typeViaContractName(Declaration::ContractNameAccessKind::Library) :
956986
userDefinedFunction->type()
957987
);
958988
}
@@ -965,7 +995,7 @@ FunctionType const* BinaryOperation::userDefinedFunctionType() const
965995
FunctionDefinition const* userDefinedFunction = *annotation().userDefinedFunction;
966996
return dynamic_cast<FunctionType const*>(
967997
userDefinedFunction->libraryFunction() ?
968-
userDefinedFunction->typeViaContractName() :
998+
userDefinedFunction->typeViaContractName(Declaration::ContractNameAccessKind::Library) :
969999
userDefinedFunction->type()
9701000
);
9711001
}

libsolidity/ast/AST.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -297,9 +297,16 @@ class Declaration: public ASTNode, public Scopable
297297
/// This can only be called once types of variable declarations have already been resolved.
298298
virtual Type const* type() const = 0;
299299

300-
/// @returns the type for members of the containing contract type that refer to this declaration.
300+
/// Defines type of member access via contract type name.
301+
/// @Local means access via contract name from local contract scope or deriving contract.
302+
/// @Foreign means access via contract name from foreign (unrelated) contract.
303+
/// @Library means access via library name.
304+
enum class ContractNameAccessKind { Local, Foreign, Library };
305+
306+
/// @returns the type for members of the containing contract type that refer to this declaration. Depends on access
307+
/// context defined by `ContractNameAccessKind`.
301308
/// This can only be called once types of variable declarations have already been resolved.
302-
virtual Type const* typeViaContractName() const { return type(); }
309+
virtual Type const* typeViaContractName(ContractNameAccessKind) const { return type(); }
303310

304311
/// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned.
305312
/// @returns null when it is not accessible as a function.
@@ -1053,7 +1060,7 @@ class FunctionDefinition: public CallableDeclaration, public StructurallyDocumen
10531060
std::string externalIdentifierHex() const;
10541061

10551062
Type const* type() const override;
1056-
Type const* typeViaContractName() const override;
1063+
Type const* typeViaContractName(ContractNameAccessKind _accessKind) const override;
10571064

10581065
/// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned.
10591066
/// @returns null when it is not accessible as a function.

libsolidity/ast/Types.cpp

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,9 @@ std::set<FunctionDefinition const*, ASTNode::CompareByID> Type::operatorDefiniti
404404
*identifierPath->annotation().referencedDeclaration
405405
);
406406
auto const* functionType = dynamic_cast<FunctionType const*>(
407-
functionDefinition.libraryFunction() ? functionDefinition.typeViaContractName() : functionDefinition.type()
407+
functionDefinition.libraryFunction() ?
408+
functionDefinition.typeViaContractName(Declaration::ContractNameAccessKind::Library) :
409+
functionDefinition.type()
408410
);
409411
solAssert(functionType && !functionType->parameterTypes().empty());
410412

@@ -426,7 +428,7 @@ MemberList::MemberMap Type::attachedFunctions(Type const& _type, ASTNode const&
426428
if (!_name)
427429
_name = _function.name();
428430
Type const* functionType =
429-
_function.libraryFunction() ? _function.typeViaContractName() : _function.type();
431+
_function.libraryFunction() ? _function.typeViaContractName(Declaration::ContractNameAccessKind::Library) : _function.type();
430432
solAssert(functionType, "");
431433
FunctionType const* withBoundFirstArgument =
432434
dynamic_cast<FunctionType const&>(*functionType).withBoundFirstArgument();
@@ -3978,21 +3980,33 @@ MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) cons
39783980
if (declaration->name().empty())
39793981
continue;
39803982

3981-
if (!contract.isLibrary() && inDerivingScope && declaration->isVisibleInDerivedContracts())
3983+
if (contract.isLibrary() && declaration->isVisibleAsLibraryMember())
39823984
{
3983-
if (
3984-
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(declaration);
3985-
functionDefinition && !functionDefinition->isImplemented()
3986-
)
3987-
members.emplace_back(declaration, declaration->typeViaContractName());
3988-
else
3989-
members.emplace_back(declaration, declaration->type());
3985+
// In case the contract is library, add only visible library members. visibility >= Internal.
3986+
members.emplace_back(
3987+
declaration,
3988+
declaration->typeViaContractName(Declaration::ContractNameAccessKind::Library)
3989+
);
3990+
}
3991+
else if (!contract.isLibrary() && inDerivingScope && declaration->visibility() > Visibility::Private)
3992+
{
3993+
// In case of regular contract (not library) and member is in the same deriving scope, add all
3994+
// members which are not private. Private members cannot be accessed via contract type name
3995+
// i.e C.fooPrivate.
3996+
members.emplace_back(
3997+
declaration,
3998+
declaration->typeViaContractName(Declaration::ContractNameAccessKind::Local)
3999+
);
4000+
}
4001+
else if (!contract.isLibrary() && !inDerivingScope && declaration->isVisibleViaContractInstance())
4002+
{
4003+
// In case of regular contract (not library) being accessed from foreign contract (not in deriving
4004+
// scope), add only externally visible members.
4005+
members.emplace_back(
4006+
declaration,
4007+
declaration->typeViaContractName(Declaration::ContractNameAccessKind::Foreign)
4008+
);
39904009
}
3991-
else if (
3992-
(contract.isLibrary() && declaration->isVisibleAsLibraryMember()) ||
3993-
declaration->isVisibleViaContractInstance()
3994-
)
3995-
members.emplace_back(declaration, declaration->typeViaContractName());
39964010
}
39974011
}
39984012
}

0 commit comments

Comments
 (0)