diff --git a/lib/fuzion/sys/fileio.fz b/lib/fuzion/sys/fileio.fz index 6412965d6..7d9b70e2b 100644 --- a/lib/fuzion/sys/fileio.fz +++ b/lib/fuzion/sys/fileio.fz @@ -53,7 +53,7 @@ module fileio is # -1 at end of file # -2 for any other error # - private read( + module read( # the file descriptor fd i64, # the internal array data representing the container for the bytes to be read from the file diff --git a/lib/fuzion/sys/stdin.fz b/lib/fuzion/sys/stdin.fz index cb12da998..abd1f44f4 100644 --- a/lib/fuzion/sys/stdin.fz +++ b/lib/fuzion/sys/stdin.fz @@ -27,5 +27,5 @@ # NYI name in does not work right now since in is reserved keyword public stdin is - private stdin0 i64 is intrinsic + module stdin0 i64 is intrinsic diff --git a/src/dev/flang/ast/AbstractFeature.java b/src/dev/flang/ast/AbstractFeature.java index ec2948a83..0c601865c 100644 --- a/src/dev/flang/ast/AbstractFeature.java +++ b/src/dev/flang/ast/AbstractFeature.java @@ -727,7 +727,7 @@ else if (isUniverse()) var p = pos(); var inh = typeFeatureInherits(res); var typeArg = new Feature(p, - visibility(), + visibility().typeVisibility(), inh.isEmpty() ? 0 : Consts.MODIFIER_REDEFINE, selfType(), FuzionConstants.TYPE_FEATURE_THIS_TYPE, @@ -828,7 +828,7 @@ private AbstractFeature existingOrNewTypeFeature(Resolution res, String name, Li if (result == null) { var p = pos(); - var typeFeature = new Feature(p, visibility(), 0, NoType.INSTANCE, new List<>(name), typeArgs, + var typeFeature = new Feature(p, visibility().typeVisibility(), 0, NoType.INSTANCE, new List<>(name), typeArgs, inh, Contract.EMPTY_CONTRACT, new Impl(p, new Block(p, new List<>()), Impl.Kind.Routine)); diff --git a/src/dev/flang/ast/AbstractType.java b/src/dev/flang/ast/AbstractType.java index 0bd43d773..3d4d1203a 100644 --- a/src/dev/flang/ast/AbstractType.java +++ b/src/dev/flang/ast/AbstractType.java @@ -27,7 +27,10 @@ package dev.flang.ast; import java.util.Set; +import java.util.TreeSet; import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.stream.Collectors; import dev.flang.util.ANY; import dev.flang.util.Errors; @@ -67,6 +70,12 @@ public abstract class AbstractType extends ANY implements Comparable _usedFeatures = null; + + /*----------------------------- methods -----------------------------*/ @@ -1680,6 +1689,38 @@ AbstractType clone(AbstractFeature originalOuterFeature) return this; } + + /** + * traverse a resolved type collecting all features this type uses. + * + * @param s the features that have already been found + */ + protected abstract void usedFeatures(Set s); + + + /** + * @param v + * + * @return this type and any of its generics that have more restrictive visibility than `v`. + */ + public Set moreRestrictiveVisibility(Visi v) + { + if (PRECONDITIONS) require + (!v.definesTypeVisibility()); + + if (_usedFeatures == null) + { + _usedFeatures = new TreeSet(); + usedFeatures(_usedFeatures); + } + + return _usedFeatures + .stream() + .filter(af -> af.visibility().typeVisibility().ordinal() < v.ordinal()) + .collect(Collectors.toSet()); + } + + } /* end of file */ diff --git a/src/dev/flang/ast/AstErrors.java b/src/dev/flang/ast/AstErrors.java index 7f17b5dea..c855d3293 100644 --- a/src/dev/flang/ast/AstErrors.java +++ b/src/dev/flang/ast/AstErrors.java @@ -132,6 +132,13 @@ static String s(AbstractAssign a) { return ss(a.toString()); } + static String s(Visi v) + { + if (PRECONDITIONS) require + (v != Visi.UNSPECIFIED); + + return code(v.toString()); + } static String ss(String s) // statement or expression { return expr(s); @@ -170,6 +177,10 @@ static String sqn(List names) // names as qualified name "a.b.c" { return ss(names.toString("", ".", "")); } + static String sv(AbstractFeature f) + { + return s(f.visibility()) + " " + s(f); + } static String code(String s) { return ticksOrNewLine(Terminal.PURPLE + s + Terminal.REGULAR_COLOR); } static String type(String s) { return ticksOrNewLine(Terminal.YELLOW + s + Terminal.REGULAR_COLOR); } static String expr(String s) { return ticksOrNewLine(Terminal.CYAN + s + Terminal.REGULAR_COLOR); } @@ -1955,6 +1966,37 @@ public static void freeTypeMustNotMaskExistingType(UnresolvedType t, AbstractFea "To solve this, you may use a different name for free type " + s(t) + "."); } + public static void calledFeatureInPreconditionHasMoreRestrictiveVisibilityThanFeature(Feature f, AbstractCall c) + { + error(c.pos(), "Called feature in precondition has more restrictive visibility than feature.", + "To solve this, increase the visibility of " + s(c.calledFeature()) + " or do not" + + " use this feature in the precondition." + ); + } + + public static void illegalVisibilityModifier(Feature f) + { + error(f.pos(), "Feature specifying type visibility does not define a type.", + "To solve this, remove the type visibility: " + s(f.visibility().typeVisibility()) + "." + ); + } + + public static void argTypeMoreRestrictiveVisbility(Feature f, AbstractFeature arg, Set s) + { + error(f.pos(), "Argument types or any of its generics have more restrictive visibility than feature.", + "To solve this, increase the visibility of " + slbn(s.stream().map(x -> x.featureName()).collect(List.collector())) + + " or specify a different type for the argument " + sbn(arg) + "." + ); + } + + public static void resultTypeMoreRestrictiveVisibility(Feature f, Set s) + { + error(f.pos(), "Result type or any of its generics have more restrictive visibility than feature.", + "To solve this, increase the visibility of " + slbn(s.stream().map(x -> x.featureName()).collect(List.collector())) + + " or specify a different return type." + ); + } + } /* end of file */ diff --git a/src/dev/flang/ast/Feature.java b/src/dev/flang/ast/Feature.java index 01e5b9404..c13ad263e 100644 --- a/src/dev/flang/ast/Feature.java +++ b/src/dev/flang/ast/Feature.java @@ -101,12 +101,21 @@ public class Feature extends AbstractFeature * The visibility of this feature */ private Visi _visibility; - public Visi visibility() { return _visibility; } + public Visi visibility() + { + return + // NYI anonymous feature should have correct visibility set. + isAnonymousInnerFeature() + ? outer().visibility() + : _visibility == Visi.UNSPECIFIED + ? Visi.PRIV + : _visibility; + } /** * This is used for feature defined using `choice of` - * to set same visibilty for choice elements as for choice in Parser. + * to set same visibility for choice elements as for choice in Parser. * * @param v */ @@ -354,7 +363,7 @@ public static Feature anonymous(SourcePosition pos, Block b) { return new Feature(pos, - Visi.PRIV, + Visi.UNSPECIFIED, 0, r, new List(FuzionConstants.ANONYMOUS_FEATURE_PREFIX + (uniqueAnonymousFeatureId++)), diff --git a/src/dev/flang/ast/FormalGenerics.java b/src/dev/flang/ast/FormalGenerics.java index 7945f9c42..4914ae969 100644 --- a/src/dev/flang/ast/FormalGenerics.java +++ b/src/dev/flang/ast/FormalGenerics.java @@ -29,6 +29,7 @@ import java.util.Iterator; import dev.flang.util.ANY; +import dev.flang.util.Errors; import dev.flang.util.List; import dev.flang.util.SourcePosition; @@ -138,6 +139,9 @@ public boolean errorIfSizeOrTypeDoesNotMatch(List actualGenerics, String detail1, String detail2) { + if (PRECONDITIONS) require + (Errors.count() > 0 || !actualGenerics.contains(Types.t_ERROR)); + boolean result = true; if (!sizeMatches(actualGenerics)) { diff --git a/src/dev/flang/ast/ResolvedNormalType.java b/src/dev/flang/ast/ResolvedNormalType.java index 148216779..3e89b9b65 100644 --- a/src/dev/flang/ast/ResolvedNormalType.java +++ b/src/dev/flang/ast/ResolvedNormalType.java @@ -26,6 +26,11 @@ package dev.flang.ast; +import java.util.Set; +import java.util.TreeSet; +import java.util.function.Consumer; +import java.util.stream.Collectors; + import dev.flang.util.Errors; import dev.flang.util.FuzionConstants; import dev.flang.util.HasSourcePosition; @@ -646,6 +651,33 @@ AbstractFeature originalOuterFeature(AbstractFeature currentOuter) }; } + + /** + * traverse a resolved type collecting all features this type uses. + * + * @param s the features that have already been found + */ + protected void usedFeatures(Set s) + { + // NYI: "This currently does not touch the outer features. + // This means that for a type like (x T).y U the visibility of x and T will be ignored, which is probably wrong." + var f = featureOfType(); + if (s.add(f)) + { + for (var g : generics()) + { + g.usedFeatures(s); + } + if (isChoice()) + { + for (var g : choiceGenerics()) + { + g.usedFeatures(s); + } + } + } + } + } /* end of file */ diff --git a/src/dev/flang/ast/ResolvedParametricType.java b/src/dev/flang/ast/ResolvedParametricType.java index 65b4f2376..5bd7c3d3f 100644 --- a/src/dev/flang/ast/ResolvedParametricType.java +++ b/src/dev/flang/ast/ResolvedParametricType.java @@ -27,9 +27,11 @@ package dev.flang.ast; import java.util.Set; +import java.util.TreeSet; +import java.util.function.Consumer; +import java.util.stream.Collectors; import dev.flang.util.Errors; -import dev.flang.util.HasSourcePosition; import dev.flang.util.List; import dev.flang.util.SourcePosition; @@ -210,6 +212,20 @@ public AbstractType asThis() } + /** + * traverse a type collecting all features this type uses. + * + * @param s the features that have already been found + */ + protected void usedFeatures(Set s) + { + if (!genericArgument().typeParameter().isTypeFeaturesThisType()) + { + genericArgument().typeParameter().resultType().usedFeatures(s); + } + } + + /** * toString * diff --git a/src/dev/flang/ast/UnresolvedType.java b/src/dev/flang/ast/UnresolvedType.java index 3429bbce4..2ee61078c 100644 --- a/src/dev/flang/ast/UnresolvedType.java +++ b/src/dev/flang/ast/UnresolvedType.java @@ -26,6 +26,9 @@ package dev.flang.ast; +import java.util.Set; +import java.util.function.Consumer; + import dev.flang.util.Errors; import dev.flang.util.FuzionConstants; import dev.flang.util.HasSourcePosition; @@ -909,6 +912,18 @@ AbstractType addAsFreeType(Resolution res, AbstractFeature outerfeat) } + + /** + * traverse a type collecting all features this type uses. + * + * @param s the features that have already been found + */ + protected void usedFeatures(Set s) + { + throw new RuntimeException("must not be called on unresolved types."); + } + + } /* end of file */ diff --git a/src/dev/flang/ast/Visi.java b/src/dev/flang/ast/Visi.java index 8ff266591..fc72def70 100644 --- a/src/dev/flang/ast/Visi.java +++ b/src/dev/flang/ast/Visi.java @@ -99,7 +99,8 @@ public static Visi from(int ordinal) { if (ANY.PRECONDITIONS) ANY.require (0 <= ordinal, - ordinal < values().length); + ordinal < values().length, + ordinal != UNSPECIFIED.ordinal()); if (ANY.CHECKS) ANY.check (values()[ordinal].ordinal() == ordinal); @@ -107,6 +108,48 @@ public static Visi from(int ordinal) return values()[ordinal]; } + /** + * @return The visibility for features/calls encoded in this. + */ + public Visi featureVisibility() + { + if (this.ordinal() <= PRIVPUB.ordinal()) + { + return PRIV; + } + else if (this.ordinal() <= MODPUB.ordinal()) + { + return MOD; + } + return PUB; + } + + + /** + * @return The visibility for types encoded in this. + * PRIV => PRIV, PRIVMOD => MOD, PRIVPUB => PUB, etc. + */ + public Visi typeVisibility() + { + return switch (this) + { + case UNSPECIFIED, PRIV -> Visi.PRIV; + case MOD, PRIVMOD -> Visi.MOD; + case PRIVPUB, MODPUB, PUB -> Visi.PUB; + default -> throw new RuntimeException("undhandled case in Visi.typeVisibility"); + }; + } + + + /** + * Does this visibility explicitly specify a different visibility for the type? + * @return + */ + public boolean definesTypeVisibility() + { + return this == Visi.PRIVMOD || this == Visi.PRIVPUB || this == Visi.MODPUB; + } + } /* end of file */ diff --git a/src/dev/flang/fe/LibraryType.java b/src/dev/flang/fe/LibraryType.java index 4d6cdd936..c312f12f5 100644 --- a/src/dev/flang/fe/LibraryType.java +++ b/src/dev/flang/fe/LibraryType.java @@ -27,19 +27,11 @@ package dev.flang.fe; import java.util.Set; +import java.util.function.Consumer; import dev.flang.ast.AbstractFeature; -import dev.flang.ast.AbstractType; -import dev.flang.ast.Expr; -import dev.flang.ast.Feature; -import dev.flang.ast.Generic; import dev.flang.ast.ResolvedType; -import dev.flang.util.List; - -import dev.flang.util.HasSourcePosition; -import dev.flang.util.SourcePosition; - /** * A LibraryType represents a Fuzion type loaded from a precompiled Fuzion @@ -82,6 +74,18 @@ public abstract class LibraryType extends ResolvedType /*----------------------------- methods -----------------------------*/ + + /** + * traverse a type collecting all features this type uses. + * + * @param s the features that have already been found + */ + protected void usedFeatures(Set s){ + // a library type has already been checked. nothing to be done. + } + + + } /* end of file */ diff --git a/src/dev/flang/fe/Module.java b/src/dev/flang/fe/Module.java index c14f698d3..d51aecd97 100644 --- a/src/dev/flang/fe/Module.java +++ b/src/dev/flang/fe/Module.java @@ -31,18 +31,17 @@ import dev.flang.ast.Consts; import dev.flang.ast.Feature; import dev.flang.ast.FeatureName; -import dev.flang.ast.FormalGenerics; +import dev.flang.ast.Visi; import dev.flang.mir.MIR; import dev.flang.util.ANY; import dev.flang.util.Errors; import dev.flang.util.HasSourcePosition; +import dev.flang.util.SourceFile; -import java.util.Map; import java.util.Set; import java.util.SortedMap; -import java.util.TreeMap; import java.util.TreeSet; @@ -269,6 +268,61 @@ else if (existing == f && !existing.generics().equals(f.generics()) || } + /** + * Is type defined by feature `af` visible in file `usedIn`? + * If `af` does not define a type, result is false. + * + * @param usedIn + * @param af + * @return + */ + protected boolean typeVisible(SourceFile usedIn, AbstractFeature af) + { + var m = (af instanceof LibraryFeature lf) ? lf._libModule : this; + var definedIn = af.pos()._sourceFile; + var v = af.visibility(); + + return af.definesType() && (usedIn.sameAs(definedIn) + || (v == Visi.PRIVMOD || v == Visi.MOD) && this == m + || v == Visi.PRIVPUB || v == Visi.MODPUB || v == Visi.PUB); + } + + + /** + * Is feature `af` visible in file `usedIn`? + * @param usedIn + * @param af + * @return + */ + protected boolean featureVisible(SourceFile usedIn, AbstractFeature af) + { + var m = (af instanceof LibraryFeature lf) ? lf._libModule : this; + var definedIn = af.pos()._sourceFile; + var v = af.visibility(); + + // in same file + return ((usedIn.sameAs(definedIn) + // at least module visible and in same module + || v.ordinal() >= Visi.MOD.ordinal() && this == m + // publicly visible + || v == Visi.PUB)); + } + + + /** + * Is `a` visible for feature `b`? + * + * @param a + * @param b + * @return + */ + protected boolean visibleFor(AbstractFeature a, AbstractFeature b) + { + var usedIn = b.pos()._sourceFile; + return featureVisible(usedIn, a) || typeVisible(usedIn, a); + } + + /** * Get declared and inherited features for given outer Feature as seen by this * module. Result is never null. @@ -313,7 +367,7 @@ public SortedMap declaredOrInheritedFeatures(Abstr // * same as previous, but there is some syntax for 'D' to // chose 'a.[B].f' or 'a.[C].f'. // - if (existing != null) + if (existing != null && f != existing) { AstErrors.duplicateFeatureDeclaration(f.pos(), outer, s.get(fn)); } diff --git a/src/dev/flang/fe/SourceModule.java b/src/dev/flang/fe/SourceModule.java index 3865ef7d1..cd07011d3 100644 --- a/src/dev/flang/fe/SourceModule.java +++ b/src/dev/flang/fe/SourceModule.java @@ -39,13 +39,16 @@ import java.util.SortedMap; import java.util.TreeMap; import java.util.TreeSet; +import java.util.stream.Collectors; import dev.flang.ast.AbstractBlock; +import dev.flang.ast.AbstractCall; import dev.flang.ast.AbstractFeature; import dev.flang.ast.AbstractType; import dev.flang.ast.AstErrors; import dev.flang.ast.Call; import dev.flang.ast.Consts; +import dev.flang.ast.Current; import dev.flang.ast.Expr; import dev.flang.ast.Feature; import dev.flang.ast.FeatureName; @@ -639,6 +642,9 @@ public void addTypeFeature(AbstractFeature outerType, */ private void addDeclared(boolean inherited, AbstractFeature outer, AbstractFeature inner) { + if (PRECONDITIONS) + require(outer.isConstructor(), inner.isTypeFeature()); + var d = data(outer); var fn = inner.featureName(); if (!inherited && d._declaredFeatures != null) @@ -777,7 +783,7 @@ else if (f instanceof Feature ff && (ff._modifiers & Consts.MODIFIER_REDEFINE) = * type features, so suppress them in this case. See flang.dev's * design/examples/typ_const2.fz as an example. */ - if (Errors.count() == 0 || !f.isTypeFeature()) + if ((Errors.count() == 0 || !f.isTypeFeature()) && visibleFor(existing, f)) { AstErrors.redefineModifierMissing(f.pos(), f, existing); } @@ -1002,7 +1008,7 @@ public List lookup(AbstractFeature outer, String name, Stmnt us } if (!fields.isEmpty()) { - var f = curOuter instanceof Feature of /* NYI: AND cutOuter loaded by this module */ + var f = curOuter instanceof Feature of ? of.findFieldDefInScope(name, use, inner) : null; fs = new TreeMap<>(fs); @@ -1026,7 +1032,8 @@ public List lookup(AbstractFeature outer, String name, Stmnt us for (var e : fs.entrySet()) { var v = e.getValue(); - if (!v.isField() || !foundFieldInScope) + if ((use == null || featureVisible(use.pos()._sourceFile, v)) && + (!v.isField() || !foundFieldInScope)) { result.add(new FeatureAndOuter(v, curOuter, inner)); foundFieldInScope = foundFieldInScope || v.isField() && foundFieldInThisScope; @@ -1107,18 +1114,20 @@ public FeatureAndOuter lookupType(SourcePosition pos, if (outer != Types.f_ERROR && name != Types.ERROR_NAME) { _res.resolveDeclarations(outer); - var curOuter = outer; var type_fs = new List(); var nontype_fs = new List(); var fs = lookup(outer, name, null, traverseOuter); for (var fo : fs) { var f = fo._feature; - (f.definesType() ? type_fs - : nontype_fs).add(f); - if (f.definesType()) + if (typeVisible(pos._sourceFile, f)) { - result = fo; + (f.definesType() ? type_fs + : nontype_fs).add(f); + if (f.definesType()) + { + result = fo; + } } } if (type_fs.size() > 1) @@ -1326,6 +1335,91 @@ else if ((t1.isChoice() AstErrors.constraintMustNotBeGenericArgument(f); } } + checkLegalTypeVisibility(f); + checkResultTypeVisibility(f); + checkArgTypesVisibility(f); + checkPreconditionVisibility(f); + } + + + /** + * Are all used features in precondition at least as visible as feature? + * @param f + */ + private void checkPreconditionVisibility(Feature f) + { + f + .contract() + .req + .forEach(r -> r.visit(new FeatureVisitor() { + @Override + public void action(AbstractCall c) + { + super.action(c); + if (// visibility of arg is allowed to be more restrictive + // since it is always known by caller + !f.arguments().contains(c.calledFeature()) + // do not check stmntResult generated by label + && !(c.target() instanceof Current) + // type param is known by caller + && !c.calledFeature().isTypeParameter() + // the called feature must be at least as visible as the feature. + && c.calledFeature().visibility().featureVisibility().ordinal() < f.visibility().featureVisibility().ordinal()) + { + AstErrors.calledFeatureInPreconditionHasMoreRestrictiveVisibilityThanFeature(f, c); + } + } + }, f)); + } + + + /** + * Check that `f` defines type if type visibility is explicitly specified. + * + * @param f + */ + private void checkLegalTypeVisibility(Feature f) + { + if (!f.definesType() && f.visibility().definesTypeVisibility()) + { + AstErrors.illegalVisibilityModifier(f); + } + } + + + /** + * Check that the types of the arguments are at least as visible as `f`. + * + * @param f + */ + private void checkArgTypesVisibility(Feature f) + { + for (AbstractFeature arg : f.arguments()) + { + if (!arg.isTypeFeaturesThisType()) + { + var s = arg.resultType().moreRestrictiveVisibility(f.visibility().featureVisibility()); + if (!s.isEmpty()) + { + AstErrors.argTypeMoreRestrictiveVisbility(f, arg, s); + } + } + } + } + + + /** + * Check that result type is at least as visible as feature `f`. + * + * @param f + */ + private void checkResultTypeVisibility(Feature f) + { + var s = f.resultType().moreRestrictiveVisibility(f.visibility().featureVisibility()); + if (!s.isEmpty()) + { + AstErrors.resultTypeMoreRestrictiveVisibility(f, s); + } } diff --git a/src/dev/flang/parser/Parser.java b/src/dev/flang/parser/Parser.java index fabe013d8..dacbee864 100644 --- a/src/dev/flang/parser/Parser.java +++ b/src/dev/flang/parser/Parser.java @@ -416,9 +416,6 @@ else if (s instanceof Feature f) { if (first) { - if (CHECKS) check - (f.visibility().equals(Visi.UNSPECIFIED)); - f.setVisbility(v); list.add(f); diff --git a/tests/visibility_modules/Makefile b/tests/visibility_modules/Makefile new file mode 100644 index 000000000..f854855b1 --- /dev/null +++ b/tests/visibility_modules/Makefile @@ -0,0 +1,60 @@ +# This file is part of the Fuzion language implementation. +# +# The Fuzion language implementation is free software: you can redistribute it +# and/or modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, version 3 of the License. +# +# The Fuzion language implementation is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +# License for more details. +# +# You should have received a copy of the GNU General Public License along with The +# Fuzion language implementation. If not, see . + + +# ----------------------------------------------------------------------- +# +# Tokiwa Software GmbH, Germany +# +# Source code of Fuzion test Makefile +# +# ----------------------------------------------------------------------- + +# NAME -- the name of the main feature to be tested +# FUZION -- the fz command +# FUZION_OPTIONS -- options to be passed to $(FUZION) +NAME = main +FUZION_OPTIONS = -modules=mod -moduleDirs=modules +FUZION ?= ../../bin/fz +FUZION_RUN = $(FUZION) $(FUZION_OPTIONS) +FILE = $(NAME).fz +ENV = \ + $(if $(FUZION_HOME) , FUZION_HOME=$(FUZION_HOME) ,) \ + $(if $(FUZION_JAVA) , FUZION_JAVA=$(FUZION_JAVA) ,) \ + $(if $(FUZION_JAVA_STACK_SIZE), FUZION_JAVA_STACK_SIZE=$(FUZION_JAVA_STACK_SIZE),) \ + $(if $(FUZION_JAVA_OPTIONS) , FUZION_JAVA_OPTIONS=$(FUZION_JAVA_OPTIONS) ,) \ + +CHECK = && echo $(OK) || (echo $(FAIL); exit 1) + + +int: clean modules/mod.fum + $(ENV) ../check_simple_example.sh "$(FUZION_RUN)" $(FILE) || exit 1 + +c: clean modules/mod.fum + $(ENV) ../check_simple_example_c.sh "$(FUZION_RUN)" $(FILE) || exit 1 + +record: clean modules/mod.fum + $(ENV) ../record_simple_example.sh "$(FUZION_RUN)" $(FILE) + +record_c: clean modules/mod.fum + $(ENV) ../record_simple_example_c.sh "$(FUZION_RUN)" $(FILE) + + +# compile mod into mod.fum +modules/mod.fum: + mkdir -p $(@D) + $(FUZION) -sourceDirs=./mod -saveLib=$@ $(CHECK) + +clean: + rm -rf modules *~ */*~ */*/*~ diff --git a/tests/visibility_modules/main.fz b/tests/visibility_modules/main.fz new file mode 100644 index 000000000..9b36f6351 --- /dev/null +++ b/tests/visibility_modules/main.fz @@ -0,0 +1,20 @@ +m => + say "calling b.hello" + b.hello + + say "calling b.hello3" + b.hello3 + + say "calling b.ret_a.hello_a" + b.ret_a.hello_a + + say "calling b.ret_b.hello_a" + b.ret_b.hello_a + + say "calling b.get_aa.aaa" + b.get_aa.aaa + + say b.get_any.as_string // indirectly calling private feature + + # NYI this does not work yet + # say b.get_field diff --git a/tests/visibility_modules/main.fz.expected_err b/tests/visibility_modules/main.fz.expected_err new file mode 100644 index 000000000..e69de29bb diff --git a/tests/visibility_modules/main.fz.expected_out b/tests/visibility_modules/main.fz.expected_out new file mode 100644 index 000000000..7a471de7e --- /dev/null +++ b/tests/visibility_modules/main.fz.expected_out @@ -0,0 +1,11 @@ +calling b.hello +hello from b +calling b.hello3 +hello2 from a +calling b.ret_a.hello_a +hello_a from a +calling b.ret_b.hello_a +hello_a from b +calling b.get_aa.aaa +aaa +priv diff --git a/tests/visibility_modules/mod/a.fz b/tests/visibility_modules/mod/a.fz new file mode 100644 index 000000000..cef39622e --- /dev/null +++ b/tests/visibility_modules/mod/a.fz @@ -0,0 +1,23 @@ +module:public a is + hello is + say "hello from a" + + module hello2 is + say "hello2 from a" + + private:public aa is + public aaa is + say "aaa" + + public hello_a unit is + say "hello_a from a" + + public get_aa aa is + a.this.aa + + field := 0 + + public get_field => + field + + diff --git a/tests/visibility_modules/mod/b.fz b/tests/visibility_modules/mod/b.fz new file mode 100644 index 000000000..d94b6dcdf --- /dev/null +++ b/tests/visibility_modules/mod/b.fz @@ -0,0 +1,25 @@ +public b : a is + + # reuse hello without redef, hello is private in a + public hello is + say "hello from b" + + public hello3 is + _ := hello2 # calling hello2 visibile only in module + + public ret_a a is + a + + public ret_b b.this is + b.this + + public redef hello_a unit is + say "hello_a from b" + + priv is + redef as_string => "priv" + + public get_any Any is + priv + + field := 1 diff --git a/tests/visibility_modules_negative/Makefile b/tests/visibility_modules_negative/Makefile new file mode 100644 index 000000000..272e38572 --- /dev/null +++ b/tests/visibility_modules_negative/Makefile @@ -0,0 +1,60 @@ +# This file is part of the Fuzion language implementation. +# +# The Fuzion language implementation is free software: you can redistribute it +# and/or modify it under the terms of the GNU General Public License as published +# by the Free Software Foundation, version 3 of the License. +# +# The Fuzion language implementation is distributed in the hope that it will be +# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +# License for more details. +# +# You should have received a copy of the GNU General Public License along with The +# Fuzion language implementation. If not, see . + + +# ----------------------------------------------------------------------- +# +# Tokiwa Software GmbH, Germany +# +# Source code of Fuzion test Makefile +# +# ----------------------------------------------------------------------- + +# NAME -- the name of the main feature to be tested +# FUZION -- the fz command +# FUZION_OPTIONS -- options to be passed to $(FUZION) +NAME = main +FUZION_OPTIONS = -XmaxErrors=-1 -modules=mod -moduleDirs=modules +FUZION ?= ../../bin/fz +FUZION_RUN = $(FUZION) $(FUZION_OPTIONS) +FILE = $(NAME).fz +ENV = \ + $(if $(FUZION_HOME) , FUZION_HOME=$(FUZION_HOME) ,) \ + $(if $(FUZION_JAVA) , FUZION_JAVA=$(FUZION_JAVA) ,) \ + $(if $(FUZION_JAVA_STACK_SIZE), FUZION_JAVA_STACK_SIZE=$(FUZION_JAVA_STACK_SIZE),) \ + $(if $(FUZION_JAVA_OPTIONS) , FUZION_JAVA_OPTIONS=$(FUZION_JAVA_OPTIONS) ,) \ + +CHECK = && echo $(OK) || (echo $(FAIL); exit 1) + + +int: clean modules/mod.fum + $(ENV) ../check_simple_example.sh "$(FUZION_RUN)" $(FILE) || exit 1 + +c: clean modules/mod.fum + $(ENV) ../check_simple_example_c.sh "$(FUZION_RUN)" $(FILE) || exit 1 + +record: clean modules/mod.fum + $(ENV) ../record_simple_example.sh "$(FUZION_RUN)" $(FILE) + +record_c: clean modules/mod.fum + $(ENV) ../record_simple_example_c.sh "$(FUZION_RUN)" $(FILE) + + +# compile mod into mod.fum +modules/mod.fum: + mkdir -p $(@D) + $(FUZION) -sourceDirs=./mod -saveLib=$@ $(CHECK) + +clean: + rm -rf modules *~ */*~ */*/*~ diff --git a/tests/visibility_modules_negative/main.fz b/tests/visibility_modules_negative/main.fz new file mode 100644 index 000000000..db2b3559e --- /dev/null +++ b/tests/visibility_modules_negative/main.fz @@ -0,0 +1,48 @@ +public m => + + # checkLegalTypeVisibility + + private:module not_a_constructor => false // should flag an error feature does not define type + private:public not_a_constructor2 => false // should flag an error feature does not define type + module:public not_a_constructor3 => false // should flag an error feature does not define type + + + # checkResultTypeVisibility + + private priv is + module mod priv is // should flag an error result type visibility more restrictive than a + priv + module mod8 := priv_ce // should flag an error priv_ce visibility more restrictive than mod8 + + + # checkArgTypesVisibility + + module mod2 (arg priv) is // should flag an error arg type visibility more restrictive than a + + + # generic visibility more restrictive + + private priv_ce is + module mod3 : choice priv_ce unit is // should flag an error priv_ce visibility more restrictive than mod3 + module mod4(T type : priv_ce) is // should flag an error priv_ce visibility more restrictive than mod4 + module mod5(arg array priv_ce) is // should flag an error priv_ce visibility more restrictive than mod5 + module mod9 := (list priv_ce).type.empty // should flag an error priv_ce visibility more restrictive than mod9 + + priv3 : Sequence a.mod is // should flag an error a.mod is not visible in this file + + + # checkPreconditionVisibility + + private my_false => false + + module feat3 + pre my_false // should flag an error my_false visibility more restrictive than feat4 + is + + + # features from different modules + b.mod_pub // should flag an error should not be callable + feat1(arg1 b.mod_pub) is // even though type is usable + b.get_mod_pub.child // and we can call feature on the type + + unit diff --git a/tests/visibility_modules_negative/main.fz.expected_err b/tests/visibility_modules_negative/main.fz.expected_err new file mode 100644 index 000000000..b8df549d9 --- /dev/null +++ b/tests/visibility_modules_negative/main.fz.expected_err @@ -0,0 +1,84 @@ + +--CURDIR--/main.fz:44:5: error 1: Could not find called feature + b.mod_pub // should flag an error should not be callable +----^^^^^^^ +Feature not found: 'mod_pub' (no arguments) +Target feature: 'b' +In call: 'b.mod_pub' + + +--CURDIR--/main.fz:31:22: error 2: Type not found + priv3 : Sequence a.mod is // should flag an error a.mod is not visible in this file +---------------------^^^ +Type 'mod' was not found, no corresponding feature nor formal type parameter exists +Type that was not found: 'mod' +in feature: 'a' +To solve this, check the spelling of the type you have used. + + +--CURDIR--/main.fz:5:18: error 3: Feature specifying type visibility does not define a type. + private:module not_a_constructor => false // should flag an error feature does not define type +-----------------^^^^^^^^^^^^^^^^^ +To solve this, remove the type visibility: 'module'. + + +--CURDIR--/main.fz:6:18: error 4: Feature specifying type visibility does not define a type. + private:public not_a_constructor2 => false // should flag an error feature does not define type +-----------------^^^^^^^^^^^^^^^^^^ +To solve this, remove the type visibility: 'public'. + + +--CURDIR--/main.fz:7:18: error 5: Feature specifying type visibility does not define a type. + module:public not_a_constructor3 => false // should flag an error feature does not define type +-----------------^^^^^^^^^^^^^^^^^^ +To solve this, remove the type visibility: 'public'. + + +--CURDIR--/main.fz:13:10: error 6: Result type or any of its generics have more restrictive visibility than feature. + module mod priv is // should flag an error result type visibility more restrictive than a +---------^^^ +To solve this, increase the visibility of 'priv' (no arguments) or specify a different return type. + + +--CURDIR--/main.fz:15:10: error 7: Result type or any of its generics have more restrictive visibility than feature. + module mod8 := priv_ce // should flag an error priv_ce visibility more restrictive than mod8 +---------^^^^ +To solve this, increase the visibility of 'priv_ce' (no arguments) or specify a different return type. + + +--CURDIR--/main.fz:20:10: error 8: Argument types or any of its generics have more restrictive visibility than feature. + module mod2 (arg priv) is // should flag an error arg type visibility more restrictive than a +---------^^^^ +To solve this, increase the visibility of 'priv' (no arguments) or specify a different type for the argument 'arg'. + + +--CURDIR--/main.fz:26:10: error 9: Result type or any of its generics have more restrictive visibility than feature. + module mod3 : choice priv_ce unit is // should flag an error priv_ce visibility more restrictive than mod3 +---------^^^^ +To solve this, increase the visibility of 'priv_ce' (no arguments) or specify a different return type. + + +--CURDIR--/main.fz:27:10: error 10: Result type or any of its generics have more restrictive visibility than feature. + module mod4(T type : priv_ce) is // should flag an error priv_ce visibility more restrictive than mod4 +---------^^^^ +To solve this, increase the visibility of 'priv_ce' (no arguments) or specify a different return type. + + +--CURDIR--/main.fz:27:10: error 11: Argument types or any of its generics have more restrictive visibility than feature. + module mod4(T type : priv_ce) is // should flag an error priv_ce visibility more restrictive than mod4 +---------^^^^ +To solve this, increase the visibility of 'priv_ce' (no arguments) or specify a different type for the argument 'T'. + + +--CURDIR--/main.fz:28:10: error 12: Argument types or any of its generics have more restrictive visibility than feature. + module mod5(arg array priv_ce) is // should flag an error priv_ce visibility more restrictive than mod5 +---------^^^^ +To solve this, increase the visibility of 'priv_ce' (no arguments) or specify a different type for the argument 'arg'. + + +--CURDIR--/main.fz:39:7: error 13: Called feature in precondition has more restrictive visibility than feature. + pre my_false // should flag an error my_false visibility more restrictive than feat4 +------^^^^^^^^ +To solve this, increase the visibility of 'm.my_false' or do not use this feature in the precondition. + +13 errors. diff --git a/tests/visibility_modules_negative/main.fz.expected_out b/tests/visibility_modules_negative/main.fz.expected_out new file mode 100644 index 000000000..e69de29bb diff --git a/tests/visibility_modules_negative/mod/a.fz b/tests/visibility_modules_negative/mod/a.fz new file mode 100644 index 000000000..efe69c8df --- /dev/null +++ b/tests/visibility_modules_negative/mod/a.fz @@ -0,0 +1,5 @@ +module:public a is + module mod is + say "a.mod" + + diff --git a/tests/visibility_modules_negative/mod/b.fz b/tests/visibility_modules_negative/mod/b.fz new file mode 100644 index 000000000..ce47eecc7 --- /dev/null +++ b/tests/visibility_modules_negative/mod/b.fz @@ -0,0 +1,8 @@ +public b : a is + + module:public mod_pub is + say "should not be directly callable from outside of module" + public child is + say "child" + + public get_mod_pub => mod_pub