diff --git a/LANGUAGE b/LANGUAGE index 693cd60..3b2685d 100644 --- a/LANGUAGE +++ b/LANGUAGE @@ -23,3 +23,6 @@ PLACE IN WORKSPACE and PLACE IN VECSPACE, both currently ignored. INITIAL and RESULT abbreviations. Array constructors. + +Direction decorators. + diff --git a/backends/BackendPasses.hs b/backends/BackendPasses.hs index 891277f..ec55a12 100644 --- a/backends/BackendPasses.hs +++ b/backends/BackendPasses.hs @@ -36,7 +36,8 @@ import Utils squashArrays :: [Pass] squashArrays = - [ simplifySlices + [ removeDirections + , simplifySlices , declareSizesArray , addSizesFormalParameters , addSizesActualParameters @@ -45,6 +46,20 @@ squashArrays = prereq :: [Property] prereq = Prop.agg_namesDone ++ Prop.agg_typesDone ++ Prop.agg_functionsGone ++ [Prop.subscriptsPulledUp, Prop.arrayLiteralsExpanded] +-- | Remove all variable directions. +-- They're unimportant in occam code once the directions have been checked, +-- and this somewhat simplifies the work of the later passes. +removeDirections :: Pass +removeDirections + = occamOnlyPass "Remove variable directions" + prereq + [Prop.directionsRemoved] + (applyDepthM (return . doVariable)) + where + doVariable :: A.Variable -> A.Variable + doVariable (A.DirectedVariable _ _ v) = v + doVariable v = v + transformWaitFor :: Pass transformWaitFor = cOnlyPass "Transform wait for guards into wait until guards" [] diff --git a/backends/GenerateC.hs b/backends/GenerateC.hs index b09245a..e919b41 100644 --- a/backends/GenerateC.hs +++ b/backends/GenerateC.hs @@ -821,7 +821,8 @@ cgenVariable' checkValid v collectSubs v = do t <- astTypeOf v return ([], v, t) - +-- | Return whether a type is one that is declared as a structure, but +-- abbreviated as a pointer. indirectedType :: A.Type -> Bool indirectedType (A.Record {}) = True indirectedType (A.Chan A.DirUnknown _ _) = True diff --git a/common/Types.hs b/common/Types.hs index 830ee4d..767fda7 100644 --- a/common/Types.hs +++ b/common/Types.hs @@ -28,6 +28,7 @@ module Types , makeAbbrevAM, makeConstant, makeDimension, addOne, subOne, addExprs, subExprs, mulExprs, divExprs , addDimensions, applyDimension, removeFixedDimensions, trivialSubscriptType, subscriptType, unsubscriptType + , applyDirection , recordFields, protocolItems , leastGeneralSharedTypeRain @@ -200,13 +201,15 @@ typeOfVariable (A.SubscriptedVariable m s v) typeOfVariable (A.DerefVariable m v) = do t <- typeOfVariable v case t of - (A.Mobile innerT) -> return innerT - _ -> dieP m $ "Tried to dereference a non-mobile variable: " ++ show v + A.Mobile innerT -> return innerT + _ -> dieP m $ "Dereference applied to non-mobile variable" typeOfVariable (A.DirectedVariable m dir v) = do t <- typeOfVariable v - case t of - (A.Chan A.DirUnknown attr innerT) -> return (A.Chan dir attr innerT) - _ -> dieP m $ "Used specifier on something that was not a directionless channel: " ++ show v + case t of + A.Chan _ attr innerT -> return $ A.Chan dir attr innerT + A.Array ds (A.Chan _ attr innerT) + -> return $ A.Array ds (A.Chan dir attr innerT) + _ -> dieP m $ "Direction specified on non-channel variable" -- | Get the abbreviation mode of a variable. abbrevModeOfVariable :: (CSMR m, Die m) => A.Variable -> m A.AbbrevMode @@ -379,6 +382,16 @@ makeConstant m n = A.Literal m A.Int $ A.IntLiteral m (show n) makeDimension :: Meta -> Int -> A.Dimension makeDimension m n = A.Dimension $ makeConstant m n +-- | Apply a direction to a type. +applyDirection :: Die m => Meta -> A.Direction -> A.Type -> m A.Type +applyDirection m dir (A.Array ds t) + = applyDirection m dir t >>* A.Array ds +applyDirection m dir (A.Chan idir ca t) + | (idir == A.DirUnknown || idir == dir) = return $ A.Chan dir ca t + | otherwise = dieP m "Direction specified does not match existing direction" +applyDirection m _ t + = dieP m "Direction specified on non-channel type" + -- | Checks whether a given conversion can be done implicitly in Rain -- Parameters are src dest isImplicitConversionRain :: A.Type -> A.Type -> Bool diff --git a/frontends/OccamTypes.hs b/frontends/OccamTypes.hs index b3e2b44..cf2403b 100644 --- a/frontends/OccamTypes.hs +++ b/frontends/OccamTypes.hs @@ -77,16 +77,26 @@ areValidDimensions (d1:ds1) (d2:ds2) else return False areValidDimensions _ _ = return False +-- | Check that the second direction can be used in a context where the first +-- is expected. +isValidDirection :: A.Direction -> A.Direction -> Bool +isValidDirection _ A.DirUnknown = True +isValidDirection ed rd = ed == rd + -- | Check that a type we've inferred matches the type we expected. checkType :: Meta -> A.Type -> A.Type -> PassM () checkType m et rt = case (et, rt) of (A.Infer, _) -> ok - ((A.Array ds t), (A.Array ds' t')) -> + (A.Array ds t, A.Array ds' t') -> do valid <- areValidDimensions ds ds' if valid then checkType m t t' else bad + (A.Chan dir ca t, A.Chan dir' ca' t') -> + if isValidDirection dir dir' && (ca == ca') + then checkType m t t' + else bad _ -> do same <- sameType rt et when (not same) $ bad @@ -1026,11 +1036,17 @@ checkVariables = checkDepthM doVariable doVariable (A.SubscriptedVariable m s v) = do t <- astTypeOf v checkSubscript m s t - doVariable (A.DirectedVariable m _ v) + doVariable (A.DirectedVariable m dir v) = do t <- astTypeOf v >>= resolveUserType m case t of - A.Chan _ _ _ -> ok - _ -> dieP m $ "Direction applied to non-channel variable" + A.Chan oldDir _ _ -> checkDir oldDir + A.Array _ (A.Chan oldDir _ _) -> checkDir oldDir + _ -> dieP m $ "Direction specified on non-channel variable" + where + checkDir oldDir + = if isValidDirection dir oldDir + then ok + else dieP m "Direction specified does not match existing direction" doVariable (A.DerefVariable m v) = do t <- astTypeOf v >>= resolveUserType m case t of diff --git a/frontends/OccamTypesTest.hs b/frontends/OccamTypesTest.hs index ccaccbb..9cff0da 100644 --- a/frontends/OccamTypesTest.hs +++ b/frontends/OccamTypesTest.hs @@ -623,6 +623,9 @@ ioTests = liftM (TestLabel "OccamTypesTest" . TestList) $ sequence $ [ testOccamTypes ] ++ map (automaticTest FrontendOccam) - [ "testcases/automatic/initial-result-1.occ.test" + [ "testcases/automatic/direction-decorators-1.occ.test" + , "testcases/automatic/direction-decorators-2.occ.test" + , "testcases/automatic/direction-decorators-3.occ.test" + , "testcases/automatic/initial-result-1.occ.test" , "testcases/automatic/initial-result-2.occ.test" ] diff --git a/frontends/ParseOccam.hs b/frontends/ParseOccam.hs index b5b48b8..c24271c 100644 --- a/frontends/ParseOccam.hs +++ b/frontends/ParseOccam.hs @@ -721,7 +721,7 @@ sizeExpr do { t <- dataType; return $ A.SizeType m t } <|> do v <- operand return $ A.SizeExpr m v - <|> do v <- (channel <|> timer <|> port) + <|> do v <- (directedChannel <|> timer <|> port) return $ A.SizeVariable m v "SIZE expression" @@ -846,6 +846,36 @@ channel' <|> maybeSliced channel A.SubscriptedVariable "channel'" +direction :: OccParser A.Direction +direction + = (sQuest >> return A.DirInput) + <|> (sBang >> return A.DirOutput) + <|> return A.DirUnknown + "direction decorator" + +-- | Parse a production with an optional direction specifier, +-- returning a function to apply the direction specifier to a type and the +-- result of the inner production. +maybeDirected :: OccParser t -> OccParser (A.Type -> OccParser A.Type, t) +maybeDirected inner + = do v <- inner + m <- md + dir <- direction + return (case dir of + A.DirUnknown -> return + _ -> applyDirection m dir, + v) + +-- | Parse a channel followed by an optional direction specifier. +directedChannel :: OccParser A.Variable +directedChannel + = do c <- channel + m <- md + dir <- direction + case dir of + A.DirUnknown -> return c + _ -> return $ A.DirectedVariable m dir c + timer :: OccParser A.Variable timer = maybeSubscripted "timer" timer' A.SubscriptedVariable @@ -956,7 +986,7 @@ abbreviation :: OccParser NameSpec abbreviation = valAbbrev <|> refAbbrev variable VariableName - <|> refAbbrev channel ChannelName + <|> refAbbrev directedChannel ChannelName <|> chanArrayAbbrev <|> refAbbrev timer TimerName <|> refAbbrev port PortName @@ -992,22 +1022,31 @@ refAbbrevMode refAbbrev :: OccParser A.Variable -> NameType -> OccParser NameSpec refAbbrev oldVar nt = do m <- md - (am, t, n, v) <- - tryVVVXV refAbbrevMode (maybeInfer specifier) (newName nt) sIS oldVar + (am, t, (direct, n), v) <- + tryVVVXV refAbbrevMode + (maybeInfer specifier) + (maybeDirected $ newName nt) + sIS + oldVar sColon eol - return (A.Specification m n $ A.Is m am t v, nt) + t' <- direct t + return (A.Specification m n $ A.Is m am t' v, nt) "abbreviation by reference" chanArrayAbbrev :: OccParser NameSpec chanArrayAbbrev = do m <- md - (t, n, cs) <- - tryVVXV (maybeInfer channelSpecifier) newChannelName (sIS >> sLeft) (sepBy1 channel sComma) + (t, (direct, n), cs) <- + tryVVXV (maybeInfer channelSpecifier) + (maybeDirected newChannelName) + (sIS >> sLeft) + (sepBy1 directedChannel sComma) sRight sColon eol - return (A.Specification m n $ A.IsChannelArray m t cs, ChannelName) + t' <- direct t + return (A.Specification m n $ A.IsChannelArray m t' cs, ChannelName) "channel array abbreviation" specMode :: OccParser () -> OccParser A.SpecMode @@ -1062,7 +1101,7 @@ retypesAbbrev return (A.Specification m n $ A.Retypes m am s v, VariableName) <|> do m <- md (s, n) <- tryVVX channelSpecifier newChannelName retypesReshapes - c <- channel + c <- directedChannel sColon eol return (A.Specification m n $ A.Retypes m A.Abbrev s c, ChannelName) @@ -1128,9 +1167,10 @@ formalItem spec nt where names :: A.AbbrevMode -> A.Type -> OccParser [NameFormal] names am t - = do n <- newName nt + = do (direct, n) <- maybeDirected $ newName nt fs <- tail am t - return $ (A.Formal am t n, nt) : fs + t' <- direct t + return $ (A.Formal am t' n, nt) : fs tail :: A.AbbrevMode -> A.Type -> OccParser [NameFormal] tail am t @@ -1571,7 +1611,7 @@ actual (A.Formal am t n) A.ValAbbrev -> expression >>* A.ActualExpression _ -> case stripArrayType t of - A.Chan {} -> var channel + A.Chan {} -> var directedChannel A.Timer {} -> var timer A.Port _ -> var port _ -> var variable diff --git a/pass/Properties.hs b/pass/Properties.hs index b0f1d15..134109e 100644 --- a/pass/Properties.hs +++ b/pass/Properties.hs @@ -34,6 +34,7 @@ module Properties , declarationsUnique , declarationTypesRecorded , declaredNamesResolved + , directionsRemoved , eachRangeTransformed , eachTransformed , expressionTypesChecked @@ -414,3 +415,11 @@ resultRemoved = Property "resultRemoved" $ checkNull "resultRemoved" . listify (== A.ResultAbbrev) +directionsRemoved :: Property +directionsRemoved + = Property "directionsRemoved" $ + checkNull "directionsRemoved" . listify findVariable + where + findVariable :: A.Variable -> Bool + findVariable (A.DirectedVariable {}) = True + findVariable _ = False diff --git a/testcases/automatic/direction-decorators-1.occ.test b/testcases/automatic/direction-decorators-1.occ.test new file mode 100644 index 0000000..5d07b66 --- /dev/null +++ b/testcases/automatic/direction-decorators-1.occ.test @@ -0,0 +1,173 @@ +-- This file tests direction decorators for abbreviations. + +PROC main () + CHAN INT c, d: + [10]CHAN INT cs: + SEQ +%% + SKIP +: + +%PASS Nothing to do + +%PASS Normal kinds of abbreviations + -- Actually, these probably shouldn't be allowed, since they're + -- abbreviating a whole channel. + CHAN INT abbrev IS c: + inferred.abbrev IS c: + + []CHAN INT chan.array IS [c, d]: + []CHAN INT chans IS cs: + +%FAIL Abbreviate channel array to channel + CHAN INT chan IS cs: + +%PASS Abbreviate output end (no dir) + CHAN INT out! IS c: + +%PASS Abbreviate output end + CHAN INT out! IS c!: + +%FAIL Abbreviate output end (bad dir) + CHAN INT out! IS c?: + +%PASS Abbreviate input end (no dir) + CHAN INT in? IS c: + +%PASS Abbreviate input end + CHAN INT in? IS c?: + +%FAIL Abbreviate input end (bad dir) + CHAN INT in? IS c!: + +%PASS Abbreviate output end from array (no dir) + CHAN INT out! IS cs[0]: + +%PASS Abbreviate output end from array + CHAN INT out! IS cs[0]!: + +%FAIL Abbreviate output end from array (bad dir) + CHAN INT out! IS cs[0]?: + +%PASS Abbreviate input end from array (no dir) + CHAN INT in? IS cs[0]: + +%PASS Abbreviate input end from array + CHAN INT in? IS cs[0]?: + +%FAIL Abbreviate input end from array (bad dir) + CHAN INT in? IS cs[0]!: + +%PASS Abbreviate input ends of array (no dir) + []CHAN INT ins? IS cs: + +%PASS Abbreviate input ends of array + []CHAN INT ins? IS cs?: + +%FAIL Abbreviate input ends of array (bad dir) + []CHAN INT ins? IS cs!: + +%PASS Abbreviate output ends of array (no dir) + []CHAN INT outs! IS cs: + +%PASS Abbreviate output ends of array + []CHAN INT outs! IS cs!: + +%FAIL Abbreviate output ends of array (bad dir) + []CHAN INT outs! IS cs?: + +%PASS Abbreviate output end of abbreviation (no dir) + CHAN INT out! IS c!: + CHAN INT out.2! IS out: + +%PASS Abbreviate output end of abbreviation + CHAN INT out! IS c!: + CHAN INT out.2! IS out!: + +%FAIL Abbreviate output end of abbreviation (bad dir) + CHAN INT out! IS c!: + CHAN INT out.2! IS out?: + +%FAIL Abbreviate output end of abbreviation (to input, mismatched) + CHAN INT out! IS c!: + CHAN INT in? IS out!: + +%FAIL Abbreviate output end of abbreviation (to input, matched) + CHAN INT out! IS c!: + CHAN INT in? IS out?: + +%PASS Abbreviate input end of abbreviation (no dir) + CHAN INT in? IS c?: + CHAN INT in.2? IS in: + +%PASS Abbreviate input end of abbreviation + CHAN INT in? IS c?: + CHAN INT in.2? IS in?: + +%FAIL Abbreviate input end of abbreviation (bad dir) + CHAN INT in? IS c?: + CHAN INT in.2? IS in!: + +%FAIL Abbreviate input end of abbreviation (to output, mismatched) + CHAN INT in? IS c?: + CHAN INT out! IS in?: + +%FAIL Abbreviate input end of abbreviation (to output, matched) + CHAN INT in? IS c?: + CHAN INT out! IS in!: + +%PASS Abbreviate input ends of abbreviation (no dir) + []CHAN INT ins? IS cs: + []CHAN INT ins.2? IS ins: + +%PASS Abbreviate input ends of abbreviation + []CHAN INT ins? IS cs: + []CHAN INT ins.2? IS ins?: + +%FAIL Abbreviate input ends of abbreviation (bad dir) + []CHAN INT ins? IS cs: + []CHAN INT ins.2? IS ins!: + +%FAIL Abbreviate input ends of abbreviation (to outputs, mismatched) + []CHAN INT ins? IS cs: + []CHAN INT outs! IS ins?: + +%FAIL Abbreviate input ends of abbreviation (to outputs, matched) + []CHAN INT ins? IS cs: + []CHAN INT outs! IS ins!: + +%FAIL Abbreviate channels to end + CHAN INT out! IS cs: + +%FAIL Abbreviate channel to ends + []CHAN INT outs! IS c: + +%PASS Abbreviate output ends array (no dirs) + []CHAN INT outs! IS [c, d]: + +%PASS Abbreviate output ends array + []CHAN INT outs! IS [c!, d!]: + +%FAIL Abbreviate output ends array (both bad) + []CHAN INT outs! IS [c?, d?]: + +%FAIL Abbreviate output ends array (inconsistent 1) + []CHAN INT outs! IS [c?, d!]: + +%FAIL Abbreviate output ends array (inconsistent 2) + []CHAN INT outs! IS [c!, d?]: + +%PASS Abbreviate input ends array + []CHAN INT ins? IS [c?, d?]: + +%FAIL Can't use directions on inferred-type abbreviations + out! IS c: + +%FAIL Can't use directions on things that aren't channels + INT x: + INT y! IS x: + +%FAIL Can't use multiple directions + CHAN INT out!!!! IS c: + +% diff --git a/testcases/automatic/direction-decorators-2.occ.test b/testcases/automatic/direction-decorators-2.occ.test new file mode 100644 index 0000000..276636b --- /dev/null +++ b/testcases/automatic/direction-decorators-2.occ.test @@ -0,0 +1,85 @@ +-- This file tests direction decorators for actuals. + +PROC main () + CHAN INT c: + [10]CHAN INT cs: + INT n: + + CHAN INT out! IS c!: + CHAN INT in? IS c?: + + PROC unknown (CHAN INT c) + SKIP + : + PROC known.out (CHAN INT c!) + c ! 42 + : + PROC known.in (CHAN INT c?) + c ? n + : + PROC known.outs ([]CHAN INT cs!) + cs[0] ! 42 + : + PROC known.ins ([]CHAN INT cs?) + cs[0] ? n + : + + SEQ +%% + SKIP +: + +%PASS Nothing to do + +%PASS Unknown for unknown + unknown (c) + +%FAIL Unknown for output + unknown (out!) + +%FAIL Unknown for input + unknown (in?) + +%PASS Output for unknown + known.out (c) + +%PASS Output for directed unknown + known.out (c!) + +%FAIL Output for directed unknown (wrong dir) + known.out (c?) + +%PASS Output for output + known.out (out!) + +%FAIL Output for output (wrong dir) + known.out (out?) + +%FAIL Output for input + known.out (in?) + +%FAIL Output for input (wrong dir) + known.out (in!) + +%PASS Input for unknown + known.in (c) + +%PASS Input for directed unknown + known.in (c?) + +%FAIL Input for directed unknown (wrong dir) + known.in (c!) + +%FAIL Input for output + known.in (out!) + +%FAIL Input for output (wrong dir) + known.in (out?) + +%PASS Input for input + known.in (in?) + +%FAIL Input for input (wrong dir) + known.in (in!) + +% diff --git a/testcases/automatic/direction-decorators-3.occ.test b/testcases/automatic/direction-decorators-3.occ.test new file mode 100644 index 0000000..7a08d71 --- /dev/null +++ b/testcases/automatic/direction-decorators-3.occ.test @@ -0,0 +1,61 @@ +-- This file tests direction decorators for formals. + +PROC main () + INT n: + + SEQ +%% + SKIP +: + +%PASS Nothing to do + +%PASS Abbreviate unknown as input + PROC p (CHAN INT c) + CHAN INT in? IS c?: + SKIP + : + +%PASS Abbreviate input as input + PROC p (CHAN INT c?) + CHAN INT in? IS c?: + SKIP + : + +%FAIL Abbreviate input as output + PROC p (CHAN INT c?) + CHAN INT out! IS c?: + SKIP + : + +%PASS Use unknown as input + PROC p (CHAN INT c) + c ? n + : + +%PASS Use input as input + PROC p (CHAN INT c?) + c ? n + : + +%FAIL Use input as output + PROC p (CHAN INT c?) + c ! 42 + : + +%PASS Use unknown array as input + PROC p ([]CHAN INT cs) + cs[0] ? n + : + +%PASS Use input array as input + PROC p ([]CHAN INT cs?) + cs[0] ? n + : + +%FAIL Use input array as output + PROC p ([]CHAN INT cs?) + cs[0] ! 42 + : + +%